@entelligentsia/forgecli 0.1.0 → 0.3.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 (144) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/README.md +177 -38
  3. package/dist/bin/argv.js +5 -0
  4. package/dist/bin/argv.js.map +1 -1
  5. package/dist/bin/forge.js +1 -0
  6. package/dist/bin/forge.js.map +1 -1
  7. package/dist/extensions/forgecli/ask-user-tool.d.ts +17 -0
  8. package/dist/extensions/forgecli/ask-user-tool.js +139 -0
  9. package/dist/extensions/forgecli/ask-user-tool.js.map +1 -0
  10. package/dist/extensions/forgecli/forge-commands.d.ts +21 -0
  11. package/dist/extensions/forgecli/forge-commands.js +141 -0
  12. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  13. package/dist/extensions/forgecli/forge-init.d.ts +26 -0
  14. package/dist/extensions/forgecli/forge-init.js +948 -0
  15. package/dist/extensions/forgecli/forge-init.js.map +1 -0
  16. package/dist/extensions/forgecli/health-check.d.ts +18 -0
  17. package/dist/extensions/forgecli/health-check.js +154 -0
  18. package/dist/extensions/forgecli/health-check.js.map +1 -0
  19. package/dist/extensions/forgecli/hook-dispatcher.d.ts +34 -1
  20. package/dist/extensions/forgecli/hook-dispatcher.js +237 -3
  21. package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
  22. package/dist/extensions/forgecli/index.js +28 -11
  23. package/dist/extensions/forgecli/index.js.map +1 -1
  24. package/dist/extensions/forgecli/init-context.d.ts +99 -0
  25. package/dist/extensions/forgecli/init-context.js +163 -0
  26. package/dist/extensions/forgecli/init-context.js.map +1 -0
  27. package/dist/extensions/forgecli/init-progress.d.ts +39 -0
  28. package/dist/extensions/forgecli/init-progress.js +117 -0
  29. package/dist/extensions/forgecli/init-progress.js.map +1 -0
  30. package/dist/extensions/forgecli/refresh-kb-links.d.ts +18 -0
  31. package/dist/extensions/forgecli/refresh-kb-links.js +228 -0
  32. package/dist/extensions/forgecli/refresh-kb-links.js.map +1 -0
  33. package/dist/extensions/forgecli/store-validator.d.ts +13 -0
  34. package/dist/extensions/forgecli/store-validator.js +35 -0
  35. package/dist/extensions/forgecli/store-validator.js.map +1 -0
  36. package/dist/extensions/forgecli/transition-guard.d.ts +20 -0
  37. package/dist/extensions/forgecli/transition-guard.js +125 -0
  38. package/dist/extensions/forgecli/transition-guard.js.map +1 -0
  39. package/dist/forge-payload/.base-pack/commands/approve.md +22 -0
  40. package/dist/forge-payload/.base-pack/commands/collate.md +22 -0
  41. package/dist/forge-payload/.base-pack/commands/commit.md +22 -0
  42. package/dist/forge-payload/.base-pack/commands/enhance.md +37 -0
  43. package/dist/forge-payload/.base-pack/commands/fix-bug.md +22 -0
  44. package/dist/forge-payload/.base-pack/commands/implement.md +22 -0
  45. package/dist/forge-payload/.base-pack/commands/plan.md +22 -0
  46. package/dist/forge-payload/.base-pack/commands/quiz-agent.md +22 -0
  47. package/dist/forge-payload/.base-pack/commands/retrospective.md +22 -0
  48. package/dist/forge-payload/.base-pack/commands/review-code.md +22 -0
  49. package/dist/forge-payload/.base-pack/commands/review-plan.md +22 -0
  50. package/dist/forge-payload/.base-pack/commands/run-sprint.md +22 -0
  51. package/dist/forge-payload/.base-pack/commands/run-task.md +22 -0
  52. package/dist/forge-payload/.base-pack/commands/sprint-intake.md +22 -0
  53. package/dist/forge-payload/.base-pack/commands/sprint-plan.md +22 -0
  54. package/dist/forge-payload/.base-pack/commands/validate.md +22 -0
  55. package/dist/forge-payload/.claude-plugin/plugin.json +15 -0
  56. package/dist/forge-payload/.init/discovery/discover-database.md +32 -0
  57. package/dist/forge-payload/.init/discovery/discover-processes.md +31 -0
  58. package/dist/forge-payload/.init/discovery/discover-routing.md +31 -0
  59. package/dist/forge-payload/.init/discovery/discover-stack.md +33 -0
  60. package/dist/forge-payload/.init/discovery/discover-testing.md +34 -0
  61. package/dist/forge-payload/.init/generation/generate-kb-doc.md +60 -0
  62. package/dist/forge-payload/.schemas/bug.schema.json +53 -0
  63. package/dist/forge-payload/.schemas/collation-state.schema.json +16 -0
  64. package/dist/forge-payload/.schemas/event-sidecar.schema.json +22 -0
  65. package/dist/forge-payload/.schemas/event.schema.json +32 -0
  66. package/dist/forge-payload/.schemas/feature.schema.json +22 -0
  67. package/dist/forge-payload/.schemas/progress-entry.schema.json +16 -0
  68. package/dist/forge-payload/.schemas/project-context.schema.json +167 -0
  69. package/dist/forge-payload/.schemas/project-overlay.schema.json +25 -0
  70. package/dist/forge-payload/.schemas/sprint.schema.json +27 -0
  71. package/dist/forge-payload/.schemas/structure-versions.schema.json +57 -0
  72. package/dist/forge-payload/.schemas/task.schema.json +58 -0
  73. package/dist/forge-payload/.tools/banners.cjs +435 -0
  74. package/dist/forge-payload/.tools/build-context-pack.cjs +290 -0
  75. package/dist/forge-payload/.tools/build-init-context.cjs +322 -0
  76. package/dist/forge-payload/.tools/build-overlay.cjs +326 -0
  77. package/dist/forge-payload/.tools/build-persona-pack.cjs +226 -0
  78. package/dist/forge-payload/.tools/collate.cjs +1041 -0
  79. package/dist/forge-payload/.tools/generation-manifest.cjs +311 -0
  80. package/dist/forge-payload/.tools/lib/forge-root.cjs +59 -0
  81. package/dist/forge-payload/.tools/lib/paths.cjs +29 -0
  82. package/dist/forge-payload/.tools/lib/pricing.cjs +165 -0
  83. package/dist/forge-payload/.tools/lib/project-root.cjs +32 -0
  84. package/dist/forge-payload/.tools/lib/result.js +40 -0
  85. package/dist/forge-payload/.tools/lib/validate.js +131 -0
  86. package/dist/forge-payload/.tools/manage-config.cjs +340 -0
  87. package/dist/forge-payload/.tools/manage-versions.cjs +365 -0
  88. package/dist/forge-payload/.tools/seed-store.cjs +237 -0
  89. package/dist/forge-payload/.tools/store-cli.cjs +1123 -0
  90. package/dist/forge-payload/.tools/store.cjs +315 -0
  91. package/dist/forge-payload/.tools/substitute-placeholders.cjs +625 -0
  92. package/dist/forge-payload/.tools/validate-store.cjs +522 -0
  93. package/package.json +1 -1
  94. /package/dist/forge-payload/{personas → .base-pack/personas}/architect.md +0 -0
  95. /package/dist/forge-payload/{personas → .base-pack/personas}/bug-fixer.md +0 -0
  96. /package/dist/forge-payload/{personas → .base-pack/personas}/collator.md +0 -0
  97. /package/dist/forge-payload/{personas → .base-pack/personas}/engineer.md +0 -0
  98. /package/dist/forge-payload/{personas → .base-pack/personas}/librarian.md +0 -0
  99. /package/dist/forge-payload/{personas → .base-pack/personas}/orchestrator.md +0 -0
  100. /package/dist/forge-payload/{personas → .base-pack/personas}/product-manager.md +0 -0
  101. /package/dist/forge-payload/{personas → .base-pack/personas}/qa-engineer.md +0 -0
  102. /package/dist/forge-payload/{personas → .base-pack/personas}/supervisor.md +0 -0
  103. /package/dist/forge-payload/{skills → .base-pack/skills}/architect-skills.md +0 -0
  104. /package/dist/forge-payload/{skills → .base-pack/skills}/bug-fixer-skills.md +0 -0
  105. /package/dist/forge-payload/{skills → .base-pack/skills}/collator-skills.md +0 -0
  106. /package/dist/forge-payload/{skills → .base-pack/skills}/engineer-skills.md +0 -0
  107. /package/dist/forge-payload/{skills → .base-pack/skills}/generic-skills.md +0 -0
  108. /package/dist/forge-payload/{skills → .base-pack/skills}/librarian-skills.md +0 -0
  109. /package/dist/forge-payload/{skills → .base-pack/skills}/qa-engineer-skills.md +0 -0
  110. /package/dist/forge-payload/{skills → .base-pack/skills}/store-custodian-skills.md +0 -0
  111. /package/dist/forge-payload/{skills → .base-pack/skills}/supervisor-skills.md +0 -0
  112. /package/dist/forge-payload/{templates → .base-pack/templates}/CODE_REVIEW_TEMPLATE.md +0 -0
  113. /package/dist/forge-payload/{templates → .base-pack/templates}/COST_REPORT_TEMPLATE.md +0 -0
  114. /package/dist/forge-payload/{templates → .base-pack/templates}/PLAN_REVIEW_TEMPLATE.md +0 -0
  115. /package/dist/forge-payload/{templates → .base-pack/templates}/PLAN_SUMMARY_TEMPLATE.json +0 -0
  116. /package/dist/forge-payload/{templates → .base-pack/templates}/PLAN_TEMPLATE.md +0 -0
  117. /package/dist/forge-payload/{templates → .base-pack/templates}/PROGRESS_TEMPLATE.md +0 -0
  118. /package/dist/forge-payload/{templates → .base-pack/templates}/RETROSPECTIVE_TEMPLATE.md +0 -0
  119. /package/dist/forge-payload/{templates → .base-pack/templates}/SPRINT_MANIFEST_TEMPLATE.md +0 -0
  120. /package/dist/forge-payload/{templates → .base-pack/templates}/SPRINT_REQUIREMENTS_TEMPLATE.md +0 -0
  121. /package/dist/forge-payload/{templates → .base-pack/templates}/TASK_PROMPT_TEMPLATE.md +0 -0
  122. /package/dist/forge-payload/{workflows → .base-pack/workflows}/_fragments/context-injection.md +0 -0
  123. /package/dist/forge-payload/{workflows → .base-pack/workflows}/_fragments/event-emission-schema.md +0 -0
  124. /package/dist/forge-payload/{workflows → .base-pack/workflows}/_fragments/finalize.md +0 -0
  125. /package/dist/forge-payload/{workflows → .base-pack/workflows}/_fragments/progress-reporting.md +0 -0
  126. /package/dist/forge-payload/{workflows → .base-pack/workflows}/architect_approve.md +0 -0
  127. /package/dist/forge-payload/{workflows → .base-pack/workflows}/architect_review_sprint_completion.md +0 -0
  128. /package/dist/forge-payload/{workflows → .base-pack/workflows}/architect_sprint_intake.md +0 -0
  129. /package/dist/forge-payload/{workflows → .base-pack/workflows}/architect_sprint_plan.md +0 -0
  130. /package/dist/forge-payload/{workflows → .base-pack/workflows}/collator_agent.md +0 -0
  131. /package/dist/forge-payload/{workflows → .base-pack/workflows}/commit_task.md +0 -0
  132. /package/dist/forge-payload/{workflows → .base-pack/workflows}/fix_bug.md +0 -0
  133. /package/dist/forge-payload/{workflows → .base-pack/workflows}/implement_plan.md +0 -0
  134. /package/dist/forge-payload/{workflows → .base-pack/workflows}/migrate_structural.md +0 -0
  135. /package/dist/forge-payload/{workflows → .base-pack/workflows}/orchestrate_task.md +0 -0
  136. /package/dist/forge-payload/{workflows → .base-pack/workflows}/plan_task.md +0 -0
  137. /package/dist/forge-payload/{workflows → .base-pack/workflows}/quiz_agent.md +0 -0
  138. /package/dist/forge-payload/{workflows → .base-pack/workflows}/review_code.md +0 -0
  139. /package/dist/forge-payload/{workflows → .base-pack/workflows}/review_plan.md +0 -0
  140. /package/dist/forge-payload/{workflows → .base-pack/workflows}/run_sprint.md +0 -0
  141. /package/dist/forge-payload/{workflows → .base-pack/workflows}/sprint_retrospective.md +0 -0
  142. /package/dist/forge-payload/{workflows → .base-pack/workflows}/update_implementation.md +0 -0
  143. /package/dist/forge-payload/{workflows → .base-pack/workflows}/update_plan.md +0 -0
  144. /package/dist/forge-payload/{workflows → .base-pack/workflows}/validate_task.md +0 -0
