projectstore-claude 0.30.4 → 0.30.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/node_modules/projectstore/.claude-plugin/marketplace.json +2 -2
  2. package/node_modules/projectstore/.claude-plugin/plugin.json +6 -2
  3. package/node_modules/projectstore/.mcp.json +1 -2
  4. package/node_modules/projectstore/README.md +65 -16
  5. package/node_modules/projectstore/agents/archaeologist.md +1 -1
  6. package/node_modules/projectstore/agents/librarian.md +1 -1
  7. package/node_modules/projectstore/agents/planner.md +1 -1
  8. package/node_modules/projectstore/commands/doctor.md +14 -11
  9. package/node_modules/projectstore/commands/reconcile.md +1 -1
  10. package/node_modules/projectstore/commands/review.md +2 -2
  11. package/node_modules/projectstore/commands/story.md +1 -1
  12. package/node_modules/projectstore/docs/getting-started.md +3 -4
  13. package/node_modules/projectstore/docs/harnesses.md +1 -1
  14. package/node_modules/projectstore/docs/how-it-works.md +7 -0
  15. package/node_modules/projectstore/docs/images/icon.png +0 -0
  16. package/node_modules/projectstore/harnesses/claude-code.json +61 -11
  17. package/node_modules/projectstore/harnesses/codex.json +45 -15
  18. package/node_modules/projectstore/hooks/band.tsx +19 -8
  19. package/node_modules/projectstore/hooks/hooks.json +6 -6
  20. package/node_modules/projectstore/hooks/session-start.mjs +55 -24
  21. package/node_modules/projectstore/hooks/session-stop.mjs +43 -20
  22. package/node_modules/projectstore/package.json +11 -12
  23. package/node_modules/projectstore/scripts/band-shared.mjs +17 -2
  24. package/node_modules/projectstore/scripts/band.mjs +20 -2
  25. package/node_modules/projectstore/scripts/binding.mjs +1 -1
  26. package/node_modules/projectstore/scripts/build-adapters.mjs +45 -4
  27. package/node_modules/projectstore/scripts/cli.mjs +74 -1373
  28. package/node_modules/projectstore/scripts/convergence.mjs +59 -16
  29. package/node_modules/projectstore/scripts/doctor.mjs +199 -36
  30. package/node_modules/projectstore/scripts/harness.mjs +88 -6
  31. package/node_modules/projectstore/scripts/install-handlers.mjs +925 -0
  32. package/node_modules/projectstore/scripts/install-harness.mjs +129 -2081
  33. package/node_modules/projectstore/scripts/install-layout.mjs +453 -0
  34. package/node_modules/projectstore/scripts/install-preview.mjs +747 -0
  35. package/node_modules/projectstore/scripts/lib.mjs +387 -107
  36. package/node_modules/projectstore/scripts/mcp-server.mjs +33 -0
  37. package/node_modules/projectstore/scripts/mcp.mjs +60 -60
  38. package/node_modules/projectstore/scripts/model-spec.mjs +24 -0
  39. package/node_modules/projectstore/scripts/models.mjs +6 -10
  40. package/node_modules/projectstore/scripts/query.mjs +7 -6
  41. package/node_modules/projectstore/scripts/read-verbs.mjs +277 -0
  42. package/node_modules/projectstore/scripts/setup.mjs +62 -12
  43. package/node_modules/projectstore/scripts/statusline-launcher.mjs +36 -3
  44. package/node_modules/projectstore/scripts/surfaces.mjs +18 -5
  45. package/node_modules/projectstore/scripts/touch-session.mjs +30 -15
  46. package/node_modules/projectstore/scripts/verbs.mjs +1202 -0
  47. package/node_modules/projectstore/scripts/version-guard.mjs +57 -7
  48. package/package.json +3 -3
  49. package/node_modules/projectstore/docs/images/loop-light.svg +0 -94
  50. package/node_modules/projectstore/docs/images/team-light.svg +0 -79
@@ -321,6 +321,28 @@ export function harnessHome(manifest, { env = process.env, home = homedir() } =
321
321
  return fromEnv || (home && r.home_default ? join(home, r.home_default) : null);
322
322
  }
323
323
 
