@outerlayer/cli 0.2.0 → 0.4.1
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 +132 -0
- package/README.md +229 -60
- package/dist/agent-setup-IFH3FB5X.js +197 -0
- package/dist/build-TZUUTRGH.js +9 -0
- package/dist/build-info.json +1 -1
- package/dist/check-7MFH6HNC.js +94 -0
- package/dist/chunk-3TFPDVTI.js +94 -0
- package/dist/{chunk-4TNZMO7V.js → chunk-4C4THABW.js} +5566 -754
- package/dist/{chunk-R5KBGVII.js → chunk-5QRP3MVS.js} +30 -14
- package/dist/chunk-5Y3QQRUH.js +300 -0
- package/dist/chunk-774HEQL6.js +178 -0
- package/dist/{work-pr-cmd-AZQPWH4H.js → chunk-7WERLFVR.js} +9 -26
- package/dist/chunk-ABIWDUMS.js +104 -0
- package/dist/chunk-AUFG23AA.js +534 -0
- package/dist/chunk-B2G7JHHB.js +42 -0
- package/dist/chunk-BCJNHMZT.js +34 -0
- package/dist/chunk-BCLJSQCV.js +56 -0
- package/dist/chunk-BFESLKPP.js +61 -0
- package/dist/chunk-BJ3KTMDH.js +63 -0
- package/dist/chunk-BKO6JEZI.js +47 -0
- package/dist/chunk-BOLTI6LR.js +37 -0
- package/dist/chunk-CBPPJSAR.js +89 -0
- package/dist/chunk-DM3VFDS3.js +1461 -0
- package/dist/{chunk-65WXAANR.js → chunk-DVDEBNQJ.js} +17 -2
- package/dist/{chunk-TFUIDMOB.js → chunk-EABW6AJQ.js} +12 -1
- package/dist/{chunk-KFYJV2ZG.js → chunk-EMY4I27X.js} +1 -1
- package/dist/{chunk-NTTPJV35.js → chunk-F47JBIAW.js} +3 -1
- package/dist/chunk-F6GFZXZ3.js +60 -0
- package/dist/{chunk-YOOSBOKS.js → chunk-F7CU5ABH.js} +1 -1
- package/dist/{emit-cmd-TWSYYEZI.js → chunk-H5NCNVDA.js} +68 -17
- package/dist/chunk-HZRNMGLF.js +2233 -0
- package/dist/chunk-LF6MLJCY.js +38 -0
- package/dist/{chunk-U32VSRLO.js → chunk-LT5TZHYH.js} +95 -634
- package/dist/chunk-LYCDNMOH.js +83 -0
- package/dist/chunk-M5POMKKW.js +1051 -0
- package/dist/{mcp-install-cmd-FDQH6SEN.js → chunk-MQ3IPHIZ.js} +35 -18
- package/dist/{chunk-D77LS3UI.js → chunk-N5FOE5PS.js} +33 -4
- package/dist/chunk-N6LURUEF.js +64 -0
- package/dist/chunk-OGFGZQA3.js +23 -0
- package/dist/chunk-QDQEUVUF.js +9 -0
- package/dist/chunk-QOZKPLVJ.js +226 -0
- package/dist/chunk-QY22NZUW.js +1764 -0
- package/dist/chunk-RA3O54FC.js +30 -0
- package/dist/chunk-RFUPN6KX.js +71 -0
- package/dist/chunk-SY4GK4SP.js +55 -0
- package/dist/chunk-TBT347UY.js +41 -0
- package/dist/chunk-TWNWNS2Q.js +1084 -0
- package/dist/{chunk-NXLLURA4.js → chunk-UFFXNXLQ.js} +115 -71
- package/dist/chunk-USO2DKBD.js +421 -0
- package/dist/chunk-W4SQCOZU.js +206 -0
- package/dist/{chunk-Q6CL5THG.js → chunk-W5FVUZXV.js} +208 -61
- package/dist/chunk-WFJ65NUD.js +383 -0
- package/dist/{chunk-A3WLZX2F.js → chunk-WIOZAJ2W.js} +15 -2
- package/dist/chunk-WO2BXCTQ.js +83 -0
- package/dist/{chunk-I3ETLSNE.js → chunk-XCCLFVXM.js} +120 -11
- package/dist/{context-materialize-6WKT3RBQ.js → chunk-XDDW4FRS.js} +134 -26
- package/dist/chunk-Y7KJLXYN.js +232 -0
- package/dist/chunk-YFHIIH4B.js +102 -0
- package/dist/chunk-YYYXRJUW.js +101 -0
- package/dist/chunk-ZM2IMMYN.js +76 -0
- package/dist/chunk-ZMYPLFG3.js +71 -0
- package/dist/chunk-ZNA27WEV.js +470 -0
- package/dist/{cli-G7DILYJY.js → cli-W62AFRSW.js} +616 -878
- package/dist/{paths-D2VGWWFI.js → cli-build-K56DK4DS.js} +1 -1
- package/dist/config-XVJYZ4IQ.js +9 -0
- package/dist/connect-cmd-ZTJONP37.js +16 -0
- package/dist/context-adopt-UMA4O5NY.js +105 -0
- package/dist/context-materialize-G2FUFC72.js +19 -0
- package/dist/dist-44CQVWGT.js +6 -0
- package/dist/docker-NEGDLU6D.js +7 -0
- package/dist/doctor-53F2PYY7.js +43 -0
- package/dist/doctor-ZLGZYMDK.js +426 -0
- package/dist/{emit-artifact-cmd-UQVT3OKW.js → emit-artifact-cmd-AKZP7TMF.js} +87 -23
- package/dist/emit-cmd-DCERAU27.js +9 -0
- package/dist/emit-criteria-cmd-7MXQVCKD.js +166 -0
- package/dist/{emit-finding-cmd-FDOU2OPD.js → emit-finding-cmd-N2FTGCVC.js} +34 -31
- package/dist/{emit-result-cmd-H2T4C2K3.js → emit-result-cmd-4CPDBMN2.js} +34 -62
- package/dist/exec-client-4XGXV3XN.js +8 -0
- package/dist/guest-init-GENRHNP7.js +8 -0
- package/dist/hook-fast-EYENPK6F.js +9 -0
- package/dist/{hook-wrap-fast-KXYNX3AD.js → hook-wrap-fast-NPLHJXFK.js} +2 -2
- package/dist/host-key-KDYZ5ECC.js +7 -0
- package/dist/import-capture-cmd-AVZ4VIVJ.js +68 -0
- package/dist/{import-ruler-cmd-7LH2QOMG.js → import-ruler-cmd-GUHL6IY5.js} +1 -1
- package/dist/index.js +4 -4
- package/dist/init-55Y4LHWF.js +36 -0
- package/dist/init-XXEKWZQP.js +221 -0
- package/dist/init-cmd-WLST2F7G.js +135 -0
- package/dist/install-cmd-7WXOIFDO.js +55 -0
- package/dist/lima-XN65D7GN.js +55 -0
- package/dist/login-browser-7XGSV7MA.js +10 -0
- package/dist/{config-POF7DEQW.js → logout-cmd-QAUFDJ6J.js} +3 -1
- package/dist/{logs-TRNPQM42.js → logs-7RYUH5A6.js} +1 -1
- package/dist/loop-JFIBP4B7.js +43 -0
- package/dist/machine-ZBPT2J3R.js +35 -0
- package/dist/mcp-install-cmd-RLK4SO3N.js +9 -0
- package/dist/{mcp-serve-cmd-57EZZOTL.js → mcp-serve-cmd-N6UD2KBX.js} +20 -9
- package/dist/paths-OYKMVYJP.js +6 -0
- package/dist/{pidfile-PTW76F56.js → pidfile-UZRH774M.js} +2 -3
- package/dist/real-deps-SU24ZA2K.js +21 -0
- package/dist/relay-L76HDX72.js +46 -0
- package/dist/settings-J2652U5N.js +7 -0
- package/dist/starter-pack-LIZYMKYQ.js +10 -0
- package/dist/{status-I27IST4L.js → status-UJXWZRN5.js} +30 -13
- package/dist/{statusline-fast-3C5OXHDD.js → statusline-fast-SVTMBMD7.js} +3 -2
- package/dist/sync-cmd-4G4A7EVN.js +28 -0
- package/dist/version-BWM6VLDI.js +6 -0
- package/dist/{watch-3DTPJETH.js → watch-7SKJKGAA.js} +19 -8
- package/dist/{work-claim-cmd-5AUNCLS2.js → work-claim-cmd-5F7VZ42N.js} +34 -21
- package/dist/work-cmd-QAUZ7MTD.js +17 -0
- package/dist/work-comment-cmd-BNCSBTLH.js +106 -0
- package/dist/{work-launch-LT663PB3.js → work-launch-YI4CJDAP.js} +1 -1
- package/dist/work-open-pr-cmd-VFYZHIIW.js +156 -0
- package/dist/work-pr-cmd-PV32ZLSI.js +16 -0
- package/package.json +13 -3
- package/skill-pack/maintained/amend/SKILL.md +104 -0
- package/skill-pack/maintained/emitting-evidence/SKILL.md +108 -0
- package/skill-pack/maintained/emitting-evidence/references/agents-snippet.md +20 -0
- package/skill-pack/maintained/outerlayer/SKILL.md +55 -0
- package/skill-pack/maintained/reporting-findings/SKILL.md +148 -0
- package/skill-pack/template/build/SKILL.md +193 -0
- package/skill-pack/template/build/references/agent-briefs.md +243 -0
- package/skill-pack/template/build/references/criteria-judge.md +91 -0
- package/skill-pack/template/build/references/evidence.md +42 -0
- package/skill-pack/template/build/references/release.md +74 -0
- package/skill-pack/template/build/references/review-briefs.md +275 -0
- package/skill-pack/template/build/references/review-loop.md +158 -0
- package/skill-pack/template/build/scripts/record-criteria.mjs +235 -0
- package/skill-pack/template/spec/SKILL.md +84 -0
- package/skill-pack/template/writing-specs/SKILL.md +134 -0
- package/dist/chunk-DCNOXRMV.js +0 -589
- package/dist/chunk-JJP7YLMN.js +0 -25
- package/dist/chunk-OZ7C3XUE.js +0 -34
- package/dist/chunk-WQ6VGRGZ.js +0 -150
- package/dist/hook-fast-J5LCDHUJ.js +0 -8
- package/dist/import-capture-cmd-EUMBGIV3.js +0 -176
- package/dist/init-PTBITAUO.js +0 -103
- package/dist/login-cmd-IRX6LZT7.js +0 -62
- package/dist/loop-ASZRX3CZ.js +0 -648
- package/dist/sync-cmd-BBAUZ5JD.js +0 -17
- package/dist/work-cmd-2P4BVX47.js +0 -18
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { runWorkPr, WorkPrCommandError } from './chunk-7WERLFVR.js';
|
|
4
|
+
import { resolveCredentials } from './chunk-W5FVUZXV.js';
|
|
5
|
+
import './chunk-BFESLKPP.js';
|
|
6
|
+
import './chunk-WFJ65NUD.js';
|
|
7
|
+
import './chunk-4C4THABW.js';
|
|
8
|
+
import './chunk-WZBHBH3J.js';
|
|
9
|
+
import { defaultFetch } from './chunk-BCLJSQCV.js';
|
|
10
|
+
import './chunk-BOLTI6LR.js';
|
|
11
|
+
import './chunk-EABW6AJQ.js';
|
|
12
|
+
import './chunk-M5POMKKW.js';
|
|
13
|
+
import './chunk-ZM2IMMYN.js';
|
|
14
|
+
import './chunk-BJ3KTMDH.js';
|
|
15
|
+
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
16
|
+
import './chunk-Z5GEFMRW.js';
|
|
17
|
+
import { spawnSync } from 'child_process';
|
|
18
|
+
import { readFileSync } from 'fs';
|
|
19
|
+
|
|
20
|
+
createRequire(import.meta.url);
|
|
21
|
+
var GREEN = "\x1B[32m";
|
|
22
|
+
var DIM = "\x1B[2m";
|
|
23
|
+
var RESET = "\x1B[0m";
|
|
24
|
+
var ITEM_KEY_PREFIX = "olitem_";
|
|
25
|
+
var WorkOpenPrCommandError = class extends Error {
|
|
26
|
+
};
|
|
27
|
+
function spawnGh(args, cwd) {
|
|
28
|
+
const result = spawnSync("gh", args, { cwd, encoding: "utf8" });
|
|
29
|
+
if (result.error !== void 0) {
|
|
30
|
+
return { status: 127, stdout: "", stderr: `could not run gh: ${result.error.message}` };
|
|
31
|
+
}
|
|
32
|
+
return { status: result.status ?? 1, stdout: result.stdout, stderr: result.stderr };
|
|
33
|
+
}
|
|
34
|
+
function firstLine(text) {
|
|
35
|
+
return text.trim().split("\n")[0] ?? "";
|
|
36
|
+
}
|
|
37
|
+
function readBody(path) {
|
|
38
|
+
try {
|
|
39
|
+
return readFileSync(path, "utf8");
|
|
40
|
+
} catch (err) {
|
|
41
|
+
throw new WorkOpenPrCommandError(`cannot read --body-file ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function openThroughGateway(creds, workItem, title, body, fetchImpl) {
|
|
45
|
+
const endpoint = new URL(`/v1/work-items/${encodeURIComponent(workItem)}/pull-request`, creds.url).toString();
|
|
46
|
+
let response;
|
|
47
|
+
try {
|
|
48
|
+
response = await fetchImpl(endpoint, {
|
|
49
|
+
method: "PUT",
|
|
50
|
+
headers: {
|
|
51
|
+
"content-type": "application/json",
|
|
52
|
+
authorization: `Bearer ${creds.apiKey}`,
|
|
53
|
+
"x-outerlayer-app-id": creds.appId
|
|
54
|
+
},
|
|
55
|
+
body: JSON.stringify({ title, body })
|
|
56
|
+
});
|
|
57
|
+
} catch (err) {
|
|
58
|
+
throw new WorkOpenPrCommandError(`network error reaching ${endpoint}: ${String(err)}`);
|
|
59
|
+
}
|
|
60
|
+
let json;
|
|
61
|
+
try {
|
|
62
|
+
json = await response.json();
|
|
63
|
+
} catch {
|
|
64
|
+
throw new WorkOpenPrCommandError(
|
|
65
|
+
`${endpoint} answered ${response.status} with a body that is not JSON \u2014 the request may not have reached the gateway`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
if (!response.ok || !json.data) {
|
|
69
|
+
if (response.status === 401) {
|
|
70
|
+
throw new WorkOpenPrCommandError(
|
|
71
|
+
json.error?.message ?? "not authorized \u2014 the item key is unknown, expired, or its claim has ended."
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
throw new WorkOpenPrCommandError(json.error?.message ?? `request failed (${response.status})`);
|
|
75
|
+
}
|
|
76
|
+
return json.data;
|
|
77
|
+
}
|
|
78
|
+
async function openWithGh(opts, title, bodyFile, cwd) {
|
|
79
|
+
const runGh = opts.runGh ?? spawnGh;
|
|
80
|
+
const viewed = runGh(["pr", "view", "--json", "number"], cwd);
|
|
81
|
+
let existing;
|
|
82
|
+
if (viewed.status === 0) {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(viewed.stdout);
|
|
85
|
+
if (typeof parsed.number === "number") existing = parsed.number;
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const written = existing === void 0 ? runGh(["pr", "create", "--title", title, "--body-file", bodyFile], cwd) : runGh(["pr", "edit", String(existing), "--title", title, "--body-file", bodyFile], cwd);
|
|
90
|
+
if (written.status !== 0) {
|
|
91
|
+
const verb = existing === void 0 ? "create" : "edit";
|
|
92
|
+
throw new WorkOpenPrCommandError(`gh pr ${verb} failed: ${firstLine(written.stderr) || `exit ${written.status}`}`);
|
|
93
|
+
}
|
|
94
|
+
const urlMatch = /https:\/\/\S+\/pull\/(\d+)/.exec(written.stdout);
|
|
95
|
+
const number = existing ?? (urlMatch ? Number(urlMatch[1]) : void 0);
|
|
96
|
+
if (number === void 0) {
|
|
97
|
+
throw new WorkOpenPrCommandError("gh pr create printed no pull request URL, so there is no pull request to declare");
|
|
98
|
+
}
|
|
99
|
+
let declared;
|
|
100
|
+
try {
|
|
101
|
+
declared = await runWorkPr({
|
|
102
|
+
number,
|
|
103
|
+
repo: opts.repo,
|
|
104
|
+
sessionId: opts.sessionId,
|
|
105
|
+
cwd,
|
|
106
|
+
home: opts.home,
|
|
107
|
+
env: opts.env,
|
|
108
|
+
url: opts.url,
|
|
109
|
+
apiKey: opts.apiKey,
|
|
110
|
+
appId: opts.appId,
|
|
111
|
+
fetchImpl: opts.fetchImpl,
|
|
112
|
+
quiet: true
|
|
113
|
+
});
|
|
114
|
+
} catch (err) {
|
|
115
|
+
if (err instanceof WorkPrCommandError) {
|
|
116
|
+
throw new WorkOpenPrCommandError(
|
|
117
|
+
`the pull request is ${existing === void 0 ? "open" : "edited"} (#${number}) but declaring it on the item failed: ${err.message}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
const repository = declared.data.repository;
|
|
123
|
+
return {
|
|
124
|
+
repository,
|
|
125
|
+
number,
|
|
126
|
+
url: urlMatch ? urlMatch[0] : `https://${repository.startsWith("github.com/") ? repository : `github.com/${repository}`}/pull/${number}`,
|
|
127
|
+
created: existing === void 0
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async function runWorkOpenPr(opts) {
|
|
131
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
132
|
+
const env = opts.env ?? process.env;
|
|
133
|
+
if (opts.title === void 0 || opts.title.trim() === "") {
|
|
134
|
+
throw new WorkOpenPrCommandError("pass --title");
|
|
135
|
+
}
|
|
136
|
+
if (opts.bodyFile === void 0 || opts.bodyFile === "") {
|
|
137
|
+
throw new WorkOpenPrCommandError("pass --body-file <path>");
|
|
138
|
+
}
|
|
139
|
+
const body = readBody(opts.bodyFile);
|
|
140
|
+
const creds = resolveCredentials({ url: opts.url, apiKey: opts.apiKey, appId: opts.appId, home: opts.home ?? homeFromEnv(env), env });
|
|
141
|
+
let data;
|
|
142
|
+
if (creds.apiKey.startsWith(ITEM_KEY_PREFIX)) {
|
|
143
|
+
const workItem = env.OUTERLAYER_WORK;
|
|
144
|
+
if (workItem === void 0 || workItem === "") {
|
|
145
|
+
throw new WorkOpenPrCommandError("an item key names its work item through OUTERLAYER_WORK, which is not set");
|
|
146
|
+
}
|
|
147
|
+
data = await openThroughGateway(creds, workItem, opts.title, body, opts.fetchImpl ?? defaultFetch);
|
|
148
|
+
} else {
|
|
149
|
+
data = await openWithGh({ ...opts, env }, opts.title, opts.bodyFile, cwd);
|
|
150
|
+
}
|
|
151
|
+
const output = opts.json ? JSON.stringify(data) : `${GREEN}\u2713${RESET} ${data.created ? "opened" : "edited"} ${DIM}\xB7${RESET} ${data.repository}#${data.number} ${DIM}\xB7${RESET} ${data.url}`;
|
|
152
|
+
if (!opts.quiet) process.stdout.write(output + "\n");
|
|
153
|
+
return { data, output };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export { WorkOpenPrCommandError, runWorkOpenPr };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
export { WorkPrCommandError, runWorkPr } from './chunk-7WERLFVR.js';
|
|
4
|
+
import './chunk-BFESLKPP.js';
|
|
5
|
+
import './chunk-4C4THABW.js';
|
|
6
|
+
import './chunk-WZBHBH3J.js';
|
|
7
|
+
import './chunk-BCLJSQCV.js';
|
|
8
|
+
import './chunk-BOLTI6LR.js';
|
|
9
|
+
import './chunk-EABW6AJQ.js';
|
|
10
|
+
import './chunk-M5POMKKW.js';
|
|
11
|
+
import './chunk-ZM2IMMYN.js';
|
|
12
|
+
import './chunk-BJ3KTMDH.js';
|
|
13
|
+
import './chunk-VNVZDWO3.js';
|
|
14
|
+
import './chunk-Z5GEFMRW.js';
|
|
15
|
+
|
|
16
|
+
createRequire(import.meta.url);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@outerlayer/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "OuterLayer CLI: init
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "OuterLayer CLI — commands: init, connect, doctor, login, logout, sync, daemon (alias: watch), emit, work, runner, hooks, mcp, import, context. Capture and sync coding-agent sessions, and record proof of factory work.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Magu Studios, Inc.",
|
|
7
7
|
"repository": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"files": [
|
|
27
27
|
"dist",
|
|
28
28
|
"!dist/**/*.map",
|
|
29
|
+
"skill-pack",
|
|
29
30
|
"LICENSE"
|
|
30
31
|
],
|
|
31
32
|
"publishConfig": {
|
|
@@ -38,21 +39,30 @@
|
|
|
38
39
|
"test:mutate": "stryker run",
|
|
39
40
|
"typecheck": "tsc --noEmit",
|
|
40
41
|
"bench:hook": "node scripts/bench-hook.mjs",
|
|
41
|
-
"clean": "rm -rf dist"
|
|
42
|
+
"clean": "rm -rf dist",
|
|
43
|
+
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
44
|
+
"test:e2e:wsl2": "vitest run --config vitest.e2e-wsl2.config.ts",
|
|
45
|
+
"test:e2e:macos": "vitest run --config vitest.e2e-macos.config.ts"
|
|
42
46
|
},
|
|
43
47
|
"devDependencies": {
|
|
44
48
|
"@outerlayer/capture": "0.1.0",
|
|
45
49
|
"@outerlayer/context-format": "0.1.0",
|
|
50
|
+
"@outerlayer/runner-signature": "0.1.0",
|
|
46
51
|
"@outerlayer/session-schema": "0.1.0",
|
|
47
52
|
"@stryker-mutator/core": "10.0.0",
|
|
48
53
|
"@stryker-mutator/vitest-runner": "10.0.0",
|
|
49
54
|
"@types/node": "^22.20.1",
|
|
50
55
|
"commander": "^15.0.0",
|
|
56
|
+
"http-message-sig": "0.3.0",
|
|
57
|
+
"structured-headers": "2.0.3",
|
|
51
58
|
"tsup": "^8.5.0",
|
|
52
59
|
"typescript": "^5.5.3",
|
|
53
60
|
"vitest": "^4.1.8"
|
|
54
61
|
},
|
|
55
62
|
"engines": {
|
|
56
63
|
"node": ">=22"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@devcontainers/cli": "0.89.0"
|
|
57
67
|
}
|
|
58
68
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: amend
|
|
3
|
+
description: >
|
|
4
|
+
Answer the review threads waiting on an agent on an open pull request: read each
|
|
5
|
+
thread a person handed to an agent, fix what it says on the pull request's branch,
|
|
6
|
+
reply on the thread with a replacement artifact or a sentence, and push. Use when a
|
|
7
|
+
work item has threads waiting on an agent, or when asked to "answer the review" or
|
|
8
|
+
"fix what the reviewer failed". Invoke with /amend.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Answer every thread waiting on an agent on one work item's pull request, then
|
|
12
|
+
stop. A work item has one general thread and one thread per acceptance
|
|
13
|
+
criterion. A person's fail, or a plain comment from a person, leaves a thread
|
|
14
|
+
waiting on an agent. The person's newest comment says what they want. This
|
|
15
|
+
skill never records a verdict. Only a person passes or fails a thread. A
|
|
16
|
+
session can only reply.
|
|
17
|
+
|
|
18
|
+
A session never records a pass or fail on an artifact.
|
|
19
|
+
|
|
20
|
+
The work item is `OUTERLAYER_WORK`, the work item's number. Check out the pull
|
|
21
|
+
request's branch first if the working tree is not already on it.
|
|
22
|
+
|
|
23
|
+
## Arguments
|
|
24
|
+
|
|
25
|
+
- `--unattended`: no person is watching. Never ask a question. Anything that
|
|
26
|
+
needs a person becomes a hand-off (step 5), never a stop that leaves a thread
|
|
27
|
+
silently waiting on an agent.
|
|
28
|
+
|
|
29
|
+
## Steps
|
|
30
|
+
|
|
31
|
+
1. **Read the threads.**
|
|
32
|
+
`outerlayer work threads --item "$OUTERLAYER_WORK" --json` prints
|
|
33
|
+
`{ "general": <thread>, "criteria": [<thread>, ...] }`. Each thread carries
|
|
34
|
+
`criterion` (null for the general thread), `waitingOn` (`agent`, `person` or
|
|
35
|
+
null), `verdict`, `proof` and `comments`. Each comment carries `author`
|
|
36
|
+
(`kind` is `person` or `agent`), `body`, `action` and `artifact`. Keep the
|
|
37
|
+
threads whose `waitingOn` is `agent`. If none is, say so and stop: there is
|
|
38
|
+
nothing to answer. Then read `outerlayer work status --item "$OUTERLAYER_WORK" --json`
|
|
39
|
+
for the pull request in `pullRequests` (`repository`, `prNumber`).
|
|
40
|
+
2. **Re-orient.** Read the issue the item names and the pull request, including
|
|
41
|
+
its evidence comment, through the team's tracker tool or the git host. The
|
|
42
|
+
issue body is the spec. Find each thread's criterion there. Read every
|
|
43
|
+
person's comment since the last agent comment on the thread. Read them
|
|
44
|
+
literally: they are the reviewer's instruction.
|
|
45
|
+
3. **Decide each thread.** For each waiting thread, pick one:
|
|
46
|
+
- **Fix it.** The comment names something the change got wrong or did not
|
|
47
|
+
show. Change the code, tests, docs or evidence so the criterion is met as
|
|
48
|
+
written.
|
|
49
|
+
- **The spec is wrong.** The comment shows the criterion cannot be met as
|
|
50
|
+
written, for example because it names a path a repository rule forbids. A
|
|
51
|
+
session never rewrites a criterion on its own. Hand it off (step 5), naming
|
|
52
|
+
the exact wording change the owner should make.
|
|
53
|
+
- **Cannot tell.** The comment is ambiguous. Hand it off, quoting it and
|
|
54
|
+
saying what you would need to know.
|
|
55
|
+
4. **Fix, prove, reply.** For each thread you fix:
|
|
56
|
+
- Make the change on the pull request's branch. Keep it to what the comment
|
|
57
|
+
asks. Do not refactor around it.
|
|
58
|
+
- Run the tests that cover the criterion, then the repository's full
|
|
59
|
+
pre-push checks through a normal `git push`. Never bypass a hook. If the
|
|
60
|
+
git host drops the connection during a long gate, push again with
|
|
61
|
+
`GIT_SSH_COMMAND="ssh -o ServerAliveInterval=20 -o ServerAliveCountMax=60"`.
|
|
62
|
+
- **When the thread asks for new proof** (a criterion thread whose proof the
|
|
63
|
+
person failed), capture it after the fixed state exists, in the form the
|
|
64
|
+
criterion declares (`proof: screenshot` and so on) or the form of the
|
|
65
|
+
current proof. Emit it bound to that criterion, without `--replaces`.
|
|
66
|
+
Attaching it on the thread supersedes the old proof.
|
|
67
|
+
`outerlayer emit artifact <file> --caption "<what it shows>" --for <criterion>`.
|
|
68
|
+
Note the id it prints. Run `outerlayer sync` so the artifact reaches the
|
|
69
|
+
server. The attach looks it up there and refuses an artifact it cannot
|
|
70
|
+
find. Then reply:
|
|
71
|
+
`outerlayer work comment --item "$OUTERLAYER_WORK" --criterion <criterion> --body "<what changed>" --artifact <uuid> --attach`.
|
|
72
|
+
A replacement whose bytes match the current proof is flagged `unchanged`
|
|
73
|
+
and keeps the turn where the person's comment left it, so the thread
|
|
74
|
+
still waits on you.
|
|
75
|
+
- **Otherwise** (the general thread, or a fix that needs no new proof),
|
|
76
|
+
reply with a sentence naming what changed and the commit:
|
|
77
|
+
`outerlayer work comment --item "$OUTERLAYER_WORK" [--criterion <criterion>] --body "<what changed>" --ready`.
|
|
78
|
+
Omit `--criterion` on the general thread.
|
|
79
|
+
5. **Hand off what you cannot fix.** Reply on the thread with `--ready` and a
|
|
80
|
+
body of one sentence of fact, then the decision a person must make. For a
|
|
81
|
+
spec problem, quote the criterion and give the replacement wording.
|
|
82
|
+
`--ready` hands the thread back to a person.
|
|
83
|
+
6. **Check nothing is left waiting.** Read the threads again. No thread you
|
|
84
|
+
started with may still be waiting on an agent. If one is, the reply did not
|
|
85
|
+
land or was flagged `unchanged`. Find out why and fix it, or report it.
|
|
86
|
+
7. **Report.** Comment once on the pull request: one line per thread, naming the
|
|
87
|
+
criterion (or the general thread) and saying fixed (with the replacement) or
|
|
88
|
+
handed off (with the reason). Plain language. No session labels, no raw
|
|
89
|
+
command output.
|
|
90
|
+
|
|
91
|
+
## Rules
|
|
92
|
+
|
|
93
|
+
- **Answer only threads waiting on an agent.** A thread waiting on a person, or
|
|
94
|
+
on no one, is not yours to reply on.
|
|
95
|
+
- **One thread, one proof.** Never attach an artifact that does not prove that
|
|
96
|
+
thread's criterion. The attach refuses an artifact bound to a different
|
|
97
|
+
criterion.
|
|
98
|
+
- **A person decides.** Never pass `--pass` or `--fail`, never mark a check as
|
|
99
|
+
a person, and never edit the issue's criteria.
|
|
100
|
+
- **Stay on the branch.** Push to the pull request's own branch. Never open a
|
|
101
|
+
second pull request, and never force-push over someone else's commits.
|
|
102
|
+
- **Write for a reader who was not here.** Every comment, commit and reply
|
|
103
|
+
follows the repository's writing rule: outcome first, short sentences, plain
|
|
104
|
+
words.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: emitting-evidence
|
|
3
|
+
description: >
|
|
4
|
+
Emit artifacts — screenshots, recordings, reports, logs — as proof that a
|
|
5
|
+
change works, bound to the pull request via `outerlayer emit artifact`.
|
|
6
|
+
Use when finishing work a spec criterion or tracked requirement covers,
|
|
7
|
+
when asked to "provide evidence", "attach a screenshot", or "prove it
|
|
8
|
+
works", and in CI steps that produce verifiable output.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
An artifact is an exhibit: evidence *of* a specific change, anchored to the
|
|
12
|
+
pull request and rendered in its evidence comment. Emit the few exhibits the
|
|
13
|
+
spec asks for — a reviewer should see the change working, not a gallery.
|
|
14
|
+
|
|
15
|
+
## When to capture
|
|
16
|
+
|
|
17
|
+
Capture AFTER the state exists, never before: run the app, the test, or the
|
|
18
|
+
flow first, and shoot the working result. A screenshot of code is not
|
|
19
|
+
evidence; a screenshot of the rendered page is. If the state takes setup
|
|
20
|
+
(seed data, a logged-in user), finish the setup, verify by eye, then capture.
|
|
21
|
+
|
|
22
|
+
## Per-kind mechanics
|
|
23
|
+
|
|
24
|
+
Kind is inferred from the file's media type — name files honestly. Every
|
|
25
|
+
artifact shares one upload cap of 8 MiB, whatever its kind:
|
|
26
|
+
|
|
27
|
+
- **screenshot** (`.png`, `.jpg`) — one focused window or region showing the
|
|
28
|
+
proven state. Crop noise; keep enough chrome (URL bar, test summary line)
|
|
29
|
+
to show it is real.
|
|
30
|
+
- **video** (`.webm`, `.mp4`) — a short recording of the flow, start to
|
|
31
|
+
outcome. Video is the kind most likely to hit the 8 MiB cap: trim dead
|
|
32
|
+
time, prefer webm.
|
|
33
|
+
- **report** (`.html`, `.pdf`) — generated reports: coverage, benchmark,
|
|
34
|
+
audit output. Emit the file the tool produced, unedited.
|
|
35
|
+
- **log** (`.txt`, `.log`) — command output proving a run happened: test
|
|
36
|
+
runs, migrations, gate output. Pipe to a file and emit that file.
|
|
37
|
+
|
|
38
|
+
- **test** (`.xml`) — JUnit XML results from a test runner, for a criterion
|
|
39
|
+
that declares `"proof": "test"`. Emit the file the runner wrote:
|
|
40
|
+
`outerlayer emit artifact results.xml --caption "…" --for LOGIN-01`. Every
|
|
41
|
+
test in the file binds. To bind only some, add `--test "<name>"` once per
|
|
42
|
+
test, or `--test "<name>=<path>:<line>"` to give where the test is written
|
|
43
|
+
when the runner's file does not. The criterion is proven only when every
|
|
44
|
+
bound test passed, at the pull request's head commit: run the tests again
|
|
45
|
+
and emit again after a fix, with `--replaces` for the old results.
|
|
46
|
+
|
|
47
|
+
Anything else uploads as plain `file` — it is never guessed into a stronger
|
|
48
|
+
kind, so a `.mov` will NOT count where a video is required; convert first.
|
|
49
|
+
|
|
50
|
+
## Captions
|
|
51
|
+
|
|
52
|
+
One sentence, present tense, saying what the exhibit shows and what that
|
|
53
|
+
proves: "Signup blocked for a disallowed domain — the 403 page renders."
|
|
54
|
+
Never put secrets, tokens, or personal data in the caption — or the pixels.
|
|
55
|
+
|
|
56
|
+
## Binding with --for
|
|
57
|
+
|
|
58
|
+
Record the item's acceptance criteria before you bind evidence to them:
|
|
59
|
+
|
|
60
|
+
outerlayer emit criteria criteria.json
|
|
61
|
+
|
|
62
|
+
The file is `{"criteria": [{"id": "LOGIN-01", "text": "…", "proof": null}]}`,
|
|
63
|
+
one entry per criterion as your team wrote it. `proof` is the artifact kind
|
|
64
|
+
that proves it (`video`, `screenshot`, `report`, `log`, `test`, `file`) or `null`. The
|
|
65
|
+
id is yours, 1 to 64 characters of `A-Z a-z 0-9 . _ : -`. The list is the
|
|
66
|
+
only source for the item's Criteria tab: nothing reads criteria out of the
|
|
67
|
+
issue. Once an item has a list, an agent session cannot replace it; ask a
|
|
68
|
+
person to run the command again outside the session.
|
|
69
|
+
|
|
70
|
+
When a criterion is the reason you captured, bind the artifact to its
|
|
71
|
+
recorded id:
|
|
72
|
+
|
|
73
|
+
outerlayer emit artifact shot.png --caption "…" --for LOGIN-01
|
|
74
|
+
|
|
75
|
+
The PR comment's Evidence section shows the id next to the artifact, and the
|
|
76
|
+
dashboard's Criteria tab lists the artifact on that criterion's row. An id
|
|
77
|
+
the recorded list does not hold gets no row.
|
|
78
|
+
|
|
79
|
+
## The noise rule
|
|
80
|
+
|
|
81
|
+
Satisfy the declared proofs; don't document everything. One exhibit per
|
|
82
|
+
criterion is the norm. An unrequested artifact is worth emitting only when
|
|
83
|
+
it would change how a reviewer reads the diff. When in doubt, leave it out —
|
|
84
|
+
evidence works because there is little of it.
|
|
85
|
+
|
|
86
|
+
## Mechanics
|
|
87
|
+
|
|
88
|
+
Inside a recorded session, just run the command — the artifact spools
|
|
89
|
+
locally and uploads with the next `outerlayer sync`, bound to this session
|
|
90
|
+
and turn. In CI, run it after the step that produced the file (repo and PR
|
|
91
|
+
come from the CI environment). From a plain machine it anchors through the
|
|
92
|
+
git checkout, or pass `--pr <n>` explicitly. If there is nothing to attach
|
|
93
|
+
to, the command refuses — emit from the work, not from nowhere.
|
|
94
|
+
|
|
95
|
+
## Report artifacts the default checks read
|
|
96
|
+
|
|
97
|
+
Two report artifacts feed the default checks. Bind each to its check name with `--for`:
|
|
98
|
+
|
|
99
|
+
- `--for code-review-ran` for the review report, an HTML file.
|
|
100
|
+
- `--for acceptance-criteria` for the judge's report on each criterion, an HTML file. Emit it only when the issue has criteria.
|
|
101
|
+
|
|
102
|
+
outerlayer emit artifact review.html --caption "Review of the change and what it found" --for code-review-ran
|
|
103
|
+
|
|
104
|
+
A session never records a pass or fail on an artifact. It emits the artifact, and a person or a check decides.
|
|
105
|
+
|
|
106
|
+
## Instructions-file snippet
|
|
107
|
+
|
|
108
|
+
The text to append to a repository's instructions file is in [references/agents-snippet.md](references/agents-snippet.md).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!-- outerlayer:capture-pack -->
|
|
2
|
+
## Emitting evidence
|
|
3
|
+
|
|
4
|
+
When you finish work a spec criterion or tracked requirement covers,
|
|
5
|
+
capture the working state and emit it:
|
|
6
|
+
|
|
7
|
+
outerlayer emit artifact <file> --caption "what it shows" [--for <criterion-id>] [--pr <n>]
|
|
8
|
+
|
|
9
|
+
Rules: capture AFTER the state exists (run it, then shoot it); kind comes
|
|
10
|
+
from the file type (png/jpg screenshot, webm/mp4 video, html/pdf report,
|
|
11
|
+
txt/log log — anything else is a plain file); every artifact caps at 8
|
|
12
|
+
MiB, and video is the kind most likely to hit it; captions are one
|
|
13
|
+
present-tense sentence with no secrets; bind an artifact to the id of the
|
|
14
|
+
requirement it proves — `--for <id>`, from the team's own spec or
|
|
15
|
+
tracker — and pick the kind that shows the state best; the platform
|
|
16
|
+
records the kind and the id and shows them next to the artifact; satisfy
|
|
17
|
+
what's asked for and stop — don't document everything. Inside a recorded
|
|
18
|
+
session the artifact uploads on the next `outerlayer sync`; in CI it
|
|
19
|
+
anchors via the CI environment; otherwise the git checkout or `--pr`
|
|
20
|
+
anchors it, and with nothing to attach to the command refuses.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: outerlayer
|
|
3
|
+
description: >
|
|
4
|
+
Explain what OuterLayer is and how work flows through it: work items, the
|
|
5
|
+
OUTERLAYER_WORK variable, the factory, and building an issue or specifying one.
|
|
6
|
+
Use when asked about OuterLayer, a work item, OUTERLAYER_WORK, "build issue
|
|
7
|
+
42", how an issue becomes a pull request, or how to attach evidence or answer
|
|
8
|
+
a review.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
OuterLayer is a software factory. An issue goes in. A reviewed and verified
|
|
12
|
+
pull request comes out, with the evidence that backs it. An agent builds it in
|
|
13
|
+
this repository, on the machine it already runs on.
|
|
14
|
+
|
|
15
|
+
## Work items
|
|
16
|
+
|
|
17
|
+
An approved issue becomes a work item. A work item has its own number, which
|
|
18
|
+
is not the issue's number. Everything the factory records about the build
|
|
19
|
+
hangs off that number: the sessions, the evidence and the review threads.
|
|
20
|
+
|
|
21
|
+
`OUTERLAYER_WORK` carries the work item's number, never the issue's. A session
|
|
22
|
+
started without it is not recorded against any work item. Do not guess the
|
|
23
|
+
number from an issue reference. Read it from `outerlayer work status --issue <n>`, or
|
|
24
|
+
from the output of `outerlayer work build`.
|
|
25
|
+
|
|
26
|
+
## The loop
|
|
27
|
+
|
|
28
|
+
1. `/spec` agrees an issue with a person and files it once they approve.
|
|
29
|
+
2. A person adds the issue on the Work page and presses **Build**. A host
|
|
30
|
+
that runs work picks it up and builds it with `/build`: implement, review,
|
|
31
|
+
run the repository's checks and open the pull request.
|
|
32
|
+
3. An agent asked to add work runs `outerlayer work build --issue <n>`
|
|
33
|
+
instead. With `--local`, the person builds it on their own machine with
|
|
34
|
+
`OUTERLAYER_WORK=<number> claude "/build"`.
|
|
35
|
+
4. `outerlayer emit artifact <file> --caption "<what it shows>"` attaches a
|
|
36
|
+
screenshot, recording, report or log as evidence. The `emitting-evidence`
|
|
37
|
+
skill says what to capture.
|
|
38
|
+
5. `/amend` answers the review threads a person handed to an agent.
|
|
39
|
+
|
|
40
|
+
Launching recorded work needs Claude Code. It is the one agent whose sessions
|
|
41
|
+
OuterLayer can start with `OUTERLAYER_WORK` set and record. Other agents can
|
|
42
|
+
read the files this repository emits, but they cannot be launched as work.
|
|
43
|
+
|
|
44
|
+
## Reading work items
|
|
45
|
+
|
|
46
|
+
When the OuterLayer MCP server is configured in this repository, its
|
|
47
|
+
`list_work_items` tool lists the factory's work items. Its `get_work_item`
|
|
48
|
+
tool reads one by number. It reads the key from `outerlayer login`, so if its
|
|
49
|
+
tools fail, run `outerlayer login` and reconnect the server.
|
|
50
|
+
|
|
51
|
+
## Keeping this repository's context current
|
|
52
|
+
|
|
53
|
+
`outerlayer context emit` compiles `.outerlayer/` into the files each agent
|
|
54
|
+
tool reads, such as `.claude/skills/`. This skill is maintained: that command
|
|
55
|
+
rewrites it from the installed CLI, so edits to it do not last.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reporting-findings
|
|
3
|
+
description: >
|
|
4
|
+
Records factory problems: anything in a repository's instructions, tests, tooling or
|
|
5
|
+
local setup that cost an agent time and was not caused by its own change. Use when
|
|
6
|
+
writing a "Factory problems" section, a build or review report, a retro or session
|
|
7
|
+
summary, or any list of blockers, gotchas, flaky tests or wrong instructions. Also
|
|
8
|
+
use it the moment a CLAUDE.md, AGENTS.md or skill sentence turns out wrong or stale,
|
|
9
|
+
a test passes on rerun with no change, a gate or command misbehaves, or a setup step
|
|
10
|
+
fails for a reason outside the diff. Defines the six categories, what evidence each
|
|
11
|
+
carries, the JSON batch shape, and the `outerlayer emit finding` command.
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## What this is for
|
|
15
|
+
|
|
16
|
+
A finding is a problem in the factory, not in the change: an instruction
|
|
17
|
+
that was wrong, a test that flaked, a tool or gate that misbehaved, a local
|
|
18
|
+
setup trap, or a bug outside the diff. Record one whenever a report, a
|
|
19
|
+
summary or a "Factory problems" section lists such a problem, so the next
|
|
20
|
+
agent does not pay for it again. Findings are informational. They show on the
|
|
21
|
+
work item page, grouped by category, and nothing reads them to pass or fail
|
|
22
|
+
the work.
|
|
23
|
+
|
|
24
|
+
## Categories
|
|
25
|
+
|
|
26
|
+
Each finding has exactly one category. Put the evidence in the title, file,
|
|
27
|
+
line and rule fields.
|
|
28
|
+
|
|
29
|
+
### `context`
|
|
30
|
+
|
|
31
|
+
An instruction, skill or rule that is wrong, broken, or silent on something you
|
|
32
|
+
had to discover. Include the path of the file that holds the rule and its line.
|
|
33
|
+
Set `--rule-relation` to one of:
|
|
34
|
+
|
|
35
|
+
- `wrong`: the rule says the wrong thing. Quote the sentence.
|
|
36
|
+
- `broken`: the rule cannot work, such as a path or command that no longer
|
|
37
|
+
exists. Quote the sentence.
|
|
38
|
+
- `missing`: no rule covers what you had to discover. There is no sentence to
|
|
39
|
+
quote. Name the file where it belongs.
|
|
40
|
+
|
|
41
|
+
### `flaky-test`
|
|
42
|
+
|
|
43
|
+
A test that failed and then passed with no change. Include the test file and
|
|
44
|
+
test name, and both commands: the one that failed and the one that passed.
|
|
45
|
+
|
|
46
|
+
### `tooling`
|
|
47
|
+
|
|
48
|
+
A command, script or gate that misbehaved. Include the command, what you
|
|
49
|
+
expected, and what happened.
|
|
50
|
+
|
|
51
|
+
### `environment`
|
|
52
|
+
|
|
53
|
+
A local setup problem: a port in use, a shared database, a missing
|
|
54
|
+
dependency. Include what you needed and what you found.
|
|
55
|
+
|
|
56
|
+
### `defect`
|
|
57
|
+
|
|
58
|
+
A bug in code outside your own change. Include the file, the line, and how to
|
|
59
|
+
reproduce it.
|
|
60
|
+
|
|
61
|
+
### `other`
|
|
62
|
+
|
|
63
|
+
Anything else that cost you time and fits none of the above. Describe it in the
|
|
64
|
+
title.
|
|
65
|
+
|
|
66
|
+
## The command
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
outerlayer emit finding --id <short-id> --category <category> \
|
|
70
|
+
--title "<one sentence>" --file <path> [--line <n>] --where "<what you were doing>"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Inside a recorded session the work item is found automatically. Outside one,
|
|
74
|
+
add `--item <number>`.
|
|
75
|
+
|
|
76
|
+
Examples, one per category:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
outerlayer emit finding --id worktree-hooks --category context \
|
|
80
|
+
--title "No document says a fresh worktree has no git hooks until yarn prepare runs" \
|
|
81
|
+
--file AGENTS.md --rule-path AGENTS.md --rule-relation missing \
|
|
82
|
+
--where "first push from a new worktree"
|
|
83
|
+
|
|
84
|
+
outerlayer emit finding --id stale-path --category context \
|
|
85
|
+
--title "The build skill names a directory that no longer exists" \
|
|
86
|
+
--file .outerlayer/skills/build/SKILL.md --line 42 \
|
|
87
|
+
--rule-path .outerlayer/skills/build/SKILL.md --rule-line 42 --rule-relation broken \
|
|
88
|
+
--rule-quote "Read the plan from reports/plan.md" \
|
|
89
|
+
--where "implementation stage"
|
|
90
|
+
|
|
91
|
+
outerlayer emit finding --id flaky-refresh --category flaky-test \
|
|
92
|
+
--title "refresh.test.ts fails once, then passes on rerun" \
|
|
93
|
+
--file apps/tenant-dashboard/src/lib/system/pr-session-comment/__tests__/refresh.test.ts \
|
|
94
|
+
--where "vitest run <file> failed, then passed with no change"
|
|
95
|
+
|
|
96
|
+
outerlayer emit finding --id gate-skip --category tooling \
|
|
97
|
+
--title "The pre-push gate prints green when a step was skipped" \
|
|
98
|
+
--file scripts/git/pre-push-checks.mjs \
|
|
99
|
+
--where "expected a failure, got exit 0"
|
|
100
|
+
|
|
101
|
+
outerlayer emit finding --id shared-db --category environment \
|
|
102
|
+
--title "A peer session reset the shared Supabase project mid-run" \
|
|
103
|
+
--file apps/tenant-dashboard/supabase/config.toml \
|
|
104
|
+
--where "needed a stable database, found it re-seeded"
|
|
105
|
+
|
|
106
|
+
outerlayer emit finding --id null-read --category defect \
|
|
107
|
+
--title "readItem throws on an item with no claim" \
|
|
108
|
+
--file src/lib/work/read.ts --line 88 \
|
|
109
|
+
--where "reproduce: call readItem on a fresh item"
|
|
110
|
+
|
|
111
|
+
outerlayer emit finding --id slow-hook --category other \
|
|
112
|
+
--title "The session-start hook adds about ten seconds on every launch" \
|
|
113
|
+
--file .husky/post-checkout --where "timed with time(1)"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
To record several at once, write a JSON batch and run
|
|
117
|
+
`outerlayer emit findings <file>`. The shape is:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"schemaVersion": 2,
|
|
122
|
+
"headSha": "<branch head, optional>",
|
|
123
|
+
"findings": [
|
|
124
|
+
{
|
|
125
|
+
"id": "<category>-<k>",
|
|
126
|
+
"category": "tooling",
|
|
127
|
+
"title": "<one sentence>",
|
|
128
|
+
"file": "<path>",
|
|
129
|
+
"line": null,
|
|
130
|
+
"rule": null,
|
|
131
|
+
"where": { "label": "<what the agent was doing, in plain words>" }
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`file` and `title` are always set; `line` and `rule` are `null` when there
|
|
138
|
+
is nothing to put in them. A `context` finding fills `rule` with
|
|
139
|
+
`{ "path", "line", "quote", "relation" }`. Leave `quote` out, rather than
|
|
140
|
+
`null`, when the relation is `missing`.
|
|
141
|
+
|
|
142
|
+
## When not to record one
|
|
143
|
+
|
|
144
|
+
- A problem in your own change is not recorded. Fix it.
|
|
145
|
+
- A cost you did not really pay is not recorded. If you noticed it in passing
|
|
146
|
+
and lost no time, leave it.
|
|
147
|
+
- A problem you already recorded on this work item is not recorded twice.
|
|
148
|
+
Re-using the same `--id` replaces the earlier finding.
|