@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.
- package/CHANGELOG.md +324 -0
- package/README.md +2 -1
- package/dist/CHANGELOG-forge-plugin.md +210 -0
- package/dist/bin/forge.js +20 -1
- package/dist/bin/forge.js.map +1 -1
- package/dist/extensions/forgecli/ask-user-tool.js +32 -20
- package/dist/extensions/forgecli/ask-user-tool.js.map +1 -1
- package/dist/extensions/forgecli/config-layer.d.ts +15 -0
- package/dist/extensions/forgecli/config-layer.js +4 -1
- package/dist/extensions/forgecli/config-layer.js.map +1 -1
- package/dist/extensions/forgecli/config-writer.js +4 -1
- package/dist/extensions/forgecli/config-writer.js.map +1 -1
- package/dist/extensions/forgecli/enhance.js +1 -1
- package/dist/extensions/forgecli/enhance.js.map +1 -1
- package/dist/extensions/forgecli/fix-bug.js +31 -1
- package/dist/extensions/forgecli/fix-bug.js.map +1 -1
- package/dist/extensions/forgecli/forge-cli-schema.json +19 -0
- package/dist/extensions/forgecli/forge-tools.js +80 -0
- package/dist/extensions/forgecli/forge-tools.js.map +1 -1
- package/dist/extensions/forgecli/forge-update-command.js +24 -18
- package/dist/extensions/forgecli/forge-update-command.js.map +1 -1
- package/dist/extensions/forgecli/friction-emit.d.ts +97 -0
- package/dist/extensions/forgecli/friction-emit.js +246 -0
- package/dist/extensions/forgecli/friction-emit.js.map +1 -0
- package/dist/extensions/forgecli/health-check.d.ts +10 -0
- package/dist/extensions/forgecli/health-check.js +160 -8
- package/dist/extensions/forgecli/health-check.js.map +1 -1
- package/dist/extensions/forgecli/hook-dispatcher.js +24 -2
- package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
- package/dist/extensions/forgecli/hooks/write-guard.js +5 -1
- package/dist/extensions/forgecli/hooks/write-guard.js.map +1 -1
- package/dist/extensions/forgecli/index.js +29 -5
- package/dist/extensions/forgecli/index.js.map +1 -1
- package/dist/extensions/forgecli/lib/store-error-remediation.d.ts +65 -0
- package/dist/extensions/forgecli/lib/store-error-remediation.js +298 -0
- package/dist/extensions/forgecli/lib/store-error-remediation.js.map +1 -0
- package/dist/extensions/forgecli/regenerate.d.ts +22 -0
- package/dist/extensions/forgecli/regenerate.js +133 -3
- package/dist/extensions/forgecli/regenerate.js.map +1 -1
- package/dist/extensions/forgecli/run-sprint.js +16 -1
- package/dist/extensions/forgecli/run-sprint.js.map +1 -1
- package/dist/extensions/forgecli/run-task.js +30 -8
- package/dist/extensions/forgecli/run-task.js.map +1 -1
- package/dist/extensions/forgecli/skill-curation-flag.d.ts +21 -0
- package/dist/extensions/forgecli/skill-curation-flag.js +71 -0
- package/dist/extensions/forgecli/skill-curation-flag.js.map +1 -0
- package/dist/extensions/forgecli/skill-curator-subagent.d.ts +101 -0
- package/dist/extensions/forgecli/skill-curator-subagent.js +342 -0
- package/dist/extensions/forgecli/skill-curator-subagent.js.map +1 -0
- package/dist/extensions/forgecli/skill-retriever.d.ts +84 -0
- package/dist/extensions/forgecli/skill-retriever.js +246 -0
- package/dist/extensions/forgecli/skill-retriever.js.map +1 -0
- package/dist/extensions/forgecli/skill-usage-tracker.d.ts +91 -0
- package/dist/extensions/forgecli/skill-usage-tracker.js +224 -0
- package/dist/extensions/forgecli/skill-usage-tracker.js.map +1 -0
- package/dist/extensions/forgecli/store-resolver.d.ts +18 -0
- package/dist/extensions/forgecli/store-resolver.js +44 -4
- package/dist/extensions/forgecli/store-resolver.js.map +1 -1
- package/dist/extensions/forgecli/store-validator.d.ts +3 -0
- package/dist/extensions/forgecli/store-validator.js +4 -2
- package/dist/extensions/forgecli/store-validator.js.map +1 -1
- package/dist/forge-payload/.base-pack/personas/supervisor.md +9 -0
- package/dist/forge-payload/.base-pack/workflows/enhance.md +344 -18
- package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
- package/dist/forge-payload/.schemas/event.schema.json +20 -2
- package/dist/forge-payload/.schemas/migrations.json +112 -0
- package/dist/forge-payload/.schemas/proposal.schema.json +40 -0
- package/dist/forge-payload/agents/store-query-validator.md +103 -0
- package/dist/forge-payload/agents/tomoshibi.md +185 -0
- package/dist/forge-payload/commands/regenerate.md +109 -20
- package/dist/forge-payload/hooks/check-update.js +378 -0
- package/dist/forge-payload/hooks/forge-permissions.js +158 -0
- package/dist/forge-payload/hooks/triage-error.js +71 -0
- package/dist/forge-payload/hooks/validate-write.js +236 -0
- package/dist/forge-payload/integrity.json +32 -0
- package/dist/forge-payload/meta/workflows/meta-enhance.md +344 -18
- package/dist/forge-payload/schemas/structure-manifest.json +511 -0
- package/dist/forge-payload/tools/build-persona-pack.cjs +120 -11
- package/dist/forge-payload/tools/compression-gate.cjs +192 -0
- package/dist/forge-payload/tools/delete-candidate-detector.cjs +114 -0
- package/dist/forge-payload/tools/judge-proposal.cjs +177 -0
- package/dist/forge-payload/tools/manage-versions.cjs +132 -4
- package/dist/forge-payload/tools/queue-drain.cjs +152 -0
- package/dist/forge-payload/tools/replay-scoring.cjs +117 -0
- package/node_modules/@mariozechner/clipboard/package.json +2 -1
- package/node_modules/@mariozechner/clipboard-linux-x64-musl/README.md +3 -0
- package/node_modules/@mariozechner/clipboard-linux-x64-musl/clipboard.linux-x64-musl.node +0 -0
- package/node_modules/@mariozechner/clipboard-linux-x64-musl/package.json +25 -0
- 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>;
|