projectstore-claude 0.30.9 → 0.30.10

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 +1 -1
  2. package/node_modules/projectstore/.claude-plugin/plugin.json +1 -1
  3. package/node_modules/projectstore/README.md +4 -3
  4. package/node_modules/projectstore/agents/archaeologist.md +1 -1
  5. package/node_modules/projectstore/agents/critic.md +26 -3
  6. package/node_modules/projectstore/agents/librarian.md +1 -1
  7. package/node_modules/projectstore/agents/planner.md +7 -1
  8. package/node_modules/projectstore/agents/reviewer.md +8 -1
  9. package/node_modules/projectstore/commands/adr.md +10 -0
  10. package/node_modules/projectstore/commands/bind.md +4 -3
  11. package/node_modules/projectstore/commands/concept.md +9 -0
  12. package/node_modules/projectstore/commands/doctor.md +17 -4
  13. package/node_modules/projectstore/commands/epic.md +10 -0
  14. package/node_modules/projectstore/commands/meeting.md +9 -0
  15. package/node_modules/projectstore/commands/research.md +9 -0
  16. package/node_modules/projectstore/commands/review.md +11 -7
  17. package/node_modules/projectstore/commands/runbook.md +9 -0
  18. package/node_modules/projectstore/commands/spec.md +10 -0
  19. package/node_modules/projectstore/commands/story.md +25 -1
  20. package/node_modules/projectstore/docs/extending.md +44 -5
  21. package/node_modules/projectstore/package.json +1 -1
  22. package/node_modules/projectstore/scaffold/guides/adr.md +42 -0
  23. package/node_modules/projectstore/scaffold/guides/concept.md +10 -0
  24. package/node_modules/projectstore/scaffold/guides/epic.md +36 -0
  25. package/node_modules/projectstore/scaffold/guides/meeting.md +11 -0
  26. package/node_modules/projectstore/scaffold/guides/research.md +16 -0
  27. package/node_modules/projectstore/scaffold/guides/runbook.md +12 -0
  28. package/node_modules/projectstore/scaffold/guides/spec.md +39 -0
  29. package/node_modules/projectstore/scaffold/guides/story.md +34 -0
  30. package/node_modules/projectstore/scripts/cli.mjs +55 -1
  31. package/node_modules/projectstore/scripts/configure-menu.mjs +2 -1
  32. package/node_modules/projectstore/scripts/configure.mjs +12 -2
  33. package/node_modules/projectstore/scripts/doctor.mjs +298 -38
  34. package/node_modules/projectstore/scripts/guides.mjs +217 -0
  35. package/node_modules/projectstore/scripts/harness.mjs +2 -0
  36. package/node_modules/projectstore/scripts/lib.mjs +134 -1
  37. package/node_modules/projectstore/scripts/mcp.mjs +30 -3
  38. package/node_modules/projectstore/scripts/read-verbs.mjs +59 -3
  39. package/node_modules/projectstore/scripts/scaffold.mjs +1 -1
  40. package/node_modules/projectstore/scripts/story-section.mjs +16 -1
  41. package/node_modules/projectstore/scripts/verbs.mjs +33 -1
  42. package/node_modules/projectstore/skills/projectstore-peer-reviewer/SKILL.md +9 -3
  43. package/node_modules/projectstore/templates/de/story.md.tmpl +1 -0
  44. package/node_modules/projectstore/templates/en/story.md.tmpl +1 -0
  45. package/node_modules/projectstore/templates/es/story.md.tmpl +1 -0
  46. package/node_modules/projectstore/templates/fr/story.md.tmpl +1 -0
  47. package/node_modules/projectstore/templates/ru/story.md.tmpl +1 -0
  48. package/node_modules/projectstore/templates/zh/story.md.tmpl +1 -0
  49. package/package.json +2 -2
  50. package/node_modules/projectstore/scaffold/checklists.json +0 -88
