@seanmars/tospec 0.13.0 → 0.14.0

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 (100) hide show
  1. package/CHANGELOG.md +233 -0
  2. package/README.md +20 -7
  3. package/assets/dashboard/app.js +6 -0
  4. package/assets/dashboard/style.css +34 -2
  5. package/dist/cli/index.js +1 -1
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/commands/config.d.ts.map +1 -1
  8. package/dist/commands/config.js +21 -7
  9. package/dist/commands/config.js.map +1 -1
  10. package/dist/commands/dashboard.d.ts.map +1 -1
  11. package/dist/commands/dashboard.js +8 -4
  12. package/dist/commands/dashboard.js.map +1 -1
  13. package/dist/commands/validate.d.ts.map +1 -1
  14. package/dist/commands/validate.js +44 -3
  15. package/dist/commands/validate.js.map +1 -1
  16. package/dist/commands/workflow/instructions.d.ts.map +1 -1
  17. package/dist/commands/workflow/instructions.js +1 -1
  18. package/dist/commands/workflow/instructions.js.map +1 -1
  19. package/dist/core/archive.d.ts +3 -3
  20. package/dist/core/archive.d.ts.map +1 -1
  21. package/dist/core/archive.js +41 -14
  22. package/dist/core/archive.js.map +1 -1
  23. package/dist/core/artifact-graph/instruction-loader.d.ts.map +1 -1
  24. package/dist/core/artifact-graph/instruction-loader.js +7 -4
  25. package/dist/core/artifact-graph/instruction-loader.js.map +1 -1
  26. package/dist/core/artifact-graph/resolver.d.ts +21 -1
  27. package/dist/core/artifact-graph/resolver.d.ts.map +1 -1
  28. package/dist/core/artifact-graph/resolver.js +35 -3
  29. package/dist/core/artifact-graph/resolver.js.map +1 -1
  30. package/dist/core/artifact-graph/schema.d.ts.map +1 -1
  31. package/dist/core/artifact-graph/schema.js +22 -0
  32. package/dist/core/artifact-graph/schema.js.map +1 -1
  33. package/dist/core/change-metadata/schema.d.ts +1 -0
  34. package/dist/core/change-metadata/schema.d.ts.map +1 -1
  35. package/dist/core/change-metadata/schema.js +7 -0
  36. package/dist/core/change-metadata/schema.js.map +1 -1
  37. package/dist/core/change-presenter.js +1 -1
  38. package/dist/core/change-presenter.js.map +1 -1
  39. package/dist/core/dashboard-data.d.ts +15 -9
  40. package/dist/core/dashboard-data.d.ts.map +1 -1
  41. package/dist/core/dashboard-data.js +20 -12
  42. package/dist/core/dashboard-data.js.map +1 -1
  43. package/dist/core/global-config.d.ts +59 -16
  44. package/dist/core/global-config.d.ts.map +1 -1
  45. package/dist/core/global-config.js +99 -41
  46. package/dist/core/global-config.js.map +1 -1
  47. package/dist/core/init.d.ts.map +1 -1
  48. package/dist/core/init.js +5 -1
  49. package/dist/core/init.js.map +1 -1
  50. package/dist/core/project-config.d.ts +12 -7
  51. package/dist/core/project-config.d.ts.map +1 -1
  52. package/dist/core/project-config.js +13 -8
  53. package/dist/core/project-config.js.map +1 -1
  54. package/dist/core/shared/skill-generation.d.ts +4 -4
  55. package/dist/core/shared/skill-generation.d.ts.map +1 -1
  56. package/dist/core/shared/skill-generation.js +2 -2
  57. package/dist/core/shared/skill-generation.js.map +1 -1
  58. package/dist/core/shared/tool-detection.d.ts +9 -3
  59. package/dist/core/shared/tool-detection.d.ts.map +1 -1
  60. package/dist/core/shared/tool-detection.js +9 -3
  61. package/dist/core/shared/tool-detection.js.map +1 -1
  62. package/dist/core/templates/skill-templates.d.ts +1 -1
  63. package/dist/core/templates/skill-templates.d.ts.map +1 -1
  64. package/dist/core/templates/skill-templates.js +1 -1
  65. package/dist/core/templates/skill-templates.js.map +1 -1
  66. package/dist/core/templates/workflows/archive.d.ts.map +1 -1
  67. package/dist/core/templates/workflows/archive.js +36 -22
  68. package/dist/core/templates/workflows/archive.js.map +1 -1
  69. package/dist/core/templates/workflows/propose.js +2 -2
  70. package/dist/core/templates/workflows/sync.d.ts +15 -0
  71. package/dist/core/templates/workflows/sync.d.ts.map +1 -0
  72. package/dist/core/templates/workflows/sync.js +104 -0
  73. package/dist/core/templates/workflows/sync.js.map +1 -0
  74. package/dist/core/templates/workflows/update.js +1 -1
  75. package/dist/core/update.d.ts.map +1 -1
  76. package/dist/core/update.js +4 -0
  77. package/dist/core/update.js.map +1 -1
  78. package/dist/core/user-state-migration.d.ts +58 -0
  79. package/dist/core/user-state-migration.d.ts.map +1 -0
  80. package/dist/core/user-state-migration.js +107 -0
  81. package/dist/core/user-state-migration.js.map +1 -0
  82. package/dist/core/validation/validator.d.ts +0 -7
  83. package/dist/core/validation/validator.d.ts.map +1 -1
  84. package/dist/core/validation/validator.js +50 -41
  85. package/dist/core/validation/validator.js.map +1 -1
  86. package/dist/utils/change-metadata.d.ts +18 -0
  87. package/dist/utils/change-metadata.d.ts.map +1 -1
  88. package/dist/utils/change-metadata.js +39 -0
  89. package/dist/utils/change-metadata.js.map +1 -1
  90. package/dist/utils/spec-files.d.ts +19 -0
  91. package/dist/utils/spec-files.d.ts.map +1 -0
  92. package/dist/utils/spec-files.js +44 -0
  93. package/dist/utils/spec-files.js.map +1 -0
  94. package/package.json +11 -2
  95. package/schemas/issue/schema.yaml +6 -3
  96. package/schemas/sdd/schema.yaml +15 -2
  97. package/dist/core/templates/workflows/reconcile.d.ts +0 -14
  98. package/dist/core/templates/workflows/reconcile.d.ts.map +0 -1
  99. package/dist/core/templates/workflows/reconcile.js +0 -94
  100. package/dist/core/templates/workflows/reconcile.js.map +0 -1
