agent-afk 3.80.3 → 3.80.5
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/LICENSE +201 -80
- package/NOTICE +16 -0
- package/README.md +14 -3
- package/dist/bundled-plugins/awa-bundled/.claude-plugin/plugin.json +5 -0
- package/dist/bundled-plugins/awa-bundled/bundled.test.ts +403 -0
- package/dist/bundled-plugins/awa-bundled/skills/contract/SKILL.md +34 -0
- package/dist/bundled-plugins/awa-bundled/skills/devils-advocate/SKILL.md +46 -0
- package/dist/bundled-plugins/awa-bundled/skills/gather/SKILL.md +42 -0
- package/dist/bundled-plugins/awa-bundled/skills/ground-claim/SKILL.md +54 -0
- package/dist/bundled-plugins/awa-bundled/skills/ground-state/SKILL.md +51 -0
- package/dist/bundled-plugins/awa-bundled/skills/intent-lock/SKILL.md +99 -0
- package/dist/bundled-plugins/awa-bundled/skills/parallelize/SKILL.md +9 -0
- package/dist/bundled-plugins/awa-bundled/skills/refactor/SKILL.md +154 -0
- package/dist/bundled-plugins/awa-bundled/skills/research/SKILL.md +33 -0
- package/dist/bundled-plugins/awa-bundled/skills/review/SKILL.md +104 -0
- package/dist/bundled-plugins/awa-bundled/skills/shadow-verify/SKILL.md +38 -0
- package/dist/bundled-plugins/awa-bundled/skills/ship/SKILL.md +128 -0
- package/dist/bundled-plugins/awa-bundled/skills/simplify/SKILL.md +123 -0
- package/dist/bundled-plugins/awa-bundled/skills/spec/SKILL.md +29 -0
- package/dist/cli.mjs +1 -1
- package/package.json +12 -3
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { join, dirname } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
|
|
10
|
+
// Pinned hashes for the 12 bundled skills shipped under awa-bundled/. These
|
|
11
|
+
// files mirror — but are NOT byte-equal to — corresponding skills in the
|
|
12
|
+
// upstream awa-private repo. Permanent intentional differences include:
|
|
13
|
+
//
|
|
14
|
+
// - Namespace prefixes (`/awa-dev:contract` upstream → `/contract` here)
|
|
15
|
+
// - Sub-agent dispatch identifiers (`awa-private:research-agent` → `research-agent`)
|
|
16
|
+
// - Occasional wording divergence between maintainers
|
|
17
|
+
//
|
|
18
|
+
// Because byte-equality is a false invariant, this file enforces only the
|
|
19
|
+
// pinned-hash snapshot: any unauthored edit to a bundled SKILL.md fails the
|
|
20
|
+
// test until the developer explicitly bumps the hash here. That bump is the
|
|
21
|
+
// forcing function for cross-repo discipline:
|
|
22
|
+
//
|
|
23
|
+
// *** Workflow when bumping a pinned hash ***
|
|
24
|
+
// 1. Identify what changed in the bundled SKILL.md.
|
|
25
|
+
// 2. Check whether the same change applies upstream in
|
|
26
|
+
// awa-private/plugins/{awa-dev,awa-private}/skills/<name>/SKILL.md.
|
|
27
|
+
// 3. If yes → open a parallel PR in awa-private. Land both before either
|
|
28
|
+
// is released.
|
|
29
|
+
// 4. If no → document why the change is bundled-only in the PR description.
|
|
30
|
+
// 5. Only then update the hash below.
|
|
31
|
+
//
|
|
32
|
+
// This convention exists because in November 2026 a critical /ship guardrail
|
|
33
|
+
// (the "Branch lock" + "Never push to main" Hard Rules in commit 63f3ed3)
|
|
34
|
+
// was added to the bundled mirror but never back-ported to awa-private. The
|
|
35
|
+
// deployed plugin therefore lacked the guardrail until the next sync. This
|
|
36
|
+
// test cannot prevent that on its own — but the hash-bump moment forces the
|
|
37
|
+
// developer to look at both copies.
|
|
38
|
+
const PINNED_HASHES = {
|
|
39
|
+
contract: '2c8a3779f225902f2a8b0af74bfc66c1cdbae58f863d1205c66ce44a14e275b5',
|
|
40
|
+
'devils-advocate':
|
|
41
|
+
'84275b097fa3ed270b0b71c87e2dad0366794fd7efc7a47d29abaa85da97f974',
|
|
42
|
+
gather: 'ec2964fb1f47970fffba6bafacb4dc4f0c76291a7cc0da92ff069a0a986decb4',
|
|
43
|
+
'ground-claim':
|
|
44
|
+
'64a4fa0b63467a9a7ae6e61afd68813ff59bfb46a8c5e072feafa15473e36f2a',
|
|
45
|
+
'ground-state':
|
|
46
|
+
'ae4c167296e96b640a54cd4cd317e5810894cffff6dac3c022b1433dff003105',
|
|
47
|
+
// intent-lock is bundled-only (not present in upstream awa-private or
|
|
48
|
+
// awa-dev). Hash bumps need no parallel PR — document the change in the
|
|
49
|
+
// commit message instead.
|
|
50
|
+
'intent-lock':
|
|
51
|
+
'7a466075e5a64c1145b97aa24b9a6990a3ee1dc818b93c158433e53d7416aef0',
|
|
52
|
+
parallelize:
|
|
53
|
+
'74b1a7cf866d630dce0d33323663a8b818f149b5f4d4ef60feba1aeb3472e49b',
|
|
54
|
+
// refactor is bundled-only (no upstream awa-private counterpart); verbatim
|
|
55
|
+
// copy of the user-scope /refactor at ~/.afk/skills/.
|
|
56
|
+
refactor: '23ab4836653159deeafbca45e516af8d43e8c5275535613e36f7bcb2d77de64e',
|
|
57
|
+
research: '0d04d0a05891ed1b63679e5a0237b743364a6165731a8f694c5584ed7661505f',
|
|
58
|
+
review: '816ea27cf665be23c67cf887d639d40e1435954f80ceeb43740bcd7f39c205e7',
|
|
59
|
+
'shadow-verify':
|
|
60
|
+
'8bce741e55be049a196ed6c71efd0acd271f272a8e2202917c3f1243b875eb33',
|
|
61
|
+
ship: '4b9a0e40372c36f953ad6d37347e1682950c9825ca5e312fae4e9b320cde975f',
|
|
62
|
+
// simplify is bundled-only (no upstream awa-private counterpart).
|
|
63
|
+
simplify:
|
|
64
|
+
'b863890eead7011c90d4f93b65e5a1533c8f88292728ec771f8b128e9535d996',
|
|
65
|
+
spec: 'c08f3b4fbe1f585b1e8354a000e0d2d3a48455ad322c7a27112d509aa9698fe7',
|
|
66
|
+
} as const;
|
|
67
|
+
|
|
68
|
+
type SkillName = keyof typeof PINNED_HASHES;
|
|
69
|
+
|
|
70
|
+
const SKILLS = Object.keys(PINNED_HASHES) as SkillName[];
|
|
71
|
+
|
|
72
|
+
// ── Namespace-normalized drift detection ──────────────────────────────────────
|
|
73
|
+
//
|
|
74
|
+
// Workspace root is four levels above __dirname (src/bundled-plugins/awa-bundled).
|
|
75
|
+
// awa-private is a sibling of agent-afk at the workspace root level.
|
|
76
|
+
// This mirrors the pattern used in src/skills/_agents/vendored.test.ts.
|
|
77
|
+
const WORKSPACE_ROOT = join(__dirname, '../../../..');
|
|
78
|
+
|
|
79
|
+
// Upstream source paths relative to WORKSPACE_ROOT.
|
|
80
|
+
// intent-lock is bundled-only — no upstream comparison row.
|
|
81
|
+
const UPSTREAM_PATHS: Partial<Record<SkillName, string>> = {
|
|
82
|
+
contract: 'awa-private/plugins/awa-dev/skills/contract/SKILL.md',
|
|
83
|
+
gather: 'awa-private/plugins/awa-dev/skills/gather/SKILL.md',
|
|
84
|
+
'ground-claim': 'awa-private/plugins/awa-dev/skills/ground-claim/SKILL.md',
|
|
85
|
+
'ground-state': 'awa-private/plugins/awa-dev/skills/ground-state/SKILL.md',
|
|
86
|
+
research: 'awa-private/plugins/awa-dev/skills/research/SKILL.md',
|
|
87
|
+
ship: 'awa-private/plugins/awa-dev/skills/ship/SKILL.md',
|
|
88
|
+
spec: 'awa-private/plugins/awa-dev/skills/spec/SKILL.md',
|
|
89
|
+
'devils-advocate':
|
|
90
|
+
'awa-private/plugins/awa-private/skills/devils-advocate/SKILL.md',
|
|
91
|
+
parallelize: 'awa-private/plugins/awa-private/skills/parallelize/SKILL.md',
|
|
92
|
+
review: 'awa-private/plugins/awa-private/skills/review/SKILL.md',
|
|
93
|
+
'shadow-verify':
|
|
94
|
+
'awa-private/plugins/awa-private/skills/shadow-verify/SKILL.md',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Normalize both copies before comparing, removing all permanent intentional
|
|
98
|
+
// namespace shifts:
|
|
99
|
+
//
|
|
100
|
+
// /awa-dev:contract → /contract
|
|
101
|
+
// /awa-private:ship → /ship
|
|
102
|
+
// `awa-dev:ground-state` → `ground-state`
|
|
103
|
+
// "awa-private:research-agent" → "research-agent"
|
|
104
|
+
//
|
|
105
|
+
// After normalization, any remaining diff is either real drift (a change
|
|
106
|
+
// landed in one mirror but not the other) or an explicitly allowlisted
|
|
107
|
+
// intentional divergence documented in INTENTIONAL_DIFFS below.
|
|
108
|
+
function normalize(content: string): string {
|
|
109
|
+
return content
|
|
110
|
+
.replace(/\/awa-dev:/g, '/')
|
|
111
|
+
.replace(/\/awa-private:/g, '/')
|
|
112
|
+
.replace(/`awa-dev:/g, '`')
|
|
113
|
+
.replace(/`awa-private:/g, '`')
|
|
114
|
+
.replace(/"awa-dev:/g, '"')
|
|
115
|
+
.replace(/"awa-private:/g, '"');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// INTENTIONAL_DIFFS: per-skill array of RegExp patterns. A normalized diff
|
|
119
|
+
// line matching any pattern for that skill is silently accepted — the line is
|
|
120
|
+
// removed from BOTH sides before comparison (each pattern is applied to both
|
|
121
|
+
// the bundled and upstream line arrays independently).
|
|
122
|
+
//
|
|
123
|
+
// *** Adding an entry here requires an inline comment justifying why the
|
|
124
|
+
// divergence is intentional. "It seems fine" is NOT sufficient — if you
|
|
125
|
+
// cannot defensibly justify it, surface it as unclassified drift in the PR
|
|
126
|
+
// body instead. ***
|
|
127
|
+
const INTENTIONAL_DIFFS: Partial<Record<SkillName, RegExp[]>> = {
|
|
128
|
+
// devils-advocate, parallelize, shadow-verify:
|
|
129
|
+
// Both sides contain a "Sub-agent contract" invocation line immediately
|
|
130
|
+
// after the frontmatter block, but they use different plugin namespaces:
|
|
131
|
+
//
|
|
132
|
+
// Bundled: /contract (resolves to the co-bundled contract skill)
|
|
133
|
+
// Upstream: /agent-workflow-amplifiers:contract (third-party plugin ns)
|
|
134
|
+
//
|
|
135
|
+
// The `normalize()` function only strips `awa-dev:` and `awa-private:`
|
|
136
|
+
// prefixes; it intentionally does NOT touch `agent-workflow-amplifiers:`
|
|
137
|
+
// because that is a distinct third-party plugin, not a namespace shift of
|
|
138
|
+
// the same plugin. Both copies invoke the same logical skill — the
|
|
139
|
+
// difference is which plugin registry entry resolves the name. This is
|
|
140
|
+
// intentional structural divergence: bundled uses self-contained routing;
|
|
141
|
+
// upstream relies on the agent-workflow-amplifiers plugin being installed.
|
|
142
|
+
//
|
|
143
|
+
// Pattern rationale: we match both the bare `/contract` line (bundled side)
|
|
144
|
+
// and the namespaced `/agent-workflow-amplifiers:contract` line (upstream
|
|
145
|
+
// side) so both are removed before the equality check.
|
|
146
|
+
'devils-advocate': [
|
|
147
|
+
// Bundled side: bare /contract invocation (no plugin prefix).
|
|
148
|
+
/^\/contract$/,
|
|
149
|
+
// Upstream side: /agent-workflow-amplifiers:contract invocation.
|
|
150
|
+
/\/agent-workflow-amplifiers:contract/,
|
|
151
|
+
],
|
|
152
|
+
parallelize: [
|
|
153
|
+
// Same structural divergence as devils-advocate — different contract
|
|
154
|
+
// skill namespace on bundled vs upstream.
|
|
155
|
+
/^\/contract$/,
|
|
156
|
+
/\/agent-workflow-amplifiers:contract/,
|
|
157
|
+
],
|
|
158
|
+
'shadow-verify': [
|
|
159
|
+
// Same structural divergence as devils-advocate.
|
|
160
|
+
/^\/contract$/,
|
|
161
|
+
/\/agent-workflow-amplifiers:contract/,
|
|
162
|
+
],
|
|
163
|
+
|
|
164
|
+
// research — 1-line divergence, #441 back-port gap:
|
|
165
|
+
// "if the research-agent is not available" (bundled) vs
|
|
166
|
+
// "if the private plugin is not installed" (upstream).
|
|
167
|
+
// Bundled users have no concept of "private plugin" — the research-agent
|
|
168
|
+
// IS bundled, so "not available" is the correct user-facing phrase. The
|
|
169
|
+
// upstream wording assumed plugin-based deployment context. This divergence
|
|
170
|
+
// is intentional for bundled context; upstream should ideally adopt a
|
|
171
|
+
// context-neutral phrasing. Flagged for #441 reconciliation.
|
|
172
|
+
research: [
|
|
173
|
+
/if the research-agent is not available/,
|
|
174
|
+
/if the private plugin is not installed/,
|
|
175
|
+
],
|
|
176
|
+
|
|
177
|
+
// ship — 3 divergences, all #441 back-port gaps:
|
|
178
|
+
//
|
|
179
|
+
// 1. Phase 3 heading:
|
|
180
|
+
// Bundled: "Phase 3 — Draft commit message."
|
|
181
|
+
// Upstream: "Phase 3 — Draft commit message (user-approval gate)."
|
|
182
|
+
// The "(user-approval gate)" annotation was added in upstream but not
|
|
183
|
+
// back-ported to bundled. Both copies have the same behavior (no
|
|
184
|
+
// approval gate); the annotation is a clarifying label. Real drift,
|
|
185
|
+
// flagged for #441 back-port.
|
|
186
|
+
//
|
|
187
|
+
// 2. Phase 3 body prose:
|
|
188
|
+
// Bundled: "Print the draft message + file list to the user as
|
|
189
|
+
// info-only output, then **immediately** invoke Phase 4.
|
|
190
|
+
// **This is not a gate. Do not ask "does this look good?" Do not
|
|
191
|
+
// wait for approval.** The user surface is one continuous turn:
|
|
192
|
+
// draft → commit → push → PR URL."
|
|
193
|
+
// Upstream: "Surface the draft message + file list to the user for
|
|
194
|
+
// visibility, then proceed immediately to commit. Do not wait for
|
|
195
|
+
// approval."
|
|
196
|
+
// Upstream simplified the prose; semantics are identical. Real drift
|
|
197
|
+
// (editorial improvement in upstream not back-ported). Flagged for #441.
|
|
198
|
+
//
|
|
199
|
+
// 3. Phase 5 Never-push-main bullet order:
|
|
200
|
+
// Bundled: bullet appears after "Non-fast-forward rejection" bullet.
|
|
201
|
+
// Upstream: bullet appears before "Upstream unset" bullet (earlier).
|
|
202
|
+
// Same safety rule, different list position. Real drift (harmless
|
|
203
|
+
// reordering). Flagged for #441 back-port.
|
|
204
|
+
ship: [
|
|
205
|
+
// Heading divergence (1 above).
|
|
206
|
+
/Phase 3 — Draft commit message\./,
|
|
207
|
+
/Phase 3 — Draft commit message \(user-approval gate\)\./,
|
|
208
|
+
// Prose divergence (2 above) — match the diverging body paragraph.
|
|
209
|
+
/Print the draft message \+ file list to the user as info-only output/,
|
|
210
|
+
/then \*\*immediately\*\* invoke Phase 4\./,
|
|
211
|
+
/\*\*This is not a gate\. Do not ask "does this look good\?" Do not wait for approval\.\*\*/,
|
|
212
|
+
/The user surface is one continuous turn: draft → commit → push → PR URL\./,
|
|
213
|
+
/Surface the draft message \+ file list to the user for visibility/,
|
|
214
|
+
/then proceed immediately to commit\. Do not wait for approval\./,
|
|
215
|
+
// Bullet ordering divergence (3 above).
|
|
216
|
+
/\*\*Never\*\* `git push origin main` \(or `master`\)\. Pushing the feature branch is the only allowed form\./,
|
|
217
|
+
],
|
|
218
|
+
|
|
219
|
+
// review — namespace-only divergence (back-port landed; #441 closed):
|
|
220
|
+
// The bundled review is now the de-namespaced mirror of upstream
|
|
221
|
+
// awa-private review. The previously-allowlisted #441 drift —
|
|
222
|
+
// Wave 1.5 (citation + absence-claim verification), reviewed-ref
|
|
223
|
+
// capture / SHA pinning, the citation-requirement block, the severity
|
|
224
|
+
// sort-order block, epistemic scope disclosure, and the ref:<sha>
|
|
225
|
+
// finding-schema fields — has been back-ported into bundled; and the
|
|
226
|
+
// api-compat reachability + absence-claim grounding gates were ported
|
|
227
|
+
// the other way into upstream (griffinwork40/awa-private#40). Both
|
|
228
|
+
// copies now carry the full superset, so the only remaining divergence
|
|
229
|
+
// is the same contract-namespace shift as devils-advocate / parallelize
|
|
230
|
+
// / shadow-verify: bundled uses /contract (self-contained routing),
|
|
231
|
+
// upstream uses /agent-workflow-amplifiers:contract (third-party ns).
|
|
232
|
+
review: [
|
|
233
|
+
// Bundled side: bare /contract invocation (no plugin prefix).
|
|
234
|
+
/^\/contract$/,
|
|
235
|
+
// Upstream side: /agent-workflow-amplifiers:contract invocation.
|
|
236
|
+
/\/agent-workflow-amplifiers:contract/,
|
|
237
|
+
],
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
241
|
+
|
|
242
|
+
function computeHash(content: string): string {
|
|
243
|
+
return createHash('sha256').update(content).digest('hex');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function bundledPath(name: SkillName): string {
|
|
247
|
+
return join(__dirname, 'skills', name, 'SKILL.md');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function readBundled(name: SkillName): string {
|
|
251
|
+
return readFileSync(bundledPath(name), 'utf8');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function upstreamAbsPath(name: SkillName): string | null {
|
|
255
|
+
const rel = UPSTREAM_PATHS[name];
|
|
256
|
+
if (!rel) return null;
|
|
257
|
+
return join(WORKSPACE_ROOT, rel);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function upstreamAvailable(name: SkillName): boolean {
|
|
261
|
+
const abs = upstreamAbsPath(name);
|
|
262
|
+
return abs !== null && existsSync(abs);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// isAllowlisted returns true if the given line matches any pattern in the
|
|
266
|
+
// skill's INTENTIONAL_DIFFS entry.
|
|
267
|
+
function isAllowlisted(line: string, name: SkillName): boolean {
|
|
268
|
+
const patterns = INTENTIONAL_DIFFS[name] ?? [];
|
|
269
|
+
return patterns.some((re) => re.test(line));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// diffLines computes the symmetric difference between two ordered line arrays:
|
|
273
|
+
// lines that are in `aLines` but not `bLines` (bundled-only), and lines that
|
|
274
|
+
// are in `bLines` but not `aLines` (upstream-only). Returns the two sets.
|
|
275
|
+
// This is intentionally set-based (not position-sensitive) to avoid false
|
|
276
|
+
// positives from harmless reorderings of identical content.
|
|
277
|
+
function diffLines(
|
|
278
|
+
aLines: string[],
|
|
279
|
+
bLines: string[],
|
|
280
|
+
): { bundledOnly: string[]; upstreamOnly: string[] } {
|
|
281
|
+
const aCount = new Map<string, number>();
|
|
282
|
+
const bCount = new Map<string, number>();
|
|
283
|
+
for (const l of aLines) aCount.set(l, (aCount.get(l) ?? 0) + 1);
|
|
284
|
+
for (const l of bLines) bCount.set(l, (bCount.get(l) ?? 0) + 1);
|
|
285
|
+
|
|
286
|
+
const bundledOnly: string[] = [];
|
|
287
|
+
const upstreamOnly: string[] = [];
|
|
288
|
+
|
|
289
|
+
for (const [l, cnt] of aCount) {
|
|
290
|
+
const excess = cnt - (bCount.get(l) ?? 0);
|
|
291
|
+
for (let i = 0; i < excess; i++) bundledOnly.push(l);
|
|
292
|
+
}
|
|
293
|
+
for (const [l, cnt] of bCount) {
|
|
294
|
+
const excess = cnt - (aCount.get(l) ?? 0);
|
|
295
|
+
for (let i = 0; i < excess; i++) upstreamOnly.push(l);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return { bundledOnly, upstreamOnly };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ── Test suites ───────────────────────────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
describe('bundled skills', () => {
|
|
304
|
+
describe('pinned-hash snapshot tests', () => {
|
|
305
|
+
for (const name of SKILLS) {
|
|
306
|
+
it(`${name} bundled copy matches pinned hash`, () => {
|
|
307
|
+
const content = readBundled(name);
|
|
308
|
+
const hash = computeHash(content);
|
|
309
|
+
expect(hash).toBe(PINNED_HASHES[name]);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('skill inventory invariants', () => {
|
|
315
|
+
it('covers every bundled skill directory', () => {
|
|
316
|
+
// Sentinel: if a new skill is added to awa-bundled/skills/ but not
|
|
317
|
+
// PINNED_HASHES, this test fails — forcing the author to register it.
|
|
318
|
+
const skillsDir = join(__dirname, 'skills');
|
|
319
|
+
const entries = readdirSync(skillsDir)
|
|
320
|
+
.filter((name) => statSync(join(skillsDir, name)).isDirectory())
|
|
321
|
+
.sort();
|
|
322
|
+
const registered = [...SKILLS].sort();
|
|
323
|
+
expect(entries).toEqual(registered);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// ── Namespace-normalized drift comparison ──────────────────────────────────
|
|
328
|
+
//
|
|
329
|
+
// Each test below compares a bundled SKILL.md against its upstream
|
|
330
|
+
// counterpart after normalization (namespace prefixes stripped) and
|
|
331
|
+
// allowlisting (known intentional divergences removed).
|
|
332
|
+
//
|
|
333
|
+
// The test is skipped — NOT failed — when awa-private is not co-located
|
|
334
|
+
// (e.g. standalone CI clone). The pinned-hash tests above still guard
|
|
335
|
+
// against local-only edits. These tests guard against the cross-repo case:
|
|
336
|
+
// a change landing in one mirror without being back-ported to the other.
|
|
337
|
+
//
|
|
338
|
+
// Workflow when a test fails here:
|
|
339
|
+
// 1. Is the diff intentional? Add a justified entry to INTENTIONAL_DIFFS.
|
|
340
|
+
// 2. Is it real drift? Land the back-port and re-run. Then bump the hash.
|
|
341
|
+
// 3. Is it unclassifiable? Surface it as unclassified drift in the PR body.
|
|
342
|
+
describe('namespace-normalized drift comparison (skipped if awa-private not co-located)', () => {
|
|
343
|
+
// Invariant: for every mirrorable skill, after normalize() and after
|
|
344
|
+
// removing allowlisted lines, the bundled and upstream copies must be
|
|
345
|
+
// line-for-line identical. Any remaining difference is a back-port gap.
|
|
346
|
+
|
|
347
|
+
const mirrorableSkills = SKILLS.filter(
|
|
348
|
+
(s) => s !== 'intent-lock' && s !== 'simplify' && s !== 'refactor',
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
for (const name of mirrorableSkills) {
|
|
352
|
+
it.skipIf(!upstreamAvailable(name))(
|
|
353
|
+
`${name}: normalized bundled matches normalized upstream (after allowlist)`,
|
|
354
|
+
() => {
|
|
355
|
+
// Contract: upstreamAbsPath is non-null when upstreamAvailable() is true.
|
|
356
|
+
const abs = upstreamAbsPath(name) as string;
|
|
357
|
+
const bundledRaw = readBundled(name);
|
|
358
|
+
const upstreamRaw = readFileSync(abs, 'utf8');
|
|
359
|
+
|
|
360
|
+
const bundledLines = normalize(bundledRaw).split('\n');
|
|
361
|
+
const upstreamLines = normalize(upstreamRaw).split('\n');
|
|
362
|
+
|
|
363
|
+
// Compute symmetric difference: lines unique to each side.
|
|
364
|
+
// Context lines (identical on both sides) are ignored — we only care
|
|
365
|
+
// about lines that changed.
|
|
366
|
+
const { bundledOnly, upstreamOnly } = diffLines(
|
|
367
|
+
bundledLines,
|
|
368
|
+
upstreamLines,
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
// Remove allowlisted divergences from each side.
|
|
372
|
+
const unexpectedBundledOnly = bundledOnly.filter(
|
|
373
|
+
(l) => !isAllowlisted(l, name),
|
|
374
|
+
);
|
|
375
|
+
const unexpectedUpstreamOnly = upstreamOnly.filter(
|
|
376
|
+
(l) => !isAllowlisted(l, name),
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
if (
|
|
380
|
+
unexpectedBundledOnly.length > 0 ||
|
|
381
|
+
unexpectedUpstreamOnly.length > 0
|
|
382
|
+
) {
|
|
383
|
+
const lines: string[] = [
|
|
384
|
+
`--- bundled (normalized, non-allowlisted unique lines)`,
|
|
385
|
+
`+++ upstream (normalized, non-allowlisted unique lines)`,
|
|
386
|
+
];
|
|
387
|
+
for (const l of unexpectedBundledOnly) lines.push(`-${l}`);
|
|
388
|
+
for (const l of unexpectedUpstreamOnly) lines.push(`+${l}`);
|
|
389
|
+
|
|
390
|
+
throw new Error(
|
|
391
|
+
`Namespace-normalized drift detected in ${name}.\n` +
|
|
392
|
+
` Bundled: ${bundledPath(name)}\n` +
|
|
393
|
+
` Upstream: ${abs}\n` +
|
|
394
|
+
` If the diff is intentional, add a justified entry to INTENTIONAL_DIFFS['${name}'].\n` +
|
|
395
|
+
` If it is real drift, back-port the change and bump the pinned hash.\n\n` +
|
|
396
|
+
lines.join('\n'),
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: contract
|
|
3
|
+
description: "Reference convention for sub-agent I/O schemas. Loaded by orchestrator skills via /contract and into agents (e.g., qualify) via the `skills:` field."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Contract
|
|
7
|
+
|
|
8
|
+
For each sub-agent you plan to dispatch, define a schema before the call:
|
|
9
|
+
|
|
10
|
+
- `goal` — one-sentence objective
|
|
11
|
+
- `inputs` — data/context the sub-agent receives
|
|
12
|
+
- `artifacts` — named structured fields expected back (not freeform prose)
|
|
13
|
+
- `non_goals` — what the sub-agent must NOT do
|
|
14
|
+
- `failure_modes` — how to report blocked or partial work
|
|
15
|
+
- `domain` *(optional)* — the knowledge domain for this task. Guides how research, specification, and verification adapt. Common values: `software`, `research`, `design`, `business` — but any freeform string works (e.g., `healthcare`, `legal`, `education`). When omitted, infer from context: git repo present → `software`; PDFs/papers/citations in working directory → `research`; design files/brand assets → `design`; financial models/strategy docs → `business`. Default fallback: `software`.
|
|
16
|
+
|
|
17
|
+
Embed the schema at the top of every sub-agent's prompt and require results in that exact shape. Instruct each sub-agent explicitly: "Return ONLY the schema fields. No preamble, no analysis prose, no explanation — begin your response with the first schema field." When sub-agents return, validate field-by-field. If any artifact is missing, malformed, or wrapped in prose, re-dispatch only the failing sub-agent with the gap cited. Merge only schema-valid responses.
|
|
18
|
+
|
|
19
|
+
## Epistemic confidence
|
|
20
|
+
|
|
21
|
+
Recommended for all sub-agents. Add to your return schema:
|
|
22
|
+
|
|
23
|
+
- `confidence` — low / medium / high — how confident is the sub-agent in the completeness and accuracy of its findings?
|
|
24
|
+
- `coverage_gaps` — what the sub-agent couldn't access, verify, or search (e.g., proprietary databases, paywalled sources, unpublished practitioner knowledge, subjective judgment areas)
|
|
25
|
+
- `boundary_flag` — if the sub-agent hit an epistemic boundary, name it: `non-falsifiable` (claim can't be tested), `low-coverage` (search was limited), `tacit-knowledge` (unwritten knowledge required), `unprecedented` (genuinely novel, no baseline), `time-sensitive` (answer depends on current state), or `none`
|
|
26
|
+
- `recommended_action` — what should happen next: `proceed` (findings solid, move ahead), `human-gate` (pause for human judgment before acting), `re-retrieve` (try different search strategy or sources), `elicit` (generate prompts to validate with domain experts)
|
|
27
|
+
|
|
28
|
+
This is NOT required — skills that don't return it continue to work. But when present, coverage gaps and boundary flags surface automatically during merge, preventing silent failures.
|
|
29
|
+
|
|
30
|
+
## Skip if
|
|
31
|
+
|
|
32
|
+
- Single-agent dispatch
|
|
33
|
+
- Sub-agents returning freeform prose where structure doesn't help merge
|
|
34
|
+
- Exploratory tasks where the output shape isn't known yet
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devils-advocate
|
|
3
|
+
description: "Adversarially critique a proposal by generating alternatives. Dispatches 3 parallel critics (pragmatist, paranoid, architect lenses) — each invents one alternative approach — then a synthesis step ranks all 4 options and recommends the top choice. Use when a plan, fix, scoping, decomposition, or named recommendation will drive decisions and you want structured alternative-generation before committing. Complements /shadow-verify — that skill re-derives factual claims; this one critiques whether the chosen approach itself is best."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Sub-agent contract
|
|
7
|
+
/contract
|
|
8
|
+
|
|
9
|
+
When a proposal — a plan, fix, decomposition, scoping, or named recommendation — will drive user decisions, file edits, or commits, do NOT act on it as-given. Run a devils-advocate critique wave **before** acting, and use the recommendation as input to the decision.
|
|
10
|
+
|
|
11
|
+
**Wave 2 — Parallel critics (3 fixed lenses, independent):**
|
|
12
|
+
1. Extract the **proposal** (the approach being critiqued) and the **goal** (what the proposal is trying to accomplish). Both should be plain prose. Do NOT include the original proposer's reasoning or evidence — critics must invent alternatives without anchoring on the chosen path.
|
|
13
|
+
2. Dispatch 3 critics in parallel. **Default `subagent_type: "research-agent"`** (mechanically locked to Read/Grep/Glob/WebFetch/WebSearch — cannot Edit/Write/commit). Each critic receives ONLY the proposal + goal + ONE lens:
|
|
14
|
+
- **pragmatist** — cheapest-path. "What is the cheapest approach that solves the goal? Argue why the proposal may be over-engineered."
|
|
15
|
+
- **paranoid** — safest-path. "What could go wrong with the proposal? Propose a safer alternative with narrower blast radius."
|
|
16
|
+
- **architect** — right-level. "Is the proposal addressing the right abstraction level? Propose an alternative one level up (systemic fix) or down (targeted fix)."
|
|
17
|
+
3. Each critic returns `{lens, alternative, tradeoff, strength}` where `strength ∈ {weak, medium, strong}` reflects the critic's confidence that its alternative beats the original.
|
|
18
|
+
|
|
19
|
+
**Wave 3 — Synthesis (sequential, single agent):**
|
|
20
|
+
1. Dispatch one synthesis agent (same research-agent base). Input: original proposal + goal + all 3 critic outputs.
|
|
21
|
+
2. Rank all 4 options (original + 3 alternatives) along: **cost** (implementation + ongoing), **risk** (blast radius + reversibility), **scope-fit** (how cleanly it solves the stated goal, no more), **goal-fit** (how well it addresses the underlying intent, not just the surface goal).
|
|
22
|
+
3. Recommend ONE top choice with a one-paragraph rationale.
|
|
23
|
+
4. Flag `dissent = true` when ≥2 critics returned `strong` alternatives disagreeing with the recommendation — signals the synthesizer is overruling well-argued dissent, so confidence is low. Include a `dissent_note` summarizing the strongest counter-argument.
|
|
24
|
+
|
|
25
|
+
**Merge + surface:**
|
|
26
|
+
- Recommendation = `original` → the proposal survived critique; proceed with it.
|
|
27
|
+
- Recommendation ≠ `original`, `dissent = false` → synthesis found a better path; surface the alternative with rationale before acting.
|
|
28
|
+
- `dissent = true` → present the matrix to the user; do not act. Confidence is low.
|
|
29
|
+
|
|
30
|
+
**When to invoke:**
|
|
31
|
+
Any time a proposal, plan, root-cause + fix, decomposition, or named recommendation will drive user decisions, file edits, commits, or external side-effects. Especially useful when the proposal "feels right" — that's when alternative-generation has the highest value.
|
|
32
|
+
|
|
33
|
+
**Skip when:**
|
|
34
|
+
- Single-line edits or trivial fixes where alternative space is empty.
|
|
35
|
+
- User explicitly named the chosen approach by name (critiquing a directly-requested action is friction, not signal).
|
|
36
|
+
- An upstream orchestrator already produced comparative output on the same claim-space (`/diagnose`'s hypothesis ranking does not need a second opinion on its hypotheses — though the *final fix* it produces can still benefit).
|
|
37
|
+
|
|
38
|
+
## Appendix: lens selection (non-binding)
|
|
39
|
+
|
|
40
|
+
V1 ships three fixed lenses; domain-specific lens packs (software-perf, research-methodology, business-risk) are V2 work. When the proposal's domain is clear, the synthesis agent may weight dimensions accordingly — but the critic lenses themselves remain fixed.
|
|
41
|
+
|
|
42
|
+
| Lens | Typical alternatives it surfaces |
|
|
43
|
+
|------|----------------------------------|
|
|
44
|
+
| pragmatist | narrower scope, simpler implementation, reuse-over-build |
|
|
45
|
+
| paranoid | smaller blast radius, reversibility, guardrails, staged rollout |
|
|
46
|
+
| architect | systemic fix one level up, targeted fix one level down, different subsystem ownership |
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gather
|
|
3
|
+
description: "Parallel context-gathering for a code area. Use when you need to understand a module, feature, or subsystem and would otherwise read 3+ files sequentially — dispatches two agents in parallel to map structure and test coverage in one wave."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Dispatch protocol
|
|
7
|
+
|
|
8
|
+
You MUST emit **exactly two** `agent` tool_use blocks in a **single response turn** — both calls in the same assistant message, before either result arrives. Do not dispatch the second agent in a later turn after seeing the first agent's reply. Do not dispatch three agents. Do not dispatch one.
|
|
9
|
+
|
|
10
|
+
Correct shape of your next response:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
<assistant turn>
|
|
14
|
+
<tool_use name="agent" id="…"> Structure Agent prompt </tool_use>
|
|
15
|
+
<tool_use name="agent" id="…"> Test Agent prompt </tool_use>
|
|
16
|
+
</assistant turn>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
If you find yourself about to send a single `agent` call and wait, stop — that is the failure mode this skill exists to prevent.
|
|
20
|
+
|
|
21
|
+
## The two agents
|
|
22
|
+
|
|
23
|
+
When understanding a task requires reading multiple related files (imports, callers, tests, configs, types), dispatch these two — concurrently, per the protocol above:
|
|
24
|
+
|
|
25
|
+
1. **Structure Agent** (Explore, thoroughness matched to scope) — Find and read the target file(s), all direct imports, callers, and config references. Return:
|
|
26
|
+
- `files_read`: absolute paths examined
|
|
27
|
+
- `call_graph`: how components connect (one paragraph)
|
|
28
|
+
- `public_interfaces`: function signatures, types, or contracts that govern the area
|
|
29
|
+
- `entry_points`: where control flow enters
|
|
30
|
+
|
|
31
|
+
2. **Test Agent** (Explore, "medium") — Find test files that exercise the target area, read them, identify what paths are covered and what's missing. Return:
|
|
32
|
+
- `test_files`: absolute paths of relevant tests
|
|
33
|
+
- `coverage_summary`: what behaviors/branches tests exercise
|
|
34
|
+
- `untested_paths`: code paths with no test coverage
|
|
35
|
+
|
|
36
|
+
When both return, merge into a unified context map. If either agent's output has gaps (e.g., Structure Agent missed config, Test Agent found no tests), issue one targeted follow-up Read — do not re-dispatch.
|
|
37
|
+
|
|
38
|
+
### When NOT to use
|
|
39
|
+
|
|
40
|
+
- You already know exactly which 1–2 files to read — just read them directly.
|
|
41
|
+
- The task is a simple grep or symbol lookup — use Grep or Glob.
|
|
42
|
+
- You're mid-edit and need to check one adjacent file — a single Read is fine.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ground-claim
|
|
3
|
+
description: "Use when the user asks a meta-capability question about a system/framework/repo ('what does X enable', 'what can this do', 'list the capabilities'). Forces file-read grounding with path:line citations before answering; tags any unverifiable claim as [UNVERIFIED]."
|
|
4
|
+
argument-hint: "<the meta-capability question>"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Trigger
|
|
8
|
+
|
|
9
|
+
Self-referential meta-capability questions about the current repository, framework, or system. Examples:
|
|
10
|
+
|
|
11
|
+
- "What does this repo enable?"
|
|
12
|
+
- "What are the orchestration patterns available?"
|
|
13
|
+
- "List the skills in agent-framework-private."
|
|
14
|
+
- "What capabilities does the plugin provide?"
|
|
15
|
+
- "Show me what the framework can do."
|
|
16
|
+
|
|
17
|
+
Skip: usage questions ("how do I use X?"), bug reports, feature requests, technical implementation questions.
|
|
18
|
+
|
|
19
|
+
## Procedure
|
|
20
|
+
|
|
21
|
+
1. **Extract capability nouns.** From the user's question, identify 2–5 concrete capability categories (e.g., skills, hooks, agents, orchestration patterns, CLI commands, verification methods). Write them down.
|
|
22
|
+
|
|
23
|
+
2. **Locate and read evidence.** For each capability noun:
|
|
24
|
+
- Use Glob or Grep to locate source files (e.g., `skills/*/SKILL.md` for skills, `hooks/` for hooks, `agents/` for agents).
|
|
25
|
+
- Read at least one concrete source file per capability. Record the file path and specific line numbers.
|
|
26
|
+
- Do not rely on training data, model recall, or session-listing attachments. Evidence must come from Read tool output.
|
|
27
|
+
|
|
28
|
+
3. **Build the answer inline.** As you write the response, embed citations **within claims**, not in a separate appendix. Format: `path/to/file.md:line—<claim context>`. Example: `agents/qualify.md:5—the qualify agent enforces a force-multiplier rubric`.
|
|
29
|
+
|
|
30
|
+
4. **Tag ungrounded claims.** If a capability claim cannot be traced to a file read, prefix it with `[UNVERIFIED: what would be needed to verify this]`. Never present an unverified claim without the tag.
|
|
31
|
+
|
|
32
|
+
5. **Declare sources read.** Explicitly name which files you read in the response (e.g., "Read: `skills/mint/SKILL.md`, `agents/qualify.md`, `hooks/hooks.json`").
|
|
33
|
+
|
|
34
|
+
## Hard rules
|
|
35
|
+
|
|
36
|
+
- Do not answer from model recall alone.
|
|
37
|
+
- Do not answer from session-listing attachments without reading the underlying SKILL.md or manifest files.
|
|
38
|
+
- Do not summarize without citation. Every capability claim must point to a source.
|
|
39
|
+
- Do not bury unverified claims. Use the `[UNVERIFIED]` prefix and state the evidence gap.
|
|
40
|
+
- At least one `path:line` citation per named capability.
|
|
41
|
+
|
|
42
|
+
## Exit criteria
|
|
43
|
+
|
|
44
|
+
- Response contains ≥1 `path:line` citation per capability mentioned.
|
|
45
|
+
- Every unverified claim is explicitly tagged with `[UNVERIFIED: …]`.
|
|
46
|
+
- Response explicitly lists which files were read (not just quoted).
|
|
47
|
+
- No claims rest on model recall or default knowledge.
|
|
48
|
+
|
|
49
|
+
## Out of scope
|
|
50
|
+
|
|
51
|
+
- Usage questions ("how do I use library X?") → normal research.
|
|
52
|
+
- Bug reports → `/diagnose`.
|
|
53
|
+
- Building new capability → `/mint`.
|
|
54
|
+
- Verification of sub-agent findings → `/shadow-verify`.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ground-state
|
|
3
|
+
description: "Before starting any non-trivial implementation (multi-file edits, new features, config changes, anything that writes), dispatch a parallel pre-flight reconnaissance wave to triangulate git state, project infrastructure, and prior-session memory. Produces a 5-line ground-truth snapshot that grounds the implementation and catches wrong-branch edits, assumed-no-CI, stale origin, and missed memory context before the first edit."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Sub-agent contract
|
|
7
|
+
/contract
|
|
8
|
+
|
|
9
|
+
**Constraint: read-only reconnaissance.** Surveyors and the synthesizer MUST NOT call `edit_file`, `write_file`, or any mutating bash command (no `git commit`, `git push`, `git checkout`, `mv`, `rm`, file redirection, package installs, etc.). Read-only tools only: `read_file`, `grep`, `glob`, `list_directory`, and read-only bash (`git status`, `git log`, `git diff`, `cat`, `ls`, `find`, etc.).
|
|
10
|
+
|
|
11
|
+
If the survey reveals a fix that's tempting to apply, **return it as a recommendation in the snapshot** — the orchestrator decides whether to act. Even if the invoking brief sounds prescriptive ("draft the edit", "apply the change"), this skill stops at the snapshot. The orchestrator dispatches a separate implementation step afterward.
|
|
12
|
+
|
|
13
|
+
Before any multi-step implementation (not single-file fixes, not pure Q&A), dispatch three parallel reconnaissance sub-agents, each with a narrow target. Adapt the first two surveyors to the domain:
|
|
14
|
+
|
|
15
|
+
**State surveyor** *(domain-aware)*
|
|
16
|
+
|
|
17
|
+
| Domain | What to survey |
|
|
18
|
+
|--------|---------------|
|
|
19
|
+
| `software` | Current branch, `git log --oneline -5`, `git status -s`, diff-summary vs `origin/<default-branch>`, stash list. Flag: diverged, uncommitted changes, stale upstream. |
|
|
20
|
+
| `research` | Bibliography state (how many papers collected, citation manager in use), data pipeline status (raw data present? processed?), draft status (outline? partial draft? submitted?), publication target and deadline if known. |
|
|
21
|
+
| `design` | Design system state (component library version, Figma project structure), brand guidelines version, current design phase (research? wireframes? high-fidelity? handoff?), recent design changes. |
|
|
22
|
+
| `business` | Financial data freshness (last updated dates on models/reports), market data recency, stakeholder map (who's involved, who decides), current phase (research? proposal? execution?). |
|
|
23
|
+
| *(other)* | Scan for version-controlled artifacts, recent changes, current project phase, and any state that could cause conflicts. |
|
|
24
|
+
|
|
25
|
+
**Infrastructure surveyor** *(domain-aware)*
|
|
26
|
+
|
|
27
|
+
| Domain | What to scan |
|
|
28
|
+
|--------|-------------|
|
|
29
|
+
| `software` | CI configs (`.github/workflows/`, `.gitlab-ci.yml`, `Jenkinsfile`), package scripts (`package.json`, `Makefile`, `pyproject.toml`), existing linters/formatters, authoritative config file locations relevant to the task. Return 5-bullet inventory. |
|
|
30
|
+
| `research` | Reference manager (Zotero, Mendeley, .bib files), LaTeX setup (template, build system), data analysis tools (Jupyter, R, Python scripts), collaboration tools (Overleaf, shared drives), submission system requirements. Return 5-bullet inventory. |
|
|
31
|
+
| `design` | Design tools (Figma, Sketch, Adobe), prototyping tools (Framer, Principle), handoff tools (Zeplin, Storybook), asset pipeline (export scripts, optimization), accessibility testing tools. Return 5-bullet inventory. |
|
|
32
|
+
| `business` | Modeling tools (Excel, Google Sheets, financial software), presentation tools (PowerPoint, Google Slides, Pitch), data sources (CRM, analytics platforms), collaboration tools (Notion, Confluence), approval workflows. Return 5-bullet inventory. |
|
|
33
|
+
| *(other)* | Scan for tooling, build/export pipelines, collaboration infrastructure, and config files relevant to the stated domain. Return 5-bullet inventory. |
|
|
34
|
+
|
|
35
|
+
When domain is unspecified, infer from the working directory contents.
|
|
36
|
+
|
|
37
|
+
**Memory surveyor**
|
|
38
|
+
Grep the user's auto-memory store (`~/.claude/projects/-<cwd-slug>/memory/`) + any project CLAUDE.md for keywords from the user's current request. Return relevant memory file pointers with 1-line summaries, or "no relevant memory found."
|
|
39
|
+
|
|
40
|
+
**Synthesize** into a 6-line ground-truth snapshot:
|
|
41
|
+
- Branch: `<current>`, `<clean|diverged>`, upstream: `<fresh|stale>`
|
|
42
|
+
- Recent work: last 3 commits or stash items
|
|
43
|
+
- Infrastructure: CI present? package scripts? authoritative configs for this task
|
|
44
|
+
- Memory hits: file refs or "none"
|
|
45
|
+
- Implementation risks: e.g. "branch is `main`, don't edit directly"; "CI runs on push"; "memory says prior attempt used approach X"
|
|
46
|
+
- Epistemic confidence: `<high|medium|low>` — based on how much state could be verified. Flag if working directory is sparse, if domain is unfamiliar, or if key artifacts may be missing.
|
|
47
|
+
|
|
48
|
+
Surface the snapshot and stop. The orchestrator then uses these verified facts — not assumptions — to decide the next step. This skill never edits files.
|
|
49
|
+
|
|
50
|
+
**Skip when:**
|
|
51
|
+
Task is Q&A only; single-line fix on an already-identified file; user says "skip pre-flight".
|