@@ -0,0 +1,36 @@
1
+ ---
2
+ guide_for: epic
3
+ guide_version: 1
4
+ default_review: true
5
+ ---
6
+
7
+ ## Lens
8
+
9
+ The product owner who must say, later, whether this epic worked. State the outcome in terms someone can check, then the stories that deliver it.
10
+
11
+ ## Must
12
+
13
+ - Goal is measurable — success can be verified, not just described.
14
+ - Context explains why this epic exists and which downstream pieces depend on it.
15
+ - Stories table has at least 2 entries; first story description is concrete enough to start.
16
+ - Expected Results contains testable outcomes, not aspirations.
17
+ - Dependencies list external blockers (people, systems, decisions), not just sibling stories.
18
+ - Open Questions are real unknowns, not rhetorical filler.
19
+ - Priority assigned (p0 / p1 / p2), not left as default if epic is non-trivial.
20
+
21
+ ## Common mistakes
22
+
23
+ - A Goal that describes activity (build, improve, refactor), not a result that can be verified.
24
+ - Expected Results that are aspirations. Each one should be a check someone can run or read.
25
+ - A Stories table that is a to-do dump: a vague first story, no order.
26
+ - Dependencies that name only sibling stories. The blockers are people, systems and decisions outside the epic.
27
+ - `code_refs` left empty after the first story lands, or the priority left at the default.
28
+
29
+ ## Do not write
30
+
31
+ - Implementation detail (files, functions): it belongs in the spec and the plans.
32
+ - A story's acceptance criteria or an ADR's text copied into the epic: link them.
33
+
34
+ ## Length
35
+
36
+ Goal in two to four sentences; the whole epic 40 to 100 lines plus the stories table.
@@ -0,0 +1,11 @@
1
+ ---
2
+ guide_for: meeting
3
+ guide_version: 1
4
+ default_review: false
5
+ ---
6
+
7
+ ## Must
8
+
9
+ - Attendees listed.
10
+ - Decisions section captures what was actually decided (not just discussed).
11
+ - Action items have an owner.
@@ -0,0 +1,16 @@
1
+ ---
2
+ guide_for: research
3
+ guide_version: 1
4
+ default_review: true
5
+ ---
6
+
7
+ ## Must
8
+
9
+ - Question is sharp — answerable by yes/no/comparison, not 'tell me about X'.
10
+ - TL;DR is a single paragraph that gives the conclusion before the body.
11
+ - Method describes how findings were gathered (docs read, benchmarks run, experts asked) — not just 'I researched'.
12
+ - Comparison table has populated Pros AND Cons cells; empty cells flagged.
13
+ - Conclusion explicitly ties back to the original Question. Recommendation is actionable.
14
+ - References are linkable (URL / file path), not 'see [some doc]'.
15
+ - No unstated benchmarks ('it's faster' without numbers).
16
+ - Findings are evidence-based, not opinion. Where opinion appears, it's labelled.
@@ -0,0 +1,12 @@
1
+ ---
2
+ guide_for: runbook
3
+ guide_version: 1
4
+ default_review: false
5
+ ---
6
+
7
+ ## Must
8
+
9
+ - Purpose states WHEN to run this runbook (trigger conditions).
10
+ - Prerequisites are listable checks, not prose.
11
+ - Verification step exists and is testable.
12
+ - Rollback step exists for any irreversible action.
@@ -0,0 +1,39 @@
1
+ ---
2
+ guide_for: spec
3
+ guide_version: 1
4
+ default_review: true
5
+ ---
6
+
7
+ ## Lens
8
+
9
+ The systems analyst who writes the contract a test can be written from before the code exists. Each contract states an observable outcome and the scenario that would prove it.
10
+
11
+ ## Must
12
+
13
+ - How we solve references at least one ADR by id; the references resolve.
14
+ - Behavioral contracts are numbered and individually testable — each states an observable outcome, not an implementation step.
15
+ - Acceptance items are checkboxes; acceptance is explicitly ADDITIVE to the covered stories' own criteria, not a replacement.
16
+ - Acceptance items that belong to specific stories carry a '— stories: <id>' attribution; items meant for all covered stories are unattributed on purpose.
17
+ - frontmatter stories: uses <epic-id>/<story-id> path form, inline flow list.
18
+ - Mandatory sections present (How we solve / Behavioral contracts / Acceptance); Modules-on-disk and Testing may be omitted only for research spikes.
19
+ - For a spike spec: contracts read as questions-to-answer plus exit criteria.
20
+ - No TODOs, no '...', no empty bullets in the final body.
21
+
22
+ ## Common mistakes
23
+
24
+ - A contract that describes an implementation step (calls X, stores Y) instead of what a caller observes.
25
+ - Scenarios that show only the happy path. Add the boundary, the invalid input and the failed dependency a production run will hit.
26
+ - Acceptance that restates the covered stories' own criteria. It is additive: each item adds something only the spec promises.
27
+ - Numbers, tables and contract references copied from memory. Open the ADR and the sibling spec and copy from them.
28
+ - Frontmatter lists in block form (the parser reads inline flow), or an `adr:` id that does not resolve.
29
+ - A citation token for a real key written in prose or an example. Use the reserved key, as in `[example#1]`.
30
+
31
+ ## Do not write
32
+
33
+ - The argument for the decision, or its alternatives: link the ADR.
34
+ - A plan or a task list: that is the story's Implementation Plan.
35
+ - Files the spec does not govern, or code beyond a three-line illustration.
36
+
37
+ ## Length
38
+
39
+ A contract is one paragraph and at most six scenario items. A spec past twelve contracts or 400 lines covers two subsystems: split it.
@@ -0,0 +1,34 @@
1
+ ---
2
+ guide_for: story
3
+ guide_version: 1
4
+ default_review: false
5
+ ---
6
+
7
+ ## Lens
8
+
9
+ The engineer who picks this up in a fresh session, with none of this conversation. Say who benefits and what changes, in steps small enough to finish one at a time, and criteria a test can be written from.
10
+
11
+ ## Must
12
+
13
+ - Description states the user/actor and the value of the change.
14
+ - Decomposition checklist has items granular enough to finish in a session each.
15
+ - Acceptance Criteria are testable (someone could write tests from these).
16
+ - Technical Notes flag any non-obvious risks or constraints.
17
+
18
+ ## Common mistakes
19
+
20
+ - Acceptance criteria that repeat decomposition tasks (implement X) instead of an outcome someone can observe.
21
+ - A decomposition item the size of the story (Tests, Docs) with no named cases.
22
+ - Contract numbers and tables quoted from memory. Open the covering spec or ADR and copy them.
23
+ - A non-trivial "how" with neither a `specs:` entry nor a `no_spec` reason.
24
+ - Technical Notes left empty while the risks are known. Put the trap there, not only in the chat.
25
+
26
+ ## Do not write
27
+
28
+ - The Implementation Plan or the Final Summary at creation: the gates write them.
29
+ - A spec's contracts restated in full: cite them by number.
30
+ - A criterion that cannot fail.
31
+
32
+ ## Length
33
+
34
+ Description one to three short paragraphs; five to nine decomposition items; four to ten criteria; the plan a thin route of at most 40 lines.
@@ -68,7 +68,8 @@ import {
68
68
  SUBCOMMANDS as BAND_SUBCOMMANDS,
69
69
  } from "./band.mjs";
70
70
  import { dispatch, envelope, usageFail, ownEnv, nearest, notBoundText, PACKAGE_ROOT } from "./verbs.mjs";
71
- import { READ_HANDLERS } from "./read-verbs.mjs";
71
+ import { READ_HANDLERS, guideVault } from "./read-verbs.mjs";
72
+ import { checkGuideName, planEject, applyEject } from "./guides.mjs";
72
73
 
73
74
  // What this module exported before the table moved, from where it lives now.
74
75
  export {
@@ -640,8 +641,61 @@ async function runInstallVerb({ row, values, project, env, cwd, stdin, stdout, a
640
641
  // Every verb's handler, keyed by verb: the read half from read-verbs.mjs,
641
642
  // the rest here. Exported so a test can wrap one (setup's sandboxed home) in
642
643
  // a table of its own.
644
+ // guide — the read is the read runner's; `guide eject <kind>` is the one
645
+ // write (the authoring-guides ADR, decision 5): it copies the package guide
646
+ // into <vault>/guides/ with based_on, create-only, and confirms as scaffold
647
+ // does — a terminal asks, --write without one is the confirmation.
648
+ async function runGuideVerb(ctx) {
649
+ const { row, values, positionals, cfg, project, env, stdin, stdout, stderr, ask } = ctx;
650
+ if (positionals[0] !== "eject") return READ_HANDLERS.guide(ctx);
651
+ const usage = (m) => usageFail(Object.assign(new Error(m), { code: "USAGE" }), { verb: row.verb, ...ctx });
652
+ const name = positionals[1];
653
+ if (!name) return usage("guide eject takes a kind, such as `guide eject spec`");
654
+ if (values.section || values.source) return usage("--section and --source belong to the read, not to eject");
655
+ try {
656
+ checkGuideName(name);
657
+ } catch (e) {
658
+ return usageFail(e, { verb: row.verb, ...ctx });
659
+ }
660
+ const emit = (ok, result, text, code) => {
661
+ if (values.json) stdout.write(JSON.stringify(envelope(row.verb, project, ok, result), null, 2) + "\n");
662
+ else (ok ? stdout : stderr).write(text);
663
+ return code;
664
+ };
665
+ const plan = planEject(name, { vault: guideVault(cfg, project), root: PACKAGE_ROOT });
666
+ const result = (written) => ({ name, target: plan.target, based_on: plan.based_on ?? null, written });
667
+ if (!plan.ok) return emit(false, { ...result(false), error: plan.refusal }, `guide eject: ${plan.refusal}\n`, 1);
668
+ const preview = [
669
+ `guide eject ${name} — creates ${plan.target} (based_on: ${plan.based_on}):`,
670
+ "",
671
+ plan.content.replace(/\n*$/, ""),
672
+ "",
673
+ ].join("\n");
674
+ if (!values.write)
675
+ return emit(
676
+ true,
677
+ result(false),
678
+ `${preview}\nNothing written. To create it, ${fromTerminalIn(project)}:\n${packageCommand([], "guide", { args: `eject ${name} --write` })}\n`,
679
+ 0,
680
+ );
681
+ const interactive =
682
+ !values.json &&
683
+ (ask ? true : (await import("./install-harness.mjs")).isInteractive({ stdin, stdout, env, json: false }));
684
+ if (interactive) {
685
+ stdout.write(preview + "\n");
686
+ if (!(await term.askApply(1, { stdin, stdout, ask, caps: term.caps(stdout, env) }))) {
687
+ stdout.write("Nothing written.\n");
688
+ return 1;
689
+ }
690
+ }
691
+ const done = applyEject(plan);
692
+ if (!done.written) return emit(false, { ...result(false), error: done.refusal }, `guide eject: ${done.refusal}\n`, 1);
693
+ return emit(true, result(true), `Created ${plan.target}.\n`, 0);
694
+ }
695
+
643
696
  export const HANDLERS = Object.freeze({
644
697
  ...READ_HANDLERS,
698
+ guide: runGuideVerb,
645
699
  setup: runSetupVerb,
646
700
  configure: runConfigureVerb,
647
701
  reconcile: runReconcile,
@@ -104,7 +104,8 @@ const KEY = Object.freeze({
104
104
  lifecycle_gates: {
105
105
  text: () => "Whether doctor checks a story's plan and close sections.",
106
106
  values: {
107
- on: () => "doctor checks a story's plan when it starts, and its final summary and evidence when it closes.",
107
+ on: () =>
108
+ "doctor checks a story's plan when it starts, and its final summary and evidence when it closes. Stories closed after now are checked.",
108
109
  off: () => "The plan and close sections are still written; doctor does not check them.",
109
110
  },
110
111
  },
@@ -53,6 +53,7 @@ const CLEAR = "-";
53
53
  const LANGUAGE_NOTE =
54
54
  "new artifacts, the derived views on their next reconcile, the plan and close sections of stories still in flight, the session header and the status line take the new language; existing artifacts and the vault's READMEs are untouched";
55
55
  const STAMP_NOTE = "stories closed before now are exempt from the spec and lifecycle gates";
56
+ const GATES_NOTE = "stories closed after now are checked by the lifecycle gates";
56
57
 
57
58
  const parseBool = (v) => {
58
59
  const s = String(v).trim().toLowerCase();
@@ -466,7 +467,11 @@ export function planChanges(f, changes, { env = process.env, home = homedir(), r
466
467
  const patch = Object.fromEntries(policy.map((c) => [c.key, c.value]));
467
468
  const next = nextVaultConfig(strict.cfg, patch, new Date().toISOString());
468
469
  const inEffect = (k) => strict.cfg[k] ?? (k === "spec_policy" ? "optional" : undefined);
469
- const changed = policy.filter((c) => inEffect(c.key) !== c.value);
470
+ // `lifecycle_gates=on` on a vault already on but with no gates_since is a
471
+ // change: it stamps the date the gates count from (the configure spec,
472
+ // contract 7 as amended 2026-11-08).
473
+ const stampsGates = (c) => c.key === "lifecycle_gates" && c.value === "on" && !strict.cfg.gates_since;
474
+ const changed = policy.filter((c) => inEffect(c.key) !== c.value || stampsGates(c));
470
475
  for (const c of changed)
471
476
  items.push({
472
477
  key: c.key,
@@ -474,7 +479,12 @@ export function planChanges(f, changes, { env = process.env, home = homedir(), r
474
479
  before: shown(strict.cfg[c.key] ?? (c.key === "spec_policy" ? "optional" : "not set")),
475
480
  after: shown(c.value),
476
481
  team: true,
477
- note: c.key === "spec_policy" && next.spec_policy_since !== strict.cfg.spec_policy_since ? STAMP_NOTE : null,
482
+ note:
483
+ c.key === "spec_policy" && next.spec_policy_since !== strict.cfg.spec_policy_since
484
+ ? STAMP_NOTE
485
+ : c.key === "lifecycle_gates" && next.gates_since !== strict.cfg.gates_since
486
+ ? GATES_NOTE
487
+ : null,
478
488
  });
479
489
  if (changed.length) steps.push({ writer: "vault", patch, keys: changed.map((c) => c.key) });
480
490
  }