@entelligentsia/forgecli 0.11.2 → 0.15.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 (89) hide show
  1. package/CHANGELOG.md +324 -0
  2. package/README.md +2 -1
  3. package/dist/CHANGELOG-forge-plugin.md +210 -0
  4. package/dist/bin/forge.js +20 -1
  5. package/dist/bin/forge.js.map +1 -1
  6. package/dist/extensions/forgecli/ask-user-tool.js +32 -20
  7. package/dist/extensions/forgecli/ask-user-tool.js.map +1 -1
  8. package/dist/extensions/forgecli/config-layer.d.ts +15 -0
  9. package/dist/extensions/forgecli/config-layer.js +4 -1
  10. package/dist/extensions/forgecli/config-layer.js.map +1 -1
  11. package/dist/extensions/forgecli/config-writer.js +4 -1
  12. package/dist/extensions/forgecli/config-writer.js.map +1 -1
  13. package/dist/extensions/forgecli/enhance.js +1 -1
  14. package/dist/extensions/forgecli/enhance.js.map +1 -1
  15. package/dist/extensions/forgecli/fix-bug.js +31 -1
  16. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  17. package/dist/extensions/forgecli/forge-cli-schema.json +19 -0
  18. package/dist/extensions/forgecli/forge-tools.js +80 -0
  19. package/dist/extensions/forgecli/forge-tools.js.map +1 -1
  20. package/dist/extensions/forgecli/forge-update-command.js +24 -18
  21. package/dist/extensions/forgecli/forge-update-command.js.map +1 -1
  22. package/dist/extensions/forgecli/friction-emit.d.ts +97 -0
  23. package/dist/extensions/forgecli/friction-emit.js +246 -0
  24. package/dist/extensions/forgecli/friction-emit.js.map +1 -0
  25. package/dist/extensions/forgecli/health-check.d.ts +10 -0
  26. package/dist/extensions/forgecli/health-check.js +160 -8
  27. package/dist/extensions/forgecli/health-check.js.map +1 -1
  28. package/dist/extensions/forgecli/hook-dispatcher.js +24 -2
  29. package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
  30. package/dist/extensions/forgecli/hooks/write-guard.js +5 -1
  31. package/dist/extensions/forgecli/hooks/write-guard.js.map +1 -1
  32. package/dist/extensions/forgecli/index.js +29 -5
  33. package/dist/extensions/forgecli/index.js.map +1 -1
  34. package/dist/extensions/forgecli/lib/store-error-remediation.d.ts +65 -0
  35. package/dist/extensions/forgecli/lib/store-error-remediation.js +298 -0
  36. package/dist/extensions/forgecli/lib/store-error-remediation.js.map +1 -0
  37. package/dist/extensions/forgecli/regenerate.d.ts +22 -0
  38. package/dist/extensions/forgecli/regenerate.js +133 -3
  39. package/dist/extensions/forgecli/regenerate.js.map +1 -1
  40. package/dist/extensions/forgecli/run-sprint.js +16 -1
  41. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  42. package/dist/extensions/forgecli/run-task.js +30 -8
  43. package/dist/extensions/forgecli/run-task.js.map +1 -1
  44. package/dist/extensions/forgecli/skill-curation-flag.d.ts +21 -0
  45. package/dist/extensions/forgecli/skill-curation-flag.js +71 -0
  46. package/dist/extensions/forgecli/skill-curation-flag.js.map +1 -0
  47. package/dist/extensions/forgecli/skill-curator-subagent.d.ts +101 -0
  48. package/dist/extensions/forgecli/skill-curator-subagent.js +342 -0
  49. package/dist/extensions/forgecli/skill-curator-subagent.js.map +1 -0
  50. package/dist/extensions/forgecli/skill-retriever.d.ts +84 -0
  51. package/dist/extensions/forgecli/skill-retriever.js +246 -0
  52. package/dist/extensions/forgecli/skill-retriever.js.map +1 -0
  53. package/dist/extensions/forgecli/skill-usage-tracker.d.ts +91 -0
  54. package/dist/extensions/forgecli/skill-usage-tracker.js +224 -0
  55. package/dist/extensions/forgecli/skill-usage-tracker.js.map +1 -0
  56. package/dist/extensions/forgecli/store-resolver.d.ts +18 -0
  57. package/dist/extensions/forgecli/store-resolver.js +44 -4
  58. package/dist/extensions/forgecli/store-resolver.js.map +1 -1
  59. package/dist/extensions/forgecli/store-validator.d.ts +3 -0
  60. package/dist/extensions/forgecli/store-validator.js +4 -2
  61. package/dist/extensions/forgecli/store-validator.js.map +1 -1
  62. package/dist/forge-payload/.base-pack/personas/supervisor.md +9 -0
  63. package/dist/forge-payload/.base-pack/workflows/enhance.md +344 -18
  64. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  65. package/dist/forge-payload/.schemas/event.schema.json +20 -2
  66. package/dist/forge-payload/.schemas/migrations.json +112 -0
  67. package/dist/forge-payload/.schemas/proposal.schema.json +40 -0
  68. package/dist/forge-payload/agents/store-query-validator.md +103 -0
  69. package/dist/forge-payload/agents/tomoshibi.md +185 -0
  70. package/dist/forge-payload/commands/regenerate.md +109 -20
  71. package/dist/forge-payload/hooks/check-update.js +378 -0
  72. package/dist/forge-payload/hooks/forge-permissions.js +158 -0
  73. package/dist/forge-payload/hooks/triage-error.js +71 -0
  74. package/dist/forge-payload/hooks/validate-write.js +236 -0
  75. package/dist/forge-payload/integrity.json +32 -0
  76. package/dist/forge-payload/meta/workflows/meta-enhance.md +344 -18
  77. package/dist/forge-payload/schemas/structure-manifest.json +511 -0
  78. package/dist/forge-payload/tools/build-persona-pack.cjs +120 -11
  79. package/dist/forge-payload/tools/compression-gate.cjs +192 -0
  80. package/dist/forge-payload/tools/delete-candidate-detector.cjs +114 -0
  81. package/dist/forge-payload/tools/judge-proposal.cjs +177 -0
  82. package/dist/forge-payload/tools/manage-versions.cjs +132 -4
  83. package/dist/forge-payload/tools/queue-drain.cjs +152 -0
  84. package/dist/forge-payload/tools/replay-scoring.cjs +117 -0
  85. package/node_modules/@mariozechner/clipboard/package.json +2 -1
  86. package/node_modules/@mariozechner/clipboard-linux-x64-musl/README.md +3 -0
  87. package/node_modules/@mariozechner/clipboard-linux-x64-musl/clipboard.linux-x64-musl.node +0 -0
  88. package/node_modules/@mariozechner/clipboard-linux-x64-musl/package.json +25 -0
  89. package/package.json +4 -2
