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.
- package/node_modules/projectstore/.claude-plugin/marketplace.json +2 -2
- package/node_modules/projectstore/.claude-plugin/plugin.json +6 -2
- package/node_modules/projectstore/.mcp.json +1 -2
- package/node_modules/projectstore/README.md +65 -16
- package/node_modules/projectstore/agents/archaeologist.md +1 -1
- package/node_modules/projectstore/agents/librarian.md +1 -1
- package/node_modules/projectstore/agents/planner.md +1 -1
- package/node_modules/projectstore/commands/doctor.md +14 -11
- package/node_modules/projectstore/commands/reconcile.md +1 -1
- package/node_modules/projectstore/commands/review.md +2 -2
- package/node_modules/projectstore/commands/story.md +1 -1
- package/node_modules/projectstore/docs/getting-started.md +3 -4
- package/node_modules/projectstore/docs/harnesses.md +1 -1
- package/node_modules/projectstore/docs/how-it-works.md +7 -0
- package/node_modules/projectstore/docs/images/icon.png +0 -0
- package/node_modules/projectstore/harnesses/claude-code.json +61 -11
- package/node_modules/projectstore/harnesses/codex.json +45 -15
- package/node_modules/projectstore/hooks/band.tsx +19 -8
- package/node_modules/projectstore/hooks/hooks.json +6 -6
- package/node_modules/projectstore/hooks/session-start.mjs +55 -24
- package/node_modules/projectstore/hooks/session-stop.mjs +43 -20
- package/node_modules/projectstore/package.json +11 -12
- package/node_modules/projectstore/scripts/band-shared.mjs +17 -2
- package/node_modules/projectstore/scripts/band.mjs +20 -2
- package/node_modules/projectstore/scripts/binding.mjs +1 -1
- package/node_modules/projectstore/scripts/build-adapters.mjs +45 -4
- package/node_modules/projectstore/scripts/cli.mjs +74 -1373
- package/node_modules/projectstore/scripts/convergence.mjs +59 -16
- package/node_modules/projectstore/scripts/doctor.mjs +199 -36
- package/node_modules/projectstore/scripts/harness.mjs +88 -6
- package/node_modules/projectstore/scripts/install-handlers.mjs +925 -0
- package/node_modules/projectstore/scripts/install-harness.mjs +129 -2081
- package/node_modules/projectstore/scripts/install-layout.mjs +453 -0
- package/node_modules/projectstore/scripts/install-preview.mjs +747 -0
- package/node_modules/projectstore/scripts/lib.mjs +387 -107
- package/node_modules/projectstore/scripts/mcp-server.mjs +33 -0
- package/node_modules/projectstore/scripts/mcp.mjs +60 -60
- package/node_modules/projectstore/scripts/model-spec.mjs +24 -0
- package/node_modules/projectstore/scripts/models.mjs +6 -10
- package/node_modules/projectstore/scripts/query.mjs +7 -6
- package/node_modules/projectstore/scripts/read-verbs.mjs +277 -0
- package/node_modules/projectstore/scripts/setup.mjs +62 -12
- package/node_modules/projectstore/scripts/statusline-launcher.mjs +36 -3
- package/node_modules/projectstore/scripts/surfaces.mjs +18 -5
- package/node_modules/projectstore/scripts/touch-session.mjs +30 -15
- package/node_modules/projectstore/scripts/verbs.mjs +1202 -0
- package/node_modules/projectstore/scripts/version-guard.mjs +57 -7
- package/package.json +3 -3
- package/node_modules/projectstore/docs/images/loop-light.svg +0 -94
- 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
|
|
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)
|
|
697
|
-
// (<root>/registry.json, named now and read by no one yet)
|
|
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).
|
|
769
|
-
|
|
770
|
-
|
|
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
|
|