npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@odla-ai/pm

v0.13.3

Published

Project management for agents — a conformance suite, kanban boards, a searchable decision log, and a bug tracker, built natively on odla-db and shared across the projects a person co-owns. Discussion and audit ride @odla-ai/chat, so humans and agents coll

Readme

@odla-ai/pm

Shared project management for people and agents — conformance goals, kanban boards, a searchable decision log, bugs, and live versioned runbooks, built natively on @odla-ai/db and shared across the projects a person co-owns. Discussion and the audit trail ride @odla-ai/chat, so humans and agents collaborate over the same rule-governed graph rather than a bolted-on comment box.

PM scope is Product → Project → Work. The Registry app id remains the Product identity; PmProject is the durable initiative beneath it, and goals, tasks, decisions, and bugs carry projectId. Existing rows without that field appear in a stable synthetic General project, so the rollout requires no destructive backfill. Runbooks remain Product-scoped operational knowledge.

It ships a pre-serialized odla-db schema, default-deny CEL rules, typed op functions, a mountable route factory, a browser/CLI client, and a @odla-ai/ai agent skill.

Ask the runbooks first. odla's operational procedures live in a database, not in this file: npx @odla-ai/cli runbook ask "<question>" returns the current steps, and unlike anything written here it cannot be out of date. Use it before searching the web or working from memory. This README and the JSDoc in the shipped .d.ts are the version-matched API reference; a runbook is the procedure. Most tasks need an answer from both.

Scope is inherited, not isolated: asking or searching with an app id considers that app's own runbooks and odla's shared platform runbooks together. An app may add local instructions, but selecting it must never hide the common procedure it is expected to follow.

Projects and the four work types

Project routes are available at /registry/pm/project; PmClient exposes listProjects, createProject, getProject, and updateProject. Pass both appId and projectId when creating work, and use ?app=<product>&project=<id> to list one project's records.

  • Conformance goals — a key outcome plus the test (proof) that would prove it met; openmet.
  • Kanban tasks — units of work positioned by column and rank; move them across the board.
  • Decisions — the critical/technical decisions made while building, full-text searchable.
  • Bugs — reported defects and missing pieces; opentriagedfixed/wontfix. triaged is the one status with a bar to clear: it means someone decided how bad and who carries it, so it requires a severity plus an assigneeId or a goalId, enforced on write. Without that rule it was a label anyone could apply to nothing, which is exactly what it kept being. The other three states are self-evident from the record, so they are ungated.

Who filed it

Ownership and authorship are deliberately separate:

  • ownerId / ownerEmail — the accountable human account used for project ownership.
  • principalId / principalKind — the actual credential-bound human, agent, or service that acted.
  • credentialId / credentialKind, managerPrincipalId, delegatedByPrincipalId, grantId, and grantVersion — the credential and authority provenance available for audit.
  • agentId / agentLabel — a compatibility projection when that authenticated principal is an agent.
  • lastEditedByPrincipalId / lastEditedByPrincipalKind plus the corresponding credential, manager, delegator, grant revision, and label fields — the actual author and authority behind the record's current contents. Creation provenance above remains immutable.

The registry supplies this context through PmOperator; PmClient only sends the bearer credential. The legacy x-odla-agent-id and x-odla-agent-label headers are rejected with actor_spoof_forbidden, including when empty. An agent cannot rename itself, claim another principal, or turn manager metadata into authority.

For app-scoped actions, the registry implements PmAuthz.resolveAppAuthority: it resolves the live exact/wildcard project grant immediately before the action and stamps that grant's id, version, and grantor. A revoked grant denies the next action. Platform runbook capability writes keep their credential provenance and do not fabricate a project grant.

PM task authority is deliberately split at that boundary:

  • pm.read observes current state and the work journal.
  • pm.write covers general PM mutation and remains executable for compatibility with grants issued before the split.
  • pm.plan includes read/write, task refinement and deletion, and the reviewed transition into Ready; it does not permit claim or active execution.
  • pm.execute includes read/write, Backlog proposals and findings, atomic claim/release, and claimed Doing → Review → Done transitions; it does not permit contract refinement or Ready approval.

Ordinary managed-agent handshakes receive pm.execute, not pm.plan. A human owner or deliberately authorized planner must make the durable Ready handoff. A mixed task patch is treated as planning, so an executor cannot attach a contract edit to an otherwise executable column change.

