@outerlayer/cli 0.1.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +238 -77
  3. package/dist/agent-setup-IFH3FB5X.js +197 -0
  4. package/dist/build-TZUUTRGH.js +9 -0
  5. package/dist/build-info.json +1 -1
  6. package/dist/check-WQP2CZDW.js +94 -0
  7. package/dist/chunk-2E54Z3P5.js +232 -0
  8. package/dist/chunk-3TT7RQYB.js +55 -0
  9. package/dist/chunk-3YPKXE5L.js +1683 -0
  10. package/dist/{chunk-HNCCD2IX.js → chunk-5DLBAXY5.js} +209 -61
  11. package/dist/{chunk-R5KBGVII.js → chunk-5QRP3MVS.js} +30 -14
  12. package/dist/chunk-5Y3QQRUH.js +300 -0
  13. package/dist/chunk-6Y63SXFO.js +383 -0
  14. package/dist/chunk-774HEQL6.js +178 -0
  15. package/dist/chunk-7A6RVXPZ.js +2233 -0
  16. package/dist/chunk-7YIOOFVJ.js +71 -0
  17. package/dist/chunk-ABIWDUMS.js +104 -0
  18. package/dist/chunk-AUFG23AA.js +534 -0
  19. package/dist/chunk-B2G7JHHB.js +42 -0
  20. package/dist/chunk-BCJNHMZT.js +34 -0
  21. package/dist/chunk-BCLJSQCV.js +56 -0
  22. package/dist/chunk-BFESLKPP.js +61 -0
  23. package/dist/chunk-BJ3KTMDH.js +63 -0
  24. package/dist/chunk-BKO6JEZI.js +47 -0
  25. package/dist/chunk-BOLTI6LR.js +37 -0
  26. package/dist/chunk-CBPPJSAR.js +89 -0
  27. package/dist/chunk-CBZB6XY6.js +102 -0
  28. package/dist/{chunk-RCQXYLMO.js → chunk-DVDEBNQJ.js} +18 -18
  29. package/dist/{work-pr-cmd-GOYSPEN2.js → chunk-DVYHCMB2.js} +10 -25
  30. package/dist/{chunk-TFUIDMOB.js → chunk-EABW6AJQ.js} +12 -1
  31. package/dist/{chunk-KFYJV2ZG.js → chunk-EMY4I27X.js} +1 -1
  32. package/dist/{chunk-NTTPJV35.js → chunk-F47JBIAW.js} +3 -1
  33. package/dist/chunk-F6GFZXZ3.js +60 -0
  34. package/dist/{chunk-YOOSBOKS.js → chunk-F7CU5ABH.js} +1 -1
  35. package/dist/chunk-FAJSS2WN.js +1357 -0
  36. package/dist/chunk-FS2VYGZT.js +1764 -0
  37. package/dist/chunk-FV2CCGXK.js +9 -0
  38. package/dist/{chunk-BQB3U2VD.js → chunk-G7FSV7JX.js} +5113 -3729
  39. package/dist/{emit-cmd-TWSYYEZI.js → chunk-H5NCNVDA.js} +68 -17
  40. package/dist/chunk-HE2D4EY5.js +83 -0
  41. package/dist/chunk-L2DBRKRA.js +94 -0
  42. package/dist/chunk-LF6MLJCY.js +38 -0
  43. package/dist/chunk-M5POMKKW.js +1051 -0
  44. package/dist/{mcp-install-cmd-FDQH6SEN.js → chunk-MQ3IPHIZ.js} +35 -18
  45. package/dist/{chunk-D77LS3UI.js → chunk-N5FOE5PS.js} +33 -4
  46. package/dist/chunk-N6LURUEF.js +64 -0
  47. package/dist/chunk-NYC54VBD.js +1084 -0
  48. package/dist/chunk-OGFGZQA3.js +23 -0
  49. package/dist/chunk-QOZKPLVJ.js +226 -0
  50. package/dist/chunk-RA3O54FC.js +30 -0
  51. package/dist/chunk-TBT347UY.js +41 -0
  52. package/dist/{chunk-3VQ4XXQA.js → chunk-UFFXNXLQ.js} +115 -71
  53. package/dist/chunk-USO2DKBD.js +421 -0
  54. package/dist/chunk-W4SQCOZU.js +206 -0
  55. package/dist/{chunk-A3WLZX2F.js → chunk-WIOZAJ2W.js} +15 -2
  56. package/dist/chunk-WO2BXCTQ.js +83 -0
  57. package/dist/chunk-WZBHBH3J.js +20 -0
  58. package/dist/{chunk-IWIUYLDR.js → chunk-XCCLFVXM.js} +118 -189
  59. package/dist/{context-materialize-J6CRQ7O7.js → chunk-XDDW4FRS.js} +134 -26
  60. package/dist/chunk-YYYXRJUW.js +101 -0
  61. package/dist/chunk-ZM2IMMYN.js +76 -0
  62. package/dist/chunk-ZMYPLFG3.js +71 -0
  63. package/dist/chunk-ZNA27WEV.js +470 -0
  64. package/dist/{cli-EFJIG2VT.js → cli-NHUXABYH.js} +597 -965
  65. package/dist/{paths-D2VGWWFI.js → cli-build-K56DK4DS.js} +1 -1
  66. package/dist/config-XVJYZ4IQ.js +9 -0
  67. package/dist/connect-cmd-OOG4TPU7.js +16 -0
  68. package/dist/context-adopt-UMA4O5NY.js +105 -0
  69. package/dist/context-materialize-G2FUFC72.js +19 -0
  70. package/dist/dist-RW4UPPC2.js +6 -0
  71. package/dist/docker-NEGDLU6D.js +7 -0
  72. package/dist/doctor-53F2PYY7.js +43 -0
  73. package/dist/doctor-UFVL4PZY.js +426 -0
  74. package/dist/{emit-artifact-cmd-KPPD3YLO.js → emit-artifact-cmd-KTYW2TN2.js} +25 -19
  75. package/dist/emit-cmd-DCERAU27.js +9 -0
  76. package/dist/emit-criteria-cmd-VAUUSAYD.js +166 -0
  77. package/dist/{emit-finding-cmd-VR5VEGXJ.js → emit-finding-cmd-UR4ID45Z.js} +35 -30
  78. package/dist/{emit-result-cmd-YOA5BJKL.js → emit-result-cmd-FDTUKGKE.js} +35 -61
  79. package/dist/exec-client-4XGXV3XN.js +8 -0
  80. package/dist/guest-init-GENRHNP7.js +8 -0
  81. package/dist/hook-fast-EYENPK6F.js +9 -0
  82. package/dist/{hook-wrap-fast-KXYNX3AD.js → hook-wrap-fast-NPLHJXFK.js} +2 -2
  83. package/dist/host-key-KDYZ5ECC.js +7 -0
  84. package/dist/import-capture-cmd-AVZ4VIVJ.js +68 -0
  85. package/dist/{import-ruler-cmd-7LH2QOMG.js → import-ruler-cmd-GUHL6IY5.js} +1 -1
  86. package/dist/index.js +4 -4
  87. package/dist/init-DXBCOAJK.js +36 -0
  88. package/dist/init-YEXK35CF.js +221 -0
  89. package/dist/init-cmd-NHR7PRSQ.js +135 -0
  90. package/dist/install-cmd-7WXOIFDO.js +55 -0
  91. package/dist/lima-XN65D7GN.js +55 -0
  92. package/dist/login-browser-7XGSV7MA.js +10 -0
  93. package/dist/{config-POF7DEQW.js → logout-cmd-QAUFDJ6J.js} +3 -1
  94. package/dist/{logs-TRNPQM42.js → logs-7RYUH5A6.js} +1 -1
  95. package/dist/loop-VZOLL4WQ.js +43 -0
  96. package/dist/machine-WSG52J75.js +35 -0
  97. package/dist/mcp-install-cmd-RLK4SO3N.js +9 -0
  98. package/dist/{mcp-serve-cmd-57EZZOTL.js → mcp-serve-cmd-JPTP5FMS.js} +20 -9
  99. package/dist/paths-OYKMVYJP.js +6 -0
  100. package/dist/{pidfile-PTW76F56.js → pidfile-UZRH774M.js} +2 -3
  101. package/dist/real-deps-SU24ZA2K.js +21 -0
  102. package/dist/relay-L76HDX72.js +46 -0
  103. package/dist/settings-J2652U5N.js +7 -0
  104. package/dist/starter-pack-LIZYMKYQ.js +10 -0
  105. package/dist/{status-JHVSZYC5.js → status-2UZKKNB7.js} +31 -12
  106. package/dist/{statusline-fast-3C5OXHDD.js → statusline-fast-SVTMBMD7.js} +3 -2
  107. package/dist/sync-cmd-VPPYCNNM.js +28 -0
  108. package/dist/version-BWM6VLDI.js +6 -0
  109. package/dist/{watch-NRLXJUST.js → watch-5C4ZBBGO.js} +20 -7
  110. package/dist/{work-claim-cmd-HXB7K27H.js → work-claim-cmd-LSO2B2EX.js} +34 -19
  111. package/dist/work-cmd-UOQO2AD4.js +17 -0
  112. package/dist/work-comment-cmd-WF3EOLAE.js +106 -0
  113. package/dist/{work-launch-YNNCKIH3.js → work-launch-YI4CJDAP.js} +2 -1
  114. package/dist/work-open-pr-cmd-4SSQZMYG.js +156 -0
  115. package/dist/work-pr-cmd-6AN6RRQJ.js +16 -0
  116. package/package.json +13 -3
  117. package/skill-pack/maintained/amend/SKILL.md +104 -0
  118. package/skill-pack/maintained/emitting-evidence/SKILL.md +99 -0
  119. package/skill-pack/maintained/emitting-evidence/references/agents-snippet.md +20 -0
  120. package/skill-pack/maintained/outerlayer/SKILL.md +55 -0
  121. package/skill-pack/maintained/reporting-findings/SKILL.md +148 -0
  122. package/skill-pack/template/build/SKILL.md +193 -0
  123. package/skill-pack/template/build/references/agent-briefs.md +243 -0
  124. package/skill-pack/template/build/references/criteria-judge.md +91 -0
  125. package/skill-pack/template/build/references/evidence.md +42 -0
  126. package/skill-pack/template/build/references/release.md +74 -0
  127. package/skill-pack/template/build/references/review-briefs.md +275 -0
  128. package/skill-pack/template/build/references/review-loop.md +158 -0
  129. package/skill-pack/template/build/scripts/record-criteria.mjs +235 -0
  130. package/skill-pack/template/spec/SKILL.md +84 -0
  131. package/skill-pack/template/writing-specs/SKILL.md +134 -0
  132. package/dist/chunk-JJP7YLMN.js +0 -25
  133. package/dist/chunk-KKEU6FFR.js +0 -589
  134. package/dist/chunk-OZ7C3XUE.js +0 -34
  135. package/dist/chunk-WQ6VGRGZ.js +0 -150
  136. package/dist/emit-commit-credit-cmd-656BRAQ4.js +0 -121
  137. package/dist/hook-fast-I2XTHDE6.js +0 -8
  138. package/dist/import-capture-cmd-EUMBGIV3.js +0 -176
  139. package/dist/init-PTBITAUO.js +0 -103
  140. package/dist/login-cmd-IRX6LZT7.js +0 -62
  141. package/dist/loop-BWE2BS6J.js +0 -646
  142. package/dist/sync-cmd-S7Q6DWU3.js +0 -15
  143. package/dist/work-cmd-ABP6XDBP.js +0 -16
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
3
+ import { runWorkPr, WorkPrCommandError } from './chunk-DVYHCMB2.js';
4
+ import { resolveCredentials } from './chunk-5DLBAXY5.js';
5
+ import './chunk-BFESLKPP.js';
6
+ import './chunk-6Y63SXFO.js';
7
+ import './chunk-G7FSV7JX.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-DVYHCMB2.js';
4
+ import './chunk-BFESLKPP.js';
5
+ import './chunk-G7FSV7JX.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.1.1",
4
- "description": "OuterLayer CLI: init/doctor (hooks + daemon onboarding), watch, scan, and eval (validate/mine/qualify/report — execution-verified evals from your own PRs)",
3
+ "version": "0.4.0",
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,99 @@
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
+ Anything else uploads as plain `file` — it is never guessed into a stronger
39
+ kind, so a `.mov` will NOT count where a video is required; convert first.
40
+
41
+ ## Captions
42
+
43
+ One sentence, present tense, saying what the exhibit shows and what that
44
+ proves: "Signup blocked for a disallowed domain — the 403 page renders."
45
+ Never put secrets, tokens, or personal data in the caption — or the pixels.
46
+
47
+ ## Binding with --for
48
+
49
+ Record the item's acceptance criteria before you bind evidence to them:
50
+
51
+ outerlayer emit criteria criteria.json
52
+
53
+ The file is `{"criteria": [{"id": "LOGIN-01", "text": "…", "proof": null}]}`,
54
+ one entry per criterion as your team wrote it. `proof` is the artifact kind
55
+ that proves it (`video`, `screenshot`, `report`, `log`, `file`) or `null`. The
56
+ id is yours, 1 to 64 characters of `A-Z a-z 0-9 . _ : -`. The list is the
57
+ only source for the item's Criteria tab: nothing reads criteria out of the
58
+ issue. Once an item has a list, an agent session cannot replace it; ask a
59
+ person to run the command again outside the session.
60
+
61
+ When a criterion is the reason you captured, bind the artifact to its
62
+ recorded id:
63
+
64
+ outerlayer emit artifact shot.png --caption "…" --for LOGIN-01
65
+
66
+ The PR comment's Evidence section shows the id next to the artifact, and the
67
+ dashboard's Criteria tab lists the artifact on that criterion's row. An id
68
+ the recorded list does not hold gets no row.
69
+
70
+ ## The noise rule
71
+
72
+ Satisfy the declared proofs; don't document everything. One exhibit per
73
+ criterion is the norm. An unrequested artifact is worth emitting only when
74
+ it would change how a reviewer reads the diff. When in doubt, leave it out —
75
+ evidence works because there is little of it.
76
+
77
+ ## Mechanics
78
+
79
+ Inside a recorded session, just run the command — the artifact spools
80
+ locally and uploads with the next `outerlayer sync`, bound to this session
81
+ and turn. In CI, run it after the step that produced the file (repo and PR
82
+ come from the CI environment). From a plain machine it anchors through the
83
+ git checkout, or pass `--pr <n>` explicitly. If there is nothing to attach
84
+ to, the command refuses — emit from the work, not from nowhere.
85
+
86
+ ## Report artifacts the default checks read
87
+
88
+ Two report artifacts feed the default checks. Bind each to its check name with `--for`:
89
+
90
+ - `--for code-review-ran` for the review report, an HTML file.
91
+ - `--for acceptance-criteria` for the judge's report on each criterion, an HTML file. Emit it only when the issue has criteria.
92
+
93
+ outerlayer emit artifact review.html --caption "Review of the change and what it found" --for code-review-ran
94
+
95
+ A session never records a pass or fail on an artifact. It emits the artifact, and a person or a check decides.
96
+
97
+ ## Instructions-file snippet
98
+
99
+ 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.