release-skill 0.1.3 → 0.1.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -2
- package/.codex-plugin/plugin.json +3 -3
- package/CHANGELOG.md +29 -0
- package/INSTALL.md +69 -32
- package/INSTALL.zh-CN.md +64 -26
- package/README.md +101 -37
- package/README.zh-CN.md +87 -28
- package/adapters/claude/.claude-plugin/marketplace.json +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +79284 -0
- package/adapters/claude/bin/release-skill.mjs +34 -0
- package/adapters/claude/native/safe-write/binding.gyp +40 -0
- package/adapters/claude/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
- package/adapters/claude/native/safe-write/prebuilds.json +24 -0
- package/adapters/claude/native/safe-write/src/safe_write.cc +2032 -0
- package/adapters/claude/schemas/.render-manifest.json +37 -0
- package/adapters/claude/schemas/approval-record.schema.json +115 -0
- package/adapters/claude/schemas/artifact-lock.schema.json +111 -0
- package/adapters/claude/schemas/artifact-plan.schema.json +52 -0
- package/adapters/claude/schemas/artifact-policy.schema.json +76 -0
- package/adapters/claude/schemas/evidence-event.schema.json +89 -0
- package/adapters/claude/schemas/release-plan.schema.json +860 -0
- package/adapters/claude/schemas/release-project.schema.json +736 -0
- package/adapters/claude/schemas/release-run.schema.json +342 -0
- package/adapters/claude/skills/release-assess/SKILL.md +5 -6
- package/adapters/claude/skills/release-help/SKILL.md +11 -18
- package/adapters/claude/skills/release-prepare/SKILL.md +6 -6
- package/adapters/claude/skills/release-publish/SKILL.md +1 -4
- package/adapters/claude/skills/release-reconcile/SKILL.md +5 -5
- package/adapters/claude/skills/release-setup/SKILL.md +64 -80
- package/adapters/claude/skills/release-verify/SKILL.md +4 -7
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +79284 -0
- package/adapters/codex/bin/release-skill.mjs +34 -0
- package/adapters/codex/native/safe-write/binding.gyp +40 -0
- package/adapters/codex/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
- package/adapters/codex/native/safe-write/prebuilds.json +24 -0
- package/adapters/codex/native/safe-write/src/safe_write.cc +2032 -0
- package/adapters/codex/schemas/.render-manifest.json +37 -0
- package/adapters/codex/schemas/approval-record.schema.json +115 -0
- package/adapters/codex/schemas/artifact-lock.schema.json +111 -0
- package/adapters/codex/schemas/artifact-plan.schema.json +52 -0
- package/adapters/codex/schemas/artifact-policy.schema.json +76 -0
- package/adapters/codex/schemas/evidence-event.schema.json +89 -0
- package/adapters/codex/schemas/release-plan.schema.json +860 -0
- package/adapters/codex/schemas/release-project.schema.json +736 -0
- package/adapters/codex/schemas/release-run.schema.json +342 -0
- package/adapters/codex/skills/release-assess/SKILL.md +12 -6
- package/adapters/codex/skills/release-help/SKILL.md +18 -18
- package/adapters/codex/skills/release-prepare/SKILL.md +13 -6
- package/adapters/codex/skills/release-publish/SKILL.md +8 -4
- package/adapters/codex/skills/release-reconcile/SKILL.md +12 -5
- package/adapters/codex/skills/release-setup/SKILL.md +71 -80
- package/adapters/codex/skills/release-verify/SKILL.md +11 -7
- package/bin/release-skill-cli.mjs +807 -0
- package/bin/release-skill.bundle.mjs +79284 -0
- package/bin/release-skill.mjs +23 -788
- package/package.json +6 -2
- package/references/02-project-config.md +2 -2
- package/schemas/release-plan.schema.json +7 -1
- package/schemas/release-project.schema.json +8 -0
- package/scripts/build-bundle.mjs +133 -0
- package/skills/release-assess/SKILL.md +5 -6
- package/skills/release-help/SKILL.md +11 -18
- package/skills/release-prepare/SKILL.md +6 -6
- package/skills/release-publish/SKILL.md +1 -4
- package/skills/release-reconcile/SKILL.md +5 -5
- package/skills/release-setup/SKILL.md +64 -80
- package/skills/release-verify/SKILL.md +4 -7
- package/skills-src/release-assess/SKILL.md +5 -6
- package/skills-src/release-help/SKILL.md +11 -18
- package/skills-src/release-prepare/SKILL.md +6 -6
- package/skills-src/release-publish/SKILL.md +1 -4
- package/skills-src/release-reconcile/SKILL.md +5 -5
- package/skills-src/release-setup/SKILL.md +64 -80
- package/skills-src/release-verify/SKILL.md +4 -7
- package/src/adapters/npm.mjs +5 -13
- package/src/adapters/plugin-marketplace.mjs +67 -31
- package/src/artifacts/policy.mjs +4 -7
- package/src/artifacts/safe-fs-backend-internal.mjs +69 -21
- package/src/commands/prepare.mjs +22 -1
- package/src/commands/publish.mjs +1 -0
- package/src/commands/setup.mjs +667 -28
- package/src/core/approval.mjs +4 -6
- package/src/core/config.mjs +8 -8
- package/src/core/pkg-root.mjs +22 -0
- package/src/core/plan.mjs +9 -4
- package/src/core/run.mjs +4 -4
- package/src/core/trusted-resource.mjs +96 -0
- package/src/docs/version-gate.mjs +164 -0
- package/src/producers/build-adapters.mjs +512 -55
- package/src/snapshot/frozen.mjs +133 -3
- package/src/snapshot/public-map.mjs +7 -4
- package/src/snapshot/scan.mjs +2 -1
package/bin/release-skill.mjs
CHANGED
|
@@ -1,799 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
|
-
import { execFile as execFileCb } from 'node:child_process';
|
|
5
|
-
import { promisify } from 'node:util';
|
|
6
|
-
import { parseNodeMajor, meetsMinimum, computeReadinessStatus } from '../src/core/node-version.mjs';
|
|
7
|
-
|
|
8
|
-
const execFile = promisify(execFileCb);
|
|
9
|
-
|
|
10
|
-
const COMMANDS = new Set(['help', 'setup', 'assess', 'prepare', 'approve', 'publish', 'reconcile', 'verify', 'artifacts']);
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Check if a command is available and get its version.
|
|
14
|
-
*
|
|
15
|
-
* @param {string} command - The command to check.
|
|
16
|
-
* @param {string[]} versionArgs - Arguments to get version (e.g., ['--version']).
|
|
17
|
-
* @returns {Promise<{available: boolean, version: string|null, required: boolean, diagnostic: string}>}
|
|
18
|
-
*/
|
|
19
|
-
async function checkDependency(command, versionArgs = ['--version']) {
|
|
20
|
-
try {
|
|
21
|
-
const { stdout } = await execFile(command, versionArgs, {
|
|
22
|
-
shell: false,
|
|
23
|
-
encoding: 'utf8',
|
|
24
|
-
timeout: 5000,
|
|
25
|
-
});
|
|
26
|
-
const version = stdout.trim().split('\n')[0];
|
|
27
|
-
return {
|
|
28
|
-
available: true,
|
|
29
|
-
version,
|
|
30
|
-
required: command === 'node',
|
|
31
|
-
diagnostic: 'ok',
|
|
32
|
-
};
|
|
33
|
-
} catch (err) {
|
|
34
|
-
return {
|
|
35
|
-
available: false,
|
|
36
|
-
version: null,
|
|
37
|
-
required: command === 'node',
|
|
38
|
-
diagnostic: err.code === 'ENOENT' ? 'not found' : err.message,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Perform environment and dependency checks.
|
|
45
|
-
*
|
|
46
|
-
* @returns {Promise<object>} Environment check results.
|
|
47
|
-
*/
|
|
48
|
-
async function performEnvironmentChecks() {
|
|
49
|
-
const checks = {};
|
|
50
|
-
|
|
51
|
-
// Node.js
|
|
52
|
-
const nodeCheck = await checkDependency('node', ['--version']);
|
|
53
|
-
checks.node = {
|
|
54
|
-
...nodeCheck,
|
|
55
|
-
required: true,
|
|
56
|
-
minimumVersion: '22.0.0',
|
|
57
|
-
meetsMinimum: nodeCheck.available ? meetsMinimum(parseNodeMajor(nodeCheck.version), 22) : false,
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
// Git
|
|
61
|
-
const gitCheck = await checkDependency('git', ['--version']);
|
|
62
|
-
checks.git = {
|
|
63
|
-
...gitCheck,
|
|
64
|
-
required: true,
|
|
65
|
-
usage: '版本控制和 baseline 捕获',
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// pnpm
|
|
69
|
-
const pnpmCheck = await checkDependency('pnpm', ['--version']);
|
|
70
|
-
checks.pnpm = {
|
|
71
|
-
...pnpmCheck,
|
|
72
|
-
required: false,
|
|
73
|
-
usage: '包管理(推荐)',
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// npm
|
|
77
|
-
const npmCheck = await checkDependency('npm', ['--version']);
|
|
78
|
-
checks.npm = {
|
|
79
|
-
...npmCheck,
|
|
80
|
-
required: false,
|
|
81
|
-
usage: '包发布',
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// GitHub CLI
|
|
85
|
-
const ghCheck = await checkDependency('gh', ['--version']);
|
|
86
|
-
checks.gh = {
|
|
87
|
-
...ghCheck,
|
|
88
|
-
required: false,
|
|
89
|
-
usage: 'GitHub 操作',
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const claudeCheck = await checkDependency('claude', ['--version']);
|
|
93
|
-
checks.claude = {
|
|
94
|
-
...claudeCheck,
|
|
95
|
-
required: false,
|
|
96
|
-
usage: '仅当计划声明 claude-plugin distribution 时用于消费者安装验证',
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const codexCheck = await checkDependency('codex', ['--version']);
|
|
100
|
-
checks.codex = {
|
|
101
|
-
...codexCheck,
|
|
102
|
-
required: false,
|
|
103
|
-
usage: '仅当计划声明 codex-plugin distribution 时用于消费者安装验证',
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
return checks;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
2
|
/**
|
|
110
|
-
*
|
|
3
|
+
* release-skill CLI entry point.
|
|
111
4
|
*
|
|
112
|
-
*
|
|
5
|
+
* Loads the self-contained bundle (release-skill.bundle.mjs) which runs
|
|
6
|
+
* without node_modules. Fails closed with a clear error when the bundle
|
|
7
|
+
* is missing — never falls back to source CLI in installed state.
|
|
113
8
|
*/
|
|
114
|
-
function getCapabilityMaturity() {
|
|
115
|
-
return {
|
|
116
|
-
setup: {
|
|
117
|
-
available: true,
|
|
118
|
-
mode: 'read-only dry-run / confirmed create-once',
|
|
119
|
-
description: 'Discover first-use facts and create an absent config only after exact setupDigest confirmation',
|
|
120
|
-
},
|
|
121
|
-
assess: {
|
|
122
|
-
available: true,
|
|
123
|
-
mode: 'read-only',
|
|
124
|
-
description: 'Read-only assessment of project release readiness',
|
|
125
|
-
},
|
|
126
|
-
prepare: {
|
|
127
|
-
available: true,
|
|
128
|
-
mode: 'offline local writes',
|
|
129
|
-
description: 'Freeze a release plan with snapshots and gates',
|
|
130
|
-
},
|
|
131
|
-
publish: {
|
|
132
|
-
available: true,
|
|
133
|
-
mode: 'controlled production (protocol-tested; no OS/network sandbox)',
|
|
134
|
-
description: 'Publishes frozen GitHub/npm artifacts and runs configured Claude/Codex consumer checkpoints with approval and exact digest confirmation',
|
|
135
|
-
},
|
|
136
|
-
reconcile: {
|
|
137
|
-
available: true,
|
|
138
|
-
mode: 'evidence-based recovery (protocol-tested; no OS/network sandbox)',
|
|
139
|
-
description: 'Reconcile PARTIAL runs, retry safe missing checkpoints, and stop for human decisions on conflicts',
|
|
140
|
-
},
|
|
141
|
-
verify: {
|
|
142
|
-
available: true,
|
|
143
|
-
mode: 'fresh consumer verification (protocol-tested; no OS/network sandbox)',
|
|
144
|
-
description: 'Recheck remote state, exact npm installation, CLI help, and configured Claude/Codex installs before VERIFIED',
|
|
145
|
-
},
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function printHelp() {
|
|
150
|
-
console.log(`release-skill - Release governance Skill family
|
|
151
9
|
|
|
152
|
-
|
|
153
|
-
|
|
10
|
+
import { stat } from 'node:fs/promises';
|
|
11
|
+
import { dirname, join } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
154
13
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
setup Discover first-use configuration and gate candidates (dry-run by default)
|
|
158
|
-
assess Read-only assessment of project release readiness
|
|
159
|
-
prepare Freeze a release plan (release-skill output to .release-skill/; hooks may do remote ops)
|
|
160
|
-
approve Record local approval for a frozen release plan
|
|
161
|
-
publish Publish frozen GitHub/npm artifacts after approval and digest confirmation
|
|
162
|
-
reconcile Resume PARTIAL state from evidence; conflicts require a human
|
|
163
|
-
verify Fresh remote and consumer verification; only this reaches VERIFIED
|
|
164
|
-
artifacts Artifact status, inspect, update/apply, resolution, and diagnostics
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const bundlePath = join(__dirname, 'release-skill.bundle.mjs');
|
|
165
16
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
--confirm-production <digest> Confirm the exact production plan digest
|
|
173
|
-
--output <path> Override prepare/approve output path (non-production only)
|
|
174
|
-
--run-dir <path> Override prepare run directory; production requires one direct child of .release-skill/runs
|
|
175
|
-
--answers <path> Human-reviewed setup answers JSON
|
|
176
|
-
--write Create an absent project.yaml during setup; never overwrites
|
|
177
|
-
--confirm-setup <digest> Confirm exact setup facts and answers before create
|
|
178
|
-
--acknowledge-hook-side-effects Acknowledge unsandboxed legacy hook execution
|
|
179
|
-
--acknowledge-gate-side-effects Acknowledge unsandboxed local verification gate execution
|
|
180
|
-
--json Output results as JSON
|
|
181
|
-
--version Show version and exit
|
|
182
|
-
-h, --help Show this help message and exit
|
|
183
|
-
|
|
184
|
-
Safety:
|
|
185
|
-
Safe default: help -> setup (when config is absent) -> assess -> prepare --offline -> human review.
|
|
186
|
-
Production happy end: prepare --production -> approve -> publish -> verify.
|
|
187
|
-
prepare copies current public files into a local snapshot; it does not rewrite source files.
|
|
188
|
-
- Default mode is offline (release-skill pipeline does no remote writes)
|
|
189
|
-
- prepare output goes to .release-skill/ directory only
|
|
190
|
-
- User-configured hooks may write anywhere and perform remote operations
|
|
191
|
-
- To ensure zero remote writes, disable hooks or audit them separately
|
|
192
|
-
- publish requires explicit approval and an exact plan-digest confirmation
|
|
193
|
-
- publish consumes frozen Git/npm artifacts, never the live workspace
|
|
194
|
-
- existing remote objects and uncertain checks stop for human intervention
|
|
195
|
-
- production-equivalent protocol sandbox is verified; a real remote canary is not
|
|
196
|
-
|
|
197
|
-
First safe command:
|
|
198
|
-
release-skill help --json # Environment check (read-only)
|
|
199
|
-
release-skill setup --root <path> --json # First-use discovery (read-only)
|
|
200
|
-
release-skill assess --root <path> --offline --json # Project assessment`);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const args = process.argv.slice(2);
|
|
204
|
-
const hasJson = args.includes('--json');
|
|
205
|
-
const positional = args.filter(a => !a.startsWith('--'));
|
|
206
|
-
const command = positional[0];
|
|
207
|
-
|
|
208
|
-
if (!command && (args.includes('--version') || args.includes('-v'))) {
|
|
209
|
-
const { createRequire } = await import('node:module');
|
|
210
|
-
const require = createRequire(import.meta.url);
|
|
211
|
-
const pkg = require('../package.json');
|
|
212
|
-
console.log(pkg.version);
|
|
213
|
-
process.exit(0);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (!command || command === 'help') {
|
|
217
|
-
if (hasJson) {
|
|
218
|
-
// Perform environment checks for --json mode
|
|
219
|
-
const checks = await performEnvironmentChecks();
|
|
220
|
-
const capabilities = getCapabilityMaturity();
|
|
221
|
-
|
|
222
|
-
// Compute readiness: Node >=22 and Git are required; pnpm/npm/gh are optional
|
|
223
|
-
const readiness = computeReadinessStatus({
|
|
224
|
-
nodeAvailable: checks.node.available,
|
|
225
|
-
nodeMeetsMinimum: checks.node.meetsMinimum,
|
|
226
|
-
gitAvailable: checks.git.available,
|
|
227
|
-
});
|
|
228
|
-
const missingRequired = [];
|
|
229
|
-
if (!checks.node.available || !checks.node.meetsMinimum) missingRequired.push('node>=22');
|
|
230
|
-
if (!checks.git.available) missingRequired.push('git');
|
|
231
|
-
const productionMissing = [
|
|
232
|
-
...missingRequired,
|
|
233
|
-
...(!checks.npm.available ? ['npm'] : []),
|
|
234
|
-
...(!checks.gh.available ? ['gh'] : []),
|
|
235
|
-
];
|
|
236
|
-
|
|
237
|
-
const output = {
|
|
238
|
-
command: 'help',
|
|
239
|
-
mode: 'environment-check',
|
|
240
|
-
status: readiness.status,
|
|
241
|
-
missingRequired,
|
|
242
|
-
readiness: {
|
|
243
|
-
localPreparation: {
|
|
244
|
-
status: readiness.status,
|
|
245
|
-
missingRequired,
|
|
246
|
-
},
|
|
247
|
-
productionPublish: {
|
|
248
|
-
status: productionMissing.length > 0 ? 'NOT_READY' : 'AUTH_CHECK_REQUIRED',
|
|
249
|
-
missingRequired: productionMissing,
|
|
250
|
-
authentication: '运行生产发布前还需验证 gh auth、Git HTTPS credential 与 npm auth;help 不发起网络认证检查。',
|
|
251
|
-
conditionalConsumers: {
|
|
252
|
-
claude: '声明 claude-plugin distribution 时必须可用',
|
|
253
|
-
codex: '声明 codex-plugin distribution 时必须可用',
|
|
254
|
-
},
|
|
255
|
-
},
|
|
256
|
-
},
|
|
257
|
-
checks,
|
|
258
|
-
capabilities,
|
|
259
|
-
maturity: {
|
|
260
|
-
setup: 'read-only by default; create-once requires answers plus exact setupDigest confirmation',
|
|
261
|
-
assess: 'read-only (default); --output writes local report',
|
|
262
|
-
prepare: 'offline local writes; configured hooks/gates require their explicit side-effect acknowledgements',
|
|
263
|
-
onlinePrepare: 'previous-public-baseline observation available; production mode freezes publish artifacts and fails closed on drift or unknown state',
|
|
264
|
-
publish: 'GitHub/npm plus configured Claude/Codex consumer checkpoints are protocol-tested without an OS/network sandbox; approval and exact digest confirmation required',
|
|
265
|
-
reconcile: 'PARTIAL recovery is protocol-tested without an OS/network sandbox; remote conflicts require human intervention',
|
|
266
|
-
verify: 'fresh exact npm and Claude/Codex consumer installation checks are protocol-tested without an OS/network sandbox; configured consumer processes require explicit acknowledgement; success reaches VERIFIED',
|
|
267
|
-
},
|
|
268
|
-
recommendations: [],
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
// Add recommendations based on checks
|
|
272
|
-
if (!checks.node.available) {
|
|
273
|
-
output.recommendations.push('Install Node.js >= 22.0.0');
|
|
274
|
-
} else if (checks.node.available && !checks.node.meetsMinimum) {
|
|
275
|
-
output.recommendations.push('Upgrade Node.js to version 22 or later');
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (!checks.git.available) {
|
|
279
|
-
output.recommendations.push('Install Git for version control operations');
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (!checks.pnpm.available) {
|
|
283
|
-
output.recommendations.push('Install pnpm for package management (optional)');
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (!checks.npm.available) {
|
|
287
|
-
output.recommendations.push('Install npm for package publishing (optional)');
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (!checks.gh.available) {
|
|
291
|
-
output.recommendations.push('Install GitHub CLI for GitHub operations (optional)');
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
if (!checks.claude.available) {
|
|
295
|
-
output.recommendations.push('Install Claude CLI before releasing a configured claude-plugin distribution');
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
if (!checks.codex.available) {
|
|
299
|
-
output.recommendations.push('Install Codex CLI before releasing a configured codex-plugin distribution');
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
console.log(JSON.stringify(output, null, 2));
|
|
303
|
-
process.exit(readiness.status === 'READY' ? 0 : 1);
|
|
304
|
-
} else {
|
|
305
|
-
printHelp();
|
|
306
|
-
process.exit(0);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (!COMMANDS.has(command)) {
|
|
311
|
-
if (hasJson) {
|
|
312
|
-
const output = {
|
|
313
|
-
error: 'UNKNOWN_COMMAND',
|
|
314
|
-
message: `Unknown command: ${command}`,
|
|
315
|
-
exitCode: 2
|
|
316
|
-
};
|
|
317
|
-
console.log(JSON.stringify(output));
|
|
318
|
-
} else {
|
|
319
|
-
console.error(`Error: Unknown command '${command}'`);
|
|
320
|
-
console.error('Run "release-skill help" for available commands.');
|
|
321
|
-
}
|
|
322
|
-
process.exit(2);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// --- Setup command routing ---
|
|
326
|
-
if (command === 'setup') {
|
|
327
|
-
const { setupProject } = await import('../src/commands/setup.mjs');
|
|
328
|
-
const rootIdx = args.indexOf('--root');
|
|
329
|
-
const rawRoot = rootIdx !== -1 && args[rootIdx + 1] ? args[rootIdx + 1] : process.cwd();
|
|
330
|
-
const root = resolve(rawRoot);
|
|
331
|
-
const answersIdx = args.indexOf('--answers');
|
|
332
|
-
const answersPath = answersIdx !== -1 && args[answersIdx + 1] ? args[answersIdx + 1] : undefined;
|
|
333
|
-
const confirmationIdx = args.indexOf('--confirm-setup');
|
|
334
|
-
const confirmSetup = confirmationIdx !== -1 && args[confirmationIdx + 1]
|
|
335
|
-
? args[confirmationIdx + 1]
|
|
336
|
-
: undefined;
|
|
337
|
-
const write = args.includes('--write');
|
|
338
|
-
try {
|
|
339
|
-
const report = await setupProject({ root, answersPath, write, confirmSetup });
|
|
340
|
-
if (hasJson) {
|
|
341
|
-
console.log(JSON.stringify(report, null, 2));
|
|
342
|
-
} else {
|
|
343
|
-
console.log(`Setup status: ${report.status}`);
|
|
344
|
-
if (report.setupDigest) console.log(`Setup digest: ${report.setupDigest}`);
|
|
345
|
-
if (report.configPath) console.log(`Config: ${report.configPath}`);
|
|
346
|
-
if (report.next) console.log(`Next: ${report.next}`);
|
|
347
|
-
}
|
|
348
|
-
process.exit(['READY_TO_WRITE', 'CONFIG_CREATED', 'ALREADY_CONFIGURED'].includes(report.status) ? 0 : 2);
|
|
349
|
-
} catch (err) {
|
|
350
|
-
if (hasJson) {
|
|
351
|
-
console.log(JSON.stringify({
|
|
352
|
-
error: err.code ?? 'UNKNOWN_ERROR',
|
|
353
|
-
message: err.message,
|
|
354
|
-
details: err.details ?? {},
|
|
355
|
-
exitCode: err.exitCode ?? 1,
|
|
356
|
-
}));
|
|
357
|
-
} else {
|
|
358
|
-
console.error(`Error: ${err.message}`);
|
|
359
|
-
}
|
|
360
|
-
process.exit(err.exitCode ?? 1);
|
|
361
|
-
}
|
|
17
|
+
let bundleExists = false;
|
|
18
|
+
try {
|
|
19
|
+
const st = await stat(bundlePath);
|
|
20
|
+
bundleExists = st.isFile();
|
|
21
|
+
} catch {
|
|
22
|
+
// Bundle does not exist.
|
|
362
23
|
}
|
|
363
24
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const offline = args.includes('--offline') || !args.includes('--online');
|
|
372
|
-
const outputIdx = args.indexOf('--output');
|
|
373
|
-
const output = outputIdx !== -1 && args[outputIdx + 1] ? args[outputIdx + 1] : undefined;
|
|
374
|
-
|
|
375
|
-
try {
|
|
376
|
-
const report = await assessProject({ root, offline, output });
|
|
377
|
-
|
|
378
|
-
if (hasJson) {
|
|
379
|
-
console.log(JSON.stringify(report, null, 2));
|
|
380
|
-
} else {
|
|
381
|
-
console.log(report.summary);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
process.exit(report.status === 'ASSESSED' ? 0 : 1);
|
|
385
|
-
} catch (err) {
|
|
386
|
-
if (hasJson) {
|
|
387
|
-
const errOutput = {
|
|
388
|
-
error: err.code ?? 'UNKNOWN_ERROR',
|
|
389
|
-
message: err.message,
|
|
390
|
-
exitCode: err.exitCode ?? 1,
|
|
391
|
-
};
|
|
392
|
-
console.log(JSON.stringify(errOutput));
|
|
393
|
-
} else {
|
|
394
|
-
console.error(`Error: ${err.message}`);
|
|
395
|
-
}
|
|
396
|
-
process.exit(err.exitCode ?? 1);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// --- Prepare command routing ---
|
|
401
|
-
if (command === 'prepare') {
|
|
402
|
-
const { prepareRelease } = await import('../src/commands/prepare.mjs');
|
|
403
|
-
const { readFile: readFileFs } = await import('node:fs/promises');
|
|
404
|
-
|
|
405
|
-
const rootIdx = args.indexOf('--root');
|
|
406
|
-
const rawRoot = rootIdx !== -1 && args[rootIdx + 1] ? args[rootIdx + 1] : process.cwd();
|
|
407
|
-
const root = resolve(rawRoot);
|
|
408
|
-
const offline = args.includes('--offline') || !args.includes('--online');
|
|
409
|
-
|
|
410
|
-
// Resolve target version from --target-version or --version flag
|
|
411
|
-
let targetVersion;
|
|
412
|
-
for (const flag of ['--target-version', '--version']) {
|
|
413
|
-
const idx = args.indexOf(flag);
|
|
414
|
-
if (idx !== -1 && args[idx + 1]) {
|
|
415
|
-
targetVersion = args[idx + 1];
|
|
416
|
-
break;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
const hooksAuthorized = args.includes('--acknowledge-hook-side-effects');
|
|
421
|
-
const verificationGatesAuthorized = args.includes('--acknowledge-gate-side-effects');
|
|
422
|
-
const production = args.includes('--production');
|
|
423
|
-
const outputIdx = args.indexOf('--output');
|
|
424
|
-
const output = outputIdx !== -1 && args[outputIdx + 1] ? resolve(args[outputIdx + 1]) : undefined;
|
|
425
|
-
const runDirIdx = args.indexOf('--run-dir');
|
|
426
|
-
const runDir = runDirIdx !== -1 && args[runDirIdx + 1] ? resolve(args[runDirIdx + 1]) : undefined;
|
|
427
|
-
|
|
428
|
-
try {
|
|
429
|
-
const result = await prepareRelease({
|
|
430
|
-
root,
|
|
431
|
-
version: targetVersion,
|
|
432
|
-
offline,
|
|
433
|
-
hooksAuthorized,
|
|
434
|
-
verificationGatesAuthorized,
|
|
435
|
-
production,
|
|
436
|
-
output,
|
|
437
|
-
runDir,
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
if (hasJson) {
|
|
441
|
-
// Output the full plan object plus metadata so consumers
|
|
442
|
-
// can inspect status, units, externalActions, planDigest, etc.
|
|
443
|
-
const planContent = await readFileFs(result.planPath, 'utf8');
|
|
444
|
-
const plan = JSON.parse(planContent);
|
|
445
|
-
console.log(JSON.stringify({
|
|
446
|
-
...plan,
|
|
447
|
-
planPath: result.planPath,
|
|
448
|
-
planDigest: result.planDigest,
|
|
449
|
-
evidenceDir: result.evidenceDir,
|
|
450
|
-
}, null, 2));
|
|
451
|
-
} else {
|
|
452
|
-
console.log(`Plan frozen at: ${result.planPath}`);
|
|
453
|
-
console.log(`Plan digest: ${result.planDigest}`);
|
|
454
|
-
console.log(`Evidence: ${result.evidenceDir}`);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
process.exit(0);
|
|
458
|
-
} catch (err) {
|
|
459
|
-
if (hasJson) {
|
|
460
|
-
const errOutput = {
|
|
461
|
-
error: err.code ?? 'UNKNOWN_ERROR',
|
|
462
|
-
message: err.message,
|
|
463
|
-
exitCode: err.exitCode ?? 1,
|
|
464
|
-
};
|
|
465
|
-
console.log(JSON.stringify(errOutput));
|
|
466
|
-
} else {
|
|
467
|
-
console.error(`Error: ${err.message}`);
|
|
468
|
-
}
|
|
469
|
-
process.exit(err.exitCode ?? 1);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
// --- Approve command routing ---
|
|
474
|
-
if (command === 'approve') {
|
|
475
|
-
const { approvePlan } = await import('../src/commands/approve.mjs');
|
|
476
|
-
|
|
477
|
-
const planIdx = args.indexOf('--plan');
|
|
478
|
-
const planPath = planIdx !== -1 && args[planIdx + 1] ? args[planIdx + 1] : undefined;
|
|
479
|
-
const digestIdx = args.indexOf('--digest');
|
|
480
|
-
const expectedDigest = digestIdx !== -1 && args[digestIdx + 1] ? args[digestIdx + 1] : undefined;
|
|
481
|
-
const actorIdx = args.indexOf('--actor');
|
|
482
|
-
const actor = actorIdx !== -1 && args[actorIdx + 1] ? args[actorIdx + 1] : undefined;
|
|
483
|
-
const outputIdx = args.indexOf('--output');
|
|
484
|
-
const outputPath = outputIdx !== -1 && args[outputIdx + 1] ? resolve(args[outputIdx + 1]) : undefined;
|
|
485
|
-
|
|
486
|
-
if (!planPath || !expectedDigest || !actor) {
|
|
487
|
-
const msg = 'approve requires --plan <path>, --digest <sha256>, and --actor <name>';
|
|
488
|
-
if (hasJson) {
|
|
489
|
-
console.log(JSON.stringify({ error: 'MISSING_PARAMETERS', message: msg, exitCode: 1 }));
|
|
490
|
-
} else {
|
|
491
|
-
console.error(`Error: ${msg}`);
|
|
492
|
-
}
|
|
493
|
-
process.exit(1);
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
try {
|
|
497
|
-
const resolvedPlanPath = resolve(planPath);
|
|
498
|
-
const planDir = dirname(resolvedPlanPath);
|
|
499
|
-
const releaseDir = basename(planDir) === 'plans' && basename(resolvedPlanPath) === `${expectedDigest}.json`
|
|
500
|
-
? dirname(planDir)
|
|
501
|
-
: planDir;
|
|
502
|
-
const approvalPath = outputPath ?? join(releaseDir, 'approval-record.json');
|
|
503
|
-
const record = await approvePlan({ planPath, expectedDigest, actor, outputPath: approvalPath });
|
|
504
|
-
|
|
505
|
-
if (hasJson) {
|
|
506
|
-
console.log(JSON.stringify(record, null, 2));
|
|
507
|
-
} else {
|
|
508
|
-
console.log(`Plan approved by ${record.actor}`);
|
|
509
|
-
console.log(`Approval record: ${record.approvalPath}`);
|
|
510
|
-
console.log(`Expires at: ${record.expiresAt}`);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
process.exit(0);
|
|
514
|
-
} catch (err) {
|
|
515
|
-
if (hasJson) {
|
|
516
|
-
const errOutput = {
|
|
517
|
-
error: err.code ?? 'UNKNOWN_ERROR',
|
|
518
|
-
message: err.message,
|
|
519
|
-
exitCode: err.exitCode ?? 1,
|
|
520
|
-
};
|
|
521
|
-
console.log(JSON.stringify(errOutput));
|
|
522
|
-
} else {
|
|
523
|
-
console.error(`Error: ${err.message}`);
|
|
524
|
-
}
|
|
525
|
-
process.exit(err.exitCode ?? 1);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// --- Reconcile command routing ---
|
|
530
|
-
if (command === 'reconcile') {
|
|
531
|
-
const { reconcileRelease } = await import('../src/commands/reconcile.mjs');
|
|
532
|
-
const { createGitGithubAdapter } = await import('../src/adapters/git-github.mjs');
|
|
533
|
-
const { createNpmAdapter } = await import('../src/adapters/npm.mjs');
|
|
534
|
-
const { createPluginMarketplaceAdapter } = await import('../src/adapters/plugin-marketplace.mjs');
|
|
535
|
-
const { createPushSnapshotAdapter } = await import('../src/adapters/push-snapshot.mjs');
|
|
536
|
-
const { createAdapterRegistry } = await import('../src/adapters/contract.mjs');
|
|
537
|
-
|
|
538
|
-
const rootIdx = args.indexOf('--root');
|
|
539
|
-
const rawRoot = rootIdx !== -1 && args[rootIdx + 1] ? args[rootIdx + 1] : process.cwd();
|
|
540
|
-
const root = resolve(rawRoot);
|
|
541
|
-
const planIdx = args.indexOf('--plan');
|
|
542
|
-
const planPath = planIdx !== -1 && args[planIdx + 1] ? resolve(args[planIdx + 1]) : undefined;
|
|
543
|
-
const runIdx = args.indexOf('--run');
|
|
544
|
-
const runPath = runIdx !== -1 && args[runIdx + 1] ? resolve(args[runIdx + 1]) : undefined;
|
|
545
|
-
const approvalIdx = args.indexOf('--approval');
|
|
546
|
-
const approvalPath = approvalIdx !== -1 && args[approvalIdx + 1] ? resolve(args[approvalIdx + 1]) : undefined;
|
|
547
|
-
const confirmationIdx = args.indexOf('--confirm-production');
|
|
548
|
-
const productionConfirmation = confirmationIdx !== -1 && args[confirmationIdx + 1]
|
|
549
|
-
? args[confirmationIdx + 1]
|
|
550
|
-
: undefined;
|
|
551
|
-
|
|
552
|
-
if (!planPath || !runPath) {
|
|
553
|
-
const msg = 'reconcile requires --plan <path> and --run <path>';
|
|
554
|
-
if (hasJson) {
|
|
555
|
-
console.log(JSON.stringify({ error: 'MISSING_PARAMETERS', message: msg, exitCode: 1 }));
|
|
556
|
-
} else {
|
|
557
|
-
console.error(`Error: ${msg}`);
|
|
558
|
-
}
|
|
559
|
-
process.exit(1);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
try {
|
|
563
|
-
const registry = createAdapterRegistry([
|
|
564
|
-
createGitGithubAdapter(),
|
|
565
|
-
createNpmAdapter(),
|
|
566
|
-
createPluginMarketplaceAdapter(),
|
|
567
|
-
createPushSnapshotAdapter(),
|
|
568
|
-
]);
|
|
569
|
-
|
|
570
|
-
const result = await reconcileRelease({
|
|
571
|
-
planPath,
|
|
572
|
-
sourceRunPath: runPath,
|
|
573
|
-
approvalPath,
|
|
574
|
-
adapterRegistry: registry,
|
|
575
|
-
root,
|
|
576
|
-
productionConfirmation,
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
if (hasJson) {
|
|
580
|
-
console.log(JSON.stringify(result, null, 2));
|
|
581
|
-
} else {
|
|
582
|
-
console.log(`Reconcile status: ${result.status}`);
|
|
583
|
-
for (const cp of result.checkpoints) {
|
|
584
|
-
console.log(` ${cp.actionId}: ${cp.status}`);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
process.exit(result.status === 'PUBLISHED' ? 0 : 1);
|
|
589
|
-
} catch (err) {
|
|
590
|
-
if (hasJson) {
|
|
591
|
-
const errOutput = {
|
|
592
|
-
error: err.code ?? 'UNKNOWN_ERROR',
|
|
593
|
-
message: err.message,
|
|
594
|
-
exitCode: err.exitCode ?? 1,
|
|
595
|
-
};
|
|
596
|
-
console.log(JSON.stringify(errOutput));
|
|
597
|
-
} else {
|
|
598
|
-
console.error(`Error: ${err.message}`);
|
|
599
|
-
}
|
|
600
|
-
process.exit(err.exitCode ?? 1);
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
// --- Verify command routing ---
|
|
605
|
-
if (command === 'verify') {
|
|
606
|
-
const { verifyRelease } = await import('../src/commands/verify.mjs');
|
|
607
|
-
const { createGitGithubAdapter } = await import('../src/adapters/git-github.mjs');
|
|
608
|
-
const { createNpmAdapter } = await import('../src/adapters/npm.mjs');
|
|
609
|
-
const { createPluginMarketplaceAdapter } = await import('../src/adapters/plugin-marketplace.mjs');
|
|
610
|
-
const { createPushSnapshotAdapter } = await import('../src/adapters/push-snapshot.mjs');
|
|
611
|
-
const { createAdapterRegistry } = await import('../src/adapters/contract.mjs');
|
|
612
|
-
|
|
613
|
-
const rootIdx = args.indexOf('--root');
|
|
614
|
-
const rawRoot = rootIdx !== -1 && args[rootIdx + 1] ? args[rootIdx + 1] : process.cwd();
|
|
615
|
-
const root = resolve(rawRoot);
|
|
616
|
-
const planIdx = args.indexOf('--plan');
|
|
617
|
-
const planPath = planIdx !== -1 && args[planIdx + 1] ? resolve(args[planIdx + 1]) : undefined;
|
|
618
|
-
const runIdx = args.indexOf('--run');
|
|
619
|
-
const runPath = runIdx !== -1 && args[runIdx + 1] ? resolve(args[runIdx + 1]) : undefined;
|
|
620
|
-
const verificationGatesAuthorized = args.includes('--acknowledge-gate-side-effects');
|
|
621
|
-
|
|
622
|
-
if (!planPath || !runPath) {
|
|
623
|
-
const msg = 'verify requires --plan <path> and --run <path>';
|
|
624
|
-
if (hasJson) {
|
|
625
|
-
console.log(JSON.stringify({ error: 'MISSING_PARAMETERS', message: msg, exitCode: 1 }));
|
|
626
|
-
} else {
|
|
627
|
-
console.error(`Error: ${msg}`);
|
|
628
|
-
}
|
|
629
|
-
process.exit(1);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
try {
|
|
633
|
-
const registry = createAdapterRegistry([
|
|
634
|
-
createGitGithubAdapter(),
|
|
635
|
-
createNpmAdapter(),
|
|
636
|
-
createPluginMarketplaceAdapter(),
|
|
637
|
-
createPushSnapshotAdapter(),
|
|
638
|
-
]);
|
|
639
|
-
|
|
640
|
-
const result = await verifyRelease({
|
|
641
|
-
planPath,
|
|
642
|
-
sourceRunPath: runPath,
|
|
643
|
-
adapterRegistry: registry,
|
|
644
|
-
root,
|
|
645
|
-
verificationGatesAuthorized,
|
|
646
|
-
});
|
|
647
|
-
|
|
648
|
-
if (hasJson) {
|
|
649
|
-
console.log(JSON.stringify(result, null, 2));
|
|
650
|
-
} else {
|
|
651
|
-
console.log(`Verify status: ${result.status}`);
|
|
652
|
-
console.log(`Adapter checks: ${result.adapterChecks.length} passed`);
|
|
653
|
-
console.log(`Smoke test: ${result.smokeTest.passed ? 'PASSED' : 'FAILED'}`);
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
process.exit(result.status === 'VERIFIED' ? 0 : 1);
|
|
657
|
-
} catch (err) {
|
|
658
|
-
if (hasJson) {
|
|
659
|
-
const errOutput = {
|
|
660
|
-
error: err.code ?? 'UNKNOWN_ERROR',
|
|
661
|
-
message: err.message,
|
|
662
|
-
exitCode: err.exitCode ?? 1,
|
|
663
|
-
};
|
|
664
|
-
console.log(JSON.stringify(errOutput));
|
|
665
|
-
} else {
|
|
666
|
-
console.error(`Error: ${err.message}`);
|
|
667
|
-
}
|
|
668
|
-
process.exit(err.exitCode ?? 1);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// --- Publish command routing ---
|
|
673
|
-
if (command === 'publish') {
|
|
674
|
-
const { publishRelease } = await import('../src/commands/publish.mjs');
|
|
675
|
-
const { createGitGithubAdapter } = await import('../src/adapters/git-github.mjs');
|
|
676
|
-
const { createNpmAdapter } = await import('../src/adapters/npm.mjs');
|
|
677
|
-
const { createPluginMarketplaceAdapter } = await import('../src/adapters/plugin-marketplace.mjs');
|
|
678
|
-
const { createPushSnapshotAdapter } = await import('../src/adapters/push-snapshot.mjs');
|
|
679
|
-
const { createAdapterRegistry } = await import('../src/adapters/contract.mjs');
|
|
680
|
-
|
|
681
|
-
const rootIdx = args.indexOf('--root');
|
|
682
|
-
const rawRoot = rootIdx !== -1 && args[rootIdx + 1] ? args[rootIdx + 1] : process.cwd();
|
|
683
|
-
const root = resolve(rawRoot);
|
|
684
|
-
const planIdx = args.indexOf('--plan');
|
|
685
|
-
const planPath = planIdx !== -1 && args[planIdx + 1] ? resolve(args[planIdx + 1]) : undefined;
|
|
686
|
-
const approvalIdx = args.indexOf('--approval');
|
|
687
|
-
const approvalPath = approvalIdx !== -1 && args[approvalIdx + 1] ? resolve(args[approvalIdx + 1]) : undefined;
|
|
688
|
-
const confirmationIdx = args.indexOf('--confirm-production');
|
|
689
|
-
const productionConfirmation = confirmationIdx !== -1 && args[confirmationIdx + 1]
|
|
690
|
-
? args[confirmationIdx + 1]
|
|
691
|
-
: undefined;
|
|
692
|
-
|
|
693
|
-
if (!planPath || !approvalPath || !productionConfirmation) {
|
|
694
|
-
const msg = 'publish requires --plan <path>, --approval <path>, and --confirm-production <plan-digest>';
|
|
695
|
-
if (hasJson) {
|
|
696
|
-
console.log(JSON.stringify({ error: 'MISSING_PARAMETERS', message: msg, exitCode: 1 }));
|
|
697
|
-
} else {
|
|
698
|
-
console.error(`Error: ${msg}`);
|
|
699
|
-
}
|
|
700
|
-
process.exit(1);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
try {
|
|
704
|
-
const registry = createAdapterRegistry([
|
|
705
|
-
createGitGithubAdapter(),
|
|
706
|
-
createNpmAdapter(),
|
|
707
|
-
createPluginMarketplaceAdapter(),
|
|
708
|
-
createPushSnapshotAdapter(),
|
|
709
|
-
]);
|
|
710
|
-
|
|
711
|
-
const result = await publishRelease({
|
|
712
|
-
planPath,
|
|
713
|
-
approvalPath,
|
|
714
|
-
adapterRegistry: registry,
|
|
715
|
-
root,
|
|
716
|
-
productionMode: true,
|
|
717
|
-
productionConfirmation,
|
|
718
|
-
});
|
|
719
|
-
|
|
720
|
-
if (hasJson) {
|
|
721
|
-
console.log(JSON.stringify(result, null, 2));
|
|
722
|
-
} else {
|
|
723
|
-
console.log(`Publish status: ${result.status}`);
|
|
724
|
-
for (const cp of result.checkpoints) {
|
|
725
|
-
console.log(` ${cp.actionId}: ${cp.status}`);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
process.exit(result.status === 'PUBLISHED' ? 0 : 1);
|
|
730
|
-
} catch (err) {
|
|
731
|
-
if (hasJson) {
|
|
732
|
-
const errOutput = {
|
|
733
|
-
error: err.code ?? 'UNKNOWN_ERROR',
|
|
734
|
-
message: err.message,
|
|
735
|
-
exitCode: err.exitCode ?? 1,
|
|
736
|
-
};
|
|
737
|
-
console.log(JSON.stringify(errOutput));
|
|
738
|
-
} else {
|
|
739
|
-
console.error(`Error: ${err.message}`);
|
|
740
|
-
}
|
|
741
|
-
process.exit(err.exitCode ?? 1);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
// --- Artifacts command routing ---
|
|
746
|
-
if (command === 'artifacts') {
|
|
747
|
-
const { runArtifactsCommand } = await import('../src/commands/artifacts.mjs');
|
|
748
|
-
|
|
749
|
-
const rootIdx = args.indexOf('--root');
|
|
750
|
-
const rawRoot = rootIdx !== -1 && args[rootIdx + 1] ? args[rootIdx + 1] : process.cwd();
|
|
751
|
-
const root = resolve(rawRoot);
|
|
752
|
-
const outputIdx = args.indexOf('--output');
|
|
753
|
-
const output = outputIdx !== -1 && args[outputIdx + 1] ? resolve(args[outputIdx + 1]) : undefined;
|
|
754
|
-
|
|
755
|
-
const subcommand = positional[1] ?? 'status';
|
|
756
|
-
|
|
757
|
-
try {
|
|
758
|
-
const result = await runArtifactsCommand({ subcommand, args, root });
|
|
759
|
-
|
|
760
|
-
if (hasJson) {
|
|
761
|
-
console.log(JSON.stringify(result, null, 2));
|
|
762
|
-
} else {
|
|
763
|
-
console.log(`Status: ${result.status}`);
|
|
764
|
-
console.log(`Safe to write: ${result.safeToWrite}`);
|
|
765
|
-
console.log(`Target unchanged: ${result.targetUnchanged}`);
|
|
766
|
-
if (result.nextAction) {
|
|
767
|
-
console.log(`Next action: ${result.nextAction.command}`);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
// Exit code: 0 if clean/safe/drift-detected (dry-run), 1 if blocking
|
|
772
|
-
const blockingStatuses = new Set([
|
|
773
|
-
'BASE_UNAVAILABLE', 'POLICY_INVALID', 'PATH_UNSAFE',
|
|
774
|
-
'CONFLICT', 'DIRTY_SCOPE_CONFLICT',
|
|
775
|
-
]);
|
|
776
|
-
process.exit(blockingStatuses.has(result.status) ? 1 : 0);
|
|
777
|
-
} catch (err) {
|
|
778
|
-
if (hasJson) {
|
|
779
|
-
const errOutput = {
|
|
780
|
-
error: err.code ?? 'UNKNOWN_ERROR',
|
|
781
|
-
message: err.message,
|
|
782
|
-
status: err.code ?? 'UNKNOWN_ERROR',
|
|
783
|
-
safeToWrite: false,
|
|
784
|
-
targetUnchanged: true,
|
|
785
|
-
evidenceDir: null,
|
|
786
|
-
nextAction: { command: 'artifacts inspect --root <path>' },
|
|
787
|
-
exitCode: err.exitCode ?? 1,
|
|
788
|
-
};
|
|
789
|
-
console.log(JSON.stringify(errOutput));
|
|
790
|
-
} else {
|
|
791
|
-
console.error(`Error: ${err.message}`);
|
|
792
|
-
}
|
|
793
|
-
process.exit(err.exitCode ?? 1);
|
|
794
|
-
}
|
|
25
|
+
if (!bundleExists) {
|
|
26
|
+
console.error(
|
|
27
|
+
`Error: release-skill bundle not found at:\n ${bundlePath}\n` +
|
|
28
|
+
`The bundle is required for installed-plugin execution.\n` +
|
|
29
|
+
`Reinstall the plugin or run 'node scripts/build-bundle.mjs' in the source checkout.`,
|
|
30
|
+
);
|
|
31
|
+
process.exit(1);
|
|
795
32
|
}
|
|
796
33
|
|
|
797
|
-
|
|
798
|
-
console.error(`Command '${command}' is not yet implemented.`);
|
|
799
|
-
process.exit(1);
|
|
34
|
+
await import(bundlePath);
|