@outerlayer/cli 0.15.0 → 0.17.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 (84) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +11 -11
  3. package/dist/{agent-setup-CN2HUBEJ.js → agent-setup-T624CWPD.js} +8 -7
  4. package/dist/artifact-link-cmd-2F47LT7F.js +24 -0
  5. package/dist/build-info.json +1 -1
  6. package/dist/{check-2JSSXCKJ.js → check-5XE7XMC4.js} +22 -19
  7. package/dist/{chunk-HFKKFTXU.js → chunk-2MNPHL23.js} +19 -11
  8. package/dist/{chunk-YDSMROTB.js → chunk-3NSNIIBE.js} +8 -4
  9. package/dist/{chunk-FOJGZYFC.js → chunk-42ZI52JW.js} +1 -1
  10. package/dist/{chunk-NPQWKGUA.js → chunk-6Y5RUTHO.js} +1 -1
  11. package/dist/{chunk-PQUM37BK.js → chunk-BWQ66M6N.js} +1 -1
  12. package/dist/chunk-C5Y5ZJJZ.js +88 -0
  13. package/dist/{chunk-ASOSIKA4.js → chunk-G5NPRL2K.js} +27 -6
  14. package/dist/{chunk-QLTYTPYU.js → chunk-HGECYFEI.js} +64 -7
  15. package/dist/{chunk-SXWS25JC.js → chunk-KG5ECZGJ.js} +6 -6
  16. package/dist/{chunk-F7GBYX2P.js → chunk-LWSJQJC6.js} +18 -4
  17. package/dist/{chunk-UUDDPNRJ.js → chunk-MAK5LYRF.js} +33 -10
  18. package/dist/{chunk-MTF3PHVZ.js → chunk-NBGDBTO2.js} +3 -3
  19. package/dist/{chunk-KFRM23WD.js → chunk-NNRTPG7X.js} +2 -2
  20. package/dist/{chunk-MSASO5O4.js → chunk-NWQTTSBB.js} +1 -1
  21. package/dist/{chunk-2M63GG56.js → chunk-OHVJA253.js} +1 -1
  22. package/dist/{chunk-Z3NOIFDR.js → chunk-ONKJUM6R.js} +1 -1
  23. package/dist/{chunk-T5VPP6H7.js → chunk-OZGHL5Y5.js} +1 -1
  24. package/dist/{chunk-ZA5CHAFY.js → chunk-POJMEEJJ.js} +21 -4
  25. package/dist/{chunk-JR6ERP3M.js → chunk-S3NYLI2P.js} +3 -3
  26. package/dist/{chunk-HXLMZOKU.js → chunk-VQYBXPI7.js} +4 -4
  27. package/dist/{chunk-LCEXHCHD.js → chunk-WTE3MQML.js} +1 -1
  28. package/dist/{chunk-NXUH742G.js → chunk-WVUIRTWA.js} +10 -8
  29. package/dist/{chunk-JJEGKEFV.js → chunk-YU3VKZ5F.js} +1 -1
  30. package/dist/{chunk-3NLOVYJQ.js → chunk-YZ5F4PIS.js} +134 -38
  31. package/dist/{cli-RGGXQTWI.js → cli-A76AXELQ.js} +90 -79
  32. package/dist/{command-ETUAEXP2.js → command-R2QCVX55.js} +10 -9
  33. package/dist/{connect-cmd-JUAW6MU4.js → connect-cmd-JBKAQNSF.js} +1 -1
  34. package/dist/{context-adopt-PJUMW2EZ.js → context-adopt-NFHCLVUB.js} +5 -4
  35. package/dist/{context-materialize-FDHYKNDT.js → context-materialize-IV2TUUFO.js} +5 -4
  36. package/dist/{doctor-CORFPYY6.js → doctor-4TAIARIG.js} +15 -13
  37. package/dist/{emit-artifact-cmd-FOGNJXVR.js → emit-artifact-cmd-MPZVR56N.js} +4 -4
  38. package/dist/{emit-cmd-ZEYWLC2C.js → emit-cmd-IJ3GVRFD.js} +4 -3
  39. package/dist/{emit-criteria-cmd-HBP6XS5C.js → emit-criteria-cmd-EFGYI23V.js} +2 -2
  40. package/dist/{emit-finding-cmd-DLI4MKMN.js → emit-finding-cmd-5GF3V4LR.js} +160 -47
  41. package/dist/{emit-result-cmd-MUSL7GQA.js → emit-result-cmd-I4OADKMD.js} +4 -4
  42. package/dist/hidden-prompt-UXBFMQC5.js +68 -0
  43. package/dist/{hook-fast-WYQD6D7T.js → hook-fast-R4FVSZ2H.js} +1 -1
  44. package/dist/{import-capture-cmd-6QVCOIHK.js → import-capture-cmd-EQ35EARA.js} +3 -3
  45. package/dist/{import-ruler-cmd-KLRGRBCP.js → import-ruler-cmd-BGAZXCQM.js} +1 -1
  46. package/dist/index.js +3 -3
  47. package/dist/{init-OPB7HHXO.js → init-6XPDRDTC.js} +12 -11
  48. package/dist/{init-MIC37V6J.js → init-PWU5YVL7.js} +54 -29
  49. package/dist/{init-cmd-JR3RKNFB.js → init-cmd-XAVO7HYZ.js} +11 -10
  50. package/dist/{init-update-O5YYFGF4.js → init-update-CVCWD6VS.js} +10 -9
  51. package/dist/{loop-QNSM5GXS.js → loop-FK3LPMMZ.js} +19 -18
  52. package/dist/{machine-IT6GG7FF.js → machine-SQXEBAM2.js} +10 -9
  53. package/dist/{runner-env-XTC53CHN.js → runner-env-EN7YE7QX.js} +1 -1
  54. package/dist/runner-key-6SLBI7PP.js +174 -0
  55. package/dist/{session-sender-4M247XSV.js → session-sender-FO3LCREV.js} +3 -3
  56. package/dist/{starter-pack-IB4UUIX3.js → starter-pack-KF66HGZR.js} +3 -3
  57. package/dist/{status-7ZX57OFJ.js → status-7VNQN5PW.js} +13 -12
  58. package/dist/{sync-cmd-UWD5JOFU.js → sync-cmd-GVPPFZ3G.js} +2 -2
  59. package/dist/{watch-DBNB7R6X.js → watch-23HSAEI6.js} +2 -2
  60. package/dist/{work-claim-cmd-A6IS6CQ7.js → work-claim-cmd-LKL3GHFQ.js} +5 -5
  61. package/dist/{work-cmd-EQSW5DTD.js → work-cmd-Z2WXLZPH.js} +3 -3
  62. package/dist/{work-comment-cmd-7VLKI7FN.js → work-comment-cmd-AWHP56M2.js} +3 -3
  63. package/dist/{work-open-pr-cmd-JEVD4HMQ.js → work-open-pr-cmd-IOM4AVQQ.js} +4 -4
  64. package/dist/{work-pr-cmd-L77K32WK.js → work-pr-cmd-HNEUIEN7.js} +3 -3
  65. package/package.json +1 -1
  66. package/skill-pack/maintained/{amend → outerlayer-amend}/SKILL.md +4 -2
  67. package/skill-pack/maintained/{outerlayer → outerlayer-cli}/SKILL.md +5 -3
  68. package/skill-pack/maintained/{emitting-evidence → outerlayer-evidence}/SKILL.md +5 -3
  69. package/skill-pack/maintained/{reporting-findings → outerlayer-findings}/SKILL.md +26 -30
  70. package/skill-pack/shipped.json +20 -4
  71. package/skill-pack/template/build/SKILL.md +7 -1
  72. package/skill-pack/template/build/references/agent-briefs.md +7 -1
  73. package/skill-pack/template/build/references/evidence.md +1 -1
  74. package/skill-pack/template/build/references/release.md +14 -12
  75. package/skill-pack/template/build/references/review-briefs.md +6 -0
  76. package/skill-pack/template/build/scripts/criterion-citations.mjs +8 -3
  77. package/skill-pack/template/build/scripts/record-criteria.mjs +8 -2
  78. package/skill-pack/template/spec/SKILL.md +2 -0
  79. package/skill-pack/template/writing-specs/SKILL.md +1 -1
  80. package/skill-pack/validators/acceptance-criteria.yaml +6 -5
  81. package/skill-pack/validators/code-review.yaml +6 -5
  82. package/dist/runner-key-7SSNMUBQ.js +0 -154
  83. /package/skill-pack/maintained/{outerlayer → outerlayer-cli}/references/open-pull-request.md +0 -0
  84. /package/skill-pack/maintained/{emitting-evidence → outerlayer-evidence}/references/agents-snippet.md +0 -0