@@ -0,0 +1,65 @@
1
+ export interface RemediationResult {
2
+ /** One-line user-facing hint explaining the error and the fix. */
3
+ hint: string;
4
+ /** Optional copy-pasteable store-cli command. Empty string if N/A. */
5
+ command: string;
6
+ }
7
+ /**
8
+ * Infer the entity type from an entity name/ID string.
9
+ * Returns "task" as default if no match.
10
+ */
11
+ export declare function inferEntityType(entity: string): string;
12
+ /** Parsed breakdown of a validation error string. */
13
+ interface ParsedError {
14
+ field: string;
15
+ errorKind: "enum" | "required" | "undeclared" | "type" | "pattern" | "datetime" | "length" | "other";
16
+ observed?: string;
17
+ enumValues?: string[];
18
+ }
19
+ /**
20
+ * Parse a single validation error line from validate.js / store-cli.
21
+ *
22
+ * Examples:
23
+ * 'status: value "verified" not in [reported, triaged, in-progress, fixed]'
24
+ * 'taskId: missing required field'
25
+ * 'xyz: undeclared field'
26
+ * 'sprintId: expected string, got number'
27
+ * 'title: value length 0 is below minLength 1'
28
+ * 'createdAt: value "today" is not a valid date-time'
29
+ * 'prefix: value "AB" does not match pattern ^[A-Z]+-[A-Z]$'
30
+ */
31
+ export declare function parseValidationError(line: string): ParsedError;
32
+ /**
33
+ * Given a single validation error line (from validate.js or store-cli),
34
+ * return a user-facing remediation hint and optional copy-pasteable command.
35
+ *
36
+ * @param errorLine A single error line from validate.js / store-cli output.
37
+ * @param entityType The entity type (task, sprint, bug, feature, event).
38
+ * @param entityId The entity ID (e.g. "FORGE-S18-T02") — used for commands.
39
+ * @returns RemediationResult with hint and command.
40
+ */
41
+ export declare function remediateError(errorLine: string, entityType: string, entityId: string): RemediationResult;
42
+ /**
43
+ * Parse a multi-line validation output (e.g. from store-cli validate or
44
+ * validate-store --dry-run) into individual error lines and return
45
+ * remediation for each.
46
+ *
47
+ * @param output Raw multi-line output from validation tool.
48
+ * @param entityType Override entity type (inferred from output if not provided).
49
+ * @returns Array of { errorLine, remediation } objects.
50
+ */
51
+ export declare function remediateValidationOutput(output: string, entityType?: string): Array<{
52
+ errorLine: string;
53
+ remediation: RemediationResult;
54
+ }>;
55
+ /**
56
+ * Format a block message for a write-guard violation, appending remediation hints.
57
+ * Used by hook-dispatcher.ts for store-cli intercept blocks.
58
+ *
59
+ * @param rawReason The raw reason string from validateStoreCLIPayload or checkWriteGuard.
60
+ * @param entityType The entity type if known.
61
+ * @param entityId The entity ID if known.
62
+ * @returns Enhanced reason string with remediation hints appended.
63
+ */
64
+ export declare function enhanceBlockMessage(rawReason: string, entityType?: string, entityId?: string): string;
65
+ export {};
@@ -0,0 +1,298 @@
1
+ // Store-error remediation — forge-cli#24
2
+ //
3
+ // Shared remediation-hint surface for store validation errors.
4
+ // Consumed by:
5
+ // - health-check.ts (per-error row in store-integrity output)
6
+ // - hooks/write-guard.ts (block-message body for schema violations)
7
+ // - hook-dispatcher.ts (store-cli intercept block messages)
8
+ // - store-validator.ts (structured result for hook callers)
9
+ //
10
+ // Error sources:
11
+ // 1. validate.js (in-process via write-guard) — produces error strings like:
12
+ // "status: value "verified" not in [reported, triaged, in-progress, fixed]"
13
+ // "taskId: missing required field"
14
+ // "xyz: undeclared field"
15
+ // 2. store-cli.cjs validate (subprocess via store-validator.ts) — same format,
16
+ // piped through stderr.
17
+ // 3. validate-store.cjs --dry-run (subprocess via health-check.ts) — lines like:
18
+ // "ERROR FORGE-S18-T02: status: value "verified" not in [reported, triaged, ...]"
19
+ // "WARN FORGE-S18-T02: ..."
20
+ //
21
+ // This module parses those formats and returns a one-line user-facing hint
22
+ // plus an optional copy-pasteable store-cli command.
23
+ // ── Entity-type inference from entity name / ID ────────────────────────────────
24
+ /** Map entity type to the corresponding schema's status enum values. */
25
+ const STATUS_ENUMS = {
26
+ task: [
27
+ "draft", "planned", "plan-approved", "implementing", "implemented",
28
+ "review-approved", "approved", "committed", "plan-revision-required",
29
+ "code-revision-required", "blocked", "escalated", "abandoned",
30
+ ],
31
+ sprint: [
32
+ "planning", "active", "completed", "retrospective-done",
33
+ "partially-completed", "blocked", "abandoned",
34
+ ],
35
+ bug: ["reported", "triaged", "in-progress", "fixed"],
36
+ feature: ["proposed", "accepted", "in-progress", "delivered", "declined"],
37
+ event: [], // no status field
38
+ };
39
+ /** Known entity types that support write via store-cli. */
40
+ const ENTITY_TYPES = new Set(["task", "sprint", "bug", "feature", "event"]);
41
+ /**
42
+ * Infer the entity type from an entity name/ID string.
43
+ * Returns "task" as default if no match.
44
+ */
45
+ export function inferEntityType(entity) {
46
+ const lower = entity.toLowerCase();
47
+ // Bug patterns: BUG-015, FORGE-BUG-015, bug-031
48
+ if (/\bbug/i.test(entity))
49
+ return "bug";
50
+ // Sprint patterns: S18 (standalone), sprint-01 — but NOT inside task IDs like FORGE-S18-T02
51
+ if (/^[A-Z]+-S\d+$/i.test(entity) || /\bsprint/i.test(entity))
52
+ return "sprint";
53
+ // Feature patterns: FORGE-F01
54
+ if (/\bf\d+\b/i.test(entity) || lower.includes("feat"))
55
+ return "feature";
56
+ // Event patterns
57
+ if (lower.includes("event"))
58
+ return "event";
59
+ // Task IDs: FORGE-S18-T02, PROJECT-T01
60
+ if (/\bt\d+\b/i.test(entity))
61
+ return "task";
62
+ // Default
63
+ return "task";
64
+ }
65
+ const STATUS_REMEDIATION = {
66
+ hint: "Set status to one of the legal values for this entity type.",
67
+ command: (entityType, entityId) => `node "$FORGE_ROOT/tools/store-cli.cjs" update-status ${entityType} ${entityId} status <legal-value>`,
68
+ };
69
+ const REQUIRED_FIELD_REMEDIATION = {
70
+ hint: "Add the missing field with a valid value. Use the template command to see the canonical shape.",
71
+ command: (entityType, _entityId) => `node "$FORGE_ROOT/tools/store-cli.cjs" template ${entityType}`,
72
+ };
73
+ const UNDECLARED_FIELD_REMEDIATION = {
74
+ hint: "Remove the undeclared field, or check the schema for the correct property name.",
75
+ command: (entityType, _entityId) => `node "$FORGE_ROOT/tools/store-cli.cjs" describe ${entityType}`,
76
+ };
77
+ const TYPE_MISMATCH_REMEDIATION = {
78
+ hint: "Use the correct type for this field. Use the describe command to see the expected type.",
79
+ command: (entityType, _entityId) => `node "$FORGE_ROOT/tools/store-cli.cjs" describe ${entityType}`,
80
+ };
81
+ const PATTERN_REMEDIATION = {
82
+ hint: "The value must match the expected pattern (e.g. a date-time or ID format).",
83
+ command: (entityType, _entityId) => `node "$FORGE_ROOT/tools/store-cli.cjs" describe ${entityType}`,
84
+ };
85
+ const DATE_TIME_REMEDIATION = {
86
+ hint: "Use an ISO 8601 date-time string (e.g. 2026-05-21T12:00:00Z).",
87
+ command: (entityType, _entityId) => `node "$FORGE_ROOT/tools/store-cli.cjs" describe ${entityType}`,
88
+ };
89
+ const LENGTH_REMEDIATION = {
90
+ hint: "Adjust the value length to satisfy the schema constraint.",
91
+ command: (entityType, _entityId) => `node "$FORGE_ROOT/tools/store-cli.cjs" describe ${entityType}`,
92
+ };
93
+ /**
94
+ * Parse a single validation error line from validate.js / store-cli.
95
+ *
96
+ * Examples:
97
+ * 'status: value "verified" not in [reported, triaged, in-progress, fixed]'
98
+ * 'taskId: missing required field'
99
+ * 'xyz: undeclared field'
100
+ * 'sprintId: expected string, got number'
101
+ * 'title: value length 0 is below minLength 1'
102
+ * 'createdAt: value "today" is not a valid date-time'
103
+ * 'prefix: value "AB" does not match pattern ^[A-Z]+-[A-Z]$'
104
+ */
105
+ export function parseValidationError(line) {
106
+ // Enum violation: 'field: value "X" not in [a, b, c]'
107
+ const enumMatch = line.match(/^(\w+):\s+value\s+"([^"]+)"\s+not in \[([^\]]+)\]/);
108
+ if (enumMatch) {
109
+ return {
110
+ field: enumMatch[1],
111
+ errorKind: "enum",
112
+ observed: enumMatch[2],
113
+ enumValues: enumMatch[3].split(",").map((s) => s.trim()),
114
+ };
115
+ }
116
+ // Required field: 'field: missing required field'
117
+ if (/\bmissing required field\b/.test(line)) {
118
+ const fieldMatch = line.match(/^(\w+):/);
119
+ return { field: fieldMatch?.[1] ?? "unknown", errorKind: "required" };
120
+ }
121
+ // Undeclared field: 'xyz: undeclared field'
122
+ if (/\bundeclared field\b/.test(line)) {
123
+ const fieldMatch = line.match(/^(\w+):/);
124
+ return { field: fieldMatch?.[1] ?? "unknown", errorKind: "undeclared" };
125
+ }
126
+ // Type mismatch: 'field: expected string, got number'
127
+ const typeMatch = line.match(/^(\w+):\s+expected\s+\S+,?\s+got\s+\S+/);
128
+ if (typeMatch) {
129
+ return { field: typeMatch[1], errorKind: "type" };
130
+ }
131
+ // Date-time format: 'field: value "..." is not a valid date-time'
132
+ if (/\bis not a valid date-time\b/.test(line)) {
133
+ const fieldMatch = line.match(/^(\w+):/);
134
+ return { field: fieldMatch?.[1] ?? "unknown", errorKind: "datetime" };
135
+ }
136
+ // Pattern mismatch: 'field: value "..." does not match pattern ...'
137
+ if (/\bdoes not match pattern\b/.test(line)) {
138
+ const fieldMatch = line.match(/^(\w+):/);
139
+ return { field: fieldMatch?.[1] ?? "unknown", errorKind: "pattern" };
140
+ }
141
+ // Length violations
142
+ if (/\blength\b.*\b(exceeds|below)\b/.test(line) || /\b(exceeds|below)\b.*\blength\b/.test(line)) {
143
+ const fieldMatch = line.match(/^(\w+):/);
144
+ return { field: fieldMatch?.[1] ?? "unknown", errorKind: "length" };
145
+ }
146
+ // Generic: capture leading field name if present
147
+ const genericMatch = line.match(/^(\w+):/);
148
+ return { field: genericMatch?.[1] ?? "unknown", errorKind: "other" };
149
+ }
150
+ // ── Public API ──────────────────────────────────────────────────────────────────
151
+ /**
152
+ * Given a single validation error line (from validate.js or store-cli),
153
+ * return a user-facing remediation hint and optional copy-pasteable command.
154
+ *
155
+ * @param errorLine A single error line from validate.js / store-cli output.
156
+ * @param entityType The entity type (task, sprint, bug, feature, event).
157
+ * @param entityId The entity ID (e.g. "FORGE-S18-T02") — used for commands.
158
+ * @returns RemediationResult with hint and command.
159
+ */
160
+ export function remediateError(errorLine, entityType, entityId) {
161
+ const parsed = parseValidationError(errorLine);
162
+ switch (parsed.errorKind) {
163
+ case "enum": {
164
+ const legalValues = STATUS_ENUMS[entityType] ?? parsed.enumValues ?? [];
165
+ const isStatusField = parsed.field === "status" || parsed.field.toLowerCase().includes("status");
166
+ if (isStatusField && legalValues.length > 0) {
167
+ return {
168
+ hint: `"${parsed.observed}" is not a legal ${entityType} status. Legal values: ${legalValues.join(", ")}.`,
169
+ command: `node "$FORGE_ROOT/tools/store-cli.cjs" update-status ${entityType} ${entityId} status <${legalValues.join("|")}>`,
170
+ };
171
+ }
172
+ if (legalValues.length > 0) {
173
+ return {
174
+ hint: `Invalid value for "${parsed.field}". Allowed: ${legalValues.join(", ")}.`,
175
+ command: `node "$FORGE_ROOT/tools/store-cli.cjs" template ${entityType}`,
176
+ };
177
+ }
178
+ return {
179
+ hint: STATUS_REMEDIATION.hint,
180
+ command: STATUS_REMEDIATION.command(entityType, entityId),
181
+ };
182
+ }
183
+ case "required":
184
+ return {
185
+ hint: REQUIRED_FIELD_REMEDIATION.hint,
186
+ command: REQUIRED_FIELD_REMEDIATION.command(entityType, entityId),
187
+ };
188
+ case "undeclared":
189
+ return {
190
+ hint: UNDECLARED_FIELD_REMEDIATION.hint,
191
+ command: UNDECLARED_FIELD_REMEDIATION.command(entityType, entityId),
192
+ };
193
+ case "type":
194
+ return {
195
+ hint: TYPE_MISMATCH_REMEDIATION.hint,
196
+ command: TYPE_MISMATCH_REMEDIATION.command(entityType, entityId),
197
+ };
198
+ case "datetime":
199
+ return {
200
+ hint: DATE_TIME_REMEDIATION.hint,
201
+ command: DATE_TIME_REMEDIATION.command(entityType, entityId),
202
+ };
203
+ case "pattern":
204
+ return {
205
+ hint: PATTERN_REMEDIATION.hint,
206
+ command: PATTERN_REMEDIATION.command(entityType, entityId),
207
+ };
208
+ case "length":
209
+ return {
210
+ hint: LENGTH_REMEDIATION.hint,
211
+ command: LENGTH_REMEDIATION.command(entityType, entityId),
212
+ };
213
+ case "other":
214
+ default:
215
+ return {
216
+ hint: "Check the schema for the expected shape.",
217
+ command: `node "$FORGE_ROOT/tools/store-cli.cjs" template ${entityType}`,
218
+ };
219
+ }
220
+ }
221
+ /**
222
+ * Parse a multi-line validation output (e.g. from store-cli validate or
223
+ * validate-store --dry-run) into individual error lines and return
224
+ * remediation for each.
225
+ *
226
+ * @param output Raw multi-line output from validation tool.
227
+ * @param entityType Override entity type (inferred from output if not provided).
228
+ * @returns Array of { errorLine, remediation } objects.
229
+ */
230
+ export function remediateValidationOutput(output, entityType) {
231
+ const lines = output
232
+ .split("\n")
233
+ .map((l) => l.trim())
234
+ .filter((l) => l.length > 0);
235
+ const results = [];
236
+ for (const line of lines) {
237
+ // Strip "ERROR" / "WARN" prefix from validate-store --dry-run output
238
+ const cleaned = line.replace(/^(ERROR|WARN)\s+/, "");
239
+ if (!cleaned)
240
+ continue;
241
+ // Extract entity ID from lines like "FORGE-S18-T02: status: ..."
242
+ const entityIdMatch = cleaned.match(/^([A-Z]+-S?\d+-T?\d+|[A-Z]+-BUG-\d+|[A-Z]+-F\d+):/);
243
+ const entityId = entityIdMatch?.[1] ?? "unknown";
244
+ // If entity type not overridden, try to infer
245
+ const inferredType = entityType ?? inferEntityType(entityId);
246
+ // Skip non-error lines (hint lines from validate.js, blank lines, etc.)
247
+ if (cleaned.startsWith("(") || cleaned.startsWith("hint:") || cleaned.startsWith("#")) {
248
+ continue;
249
+ }
250
+ // Skip lines that are just an entity name without an error field
251
+ // (the real error is on the same line after the entity prefix)
252
+ const errorPart = entityIdMatch ? cleaned.slice(entityIdMatch[0].length).trim() : cleaned;
253
+ if (!errorPart || errorPart.length < 3)
254
+ continue;
255
+ results.push({
256
+ errorLine: cleaned,
257
+ remediation: remediateError(errorPart, inferredType, entityId),
258
+ });
259
+ }
260
+ return results;
261
+ }
262
+ /**
263
+ * Format a block message for a write-guard violation, appending remediation hints.
264
+ * Used by hook-dispatcher.ts for store-cli intercept blocks.
265
+ *
266
+ * @param rawReason The raw reason string from validateStoreCLIPayload or checkWriteGuard.
267
+ * @param entityType The entity type if known.
268
+ * @param entityId The entity ID if known.
269
+ * @returns Enhanced reason string with remediation hints appended.
270
+ */
271
+ export function enhanceBlockMessage(rawReason, entityType, entityId) {
272
+ // Parse the raw reason into lines and try to add remediation to each error line.
273
+ const lines = rawReason.split("\n");
274
+ const enhanced = [];
275
+ for (const line of lines) {
276
+ // Strip leading list markers and bullet points
277
+ const stripped = line.replace(/^\s*[-•]\s*/, "").trim();
278
+ // Check if this line contains a recognizable validation error pattern
279
+ const parsed = parseValidationError(stripped);
280
+ // Also match lines that contain validation error patterns not caught by parseValidationError
281
+ const hasKnownError = parsed.errorKind !== "other" || /\bmissing required\b|\bnot in \[|\bundeclared\b|\bexpected\b.*\bgot\b/.test(stripped);
282
+ if (hasKnownError) {
283
+ const type = entityType ?? "task";
284
+ const id = entityId ?? "unknown";
285
+ const remediation = remediateError(stripped, type, id);
286
+ enhanced.push(line);
287
+ enhanced.push(` 💡 ${remediation.hint}`);
288
+ if (remediation.command) {
289
+ enhanced.push(` → ${remediation.command}`);
290
+ }
291
+ }
292
+ else {
293
+ enhanced.push(line);
294
+ }
295
+ }
296
+ return enhanced.join("\n");
297
+ }
298
+ //# sourceMappingURL=store-error-remediation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-error-remediation.js","sourceRoot":"","sources":["../../../../src/extensions/forgecli/lib/store-error-remediation.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,24 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ export interface ModifiedFile {
3
+ category: string;
4
+ relativePath: string;
5
+ /** "modified" = manifest hash mismatch; "untracked" = no manifest entry (file exists but never recorded). */
6
+ state: "modified" | "untracked";
7
+ }
8
+ /**
9
+ * Enumerate files in .forge/{personas,skills,workflows,templates}/ and return
10
+ * those whose state diverges from the manifest baseline. Two divergence states
11
+ * are surfaced (both warrant a prompt before overwrite):
12
+ *
13
+ * - "modified": file content differs from the recorded manifest hash. Typically
14
+ * caused by /forge:enhance Phase 2 applying edits in-place.
15
+ * - "untracked": file exists on disk but has NO manifest entry. Caused by a
16
+ * prior regenerate that ran `clear-namespace` without re-recording. A fresh
17
+ * /forge:init records hashes for every generated file, so "untracked" should
18
+ * never appear in a clean install — its presence signals user content that's
19
+ * been disconnected from the tracking system. See forge-cli#30.
20
+ *
21
+ * Pure function (no I/O beyond fs reads + manifest tool spawns).
22
+ */
23
+ export declare function findModifiedStructuralFiles(cwd: string, toolsRoot: string): ModifiedFile[];
2
24
  export declare function registerRegenerate(pi: ExtensionAPI): void;