Trusted in-process code may construct PmSelf directly. At that seam the caller is the host: do not populate it from untrusted request fields.

What changed lately

List routes take ?since=hour|day|week, narrowing to records whose updatedAt falls inside the window and ordering them most-recently-touched first. This tracks record changes — a field edit, or the create itself; discussion comments live in @odla-ai/chat and do not bump the record. Status-style filters also accept a comma-separated set (?status=open,triaged), which is how the bugs view asks for everything still wrong in one round trip.

Views are the subscription

A PM view renders straight off a WebSocket onto the shared odla-pm tenant. Rows and their pagination totals arrive by push; nothing polls.

Pass the suite a reactive query hook bound to that tenant and it uses it:

<OdlaProvider db={pmDb}>
  <PmSuite client={api.pm} useQuery={useQuery} projects={projects} />
</OdlaProvider>

Omit useQuery and the same views fall back to the REST route on a timer, so the kit still works for a host without a socket.

Bridging a record into Discussion

Pass onDiscuss to PmSuite to expose a Discuss action beside Copy reference. The callback receives a PmDiscussionTarget with the exact project, record kind/id, current title, and copy-ready pm:* markup. A host can use that identity to open a scoped Discussion composer; PmSuite never posts or assumes Discussion authority itself.

Studio carries that target as bounded URL draft state and preloads a structured reference into Discussion. Posting remains an explicit viewer action. This is a navigation/authoring bridge, not a canonical one-topic-per-record migration: PM-attached comments and general Discussion keep their independent capability checks and event streams.

Durable agent work events

Every goal, task, decision, and bug create/update/delete appends an immutable pm_event snapshot in the same odla-db transaction. Operational runbooks keep their separate visibility-sensitive history. GET /registry/pm/work/watch?app=... projects those rows through the database change feed with an opaque resumable cursor, stable event ids, at-least-once delivery, and an explicit checkpoint_required response when history can no longer resume safely.

Use PmClient.watchWork(appId, cursor) for one page, the PM agent skill's watch_work tool for supervisor-driven page reads, or odla-ai pm watch --app <id> --jsonl for a long-running process. The first read establishes a checkpoint rather than replaying history. Event delivery is a signal to reload authoritative current state; a task still requires a winning revisioned claim before execution.