@@ -9,7 +9,8 @@
9
9
  // The criteria come from the file `record-criteria.mjs` wrote, by default
10
10
  // `<git dir>/build-reports/criteria.json`. A test cites a criterion when the
11
11
  // criterion's id appears as a whole token in a test's title or in a comment
12
- // above it. Only git-tracked test files are scanned, so every link names code
12
+ // above it. `WC-01 counts`, `WC-01: counts` and `WC-01 - counts` all cite
13
+ // WC-01. Only git-tracked test files are scanned, so every link names code
13
14
  // that a commit contains. The link names the HEAD commit and is written only
14
15
  // when the origin remote is on GitHub; otherwise the test is listed unlinked.
15
16
  // A criterion no test cites gets an empty list. An existing block in the
@@ -32,9 +33,13 @@ function escapeRegExp(text) {
32
33
  return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
33
34
  }
34
35
 
35
- /** A pattern that finds `id` as a whole token: not inside a longer id such as `WC-10` for `WC-1`, and still found before a sentence's full stop. */
36
+ /**
37
+ * A pattern that finds `id` as a whole token: not inside a longer id such as
38
+ * `WC-10` for `WC-1` or `WC-1:b` for `WC-1`, and still found before a
39
+ * sentence's full stop or a title's colon, as in `WC-1: counts words`.
40
+ */
36
41
  export function idPattern(id) {
37
- return new RegExp(`(?<![A-Za-z0-9_:-])${escapeRegExp(id)}(?![A-Za-z0-9_:-]|\\.[A-Za-z0-9])`);
42
+ return new RegExp(`(?<![A-Za-z0-9_:-])${escapeRegExp(id)}(?![A-Za-z0-9_-]|:[A-Za-z0-9_-]|\\.[A-Za-z0-9])`);
38
43
  }