@@ -12,10 +12,63 @@
12
12
  // Idempotent — re-running with no changes overwrites with identical bytes.
13
13
  //
14
14
  // See forge-cli companion to forge#83 / #85.
15
- import { spawn } from "node:child_process";
15
+ import { spawn, spawnSync } from "node:child_process";
16
16
  import * as fs from "node:fs";
17
17
  import * as path from "node:path";
18
18
  import { getBundledPayloadRoot, getBundledToolsRoot } from "./forge-init.js";
19
+ // ── Pre-write modification guard (forge-cli#26 / forge#106 / FORGE-BUG-037) ──
20
+ //
21
+ // `substitute-placeholders.cjs` blanket-overwrites every file in the four
22
+ // structural-element categories. Before invoking it, enumerate each .forge/<cat>/
23
+ // file and run `generation-manifest.cjs check`. Any file whose recorded hash
24
+ // no longer matches its on-disk content is a manual modification — typically
25
+ // applied by /forge:enhance Phase 2 — and would be silently destroyed.
26
+ // Surface the list to the user and require explicit confirmation.
27
+ const STRUCTURAL_CATEGORIES = ["personas", "skills", "workflows", "templates"];
28
+ /**
29
+ * Enumerate files in .forge/{personas,skills,workflows,templates}/ and return
30
+ * those whose state diverges from the manifest baseline. Two divergence states
31
+ * are surfaced (both warrant a prompt before overwrite):
32
+ *
33
+ * - "modified": file content differs from the recorded manifest hash. Typically
34
+ * caused by /forge:enhance Phase 2 applying edits in-place.
35
+ * - "untracked": file exists on disk but has NO manifest entry. Caused by a
36
+ * prior regenerate that ran `clear-namespace` without re-recording. A fresh
37
+ * /forge:init records hashes for every generated file, so "untracked" should
38
+ * never appear in a clean install — its presence signals user content that's
39
+ * been disconnected from the tracking system. See forge-cli#30.
40
+ *
41
+ * Pure function (no I/O beyond fs reads + manifest tool spawns).
42
+ */
43
+ export function findModifiedStructuralFiles(cwd, toolsRoot) {
44
+ const manifestTool = path.join(toolsRoot, "generation-manifest.cjs");
45
+ if (!fs.existsSync(manifestTool))
46
+ return [];
47
+ const modified = [];
48
+ for (const category of STRUCTURAL_CATEGORIES) {
49
+ const dir = path.join(cwd, ".forge", category);
50
+ if (!fs.existsSync(dir))
51
+ continue;
52
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith(".md"));
53
+ for (const f of files) {
54
+ const relPath = path.posix.join(".forge", category, f);
55
+ const result = spawnSync("node", [manifestTool, "check", relPath], {
56
+ cwd,
57
+ encoding: "utf8",
58
+ timeout: 5_000,
59
+ });
60
+ // generation-manifest check exit codes:
61
+ // 0 = pristine, 1 = modified, 2 = untracked, 3 = file missing
62
+ if (result.status === 1) {
63
+ modified.push({ category, relativePath: relPath, state: "modified" });
64
+ }
65
+ else if (result.status === 2) {
66
+ modified.push({ category, relativePath: relPath, state: "untracked" });
67
+ }
68
+ }
69
+ }
70
+ return modified;
71
+ }
19
72
  async function runTool(toolPath, argv, cwd, ctx, label, timeoutMs = 60_000) {
20
73
  return await new Promise((resolve) => {
21
74
  const child = spawn("node", [toolPath, ...argv], { cwd, stdio: ["ignore", "pipe", "pipe"] });
@@ -55,7 +108,7 @@ export function registerRegenerate(pi) {
55
108
  pi.registerCommand("forge:regenerate", {
56
109
  description: "Re-materialize .forge/ and .claude/commands/ from the bundled forge-payload " +
57
110
  "(deterministic subset of the plugin's /forge:regenerate — runs substitute-placeholders.cjs).",
58
- async handler(_args, ctx) {
111
+ async handler(args, ctx) {
59
112
  const cwd = process.cwd();
60
113
  const configPath = path.join(cwd, ".forge", "config.json");
61
114
  if (!fs.existsSync(configPath)) {
@@ -75,6 +128,42 @@ export function registerRegenerate(pi) {
75
128
  ctx.ui.notify(`× forge:regenerate — base-pack missing at ${basePackDir}`, "error");
76
129
  return;
77
130
  }
131
+ // Pre-write modification guard (forge-cli#26 / forge#106 / FORGE-BUG-037).
132
+ // Skip with --force (e.g. CI / dogfood reset). Otherwise, surface any
133
+ // manual modifications (typically applied by /forge:enhance Phase 2)
134
+ // and require explicit confirmation before overwriting them.
135
+ const force = (args ?? []).includes("--force");
136
+ if (!force) {
137
+ const divergent = findModifiedStructuralFiles(cwd, toolsRoot);
138
+ if (divergent.length > 0) {
139
+ const modCount = divergent.filter((m) => m.state === "modified").length;
140
+ const untCount = divergent.filter((m) => m.state === "untracked").length;
141
+ const summary = [
142
+ `${divergent.length} file(s) in .forge/ diverge from the manifest baseline:`,
143
+ ` ${modCount} modified (hash mismatch — typical /forge:enhance Phase 2 edit)`,
144
+ ` ${untCount} untracked (no manifest entry — usually means a prior regenerate cleared the namespace)`,
145
+ "",
146
+ "Divergent files:",
147
+ ...divergent.map((m) => ` △ [${m.state}] ${m.relativePath}`),
148
+ "",
149
+ "Edits CAPTURED by /forge:enhance Phase 2 snapshots (.forge/archive/snap-N/)",
150
+ "will be RESTORED automatically post-regenerate via `manage-versions replay`",
151
+ "(forge#107 / Approach A — overlay semantics, later snapshots win).",
152
+ "",
153
+ "Edits NOT captured in any snapshot will be lost. To capture before regen, run",
154
+ "/forge:enhance Phase 2 → approve edits → ensure `add-snapshot` was called.",
155
+ "",
156
+ "Answer 'yes' to proceed (snapshot-captured edits survive automatically).",
157
+ "Answer 'no' to abort.",
158
+ "Use --force to skip this prompt AND skip replay (pristine base-pack).",
159
+ ].join("\n");
160
+ const proceed = await ctx.ui.confirm(`Proceed with regenerate? (${divergent.length} divergent file(s): ${modCount} modified, ${untCount} untracked)`, summary);
161
+ if (!proceed) {
162
+ ctx.ui.notify(`〇 forge:regenerate cancelled — ${divergent.length} divergent file(s) preserved.`, "info");
163
+ return;
164
+ }
165
+ }
166
+ }
78
167
  ctx.ui.setStatus?.("forge:regenerate", "rebuilding init-context…");
79
168
  // 1. Rebuild init-context.json so substitute has fresh placeholders.
80
169
  if (fs.existsSync(buildInitContextTool)) {
@@ -111,7 +200,48 @@ export function registerRegenerate(pi) {
111
200
  "--out",
112
201
  cwd,
113
202
  ], cwd, ctx, "substitute-placeholders", 60_000);
114
- // 3. Refresh schemas from bundled .schemas/. substitute-placeholders does
203
+ // 3a. Replay user enhancements (forge#107 / forge-cli#27 — Approach A layer 3).
204
+ // After substitute-placeholders wrote fresh base-pack content over
205
+ // .forge/{personas,skills,workflows,templates}/, restore any user-enhanced
206
+ // files captured by /forge:enhance Phase 2 snapshots from .forge/archive/snap-N/.
207
+ // Mirrors regenerate.md's per-category replay step. Pure additive — replay
208
+ // is a no-op when no snapshots match. Skipped when --force is used (force
209
+ // is "I want pristine base-pack content, no overlay").
210
+ const manageVersionsTool = path.join(toolsRoot, "manage-versions.cjs");
211
+ let replayTotal = 0;
212
+ if (ok && !force && fs.existsSync(manageVersionsTool)) {
213
+ ctx.ui.setStatus?.("forge:regenerate", "replaying user enhancements…");
214
+ const replayCategories = ["personas", "skills", "workflows", "templates"];
215
+ for (const category of replayCategories) {
216
+ await new Promise((resolve) => {
217
+ const child = spawn("node", [manageVersionsTool, "replay", "--target", category], {
218
+ cwd,
219
+ stdio: ["ignore", "pipe", "pipe"],
220
+ });
221
+ let stdoutBuf = "";
222
+ child.stdout?.on("data", (d) => {
223
+ stdoutBuf += d.toString();
224
+ });
225
+ child.on("close", (code) => {
226
+ if (code === 0) {
227
+ // Parse "restored — N file(s)" out of stdout to track total
228
+ const m = stdoutBuf.match(/(\d+)\s+file\(s\)\s+restored/);
229
+ if (m) {
230
+ replayTotal += parseInt(m[1], 10);
231
+ }
232
+ }
233
+ // Non-zero exit (e.g. structure-versions.json missing on a
234
+ // project that never ran /forge:enhance): treat as no-op.
235
+ resolve();
236
+ });
237
+ child.on("error", () => resolve());
238
+ });
239
+ }
240
+ if (replayTotal > 0) {
241
+ ctx.ui.notify(`〇 replay: ${replayTotal} user enhancement(s) restored from snapshots`, "info");
242
+ }
243
+ }
244
+ // 3b. Refresh schemas from bundled .schemas/. substitute-placeholders does
115
245
  // not touch .forge/schemas/, but a forge plugin version bump can ship
116
246
  // schema changes (e.g. 0.43.13 added `provider` required + dropped
117
247
  // `estimatedCostUSD`). Without this step, regenerated workflows assume
@@ -1 +1 @@
1
- {"version":3,"file":"regenerate.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/regenerate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"regenerate.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/regenerate.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,7 @@ import * as path from "node:path";
26
26
  import { fileURLToPath } from "node:url";
27
27
  import { spawnSync } from "node:child_process";
28
28
  import { assertAudience } from "./audience-gate.js";
29
+ import { resolveToCanonicalId, resolveToolDir } from "./store-resolver.js";
29
30
  import { checkMaterialization } from "./plan.js";
30
31
  import { loadWorkflow } from "./loaders/workflow-loader.js";
31
32
  import { discoverForgeConfig } from "./forge-root.js";
@@ -233,7 +234,7 @@ export function registerRunSprint(pi, options = {}) {
233
234
  "Orchestrator archetype: delegates per-task execution to runTaskPipeline.",
234
235
  async handler(args, ctx) {
235
236
  const cwd = options.cwd ?? process.cwd();
236
- const sprintId = args.trim();
237
+ let sprintId = args.trim();
237
238
  if (!sprintId) {
238
239
  ctx.ui.notify("× forge:run-sprint — sprint ID required. Usage: /forge:run-sprint <SPRINT_ID>", "error");
239
240
  return;
@@ -252,6 +253,20 @@ export function registerRunSprint(pi, options = {}) {
252
253
  return;
253
254
  }
254
255
  const forgeRoot = forgeConfig.forgeRoot;
256
+ // ── Resolve sprint ID (prefix-normalize, suffix-match, NLP fallback) ──
257
+ // Handles unprefixed IDs like "S22" → "FORGE-S22".
258
+ // Issue #20: unprefixed entity IDs silently poisoned substitutions.
259
+ const toolDir = resolveToolDir(forgeRoot);
260
+ const resolvedSprintId = await resolveToCanonicalId(sprintId, toolDir, cwd, "sprint", { ctx, commandLabel: "forge:run-sprint" });
261
+ if (!resolvedSprintId) {
262
+ // Error already emitted by resolver
263
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, undefined);
264
+ return;
265
+ }
266
+ // Replace raw arg with canonical ID for all subsequent operations.
267
+ sprintId = resolvedSprintId;
268
+ // Update status with canonical ID so the user sees the resolved form.
269
+ ctx.ui.setStatus?.(SPRINT_STATUS_KEY, `run-sprint ${sprintId}: ready`);
255
270
  const storeCli = path.join(forgeRoot, "tools", "store-cli.cjs");
256
271
  const preflightGate = path.join(forgeRoot, "tools", "preflight-gate.cjs");
257
272
  // ── Sprint resolution ────────────────────────────────────────────