The same cursor also delivers *.comment.created/*.comment.updated for chat messages whose channel or topic root correlates to an active goal, task, decision, or bug. General project topics and mentions remain on the independent Discussion watch: pm.read and discussion.read are distinct capabilities, so merging those rows before Discussion has context-aware authorization would leak one domain through the other. Visibility-sensitive runbook comments remain off the work stream for the same reason. Deleting a work item removes its current row but deliberately retains the authorized deletion snapshot until the exact project lifetime is purged.

How the socket is scoped

The browser holds no credential for odla-pm and must not — the tenant carries every project's PM rows, and only the registry knows which projects an operator co-owns. POST /registry/pm/live-ticket resolves that and signs it into a short-lived scoped ticket: the DO accepts it as a rules-enforced session (never admin), and the tenant's CEL rules filter every row against the claims:

view: data.projectScope in auth.claims.projectScopes

The claims are covered by the signature, so a holder cannot widen their own scope. The exact scope contains the Registry incarnation, so a ticket for lifetime A cannot read replacement lifetime B even when both share an app id. Authorship is not an authority bypass: a removed collaborator's fresh empty ticket cannot keep reading rows they created. Archived lifetimes stay in read claims while their writes are suspended. Tickets are minted per (re)connect, so ownership and archive changes take effect on the next connect. The registry installs these rules itself when they differ from the running build's.

Reads are live; writes still go through the route

Writes stay on PmClient; every rules-enforced create/update/delete is false. Each route write resolves credential-bound authority for that exact project lifetime: a human through current ownership, or a managed agent through its current capability grant. Only then does it create or atomically normalize the exact lifetime's open-write fence. REST reads only inspect an existing lifetime: a retained legacy read with no lifetime remains read-only, while a mismatched or tombstoned lifetime fails closed. It also performs side effects (a status change posts to the item's @odla-ai/chat thread) that a direct transact would skip. The socket then delivers the result, so a write still repaints on its own — the write path just isn't the read path run backwards.

Runbook revisions are deliberately not readable over this channel: they carry no visibility of their own and there are no links for a rule to reach their parent runbook, so a claims-based read would expose an admin-visibility runbook's body through its history. They stay on the REST route, which checks the parent.

Runbook body writes also carry expectedVersion, the exact version the human or agent reviewed. The prior-body snapshot and replacement commit in one guarded transaction; if another collaborator wins first, the stale write returns 409 and creates no orphan revision. The Studio editor, CLI, and amend_runbook tool all forward the version they actually read.

Shared across the projects you can access

Every PM record carries an exact project scope (logical app id plus Registry incarnation) and lives in one shared odla-db tenant. Humans receive access through current ownership; managed agents receive only the explicit pm.read/pm.write/pm.plan/pm.execute project grants attached to their credential-bound principal. The Registry resolves that action-specific authority at each REST action, while a signed live ticket carries only the exact read scopes authorized at connect time. That keeps a cross-project board convenient without turning the shared store into shared authority.

Quick start

import {
  createBug,
  listBugs,
  type PmDb,
  type PmSelf,
} from "@odla-ai/pm";
import type { ProjectScope } from "@odla-ai/chat";

type TrustedActor = PmSelf & { project: ProjectScope };

// The trusted Registry host injects these values; browsers and agents never
// receive its DB_ADMIN_SECRET or a direct odla-pm tenant key.
export async function quickStart(
  db: PmDb,
  self: TrustedActor,
  readableProjects: ProjectScope[],
) {
  const { id } = await createBug(
    { db },
    {
      appId: self.project.appId,
      self,
      input: {
        title: "Checkout 500s on retry",
        description: "A second submit after a timeout returns 500; request id …",
        severity: "high",
      },
    },
  );

  const { records } = await listBugs(
    { db },
    { apps: readableProjects, status: "open" },
  );
  return { id, records };
}

For agents

import { pmSkill, type PmDb, type PmSelf } from "@odla-ai/pm";
import type { ProjectScope } from "@odla-ai/chat";

// Attach to a @odla-ai/ai Persona; the trusted host supplies the exact project.
export function projectSkill(
  db: PmDb,
  self: PmSelf & { project: ProjectScope },
) {
  return pmSkill({ db, self, appId: self.project.appId });
}
// add_goal, mark_goal_met, list_goals, get_goal,
// add_task, list_tasks, get_task, mark_task_ready, claim_task, release_task, move_task,
// report_bug, resolve_bug, list_bugs, record_decision, search_decisions,
// project_handoff

project_handoff returns the project's unmet goals, active tasks, and open bugs directly from PM. Bug descriptions are required. A bug may link an accepted same-project decision with decisionId, making an accepted delta or retained risk explicit instead of leaving a contradictory open title behind. Goals cannot be marked met while linked tasks or bugs remain unresolved.

todo is the stable storage value for the user-facing Ready state. Entering it requires an open same-project goal or accepted alignment decision, an actionable description, and acceptance criteria. Tasks carry a monotonic revision. The REST boundary additionally requires planning authority for Ready. Agents start work with claimTask/claim_task, which compares the reviewed revision and atomically assigns the authenticated principal while moving to doing; releaseTask returns an owned claim to Ready. Blind agent moves into Doing are rejected.

Pieces

  • PM_SCHEMA / PM_RULES — the odla-db schema + default-deny rules for the shared tenant.
  • pmTenantSchema(installed?) — the non-destructive merged PM + Discussion schema builder for fresh installs and legacy numeric-timestamp tenants.
  • createGoal / createTask / createDecision / createBug (+ get/update/list/status verbs) — typed ops.
  • markTaskReady / claimTask / releaseTask — revision-guarded work intake and ownership transitions.
  • createPmRoutes({ db, authz }) — the mountable route factory the odla-ai registry composes.
  • PmClient — a typed fetch wrapper for the CLI and Studio UI.
  • pmSkill — the agent skill; ensureItemChannel / comment / listMessages — chat-backed discussion.
  • pmIntegration — documentation-as-data for provisioning: one merged PM + Discussion schema in tenantSchema, plus the scoped tenantRules Registry reconciles over its exact-lifetime DB binding before /registry/pm/live-ticket. Its legacy fragment fields remain only for compatibility. Do not install plain CHAT_RULES or mint a direct shared-tenant key.

See the exported TypeScript declarations / JSDoc, and the rendered reference at https://odla.ai/docs/packages/pm.

Agentic experiment. odla is an experiment in software built primarily by agents. Treat it accordingly.

License

MIT