39
44
 
40
45
  /** The ids of a criteria file shaped `{ "criteria": [{ "id" }] }`. */
@@ -85,10 +85,16 @@ export function classifyEmitFailure(message) {
85
85
  return /\brefused \(409\):/.test(message) ? "item-list-stands" : "refused";
86
86
  }
87
87
 
88
- /** Builds the `emit` function that runs `outerlayer emit criteria`; `spawn` is `spawnSync`, or a stand-in in tests. */
88
+ /**
89
+ * Builds the `emit` function that runs `outerlayer emit criteria`; `spawn` is `spawnSync`, or a stand-in in tests.
90
+ * A machine that ran the CLI through `npx` has no `outerlayer` on PATH, so a missing command falls back to `npx -y @outerlayer/cli`.
91
+ */
89
92
  export function makeEmit(spawn, item) {
90
93
  return (file) => {
91
- const run = spawn("outerlayer", ["emit", "criteria", file, "--item", item], { encoding: "utf8", timeout: 120000 });
94
+ const args = ["emit", "criteria", file, "--item", item];
95
+ const options = { encoding: "utf8", timeout: 120000 };
96
+ let run = spawn("outerlayer", args, options);
97
+ if (run.error?.code === "ENOENT") run = spawn("npx", ["-y", "@outerlayer/cli", ...args], options);
92
98
  if (run.status === 0) return { ok: true };
93
99
  if (run.error) return { ok: false, message: `could not run outerlayer emit criteria: ${run.error.message}` };
94
100
  if (run.signal) return { ok: false, message: `outerlayer emit criteria was stopped by ${run.signal}` };
@@ -9,6 +9,8 @@ description: >
9
9
  /spec <what you want built>.
10
10
  ---
11
11
 
12
+ Run the CLI as `outerlayer`. If that command is not on PATH, run `npx -y @outerlayer/cli` with the same arguments. Every `outerlayer ...` command below follows this rule.
13
+
12
14
  Take one idea from a sentence to an issue a person can build from. This file
13
15
  is the procedure. The content discipline is the `writing-specs` skill: summary
14
16
  block, problem before solution, non-goals, Given/When/Then criteria and the
@@ -103,7 +103,7 @@ implementing. Never write them by reading the finished implementation back.
103
103
  human-visible evidence. Use `video` for a flow of more than one step and
104
104
  `screenshot` for a single state. A test is the proof only where nothing visible
105
105
  changes. The proof shows the running page, never a component render. The
106
- emitting-evidence skill covers capture.
106
+ outerlayer-evidence skill covers capture.
107
107
 
108
108
  ## The two verdicts
109
109
 
@@ -5,11 +5,12 @@
5
5
  # the test that cites it. Write its verdicts as an .html file and emit it as
6
6
  # a report artifact bound to this check:
7
7
  # outerlayer emit artifact criteria.html --for acceptance-criteria --caption "Each criterion judged against the test that cites it"
8
- # Then run `outerlayer sync`, and copy the report's deep link from the pull
9
- # request's evidence comment. The link can take up to a minute to appear
10
- # after the sync. If it is not there yet, wait a minute and read the comment
11
- # again; do not give up after one look. Never build the link by hand, and
12
- # never link a pull request comment: a comment can be edited or deleted later.
8
+ # Then run `outerlayer sync`. It prints a line for the report that ends with
9
+ # its link; take the link from that line. If `sync` printed no line for it,
10
+ # another sync already uploaded the report: run
11
+ # `outerlayer sync --link <the artifact id>`, with the id `emit artifact`
12
+ # printed. Never build the link by hand, and never link a pull request
13
+ # comment: a comment can be edited or deleted later.
13
14
  #
14
15
  # Then record the verdict once, with that link as its proof:
15
16
  # outerlayer emit acceptance-criteria --item "$OUTERLAYER_WORK" --result pass --link <the report's deep link>
@@ -4,11 +4,12 @@
4
4
  # findings report (each confirmed finding with its severity and fix, or a
5
5
  # clean bill) as an .html file. Emit it as a report artifact with no --for:
6
6
  # outerlayer emit artifact review.html --caption "Review of the change and what it found"
7
- # Then run `outerlayer sync`, and copy the report's deep link from the pull
8
- # request's evidence comment. The link can take up to a minute to appear
9
- # after the sync. If it is not there yet, wait a minute and read the comment
10
- # again; do not give up after one look. Never build the link by hand, and
11
- # never link a pull request comment: a comment can be edited or deleted later.
7
+ # Then run `outerlayer sync`. It prints a line for the report that ends with
8
+ # its link; take the link from that line. If `sync` printed no line for it,
9
+ # another sync already uploaded the report: run
10
+ # `outerlayer sync --link <the artifact id>`, with the id `emit artifact`
11
+ # printed. Never build the link by hand, and never link a pull request
12
+ # comment: a comment can be edited or deleted later.
12
13
  #
13
14
  # Then record the verdict once, with that link as its proof:
14
15
  # outerlayer emit code-review --item "$OUTERLAYER_WORK" --result pass --link <the report's deep link>
@@ -1,154 +0,0 @@
1
- #!/usr/bin/env node
2
- import { createRequire } from 'node:module';
3
- import { readConfigObject, RunnerInitError } from './chunk-HXLMZOKU.js';
4
- import './chunk-Z3NOIFDR.js';
5
- import './chunk-UUDDPNRJ.js';
6
- import './chunk-KMDLFI4P.js';
7
- import './chunk-QKFB4K7X.js';
8
- import './chunk-RSNSZ2LJ.js';
9
- import './chunk-T5VPP6H7.js';
10
- import './chunk-RLYZLUQ7.js';
11
- import './chunk-6MZSX4M7.js';
12
- import { postJson, WorkCommandError, getJson } from './chunk-JR6ERP3M.js';
13
- import './chunk-NPQWKGUA.js';
14
- import './chunk-IP7BIBXJ.js';
15
- import { runnerCredentialProblem } from './chunk-22RMZBGW.js';
16
- import './chunk-UXYLWVTQ.js';
17
- import './chunk-R7PF5OOU.js';
18
- import './chunk-QOZKPLVJ.js';
19
- import './chunk-N5FOE5PS.js';
20
- import './chunk-OGFGZQA3.js';
21
- import './chunk-4NPZVMJU.js';
22
- import './chunk-7RQ73DLO.js';
23
- import './chunk-4Q7OMKAT.js';
24
- import './chunk-CX22S5BA.js';
25
- import './chunk-YDSMROTB.js';
26
- import './chunk-WUYTWNMA.js';
27
- import './chunk-JXYVA45C.js';
28
- import './chunk-4KSQKQXE.js';
29
- import './chunk-N6LURUEF.js';
30
- import './chunk-YRYMNBGQ.js';
31
- import './chunk-WZBHBH3J.js';
32
- import './chunk-3NLOVYJQ.js';
33
- import './chunk-LCEXHCHD.js';
34
- import './chunk-ZJSNLVCE.js';
35
- import './chunk-YO4PSLVB.js';
36
- import './chunk-BYT5O5RV.js';
37
- import './chunk-ZA5CHAFY.js';
38
- import './chunk-2M63GG56.js';
39
- import './chunk-PLMTLGYH.js';
40
- import './chunk-J5M7BSJQ.js';
41
- import { readCloudConfig } from './chunk-EABW6AJQ.js';
42
- import './chunk-RA3O54FC.js';
43
- import './chunk-V7NIVOFD.js';
44
- import './chunk-ZM2IMMYN.js';
45
- import './chunk-XCCLFVXM.js';
46
- import { readRepoConnection } from './chunk-BJ3KTMDH.js';
47
- import { homeFromEnv } from './chunk-VNVZDWO3.js';
48
- import './chunk-Z5GEFMRW.js';
49
- import { existsSync } from 'fs';
50
- import { hostname } from 'os';
51
-
52
- createRequire(import.meta.url);
53
- var RUNNER_KEY_PERMISSIONS = [
54
- "sessions.ingest",
55
- "evidence.insert",
56
- "work.read",
57
- "git.read",
58
- "context.read",
59
- "work.update",
60
- "work.claim",
61
- "hosts.ingest"
62
- ];
63
- var MAX_NAME_ATTEMPTS = 10;
64
- var PASTED_KEY_FALLBACK = "Ask a member who can create keys to make one with the Runner preset on the factory's API keys page, then put it in the runner's config file as apiKey, with the Factory Id as appId, and run this again.";
65
- function runnerKeyLogin(configPath, opts = {}) {
66
- const env = opts.env ?? process.env;
67
- const config = existsSync(configPath) ? readConfigObject(configPath) : {};
68
- if (config.apiKey !== void 0) {
69
- if (opts.appId !== void 0 && opts.appId !== config.appId) {
70
- throw new RunnerInitError(
71
- `${configPath} already holds a runner key, for the factory its appId names, so --app-id ${opts.appId} creates no key. Leave --app-id out, or give it the same id.`
72
- );
73
- }
74
- return void 0;
75
- }
76
- const saved = readCloudConfig(opts.home ?? homeFromEnv(env));
77
- const own = typeof config.accountToken === "string" && config.accountToken !== "";
78
- const accountToken = own ? config.accountToken : saved.accountToken;
79
- if (accountToken === void 0 || accountToken === "") return void 0;
80
- const url = config.url ?? (own ? void 0 : saved.url);
81
- const appId = opts.appId ?? (env.OUTERLAYER_APP_ID || void 0) ?? config.appId ?? readRepoConnection(opts.cwd ?? process.cwd())?.appId;
82
- if (appId === void 0) {
83
- throw new RunnerInitError(
84
- "you are logged in, so this command creates the runner's key, but no factory is chosen. Pass --app-id with the Factory Id from the factory's Settings \u2192 General, or set OUTERLAYER_APP_ID."
85
- );
86
- }
87
- const problem = runnerCredentialProblem({ url, apiKey: accountToken, appId });
88
- if (problem !== void 0) {
89
- const where = problem.field === "appId" ? "the factory id" : `${configPath}: ${problem.field}`;
90
- throw new RunnerInitError(`${where} ${problem.reason}.`);
91
- }
92
- return { url, accountToken, appId };
93
- }
94
- function defaultRunnerKeyName(host = hostname()) {
95
- const short = host.replace(/\..*$/, "").trim();
96
- return short === "" ? "runner" : short;
97
- }
98
- function clause(message) {
99
- return message.replace(/\.+$/, "");
100
- }
101
- function forbidden(err) {
102
- return new RunnerInitError(`your login cannot create a runner key on this factory: ${clause(err.message)}. ${PASTED_KEY_FALLBACK}`);
103
- }
104
- async function createKey(login, name, fetchImpl) {
105
- for (let attempt = 1; attempt <= MAX_NAME_ATTEMPTS; attempt++) {
106
- const tried = attempt === 1 ? name : `${name}-${attempt}`;
107
- try {
108
- const { data } = await postJson(
109
- login.url,
110
- "/v1/api-keys",
111
- login.accountToken,
112
- login.appId,
113
- { name: tried, permissions: [...RUNNER_KEY_PERMISSIONS], app_id: login.appId },
114
- fetchImpl
115
- );
116
- const apiKey = data.plaintext_key;
117
- if (typeof apiKey !== "string" || apiKey === "") {
118
- throw new RunnerInitError(`the gateway created the key ${tried} but did not return it. Delete it on the factory's API keys page, then run this again.`);
119
- }
120
- return { apiKey, name: tried };
121
- } catch (err) {
122
- if (!(err instanceof WorkCommandError)) throw err;
123
- if (err.status === 409) continue;
124
- if (err.status === 403) throw forbidden(err);
125
- throw new RunnerInitError(`the runner key was not created: ${clause(err.message)}.`);
126
- }
127
- }
128
- throw new RunnerInitError(
129
- `the names ${name} to ${name}-${MAX_NAME_ATTEMPTS} are all taken by keys on this factory. Pass --key-name with another name.`
130
- );
131
- }
132
- async function checkKey(key, fetchImpl) {
133
- const unusable = (why) => new RunnerInitError(
134
- `the runner key ${key.name} was created, but ${why}. Nothing else was changed. Delete the key on the factory's API keys page, then run this again.`
135
- );
136
- let data;
137
- try {
138
- ({ data } = await getJson(key.url, "/v1/runner/host-key", key.apiKey, key.appId, fetchImpl));
139
- } catch (err) {
140
- throw unusable(`the gateway did not accept it: ${clause(err instanceof Error ? err.message : String(err))}`);
141
- }
142
- if (data.bound !== false) {
143
- throw unusable("the gateway did not report it as a new runner key");
144
- }
145
- }
146
- async function createRunnerKey(login, opts = {}) {
147
- const fetchImpl = opts.fetchImpl ?? fetch;
148
- const created = await createKey(login, opts.name ?? defaultRunnerKeyName(), fetchImpl);
149
- const key = { url: login.url, apiKey: created.apiKey, appId: login.appId, name: created.name };
150
- await checkKey(key, fetchImpl);
151
- return key;
152
- }
153
-
154
- export { RUNNER_KEY_PERMISSIONS, createRunnerKey, defaultRunnerKeyName, runnerKeyLogin };