@outerlayer/cli 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +9 -4
- package/dist/agent-setup-CN2HUBEJ.js +26 -0
- package/dist/{build-LELVCWHV.js → build-BG2YENGK.js} +2 -2
- package/dist/build-info.json +1 -1
- package/dist/{builder-OF67QKDS.js → builder-2D3WSOOY.js} +4 -4
- package/dist/{check-RJOJU2WA.js → check-SRGR36KI.js} +37 -37
- package/dist/{chunk-XR3BJN33.js → chunk-22RMZBGW.js} +20 -1
- package/dist/{chunk-AHZQBGD2.js → chunk-2M63GG56.js} +1 -1
- package/dist/{chunk-SZOVKFWB.js → chunk-3NLOVYJQ.js} +4 -4
- package/dist/{chunk-USO2DKBD.js → chunk-4KSQKQXE.js} +8 -2
- package/dist/{chunk-QEESCZ26.js → chunk-4NPZVMJU.js} +2 -2
- package/dist/{chunk-DVT73QJO.js → chunk-4OGPT6I2.js} +14 -14
- package/dist/{chunk-ZQUCTFXC.js → chunk-FOJGZYFC.js} +11 -5
- package/dist/{chunk-G5XY6CVG.js → chunk-HFKKFTXU.js} +12 -16
- package/dist/{chunk-ANW7VHMH.js → chunk-HFKQIZXL.js} +95 -16
- package/dist/{chunk-325272HM.js → chunk-J5M7BSJQ.js} +11 -4
- package/dist/{chunk-NPXQXYVA.js → chunk-JJEGKEFV.js} +6 -8
- package/dist/{chunk-3ZPYPFNM.js → chunk-JXYVA45C.js} +1 -1
- package/dist/{chunk-IBB4D4KE.js → chunk-JYIEHQIF.js} +1 -1
- package/dist/{chunk-5TGQHOUX.js → chunk-KFRM23WD.js} +3 -3
- package/dist/{chunk-GGMRZPEA.js → chunk-KMDLFI4P.js} +1 -1
- package/dist/{chunk-G5IEVZGI.js → chunk-LCEXHCHD.js} +1 -1
- package/dist/{chunk-27Y7AX2O.js → chunk-MSASO5O4.js} +44 -8
- package/dist/chunk-MTF3PHVZ.js +161 -0
- package/dist/{chunk-G4RXNF2L.js → chunk-NHDVD7DU.js} +12 -12
- package/dist/{chunk-GATMPAMK.js → chunk-NPQWKGUA.js} +2 -2
- package/dist/{chunk-YEQL54G5.js → chunk-PQUM37BK.js} +1 -1
- package/dist/{chunk-GEBQXUFY.js → chunk-Q2UK3C4P.js} +24 -10
- package/dist/{chunk-SKHN72JO.js → chunk-RBKIH5ST.js} +13 -13
- package/dist/{chunk-2KLBKHT4.js → chunk-RLYZLUQ7.js} +1 -1
- package/dist/chunk-RMWLBUCJ.js +50 -0
- package/dist/{chunk-PDRYXERC.js → chunk-SMHOCDCI.js} +5 -5
- package/dist/{chunk-6FH2AHJY.js → chunk-SWHQ4LUM.js} +30 -17
- package/dist/{agent-setup-52LRZ6AY.js → chunk-SXWS25JC.js} +8 -21
- package/dist/{chunk-VK25O2ZM.js → chunk-T5VPP6H7.js} +1 -1
- package/dist/{chunk-JOXYKGUV.js → chunk-WUYTWNMA.js} +4 -2
- package/dist/{chunk-A56PYPDF.js → chunk-XCK2YAY5.js} +14 -4
- package/dist/{chunk-C7N4TDHW.js → chunk-YDSMROTB.js} +5 -5
- package/dist/{chunk-FO7KKPYG.js → chunk-ZA5CHAFY.js} +2 -2
- package/dist/{cli-JSSRGA6V.js → cli-UW2H3UUW.js} +138 -85
- package/dist/{command-3WYZ2ML5.js → command-5LD7G2QQ.js} +27 -27
- package/dist/{config-MKBX2ZQD.js → config-N3C6DFQA.js} +1 -1
- package/dist/{connect-cmd-A5NGODCJ.js → connect-cmd-JUAW6MU4.js} +5 -5
- package/dist/{context-adopt-BAHGXJZV.js → context-adopt-PJUMW2EZ.js} +6 -6
- package/dist/{context-materialize-45OVRRK2.js → context-materialize-FDHYKNDT.js} +6 -6
- package/dist/{dist-EQDW7MZW.js → dist-LKDWQWZ7.js} +2 -2
- package/dist/{docker-VGWEJWI7.js → docker-4J77Q2BF.js} +5 -5
- package/dist/{doctor-4FFXTQJ2.js → doctor-H7REBSLQ.js} +29 -29
- package/dist/{emit-artifact-cmd-QS7XNKBT.js → emit-artifact-cmd-FOGNJXVR.js} +18 -21
- package/dist/emit-cmd-ZEYWLC2C.js +11 -0
- package/dist/{emit-criteria-cmd-XN6AKA53.js → emit-criteria-cmd-HBP6XS5C.js} +8 -11
- package/dist/{emit-finding-cmd-SQJSEHYS.js → emit-finding-cmd-DLI4MKMN.js} +8 -11
- package/dist/{emit-result-cmd-56RHPHOA.js → emit-result-cmd-MUSL7GQA.js} +12 -15
- package/dist/{hook-fast-HLESEJAL.js → hook-fast-WYQD6D7T.js} +1 -1
- package/dist/{import-capture-cmd-NHMAFDM6.js → import-capture-cmd-6QVCOIHK.js} +10 -13
- package/dist/{import-ruler-cmd-UYB32YUT.js → import-ruler-cmd-KLRGRBCP.js} +7 -10
- package/dist/index.js +3 -3
- package/dist/{init-UZKD4RY7.js → init-NTXA3SN7.js} +28 -28
- package/dist/{init-6AY5T3P6.js → init-UBXK6MF7.js} +47 -37
- package/dist/{init-cmd-XGSX2BCC.js → init-cmd-NARDUPIG.js} +20 -13
- package/dist/init-update-O5YYFGF4.js +165 -0
- package/dist/{install-VXXI7UR6.js → install-HDBIPHTX.js} +2 -2
- package/dist/{login-browser-7XGSV7MA.js → login-browser-QZCS3WW6.js} +2 -1
- package/dist/{loop-4NMI2USY.js → loop-SW2M5VDD.js} +37 -37
- package/dist/{machine-4JX2D4TS.js → machine-U66FKVDP.js} +23 -23
- package/dist/mcp-install-cmd-FDTPQQS6.js +9 -0
- package/dist/{runner-env-WAXTJXZK.js → runner-env-XTC53CHN.js} +3 -3
- package/dist/runner-key-3JSMDRJA.js +154 -0
- package/dist/{session-sender-T7PN4YRC.js → session-sender-4M247XSV.js} +7 -7
- package/dist/starter-pack-IB4UUIX3.js +11 -0
- package/dist/{status-OXBIRWJ7.js → status-STZYPU3W.js} +30 -30
- package/dist/{sync-cmd-EVXNR47N.js → sync-cmd-UWD5JOFU.js} +6 -6
- package/dist/{watch-7NSFOAL5.js → watch-DBNB7R6X.js} +6 -6
- package/dist/{work-claim-cmd-KR7UR2AB.js → work-claim-cmd-LY4U6QHN.js} +13 -13
- package/dist/{work-cmd-JPM37MLF.js → work-cmd-RVRQ32EH.js} +7 -7
- package/dist/{work-comment-cmd-3DC6BC34.js → work-comment-cmd-FFUM4TJU.js} +10 -13
- package/dist/{work-open-pr-cmd-XZQRJBNZ.js → work-open-pr-cmd-HLDWP4T2.js} +10 -13
- package/dist/{work-pr-cmd-WHGZOP22.js → work-pr-cmd-L77K32WK.js} +7 -7
- package/package.json +1 -1
- package/skill-pack/maintained/emitting-evidence/SKILL.md +8 -2
- package/skill-pack/shipped.json +70 -0
- package/skill-pack/template/build/SKILL.md +66 -35
- package/skill-pack/template/build/references/agent-briefs.md +4 -2
- package/skill-pack/template/build/references/release.md +36 -8
- package/skill-pack/template/build/references/review-loop.md +6 -2
- package/skill-pack/template/build/scripts/record-criteria.mjs +109 -143
- package/skill-pack/template/spec/SKILL.md +33 -22
- package/skill-pack/template/writing-specs/SKILL.md +36 -3
- package/skill-pack/template/writing-specs/references/agents-snippet.md +57 -0
- package/skill-pack/template/writing-tests/SKILL.md +117 -0
- package/skill-pack/validators/acceptance-criteria.yaml +35 -0
- package/skill-pack/validators/code-review.yaml +4 -2
- package/dist/chunk-T6OIJQRT.js +0 -31
- package/dist/emit-cmd-LXEIDKLT.js +0 -10
- package/dist/mcp-install-cmd-XZARXNYT.js +0 -9
- package/dist/starter-pack-Z22VRFPF.js +0 -11
- package/dist/{chunk-3FMPZPJQ.js → chunk-R7PF5OOU.js} +1 -1
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
+
import { checkStarterFiles } from './chunk-MTF3PHVZ.js';
|
|
4
|
+
import { resolveCredentials, getJson, WorkCommandError } from './chunk-SWHQ4LUM.js';
|
|
3
5
|
import { readStatuslineState, STATE_FRESH_MS } from './chunk-F47JBIAW.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-3ZPYPFNM.js';
|
|
6
|
+
import { isLinkedRepository } from './chunk-YDSMROTB.js';
|
|
7
|
+
import { sanitizeTerminal } from './chunk-WUYTWNMA.js';
|
|
8
|
+
import { resolveRepoIdentity, compareVersions, SUPPORTED_VERSIONS } from './chunk-JXYVA45C.js';
|
|
8
9
|
import { C } from './chunk-YRYMNBGQ.js';
|
|
9
10
|
import { readSettings, settingsPath, isOurStatuslineSlot, statuslineWrappedOriginal, REGISTERED_EVENTS, isOurHookCommand, ourCommandBins, cliBinResolvable, ourDirectScriptBins, wrappedOriginalCommand } from './chunk-5QRP3MVS.js';
|
|
10
|
-
import { readPin, readContextManifest, readDegraded, readSourceCache } from './chunk-
|
|
11
|
+
import { readPin, readContextManifest, readDegraded, readSourceCache } from './chunk-LCEXHCHD.js';
|
|
11
12
|
import { defaultFetch } from './chunk-YO4PSLVB.js';
|
|
12
13
|
import { resolveGatewayCredentials } from './chunk-PLMTLGYH.js';
|
|
13
14
|
import { readCloudConfig } from './chunk-EABW6AJQ.js';
|
|
14
|
-
import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-
|
|
15
|
+
import { sanitizeForContext, spoolDir, assessSyncHealth, readSyncStatus, SYNC_STALE_AFTER_MS, formatAgo, checkInstalledCliStaleness, workspaceRootOf, hookFiredMarkerPath, refreshCommand } from './chunk-PQUM37BK.js';
|
|
15
16
|
import { deadHooksState, preparePackageManagerCommand, contextExcludesPresent } from './chunk-XCCLFVXM.js';
|
|
16
17
|
import { findGitDirs } from './chunk-BJ3KTMDH.js';
|
|
17
18
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
18
|
-
import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync, accessSync, constants, statSync, readdirSync } from 'fs';
|
|
19
|
-
import { join, dirname } from 'path';
|
|
19
|
+
import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync, accessSync, constants, statSync, readdirSync, realpathSync } from 'fs';
|
|
20
|
+
import { join, dirname, resolve, delimiter } from 'path';
|
|
20
21
|
import { execFileSync } from 'child_process';
|
|
21
22
|
|
|
22
23
|
createRequire(import.meta.url);
|
|
@@ -113,6 +114,57 @@ function recordStatusline(home, settingsPath2, outcome, original, now = Date.now
|
|
|
113
114
|
};
|
|
114
115
|
});
|
|
115
116
|
}
|
|
117
|
+
var NAME = "MCP server";
|
|
118
|
+
var SERVER = "outerlayer";
|
|
119
|
+
var NPX_FIX = 'Change the "outerlayer" entry in .mcp.json to run `npx -y @outerlayer/cli mcp serve`, or install the CLI with `npm install -g @outerlayer/cli`.';
|
|
120
|
+
function expand(value, env) {
|
|
121
|
+
return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) => env[name] ?? "");
|
|
122
|
+
}
|
|
123
|
+
function isExecutableFile(path, platform) {
|
|
124
|
+
try {
|
|
125
|
+
if (!statSync(path).isFile()) return false;
|
|
126
|
+
if (platform !== "win32") accessSync(path, constants.X_OK);
|
|
127
|
+
return true;
|
|
128
|
+
} catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function resolveCommand(command, root, env, platform) {
|
|
133
|
+
if (/[\\/]/.test(command)) {
|
|
134
|
+
const path = resolve(root, command);
|
|
135
|
+
return isExecutableFile(path, platform) ? path : void 0;
|
|
136
|
+
}
|
|
137
|
+
const exts = platform === "win32" ? ["", ...(env.PATHEXT ?? ".EXE;.CMD;.BAT").split(";")] : [""];
|
|
138
|
+
for (const dir of (env.PATH ?? "").split(delimiter)) {
|
|
139
|
+
for (const ext of exts) {
|
|
140
|
+
const candidate = join(dir, command + ext);
|
|
141
|
+
if (isExecutableFile(candidate, platform)) return candidate;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return void 0;
|
|
145
|
+
}
|
|
146
|
+
function checkMcpCommand(cwd, env = process.env, platform = process.platform) {
|
|
147
|
+
const root = findGitDirs(cwd ?? process.cwd())?.repoRoot;
|
|
148
|
+
if (root === void 0) return [];
|
|
149
|
+
const path = join(root, ".mcp.json");
|
|
150
|
+
if (!existsSync(path)) return [];
|
|
151
|
+
let entry;
|
|
152
|
+
try {
|
|
153
|
+
entry = JSON.parse(readFileSync(path, "utf8")).mcpServers?.[SERVER];
|
|
154
|
+
} catch {
|
|
155
|
+
return [{ name: NAME, status: "warn", detail: ".mcp.json is not valid JSON, so the agent starts none of its servers", fix: "Fix the JSON syntax in .mcp.json." }];
|
|
156
|
+
}
|
|
157
|
+
const command = entry?.command;
|
|
158
|
+
if (typeof command !== "string") return [];
|
|
159
|
+
const shown = sanitizeTerminal(command);
|
|
160
|
+
const resolved = resolveCommand(expand(command, env), root, env, platform);
|
|
161
|
+
if (resolved === void 0) {
|
|
162
|
+
return [{ name: NAME, status: "warn", detail: `the "outerlayer" server runs \`${shown}\`, which is not on PATH here, so the agent has no OuterLayer tools`, fix: NPX_FIX }];
|
|
163
|
+
}
|
|
164
|
+
return [{ name: NAME, status: "pass", detail: `the "outerlayer" server runs ${sanitizeTerminal(resolved)}` }];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/doctor.ts
|
|
116
168
|
var LAPTOP_CHECKS_NOT_APPLICABLE = {
|
|
117
169
|
name: "Laptop checks",
|
|
118
170
|
status: "skipped",
|
|
@@ -123,6 +175,7 @@ function runDoctor(env = {}) {
|
|
|
123
175
|
const now = env.now ?? Date.now;
|
|
124
176
|
const laptop = env.runnerHost !== true;
|
|
125
177
|
const transcripts = laptop ? scanTranscripts(home) : void 0;
|
|
178
|
+
const starter = laptop ? checkStarterFiles(env.cwd) : void 0;
|
|
126
179
|
return [
|
|
127
180
|
...laptop ? [checkClaudeDir(home), checkTranscripts(transcripts, now), ...checkHooks(home, env.cwd, transcripts, now)] : [LAPTOP_CHECKS_NOT_APPLICABLE],
|
|
128
181
|
...checkDeadHooksState(env.cwd),
|
|
@@ -135,6 +188,8 @@ function runDoctor(env = {}) {
|
|
|
135
188
|
checkSettingsValid(home, env.cwd),
|
|
136
189
|
...laptop ? checkStatusline(home, now, env.cwd) : [],
|
|
137
190
|
...checkInstalls(env),
|
|
191
|
+
...starter?.checks ?? [],
|
|
192
|
+
...starter !== void 0 && !starter.mcpOutdated ? checkMcpCommand(env.cwd, env.processEnv) : [],
|
|
138
193
|
...checkContextSource(home, env.cwd),
|
|
139
194
|
...checkInstalledCli(home, env.cwd ?? process.cwd())
|
|
140
195
|
];
|
|
@@ -462,7 +517,9 @@ function checkDeadHooksState(cwd) {
|
|
|
462
517
|
function checkSpoolWritable(home) {
|
|
463
518
|
const dir = spoolDir(home);
|
|
464
519
|
try {
|
|
465
|
-
if (!existsSync(dir))
|
|
520
|
+
if (!existsSync(dir)) {
|
|
521
|
+
return { name: "Spool writable", status: "warn", detail: "spool not created yet", fix: "The first hook run creates it: start a Claude Code session in the repository." };
|
|
522
|
+
}
|
|
466
523
|
accessSync(dir, constants.W_OK);
|
|
467
524
|
return { name: "Spool writable", status: "pass", detail: dir };
|
|
468
525
|
} catch {
|
|
@@ -561,10 +618,25 @@ function checkSettingsValid(home, cwd) {
|
|
|
561
618
|
}
|
|
562
619
|
return { name: "Settings JSON valid", status: "pass", detail: "all settings files parse" };
|
|
563
620
|
}
|
|
621
|
+
function distinctInstalls(paths) {
|
|
622
|
+
const seen = /* @__PURE__ */ new Set();
|
|
623
|
+
const out = [];
|
|
624
|
+
for (const path of paths) {
|
|
625
|
+
let real = path;
|
|
626
|
+
try {
|
|
627
|
+
real = realpathSync(path);
|
|
628
|
+
} catch {
|
|
629
|
+
}
|
|
630
|
+
if (seen.has(real)) continue;
|
|
631
|
+
seen.add(real);
|
|
632
|
+
out.push(path);
|
|
633
|
+
}
|
|
634
|
+
return out;
|
|
635
|
+
}
|
|
564
636
|
function checkInstalls(env) {
|
|
565
637
|
let installs;
|
|
566
638
|
try {
|
|
567
|
-
installs = (env.claudeInstalls ?? defaultClaudeInstalls)();
|
|
639
|
+
installs = distinctInstalls((env.claudeInstalls ?? defaultClaudeInstalls)());
|
|
568
640
|
} catch {
|
|
569
641
|
return [];
|
|
570
642
|
}
|
|
@@ -708,7 +780,14 @@ async function githubAppChecks(options = {}) {
|
|
|
708
780
|
return [{ name: "GitHub App", status: "warn", detail: "the gateway's answer listed no repositories", fix: "Update the outerlayer CLI and the gateway to the same release." }];
|
|
709
781
|
}
|
|
710
782
|
if (data.length === 0) {
|
|
711
|
-
return [
|
|
783
|
+
return [
|
|
784
|
+
{
|
|
785
|
+
name: "GitHub App",
|
|
786
|
+
status: "warn",
|
|
787
|
+
detail: "the factory has no connected repositories, so it cannot build or check any work",
|
|
788
|
+
fix: `On the factory's Work page, choose Connect GitHub repository, then Link repository: ${CONNECT_REPOSITORY_DOCS}`
|
|
789
|
+
}
|
|
790
|
+
];
|
|
712
791
|
}
|
|
713
792
|
return data.map(repositoryAccessCheck);
|
|
714
793
|
}
|
|
@@ -1004,7 +1083,7 @@ function doctorExitCode(checks) {
|
|
|
1004
1083
|
|
|
1005
1084
|
// src/doctor-report.ts
|
|
1006
1085
|
async function gatherDoctorChecks() {
|
|
1007
|
-
const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-
|
|
1086
|
+
const { defaultRunnerConfigPath, hasRunnerBlock, loadRunnerConfig, runnerVmOf } = await import('./config-N3C6DFQA.js');
|
|
1008
1087
|
const configPath = defaultRunnerConfigPath();
|
|
1009
1088
|
const checks = runDoctor({ runnerHost: hasRunnerBlock(configPath) });
|
|
1010
1089
|
const credential = await credentialChecks();
|
|
@@ -1016,7 +1095,7 @@ async function gatherDoctorChecks() {
|
|
|
1016
1095
|
const { runnerVmCheck } = await import('./doctor-53F2PYY7.js');
|
|
1017
1096
|
checks.push(runnerVmCheck(configPath, { lima: realLima(), home: homeFromEnv(), instance: runnerVm.instance }));
|
|
1018
1097
|
} else if (hasRunnerBlock(configPath)) {
|
|
1019
|
-
const { runnerHostChecks } = await import('./doctor-
|
|
1098
|
+
const { runnerHostChecks } = await import('./doctor-H7REBSLQ.js');
|
|
1020
1099
|
const hooks = (() => {
|
|
1021
1100
|
try {
|
|
1022
1101
|
return loadRunnerConfig(configPath).hooks;
|
|
@@ -1048,10 +1127,10 @@ async function gatherDoctorChecks() {
|
|
|
1048
1127
|
}
|
|
1049
1128
|
})();
|
|
1050
1129
|
const service = await (async () => {
|
|
1051
|
-
const { existsSync:
|
|
1052
|
-
if (process.platform !== "linux" || !
|
|
1130
|
+
const { existsSync: existsSync3 } = await import('fs');
|
|
1131
|
+
if (process.platform !== "linux" || !existsSync3("/run/systemd/system")) return void 0;
|
|
1053
1132
|
const { inspectService, realInspectProbes } = await import('./inspect-6UYAAXH7.js');
|
|
1054
|
-
const { DEFAULT_UNIT_NAME } = await import('./install-
|
|
1133
|
+
const { DEFAULT_UNIT_NAME } = await import('./install-HDBIPHTX.js');
|
|
1055
1134
|
const { dirname: dirname2 } = await import('path');
|
|
1056
1135
|
return inspectService(DEFAULT_UNIT_NAME, dirname2(configPath), realInspectProbes());
|
|
1057
1136
|
})();
|
|
@@ -22273,9 +22273,14 @@ var ArtifactTombstoneRecordSchema = external_exports.looseObject({
|
|
|
22273
22273
|
});
|
|
22274
22274
|
external_exports.union([ArtifactSpoolRecordSchema, ArtifactTombstoneRecordSchema]);
|
|
22275
22275
|
var EMITTED_RESULTS = ["pass", "fail"];
|
|
22276
|
-
var RESERVED_EMIT_NAMES = ["criteria"];
|
|
22277
|
-
var EmittedNameSchema = external_exports.string().regex(/^[a-z][a-z0-9._-]{0,63}$/).
|
|
22278
|
-
|
|
22276
|
+
var RESERVED_EMIT_NAMES = ["artifact", "criteria", "finding", "findings"];
|
|
22277
|
+
var EmittedNameSchema = external_exports.string().regex(/^[a-z][a-z0-9._-]{0,63}$/).superRefine((name, ctx) => {
|
|
22278
|
+
if (RESERVED_EMIT_NAMES.includes(name)) {
|
|
22279
|
+
ctx.addIssue({
|
|
22280
|
+
code: "custom",
|
|
22281
|
+
message: `is reserved: \`outerlayer emit ${name}\` is a subcommand, so a check cannot be recorded under that name`
|
|
22282
|
+
});
|
|
22283
|
+
}
|
|
22279
22284
|
});
|
|
22280
22285
|
var EMITTED_RESULT_MAX_LINK_LENGTH = 500;
|
|
22281
22286
|
var EMITTED_RESULT_MAX_BODY_LENGTH = 2e3;
|
|
@@ -22292,6 +22297,7 @@ var CriterionSchema = external_exports.object({
|
|
|
22292
22297
|
proof: external_exports.enum(ARTIFACT_KINDS).nullable()
|
|
22293
22298
|
}).strict();
|
|
22294
22299
|
var CriteriaArraySchema = external_exports.array(CriterionSchema).min(1).max(CRITERIA_MAX);
|
|
22300
|
+
var DraftedSchema = external_exports.boolean().optional();
|
|
22295
22301
|
function refuseDuplicateIds(criteria, ctx) {
|
|
22296
22302
|
const seen = /* @__PURE__ */ new Set();
|
|
22297
22303
|
criteria.forEach((criterion, index) => {
|
|
@@ -22305,9 +22311,10 @@ function refuseDuplicateIds(criteria, ctx) {
|
|
|
22305
22311
|
seen.add(criterion.id);
|
|
22306
22312
|
});
|
|
22307
22313
|
}
|
|
22308
|
-
var CriteriaListSchema = external_exports.object({ criteria: CriteriaArraySchema }).strict().superRefine((list, ctx) => refuseDuplicateIds(list.criteria, ctx));
|
|
22314
|
+
var CriteriaListSchema = external_exports.object({ criteria: CriteriaArraySchema, drafted: DraftedSchema }).strict().superRefine((list, ctx) => refuseDuplicateIds(list.criteria, ctx));
|
|
22309
22315
|
external_exports.object({
|
|
22310
22316
|
criteria: CriteriaArraySchema,
|
|
22317
|
+
drafted: DraftedSchema,
|
|
22311
22318
|
item: external_exports.object({ number: external_exports.number().int().positive() }).optional(),
|
|
22312
22319
|
sessionId: external_exports.string().optional()
|
|
22313
22320
|
}).superRefine((request, ctx) => refuseDuplicateIds(request.criteria, ctx));
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { autoConnect } from './chunk-
|
|
3
|
+
import { autoConnect } from './chunk-NPQWKGUA.js';
|
|
4
4
|
import { detectActiveSession } from './chunk-IP7BIBXJ.js';
|
|
5
|
-
import { resolveRepoIdentity } from './chunk-
|
|
5
|
+
import { resolveRepoIdentity } from './chunk-JXYVA45C.js';
|
|
6
|
+
import { C } from './chunk-YRYMNBGQ.js';
|
|
6
7
|
import { parseWorkItemEnv } from './chunk-GQD22KKN.js';
|
|
7
8
|
import { parseTrackerNumber, trackerNumberError } from './chunk-WZBHBH3J.js';
|
|
8
9
|
import { defaultFetch } from './chunk-YO4PSLVB.js';
|
|
@@ -12,9 +13,6 @@ import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
|
12
13
|
createRequire(import.meta.url);
|
|
13
14
|
|
|
14
15
|
// src/work-pr-cmd.ts
|
|
15
|
-
var GREEN = "\x1B[32m";
|
|
16
|
-
var DIM = "\x1B[2m";
|
|
17
|
-
var RESET = "\x1B[0m";
|
|
18
16
|
var WorkPrCommandError = class extends Error {
|
|
19
17
|
};
|
|
20
18
|
function resolveCredentials(opts) {
|
|
@@ -43,7 +41,7 @@ function sessionNotOnItemMessage(error, sessionId) {
|
|
|
43
41
|
}
|
|
44
42
|
function itemLabel(data) {
|
|
45
43
|
if (data.workItemNumber == null) return data.workItemId;
|
|
46
|
-
return data.workItemTitle ? `item ${data.workItemNumber} ${
|
|
44
|
+
return data.workItemTitle ? `item ${data.workItemNumber} ${C.dim}\xB7${C.reset} ${data.workItemTitle}` : `item ${data.workItemNumber}`;
|
|
47
45
|
}
|
|
48
46
|
async function runWorkPrWithdraw(opts, ctx) {
|
|
49
47
|
const itemNumber = parseTrackerNumber(opts.item);
|
|
@@ -88,7 +86,7 @@ async function runWorkPrWithdraw(opts, ctx) {
|
|
|
88
86
|
throw new WorkPrCommandError(json.error?.message ?? `request failed (${response.status})`);
|
|
89
87
|
}
|
|
90
88
|
const data = json.data;
|
|
91
|
-
const output = opts.json ? JSON.stringify(data) : `${
|
|
89
|
+
const output = opts.json ? JSON.stringify(data) : `${C.green}\u2713${C.reset} withdrawn ${C.dim}\xB7${C.reset} ${data.repository}#${data.prNumber} ${C.dim}from${C.reset} item ${itemNumber}`;
|
|
92
90
|
if (!opts.quiet) process.stdout.write(output + "\n");
|
|
93
91
|
return { data, output };
|
|
94
92
|
}
|
|
@@ -170,7 +168,7 @@ async function runWorkPr(opts) {
|
|
|
170
168
|
}
|
|
171
169
|
const data = json.data;
|
|
172
170
|
const verb = data.created ? "declared" : "already declared";
|
|
173
|
-
const output = opts.json ? JSON.stringify(data) : `${
|
|
171
|
+
const output = opts.json ? JSON.stringify(data) : `${C.green}\u2713${C.reset} ${verb} ${C.dim}\xB7${C.reset} ${data.repository}#${data.prNumber} ${C.dim}\u2192${C.reset} ${itemLabel(data)}`;
|
|
174
172
|
if (!opts.quiet) process.stdout.write(output + "\n");
|
|
175
173
|
return { data, output };
|
|
176
174
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { EVENT_TYPES, isHumanUserTurn, parseAgentSession, SCHEMA_VERSION, SPEED_MAX_LENGTH, inputBandOf, BUCKET_MODEL_MAX_LENGTH } from './chunk-
|
|
3
|
+
import { EVENT_TYPES, isHumanUserTurn, parseAgentSession, SCHEMA_VERSION, SPEED_MAX_LENGTH, inputBandOf, BUCKET_MODEL_MAX_LENGTH } from './chunk-J5M7BSJQ.js';
|
|
4
4
|
import { createHash } from 'crypto';
|
|
5
5
|
import { readFileSync, readdirSync, statSync, mkdirSync, copyFileSync, renameSync, unlinkSync, writeFileSync, stat as stat$1, unwatchFile, watchFile, watch as watch$1 } from 'fs';
|
|
6
6
|
import { type, homedir } from 'os';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { MIN_ENGINE_MAJOR } from './chunk-
|
|
3
|
+
import { MIN_ENGINE_MAJOR } from './chunk-RBKIH5ST.js';
|
|
4
4
|
import { firecrackerArch, MICROVM_ARCHES, guestAssetsPinned } from './chunk-CX22S5BA.js';
|
|
5
5
|
import { spawnSync } from 'child_process';
|
|
6
6
|
import { existsSync, accessSync, constants, statSync, readFileSync } from 'fs';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { LiveUploader } from './chunk-
|
|
4
|
-
import { CaptureDaemon } from './chunk-
|
|
3
|
+
import { LiveUploader } from './chunk-MSASO5O4.js';
|
|
4
|
+
import { CaptureDaemon } from './chunk-JXYVA45C.js';
|
|
5
5
|
import { parseWorkItemEnv } from './chunk-GQD22KKN.js';
|
|
6
|
-
import { spoolDir } from './chunk-
|
|
6
|
+
import { spoolDir } from './chunk-PQUM37BK.js';
|
|
7
7
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
8
8
|
import { mkdirSync, writeFileSync, rmSync } from 'fs';
|
|
9
9
|
import { join } from 'path';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { RecipeError } from './chunk-QOZKPLVJ.js';
|
|
4
|
-
import { trackImageBuild, isOutOfMemory, ensureBuilder } from './chunk-
|
|
4
|
+
import { trackImageBuild, isOutOfMemory, ensureBuilder } from './chunk-4NPZVMJU.js';
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
6
|
import { existsSync, mkdirSync, createWriteStream, rmSync, readFileSync } from 'fs';
|
|
7
7
|
import { join, dirname } from 'path';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { CONTEXT_PIN_SCHEMA_VERSION, CONTEXT_PIN_FILENAME } from './chunk-
|
|
3
|
+
import { CONTEXT_PIN_SCHEMA_VERSION, CONTEXT_PIN_FILENAME } from './chunk-2M63GG56.js';
|
|
4
4
|
import { readFileSync, existsSync, writeFileSync, mkdirSync, utimesSync, statSync, unlinkSync } from 'fs';
|
|
5
5
|
import { join, dirname } from 'path';
|
|
6
6
|
|
|
@@ -4,17 +4,17 @@ import { LaunchFilter } from './chunk-HHJDN6AU.js';
|
|
|
4
4
|
import { RepoFilter } from './chunk-TBT347UY.js';
|
|
5
5
|
import { hookExecSpoolPath } from './chunk-F7CU5ABH.js';
|
|
6
6
|
import { applyReportedCost } from './chunk-BKO6JEZI.js';
|
|
7
|
-
import { migrateLegacyArtifactSidecars, planArtifactUpload, artifactsStatePath, sanitizeTerminal, acquireArtifactSpoolLock, artifactBlobsDir, resolveArtifactTurnIndex, isArtifactTombstoned, ARTIFACT_RETRY_MAX_AGE_MS, writeArtifactSpoolState, reclaimUnneededArtifactBlobs, neededArtifactShas } from './chunk-
|
|
8
|
-
import { setCustomScrubbers, setLocalIdentity, tailTranscript, enrichSessionRepo, scanAll, setRepoRoot, resolveRepoIdentity, sessionIdFromPath, scrubDeep, SOURCE_ADAPTERS } from './chunk-
|
|
7
|
+
import { migrateLegacyArtifactSidecars, planArtifactUpload, artifactsStatePath, sanitizeTerminal, acquireArtifactSpoolLock, artifactBlobsDir, resolveArtifactTurnIndex, isArtifactTombstoned, ARTIFACT_RETRY_MAX_AGE_MS, writeArtifactSpoolState, reclaimUnneededArtifactBlobs, neededArtifactShas } from './chunk-WUYTWNMA.js';
|
|
8
|
+
import { setCustomScrubbers, setLocalIdentity, tailTranscript, enrichSessionRepo, scanAll, setRepoRoot, resolveRepoIdentity, sessionIdFromPath, scrubDeep, SOURCE_ADAPTERS } from './chunk-JXYVA45C.js';
|
|
9
9
|
import { C } from './chunk-YRYMNBGQ.js';
|
|
10
10
|
import { readLaunchSpoolState, launchStatusIsDecisive, launchedItemsBySession, launchesPath } from './chunk-GQD22KKN.js';
|
|
11
11
|
import { readLatestProvenanceForSession, readContextProvenance } from './chunk-ZJSNLVCE.js';
|
|
12
12
|
import { defaultFetch } from './chunk-YO4PSLVB.js';
|
|
13
13
|
import { resolveGatewayCredentials, unauthorizedMessage } from './chunk-PLMTLGYH.js';
|
|
14
|
-
import { CAPTURE_TIERS, inferArtifactKind, bannedPathsForTier, downconvertSession, isHumanUserTurn, turnCountBound, EVENT_TYPES } from './chunk-
|
|
14
|
+
import { CAPTURE_TIERS, inferArtifactKind, bannedPathsForTier, downconvertSession, isHumanUserTurn, turnCountBound, EVENT_TYPES } from './chunk-J5M7BSJQ.js';
|
|
15
15
|
import { readCloudConfig, cloudConfigPath, writeCloudConfig } from './chunk-EABW6AJQ.js';
|
|
16
16
|
import { signerFor, signRunnerRequest } from './chunk-V7NIVOFD.js';
|
|
17
|
-
import { writeSyncStatus, spoolDir } from './chunk-
|
|
17
|
+
import { writeSyncStatus, spoolDir } from './chunk-PQUM37BK.js';
|
|
18
18
|
import { removeJournalHooks } from './chunk-XCCLFVXM.js';
|
|
19
19
|
import { readRepoConnection, findGitDirs } from './chunk-BJ3KTMDH.js';
|
|
20
20
|
import { homeFromEnv } from './chunk-VNVZDWO3.js';
|
|
@@ -928,6 +928,21 @@ var NEVER_REACHED_SERVER_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
928
928
|
"payload_too_large",
|
|
929
929
|
"unreadable_test_results"
|
|
930
930
|
]);
|
|
931
|
+
async function readUploadedIdentity(response) {
|
|
932
|
+
let data;
|
|
933
|
+
try {
|
|
934
|
+
data = (await response.json()).data;
|
|
935
|
+
} catch {
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
id: typeof data?.id === "string" ? data.id : null,
|
|
939
|
+
url: typeof data?.url === "string" ? data.url : null
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
function uploadedArtifactLine(artifact) {
|
|
943
|
+
const where = artifact.url !== null ? sanitizeTerminal(artifact.url) : artifact.id !== null ? `id ${sanitizeTerminal(artifact.id)} ${C.dim}(the gateway returned no link)${C.reset}` : `${C.dim}(the gateway returned no link)${C.reset}`;
|
|
944
|
+
return ` ${C.green}\u2191${C.reset} ${sanitizeTerminal(artifact.filename)} ${C.dim}\xB7${C.reset} ${sanitizeTerminal(artifact.caption)} ${C.dim}\xB7${C.reset} ${where}`;
|
|
945
|
+
}
|
|
931
946
|
function splitPayload(payload) {
|
|
932
947
|
const mid = Math.ceil(payload.sessions.length / 2);
|
|
933
948
|
const first = payload.sessions.slice(0, mid);
|
|
@@ -1553,10 +1568,12 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
|
|
|
1553
1568
|
}
|
|
1554
1569
|
let artifactsUploaded;
|
|
1555
1570
|
let artifactsHeld;
|
|
1571
|
+
let uploadedArtifacts;
|
|
1556
1572
|
const artifactLock = combinedRecords.length > 0 || artifactPlan.held.length > 0 ? await acquireArtifactSpoolLock(home, { waitMs: opts.artifactLockWaitMs }) : void 0;
|
|
1557
1573
|
if (artifactLock !== void 0 && !artifactLock.ok) {
|
|
1558
1574
|
artifactsUploaded = 0;
|
|
1559
1575
|
artifactsHeld = combinedRecords.length + artifactPlan.held.length;
|
|
1576
|
+
uploadedArtifacts = [];
|
|
1560
1577
|
if (!opts.quiet) {
|
|
1561
1578
|
process.stderr.write(
|
|
1562
1579
|
`${C.yellow}!${C.reset} artifacts not attempted: ${artifactLock.reason} \u2014 ${artifactsHeld} record(s) stay queued for the next sync
|
|
@@ -1575,6 +1592,7 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
|
|
|
1575
1592
|
let uploadedCount = 0;
|
|
1576
1593
|
let heldCount = livePlan.held.length;
|
|
1577
1594
|
let droppedCount = 0;
|
|
1595
|
+
const uploadedThisRun = [];
|
|
1578
1596
|
const warn = (message) => {
|
|
1579
1597
|
if (!opts.quiet) process.stderr.write(`${C.yellow}!${C.reset} ${message}
|
|
1580
1598
|
`);
|
|
@@ -1651,6 +1669,7 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
|
|
|
1651
1669
|
let status;
|
|
1652
1670
|
let errorCode = "";
|
|
1653
1671
|
let serverMessage = "";
|
|
1672
|
+
let accepted;
|
|
1654
1673
|
try {
|
|
1655
1674
|
const response = await fetchImpl(artifactEndpoint, {
|
|
1656
1675
|
method: "POST",
|
|
@@ -1670,19 +1689,31 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
|
|
|
1670
1689
|
if (typeof body.error?.message === "string") serverMessage = body.error.message;
|
|
1671
1690
|
} catch {
|
|
1672
1691
|
}
|
|
1692
|
+
} else {
|
|
1693
|
+
accepted = response;
|
|
1673
1694
|
}
|
|
1674
1695
|
} catch (err) {
|
|
1675
1696
|
failure = String(err);
|
|
1676
1697
|
}
|
|
1677
|
-
if (
|
|
1698
|
+
if (accepted !== void 0) {
|
|
1699
|
+
const identity = await readUploadedIdentity(accepted);
|
|
1678
1700
|
state.set(record.artifactId, {
|
|
1679
1701
|
state: "uploaded",
|
|
1680
1702
|
attempts: (existing?.attempts ?? 0) + 1,
|
|
1681
1703
|
firstSeen: firstSeenIso,
|
|
1682
1704
|
sha256: record.sha256,
|
|
1683
|
-
reachedServer: true
|
|
1705
|
+
reachedServer: true,
|
|
1706
|
+
...identity.id !== null ? { serverId: identity.id } : {},
|
|
1707
|
+
...identity.url !== null ? { url: identity.url } : {}
|
|
1684
1708
|
});
|
|
1685
1709
|
uploadedCount += 1;
|
|
1710
|
+
uploadedThisRun.push({
|
|
1711
|
+
artifactId: record.artifactId,
|
|
1712
|
+
filename: record.filename,
|
|
1713
|
+
caption: record.caption,
|
|
1714
|
+
id: identity.id,
|
|
1715
|
+
url: identity.url
|
|
1716
|
+
});
|
|
1686
1717
|
continue;
|
|
1687
1718
|
}
|
|
1688
1719
|
if (status === 401) {
|
|
@@ -1739,11 +1770,15 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
|
|
|
1739
1770
|
artifactsUploaded = uploadedCount;
|
|
1740
1771
|
artifactsHeld = heldCount;
|
|
1741
1772
|
artifactsDropped = droppedCount;
|
|
1773
|
+
uploadedArtifacts = uploadedThisRun;
|
|
1742
1774
|
if (!opts.quiet) {
|
|
1743
1775
|
process.stderr.write(
|
|
1744
1776
|
`${C.dim}artifacts${C.reset} ${uploadedCount} uploaded \xB7 ${heldCount} held \xB7 ${droppedCount} dropped
|
|
1745
1777
|
`
|
|
1746
1778
|
);
|
|
1779
|
+
if (!opts.json) {
|
|
1780
|
+
for (const uploaded of uploadedThisRun) process.stdout.write(uploadedArtifactLine(uploaded) + "\n");
|
|
1781
|
+
}
|
|
1747
1782
|
}
|
|
1748
1783
|
}
|
|
1749
1784
|
const persisted = writeArtifactSpoolState(state, home);
|
|
@@ -1776,6 +1811,7 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
|
|
|
1776
1811
|
);
|
|
1777
1812
|
}
|
|
1778
1813
|
}
|
|
1814
|
+
const artifactReport = artifactsUploaded !== void 0 ? { artifactsUploaded, artifactsHeld, uploadedArtifacts } : {};
|
|
1779
1815
|
let output;
|
|
1780
1816
|
if (opts.json) {
|
|
1781
1817
|
output = JSON.stringify({
|
|
@@ -1787,7 +1823,7 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
|
|
|
1787
1823
|
artifactsDropped,
|
|
1788
1824
|
tier,
|
|
1789
1825
|
url,
|
|
1790
|
-
...
|
|
1826
|
+
...artifactReport
|
|
1791
1827
|
});
|
|
1792
1828
|
} else {
|
|
1793
1829
|
const lines = [];
|
|
@@ -1826,7 +1862,7 @@ ${C.yellow}!${C.reset} batch ${shippedBatches + 1}/${batches.length} accounting
|
|
|
1826
1862
|
url,
|
|
1827
1863
|
dryRun: false,
|
|
1828
1864
|
watermarkMs,
|
|
1829
|
-
...
|
|
1865
|
+
...artifactReport,
|
|
1830
1866
|
output
|
|
1831
1867
|
};
|
|
1832
1868
|
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { mcpTargetPath, INIT_STDIO_ENTRY, INIT_DOCS_ENTRY, DOCS_MCP_SERVER_NAME } from './chunk-XCK2YAY5.js';
|
|
4
|
+
import { POLICY_CONTENT, VALIDATOR_FILES, VALIDATORS_DIR, POLICY_PATH } from './chunk-FOJGZYFC.js';
|
|
5
|
+
import { pinExists, readContextManifest } from './chunk-LCEXHCHD.js';
|
|
6
|
+
import { resolvePackRoot, loadTemplateFiles, skillDir } from './chunk-ZA5CHAFY.js';
|
|
7
|
+
import { findGitDirs } from './chunk-BJ3KTMDH.js';
|
|
8
|
+
import { createHash } from 'crypto';
|
|
9
|
+
import { existsSync, readFileSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
|
|
12
|
+
createRequire(import.meta.url);
|
|
13
|
+
var SHIPPED_MANIFEST = "shipped.json";
|
|
14
|
+
function currentStarterFiles(packRoot = resolvePackRoot(), policy = POLICY_CONTENT) {
|
|
15
|
+
const templates = loadTemplateFiles(packRoot).map((f) => ({
|
|
16
|
+
path: f.path,
|
|
17
|
+
content: f.content,
|
|
18
|
+
skill: f.skill,
|
|
19
|
+
source: join(packRoot, "template", f.skill, f.path.slice(skillDir(f.skill).length + 1))
|
|
20
|
+
}));
|
|
21
|
+
const validators = VALIDATOR_FILES.map(({ name }) => {
|
|
22
|
+
const source = join(packRoot, "validators", name);
|
|
23
|
+
return { path: `${VALIDATORS_DIR}/${name}`, content: readFileSync(source, "utf8"), source };
|
|
24
|
+
});
|
|
25
|
+
return [...templates, ...validators, { path: POLICY_PATH, content: policy }];
|
|
26
|
+
}
|
|
27
|
+
function contentHash(content) {
|
|
28
|
+
return createHash("sha256").update(content.replace(/\r\n/g, "\n")).digest("hex");
|
|
29
|
+
}
|
|
30
|
+
function readShippedManifest(packRoot = resolvePackRoot()) {
|
|
31
|
+
const path = join(packRoot, SHIPPED_MANIFEST);
|
|
32
|
+
if (!existsSync(path)) return { ok: false, problem: "missing" };
|
|
33
|
+
try {
|
|
34
|
+
const doc = JSON.parse(readFileSync(path, "utf8"));
|
|
35
|
+
if (doc.schemaVersion !== 1 || doc.files === null || typeof doc.files !== "object" || Array.isArray(doc.files)) {
|
|
36
|
+
return { ok: false, problem: "unreadable" };
|
|
37
|
+
}
|
|
38
|
+
const files = /* @__PURE__ */ new Map();
|
|
39
|
+
for (const [file, hashes] of Object.entries(doc.files)) {
|
|
40
|
+
if (!Array.isArray(hashes) || !hashes.every((h) => typeof h === "string" && /^[0-9a-f]{64}$/.test(h))) {
|
|
41
|
+
return { ok: false, problem: "unreadable" };
|
|
42
|
+
}
|
|
43
|
+
files.set(file, new Set(hashes));
|
|
44
|
+
}
|
|
45
|
+
return { ok: true, files };
|
|
46
|
+
} catch {
|
|
47
|
+
return { ok: false, problem: "unreadable" };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function mcpItemsInit() {
|
|
51
|
+
return [
|
|
52
|
+
// Through 0.12.0 `init` wrote a command that needs a global install on every teammate's machine.
|
|
53
|
+
{ name: "outerlayer", current: INIT_STDIO_ENTRY, shipped: [{ type: "stdio", command: "outerlayer", args: ["mcp", "serve"] }] },
|
|
54
|
+
{ name: DOCS_MCP_SERVER_NAME, current: INIT_DOCS_ENTRY, shipped: [] }
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
function canonical(value) {
|
|
58
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
59
|
+
if (value !== null && typeof value === "object") {
|
|
60
|
+
const entries = Object.entries(value).sort(([a], [b]) => a.localeCompare(b));
|
|
61
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(",")}}`;
|
|
62
|
+
}
|
|
63
|
+
return JSON.stringify(value);
|
|
64
|
+
}
|
|
65
|
+
function fileState(root, file, manifest) {
|
|
66
|
+
const abs = join(root, file.path);
|
|
67
|
+
if (!existsSync(abs)) return "missing";
|
|
68
|
+
const hash = contentHash(readFileSync(abs, "utf8"));
|
|
69
|
+
if (hash === contentHash(file.content)) return "current";
|
|
70
|
+
if (manifest.ok && manifest.files.get(file.path)?.has(hash) === true) return "outdated";
|
|
71
|
+
return "edited";
|
|
72
|
+
}
|
|
73
|
+
function mcpItems(root) {
|
|
74
|
+
const path = mcpTargetPath(root);
|
|
75
|
+
let servers;
|
|
76
|
+
let unreadable = false;
|
|
77
|
+
if (existsSync(join(root, path))) {
|
|
78
|
+
try {
|
|
79
|
+
const doc = JSON.parse(readFileSync(join(root, path), "utf8"));
|
|
80
|
+
const value = doc.mcpServers;
|
|
81
|
+
if (value !== void 0 && (value === null || typeof value !== "object" || Array.isArray(value))) unreadable = true;
|
|
82
|
+
else servers = value ?? {};
|
|
83
|
+
} catch {
|
|
84
|
+
unreadable = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return mcpItemsInit().map((item) => {
|
|
88
|
+
const base = { kind: "mcp", path, server: item.name, entry: item.current };
|
|
89
|
+
if (unreadable) return { ...base, state: "unreadable" };
|
|
90
|
+
if (servers === void 0 || !(item.name in servers)) return { ...base, state: "missing" };
|
|
91
|
+
const have = canonical(servers[item.name]);
|
|
92
|
+
if (have === canonical(item.current)) return { ...base, state: "current" };
|
|
93
|
+
if (item.shipped.some((entry) => canonical(entry) === have)) return { ...base, state: "outdated" };
|
|
94
|
+
return { ...base, state: "edited" };
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function inspectStarterFiles(root, opts = {}) {
|
|
98
|
+
const packRoot = opts.packRoot ?? resolvePackRoot();
|
|
99
|
+
const manifest = readShippedManifest(packRoot);
|
|
100
|
+
const files = currentStarterFiles(packRoot).map(
|
|
101
|
+
(file) => ({ kind: "file", path: file.path, state: fileState(root, file, manifest), file })
|
|
102
|
+
);
|
|
103
|
+
return { items: [...files, ...mcpItems(root)], manifest: manifest.ok ? "ok" : manifest.problem };
|
|
104
|
+
}
|
|
105
|
+
function itemLabel(item) {
|
|
106
|
+
return item.kind === "file" ? item.path : `${item.path} "${item.server}" server`;
|
|
107
|
+
}
|
|
108
|
+
function contextIsGoverned(root, gitCommonDir) {
|
|
109
|
+
return pinExists(root) || readContextManifest(gitCommonDir) !== void 0;
|
|
110
|
+
}
|
|
111
|
+
var NAME = "Starter files";
|
|
112
|
+
var UPDATE = "outerlayer init --update";
|
|
113
|
+
function shownPaths(items, all) {
|
|
114
|
+
const shown = [];
|
|
115
|
+
const whole = /* @__PURE__ */ new Set();
|
|
116
|
+
for (const item of items) {
|
|
117
|
+
const skill = item.kind === "file" ? item.file.skill : void 0;
|
|
118
|
+
if (skill !== void 0) {
|
|
119
|
+
const ofSkill = all.filter((i) => i.kind === "file" && i.file.skill === skill);
|
|
120
|
+
if (ofSkill.every((i) => items.includes(i))) {
|
|
121
|
+
if (!whole.has(skill)) shown.push(`${skillDir(skill)}/`);
|
|
122
|
+
whole.add(skill);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
shown.push(itemLabel(item));
|
|
127
|
+
}
|
|
128
|
+
return shown;
|
|
129
|
+
}
|
|
130
|
+
function checkStarterFiles(cwd, opts = {}) {
|
|
131
|
+
const none = { checks: [], mcpOutdated: false };
|
|
132
|
+
const dirs = findGitDirs(cwd ?? process.cwd());
|
|
133
|
+
if (dirs === void 0 || contextIsGoverned(dirs.repoRoot, dirs.gitCommonDir)) return none;
|
|
134
|
+
const report = inspectStarterFiles(dirs.repoRoot, opts);
|
|
135
|
+
if (report.items.every((i) => i.kind === "mcp" || i.state === "missing")) return none;
|
|
136
|
+
const by = (state) => report.items.filter((i) => i.state === state);
|
|
137
|
+
const current = by("current");
|
|
138
|
+
const outdated = by("outdated");
|
|
139
|
+
const missing = by("missing");
|
|
140
|
+
const edited = by("edited");
|
|
141
|
+
const unreadable = by("unreadable");
|
|
142
|
+
const parts = [];
|
|
143
|
+
if (current.length > 0) parts.push(`${current.length} current`);
|
|
144
|
+
if (outdated.length > 0) parts.push(`outdated and unedited: ${shownPaths(outdated, report.items).join(", ")}`);
|
|
145
|
+
if (missing.length > 0) parts.push(`missing: ${shownPaths(missing, report.items).join(", ")}`);
|
|
146
|
+
if (edited.length > 0) parts.push(`edited by you, left as they are: ${edited.map(itemLabel).join(", ")}`);
|
|
147
|
+
if (unreadable.length > 0) parts.push(`${unreadable[0].path} is not valid JSON, so its servers were not read`);
|
|
148
|
+
if (report.manifest !== "ok") {
|
|
149
|
+
parts.push(`this CLI's record of earlier versions is ${report.manifest}, so an outdated file cannot be told from an edited one`);
|
|
150
|
+
}
|
|
151
|
+
const mcpOutdated = outdated.some((i) => i.kind === "mcp" && i.server === "outerlayer");
|
|
152
|
+
const actionable = outdated.length + missing.length > 0;
|
|
153
|
+
const compare = edited.length > 0 ? ` Compare an edited file with this CLI's version: \`${UPDATE} --dry-run\`.` : "";
|
|
154
|
+
if (!actionable && report.manifest === "ok") {
|
|
155
|
+
return { checks: [{ name: NAME, status: "pass", detail: parts.join("; ") + (compare === "" ? "" : `.${compare}`) }], mcpOutdated };
|
|
156
|
+
}
|
|
157
|
+
const fix = actionable ? `Run \`${UPDATE}\` to update the outdated files and write the missing ones. It never changes a file you edited.${compare}` : "Reinstall the CLI: `npm install -g @outerlayer/cli`, or run it with `npx -y @outerlayer/cli`.";
|
|
158
|
+
return { checks: [{ name: NAME, status: "warn", detail: parts.join("; "), fix }], mcpOutdated };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export { canonical, checkStarterFiles, contextIsGoverned, inspectStarterFiles };
|