@entelligentsia/forgecli 0.10.1 → 0.11.2

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 (161) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +21 -3
  3. package/dist/CHANGELOG-forge-plugin.md +22 -0
  4. package/dist/extensions/forgecli/add-pipeline.d.ts +19 -0
  5. package/dist/extensions/forgecli/add-pipeline.js +143 -0
  6. package/dist/extensions/forgecli/add-pipeline.js.map +1 -0
  7. package/dist/extensions/forgecli/add-task.d.ts +20 -0
  8. package/dist/extensions/forgecli/add-task.js +154 -0
  9. package/dist/extensions/forgecli/add-task.js.map +1 -0
  10. package/dist/extensions/forgecli/calibrate.d.ts +61 -0
  11. package/dist/extensions/forgecli/calibrate.js +488 -0
  12. package/dist/extensions/forgecli/calibrate.js.map +1 -0
  13. package/dist/extensions/forgecli/fix-bug.d.ts +9 -1
  14. package/dist/extensions/forgecli/fix-bug.js +70 -8
  15. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  16. package/dist/extensions/forgecli/forge-commands.js +15 -22
  17. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  18. package/dist/extensions/forgecli/forge-subagent.js +34 -7
  19. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  20. package/dist/extensions/forgecli/forge-update-command.d.ts +9 -0
  21. package/dist/extensions/forgecli/forge-update-command.js +106 -7
  22. package/dist/extensions/forgecli/forge-update-command.js.map +1 -1
  23. package/dist/extensions/forgecli/health-check.d.ts +22 -1
  24. package/dist/extensions/forgecli/health-check.js +177 -4
  25. package/dist/extensions/forgecli/health-check.js.map +1 -1
  26. package/dist/extensions/forgecli/hook-dispatcher.d.ts +25 -1
  27. package/dist/extensions/forgecli/hook-dispatcher.js +104 -9
  28. package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
  29. package/dist/extensions/forgecli/hooks/check-update.d.ts +81 -0
  30. package/dist/extensions/forgecli/hooks/check-update.js +308 -0
  31. package/dist/extensions/forgecli/hooks/check-update.js.map +1 -0
  32. package/dist/extensions/forgecli/hooks/forge-permissions.d.ts +32 -0
  33. package/dist/extensions/forgecli/hooks/forge-permissions.js +119 -0
  34. package/dist/extensions/forgecli/hooks/forge-permissions.js.map +1 -0
  35. package/dist/extensions/forgecli/hooks/triage-error.d.ts +23 -0
  36. package/dist/extensions/forgecli/hooks/triage-error.js +62 -0
  37. package/dist/extensions/forgecli/hooks/triage-error.js.map +1 -0
  38. package/dist/extensions/forgecli/hooks/write-guard.d.ts +28 -0
  39. package/dist/extensions/forgecli/hooks/write-guard.js +225 -0
  40. package/dist/extensions/forgecli/hooks/write-guard.js.map +1 -0
  41. package/dist/extensions/forgecli/index.js +60 -0
  42. package/dist/extensions/forgecli/index.js.map +1 -1
  43. package/dist/extensions/forgecli/init-context.d.ts +1 -1
  44. package/dist/extensions/forgecli/init-context.js +21 -6
  45. package/dist/extensions/forgecli/init-context.js.map +1 -1
  46. package/dist/extensions/forgecli/materialize.d.ts +16 -0
  47. package/dist/extensions/forgecli/materialize.js +195 -0
  48. package/dist/extensions/forgecli/materialize.js.map +1 -0
  49. package/dist/extensions/forgecli/migrate.d.ts +19 -0
  50. package/dist/extensions/forgecli/migrate.js +258 -0
  51. package/dist/extensions/forgecli/migrate.js.map +1 -0
  52. package/dist/extensions/forgecli/migration-engine.d.ts +111 -0
  53. package/dist/extensions/forgecli/migration-engine.js +533 -0
  54. package/dist/extensions/forgecli/migration-engine.js.map +1 -0
  55. package/dist/extensions/forgecli/quiz-agent.d.ts +17 -0
  56. package/dist/extensions/forgecli/quiz-agent.js +98 -0
  57. package/dist/extensions/forgecli/quiz-agent.js.map +1 -0
  58. package/dist/extensions/forgecli/remove-command.d.ts +17 -0
  59. package/dist/extensions/forgecli/remove-command.js +124 -0
  60. package/dist/extensions/forgecli/remove-command.js.map +1 -0
  61. package/dist/extensions/forgecli/report-bug.d.ts +25 -0
  62. package/dist/extensions/forgecli/report-bug.js +159 -0
  63. package/dist/extensions/forgecli/report-bug.js.map +1 -0
  64. package/dist/extensions/forgecli/retrospective.d.ts +19 -0
  65. package/dist/extensions/forgecli/retrospective.js +156 -0
  66. package/dist/extensions/forgecli/retrospective.js.map +1 -0
  67. package/dist/extensions/forgecli/run-sprint.js +34 -0
  68. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  69. package/dist/extensions/forgecli/run-task.d.ts +9 -1
  70. package/dist/extensions/forgecli/run-task.js +64 -10
  71. package/dist/extensions/forgecli/run-task.js.map +1 -1
  72. package/dist/extensions/forgecli/session-registry.d.ts +27 -2
  73. package/dist/extensions/forgecli/session-registry.js +52 -1
  74. package/dist/extensions/forgecli/session-registry.js.map +1 -1
  75. package/dist/extensions/forgecli/status-command.d.ts +19 -0
  76. package/dist/extensions/forgecli/status-command.js +140 -0
  77. package/dist/extensions/forgecli/status-command.js.map +1 -0
  78. package/dist/extensions/forgecli/store-query.d.ts +22 -0
  79. package/dist/extensions/forgecli/store-query.js +107 -0
  80. package/dist/extensions/forgecli/store-query.js.map +1 -0
  81. package/dist/extensions/forgecli/store-repair.d.ts +17 -0
  82. package/dist/extensions/forgecli/store-repair.js +123 -0
  83. package/dist/extensions/forgecli/store-repair.js.map +1 -0
  84. package/dist/extensions/forgecli/thread-switcher.js +213 -28
  85. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  86. package/dist/extensions/forgecli/update-tools.d.ts +23 -0
  87. package/dist/extensions/forgecli/update-tools.js +136 -0
  88. package/dist/extensions/forgecli/update-tools.js.map +1 -0
  89. package/dist/extensions/forgecli/viewport-theme.js +4 -0
  90. package/dist/extensions/forgecli/viewport-theme.js.map +1 -1
  91. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  92. package/dist/forge-payload/.schemas/config.schema.json +83 -0
  93. package/dist/forge-payload/.schemas/migrations.json +2049 -0
  94. package/dist/forge-payload/commands/regenerate.md +17 -1
  95. package/dist/forge-payload/meta/personas/README.md +16 -0
  96. package/dist/forge-payload/meta/personas/meta-architect.md +70 -0
  97. package/dist/forge-payload/meta/personas/meta-bug-fixer.md +73 -0
  98. package/dist/forge-payload/meta/personas/meta-collator.md +72 -0
  99. package/dist/forge-payload/meta/personas/meta-engineer.md +70 -0
  100. package/dist/forge-payload/meta/personas/meta-orchestrator.md +71 -0
  101. package/dist/forge-payload/meta/personas/meta-product-manager.md +82 -0
  102. package/dist/forge-payload/meta/personas/meta-qa-engineer.md +91 -0
  103. package/dist/forge-payload/meta/personas/meta-supervisor.md +92 -0
  104. package/dist/forge-payload/meta/skill-recommendations.md +154 -0
  105. package/dist/forge-payload/meta/skills/meta-architect-skills.md +43 -0
  106. package/dist/forge-payload/meta/skills/meta-bug-fixer-skills.md +43 -0
  107. package/dist/forge-payload/meta/skills/meta-collator-skills.md +41 -0
  108. package/dist/forge-payload/meta/skills/meta-engineer-skills.md +43 -0
  109. package/dist/forge-payload/meta/skills/meta-generic-skills.md +58 -0
  110. package/dist/forge-payload/meta/skills/meta-qa-engineer-skills.md +46 -0
  111. package/dist/forge-payload/meta/skills/meta-supervisor-skills.md +43 -0
  112. package/dist/forge-payload/meta/store-schema/bug.schema.md +71 -0
  113. package/dist/forge-payload/meta/store-schema/event.schema.md +76 -0
  114. package/dist/forge-payload/meta/store-schema/feature.schema.md +65 -0
  115. package/dist/forge-payload/meta/store-schema/sprint.schema.md +64 -0
  116. package/dist/forge-payload/meta/store-schema/task.schema.md +78 -0
  117. package/dist/forge-payload/meta/templates/meta-code-review.md +26 -0
  118. package/dist/forge-payload/meta/templates/meta-plan-review.md +28 -0
  119. package/dist/forge-payload/meta/templates/meta-plan.md +28 -0
  120. package/dist/forge-payload/meta/templates/meta-progress.md +25 -0
  121. package/dist/forge-payload/meta/templates/meta-retrospective.md +28 -0
  122. package/dist/forge-payload/meta/templates/meta-sprint-manifest.md +26 -0
  123. package/dist/forge-payload/meta/templates/meta-sprint-requirements.md +91 -0
  124. package/dist/forge-payload/meta/templates/meta-task-prompt.md +26 -0
  125. package/dist/forge-payload/meta/tool-specs/collate.spec.md +88 -0
  126. package/dist/forge-payload/meta/tool-specs/generation-manifest.spec.md +139 -0
  127. package/dist/forge-payload/meta/tool-specs/manage-config.spec.md +143 -0
  128. package/dist/forge-payload/meta/tool-specs/seed-store.spec.md +91 -0
  129. package/dist/forge-payload/meta/tool-specs/store-cli.spec.md +328 -0
  130. package/dist/forge-payload/meta/tool-specs/validate-store.spec.md +191 -0
  131. package/dist/forge-payload/meta/workflows/_fragments/context-injection.md +75 -0
  132. package/dist/forge-payload/meta/workflows/_fragments/event-emission-schema.md +73 -0
  133. package/dist/forge-payload/meta/workflows/_fragments/finalize.md +13 -0
  134. package/dist/forge-payload/meta/workflows/_fragments/friction-emit.md +73 -0
  135. package/dist/forge-payload/meta/workflows/_fragments/progress-reporting.md +38 -0
  136. package/dist/forge-payload/meta/workflows/_fragments/store-cli-verbs.md +39 -0
  137. package/dist/forge-payload/meta/workflows/meta-approve.md +119 -0
  138. package/dist/forge-payload/meta/workflows/meta-collate.md +89 -0
  139. package/dist/forge-payload/meta/workflows/meta-commit.md +93 -0
  140. package/dist/forge-payload/meta/workflows/meta-enhance.md +286 -0
  141. package/dist/forge-payload/meta/workflows/meta-fix-bug.md +501 -0
  142. package/dist/forge-payload/meta/workflows/meta-implement.md +132 -0
  143. package/dist/forge-payload/meta/workflows/meta-migrate.md +455 -0
  144. package/dist/forge-payload/meta/workflows/meta-orchestrate.md +993 -0
  145. package/dist/forge-payload/meta/workflows/meta-plan-task.md +133 -0
  146. package/dist/forge-payload/meta/workflows/meta-quiz-agent.md +135 -0
  147. package/dist/forge-payload/meta/workflows/meta-retrospective.md +65 -0
  148. package/dist/forge-payload/meta/workflows/meta-review-implementation.md +119 -0
  149. package/dist/forge-payload/meta/workflows/meta-review-plan.md +108 -0
  150. package/dist/forge-payload/meta/workflows/meta-review-sprint-completion.md +65 -0
  151. package/dist/forge-payload/meta/workflows/meta-sprint-intake.md +76 -0
  152. package/dist/forge-payload/meta/workflows/meta-sprint-plan.md +147 -0
  153. package/dist/forge-payload/meta/workflows/meta-update-implementation.md +76 -0
  154. package/dist/forge-payload/meta/workflows/meta-update-plan.md +76 -0
  155. package/dist/forge-payload/meta/workflows/meta-validate.md +111 -0
  156. package/dist/forge-payload/tools/check-structure.cjs +344 -0
  157. package/dist/forge-payload/tools/list-skills.js +76 -0
  158. package/dist/forge-payload/tools/store-cli.cjs +27 -1
  159. package/dist/forge-payload/tools/substitute-placeholders.cjs +60 -8
  160. package/dist/forge-payload/tools/verify-integrity.cjs +86 -0
  161. package/package.json +2 -2