@@ -0,0 +1,44 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ /**
4
+ * Every `spec.md` under a change's `specs/` directory, at any depth.
5
+ *
6
+ * Deliberately recursive even though the supported layout is exactly
7
+ * `specs/<capability>/spec.md`: the merge path reads one level, so anything
8
+ * deeper is a file that would be applied by nobody. Finding it is what lets the
9
+ * validator report it and archive refuse — the original defect was that the
10
+ * merge path and the discovery path each answered "is there a delta spec here"
11
+ * on their own, and disagreed silently.
12
+ *
13
+ * Shared so that answer has exactly one definition. See
14
+ * tospec/decisions/20260730_014309-delta-spec-layout-one-level.md
15
+ *
16
+ * Returns absolute paths, sorted by code point for deterministic issue ordering
17
+ * (not localeCompare — that follows the process ICU locale and would order
18
+ * differently on CI than locally).
19
+ */
20
+ export async function findSpecFiles(specsDir) {
21
+ const results = [];
22
+ const walk = async (dir) => {
23
+ let entries;
24
+ try {
25
+ entries = await fs.readdir(dir, { withFileTypes: true });
26
+ }
27
+ catch {
28
+ // A missing or unreadable specs/ means "no delta specs" to every caller.
29
+ return;
30
+ }
31
+ for (const entry of entries) {
32
+ const full = path.join(dir, entry.name);
33
+ if (entry.isDirectory()) {
34
+ await walk(full);
35
+ }
36
+ else if (entry.isFile() && entry.name === 'spec.md') {
37
+ results.push(full);
38
+ }
39
+ }
40
+ };
41
+ await walk(specsDir);
42
+ return results.sort();
43
+ }
44
+ //# sourceMappingURL=spec-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-files.js","sourceRoot":"","sources":["../../src/utils/spec-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB;IAClD,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,MAAM,IAAI,GAAG,KAAK,EAAE,GAAW,EAAiB,EAAE;QAChD,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;YACzE,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;iBAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBACtD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;AACxB,CAAC"}
package/package.json CHANGED
@@ -1,8 +1,16 @@
1
1
  {
2
2
  "name": "@seanmars/tospec",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Spec-driven development CLI for structured requirements and issue workflows",
5
5
  "license": "MIT",
6
+ "homepage": "https://github.com/seanmars/tospec#readme",
7
+ "bugs": {
8
+ "url": "https://github.com/seanmars/tospec/issues"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/seanmars/tospec.git"
13
+ },
6
14
  "publishConfig": {
7
15
  "access": "public"
8
16
  },
@@ -26,7 +34,8 @@
26
34
  "dist",
27
35
  "bin",
28
36
  "schemas",
29
- "assets"
37
+ "assets",
38
+ "CHANGELOG.md"
30
39
  ],
