@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,246 @@
1
+ // Skill retriever — FORGE-S24-T08.
2
+ //
3
+ // BM25-ranked retrieval over the project's skill corpus using `lunr.js`
4
+ // (exact-pinned in package.json). Each retrieval emits one `skill_usage`
5
+ // event per top-k hit with `retrieved: true` and a populated, [0,1]-clamped
6
+ // `retrieval_score`. Conforms to the canonical event schema variant landed
7
+ // in FORGE-S24-T01 (plugin v0.45.0).
8
+ //
9
+ // Iron Laws (forge-cli-engineer):
10
+ // IL2 — TypeScript + TypeBox. Schemas defined here; runtime input is
11
+ // validated by `Value.Parse(...)` at the public entry points.
12
+ // IL6 — No raw shell-string interpolation. `emitSkillUsageEvents` calls
13
+ // `spawnSync("node", [storeCli, "emit", sprintId, JSON.stringify(event)])`
14
+ // — argv array, no shell.
15
+ // IL7 — No silent continuation past failures. A non-zero `store-cli emit`
16
+ // exit increments the `failed` counter and the stderr text is
17
+ // forwarded to the caller in the returned result; nothing is
18
+ // swallowed.
19
+ // IL10 — This module is a primitive consumed by orchestrator code. The
20
+ // orchestrator/caller supplies the runtime attribution
21
+ // (model/provider/timestamps/durationMinutes) — never fabricated
22
+ // here.
23
+ //
24
+ // Scope boundary: this module owns ranking + emission. It does NOT crawl
25
+ // the filesystem for skills; corpus construction (reading SKILL.md
26
+ // frontmatter via `loaders/persona-skill-loader.ts`) is the caller's
27
+ // responsibility. Keeping IO out of the retriever keeps the module
28
+ // deterministic and trivially unit-testable.
29
+ import { spawnSync } from "node:child_process";
30
+ import lunr from "lunr";
31
+ import { Type } from "typebox";
32
+ import { Value } from "typebox/value";
33
+ import { isSkillCurationEnabled } from "./skill-curation-flag.js";
34
+ // ── Public schemas ───────────────────────────────────────────────────────
35
+ /**
36
+ * One skill in the retrieval corpus. `frontmatter` is the parsed YAML head
37
+ * of the SKILL.md file (matches `loaders/persona-skill-loader.ts` Skill shape
38
+ * for `frontmatter` field). Only `description` plus selected frontmatter
39
+ * fields are indexed — the body is intentionally excluded to keep the index
40
+ * small and keyword-focused.
41
+ */
42
+ export const RetrievableSkillSchema = Type.Object({
43
+ skillId: Type.String({ minLength: 1 }),
44
+ name: Type.String({ minLength: 1 }),
45
+ description: Type.String(),
46
+ frontmatter: Type.Record(Type.String(), Type.Unknown()),
47
+ });
48
+ export const RetrievalHitSchema = Type.Object({
49
+ skillId: Type.String({ minLength: 1 }),
50
+ score: Type.Number(),
51
+ });
52
+ /**
53
+ * Runtime attribution supplied by the caller (orchestrator).
54
+ * These are NEVER fabricated inside the retriever (IL10).
55
+ */
56
+ export const EmitRuntimeSchema = Type.Object({
57
+ storeCli: Type.String({ minLength: 1 }),
58
+ cwd: Type.String({ minLength: 1 }),
59
+ sprintId: Type.String({ minLength: 1 }),
60
+ taskId: Type.String({ minLength: 1 }),
61
+ role: Type.String({ minLength: 1 }),
62
+ action: Type.String({ minLength: 1 }),
63
+ phase: Type.Optional(Type.String()),
64
+ iteration: Type.Optional(Type.Integer({ minimum: 1 })),
65
+ startTimestamp: Type.String({ format: "date-time" }),
66
+ endTimestamp: Type.String({ format: "date-time" }),
67
+ durationMinutes: Type.Number({ minimum: 0 }),
68
+ model: Type.String({ minLength: 1 }),
69
+ provider: Type.String({ minLength: 1 }),
70
+ });
71
+ /** Join indexable frontmatter fields (tags + name) into a single string. */
72
+ function frontmatterText(fm) {
73
+ const parts = [];
74
+ if (typeof fm.name === "string")
75
+ parts.push(fm.name);
76
+ const tags = fm.tags;
77
+ if (Array.isArray(tags)) {
78
+ for (const t of tags)
79
+ if (typeof t === "string")
80
+ parts.push(t);
81
+ }
82
+ const aliases = fm.aliases;
83
+ if (Array.isArray(aliases)) {
84
+ for (const a of aliases)
85
+ if (typeof a === "string")
86
+ parts.push(a);
87
+ }
88
+ return parts.join(" ");
89
+ }
90
+ /**
91
+ * Build a BM25 index over the supplied corpus. Indexed fields:
92
+ * - `name` (boost 4) — the skill identifier itself
93
+ * - `description` (boost 2) — primary intent text
94
+ * - `frontmatter` (boost 1) — tags / aliases
95
+ *
96
+ * lunr's default scoring is Okapi BM25.
97
+ */
98
+ export function buildSkillIndex(corpus) {
99
+ for (const s of corpus)
100
+ Value.Parse(RetrievableSkillSchema, s);
101
+ const byRef = new Map();
102
+ for (const s of corpus)
103
+ byRef.set(s.skillId, s);
104
+ const index = lunr(function () {
105
+ this.ref("skillId");
106
+ this.field("name", { boost: 4 });
107
+ this.field("description", { boost: 2 });
108
+ this.field("frontmatter", { boost: 1 });
109
+ // Determinism: lunr's pipeline is deterministic by construction; we do
110
+ // not register custom plugins or randomised tokenisers.
111
+ for (const s of corpus) {
112
+ this.add({
113
+ skillId: s.skillId,
114
+ name: s.name,
115
+ description: s.description,
116
+ frontmatter: frontmatterText(s.frontmatter),
117
+ });
118
+ }
119
+ });
120
+ return { _index: index, _byRef: byRef };
121
+ }
122
+ // ── Retrieval ────────────────────────────────────────────────────────────
123
+ /**
124
+ * Tokenise the query for lunr. We lower-case, strip non-alphanumerics, and
125
+ * drop empties. Avoids lunr's query-language operators (`:`, `^`, `~`,
126
+ * `+`, `-`, `*`) leaking from natural-language task descriptions.
127
+ */
128
+ function safeQueryTokens(query) {
129
+ return query
130
+ .toLowerCase()
131
+ .replace(/[^a-z0-9\s]+/g, " ")
132
+ .split(/\s+/)
133
+ .filter((t) => t.length > 0);
134
+ }
135
+ /**
136
+ * Return the top-k skills by BM25 score for the given query. Deterministic
137
+ * for fixed (corpus, query) pairs. Returns `[]` when no token in the query
138
+ * is present in any indexed field.
139
+ */
140
+ export function retrieveTopK(index, query, k) {
141
+ if (!Number.isInteger(k) || k <= 0) {
142
+ throw new Error(`retrieveTopK: k must be a positive integer, got ${k}`);
143
+ }
144
+ const tokens = safeQueryTokens(query);
145
+ if (tokens.length === 0)
146
+ return [];
147
+ let results;
148
+ try {
149
+ results = index._index.query((q) => {
150
+ for (const t of tokens) {
151
+ q.term(t, { usePipeline: true, boost: 1 });
152
+ }
153
+ });
154
+ }
155
+ catch {
156
+ // lunr throws on a small set of malformed inputs; treat as no hits
157
+ // rather than propagating an opaque error into the orchestrator.
158
+ return [];
159
+ }
160
+ const hits = [];
161
+ for (const r of results) {
162
+ if (hits.length >= k)
163
+ break;
164
+ if (!index._byRef.has(r.ref))
165
+ continue;
166
+ hits.push({ skillId: r.ref, score: r.score });
167
+ }
168
+ return hits;
169
+ }
170
+ // ── Emission ─────────────────────────────────────────────────────────────
171
+ /**
172
+ * Clamp a raw BM25 score into the [0,1] window required by the event
173
+ * schema (`retrieval_score`). BM25 scores are unbounded above; we apply
174
+ * `s / (1 + s)` so the relative ordering is preserved while the absolute
175
+ * value is monotonically squashed into (0,1). Negative scores (lunr never
176
+ * produces these in practice) clamp to 0.
177
+ */
178
+ export function clampRetrievalScore(rawScore) {
179
+ if (!Number.isFinite(rawScore) || rawScore <= 0)
180
+ return 0;
181
+ const squashed = rawScore / (1 + rawScore);
182
+ if (squashed >= 1)
183
+ return 1;
184
+ return squashed;
185
+ }
186
+ /** Compose an ISO-compact timestamp segment for the eventId. */
187
+ function isoCompact(iso) {
188
+ return iso.replace(/[-:.]/g, "").replace(/Z$/, "Z");
189
+ }
190
+ /**
191
+ * Emit one `skill_usage` event per supplied hit via
192
+ * `node <storeCli> emit <sprintId> <json>`. Returns the (emitted, failed)
193
+ * counts. Never throws on subprocess failure — the failure is surfaced via
194
+ * the returned counter and stderr text (IL7 — explicit, not silent).
195
+ */
196
+ export function emitSkillUsageEvents(hits, runtime) {
197
+ // FORGE-S24-T12 — gated rollout. Default off ⇒ zero events, zero
198
+ // subprocess calls, byte-identical to pre-FORGE-S24 behaviour.
199
+ if (!isSkillCurationEnabled(runtime.cwd)) {
200
+ return { emitted: 0, failed: 0, stderrs: [] };
201
+ }
202
+ Value.Parse(EmitRuntimeSchema, runtime);
203
+ let emitted = 0;
204
+ let failed = 0;
205
+ const stderrs = [];
206
+ for (let i = 0; i < hits.length; i++) {
207
+ const hit = hits[i];
208
+ const eventId = `${isoCompact(runtime.startTimestamp)}_${runtime.taskId}_skill-retriever_skill_usage_${i}_${hit.skillId}`;
209
+ const event = {
210
+ eventId,
211
+ sprintId: runtime.sprintId,
212
+ taskId: runtime.taskId,
213
+ role: runtime.role,
214
+ action: runtime.action,
215
+ startTimestamp: runtime.startTimestamp,
216
+ endTimestamp: runtime.endTimestamp,
217
+ durationMinutes: runtime.durationMinutes,
218
+ model: runtime.model,
219
+ provider: runtime.provider,
220
+ type: "skill_usage",
221
+ skillId: hit.skillId,
222
+ retrieved: true,
223
+ used: false,
224
+ // No tool calls have happened yet at retrieval time. T09 (usage
225
+ // tracker) will later emit a follow-up event with the observed
226
+ // success rate.
227
+ tool_call_success_rate: 0,
228
+ retrieval_score: clampRetrievalScore(hit.score),
229
+ };
230
+ if (runtime.phase !== undefined)
231
+ event.phase = runtime.phase;
232
+ if (runtime.iteration !== undefined)
233
+ event.iteration = runtime.iteration;
234
+ const result = spawnSync("node", [runtime.storeCli, "emit", runtime.sprintId, JSON.stringify(event)], { cwd: runtime.cwd, encoding: "utf8" });
235
+ if (result.status === 0) {
236
+ emitted++;
237
+ }
238
+ else {
239
+ failed++;
240
+ const stderr = typeof result.stderr === "string" ? result.stderr : "";
241
+ stderrs.push(stderr);
242
+ }
243
+ }
244
+ return { emitted, failed, stderrs };
245
+ }
246
+ //# sourceMappingURL=skill-retriever.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-retriever.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/skill-retriever.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ import { type Static, Type } from "typebox";
2
+ /**
3
+ * One skill that was previously retrieved (T08) and is now being evaluated
4
+ * for actual usage. `workflowSteps` is the list of declared step / tool /
5
+ * action names the skill's SKILL.md (or workflow doc) advertises — these
6
+ * are what we diff the trajectory against.
7
+ *
8
+ * Step matching is case-insensitive substring: a tool call's `name` matches
9
+ * a workflow step if the step (after lowercasing + whitespace collapse)
10
+ * appears anywhere in the tool call's name. This is intentionally permissive
11
+ * — declared steps are human-authored phrases ("store-cli query"), and tool
12
+ * call names may be qualified (`mcp__store__store-cli-query`). Misses bias
13
+ * toward `used: false`, which is the safer of the two error modes for a
14
+ * curation signal.
15
+ */
16
+ export declare const RetrievedSkillForTrackingSchema: Type.TObject<{
17
+ skillId: Type.TString;
18
+ name: Type.TString;
19
+ workflowSteps: Type.TArray<Type.TString>;
20
+ }>;
21
+ export type RetrievedSkillForTracking = Static<typeof RetrievedSkillForTrackingSchema>;
22
+ /** One observed tool invocation in the task's trajectory. */
23
+ export declare const ToolCallObservationSchema: Type.TObject<{
24
+ name: Type.TString;
25
+ }>;
26
+ export type ToolCallObservation = Static<typeof ToolCallObservationSchema>;
27
+ /**
28
+ * The observable trajectory of a closed task: the sequence of tool calls
29
+ * the agent made, plus the concatenated reasoning / assistant text the
30
+ * orchestrator captured. Both inputs are supplied by the caller — this
31
+ * module does not read transcripts itself.
32
+ */
33
+ export declare const TaskTrajectorySchema: Type.TObject<{
34
+ toolCalls: Type.TArray<Type.TObject<{
35
+ name: Type.TString;
36
+ }>>;
37
+ reasoningText: Type.TString;
38
+ }>;
39
+ export type TaskTrajectory = Static<typeof TaskTrajectorySchema>;
40
+ /**
41
+ * Runtime attribution supplied by the caller (orchestrator).
42
+ * These are NEVER fabricated inside the tracker (IL10).
43
+ */
44
+ export declare const EmitRuntimeSchema: Type.TObject<{
45
+ storeCli: Type.TString;
46
+ cwd: Type.TString;
47
+ sprintId: Type.TString;
48
+ taskId: Type.TString;
49
+ role: Type.TString;
50
+ action: Type.TString;
51
+ phase: Type.TOptional<Type.TString>;
52
+ iteration: Type.TOptional<Type.TInteger>;
53
+ startTimestamp: Type.TString;
54
+ endTimestamp: Type.TString;
55
+ durationMinutes: Type.TNumber;
56
+ model: Type.TString;
57
+ provider: Type.TString;
58
+ }>;
59
+ export type EmitRuntime = Static<typeof EmitRuntimeSchema>;
60
+ export interface EmitResult {
61
+ emitted: number;
62
+ failed: number;
63
+ stderrs: string[];
64
+ }
65
+ /** Why the classifier ruled `used: true`, or `"none"` when it ruled false. */
66
+ export type UsageSignal = "overlap" | "reasoning" | "none";
67
+ export interface UsageVerdict {
68
+ used: boolean;
69
+ signal: UsageSignal;
70
+ tool_call_success_rate: number;
71
+ }
72
+ /**
73
+ * Apply the FORGE-S24-T09 heuristic to a single retrieved skill against
74
+ * the observed task trajectory. Pure function — no IO, no allocation
75
+ * beyond return value.
76
+ */
77
+ export declare function classifySkillUsage(skill: RetrievedSkillForTracking, trajectory: TaskTrajectory): UsageVerdict;
78
+ /**
79
+ * Emit one `skill_usage` tracking event per retrieved skill via
80
+ * `node <storeCli> emit <sprintId> <json>`. Each event carries the
81
+ * classifier's `used` verdict and the observed `tool_call_success_rate`.
82
+ *
83
+ * `retrieval_score` is set to 0 — at tracking time, the score has already
84
+ * been emitted in the T08 retrieval event; this follow-up captures the
85
+ * usage signal only. (The schema requires the field to be present; it does
86
+ * not require it to be re-derived per emission.)
87
+ *
88
+ * Never throws on subprocess failure — the failure is surfaced via the
89
+ * returned counter and stderr text (IL7, explicit not silent).
90
+ */
91
+ export declare function emitSkillUsageTrackingEvents(retrieved: readonly RetrievedSkillForTracking[], trajectory: TaskTrajectory, runtime: EmitRuntime): EmitResult;
@@ -0,0 +1,224 @@
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 { spawnSync } from "node:child_process";
41
+ import { Type } from "typebox";
42
+ import { Value } from "typebox/value";
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
+ const result = spawnSync("node", [runtime.storeCli, "emit", runtime.sprintId, JSON.stringify(event)], { cwd: runtime.cwd, encoding: "utf8" });
213
+ if (result.status === 0) {
214
+ emitted++;
215
+ }
216
+ else {
217
+ failed++;
218
+ const stderr = typeof result.stderr === "string" ? result.stderr : "";
219
+ stderrs.push(stderr);
220
+ }
221
+ }
222
+ return { emitted, failed, stderrs };
223
+ }
224
+ //# sourceMappingURL=skill-usage-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-usage-tracker.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/skill-usage-tracker.ts"],"names":[],"mappings":"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"}
@@ -36,3 +36,21 @@ export declare function suffixMatch(toolDir: string, cwd: string, kind: "task" |
36
36
  * 5. NLP fallback → store-cli nlp "<query>"
37
37
  */
38
38
  export declare function resolveEntityRef(arg: string, toolDir: string, cwd: string, opts?: ResolveOptions): Promise<ResolverHit | null>;
39
+ export interface ResolveToCanonicalIdOptions {
40
+ /** Which entity types to search. Defaults to a single-element set matching `kind`. */
41
+ entityTypes?: Set<string>;
42
+ /** Command label used in error messages (e.g. "forge:run-task"). */
43
+ commandLabel?: string;
44
+ }
45
+ /**
46
+ * Resolve a raw user arg to a canonical entity ID string.
47
+ *
48
+ * 1. If the arg is already a canonical ID that resolves directly → return it.
49
+ * 2. If the arg is an unprefixed ID (e.g. "S22-T03") → suffix-match or
50
+ * prefix-normalize to the canonical form → return it.
51
+ * 3. If the arg is ambiguous → prompt the user (or hard-fail in non-interactive).
52
+ * 4. If the arg cannot be resolved → emit an actionable error and return null.
53
+ */
54
+ export declare function resolveToCanonicalId(arg: string, toolDir: string, cwd: string, kind: "task" | "sprint" | "bug" | "feature", opts: ResolveToCanonicalIdOptions & {
55
+ ctx?: ExtensionCommandContext;
56
+ }): Promise<string | null>;