@@ -0,0 +1,139 @@
1
+ # Tool Spec: generation-manifest
2
+
3
+ ## Purpose
4
+
5
+ Track and verify the integrity of Forge-generated files via content hashes.
6
+ Distinguishes between files that are pristine (unchanged since generation) and
7
+ files that have been manually modified. Used by regeneration, migration, and
8
+ health checks to avoid silently overwriting user edits.
9
+
10
+ ## Manifest file
11
+
12
+ Stored at `.forge/generation-manifest.json`. Committed to version control so
13
+ the whole team shares the same baseline. Structure:
14
+
15
+ ```json
16
+ {
17
+ "files": {
18
+ ".forge/workflows/plan_task.md": {
19
+ "hash": "sha256:a3f9...",
20
+ "generatedAt": "2026-04-06T10:00:00.000Z",
21
+ "generatedByVersion": "0.5.0"
22
+ }
23
+ }
24
+ }
25
+ ```
26
+
27
+ ## Inputs
28
+
29
+ - `.forge/generation-manifest.json` — the manifest file (read/write)
30
+ - CLI arguments — subcommand and target path
31
+
32
+ ## Outputs
33
+
34
+ - Modified `.forge/generation-manifest.json` in-place (write subcommands)
35
+ - Printed status or tables to stdout (read subcommands)
36
+ - Exit codes (see per-subcommand below)
37
+
38
+ ## CLI Interface
39
+
40
+ ```
41
+ generation-manifest record <path>
42
+ ```
43
+ Hash the file at `<path>` and store/update its entry in the manifest.
44
+ Records `hash`, `generatedAt` (ISO timestamp), and `generatedByVersion`
45
+ (read from `.forge/config.json → version`, or `"unknown"`).
46
+ Exit 0 on success.
47
+
48
+ ```
49
+ generation-manifest record-all
50
+ ```
51
+ Re-hash every file currently listed in the manifest. Skips missing files
52
+ with a `△ Missing` note. Prints a summary line on completion.
53
+ Exit 0 always (missing files are not an error).
54
+
55
+ ```
56
+ generation-manifest check <path>
57
+ ```
58
+ Compare the current content hash of `<path>` against the stored hash.
59
+
60
+ | Exit code | Meaning |
61
+ |-----------|---------|
62
+ | 0 | Pristine — content matches the stored hash |
63
+ | 1 | Modified — content has changed since it was recorded |
64
+ | 2 | Untracked — file is not in the manifest |
65
+ | 3 | File not found on disk |
66
+
67
+ Output one status line to stdout regardless of exit code.
68
+
69
+ ```
70
+ generation-manifest list [--modified]
71
+ ```
72
+ Print a markdown pipe table of all tracked files with columns
73
+ `Status | File | Version | Date`.
74
+ With `--modified`: only show modified and missing files.
75
+ If all files are pristine, print `〇 All tracked files are pristine.`
76
+
77
+ ```
78
+ generation-manifest status
79
+ ```
80
+ Print a compact summary: total tracked count, and counts per status
81
+ (pristine / modified / missing). No table.
82
+
83
+ ```
84
+ generation-manifest remove <path>
85
+ ```
86
+ Remove a file from the manifest without touching the file itself.
87
+ Exit 1 if the file is not tracked.
88
+
89
+ ## Hash algorithm
90
+
91
+ SHA-256 of normalised content:
92
+ 1. Normalise line endings to LF (`\r\n` → `\n`)
93
+ 2. Strip trailing whitespace from each line
94
+ 3. Hash the resulting UTF-8 string
95
+ 4. Prefix the hex digest with `"sha256:"`
96
+
97
+ This prevents false positives from editor-inserted trailing spaces or
98
+ CRLF/LF differences across platforms.
99
+
100
+ ## What to track
101
+
102
+ Record hashes for files that Forge generates and might overwrite on
103
+ regeneration, but that users may legitimately customise:
104
+
105
+ | Category | Pattern |
106
+ |----------|---------|
107
+ | Workflows | `.forge/workflows/*.md` |
108
+ | Templates | `.forge/templates/*.md` |
109
+ | Generated commands | `.claude/commands/{forge-generated}.md` |
110
+
111
+ Do NOT track:
112
+ - Knowledge base files (`engineering/architecture/`, `stack-checklist.md`) — expected to evolve via writeback
113
+ - Collated views (`MASTER_INDEX.md`, `TIMESHEET.md`) — rebuilt from the store
114
+ - Store JSON files (`.forge/store/`) — data, not generated artifacts
115
+
116
+ ## Status symbols
117
+
118
+ | Symbol | Meaning |
119
+ |--------|---------|
120
+ | 〇 | Pristine — matches stored hash |
121
+ | △ | Modified — diverged from stored hash |
122
+ | × | Missing — file not found on disk |
123
+
124
+ ## Error handling
125
+
126
+ - Wrap entry point in `process.on('uncaughtException')` — all errors print to
127
+ stderr prefixed with `×` and exit 1. No unhandled exceptions or stack traces.
128
+ - If `.forge/generation-manifest.json` is missing, treat it as an empty manifest
129
+ (no files tracked) — do not error.
130
+ - If `.forge/generation-manifest.json` contains invalid JSON, exit 1 with a
131
+ parse error message.
132
+ - Write atomically: temp file + rename. Never leave a partial write.
133
+
134
+ ## Formatting rules
135
+
136
+ - `list` output: markdown pipe table, status column shows `{symbol} {status}`
137
+ - `check` output: one line to stdout: `{symbol} {relpath}: {status-description}`
138
+ - `status` output: one line per non-zero count, prefixed with symbol
139
+ - Success messages prefixed with `〇`; warnings with `△`; errors with `×`
@@ -0,0 +1,143 @@
1
+ # Tool Spec: manage-config
2
+
3
+ ## Purpose
4
+
5
+ Read and write `.forge/config.json` safely. Deterministic — no AI needed.
6
+ Preserves key order, indentation, and all fields not explicitly modified.
7
+
8
+ ## Inputs
9
+
10
+ - `.forge/config.json` — the config file to read or modify
11
+ - CLI arguments — subcommand and options (see CLI Interface)
12
+
13
+ ## Outputs
14
+
15
+ - Modified `.forge/config.json` in-place (write subcommands)
16
+ - Printed values or reports to stdout (read subcommands)
17
+ - Exit 0 on success, 1 on validation error, 2 on usage error
18
+
19
+ ## CLI Interface
20
+
21
+ ### Read subcommands
22
+
23
+ ```
24
+ <tool> manage-config get <key.path>
25
+ ```
26
+ Print the value at the dot-notation path (e.g., `project.prefix`,
27
+ `pipelines.measure-conversion`). Exit 1 if the path does not exist.
28
+
29
+ ```
30
+ <tool> manage-config list-pipelines
31
+ ```
32
+ Print a table of all pipeline names, their descriptions, and phase counts.
33
+ If no `pipelines` key exists, print `── No pipelines configured.` and exit 0.
34
+
35
+ ```
36
+ <tool> manage-config pipeline get <name>
37
+ ```
38
+ Print the full detail of a named pipeline as a markdown phase table with
39
+ columns `# | Role | Command | Workflow | Model | maxIter`.
40
+ Exit 1 with `× Pipeline '{name}' not found` if the name does not exist.
41
+
42
+ ### Write subcommands
43
+
44
+ ```
45
+ <tool> manage-config pipeline add <name> --description <text> --phases <json>
46
+ ```
47
+ Add or replace a named pipeline. `--phases` accepts a JSON array string of
48
+ phase objects, each with `command` and `role` (required), and optionally
49
+ `model`, `maxIterations`, `on_revision`, and `workflow`.
50
+
51
+ ```
52
+ <tool> manage-config pipeline remove <name>
53
+ ```
54
+ Remove a named pipeline. Exit 1 if the name does not exist.
55
+
56
+ ```
57
+ <tool> manage-config set <key.path> <json-value>
58
+ ```
59
+ Set an arbitrary dot-notation path to a JSON value. Creates intermediate
60
+ objects as needed. Intended for first-party Forge tooling only — not
61
+ advertised in user-facing help.
62
+
63
+ ## Validation Rules
64
+
65
+ Applied before any write. Exit 1 and print a clear error if any rule fails.
66
+
67
+ ### Pipeline phases
68
+ - Each phase must have `command` (non-empty string) and `role` (string).
69
+ - `role` must be one of: `plan`, `review-plan`, `implement`, `review-code`,
70
+ `approve`, `commit`.
71
+ - `maxIterations` must be a positive integer when present.
72
+ - `workflow` (optional string) — path to a custom workflow file. When present,
73
+ the orchestrator invokes this file directly instead of the built-in workflow
74
+ for the command name. Used for custom phase commands in `engineering/commands/`.
75
+ - At least one phase is required per pipeline.
76
+
77
+ ### Pipeline name
78
+ - Must be a non-empty string containing only `[a-z0-9_-]`.
79
+ - The name `default` is valid and overrides the hardcoded default pipeline.
80
+
81
+ ### File integrity
82
+ - If `.forge/config.json` is not valid JSON, exit 1 with a parse error.
83
+ Never overwrite a corrupt file.
84
+
85
+ ## Algorithm
86
+
87
+ ### Read path (`get`)
88
+ 1. Read and parse `.forge/config.json`.
89
+ 2. Traverse the dot-notation path.
90
+ 3. Print the value as JSON (pretty-printed if object/array, bare if scalar).
91
+
92
+ ### Write path (`pipeline add`)
93
+ 1. Read and parse `.forge/config.json`. Fail fast on parse error.
94
+ 2. Run validation rules against the incoming phases. Fail fast on error.
95
+ 3. Ensure `config.pipelines` key exists (create empty object if absent).
96
+ 4. Merge the new pipeline entry under `config.pipelines[name]`.
97
+ 5. Serialise back to JSON with the same indentation as the source file
98
+ (detect indent from the first indented line; default to 2 spaces).
99
+ 6. Write atomically: write to a temp file alongside `config.json`, then
100
+ rename over the original. Never leave a partial write.
101
+
102
+ ### Write path (`pipeline remove`)
103
+ 1. Read and parse `.forge/config.json`.
104
+ 2. Check `config.pipelines[name]` exists; exit 1 if not.
105
+ 3. Delete the key.
106
+ 4. If `config.pipelines` is now empty, remove the `pipelines` key entirely.
107
+ 5. Serialise and write atomically (same indent detection as above).
108
+
109
+ ## Error Handling
110
+
111
+ - Wrap the entire entry point in a top-level exception handler.
112
+ - On unexpected errors (file I/O failures, JSON parse errors, unhandled
113
+ exceptions), print a clear one-line message to stderr and exit 1.
114
+ - Never let the tool crash with an unhandled exception or stack trace visible
115
+ to the caller — all errors are caught and reported cleanly.
116
+ - Python pattern:
117
+ ```python
118
+ if __name__ == "__main__":
119
+ try:
120
+ sys.exit(main())
121
+ except Exception as e:
122
+ print(f"Error: {e}", file=sys.stderr)
123
+ sys.exit(1)
124
+ ```
125
+ - JS/TS pattern:
126
+ ```js
127
+ process.on('uncaughtException', (e) => {
128
+ process.stderr.write(`Error: ${e.message}\n`);
129
+ process.exit(1);
130
+ });
131
+ ```
132
+
133
+ ## Formatting Rules
134
+
135
+ - Preserve the original indentation of the file.
136
+ - Do not reorder top-level keys.
137
+ - Do not add or remove trailing newlines beyond what was present.
138
+ - `list-pipelines` output: markdown pipe table with columns
139
+ `Name | Description | Phases`. Print `(none)` when description is absent.
140
+ - `pipeline get` output: optional description line prefixed with `──`, then
141
+ a markdown pipe table with columns `# | Role | Command | Workflow | Model | maxIter`.
142
+ Print `(built-in)` when `workflow` is absent; `—` when `maxIterations` is absent.
143
+ - Success messages prefixed with `〇`; errors prefixed with `×`; neutral info with `──`.
@@ -0,0 +1,91 @@
1
+ # Tool Spec: seed-store
2
+
3
+ ## Purpose
4
+
5
+ Bootstrap the JSON store from an existing `engineering/` directory structure.
6
+ Used when a project already has sprint/task artifacts but no JSON store.
7
+
8
+ ## Inputs
9
+
10
+ - `.forge/config.json` — project prefix, paths
11
+ - `engineering/sprints/` — existing sprint directories
12
+ - `engineering/bugs/` — existing bug directories
13
+
14
+ ## Outputs
15
+
16
+ - `.forge/store/sprints/*.json` — one per discovered sprint
17
+ - `.forge/store/tasks/*.json` — one per discovered task
18
+ - `.forge/store/bugs/*.json` — one per discovered bug
19
+
20
+ ## CLI Interface
21
+
22
+ ```
23
+ <tool> seed-store # scan and create
24
+ <tool> seed-store --dry-run # preview what would be created
25
+ ```
26
+
27
+ Exit 0 on success, 1 on error.
28
+
29
+ ## Output Field Schema
30
+
31
+ All JSON files use **camelCase** field names to match `validate-store`.
32
+
33
+ **Sprint** (`.forge/store/sprints/<ID>.json`):
34
+ ```json
35
+ { "sprintId": "PREFIX-S01", "title": "...", "status": "active" }
36
+ ```
37
+
38
+ **Task** (`.forge/store/tasks/<ID>.json`):
39
+ ```json
40
+ { "taskId": "PREFIX-S01-1", "sprintId": "PREFIX-S01", "title": "...", "status": "planned" }
41
+ ```
42
+
43
+ **Bug** (`.forge/store/bugs/<ID>.json`):
44
+ ```json
45
+ { "bugId": "PREFIX-B01", "title": "...", "severity": "medium", "status": "reported" }
46
+ ```
47
+
48
+ Status defaults: sprint → `"active"`, task → `"planned"`, bug → `"reported"`.
49
+ Infer a better status from artifact presence when possible (e.g. PROGRESS.md
50
+ with "committed" → `"committed"`).
51
+
52
+ ## Error Handling
53
+
54
+ - Wrap the entire entry point in a top-level exception handler.
55
+ - On unexpected errors (missing config, unreadable directories, unhandled
56
+ exceptions), print a clear one-line message to stderr and exit 1.
57
+ - Never let the tool crash with an unhandled exception or stack trace visible
58
+ to the caller — all errors are caught and reported cleanly.
59
+ - Python pattern:
60
+ ```python
61
+ if __name__ == "__main__":
62
+ try:
63
+ sys.exit(main())
64
+ except Exception as e:
65
+ print(f"Error: {e}", file=sys.stderr)
66
+ sys.exit(1)
67
+ ```
68
+ - JS/TS pattern:
69
+ ```js
70
+ process.on('uncaughtException', (e) => {
71
+ process.stderr.write(`Error: ${e.message}\n`);
72
+ process.exit(1);
73
+ });
74
+ ```
75
+
76
+ ## Algorithm
77
+
78
+ 1. Read `.forge/config.json` for prefix and paths
79
+ 2. Scan `engineering/sprints/` for sprint directories (pattern: S{NN})
80
+ 3. For each sprint directory:
81
+ a. Create sprint JSON using the camelCase schema above
82
+ b. Scan for task directories (pattern: T{NN})
83
+ c. For each task directory:
84
+ - Read PLAN.md, PROGRESS.md if they exist
85
+ - Extract title, status from artifact content
86
+ - Create task JSON using the camelCase schema above
87
+ 4. Scan `engineering/bugs/` for bug directories
88
+ 5. For each bug directory:
89
+ a. Read available artifacts
90
+ b. Create bug JSON using the camelCase schema above
91
+ 6. Report: N sprints, N tasks, N bugs seeded
@@ -0,0 +1,328 @@
1
+ # Tool Spec: store-cli
2
+
3
+ ## Purpose
4
+
5
+ Deterministic store custodian CLI — the sole authorized gateway for the
6
+ probabilistic layer to read and write the JSON store at `.forge/store/`.
7
+ Wraps `store.cjs` facade, enforces schema validation on every write, and
8
+ enforces status transition rules on `update-status`. Deterministic — no AI
9
+ needed.
10
+
11
+ ## Inputs
12
+
13
+ - `.forge/config.json` — paths (specifically `paths.forgeRoot` for schema resolution)
14
+ - `.forge/schemas/*.schema.json` — canonical JSON Schema files (primary source)
15
+ - `forge/schemas/*.schema.json` — in-tree source schemas (fallback for dogfooding)
16
+ - CLI arguments — command, entity type, JSON payload, flags
17
+
18
+ ## Outputs
19
+
20
+ - Entity records written to `.forge/store/` (via `store.cjs` facade)
21
+ - Event records written to `.forge/store/events/{sprintId}/`
22
+ - Sidecar files written to `.forge/store/events/{sprintId}/_{eventId}_usage.json`
23
+ - `COLLATION_STATE.json` written to `.forge/store/`
24
+ - JSON results to stdout on success
25
+ - Per-field error messages to stderr on failure
26
+ - Exit 0 on success, 1 on failure
27
+
28
+ ## CLI Interface
29
+
30
+ ```
31
+ <tool> store-cli write <entity> '<json>' Write a full entity record
32
+ <tool> store-cli read <entity> <id> [--json] Read an entity record
33
+ <tool> store-cli list <entity> [key=value ...] List entities with optional filter
34
+ <tool> store-cli delete <entity> <id> Delete an entity record
35
+ <tool> store-cli update-status <entity> <id> <field> <value> [--force]
36
+ Update status/enum field with transition check
37
+ <tool> store-cli emit <sprintId> '<json>' [--sidecar] Write an event (or sidecar)
38
+ <tool> store-cli merge-sidecar <sprintId> <eventId> Merge sidecar into canonical event
39
+ <tool> store-cli purge-events <sprintId> Delete all events for a sprint
40
+ <tool> store-cli write-collation-state '<json>' Write COLLATION_STATE.json
41
+ <tool> store-cli validate <entity> '<json>' Validate against schema without writing
42
+ <tool> store-cli nlp '<intent>' Query store by natural language intent (NLP)
43
+ <tool> store-cli query [--sprint|--task|--bug|--feature <id>] [--status <s>]
44
+ [--keyword <term>] [--type <entity>] [flags]
45
+ Query store by exact flags or intent
46
+ <tool> store-cli query --mode strict|nlp|off [flags] Explicit mode control (strict=exact flags only)
47
+ <tool> store-cli schema Dump entity schemas, status enums, NLP grammar
48
+ ```
49
+
50
+ Entity types: `sprint`, `task`, `bug`, `event`, `feature`
51
+
52
+ Flags:
53
+ - `--dry-run` — validate and preview without writing (applies to all write commands)
54
+ - `--force` — bypass transition check on `update-status` (emits warning)
55
+ - `--json` — output raw JSON on `read` (no pretty-print)
56
+ - `--sidecar` — write as sidecar file on `emit` (ephemeral, `_`-prefixed)
57
+ - `--sprint <id>` — filter query by sprint ID (e.g. `S12`)
58
+ - `--task <id>` — query a specific task by ID
59
+ - `--bug <id>` — query a specific bug by ID
60
+ - `--feature <id>` — query a specific feature by ID
61
+ - `--status <value>` — filter query by status value
62
+ - `--keyword <term>` — keyword search on entity titles
63
+ - `--type <entity>` — restrict `--keyword` to `sprints|tasks|bugs|features`
64
+ - `--with-blockers` — follow `blockedBy` FK on tasks
65
+ - `--with-blocked-tasks` — follow `blocksTask` FK on bugs
66
+ - `--with-sprint` — follow `sprintId` FK on results
67
+ - `--with-feature` — follow `featureId` FK on results
68
+ - `--no-excerpts` — omit INDEX.md excerpts from results
69
+ - `--mode strict|nlp|off` — engine mode (`strict`/`off` = exact flags only; `nlp` = intent parse)
70
+
71
+ Exit codes: 0 on success, 1 on failure.
72
+
73
+ ## Entity Types
74
+
75
+ | Entity | ID Field | Required Fields (minimal) | Store Path |
76
+ |--------|----------|---------------------------|------------|
77
+ | sprint | `sprintId` | `sprintId`, `title`, `status`, `taskIds`, `createdAt` | `.forge/store/sprints/{sprintId}.json` |
78
+ | task | `taskId` | `taskId`, `sprintId`, `title`, `status`, `path` | `.forge/store/tasks/{taskId}.json` |
79
+ | bug | `bugId` | `bugId`, `title`, `severity`, `status`, `path`, `reportedAt` | `.forge/store/bugs/{bugId}.json` |
80
+ | event | `eventId` | `eventId`, `taskId`, `sprintId`, `role`, `action`, `phase`, `iteration`, `startTimestamp`, `endTimestamp`, `durationMinutes`, `model` | `.forge/store/events/{sprintId}/{eventId}.json` |
81
+ | feature | `id` | `id`, `title`, `status`, `created_at` | `.forge/store/features/{id}.json` |
82
+
83
+ Note: `feature` uses `id` (not `feature_id`) as its primary key. The
84
+ `feature_id` field on sprint/task is a foreign key pointing to `feature.id`.
85
+
86
+ Nullable fields (accepted as `null` without error): `sprintId`, `taskId`,
87
+ `endTimestamp`, `durationMinutes`, `feature_id`, `description`, `completedAt`,
88
+ `resolvedAt`.
89
+
90
+ ## Schema Validation
91
+
92
+ The CLI validates every write payload against the canonical JSON Schema before
93
+ writing. Schemas are loaded from:
94
+
95
+ 1. `.forge/schemas/{type}.schema.json` (project-installed, primary)
96
+ 2. `forge/schemas/{type}.schema.json` (in-tree source, for dogfooding)
97
+ 3. Minimal built-in fallback (required fields only, with stderr warning)
98
+
99
+ Validation rules:
100
+ - All `required` fields must be present and non-null (except nullable fields)
101
+ - Field types must match schema declarations (including multi-type)
102
+ - Enum values must be in the declared set
103
+ - `additionalProperties: false` — reject records with undeclared fields
104
+ - `minimum` constraints enforced for numeric fields
105
+
106
+ On validation failure: exit 1, one error per line on stderr (prefixed with
107
+ field name), no partial write.
108
+
109
+ ## Status Transitions
110
+
111
+ The `update-status` command enforces legal state transitions. Illegal
112
+ transitions are rejected (exit 1 with `"Illegal transition: ..."` on stderr).
113
+ Use `--force` to bypass (emits warning).
114
+
115
+ ### Task
116
+
117
+ ```
118
+ draft -> planned -> plan-approved -> implementing -> implemented
119
+ -> review-approved -> approved -> committed
120
+
121
+ Failed states (enterable from any non-terminal state):
122
+ plan-revision-required, code-revision-required, blocked, escalated, abandoned
123
+
124
+ Terminal states (no transitions out):
125
+ committed, abandoned
126
+ ```
127
+
128
+ ### Sprint
129
+
130
+ ```
131
+ planning -> active -> completed -> retrospective-done
132
+
133
+ Failed states:
134
+ blocked, partially-completed, abandoned
135
+
136
+ Terminal states:
137
+ retrospective-done, abandoned
138
+ ```
139
+
140
+ ### Bug
141
+
142
+ ```
143
+ reported -> triaged -> in-progress -> fixed
144
+
145
+ Terminal state:
146
+ fixed
147
+ ```
148
+
149
+ The architect-approve verdict for bugs travels through
150
+ `bug.summaries.approve.verdict` (read by `read-verdict.cjs §
151
+ BUG_PHASE_VERDICT_SOURCE`), not `bug.status`. Earlier revisions of this
152
+ spec listed `approved` and `verified` between `fixed` and the terminal;
153
+ they were removed because no workflow phase wrote them and their presence
154
+ in the enum invited LLM-translated task workflows to attempt
155
+ `update-status bug ... approved` — the trap that produced FORGE-BUG-002.
156
+
157
+ ### Feature
158
+
159
+ ```
160
+ draft -> active -> shipped / retired
161
+
162
+ Terminal states:
163
+ shipped, retired
164
+ ```
165
+
166
+ ## Sidecar Pattern
167
+
168
+ Events support a sidecar mechanism for passing out-of-band data from subagents
169
+ back to the orchestrator.
170
+
171
+ ### `emit --sidecar`
172
+
173
+ Writes a `_{eventId}_usage.json` ephemeral file alongside the canonical event.
174
+ Sidecar files require only an `eventId` field at minimum. Accepted fields:
175
+
176
+ | Field | Notes |
177
+ |-------|-------|
178
+ | `eventId` | Required — matches the canonical event |
179
+ | `inputTokens` | Token count |
180
+ | `outputTokens` | Token count |
181
+ | `cacheReadTokens` | Cache hit tokens |
182
+ | `cacheWriteTokens` | Cache miss tokens (alias: `cacheCreationTokens`) |
183
+ | `estimatedCostUSD` | Cost estimate (alias: `cost`) |
184
+ | `model` | Full model identifier |
185
+ | `durationMinutes` | Decimal minutes |
186
+ | `startTimestamp` | ISO 8601 |
187
+ | `endTimestamp` | ISO 8601 |
188
+ | `tokenSource` | `reported` or `estimated` |
189
+
190
+ Alias mapping at merge time: `cacheCreationTokens` -> `cacheWriteTokens`,
191
+ `cost` -> `estimatedCostUSD`.
192
+
193
+ ### `merge-sidecar`
194
+
195
+ Reads the sidecar, merges token fields into the canonical event via
196
+ `store.writeEvent()`, and deletes the sidecar file. Fails if either file is
197
+ missing.
198
+
199
+ ## Error Handling
200
+
201
+ - Wrap the entire entry point in a top-level exception handler.
202
+ - On unexpected errors (missing store files, bad JSON, unhandled exceptions),
203
+ print a clear one-line message to stderr and exit 1.
204
+ - Never let the tool crash with an unhandled exception or stack trace visible
205
+ to the caller — all errors are caught and reported cleanly.
206
+ - JS/TS pattern:
207
+ ```js
208
+ process.on('uncaughtException', (e) => {
209
+ process.stderr.write(`Error: ${e.message}\n`);
210
+ process.exit(1);
211
+ });
212
+ ```
213
+
214
+ ## Algorithm
215
+
216
+ ### `write`
217
+ 1. Parse entity type and JSON payload from CLI args.
218
+ 2. Validate entity type is one of: sprint, task, bug, event, feature.
219
+ 3. Parse JSON payload (exit 1 on parse error).
220
+ 4. Validate payload against schema for the entity type.
221
+ 5. If `--dry-run`, report what would be written and exit 0.
222
+ 6. Write entity via `store.write{Entity}()` facade.
223
+
224
+ ### `read`
225
+ 1. Parse entity type and ID from CLI args.
226
+ 2. For events, scan sprint directories to locate the event file.
227
+ 3. Output the record (pretty-print or `--json` raw).
228
+ 4. Exit 1 if entity not found.
229
+
230
+ ### `list`
231
+ 1. Parse entity type and optional key=value filter pairs.
232
+ 2. List entities via `store.list{Entities}()` with filter.
233
+ 3. Output JSON array.
234
+
235
+ ### `update-status`
236
+ 1. Read the current record via `store.get{Entity}()`.
237
+ 2. Check that `current[field] -> new` is a legal transition.
238
+ 3. If `--force`, bypass with warning on stderr.
239
+ 4. Apply the update and write back via `store.write{Entity}()`.
240
+ 5. Output JSON result with `from`/`to` fields.
241
+
242
+ ### `emit`
243
+ 1. Parse sprintId and JSON payload.
244
+ 2. If `--sidecar`: write `_{eventId}_usage.json` file directly.
245
+ 3. If canonical: validate against event schema, write via `store.writeEvent()`.
246
+ 4. Output JSON result.
247
+
248
+ ### `merge-sidecar`
249
+ 1. Read sidecar file. Read canonical event file.
250
+ 2. Merge token fields from sidecar into event (with alias resolution).
251
+ 3. Write updated event via `store.writeEvent()`.
252
+ 4. Delete sidecar file.
253
+
254
+ ### `validate`
255
+ 1. Parse entity type and JSON payload.
256
+ 2. Validate against schema (same logic as `write`).
257
+ 3. Exit 1 on errors (no write), exit 0 with `{"ok":true,"valid":true}`.
258
+
259
+ ### `nlp '<intent>'`
260
+ 1. Spawn `store-query.cjs nlp` with the intent string.
261
+ 2. Output JSON to stdout: `{query, path, traversalTrace, results, relatedFileRefs, meta}`.
262
+ 3. `results[]` contains: `{id, title, status, type, relationships, fileRefs, excerpt}`.
263
+
264
+ ### `query [flags|intent]`
265
+ 1. If exact entity flags present (`--sprint/--task/--bug/--feature`), run exact-args path.
266
+ 2. If `--keyword` present, run keyword search path.
267
+ 3. If intent string present (no flags), run NLP path.
268
+ 4. `--mode strict|off` rejects intent strings; `--mode nlp` forces NLP path.
269
+ 5. Returns same JSON structure as `nlp`.
270
+
271
+ ### `schema`
272
+ 1. Load project config (prefix, paths).
273
+ 2. Return entity schemas, status/severity enums, FK relationships, and NLP grammar vocabulary.
274
+ 3. Output: `{project, entities, entitySynonyms, statusSynonyms, grammar}`.
275
+
276
+ ## Query Output Schema
277
+
278
+ ```json
279
+ {
280
+ "query": "<input string>",
281
+ "path": "exact | keyword | intent-nlp",
282
+ "traversalTrace": ["<step descriptions>"],
283
+ "results": [
284
+ {
285
+ "id": "<entity ID>",
286
+ "title": "<entity title>",
287
+ "status": "<current status>",
288
+ "type": "task | bug | sprint | feature",
289
+ "relationships": {
290
+ "sprintId": "<sprint ID>",
291
+ "featureId": "<feature ID>",
292
+ "blockedBy": ["<bug IDs>"],
293
+ "blocksTask": ["<task IDs>"]
294
+ },
295
+ "fileRefs": {
296
+ "json": "<store JSON path>",
297
+ "md": "<INDEX.md path>"
298
+ },
299
+ "storeRef": "<store JSON path>",
300
+ "indexRef": "<INDEX.md path>",
301
+ "excerpt": "<first 4 sentences from INDEX.md, or null>"
302
+ }
303
+ ],
304
+ "relatedFileRefs": ["<all md and json paths>"],
305
+ "totalMatched": 7,
306
+ "returned": 3,
307
+ "limit": 3,
308
+ "sort": "desc",
309
+ "meta": {
310
+ "mode": "auto | strict | nlp | off",
311
+ "engineVersion": "1.0.0",
312
+ "totalTimeMs": 42
313
+ }
314
+ }
315
+ ```
316
+
317
+ Count mode response (when intent contains `how many`, `count of`, etc.):
318
+
319
+ ```json
320
+ {
321
+ "query": "how many open bugs",
322
+ "path": "intent-nlp",
323
+ "traversalTrace": ["..."],
324
+ "count": 7,
325
+ "results": [],
326
+ "totalMatched": 7
327
+ }
328
+ ```