31
40
  "engines": {
32
41
  "node": ">=22.22.0"
@@ -19,15 +19,18 @@ artifacts:
19
19
  validation:
20
20
  requiredSections: [Feedback Loop, Root Cause, Fix Plan, Test Plan, Tasks]
21
21
  - id: specs
22
- generates: specs/**/*.md
22
+ # Exactly one directory level — same constraint as sdd. See
23
+ # tospec/decisions/20260730_014309-delta-spec-layout-one-level.md
24
+ generates: specs/*/spec.md
23
25
  description: Optional behavior-changing delta specifications
24
26
  template: spec.md
25
27
  requires: []
26
28
  optional: true
27
29
  instruction: |
28
30
  Write delta specs only when the fix changes externally observable behavior
29
- (same grammar as sdd). If the bug stems from a wrong spec, fix that
30
- Requirement with MODIFIED.
31
+ (same grammar as sdd, same layout: specs/<capability>/spec.md, exactly one
32
+ directory level). If the bug stems from a wrong spec, fix that Requirement
33
+ with MODIFIED.
31
34
  apply:
32
35
  requires: [task]
33
36
  tracks: task.md
@@ -15,17 +15,25 @@ artifacts:
15
15
  add/modify/remove), Capabilities (New/Modified capabilities and their specs),
16
16
  Impact (affected code/APIs/dependencies/systems). This is what specs and
17
17
  design expand from; never write file paths or code snippets (they go stale).
18
+ If the change has no externally observable effect at all (tooling, CI,
19
+ docs), say so in Impact and set `skip_specs: true` in the change's
20
+ .tospec.yaml — see the specs artifact below.
18
21
  validation:
19
22
  requiredSections: [Why, What Changes, Impact]
20
23
  minSectionLength:
21
24
  Why: 50
22
25
  - id: specs
23
- generates: specs/**/*.md
26
+ # Exactly one directory level. A deeper path (specs/<area>/<capability>/spec.md)
27
+ # is rejected by the validator: archive's merge only reads one level, so a nested
28
+ # file would validate clean and then vanish on archive. See
29
+ # tospec/decisions/20260730_014309-delta-spec-layout-one-level.md
30
+ generates: specs/*/spec.md
24
31
  description: Delta specifications for affected capabilities
25
32
  template: spec.md
26
33
  requires: [proposal]
27
34
  instruction: |
28
- Write delta specs from the proposal. One file per capability.
35
+ Write delta specs from the proposal. One file per capability, at
36
+ specs/<capability>/spec.md — exactly one directory level, never nested.
29
37
  Grammar: ## ADDED|MODIFIED|REMOVED|RENAMED Requirements /
30
38
  ### Requirement: <name> (the body must contain SHALL or MUST) /
31
39
  #### Scenario: <name> (four #, WHEN/THEN/AND bullets).
@@ -34,6 +42,11 @@ artifacts:
34
42
  capability exists); archive carries it into the newly created main spec. Do
35
43
  not add Purpose to an existing capability's delta (edit the main spec's
36
44
  Purpose directly instead).
45
+ A change with genuinely no behavioral effect (infrastructure, tooling,
46
+ doc-only) writes no delta and declares `skip_specs: true` in its
47
+ .tospec.yaml instead. Use it only when that is actually true — never
48
+ invent a Requirement to get past validation, and never set the marker on a
49
+ change that does have delta specs (validation rejects that contradiction).
37
50
  - id: design
38
51
  generates: design.md
39
52
  description: Technical design and testing seams
@@ -1,14 +0,0 @@
1
- /**
2
- * Skill Template Workflow Modules
3
- *
4
- * This file is generated by splitting the legacy monolithic
5
- * templates file into workflow-focused modules.
6
- *
7
- * reconcile is a standalone skill again. The archive workflow proactively
8
- * decides whether a reconcile is warranted and asks the user before archiving;
9
- * when the user agrees, this is the procedure that runs.
10
- */
11
- import type { SkillTemplate, CommandTemplate } from '../types.js';
12
- export declare function getTospecReconcileSkillTemplate(): SkillTemplate;
13
- export declare function getTospecReconcileCommandTemplate(): CommandTemplate;
14
- //# sourceMappingURL=reconcile.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../../../src/core/templates/workflows/reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAoElE,wBAAgB,+BAA+B,IAAI,aAAa,CAc/D;AAED,wBAAgB,iCAAiC,IAAI,eAAe,CAYnE"}
@@ -1,94 +0,0 @@
1
- const RECONCILE_BODY = `Whether a spec still matches the source code is a semantic judgment no CLI can make — that's why this is agent work, not a \`tospec\` subcommand. The CLI's job is the part that IS machine-checkable: the reconciled spec must still pass full validation, and \`tospec archive --require-reconcile\` refuses unless \`reconcile-report.md\` reports PASS.
2
-
3
- **Steps**
4
-
5
- 1. **List the Requirements this change touches**
6
-
7
- For sdd: every Requirement in \`specs/<capability>/spec.md\` under the change directory. For issue: the Requirement(s) referenced in the ticket/\`task.md\`, plus any delta specs the change produced.
8
-
9
- **No-delta case**: an issue fix with no behavior change has no delta specs at all. Don't invent Requirements to check — instead confirm \`task.md\`'s described fix matches what the code actually does, and note this as the no-delta case in the report (see format below).
10
-
11
- 2. **Compare each Requirement against the actual implementation**
12
-
13
- For each Requirement, locate the corresponding code (the files apply touched are a good starting point) and judge: does the implemented behavior match what the Requirement's scenarios describe?
14
-
15
- **Can't find any corresponding implementation at all**: that's a CODE-BUG, same severity as a behavioral mismatch — the Requirement was agreed to and never built. Don't wave it through as MATCH.
16
-
17
- 3. **Sweep the other direction — behavior nobody asked for**
18
-
19
- Step 2 walks Requirements to code, so it can only find what is *missing*. It cannot see the opposite: behavior in the diff that **maps to no Requirement** at all. Walk the change's diff once and flag anything the specs never asked for.
20
-
21
- **Report it, never spec it.** "Code wins" settles how a Requirement is *worded*, not whether behavior *belongs* — it is not a licence to make unrequested work look agreed after the fact. Never add a Requirement to cover scope creep: tell the user what you found and let them decide to keep it, drop it, or plan it properly.
22
-
23
- 4. **Resolve every mismatch — code wins**
24
-
25
- - If the spec is stale relative to the code (the code is correct, the spec described the old behavior): update the delta spec's Requirement with **MODIFIED**, pasting the complete, corrected Requirement block. Then:
26
- \`\`\`bash
27
- tospec validate "<name>" --json
28
- \`\`\`
29
- Must pass before moving to the next Requirement — record the validate result in that Requirement's report entry.
30
- - If the code itself is wrong (it doesn't do what was actually agreed in ticket/design, or is missing entirely): **stop**. Do not touch the spec to paper over a bug. Report the mismatch to the user and suggest returning to \`tospec-apply\` to fix the code before archiving.
31
-
32
- 5. **Write the reconcile report**
33
-
34
- Create \`reconcile-report.md\` in the change directory in exactly this format — \`tospec archive --require-reconcile\` parses the first \`Conclusion:\` line, so the heading and label text must match:
35
-
36
- \`\`\`markdown
37
- # Reconcile Report: <change-name>
38
- ## Summary
39
- Conclusion: <PASS or FAIL>
40
- ## Requirements
41
- ### <Requirement name>
42
- - Implementation: <module/function description, not a hard path>
43
- - Verdict: MATCH | SPEC-UPDATED (updated to match code) | CODE-BUG (stops archiving)
44
- - Notes: <the difference and how it was handled; SPEC-UPDATED must include the validate result>
45
- \`\`\`
46
-
47
- Fill the \`Conclusion:\` line with exactly one literal — \`Conclusion: PASS\` or \`Conclusion: FAIL\` (FAIL may append a \`(reason)\` note); never leave both options in one line, or \`tospec archive --require-reconcile\` can't parse it. \`Conclusion:\` is **FAIL** if even one Requirement is CODE-BUG — a single unresolved defect blocks the whole change, not just that Requirement. It's **PASS** only when every Requirement resolved to MATCH or SPEC-UPDATED (with validate passing). For the no-delta case, still emit the Summary and a single Requirements entry describing the task.md-vs-code comparison (module described, not a Requirement name).
48
-
49
- 6. **Final artifact validation**
50
- \`\`\`bash
51
- tospec validate "<name>" --json
52
- \`\`\`
53
- Even when no spec needed updating, run the full change validation before finishing. If it reports an artifact problem, fix the affected artifact and re-run until it passes. Do not write \`Conclusion: PASS\` while validation is failing.
54
-
55
- **Output**
56
-
57
- Summarize how many Requirements matched, how many specs were updated, whether anything was flagged as a code defect, and anything found that no Requirement asked for. If \`Conclusion: PASS\`, prompt: "Run \`tospec-archive\` to finish up — it passes \`--require-reconcile\` for free now that \`reconcile-report.md\` reports PASS." If \`Conclusion: FAIL\`, prompt to return to \`tospec-apply\` first — do not proceed to archive.
58
-
59
- **Guardrails**
60
- - Code is the source of truth for behavior — spec updates always follow code, never the other way around
61
- - Scope creep is reported, never specced — "code wins" settles a Requirement's wording, never whether unrequested behavior belongs
62
- - Never edit source code from this workflow — a code-side mismatch is diagnosis, not a license to patch here
63
- - Every MODIFIED delta must re-pass \`tospec validate\` before moving to the next Requirement
64
- - \`reconcile-report.md\` is required output, in the exact format above — \`tospec archive --require-reconcile\` depends on both its existence and its \`Conclusion:\` line
65
- - One comparison pass per Requirement — check it against the code once and record the verdict. \`reconcile-report.md\` is the deliverable, not another review target: don't re-audit the report after writing it, and don't re-check a Requirement already judged MATCH`;
66
- export function getTospecReconcileSkillTemplate() {
67
- return {
68
- name: 'tospec-reconcile',
69
- description: 'Reconcile a change\'s Requirements against the actual implementation — code wins on any mismatch — and write reconcile-report.md. Use before archiving a change whose specs may have drifted from the code.',
70
- instructions: `Reconcile \`specs/\` against the implementation for every Requirement this change touched — code wins on any mismatch. Run this before \`tospec-archive\` when the change's specs may have drifted from the code.
71
-
72
- **Input**: The user's request should name the change to reconcile.
73
-
74
- ${RECONCILE_BODY}`,
75
- license: 'MIT',
76
- compatibility: 'Requires tospec CLI.',
77
- metadata: { author: 'tospec' },
78
- allowedTools: ['Bash(tospec:*)', 'Read', 'Write', 'Edit', 'Grep', 'Glob'],
79
- };
80
- }
81
- export function getTospecReconcileCommandTemplate() {
82
- return {
83
- name: 'TOSPEC: Reconcile',
84
- description: 'Reconcile specs against the implementation, code as source of truth',
85
- category: 'Workflow',
86
- tags: ['workflow', 'reconcile', 'archive-gate'],
87
- content: `Reconcile \`specs/\` against the implementation for every Requirement this change touched — code wins on any mismatch. Run this before \`tospec-archive\` when the change's specs may have drifted from the code.
88
-
89
- **Input**: The argument after \`tospec-reconcile\` is the change name to reconcile.
90
-
91
- ${RECONCILE_BODY}`,
92
- };
93
- }
94
- //# sourceMappingURL=reconcile.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../../../src/core/templates/workflows/reconcile.ts"],"names":[],"mappings":"AAYA,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wQAgEiP,CAAC;AAEzQ,MAAM,UAAU,+BAA+B;IAC7C,OAAO;QACL,IAAI,EAAE,kBAAkB;QACxB,WAAW,EAAE,6MAA6M;QAC1N,YAAY,EAAE;;;;EAIhB,cAAc,EAAE;QACd,OAAO,EAAE,KAAK;QACd,aAAa,EAAE,sBAAsB;QACrC,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;QAC9B,YAAY,EAAE,CAAC,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;KAC1E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iCAAiC;IAC/C,OAAO;QACL,IAAI,EAAE,mBAAmB;QACzB,WAAW,EAAE,qEAAqE;QAClF,QAAQ,EAAE,UAAU;QACpB,IAAI,EAAE,CAAC,UAAU,EAAE,WAAW,EAAE,cAAc,CAAC;QAC/C,OAAO,EAAE;;;;EAIX,cAAc,EAAE;KACf,CAAC;AACJ,CAAC"}