@@ -0,0 +1,228 @@
1
+ // refresh-kb-links.ts — TypeScript port of forge/forge/skills/refresh-kb-links/SKILL.md
2
+ //
3
+ // Scans coding-agent instruction files (CLAUDE.md, AGENTS.md, etc.) and
4
+ // ensures each has up-to-date managed sections linking to the Forge KB and
5
+ // workflows. Called directly by Phase 4 step 4-9 (Tomoshibi) during
6
+ // /forge:init and registered as the /forge:refresh-kb-links command handler.
7
+ //
8
+ // Per PLAN.md sub-decision #5 (F7 fix): TS port is the only valid implementation;
9
+ // forge/forge/skills/refresh-kb-links/SKILL.md is a markdown body with no
10
+ // runnable .cjs entry point.
11
+ //
12
+ // Iron Law 6: no shell-string interpolation.
13
+ // Iron Law 1: reads .forge/config.json — no writes to forge/ source.
14
+ import * as fs from "node:fs";
15
+ import * as path from "node:path";
16
+ // ── Known agent instruction files ─────────────────────────────────────────
17
+ const AGENT_INSTRUCTION_FILES = [
18
+ "CLAUDE.md",
19
+ "AGENTS.md",
20
+ "AGENT.md",
21
+ "GEMINI.md",
22
+ ".cursorrules",
23
+ ".github/copilot-instructions.md",
24
+ ];
25
+ // ── Known workflow files ───────────────────────────────────────────────────
26
+ const WORKFLOW_ENTRIES = [
27
+ { file: "plan_task.md", label: "Plan", purpose: "Research codebase → implementation plan" },
28
+ { file: "implement_plan.md", label: "Implement", purpose: "Execute approved plan → code changes" },
29
+ { file: "fix_bug.md", label: "Fix bug", purpose: "Triage → fix → verify" },
30
+ {
31
+ file: "orchestrate_task.md",
32
+ label: "Run task",
33
+ purpose: "Full task pipeline (plan → implement → review → commit)",
34
+ },
35
+ { file: "run_sprint.md", label: "Run sprint", purpose: "Full sprint orchestration" },
36
+ { file: "architect_sprint_plan.md", label: "Sprint plan", purpose: "Sprint planning and task decomposition" },
37
+ {
38
+ file: "architect_sprint_intake.md",
39
+ label: "Sprint intake",
40
+ purpose: "Sprint intake and requirements elicitation",
41
+ },
42
+ ];
43
+ // ── Managed section markers ────────────────────────────────────────────────
44
+ const KB_OPEN_PREFIX = "<!-- forge-kb-links";
45
+ const KB_CLOSE = "<!-- /forge-kb-links -->";
46
+ const WF_OPEN_PREFIX = "<!-- forge-workflow-links";
47
+ const WF_CLOSE = "<!-- /forge-workflow-links -->";
48
+ // ── Content generators ─────────────────────────────────────────────────────
49
+ function buildKbSection(cwd, kbPath) {
50
+ const rows = [
51
+ { label: "MASTER_INDEX", file: `${kbPath}/MASTER_INDEX.md`, desc: "All sprints, tasks, bugs, and features" },
52
+ {
53
+ label: "Architecture",
54
+ file: `${kbPath}/architecture/INDEX.md`,
55
+ desc: "Stack, processes, database, routing, deployment",
56
+ },
57
+ {
58
+ label: "Business Domain",
59
+ file: `${kbPath}/business-domain/INDEX.md`,
60
+ desc: "Entity model and domain concepts",
61
+ },
62
+ ].filter((r) => fs.existsSync(path.join(cwd, r.file)));
63
+ if (rows.length === 0)
64
+ return "";
65
+ const tableRows = rows.map((r) => `| [${r.label}](${r.file}) | ${r.desc} |`).join("\n");
66
+ return [
67
+ `<!-- forge-kb-links: managed by Forge — do not edit manually -->`,
68
+ `## Forge Knowledge Base`,
69
+ ``,
70
+ `| Index | Contents |`,
71
+ `|-------|----------|`,
72
+ tableRows,
73
+ ``,
74
+ `Personas live in \`.forge/personas/\`.`,
75
+ KB_CLOSE,
76
+ ].join("\n");
77
+ }
78
+ function buildWorkflowSection(cwd) {
79
+ const rows = WORKFLOW_ENTRIES.filter((e) => fs.existsSync(path.join(cwd, ".forge", "workflows", e.file)));
80
+ if (rows.length === 0)
81
+ return "";
82
+ const tableRows = rows.map((e) => `| [${e.label}](.forge/workflows/${e.file}) | ${e.purpose} |`).join("\n");
83
+ return [
84
+ `<!-- forge-workflow-links: managed by Forge — do not edit manually -->`,
85
+ `## Forge Workflows`,
86
+ ``,
87
+ `| Workflow | Purpose |`,
88
+ `|----------|---------|`,
89
+ tableRows,
90
+ WF_CLOSE,
91
+ ].join("\n");
92
+ }
93
+ // ── Section replacement ────────────────────────────────────────────────────
94
+ /**
95
+ * Replace or append a managed section in a file's content.
96
+ * If the section already exists (detected by open-prefix), replaces it.
97
+ * Otherwise, appends it to the end.
98
+ */
99
+ function upsertSection(content, openPrefix, closeMarker, newSection) {
100
+ if (!newSection)
101
+ return content; // nothing to inject
102
+ // Find existing section
103
+ const openIdx = content.indexOf(openPrefix);
104
+ if (openIdx !== -1) {
105
+ const closeIdx = content.indexOf(closeMarker, openIdx);
106
+ if (closeIdx !== -1) {
107
+ const endIdx = closeIdx + closeMarker.length;
108
+ // Replace existing section
109
+ return content.slice(0, openIdx) + newSection + content.slice(endIdx);
110
+ }
111
+ }
112
+ // Append at end, ensuring a blank line separator
113
+ const trimmed = content.trimEnd();
114
+ return trimmed + "\n\n" + newSection + "\n";
115
+ }
116
+ // ── Main exported function ─────────────────────────────────────────────────
117
+ /**
118
+ * Run the refresh-kb-links operation on the project at `cwd`.
119
+ *
120
+ * Returns a result record with counts and human-readable messages.
121
+ * Never throws — errors are captured as messages.
122
+ */
123
+ export async function runRefreshKbLinks(cwd) {
124
+ const result = {
125
+ filesUpdated: 0,
126
+ filesSkipped: 0,
127
+ messages: [],
128
+ };
129
+ // Read kbPath from .forge/config.json
130
+ let kbPath = "engineering";
131
+ try {
132
+ const configRaw = fs.readFileSync(path.join(cwd, ".forge", "config.json"), "utf8");
133
+ const config = JSON.parse(configRaw);
134
+ const paths = config.paths;
135
+ if (paths && typeof paths.engineering === "string" && paths.engineering) {
136
+ kbPath = paths.engineering;
137
+ }
138
+ }
139
+ catch {
140
+ // Config not present — use default kbPath
141
+ }
142
+ const kbSection = buildKbSection(cwd, kbPath);
143
+ const wfSection = buildWorkflowSection(cwd);
144
+ if (!kbSection && !wfSection) {
145
+ result.messages.push("△ refresh-kb-links: no KB or workflow files found to link.");
146
+ return result;
147
+ }
148
+ for (const filename of AGENT_INSTRUCTION_FILES) {
149
+ const filePath = path.join(cwd, filename);
150
+ let content;
151
+ try {
152
+ content = fs.readFileSync(filePath, "utf8");
153
+ }
154
+ catch {
155
+ // File does not exist — skip
156
+ result.filesSkipped++;
157
+ continue;
158
+ }
159
+ let updated = content;
160
+ if (kbSection) {
161
+ updated = upsertSection(updated, KB_OPEN_PREFIX, KB_CLOSE, kbSection);
162
+ }
163
+ if (wfSection) {
164
+ updated = upsertSection(updated, WF_OPEN_PREFIX, WF_CLOSE, wfSection);
165
+ }
166
+ if (updated !== content) {
167
+ try {
168
+ fs.writeFileSync(filePath, updated, "utf8");
169
+ result.filesUpdated++;
170
+ result.messages.push(`〇 refresh-kb-links: updated ${filename}`);
171
+ }
172
+ catch (err) {
173
+ const e = err;
174
+ result.messages.push(`△ refresh-kb-links: failed to write ${filename}: ${e.message ?? "unknown"}`);
175
+ }
176
+ }
177
+ else {
178
+ result.filesSkipped++;
179
+ }
180
+ }
181
+ // Handle .cursor/rules/*.mdc files
182
+ const cursorRulesDir = path.join(cwd, ".cursor", "rules");
183
+ if (fs.existsSync(cursorRulesDir)) {
184
+ try {
185
+ const mdcFiles = fs.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"));
186
+ for (const mdcFile of mdcFiles) {
187
+ const filePath = path.join(cursorRulesDir, mdcFile);
188
+ let content;
189
+ try {
190
+ content = fs.readFileSync(filePath, "utf8");
191
+ }
192
+ catch {
193
+ result.filesSkipped++;
194
+ continue;
195
+ }
196
+ let updated = content;
197
+ if (kbSection) {
198
+ updated = upsertSection(updated, KB_OPEN_PREFIX, KB_CLOSE, kbSection);
199
+ }
200
+ if (wfSection) {
201
+ updated = upsertSection(updated, WF_OPEN_PREFIX, WF_CLOSE, wfSection);
202
+ }
203
+ if (updated !== content) {
204
+ fs.writeFileSync(filePath, updated, "utf8");
205
+ result.filesUpdated++;
206
+ result.messages.push(`〇 refresh-kb-links: updated .cursor/rules/${mdcFile}`);
207
+ }
208
+ else {
209
+ result.filesSkipped++;
210
+ }
211
+ }
212
+ }
213
+ catch {
214
+ // Skip if unreadable
215
+ }
216
+ }
217
+ return result;
218
+ }
219
+ // ── Handler factory ────────────────────────────────────────────────────────
220
+ /**
221
+ * Get a standalone refresh-kb-links handler suitable for direct invocation
222
+ * from Phase 4 step 4-9. Returns a function that takes cwd and calls
223
+ * runRefreshKbLinks.
224
+ */
225
+ export function getRefreshKbLinksHandler() {
226
+ return runRefreshKbLinks;
227
+ }
228
+ //# sourceMappingURL=refresh-kb-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-kb-links.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/refresh-kb-links.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface StoreValidatorResult {
2
+ ok: boolean;
3
+ reason: string;
4
+ }
5
+ /**
6
+ * Validate a store entity payload by spawning `store-cli.cjs validate`.
7
+ *
8
+ * @param entity Entity type: "task" | "sprint" | "bug" | "event" | …
9
+ * @param payload The raw payload (will be JSON.stringify'd if object; passed as-is if string).
10
+ * @param forgeRoot Absolute path to the Forge plugin root — locates store-cli.cjs.
11
+ * @returns `{ ok: true, reason: "" }` on success, `{ ok: false, reason: <stderr> }` on failure.
12
+ */
13
+ export declare function validateStoreCLIPayload(entity: string, payload: unknown, forgeRoot: string): StoreValidatorResult;
@@ -0,0 +1,35 @@
1
+ // Store-CLI payload validator — FORGE-S18-T03
2
+ //
3
+ // Spawns `store-cli.cjs validate <entity> '<json>'` synchronously and surfaces
4
+ // any schema error as a structured result. This module is the sole point of
5
+ // contact between hook-dispatcher.ts and store-cli's validation logic.
6
+ //
7
+ // Why spawnSync?
8
+ // pi's tool_call handler is synchronous — the event system does not support
9
+ // async handlers. spawnSync completes before the block result is returned to pi.
10
+ import { spawnSync } from "node:child_process";
11
+ import * as path from "node:path";
12
+ /**
13
+ * Validate a store entity payload by spawning `store-cli.cjs validate`.
14
+ *
15
+ * @param entity Entity type: "task" | "sprint" | "bug" | "event" | …
16
+ * @param payload The raw payload (will be JSON.stringify'd if object; passed as-is if string).
17
+ * @param forgeRoot Absolute path to the Forge plugin root — locates store-cli.cjs.
18
+ * @returns `{ ok: true, reason: "" }` on success, `{ ok: false, reason: <stderr> }` on failure.
19
+ */
20
+ export function validateStoreCLIPayload(entity, payload, forgeRoot) {
21
+ const storeCliPath = path.join(forgeRoot, "tools", "store-cli.cjs");
22
+ const payloadStr = typeof payload === "string" ? payload : JSON.stringify(payload);
23
+ const result = spawnSync(process.execPath, // node
24
+ [storeCliPath, "validate", entity, payloadStr], { encoding: "utf8", timeout: 10_000 });
25
+ // Non-zero exit code or error signal → validation failed.
26
+ if (result.status !== 0 || result.error) {
27
+ const reason = result.stderr?.trim() ||
28
+ result.error?.message ||
29
+ `store-cli validate exited with code ${String(result.status)}` ||
30
+ "validation failed (no error message)";
31
+ return { ok: false, reason };
32
+ }
33
+ return { ok: true, reason: "" };
34
+ }
35
+ //# sourceMappingURL=store-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-validator.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/store-validator.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,EAAE;AACF,+EAA+E;AAC/E,4EAA4E;AAC5E,uEAAuE;AACvE,EAAE;AACF,iBAAiB;AACjB,8EAA8E;AAC9E,mFAAmF;AAEnF,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAOlC;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAc,EAAE,OAAgB,EAAE,SAAiB;IAC1F,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC;IAEpE,MAAM,UAAU,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAEnF,MAAM,MAAM,GAAG,SAAS,CACvB,OAAO,CAAC,QAAQ,EAAE,OAAO;IACzB,CAAC,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAC9C,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CACrC,CAAC;IAEF,0DAA0D;IAC1D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACzC,MAAM,MAAM,GACX,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE;YACrB,MAAM,CAAC,KAAK,EAAE,OAAO;YACrB,uCAAuC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;YAC9D,sCAAsC,CAAC;QACxC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC9B,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,20 @@
1
+ export interface TransitionGuardResult {
2
+ allowed: boolean;
3
+ reason: string;
4
+ }
5
+ export interface TransitionGuardInput {
6
+ entity: string;
7
+ entityId: string;
8
+ toStatus: string;
9
+ }
10
+ /**
11
+ * Check whether a status transition is legal.
12
+ *
13
+ * Fail-open: if the current status cannot be read, returns
14
+ * `{ allowed: true, reason: "lookup-failed" }`. The caller
15
+ * should audit-log this outcome but MUST NOT block the operation.
16
+ *
17
+ * @param input Entity, entity ID, and target status.
18
+ * @param forgeRoot Absolute path to the Forge plugin root.
19
+ */
20
+ export declare function checkTransition(input: TransitionGuardInput, forgeRoot: string): TransitionGuardResult;
@@ -0,0 +1,125 @@
1
+ // Status-transition guard — FORGE-S18-T03
2
+ //
3
+ // Encodes the legal status-transition table for each entity (task, sprint, bug)
4
+ // and checks whether a proposed transition is allowed.
5
+ //
6
+ // The guard reads the current status from disk via `store-cli read` (spawnSync).
7
+ // Fail-open: if the current-status lookup fails for any reason, the guard returns
8
+ // { allowed: true } and sets reason="lookup-failed". The caller (hook-dispatcher)
9
+ // logs this as "lookup-failed" under FORGE_HOOK_AUDIT=1 but never blocks the
10
+ // operation — a lookup failure must not block a valid operation.
11
+ import { spawnSync } from "node:child_process";
12
+ import * as path from "node:path";
13
+ // ── Legal transition tables ───────────────────────────────────────────────────
14
+ //
15
+ // Derived from task.schema.json, sprint.schema.json, bug.schema.json status enums.
16
+ // Each entry: fromStatus → Set<toStatus>
17
+ const TASK_TRANSITIONS = {
18
+ draft: new Set(["planned", "blocked", "escalated", "abandoned"]),
19
+ planned: new Set(["plan-approved", "plan-revision-required", "blocked", "escalated", "abandoned"]),
20
+ "plan-approved": new Set(["implementing", "blocked", "escalated", "abandoned"]),
21
+ implementing: new Set(["implemented", "code-revision-required", "blocked", "escalated", "abandoned"]),
22
+ implemented: new Set(["review-approved", "blocked", "escalated", "abandoned"]),
23
+ "review-approved": new Set(["approved", "blocked", "escalated", "abandoned"]),
24
+ approved: new Set(["committed", "blocked", "escalated", "abandoned"]),
25
+ "plan-revision-required": new Set(["planned", "blocked", "escalated", "abandoned"]),
26
+ "code-revision-required": new Set(["implementing", "blocked", "escalated", "abandoned"]),
27
+ // Terminal / sink states — can only be re-opened by --force.
28
+ blocked: new Set(["blocked", "escalated", "abandoned"]),
29
+ escalated: new Set(["blocked", "escalated", "abandoned"]),
30
+ abandoned: new Set(["blocked", "escalated", "abandoned"]),
31
+ committed: new Set(["blocked", "escalated", "abandoned"]),
32
+ };
33
+ const SPRINT_TRANSITIONS = {
34
+ planning: new Set(["active", "abandoned"]),
35
+ active: new Set(["completed", "partially-completed", "blocked", "abandoned"]),
36
+ completed: new Set(["retrospective-done"]),
37
+ "partially-completed": new Set(["retrospective-done"]),
38
+ "retrospective-done": new Set([]),
39
+ blocked: new Set(["active", "abandoned"]),
40
+ abandoned: new Set([]),
41
+ };
42
+ const BUG_TRANSITIONS = {
43
+ reported: new Set(["triaged", "abandoned"]),
44
+ triaged: new Set(["in-progress", "abandoned"]),
45
+ "in-progress": new Set(["fixed", "abandoned"]),
46
+ fixed: new Set(["verified"]),
47
+ verified: new Set([]),
48
+ abandoned: new Set([]),
49
+ };
50
+ const ENTITY_TABLES = {
51
+ task: TASK_TRANSITIONS,
52
+ sprint: SPRINT_TRANSITIONS,
53
+ bug: BUG_TRANSITIONS,
54
+ };
55
+ function legalNextStates(entity, fromStatus) {
56
+ const table = ENTITY_TABLES[entity];
57
+ if (!table)
58
+ return [];
59
+ const allowed = table[fromStatus];
60
+ return allowed ? [...allowed] : [];
61
+ }
62
+ // ── Current-status lookup (fail-open) ────────────────────────────────────────
63
+ /**
64
+ * Read the current status of an entity from the store via `store-cli read`.
65
+ * Returns the status string on success, or null on any failure (fail-open).
66
+ */
67
+ function readCurrentStatus(entity, entityId, forgeRoot) {
68
+ const storeCliPath = path.join(forgeRoot, "tools", "store-cli.cjs");
69
+ try {
70
+ const result = spawnSync(process.execPath, [storeCliPath, "read", entity, entityId], {
71
+ encoding: "utf8",
72
+ timeout: 10_000,
73
+ });
74
+ if (result.status !== 0 || result.error)
75
+ return null;
76
+ const stdout = result.stdout?.trim();
77
+ if (!stdout)
78
+ return null;
79
+ // store-cli read emits JSON; parse and extract the status field.
80
+ const record = JSON.parse(stdout);
81
+ const status = record.status;
82
+ return typeof status === "string" ? status : null;
83
+ }
84
+ catch {
85
+ // Any parse error or subprocess error → fail-open.
86
+ return null;
87
+ }
88
+ }
89
+ /**
90
+ * Check whether a status transition is legal.
91
+ *
92
+ * Fail-open: if the current status cannot be read, returns
93
+ * `{ allowed: true, reason: "lookup-failed" }`. The caller
94
+ * should audit-log this outcome but MUST NOT block the operation.
95
+ *
96
+ * @param input Entity, entity ID, and target status.
97
+ * @param forgeRoot Absolute path to the Forge plugin root.
98
+ */
99
+ export function checkTransition(input, forgeRoot) {
100
+ const { entity, entityId, toStatus } = input;
101
+ // Lookup current status — fail-open on any error.
102
+ const fromStatus = readCurrentStatus(entity, entityId, forgeRoot);
103
+ if (fromStatus === null) {
104
+ return {
105
+ allowed: true,
106
+ reason: "lookup-failed",
107
+ };
108
+ }
109
+ const table = ENTITY_TABLES[entity];
110
+ if (!table) {
111
+ // Unknown entity type — allow through (future-proofing).
112
+ return { allowed: true, reason: "" };
113
+ }
114
+ const allowed = table[fromStatus]?.has(toStatus) ?? false;
115
+ if (allowed) {
116
+ return { allowed: true, reason: "" };
117
+ }
118
+ const legal = legalNextStates(entity, fromStatus);
119
+ const legalStr = legal.length > 0 ? legal.join(", ") : "(none)";
120
+ return {
121
+ allowed: false,
122
+ reason: `${fromStatus} → ${toStatus} is not a legal transition for ${entity}. Legal next states from ${fromStatus}: ${legalStr}.`,
123
+ };
124
+ }
125
+ //# sourceMappingURL=transition-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transition-guard.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/transition-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: approve
3
+ description: Final architect approval gate for a completed task
4
+ ---
5
+
6
+ # /{{PREFIX}}:approve
7
+
8
+ Read the approve workflow and follow it.
9
+
10
+ ## Locate the Forge plugin
11
+
12
+ ```
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
14
+ ```
15
+
16
+ ## Execute
17
+
18
+ Read `.forge/workflows/architect_approve.md` and follow it.
19
+
20
+ ## Arguments
21
+
22
+ $ARGUMENTS
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: collate
3
+ description: Regenerate KB documents from the JSON store
4
+ ---
5
+
6
+ # /{{PREFIX}}:collate
7
+
8
+ Read the collate workflow and follow it.
9
+
10
+ ## Locate the Forge plugin
11
+
12
+ ```
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
14
+ ```
15
+
16
+ ## Execute
17
+
18
+ Read `.forge/workflows/collator_agent.md` and follow it.
19
+
20
+ ## Arguments
21
+
22
+ $ARGUMENTS
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: commit
3
+ description: Stage and commit completed task artifacts
4
+ ---
5
+
6
+ # /{{PREFIX}}:commit
7
+
8
+ Read the commit workflow and follow it.
9
+
10
+ ## Locate the Forge plugin
11
+
12
+ ```
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
14
+ ```
15
+
16
+ ## Execute
17
+
18
+ Read `.forge/workflows/commit_task.md` and follow it.
19
+
20
+ ## Arguments
21
+
22
+ $ARGUMENTS
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: enhance
3
+ description: Progressive project-specific enrichment of structural elements
4
+ ---
5
+
6
+ # /{{PREFIX}}:enhance
7
+
8
+ Run the enhancement agent to enrich structural elements with project-specific knowledge.
9
+
10
+ ## Locate the Forge plugin
11
+
12
+ ```
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
14
+ ```
15
+
16
+ ## Purpose
17
+
18
+ The enhancement agent observes the project and enriches structural elements
19
+ over time. It makes absolutely essential minimal modifications, preferring
20
+ runtime mix-in (KB references, `{{placeholder}}` substitution, `project-context.json`)
21
+ over modifying base artifacts.
22
+
23
+ ## Behavior
24
+
25
+ 1. Read `structure-versions.json` to determine current overlay level
26
+ 2. Read `project-context.json` for current project specifics
27
+ 3. Read KB changes since last enhancement
28
+ 4. Scan codebase for patterns discovered since last enhancement
29
+ 5. Compare current `.forge/` artifacts against base-pack baseline
30
+ 6. For each artifact, determine what project-specific enrichment is warranted
31
+ 7. Apply the minimal modification principle
32
+ 8. Propose diffs (Phase 2+) or auto-apply (Phase 1)
33
+ 9. Create new overlay version in `structure-versions.json` if needed
34
+
35
+ ## Arguments
36
+
37
+ $ARGUMENTS
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: fix-bug
3
+ description: Triage, diagnose, and fix a bug
4
+ ---
5
+
6
+ # /{{PREFIX}}:fix-bug
7
+
8
+ Read the fix-bug workflow and follow it.
9
+
10
+ ## Locate the Forge plugin
11
+
12
+ ```
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
14
+ ```
15
+
16
+ ## Execute
17
+
18
+ Read `.forge/workflows/fix_bug.md` and follow it.
19
+
20
+ ## Arguments
21
+
22
+ $ARGUMENTS
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: implement
3
+ description: Execute the approved implementation plan for a task
4
+ ---
5
+
6
+ # /{{PREFIX}}:implement
7
+
8
+ Read the implement workflow and follow it.
9
+
10
+ ## Locate the Forge plugin
11
+
12
+ ```
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
14
+ ```
15
+
16
+ ## Execute
17
+
18
+ Read `.forge/workflows/implement_plan.md` and follow it.
19
+
20
+ ## Arguments
21
+
22
+ $ARGUMENTS
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: plan
3
+ description: Design and document the implementation plan for a task
4
+ ---
5
+
6
+ # /{{PREFIX}}:plan
7
+
8
+ Read the plan workflow and follow it.
9
+
10
+ ## Locate the Forge plugin
11
+
12
+ ```
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
14
+ ```
15
+
16
+ ## Execute
17
+
18
+ Read `.forge/workflows/plan_task.md` and follow it.
19
+
20
+ ## Arguments
21
+
22
+ $ARGUMENTS
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: quiz-agent
3
+ description: Verify an agent has loaded and understood the project knowledge base
4
+ ---
5
+
6
+ # /{{PREFIX}}:quiz-agent
7
+
8
+ Read the quiz-agent workflow and follow it.
9
+
10
+ ## Locate the Forge plugin
11
+
12
+ ```
13
+ FORGE_ROOT: !`echo "${CLAUDE_PLUGIN_ROOT}"`
14
+ ```
15
+
16
+ ## Execute
17
+
18
+ Read `.forge/workflows/quiz_agent.md` and follow it.
19
+
20
+ ## Arguments
21
+
22
+ $ARGUMENTS