bearings 0.5.2 → 0.5.4
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/README.md +40 -22
- package/dist/chunk-MOZJECCW.js +1823 -0
- package/dist/cli.js +189 -158
- package/dist/{update-4DZKUPFP.js → update-YY5SLPDF.js} +84 -221
- package/package.json +1 -1
- package/templates/AGENTS.md +1 -5
- package/templates/agents/commands/refresh-repo-map.md +36 -13
- package/templates/agents/commands/update-bearings-setup.md +144 -0
- package/templates/agents/skills/checklist/previewer/index.html +44 -10
- package/templates/agents/skills/checklist/previewer/viewer.css +321 -77
- package/templates/agents/skills/checklist/previewer/viewer.js +797 -97
- package/templates/initial-setup.md +112 -0
- package/dist/chunk-WYXKSCUW.js +0 -818
- package/templates/agents/commands/setup-repo.md +0 -182
package/dist/cli.js
CHANGED
|
@@ -1,158 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
inspectManifest,
|
|
4
|
-
|
|
4
|
+
renderSetupScope,
|
|
5
5
|
renderVerifyReport,
|
|
6
6
|
runInit,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from "./chunk-
|
|
7
|
+
templatesDir,
|
|
8
|
+
validateHarnesses,
|
|
9
|
+
verify,
|
|
10
|
+
verifySetupOutcome
|
|
11
|
+
} from "./chunk-MOZJECCW.js";
|
|
12
12
|
|
|
13
13
|
// src/cli.ts
|
|
14
14
|
import { Command } from "commander";
|
|
15
15
|
import { readFileSync } from "fs";
|
|
16
16
|
import { fileURLToPath } from "url";
|
|
17
|
-
import { dirname
|
|
18
|
-
|
|
19
|
-
// src/verifier.ts
|
|
20
|
-
import { access, lstat, readFile, readdir, readlink, stat } from "fs/promises";
|
|
21
|
-
import { dirname, join, resolve } from "path";
|
|
22
|
-
var KINDS = ["skills", "commands"];
|
|
23
|
-
var PLACEHOLDER = /<agent:[^>\n]*>/;
|
|
24
|
-
async function exists(p) {
|
|
25
|
-
try {
|
|
26
|
-
await access(p);
|
|
27
|
-
return true;
|
|
28
|
-
} catch {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async function verify(repoDir) {
|
|
33
|
-
const failures = [];
|
|
34
|
-
const warnings = [];
|
|
35
|
-
const missingSkillBaselines = /* @__PURE__ */ new Set();
|
|
36
|
-
async function warnMissingSkillBaseline(baseline) {
|
|
37
|
-
if (missingSkillBaselines.has(baseline) || await exists(join(repoDir, baseline))) return;
|
|
38
|
-
missingSkillBaselines.add(baseline);
|
|
39
|
-
warnings.push({ code: "missing-skill-baseline", path: baseline, message: "run /setup-repo to repair skill baseline" });
|
|
40
|
-
}
|
|
41
|
-
const state = await inspectManifest(repoDir);
|
|
42
|
-
if (state.kind === "absent") {
|
|
43
|
-
return { failures: [{ code: "no-manifest", path: ".agents/bearings.json", message: "run bearings init first" }], warnings };
|
|
44
|
-
}
|
|
45
|
-
if (state.kind === "invalid") {
|
|
46
|
-
return { failures: [{ code: "invalid-manifest", path: ".agents/bearings.json", message: state.message }], warnings };
|
|
47
|
-
}
|
|
48
|
-
const m = state.manifest;
|
|
49
|
-
if ("setupPending" in m && m.setupPending) {
|
|
50
|
-
warnings.push({ code: "setup-pending", path: ".agents/bearings.json", message: "run /setup-repo to finish the pending update" });
|
|
51
|
-
}
|
|
52
|
-
for (const f of m.files) {
|
|
53
|
-
const retired = "retired" in f && f.retired === true;
|
|
54
|
-
if (!retired) {
|
|
55
|
-
const abs = join(repoDir, f.path);
|
|
56
|
-
if (!await exists(abs)) {
|
|
57
|
-
failures.push({ code: "missing-file", path: f.path, message: "managed file deleted" });
|
|
58
|
-
} else {
|
|
59
|
-
const content = await readFile(abs, "utf8");
|
|
60
|
-
if (f.owner === "agent" && PLACEHOLDER.test(content)) {
|
|
61
|
-
warnings.push({ code: "unfilled-placeholder", path: f.path, message: "run /setup-repo to fill" });
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (!retired && f.owner === "agent" && isStarterSkillTarget(f.path)) {
|
|
66
|
-
const skillName = starterSkillNameFromTarget(f.path);
|
|
67
|
-
await warnMissingSkillBaseline(skillBaselinePath(skillName));
|
|
68
|
-
}
|
|
69
|
-
if ("backup" in f && f.backup && await exists(join(repoDir, f.backup))) {
|
|
70
|
-
warnings.push({ code: "unreviewed-backup", path: f.backup, message: "review during /setup-repo, then delete" });
|
|
71
|
-
}
|
|
72
|
-
if ("reconciliations" in f && f.reconciliations) {
|
|
73
|
-
for (const r of f.reconciliations) {
|
|
74
|
-
if (r.reason === "skill-update") await warnMissingSkillBaseline(r.basePath);
|
|
75
|
-
if (await exists(join(repoDir, r.backup))) {
|
|
76
|
-
warnings.push({ code: "pending-reconciliation", path: r.backup, message: "review during /setup-repo, then delete" });
|
|
77
|
-
}
|
|
78
|
-
if (r.reason === "skill-update" && await exists(join(repoDir, r.incomingPath))) {
|
|
79
|
-
warnings.push({ code: "pending-reconciliation", path: r.incomingPath, message: "review during /setup-repo, then delete" });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if ("migrationReconciliations" in m && m.migrationReconciliations) {
|
|
85
|
-
for (const reconciliation of m.migrationReconciliations) {
|
|
86
|
-
if (await exists(join(repoDir, reconciliation.backup))) {
|
|
87
|
-
warnings.push({
|
|
88
|
-
code: "pending-reconciliation",
|
|
89
|
-
path: reconciliation.backup,
|
|
90
|
-
message: reconciliation.kind === "invalid-testplan" ? "repair with the checklist skill during /setup-repo, then delete" : "review during /setup-repo, then delete"
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
for (const kind of KINDS) {
|
|
96
|
-
const srcDir = join(repoDir, ".agents", kind);
|
|
97
|
-
let entries = [];
|
|
98
|
-
try {
|
|
99
|
-
entries = await readdir(srcDir);
|
|
100
|
-
} catch {
|
|
101
|
-
}
|
|
102
|
-
for (const h of m.harnesses) {
|
|
103
|
-
for (const name of entries) {
|
|
104
|
-
const exposurePath = join(`.${h}`, kind, name);
|
|
105
|
-
const dst = join(repoDir, `.${h}`, kind, name);
|
|
106
|
-
const l = await lstat(dst).catch(() => null);
|
|
107
|
-
if (!l) {
|
|
108
|
-
failures.push({ code: "missing-exposure", path: exposurePath, message: `not exposed to ${h}` });
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
if (l.isSymbolicLink()) {
|
|
112
|
-
if (!await stat(dst).catch(() => null)) {
|
|
113
|
-
failures.push({ code: "broken-symlink", path: exposurePath, message: "symlink target missing" });
|
|
114
|
-
} else {
|
|
115
|
-
const target = await readlink(dst);
|
|
116
|
-
if (resolve(dirname(dst), target) !== resolve(srcDir, name)) {
|
|
117
|
-
failures.push({ code: "missing-exposure", path: exposurePath, message: `not exposed to ${h}` });
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
} else if (m.exposure === "symlink") {
|
|
121
|
-
failures.push({ code: "missing-exposure", path: exposurePath, message: `not exposed to ${h}` });
|
|
122
|
-
} else {
|
|
123
|
-
const src = join(srcDir, name);
|
|
124
|
-
if (await copyDrifted(src, dst)) {
|
|
125
|
-
failures.push({ code: "copy-drift", path: exposurePath, message: "diverged from .agents source" });
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return { failures, warnings };
|
|
132
|
-
}
|
|
133
|
-
async function copyDrifted(src, dst) {
|
|
134
|
-
const s = await stat(src);
|
|
135
|
-
if (s.isDirectory()) {
|
|
136
|
-
const [srcChildren, dstChildren] = await Promise.all([
|
|
137
|
-
readdir(src),
|
|
138
|
-
readdir(dst).catch(() => null)
|
|
139
|
-
]);
|
|
140
|
-
if (dstChildren === null) return true;
|
|
141
|
-
const srcNames = new Set(srcChildren);
|
|
142
|
-
for (const child of dstChildren) {
|
|
143
|
-
if (!srcNames.has(child)) return true;
|
|
144
|
-
}
|
|
145
|
-
for (const child of srcChildren) {
|
|
146
|
-
if (await copyDrifted(join(src, child), join(dst, child))) return true;
|
|
147
|
-
}
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
const [a, b] = await Promise.all([
|
|
151
|
-
readFile(src, "utf8"),
|
|
152
|
-
readFile(dst, "utf8").catch(() => null)
|
|
153
|
-
]);
|
|
154
|
-
return b === null || sha256(a) !== sha256(b);
|
|
155
|
-
}
|
|
17
|
+
import { dirname, join as join3, resolve } from "path";
|
|
156
18
|
|
|
157
19
|
// src/commands/verify.ts
|
|
158
20
|
async function runVerify(repoDir) {
|
|
@@ -163,8 +25,8 @@ async function runVerify(repoDir) {
|
|
|
163
25
|
|
|
164
26
|
// src/commands/checklist.ts
|
|
165
27
|
import { spawn } from "child_process";
|
|
166
|
-
import { access
|
|
167
|
-
import { join
|
|
28
|
+
import { access } from "fs/promises";
|
|
29
|
+
import { join } from "path";
|
|
168
30
|
function openChecklist(path, platform = process.platform, spawnProcess = spawn) {
|
|
169
31
|
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
170
32
|
const args = platform === "win32" ? ["/c", "start", "", path] : [path];
|
|
@@ -183,18 +45,18 @@ async function runBuilder(builder, repoDir) {
|
|
|
183
45
|
});
|
|
184
46
|
}
|
|
185
47
|
async function runChecklist(repoDir, open = openChecklist, log = console.log) {
|
|
186
|
-
const bundle =
|
|
187
|
-
const builder =
|
|
188
|
-
const previewer =
|
|
48
|
+
const bundle = join(repoDir, ".agents/skills/checklist");
|
|
49
|
+
const builder = join(bundle, "scripts/build.mjs");
|
|
50
|
+
const previewer = join(bundle, "previewer/index.html");
|
|
189
51
|
log(previewer);
|
|
190
52
|
try {
|
|
191
53
|
await Promise.all([
|
|
192
54
|
builder,
|
|
193
|
-
|
|
55
|
+
join(bundle, "scripts/validate.mjs"),
|
|
194
56
|
previewer,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
].map((path) =>
|
|
57
|
+
join(bundle, "previewer/viewer.js"),
|
|
58
|
+
join(bundle, "previewer/viewer.css")
|
|
59
|
+
].map((path) => access(path)));
|
|
198
60
|
} catch {
|
|
199
61
|
throw new Error("Checklist previewer is not installed. Run `bearings init` to update this repository.");
|
|
200
62
|
}
|
|
@@ -204,18 +66,187 @@ async function runChecklist(repoDir, open = openChecklist, log = console.log) {
|
|
|
204
66
|
return 0;
|
|
205
67
|
}
|
|
206
68
|
|
|
69
|
+
// src/commands/setup.ts
|
|
70
|
+
import { readFile } from "fs/promises";
|
|
71
|
+
import { join as join2 } from "path";
|
|
72
|
+
|
|
73
|
+
// src/setup-launcher.ts
|
|
74
|
+
import { spawn as spawn2 } from "child_process";
|
|
75
|
+
function isKnownAgentContext(env = process.env) {
|
|
76
|
+
return Boolean(env.CLAUDE_CODE_CHILD_SESSION || env.OPENCODE_TERMINAL);
|
|
77
|
+
}
|
|
78
|
+
async function selectHarness(harnesses) {
|
|
79
|
+
const p = await import("@clack/prompts");
|
|
80
|
+
const selected = await p.select({
|
|
81
|
+
message: "Which harness should run guided setup?",
|
|
82
|
+
options: harnesses.map((value) => ({
|
|
83
|
+
value,
|
|
84
|
+
label: value === "claude" ? "Claude Code" : "OpenCode"
|
|
85
|
+
}))
|
|
86
|
+
});
|
|
87
|
+
return p.isCancel(selected) ? void 0 : selected;
|
|
88
|
+
}
|
|
89
|
+
async function runSetupSession(repoDir, prompt, options, deps = {}) {
|
|
90
|
+
const harnesses = [...new Set(options.harnesses)];
|
|
91
|
+
let harness = options.harness ?? (harnesses.length === 1 ? harnesses[0] : void 0);
|
|
92
|
+
const handoff = (message) => ({ status: "handoff", harness, message });
|
|
93
|
+
if (options.noAgent) return handoff("Agent launch disabled by --no-agent. Continue setup in your agent session.");
|
|
94
|
+
if (options.harness !== void 0 && (!["claude", "opencode"].includes(options.harness) || !harnesses.includes(options.harness))) {
|
|
95
|
+
return { status: "failed", message: `Harness ${options.harness} is not configured. Choose a configured harness: ${harnesses.join(", ") || "none"}.` };
|
|
96
|
+
}
|
|
97
|
+
if (!harnesses.length) return handoff("No harness is configured. Configure Claude Code or OpenCode with bearings init.");
|
|
98
|
+
if (options.yes) return handoff("Agent launch disabled by --yes. Run again without --yes in your terminal, or continue setup in your agent session.");
|
|
99
|
+
if (isKnownAgentContext(deps.env)) return handoff("Guided setup is already inside an agent session. Continue with the scoped task in this session.");
|
|
100
|
+
const terminal = deps.terminal ?? {
|
|
101
|
+
stdin: Boolean(process.stdin.isTTY),
|
|
102
|
+
stdout: Boolean(process.stdout.isTTY),
|
|
103
|
+
stderr: Boolean(process.stderr.isTTY)
|
|
104
|
+
};
|
|
105
|
+
if (!terminal.stdin || !terminal.stdout || !terminal.stderr) {
|
|
106
|
+
return handoff("Guided setup needs a user terminal for stdin, stdout, and stderr. Run again in your terminal, or continue setup in your agent session.");
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
harness ??= await (deps.selectHarness ?? selectHarness)(harnesses);
|
|
110
|
+
if (!harness) return handoff("Harness selection cancelled. Continue setup in your agent session when ready.");
|
|
111
|
+
if (!harnesses.includes(harness) || !["claude", "opencode"].includes(harness)) {
|
|
112
|
+
return { status: "failed", message: "Select a configured Claude Code or OpenCode harness and retry." };
|
|
113
|
+
}
|
|
114
|
+
const selected = harness;
|
|
115
|
+
const args = selected === "claude" ? [prompt] : [repoDir, "--prompt", prompt];
|
|
116
|
+
return await new Promise((resolve2, reject) => {
|
|
117
|
+
const child = (deps.spawn ?? spawn2)(selected, args, { cwd: repoDir, stdio: "inherit", shell: false });
|
|
118
|
+
child.once("error", reject);
|
|
119
|
+
child.once("exit", (exitCode, signal) => resolve2({
|
|
120
|
+
status: exitCode === 0 && signal === null ? "exited" : "failed",
|
|
121
|
+
harness: selected,
|
|
122
|
+
exitCode,
|
|
123
|
+
signal,
|
|
124
|
+
message: exitCode === 0 && signal === null ? `${selected} session exited. Check pending setup work before reporting completion.` : `${selected} session stopped (${signal ?? `exit ${exitCode ?? "unknown"}`}). Check pending work and resume setup in your agent session.`
|
|
125
|
+
}));
|
|
126
|
+
});
|
|
127
|
+
} catch (error) {
|
|
128
|
+
const missing = error?.code === "ENOENT";
|
|
129
|
+
return {
|
|
130
|
+
status: "failed",
|
|
131
|
+
harness,
|
|
132
|
+
message: missing ? `Could not launch ${harness}: executable or repository directory not found. Check the repository path and install ${harness} on PATH, then retry or continue setup in your agent session.` : `Could not start ${harness ?? "guided setup"}: ${error instanceof Error ? error.message : String(error)}. Retry or continue setup in your agent session.`
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/commands/setup.ts
|
|
138
|
+
function quoteInvocationPart(value, platform) {
|
|
139
|
+
return platform === "win32" ? `'${value.replaceAll("'", "''")}'` : `'${value.replaceAll("'", "'\\''")}'`;
|
|
140
|
+
}
|
|
141
|
+
function renderSetupInvocation(invocation, platform = process.platform) {
|
|
142
|
+
return {
|
|
143
|
+
command: `${platform === "win32" ? "& " : ""}${invocation.map((part) => quoteInvocationPart(part, platform)).join(" ")}`,
|
|
144
|
+
shell: platform === "win32" ? "PowerShell" : "POSIX"
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
async function runSetup(repoDir, manifest, scope, options = {}, deps = {}) {
|
|
148
|
+
const original = structuredClone(manifest);
|
|
149
|
+
if (!original.setupPending?.tasks?.length) return { report: "", exitCode: 0 };
|
|
150
|
+
const { invocation, ...sessionOptions } = options;
|
|
151
|
+
const rendered = invocation?.length ? renderSetupInvocation(invocation, deps.platform) : { command: "npx bearings", shell: deps.platform === "win32" ? "PowerShell" : "POSIX" };
|
|
152
|
+
const command = rendered.command;
|
|
153
|
+
const resume = `Ask your current agent to run \`${command} init --no-agent\` and finish the returned task.`;
|
|
154
|
+
const initial = original.setupPending.kind === "init";
|
|
155
|
+
const recipePath = initial ? join2(templatesDir(), "initial-setup.md") : join2(repoDir, ".agents/commands/update-bearings-setup.md");
|
|
156
|
+
let recipe;
|
|
157
|
+
try {
|
|
158
|
+
recipe = await readFile(recipePath, "utf8").catch((error) => {
|
|
159
|
+
if (initial || error.code !== "ENOENT") throw error;
|
|
160
|
+
return readFile(join2(templatesDir(), "agents/commands/update-bearings-setup.md"), "utf8");
|
|
161
|
+
});
|
|
162
|
+
} catch {
|
|
163
|
+
return { report: `Setup pending: cannot read the setup recipe. ${resume}`, exitCode: 1 };
|
|
164
|
+
}
|
|
165
|
+
const commands = [
|
|
166
|
+
"## bearings Commands for This Task",
|
|
167
|
+
"Use these exact shell commands from the repository. They override generic recipe examples and select the initiating bearings executable.",
|
|
168
|
+
`Shell: ${rendered.shell}.`,
|
|
169
|
+
"This is the bearings setup workflow. When AGENTS.md is listed, remove obsolete Setup Required or Pending bearings Setup sections. Repository inspection needed for these tasks is permitted.",
|
|
170
|
+
`Resume: ${command} init --no-agent`,
|
|
171
|
+
`Complete: ${command} init --complete-setup`,
|
|
172
|
+
"Checkpoint selected tasks: append --setup-path <path...> to Complete.",
|
|
173
|
+
"Decline incoming skill templates: also append --keep-local <path...> for the scoped skill-update paths the developer chose to keep.",
|
|
174
|
+
"Quote each task path as a shell argument. A checkpoint completes only its selected paths; reread the remaining journal tasks."
|
|
175
|
+
].join("\n");
|
|
176
|
+
const prompt = `${commands}
|
|
177
|
+
|
|
178
|
+
${recipe.trim()}
|
|
179
|
+
|
|
180
|
+
${scope.trim()}
|
|
181
|
+
`;
|
|
182
|
+
const terminal = deps.terminal ?? {
|
|
183
|
+
stdin: Boolean(process.stdin.isTTY),
|
|
184
|
+
stdout: Boolean(process.stdout.isTTY),
|
|
185
|
+
stderr: Boolean(process.stderr.isTTY)
|
|
186
|
+
};
|
|
187
|
+
const env = deps.env ?? process.env;
|
|
188
|
+
const nested = isKnownAgentContext(env);
|
|
189
|
+
if (!options.noAgent && !options.yes && !nested && terminal.stdin && terminal.stdout && terminal.stderr && original.harnesses.length) {
|
|
190
|
+
const harness = options.harness ?? (original.harnesses.length === 1 ? original.harnesses[0] : void 0);
|
|
191
|
+
const openCode = harness === "opencode" || !harness && original.harnesses.includes("opencode");
|
|
192
|
+
(deps.log ?? console.log)(openCode ? "Guided setup: answer the harness prompts. In OpenCode, press Enter to submit the prepared task." : "Guided setup: answer the harness trust and permission prompts.");
|
|
193
|
+
}
|
|
194
|
+
const result = await (deps.runSession ?? runSetupSession)(repoDir, prompt, {
|
|
195
|
+
...sessionOptions,
|
|
196
|
+
harnesses: original.harnesses
|
|
197
|
+
}, { terminal, ...deps.env ? { env } : {} });
|
|
198
|
+
if (result.status === "handoff") {
|
|
199
|
+
const printTask = options.noAgent || options.yes || nested || !terminal.stdin || !terminal.stdout || !terminal.stderr;
|
|
200
|
+
return {
|
|
201
|
+
report: printTask ? `Setup pending. Continue this task in your agent session:
|
|
202
|
+
|
|
203
|
+
${prompt}` : `Setup pending. ${resume}`,
|
|
204
|
+
exitCode: 0
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
const current = await inspectManifest(repoDir);
|
|
208
|
+
const pending = current.kind === "valid" && current.manifest.version === 2 && current.manifest.setupPending;
|
|
209
|
+
if (result.status === "failed" && pending) {
|
|
210
|
+
return { report: `Setup pending. ${resume}`, exitCode: 1 };
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
await verifySetupOutcome(repoDir, original);
|
|
214
|
+
return { report: "bearings setup complete.", exitCode: 0 };
|
|
215
|
+
} catch (error) {
|
|
216
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
217
|
+
return pending ? { report: `Setup pending. ${resume}`, exitCode: result.status === "failed" ? 1 : 0 } : { report: `Setup completion could not be verified: ${message}
|
|
218
|
+
${resume}`, exitCode: 1 };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
207
222
|
// src/cli.ts
|
|
208
223
|
var pkg = JSON.parse(
|
|
209
|
-
readFileSync(join3(
|
|
224
|
+
readFileSync(join3(dirname(fileURLToPath(import.meta.url)), "../package.json"), "utf8")
|
|
210
225
|
);
|
|
211
226
|
var program = new Command("bearings").version(pkg.version);
|
|
212
|
-
program.command("init").description("Scaffold the agent-friendly setup in the current repository").option("--harness <name...>", "claude and/or opencode").option("--copy", "copy instead of symlink").option("-y, --yes", "accept defaults, no prompts").action(async (o) => {
|
|
213
|
-
|
|
227
|
+
program.command("init").description("Scaffold the agent-friendly setup in the current repository").option("--harness <name...>", "claude and/or opencode").option("--copy", "copy instead of symlink").option("-y, --yes", "accept defaults, no prompts").option("--no-agent", "print the scoped setup task without launching an agent").option("--setup-harness <name>", "run guided setup with one configured harness: claude or opencode").option("--complete-setup", "validate and finalize pending setup without launching an agent").option("--setup-path <path...>", "checkpoint only these pending paths (requires --complete-setup)").option("--keep-local <path...>", "decline incoming templates for these scoped skill updates (requires --complete-setup)").action(async (o) => {
|
|
228
|
+
if (!o.completeSetup && (o.setupPath || o.keepLocal)) {
|
|
229
|
+
throw new Error("--setup-path and --keep-local require --complete-setup.");
|
|
230
|
+
}
|
|
231
|
+
const harness = validateHarnesses(o.setupHarness === void 0 ? void 0 : [o.setupHarness])?.[0];
|
|
232
|
+
const { manifest, report } = await runInit(process.cwd(), {
|
|
214
233
|
harnesses: validateHarnesses(o.harness),
|
|
215
234
|
exposure: o.copy ? "copy" : void 0,
|
|
216
|
-
yes: o.yes
|
|
235
|
+
yes: o.yes,
|
|
236
|
+
completeSetup: o.completeSetup,
|
|
237
|
+
setupPaths: o.setupPath,
|
|
238
|
+
keepLocal: o.keepLocal
|
|
217
239
|
}, pkg.version);
|
|
218
240
|
console.log(report);
|
|
241
|
+
if (o.completeSetup || !manifest.setupPending?.tasks?.length) return;
|
|
242
|
+
const setup = await runSetup(process.cwd(), manifest, renderSetupScope(manifest), {
|
|
243
|
+
harness,
|
|
244
|
+
noAgent: o.agent === false,
|
|
245
|
+
yes: o.yes,
|
|
246
|
+
invocation: [process.execPath, resolve(process.argv[1])]
|
|
247
|
+
});
|
|
248
|
+
if (setup.report) console.log(setup.report);
|
|
249
|
+
process.exitCode = setup.exitCode;
|
|
219
250
|
});
|
|
220
251
|
program.command("verify").description("Check the agent-friendly setup for drift and breakage").action(async () => {
|
|
221
252
|
process.exitCode = await runVerify(process.cwd());
|