324
+ // The registry block of a manifest's host plugin registration, where it names
325
+ // its host's own folders and files (cache_dir, synced_dir, the registry
326
+ // files), or null for a harness whose registration is not the host's plugin
327
+ // system.
328
+ export function hostRegistry(manifest) {
329
+ const [surface, s] =
330
+ Object.entries(manifest?.surfaces || {}).find(
331
+ ([k, x]) => !k.startsWith("_") && x?.format === "host-plugin-registration" && x.registry,
332
+ ) || [];
333
+ return s ? { ...s.registry, plugin_name: s.plugin_name, surface } : null;
334
+ }
335
+
336
+ // Whether the host auto-updates `marketplace` when no settings entry and no
337
+ // registry entry say (registry.auto_update: its names and name prefixes are on,
338
+ // every other marketplace off). null when the manifest states no default.
339
+ export function autoUpdateDefault(manifest, marketplace) {
340
+ const d = hostRegistry(manifest)?.auto_update;
341
+ if (!d) return null;
342
+ const name = String(marketplace || "");
343
+ return (d.on || []).includes(name) || (d.on_prefix || []).some((p) => name.startsWith(p));
344
+ }
345
+
324
346
  // The agents block of a RUN (the story "One run plans the agents block once,
325
347
  // and a bare uninstall selects every harness the project uses", rules 1–2):
326
348
  // the files every manifest of the run names for it, in the FIRST manifest's
@@ -411,6 +433,46 @@ export function activeHarness(env = process.env, dir = MANIFEST_DIR) {
411
433
  return loadHarness(detectHarnessId(env, dir), dir) || sourceHarness(dir);
412
434
  }
413
435
 
436
+ // ─── The Stop carrier (generation spec, contract 17; entry spec, contract 14,
437
+ // as amended 2026-10-15) ────────────────────────────────────────────────
438
+ //
439
+ // The shape the entry reminder's Stop carrier prints, from the manifest of the
440
+ // harness that IDENTIFIES itself — a forced harness, a parent's decision, a
441
+ // strong signal or a session marker. Never detection's cwd folder, weak ranking or source
442
+ // fallback: a wrong answer here is a hook the host refuses (Codex's Stop schema
443
+ // denies hookSpecificOutput, measured 2026-10-15), and a refused print would
444
+ // still spend the session's reminder. Nothing identified, or no carrier
445
+ // measured, is null: the hook stays silent.
446
+ export const STOP_CARRIERS = Object.freeze(["additionalContext", "block"]);
447
+ export function stopCarrier(env = process.env, dir = MANIFEST_DIR) {
448
+ const id = identifiedHarnessId(env, dir);
449
+ const carrier = id ? loadHarness(id, dir)?.output_channels?.Stop?.carrier : null;
450
+ return STOP_CARRIERS.includes(carrier) ? carrier : null;
451
+ }
452
+
453
+ // What is wrong with a manifest's Stop row, or null. The carrier is our choice
454
+ // and must be one its row supports: context needs `additionalContext` among the
455
+ // honoured fields, a block needs the block decision, and a carrier is only
456
+ // ever chosen from a measured row. Shared by the manifest check and its
457
+ // planted fixtures.
458
+ export function stopCarrierProblem(manifest) {
459
+ const row = manifest?.output_channels?.Stop;
460
+ if (row === null || row === undefined) return null;
461
+ if (typeof row !== "object") return "output_channels.Stop is not an object";
462
+ if (!("carrier" in row)) return "output_channels.Stop has no carrier (null when none is measured)";
463
+ if (![...STOP_CARRIERS, null].includes(row.carrier))
464
+ return `output_channels.Stop.carrier ${JSON.stringify(row.carrier)} is not one of ${STOP_CARRIERS.join(", ")} or null`;
465
+ if (![null, "block"].includes(row.decision))
466
+ return `output_channels.Stop.decision ${JSON.stringify(row.decision)} is not null or "block"`;
467
+ if (row.carrier === null) return null;
468
+ if (row.evidence !== "measured") return "output_channels.Stop.carrier is set on a row that is not measured";
469
+ if (row.carrier === "additionalContext" && !(row.fields || []).includes("additionalContext"))
470
+ return "output_channels.Stop.carrier is additionalContext, but its fields do not honour it";
471
+ if (row.carrier === "block" && row.decision !== "block")
472
+ return 'output_channels.Stop.carrier is block, but its decision is not "block"';
473
+ return null;
474
+ }
475
+
414
476
  // ─── Who a message is for (generation spec, contract 18) ───────────────
