@entelligentsia/forgecli 1.0.21 → 1.0.25

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 (232) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/dist/CHANGELOG-forge-plugin.md +118 -0
  3. package/dist/extensions/forgecli/forge-tools.d.ts +1 -0
  4. package/dist/extensions/forgecli/forge-tools.js +73 -0
  5. package/dist/extensions/forgecli/forge-tools.js.map +1 -1
  6. package/dist/extensions/forgecli/lib/forge-root.d.ts +5 -0
  7. package/dist/extensions/forgecli/lib/forge-root.js +14 -1
  8. package/dist/extensions/forgecli/lib/forge-root.js.map +1 -1
  9. package/dist/extensions/forgecli/orchestrators/fix-bug.d.ts +1 -0
  10. package/dist/extensions/forgecli/orchestrators/fix-bug.js +26 -0
  11. package/dist/extensions/forgecli/orchestrators/fix-bug.js.map +1 -1
  12. package/dist/extensions/forgecli/orchestrators/run-sprint.js +49 -0
  13. package/dist/extensions/forgecli/orchestrators/run-sprint.js.map +1 -1
  14. package/dist/forge-payload/.base-pack/workflows/_fragments/event-emission-schema.md +4 -0
  15. package/dist/forge-payload/.base-pack/workflows/_fragments/event-vocabulary.md +88 -0
  16. package/dist/forge-payload/.base-pack/workflows/commit_task.md +41 -38
  17. package/dist/forge-payload/.base-pack/workflows/implement_plan.md +3 -3
  18. package/dist/forge-payload/.base-pack/workflows-js/wfl-fix-bug.js +42 -6
  19. package/dist/forge-payload/.base-pack/workflows-js/wfl-run-task.js +32 -1
  20. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  21. package/dist/forge-payload/.schemas/enum-catalog.json +2 -2
  22. package/dist/forge-payload/.schemas/event.schema.json +8 -3
  23. package/dist/forge-payload/.schemas/migrations.json +56 -0
  24. package/dist/forge-payload/integrity.json +3 -3
  25. package/dist/forge-payload/meta/store-schema/event.schema.md +7 -0
  26. package/dist/forge-payload/meta/workflows/_fragments/event-emission-schema.md +4 -0
  27. package/dist/forge-payload/meta/workflows/_fragments/event-vocabulary.md +88 -0
  28. package/dist/forge-payload/meta/workflows/meta-commit.md +46 -43
  29. package/dist/forge-payload/meta/workflows/meta-fix-bug.md +7 -2
  30. package/dist/forge-payload/meta/workflows/meta-implement.md +3 -3
  31. package/dist/forge-payload/meta/workflows/meta-orchestrate.md +4 -1
  32. package/dist/forge-payload/schemas/enum-catalog.json +2 -2
  33. package/dist/forge-payload/schemas/event.schema.json +8 -3
  34. package/dist/forge-payload/schemas/structure-manifest.json +4 -2
  35. package/dist/forge-payload/tools/commit-task.cjs +218 -0
  36. package/dist/forge-payload/tools/store-cli.cjs +6 -1
  37. package/node_modules/@mariozechner/clipboard/package.json +2 -1
  38. package/node_modules/@mariozechner/clipboard-linux-x64-musl/README.md +3 -0
  39. package/node_modules/@mariozechner/clipboard-linux-x64-musl/package.json +25 -0
  40. package/package.json +2 -2
  41. package/dist/extensions/forgecli/add-pipeline.d.ts +0 -19
  42. package/dist/extensions/forgecli/add-pipeline.js +0 -143
  43. package/dist/extensions/forgecli/add-pipeline.js.map +0 -1
  44. package/dist/extensions/forgecli/add-task.d.ts +0 -20
  45. package/dist/extensions/forgecli/add-task.js +0 -154
  46. package/dist/extensions/forgecli/add-task.js.map +0 -1
  47. package/dist/extensions/forgecli/approve.d.ts +0 -22
  48. package/dist/extensions/forgecli/approve.js +0 -152
  49. package/dist/extensions/forgecli/approve.js.map +0 -1
  50. package/dist/extensions/forgecli/banner.d.ts +0 -10
  51. package/dist/extensions/forgecli/banner.js +0 -36
  52. package/dist/extensions/forgecli/banner.js.map +0 -1
  53. package/dist/extensions/forgecli/calibrate.d.ts +0 -64
  54. package/dist/extensions/forgecli/calibrate.js +0 -481
  55. package/dist/extensions/forgecli/calibrate.js.map +0 -1
  56. package/dist/extensions/forgecli/collate.d.ts +0 -22
  57. package/dist/extensions/forgecli/collate.js +0 -134
  58. package/dist/extensions/forgecli/collate.js.map +0 -1
  59. package/dist/extensions/forgecli/commit.d.ts +0 -22
  60. package/dist/extensions/forgecli/commit.js +0 -152
  61. package/dist/extensions/forgecli/commit.js.map +0 -1
  62. package/dist/extensions/forgecli/config-command.d.ts +0 -8
  63. package/dist/extensions/forgecli/config-command.js +0 -67
  64. package/dist/extensions/forgecli/config-command.js.map +0 -1
  65. package/dist/extensions/forgecli/config-layer.d.ts +0 -53
  66. package/dist/extensions/forgecli/config-layer.js +0 -72
  67. package/dist/extensions/forgecli/config-layer.js.map +0 -1
  68. package/dist/extensions/forgecli/config-writer.d.ts +0 -16
  69. package/dist/extensions/forgecli/config-writer.js +0 -69
  70. package/dist/extensions/forgecli/config-writer.js.map +0 -1
  71. package/dist/extensions/forgecli/enhance.d.ts +0 -27
  72. package/dist/extensions/forgecli/enhance.js +0 -199
  73. package/dist/extensions/forgecli/enhance.js.map +0 -1
  74. package/dist/extensions/forgecli/fix-bug.d.ts +0 -85
  75. package/dist/extensions/forgecli/fix-bug.js +0 -1580
  76. package/dist/extensions/forgecli/fix-bug.js.map +0 -1
  77. package/dist/extensions/forgecli/forge-header.d.ts +0 -12
  78. package/dist/extensions/forgecli/forge-header.js +0 -114
  79. package/dist/extensions/forgecli/forge-header.js.map +0 -1
  80. package/dist/extensions/forgecli/forge-init.d.ts +0 -26
  81. package/dist/extensions/forgecli/forge-init.js +0 -514
  82. package/dist/extensions/forgecli/forge-init.js.map +0 -1
  83. package/dist/extensions/forgecli/forge-root.d.ts +0 -10
  84. package/dist/extensions/forgecli/forge-root.js +0 -62
  85. package/dist/extensions/forgecli/forge-root.js.map +0 -1
  86. package/dist/extensions/forgecli/forge-update-command.d.ts +0 -100
  87. package/dist/extensions/forgecli/forge-update-command.js +0 -435
  88. package/dist/extensions/forgecli/forge-update-command.js.map +0 -1
  89. package/dist/extensions/forgecli/friction-emit.d.ts +0 -99
  90. package/dist/extensions/forgecli/friction-emit.js +0 -245
  91. package/dist/extensions/forgecli/friction-emit.js.map +0 -1
  92. package/dist/extensions/forgecli/implement.d.ts +0 -22
  93. package/dist/extensions/forgecli/implement.js +0 -170
  94. package/dist/extensions/forgecli/implement.js.map +0 -1
  95. package/dist/extensions/forgecli/init-context.d.ts +0 -99
  96. package/dist/extensions/forgecli/init-context.js +0 -178
  97. package/dist/extensions/forgecli/init-context.js.map +0 -1
  98. package/dist/extensions/forgecli/init-progress.d.ts +0 -39
  99. package/dist/extensions/forgecli/init-progress.js +0 -117
  100. package/dist/extensions/forgecli/init-progress.js.map +0 -1
  101. package/dist/extensions/forgecli/input-router.d.ts +0 -33
  102. package/dist/extensions/forgecli/input-router.js +0 -136
  103. package/dist/extensions/forgecli/input-router.js.map +0 -1
  104. package/dist/extensions/forgecli/lib/halt-advisor.d.ts +0 -59
  105. package/dist/extensions/forgecli/lib/halt-advisor.js +0 -113
  106. package/dist/extensions/forgecli/lib/halt-advisor.js.map +0 -1
  107. package/dist/extensions/forgecli/lib/orchestrator-preflight.d.ts +0 -46
  108. package/dist/extensions/forgecli/lib/orchestrator-preflight.js +0 -64
  109. package/dist/extensions/forgecli/lib/orchestrator-preflight.js.map +0 -1
  110. package/dist/extensions/forgecli/materialize.d.ts +0 -16
  111. package/dist/extensions/forgecli/materialize.js +0 -195
  112. package/dist/extensions/forgecli/materialize.js.map +0 -1
  113. package/dist/extensions/forgecli/migrate.d.ts +0 -22
  114. package/dist/extensions/forgecli/migrate.js +0 -260
  115. package/dist/extensions/forgecli/migrate.js.map +0 -1
  116. package/dist/extensions/forgecli/migration-engine.d.ts +0 -117
  117. package/dist/extensions/forgecli/migration-engine.js +0 -563
  118. package/dist/extensions/forgecli/migration-engine.js.map +0 -1
  119. package/dist/extensions/forgecli/model-registry.d.ts +0 -61
  120. package/dist/extensions/forgecli/model-registry.js +0 -127
  121. package/dist/extensions/forgecli/model-registry.js.map +0 -1
  122. package/dist/extensions/forgecli/model-resolver.d.ts +0 -32
  123. package/dist/extensions/forgecli/model-resolver.js +0 -65
  124. package/dist/extensions/forgecli/model-resolver.js.map +0 -1
  125. package/dist/extensions/forgecli/model-validator.d.ts +0 -29
  126. package/dist/extensions/forgecli/model-validator.js +0 -107
  127. package/dist/extensions/forgecli/model-validator.js.map +0 -1
  128. package/dist/extensions/forgecli/orchestrator-status-bar.d.ts +0 -26
  129. package/dist/extensions/forgecli/orchestrator-status-bar.js +0 -213
  130. package/dist/extensions/forgecli/orchestrator-status-bar.js.map +0 -1
  131. package/dist/extensions/forgecli/plan.d.ts +0 -22
  132. package/dist/extensions/forgecli/plan.js +0 -167
  133. package/dist/extensions/forgecli/plan.js.map +0 -1
  134. package/dist/extensions/forgecli/quiz-agent.d.ts +0 -17
  135. package/dist/extensions/forgecli/quiz-agent.js +0 -98
  136. package/dist/extensions/forgecli/quiz-agent.js.map +0 -1
  137. package/dist/extensions/forgecli/read-command.d.ts +0 -2
  138. package/dist/extensions/forgecli/read-command.js +0 -100
  139. package/dist/extensions/forgecli/read-command.js.map +0 -1
  140. package/dist/extensions/forgecli/regenerate.d.ts +0 -40
  141. package/dist/extensions/forgecli/regenerate.js +0 -438
  142. package/dist/extensions/forgecli/regenerate.js.map +0 -1
  143. package/dist/extensions/forgecli/remove-command.d.ts +0 -17
  144. package/dist/extensions/forgecli/remove-command.js +0 -124
  145. package/dist/extensions/forgecli/remove-command.js.map +0 -1
  146. package/dist/extensions/forgecli/report-bug.d.ts +0 -25
  147. package/dist/extensions/forgecli/report-bug.js +0 -159
  148. package/dist/extensions/forgecli/report-bug.js.map +0 -1
  149. package/dist/extensions/forgecli/retrospective.d.ts +0 -20
  150. package/dist/extensions/forgecli/retrospective.js +0 -126
  151. package/dist/extensions/forgecli/retrospective.js.map +0 -1
  152. package/dist/extensions/forgecli/review-code.d.ts +0 -35
  153. package/dist/extensions/forgecli/review-code.js +0 -196
  154. package/dist/extensions/forgecli/review-code.js.map +0 -1
  155. package/dist/extensions/forgecli/review-plan.d.ts +0 -35
  156. package/dist/extensions/forgecli/review-plan.js +0 -200
  157. package/dist/extensions/forgecli/review-plan.js.map +0 -1
  158. package/dist/extensions/forgecli/run-sprint.d.ts +0 -27
  159. package/dist/extensions/forgecli/run-sprint.js +0 -716
  160. package/dist/extensions/forgecli/run-sprint.js.map +0 -1
  161. package/dist/extensions/forgecli/run-task.d.ts +0 -204
  162. package/dist/extensions/forgecli/run-task.js +0 -1403
  163. package/dist/extensions/forgecli/run-task.js.map +0 -1
  164. package/dist/extensions/forgecli/skill-curation-flag.d.ts +0 -21
  165. package/dist/extensions/forgecli/skill-curation-flag.js +0 -71
  166. package/dist/extensions/forgecli/skill-curation-flag.js.map +0 -1
  167. package/dist/extensions/forgecli/skill-curator-subagent.d.ts +0 -102
  168. package/dist/extensions/forgecli/skill-curator-subagent.js +0 -339
  169. package/dist/extensions/forgecli/skill-curator-subagent.js.map +0 -1
  170. package/dist/extensions/forgecli/skill-retriever.d.ts +0 -84
  171. package/dist/extensions/forgecli/skill-retriever.js +0 -246
  172. package/dist/extensions/forgecli/skill-retriever.js.map +0 -1
  173. package/dist/extensions/forgecli/skill-usage-tracker.d.ts +0 -91
  174. package/dist/extensions/forgecli/skill-usage-tracker.js +0 -224
  175. package/dist/extensions/forgecli/skill-usage-tracker.js.map +0 -1
  176. package/dist/extensions/forgecli/sprint-intake.d.ts +0 -10
  177. package/dist/extensions/forgecli/sprint-intake.js +0 -91
  178. package/dist/extensions/forgecli/sprint-intake.js.map +0 -1
  179. package/dist/extensions/forgecli/sprint-plan.d.ts +0 -14
  180. package/dist/extensions/forgecli/sprint-plan.js +0 -122
  181. package/dist/extensions/forgecli/sprint-plan.js.map +0 -1
  182. package/dist/extensions/forgecli/status-command.d.ts +0 -19
  183. package/dist/extensions/forgecli/status-command.js +0 -140
  184. package/dist/extensions/forgecli/status-command.js.map +0 -1
  185. package/dist/extensions/forgecli/store-error-remediation.d.ts +0 -65
  186. package/dist/extensions/forgecli/store-error-remediation.js +0 -307
  187. package/dist/extensions/forgecli/store-error-remediation.js.map +0 -1
  188. package/dist/extensions/forgecli/store-query.d.ts +0 -22
  189. package/dist/extensions/forgecli/store-query.js +0 -107
  190. package/dist/extensions/forgecli/store-query.js.map +0 -1
  191. package/dist/extensions/forgecli/store-repair.d.ts +0 -17
  192. package/dist/extensions/forgecli/store-repair.js +0 -123
  193. package/dist/extensions/forgecli/store-repair.js.map +0 -1
  194. package/dist/extensions/forgecli/store-resolver.d.ts +0 -56
  195. package/dist/extensions/forgecli/store-resolver.js +0 -263
  196. package/dist/extensions/forgecli/store-resolver.js.map +0 -1
  197. package/dist/extensions/forgecli/store-validator.d.ts +0 -16
  198. package/dist/extensions/forgecli/store-validator.js +0 -32
  199. package/dist/extensions/forgecli/store-validator.js.map +0 -1
  200. package/dist/extensions/forgecli/test-orchestrate.d.ts +0 -2
  201. package/dist/extensions/forgecli/test-orchestrate.js +0 -182
  202. package/dist/extensions/forgecli/test-orchestrate.js.map +0 -1
  203. package/dist/extensions/forgecli/thread-switcher.d.ts +0 -5
  204. package/dist/extensions/forgecli/thread-switcher.js +0 -189
  205. package/dist/extensions/forgecli/thread-switcher.js.map +0 -1
  206. package/dist/extensions/forgecli/transition-guard.d.ts +0 -20
  207. package/dist/extensions/forgecli/transition-guard.js +0 -89
  208. package/dist/extensions/forgecli/transition-guard.js.map +0 -1
  209. package/dist/extensions/forgecli/update-check.d.ts +0 -37
  210. package/dist/extensions/forgecli/update-check.js +0 -185
  211. package/dist/extensions/forgecli/update-check.js.map +0 -1
  212. package/dist/extensions/forgecli/update-tools.d.ts +0 -23
  213. package/dist/extensions/forgecli/update-tools.js +0 -135
  214. package/dist/extensions/forgecli/update-tools.js.map +0 -1
  215. package/dist/extensions/forgecli/validate.d.ts +0 -22
  216. package/dist/extensions/forgecli/validate.js +0 -152
  217. package/dist/extensions/forgecli/validate.js.map +0 -1
  218. package/dist/extensions/forgecli/viewport-events.d.ts +0 -78
  219. package/dist/extensions/forgecli/viewport-events.js +0 -243
  220. package/dist/extensions/forgecli/viewport-events.js.map +0 -1
  221. package/dist/extensions/forgecli/viewport-renderer.d.ts +0 -83
  222. package/dist/extensions/forgecli/viewport-renderer.js +0 -233
  223. package/dist/extensions/forgecli/viewport-renderer.js.map +0 -1
  224. package/dist/extensions/forgecli/viewport-theme.d.ts +0 -11
  225. package/dist/extensions/forgecli/viewport-theme.js +0 -128
  226. package/dist/extensions/forgecli/viewport-theme.js.map +0 -1
  227. package/dist/extensions/forgecli/whats-new-widget.d.ts +0 -26
  228. package/dist/extensions/forgecli/whats-new-widget.js +0 -376
  229. package/dist/extensions/forgecli/whats-new-widget.js.map +0 -1
  230. package/dist/extensions/forgecli/whats-new.d.ts +0 -120
  231. package/dist/extensions/forgecli/whats-new.js +0 -470
  232. package/dist/extensions/forgecli/whats-new.js.map +0 -1