415
477
  //
416
478
  // The core prints for ONE harness at a time, and a command it names must be
@@ -563,7 +625,8 @@ export function projectRootDeclared(env = process.env) {
563
625
  // Where this projectstore's core lives on disk: its layouts, templates and
564
626
  // scripts. The bin runs ITS OWN copy of the core: a child gets that through childEnv's
565
627
  // pluginRoot, an in-process read (the query verbs' loadLayout, the headings
566
- // registry) through this pin. Set once by cli.mjs; nothing else may call it.
628
+ // registry) through this pin. Set by verbs.mjs's dispatch, on every run of
629
+ // the CLI and of the read runner; nothing else may call it.
567
630
  let _pinnedPluginRoot = null;
568
631
  export function pinPluginRoot(root) {
569
632
  _pinnedPluginRoot = root || null;
@@ -693,8 +756,10 @@ export function layoutPaths(projectDir, { harnessDir = null, dir = MANIFEST_DIR
693
756
  //
694
757
  // What is ours on the machine but belongs to no project and no harness home:
695
758
  // the shell fetch's scratch (<root>/fetch/<pid>-<ms>/, removed when its run
696
- // ends) and the registry answer the update notice caches for a day
697
- // (<root>/registry.json, named now and read by no one yet). The XDG Base
759
+ // ends), the registry answer the update notice caches for a day
760
+ // (<root>/registry.json, named now and read by no one yet), the model lists
761
+ // (<root>/models/<id>.json) and the welcome an unbound project has shown
762
+ // (<root>/welcomed/<id>). The XDG Base
698
763
  // Directory spec puts a cache at $XDG_CACHE_HOME when that is an absolute
699
764
  // path and ignores a relative one; an empty one is ignored too. Otherwise it
700
765
  // is <home>/.cache. With neither, `root` is null and the fetch refuses. This
@@ -715,6 +780,12 @@ export function cachePaths({ env = process.env, home = homedir() } = {}) {
715
780
  // A harness's model list, as its last live read answered (the ADR "Agent
716
781
  // model lists are read from each harness's own catalog…", decision 4).
717
782
  models: (id) => (root && id ? join(root, "models", `${id}.json`) : null),
783
+ // A harness's first-run welcome, seen on this machine: the SessionStart
784
+ // hook writes it in place of the project's marker when the project is not
785
+ // bound, so an unbound project gets no writes (layout spec contract 5, as
786
+ // amended by the story "Opening a project that is not bound writes nothing
787
+ // into it…"). A lost marker costs one more welcome.
788
+ welcomed: (id) => (root && id ? join(root, "welcomed", id) : null),
718
789
  from: fromEnv ? variable : "the home directory",
719
790
  variable,
720
791
  };
@@ -765,9 +836,20 @@ export function shellHarnessId(env = process.env, dir = MANIFEST_DIR) {
765
836
 
766
837
  // The overlay a harness reads: <project>/.projectstore/harness/<overlay>.json —
767
838
  // the manifest's runtime.overlay, the harness id by convention (the layout ADR,
768
- // decision 3). Null only when no manifest at all can be found.
769
- export function overlayId(env = process.env, dir = MANIFEST_DIR) {
770
- const h = activeHarness(env, dir);
839
+ // decision 3). Whose (layout spec, contract 2, as amended 2026-10-15): the
840
+ // harness that identifies itself; else the shell that bundles this core; else
841
+ // the shell the environment names; else detectHarnessId's answer (the cwd's
842
+ // harness folder, the weak ranking, the source harness). Contract 18's middle
843
+ // rungs with detection's tail: a rendered skill's exec tool carries no variable
844
+ // of its harness, and the cwd's folder would answer for it. --harness stays the
845
+ // caller's (cli.mjs runAgents). Null only when no manifest at all can be found.
846
+ export function overlayId(env = process.env, dir = MANIFEST_DIR, { core } = {}) {
847
+ const id =
848
+ identifiedHarnessId(env, dir) ||
849
+ bundlingShellHarnessId({ core, dir }) ||
850
+ shellHarnessId(env, dir) ||
851
+ detectHarnessId(env, dir);
852
+ const h = loadHarness(id, dir) || sourceHarness(dir);
771
853
  return h?.runtime?.overlay || h?.id || null;
772
854
  }
773
855