@@ -1,224 +0,0 @@
1
- // Skill usage tracker — FORGE-S24-T09.
2
- //
3
- // After a task completes, diff the actual tool-call sequence against each
4
- // retrieved skill's declared workflow steps and emit a follow-up `skill_usage`
5
- // event per retrieved skill with `used: true | false` and a populated
6
- // `tool_call_success_rate`. Conforms to the canonical `skill_usage` event
7
- // schema variant landed in FORGE-S24-T01 (plugin v0.45.0).
8
- //
9
- // Heuristic (per FORGE-S24-T09 acceptance criteria):
10
- // - Skill name appears in agent reasoning text → used: true
11
- // - ≥2 distinct workflow steps overlap with executed tool calls → used: true
12
- // - Otherwise → used: false
13
- //
14
- // `tool_call_success_rate` is the fraction of the skill's declared workflow
15
- // steps that were observed in the trajectory (distinct overlaps / total
16
- // steps), clamped to [0,1]. For skills with no declared steps, the rate is 0.
17
- //
18
- // Iron Laws (forge-cli-engineer):
19
- // IL2 — TypeScript + TypeBox. Runtime input is validated by `Value.Parse`
20
- // at the public emission entry point.
21
- // IL6 — No raw shell-string interpolation. `emitSkillUsageTrackingEvents`
22
- // calls `spawnSync("node", [storeCli, "emit", sprintId, JSON.stringify(event)])`
23
- // — argv array, no shell.
24
- // IL7 — No silent continuation past failures. A non-zero `store-cli emit`
25
- // exit increments the `failed` counter and the stderr text is
26
- // forwarded to the caller in the returned result; nothing is
27
- // swallowed.
28
- // IL10 — This module runs in **orchestrator context** (telemetry-actor
29
- // split — see `engineering/architecture/telemetry-actor-split.md`).
30
- // The orchestrator/caller supplies the runtime attribution
31
- // (model/provider/timestamps/durationMinutes) — never fabricated
32
- // here. The subagent NEVER invokes this tracker directly.
33
- //
34
- // Scope boundary: this module owns classification + emission. It does NOT
35
- // crawl the filesystem for skills, parse subagent transcripts, or decide
36
- // when "task close" is. The caller (Orchestrator handler — `run-task.ts`,
37
- // `fix-bug.ts`, gated behind FORGE-S24-T12 feature flag) assembles the
38
- // retrieved-skill list, trajectory, and runtime attribution, then invokes
39
- // `emitSkillUsageTrackingEvents`.
40
- import { Type } from "typebox";
41
- import { Value } from "typebox/value";
42
- import { spawnStoreCliEmit } from "./lib/spawn-store-cli.js";
43
- import { isSkillCurationEnabled } from "./skill-curation-flag.js";
44
- // ── Public schemas ───────────────────────────────────────────────────────
45
- /**
46
- * One skill that was previously retrieved (T08) and is now being evaluated
47
- * for actual usage. `workflowSteps` is the list of declared step / tool /
48
- * action names the skill's SKILL.md (or workflow doc) advertises — these
49
- * are what we diff the trajectory against.
50
- *
51
- * Step matching is case-insensitive substring: a tool call's `name` matches
52
- * a workflow step if the step (after lowercasing + whitespace collapse)
53
- * appears anywhere in the tool call's name. This is intentionally permissive
54
- * — declared steps are human-authored phrases ("store-cli query"), and tool
55
- * call names may be qualified (`mcp__store__store-cli-query`). Misses bias
56
- * toward `used: false`, which is the safer of the two error modes for a
57
- * curation signal.
58
- */
59
- export const RetrievedSkillForTrackingSchema = Type.Object({
60
- skillId: Type.String({ minLength: 1 }),
61
- name: Type.String({ minLength: 1 }),
62
- workflowSteps: Type.Array(Type.String()),
63
- });
64
- /** One observed tool invocation in the task's trajectory. */
65
- export const ToolCallObservationSchema = Type.Object({
66
- name: Type.String({ minLength: 1 }),
67
- });
68
- /**
69
- * The observable trajectory of a closed task: the sequence of tool calls
70
- * the agent made, plus the concatenated reasoning / assistant text the
71
- * orchestrator captured. Both inputs are supplied by the caller — this
72
- * module does not read transcripts itself.
73
- */
74
- export const TaskTrajectorySchema = Type.Object({
75
- toolCalls: Type.Array(ToolCallObservationSchema),
76
- reasoningText: Type.String(),
77
- });
78
- /**
79
- * Runtime attribution supplied by the caller (orchestrator).
80
- * These are NEVER fabricated inside the tracker (IL10).
81
- */
82
- export const EmitRuntimeSchema = Type.Object({
83
- storeCli: Type.String({ minLength: 1 }),
84
- cwd: Type.String({ minLength: 1 }),
85
- sprintId: Type.String({ minLength: 1 }),
86
- taskId: Type.String({ minLength: 1 }),
87
- role: Type.String({ minLength: 1 }),
88
- action: Type.String({ minLength: 1 }),
89
- phase: Type.Optional(Type.String()),
90
- iteration: Type.Optional(Type.Integer({ minimum: 1 })),
91
- startTimestamp: Type.String({ format: "date-time" }),
92
- endTimestamp: Type.String({ format: "date-time" }),
93
- durationMinutes: Type.Number({ minimum: 0 }),
94
- model: Type.String({ minLength: 1 }),
95
- provider: Type.String({ minLength: 1 }),
96
- });
97
- // ── Classification ───────────────────────────────────────────────────────
98
- /** Lowercase + collapse whitespace; safe for substring comparison. */
99
- function normalize(s) {
100
- return s.toLowerCase().replace(/\s+/g, " ").trim();
101
- }
102
- /**
103
- * Count the number of distinct declared workflow steps that appear in the
104
- * observed tool-call sequence (case-insensitive substring). A step that
105
- * matches multiple tool calls is counted once.
106
- */
107
- function countStepOverlap(steps, toolCalls) {
108
- if (steps.length === 0 || toolCalls.length === 0)
109
- return 0;
110
- const observed = toolCalls.map((c) => normalize(c.name));
111
- let hits = 0;
112
- for (const step of steps) {
113
- const needle = normalize(step);
114
- if (needle.length === 0)
115
- continue;
116
- if (observed.some((o) => o.includes(needle)))
117
- hits++;
118
- }
119
- return hits;
120
- }
121
- /**
122
- * Apply the FORGE-S24-T09 heuristic to a single retrieved skill against
123
- * the observed task trajectory. Pure function — no IO, no allocation
124
- * beyond return value.
125
- */
126
- export function classifySkillUsage(skill, trajectory) {
127
- const overlap = countStepOverlap(skill.workflowSteps, trajectory.toolCalls);
128
- const totalSteps = skill.workflowSteps.length;
129
- let rate = 0;
130
- if (totalSteps > 0) {
131
- const raw = overlap / totalSteps;
132
- if (!Number.isFinite(raw) || raw < 0)
133
- rate = 0;
134
- else if (raw > 1)
135
- rate = 1;
136
- else
137
- rate = raw;
138
- }
139
- // Signal 1: skill name appears in agent reasoning. The reasoning text is
140
- // normalised the same way as steps so casing/whitespace doesn't cause
141
- // false negatives.
142
- const reasoning = normalize(trajectory.reasoningText);
143
- const skillNeedle = normalize(skill.name);
144
- const mentionedInReasoning = skillNeedle.length > 0 && reasoning.includes(skillNeedle);
145
- // Signal 2: ≥2 distinct workflow steps overlapped with executed tool calls.
146
- const overlapStrong = overlap >= 2;
147
- if (overlapStrong) {
148
- return { used: true, signal: "overlap", tool_call_success_rate: rate };
149
- }
150
- if (mentionedInReasoning) {
151
- return { used: true, signal: "reasoning", tool_call_success_rate: rate };
152
- }
153
- return { used: false, signal: "none", tool_call_success_rate: rate };
154
- }
155
- // ── Emission ─────────────────────────────────────────────────────────────
156
- /** Compose an ISO-compact timestamp segment for the eventId. */
157
- function isoCompact(iso) {
158
- return iso.replace(/[-:.]/g, "").replace(/Z$/, "Z");
159
- }
160
- /**
161
- * Emit one `skill_usage` tracking event per retrieved skill via
162
- * `node <storeCli> emit <sprintId> <json>`. Each event carries the
163
- * classifier's `used` verdict and the observed `tool_call_success_rate`.
164
- *
165
- * `retrieval_score` is set to 0 — at tracking time, the score has already
166
- * been emitted in the T08 retrieval event; this follow-up captures the
167
- * usage signal only. (The schema requires the field to be present; it does
168
- * not require it to be re-derived per emission.)
169
- *
170
- * Never throws on subprocess failure — the failure is surfaced via the
171
- * returned counter and stderr text (IL7, explicit not silent).
172
- */
173
- export function emitSkillUsageTrackingEvents(retrieved, trajectory, runtime) {
174
- // FORGE-S24-T12 — gated rollout. Default off ⇒ no classification, no
175
- // events, no subprocess.
176
- if (!isSkillCurationEnabled(runtime.cwd)) {
177
- return { emitted: 0, failed: 0, stderrs: [] };
178
- }
179
- Value.Parse(EmitRuntimeSchema, runtime);
180
- Value.Parse(TaskTrajectorySchema, trajectory);
181
- for (const s of retrieved)
182
- Value.Parse(RetrievedSkillForTrackingSchema, s);
183
- let emitted = 0;
184
- let failed = 0;
185
- const stderrs = [];
186
- for (let i = 0; i < retrieved.length; i++) {
187
- const skill = retrieved[i];
188
- const verdict = classifySkillUsage(skill, trajectory);
189
- const eventId = `${isoCompact(runtime.startTimestamp)}_${runtime.taskId}_skill-usage-tracker_skill_usage_${i}_${skill.skillId}`;
190
- const event = {
191
- eventId,
192
- sprintId: runtime.sprintId,
193
- taskId: runtime.taskId,
194
- role: runtime.role,
195
- action: runtime.action,
196
- startTimestamp: runtime.startTimestamp,
197
- endTimestamp: runtime.endTimestamp,
198
- durationMinutes: runtime.durationMinutes,
199
- model: runtime.model,
200
- provider: runtime.provider,
201
- type: "skill_usage",
202
- skillId: skill.skillId,
203
- retrieved: true,
204
- used: verdict.used,
205
- tool_call_success_rate: verdict.tool_call_success_rate,
206
- retrieval_score: 0,
207
- };
208
- if (runtime.phase !== undefined)
209
- event.phase = runtime.phase;
210
- if (runtime.iteration !== undefined)
211
- event.iteration = runtime.iteration;
212
- // FORGE-S25-T17: adopt spawnStoreCliEmit; adds missing 10 s timeout (N-C-A).
213
- const emitResult = spawnStoreCliEmit(runtime.storeCli, runtime.sprintId, event, runtime.cwd);
214
- if (emitResult.ok) {
215
- emitted++;
216
- }
217
- else {
218
- failed++;
219
- stderrs.push(emitResult.stderr);
220
- }
221
- }
222
- return { emitted, failed, stderrs };
223
- }
224
- //# sourceMappingURL=skill-usage-tracker.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"skill-usage-tracker.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/skill-usage-tracker.ts"],"names":[],"mappings":"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"}
@@ -1,10 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- interface ParsedArgs {
3
- mode: "empty" | "file" | "text";
4
- seed: string;
5
- sourceLabel: string;
6
- }
7
- export declare function parseSprintIntakeArgs(rawArgs: string, cwd: string): ParsedArgs;
8
- export declare function composeKickoff(workflowMd: string, parsed: ParsedArgs): string;
9
- export declare function registerSprintIntake(pi: ExtensionAPI): void;
10
- export {};
@@ -1,91 +0,0 @@
1
- // forge:sprint-intake — LLM-driven kickoff handler (FORGE-BUG-031).
2
- //
3
- // Replaces the deterministic TUI flow shipped in FORGE-S19-T01. The native
4
- // handler is now a thin shim that:
5
- // 1. Parses argv: empty | "@<path>" (file ref) | free-form text
6
- // 2. Reads .forge/workflows/architect_sprint_intake.md
7
- // 3. Composes a kickoff message (workflow + seed input)
8
- // 4. Calls pi.sendUserMessage() to inject — LLM drives the interview using
9
- // forge_store, forge_ask_user, write, read, forge_collate tools.
10
- //
11
- // No multi-turn TUI, no checkpoints, no scripted-answers env var. The LLM
12
- // owns the conversation; the handler returns once the kickoff is queued.
13
- //
14
- // Persona/skill loading: this handler does not read `.forge/personas/` or
15
- // `.forge/skills/` directly — the LLM dereferences those via the workflow
16
- // text. Any future need for typed persona/skill access in this handler MUST
17
- // go through `./loaders/persona-skill-loader.ts` (FORGE-S20-T02). The smoke
18
- // gate enforces this with a grep assertion.
19
- import * as fs from "node:fs";
20
- import * as path from "node:path";
21
- export function parseSprintIntakeArgs(rawArgs, cwd) {
22
- const trimmed = (rawArgs ?? "").trim();
23
- if (!trimmed) {
24
- return { mode: "empty", seed: "", sourceLabel: "(no input — start interview)" };
25
- }
26
- if (trimmed.startsWith("@")) {
27
- const ref = trimmed.slice(1).trim();
28
- const filePath = path.isAbsolute(ref) ? ref : path.resolve(cwd, ref);
29
- const seed = fs.readFileSync(filePath, "utf8");
30
- return { mode: "file", seed, sourceLabel: `(seed from file: ${ref})` };
31
- }
32
- return { mode: "text", seed: trimmed, sourceLabel: "(seed from inline text)" };
33
- }
34
- // ── Kickoff composition ───────────────────────────────────────────────────
35
- export function composeKickoff(workflowMd, parsed) {
36
- const seedSection = parsed.mode === "empty"
37
- ? "\n## Input\n\n(no seed — begin interview now and elicit goals/scope/constraints conversationally)\n"
38
- : `\n## Input — ${parsed.sourceLabel}\n\n${parsed.seed.trim()}\n`;
39
- return [
40
- "# /forge:new-sprint",
41
- "",
42
- "Run the sprint-intake workflow below. Drive the interview conversationally — read referenced personas/skills/templates as needed, ask clarifying questions one at a time, and write `engineering/sprints/<SPRINT_ID>/SPRINT_REQUIREMENTS.md` plus the corresponding store records when ready. For the canonical sprint record, call the `forge_store` MCP tool with `{command:'write', args:['sprint','<json>']}` — 2-positional, id INSIDE json. Call `forge_store_template` first to get the json shape. Do NOT bash-shell `forge store ...`.",
43
- "",
44
- "---",
45
- "",
46
- "## Workflow",
47
- "",
48
- workflowMd.trim(),
49
- "",
50
- "---",
51
- seedSection,
52
- ].join("\n");
53
- }
54
- // ── Registration ──────────────────────────────────────────────────────────
55
- const WORKFLOW_REL_PATH = path.join(".forge", "workflows", "architect_sprint_intake.md");
56
- export function registerSprintIntake(pi) {
57
- pi.registerCommand("forge:new-sprint", {
58
- description: "Start an LLM-driven sprint-intake interview. " +
59
- "Usage: /forge:new-sprint [@<file> | <free-form text>]. " +
60
- "Empty args start a conversational interview from scratch.",
61
- async handler(args, ctx) {
62
- const cwd = process.cwd();
63
- const workflowPath = path.join(cwd, WORKFLOW_REL_PATH);
64
- if (!fs.existsSync(workflowPath)) {
65
- ctx.ui.notify(`forge:new-sprint — workflow not found at ${WORKFLOW_REL_PATH}; run /forge:init or /forge:rebuild first.`, "warning");
66
- return;
67
- }
68
- let parsed;
69
- try {
70
- parsed = parseSprintIntakeArgs(args, cwd);
71
- }
72
- catch (err) {
73
- const e = err;
74
- ctx.ui.notify(`forge:new-sprint — failed to read seed: ${e.message ?? "unknown"}`, "error");
75
- return;
76
- }
77
- let workflowMd;
78
- try {
79
- workflowMd = fs.readFileSync(workflowPath, "utf8");
80
- }
81
- catch (err) {
82
- const e = err;
83
- ctx.ui.notify(`forge:new-sprint — failed to read workflow: ${e.message ?? "unknown"}`, "error");
84
- return;
85
- }
86
- const kickoff = composeKickoff(workflowMd, parsed);
87
- pi.sendUserMessage(kickoff);
88
- },
89
- });
90
- }
91
- //# sourceMappingURL=sprint-intake.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sprint-intake.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/sprint-intake.ts"],"names":[],"mappings":"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"}
@@ -1,14 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- interface ParsedArgs {
3
- sprintId: string;
4
- seedMode: "none" | "file" | "text";
5
- seed: string;
6
- sourceLabel: string;
7
- }
8
- export declare function parseSprintPlanArgs(rawArgs: string, cwd: string): ParsedArgs;
9
- export declare function composeKickoff(workflowMd: string, parsed: ParsedArgs, requirements: {
10
- path: string;
11
- content: string;
12
- } | null): string;
13
- export declare function registerSprintPlan(pi: ExtensionAPI): void;
14
- export {};
@@ -1,122 +0,0 @@
1
- // forge:sprint-plan — LLM-driven kickoff handler (FORGE-BUG-032).
2
- //
3
- // Replaces the deterministic JSON-mode subagent call shipped in FORGE-S19-T02.
4
- // The handler is now a thin shim that:
5
- // 1. Parses argv: <SPRINT_ID> [@<file> | free-form text]
6
- // 2. Reads SPRINT_REQUIREMENTS.md (or REQUIREMENTS.md alias) when present
7
- // 3. Reads .forge/workflows/architect_sprint_plan.md
8
- // 4. Composes a kickoff message (workflow + requirements + optional seed)
9
- // 5. Calls pi.sendUserMessage() — LLM drives task decomposition with
10
- // forge_store write task per task, no rigid JSON contract.
11
- //
12
- // Persona/skill loading: this handler does not read `.forge/personas/` or
13
- // `.forge/skills/` directly — the LLM dereferences those via the workflow
14
- // text. Any future need for typed persona/skill access in this handler MUST
15
- // go through `./loaders/persona-skill-loader.ts` (FORGE-S20-T02). The smoke
16
- // gate enforces this with a grep assertion.
17
- import * as fs from "node:fs";
18
- import * as path from "node:path";
19
- export function parseSprintPlanArgs(rawArgs, cwd) {
20
- const trimmed = (rawArgs ?? "").trim();
21
- if (!trimmed) {
22
- throw new Error("sprint ID required — usage: /forge:plan-sprint <SPRINT_ID> [@<file> | <text>]");
23
- }
24
- const [sprintId, ...rest] = trimmed.split(/\s+/);
25
- const tail = rest.join(" ").trim();
26
- if (!tail) {
27
- return { sprintId, seedMode: "none", seed: "", sourceLabel: "" };
28
- }
29
- if (tail.startsWith("@")) {
30
- const ref = tail.slice(1).trim();
31
- const filePath = path.isAbsolute(ref) ? ref : path.resolve(cwd, ref);
32
- const seed = fs.readFileSync(filePath, "utf8");
33
- return { sprintId, seedMode: "file", seed, sourceLabel: `(seed from file: ${ref})` };
34
- }
35
- return { sprintId, seedMode: "text", seed: tail, sourceLabel: "(seed from inline text)" };
36
- }
37
- // ── Requirements lookup ───────────────────────────────────────────────────
38
- function resolveRequirements(cwd, sprintId) {
39
- const sprintDir = path.join(cwd, "engineering", "sprints", sprintId);
40
- for (const name of ["SPRINT_REQUIREMENTS.md", "REQUIREMENTS.md"]) {
41
- const p = path.join(sprintDir, name);
42
- try {
43
- const content = fs.readFileSync(p, "utf8");
44
- return { path: p, content };
45
- }
46
- catch {
47
- /* try next */
48
- }
49
- }
50
- return null;
51
- }
52
- // ── Kickoff composition ───────────────────────────────────────────────────
53
- export function composeKickoff(workflowMd, parsed, requirements) {
54
- const sections = [
55
- "# /forge:plan-sprint",
56
- "",
57
- `Decompose sprint ${parsed.sprintId} into tasks. Drive the planning conversationally per the workflow below — read referenced personas/skills, validate dependencies, and write each task by calling the \`forge_store\` MCP tool with \`{command:'write', args:['task','<json>']}\` (2-positional, id INSIDE json) once you and the user agree on the shape. Call \`forge_store_template\` first for the canonical task shape. Update the sprint record (same tool, args:['sprint','<json>']) with the final task ID list. Do not return a single bulk JSON array; commit tasks one at a time so the user can interject. Do NOT bash-shell \`forge store ...\`.`,
58
- "",
59
- "---",
60
- "",
61
- "## Workflow",
62
- "",
63
- workflowMd.trim(),
64
- "",
65
- "---",
66
- "",
67
- `## Sprint: ${parsed.sprintId}`,
68
- ];
69
- if (requirements) {
70
- sections.push("", `Requirements file: \`${path.relative(process.cwd(), requirements.path)}\``);
71
- sections.push("", "```markdown", requirements.content.trim(), "```");
72
- }
73
- else {
74
- sections.push("", `(no requirements file found at engineering/sprints/${parsed.sprintId}/SPRINT_REQUIREMENTS.md or REQUIREMENTS.md — ask the user to confirm intent or run /forge:new-sprint first)`);
75
- }
76
- if (parsed.seedMode !== "none") {
77
- sections.push("", `## Additional Input — ${parsed.sourceLabel}`, "", parsed.seed.trim());
78
- }
79
- return sections.join("\n");
80
- }
81
- // ── Registration ──────────────────────────────────────────────────────────
82
- const WORKFLOW_REL_PATH = path.join(".forge", "workflows", "architect_sprint_plan.md");
83
- export function registerSprintPlan(pi) {
84
- pi.registerCommand("forge:plan-sprint", {
85
- description: "Start an LLM-driven sprint task-decomposition session. " +
86
- "Usage: /forge:plan-sprint <SPRINT_ID> [@<file> | <free-form text>]. " +
87
- "Reads SPRINT_REQUIREMENTS.md (or REQUIREMENTS.md) from the sprint directory.",
88
- async handler(args, ctx) {
89
- const cwd = process.cwd();
90
- let parsed;
91
- try {
92
- parsed = parseSprintPlanArgs(args, cwd);
93
- }
94
- catch (err) {
95
- const e = err;
96
- ctx.ui.notify(`× forge:plan-sprint — ${e.message ?? "argv parse failed"}`, "error");
97
- return;
98
- }
99
- const workflowPath = path.join(cwd, WORKFLOW_REL_PATH);
100
- if (!fs.existsSync(workflowPath)) {
101
- ctx.ui.notify(`× forge:plan-sprint — workflow not found at ${WORKFLOW_REL_PATH}; run /forge:init or /forge:rebuild first.`, "error");
102
- return;
103
- }
104
- let workflowMd;
105
- try {
106
- workflowMd = fs.readFileSync(workflowPath, "utf8");
107
- }
108
- catch (err) {
109
- const e = err;
110
- ctx.ui.notify(`× forge:plan-sprint — failed to read workflow: ${e.message ?? "unknown"}`, "error");
111
- return;
112
- }
113
- const requirements = resolveRequirements(cwd, parsed.sprintId);
114
- if (!requirements) {
115
- ctx.ui.notify(`△ forge:plan-sprint — no SPRINT_REQUIREMENTS.md found at engineering/sprints/${parsed.sprintId}/. Continuing; LLM will ask you for context.`, "warning");
116
- }
117
- const kickoff = composeKickoff(workflowMd, parsed, requirements);
118
- pi.sendUserMessage(kickoff);
119
- },
120
- });
121
- }
122
- //# sourceMappingURL=sprint-plan.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sprint-plan.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/sprint-plan.ts"],"names":[],"mappings":"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"}
@@ -1,19 +0,0 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- export interface StatusResult {
3
- lines: string[];
4
- hasActiveSprint: boolean;
5
- }
6
- /**
7
- * Read active sprint and its tasks from store-cli.
8
- * Returns formatted text lines for display via ctx.ui.notify.
9
- * Exported so orchestrators can call directly without going through sendUserMessage.
10
- */
11
- export declare function runStatus(forgeRoot: string, cwd: string): Promise<StatusResult>;
12
- /**
13
- * Register /forge:status (v0 native handler).
14
- * forgeRoot is nullable — null means we're outside a Forge project.
15
- */
16
- export declare function registerStatusCommand(pi: ExtensionAPI, opts: {
17
- forgeRoot: string | null;
18
- }): void;
19
- export declare function readForgeRootFromConfig(cwd: string): string | null;
@@ -1,140 +0,0 @@
1
- // status-command.ts — native v0 handler for /forge:status (FORGE-S23-T10)
2
- //
3
- // Replaces the delegateMarkdownCommand stub that previously lived in forge-commands.ts.
4
- // Reads the active sprint from store and lists tasks with their statuses.
5
- // No TUI — text-only widget via ctx.ui.notify.
6
- //
7
- // Archetype: Atomic (no LLM in loop — pack-04 §Atomic).
8
- // Iron Laws:
9
- // IL1 — new code lives in forge-cli/ only
10
- // IL6 — no shell-string interpolation (argv-array throughout)
11
- // IL7 — silent continuation past failures is never acceptable
12
- import { execFile } from "node:child_process";
13
- import * as fs from "node:fs";
14
- import * as path from "node:path";
15
- import { promisify } from "node:util";
16
- import { resolveToolDir } from "./store-resolver.js";
17
- const execFileAsync = promisify(execFile);
18
- /**
19
- * Read active sprint and its tasks from store-cli.
20
- * Returns formatted text lines for display via ctx.ui.notify.
21
- * Exported so orchestrators can call directly without going through sendUserMessage.
22
- */
23
- export async function runStatus(forgeRoot, cwd) {
24
- const toolDir = resolveToolDir(forgeRoot);
25
- const storeCliPath = path.join(toolDir, "store-cli.cjs");
26
- // Step 1: list all sprints
27
- let listResult;
28
- try {
29
- const out = await execFileAsync("node", [storeCliPath, "query", "--list-sprints"], {
30
- cwd,
31
- encoding: "utf8",
32
- timeout: 10_000,
33
- });
34
- listResult = JSON.parse(out.stdout);
35
- }
36
- catch (err) {
37
- const msg = err instanceof Error ? err.message : String(err);
38
- return {
39
- lines: [`× forge:status — could not list sprints: ${msg}`],
40
- hasActiveSprint: false,
41
- };
42
- }
43
- // Step 2: filter active sprints, sort by ID descending (proxy for recency)
44
- const activeSprints = (listResult.results ?? [])
45
- .filter((s) => s.status === "active")
46
- .sort((a, b) => b.id.localeCompare(a.id));
47
- if (activeSprints.length === 0) {
48
- return {
49
- lines: [
50
- "forge:status — No active sprint found.",
51
- 'Run /forge:init to initialise a project, or check your store with /forge:search "--list-sprints".',
52
- ],
53
- hasActiveSprint: false,
54
- };
55
- }
56
- // Use the most recent active sprint
57
- const sprint = activeSprints[0];
58
- // Step 3: fetch tasks for this sprint
59
- let sprintResult;
60
- try {
61
- const out = await execFileAsync("node", [storeCliPath, "query", "--sprint", sprint.id], {
62
- cwd,
63
- encoding: "utf8",
64
- timeout: 10_000,
65
- });
66
- sprintResult = JSON.parse(out.stdout);
67
- }
68
- catch (err) {
69
- const msg = err instanceof Error ? err.message : String(err);
70
- return {
71
- lines: [`× forge:status — could not fetch tasks for sprint ${sprint.id}: ${msg}`],
72
- hasActiveSprint: true,
73
- };
74
- }
75
- const tasks = sprintResult.results ?? [];
76
- // Step 4: format output
77
- const lines = [
78
- `Sprint: ${sprint.id}${sprint.title ? ` — ${sprint.title}` : ""}`,
79
- `Tasks: ${tasks.length}`,
80
- "",
81
- ];
82
- if (tasks.length === 0) {
83
- lines.push(" (no tasks found)");
84
- }
85
- else {
86
- for (const task of tasks) {
87
- const status = task.status ?? "unknown";
88
- const title = task.title ? ` — ${task.title.slice(0, 60)}` : "";
89
- lines.push(` ${task.id} [${status}]${title}`);
90
- }
91
- }
92
- if (activeSprints.length > 1) {
93
- lines.push("");
94
- lines.push(` Note: ${activeSprints.length - 1} other active sprint(s) found. Showing most recent (${sprint.id}).`);
95
- }
96
- return { lines, hasActiveSprint: true };
97
- }
98
- // ── Register ─────────────────────────────────────────────────────────────────
99
- /**
100
- * Register /forge:status (v0 native handler).
101
- * forgeRoot is nullable — null means we're outside a Forge project.
102
- */
103
- export function registerStatusCommand(pi, opts) {
104
- pi.registerCommand("forge:status", {
105
- description: "Sprint and task summary widget — lists active sprint and task statuses.",
106
- async handler(_args, ctx) {
107
- const cwd = process.cwd();
108
- // Outside-project guard
109
- if (!opts.forgeRoot) {
110
- ctx.ui.notify("× forge:status — not inside a Forge project. Run /forge:init first.", "error");
111
- return;
112
- }
113
- ctx.ui.setStatus?.("forge:status", "Loading sprint status…");
114
- const result = await runStatus(opts.forgeRoot, cwd);
115
- ctx.ui.setStatus?.("forge:status", undefined);
116
- for (const line of result.lines) {
117
- const severity = line.startsWith("×") ? "error" : "info";
118
- ctx.ui.notify(line, severity);
119
- }
120
- },
121
- });
122
- }
123
- // ── Outside-project config guard helper ──────────────────────────────────────
124
- // Not used above because forgeRoot is passed in at register time, but exported
125
- // for use in tests that need to verify the guard path.
126
- export function readForgeRootFromConfig(cwd) {
127
- try {
128
- const configPath = path.join(cwd, ".forge", "config.json");
129
- const raw = fs.readFileSync(configPath, "utf8");
130
- const cfg = JSON.parse(raw);
131
- const paths = cfg.paths;
132
- if (paths && typeof paths.forgeRoot === "string")
133
- return paths.forgeRoot;
134
- }
135
- catch {
136
- // fall through
137
- }
138
- return null;
139
- }
140
- //# sourceMappingURL=status-command.js.map