ultra-builder-pro-cli 0.26.2 → 5.0.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/.ultra-template/contexts/TEMPLATE.md +70 -29
- package/.ultra-template/north-star.md +35 -18
- package/.ultra-template/tasks.json +1 -0
- package/.ultra-template/test-report.json +17 -1
- package/CHANGELOG.md +235 -1
- package/README.md +210 -50
- package/README.zh-CN.md +440 -0
- package/adapters/_shared/host-profile.cjs +3 -95
- package/adapters/_shared/plugin-core.cjs +61 -5
- package/adapters/_shared/runtime-assets.cjs +66 -5
- package/adapters/zcode.js +375 -0
- package/bin/delegate-worker.cjs +2 -325
- package/bin/delegate.cjs +3 -461
- package/bin/install.js +11 -6
- package/docs/ARCHITECTURE.md +133 -31
- package/docs/ARTIFACT-AUTHORITY.md +185 -24
- package/docs/DECISIONS.md +153 -18
- package/docs/PHILOSOPHY.md +58 -7
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +34 -12
- package/docs/RUNTIME-COMPAT-MATRIX.md +195 -23
- package/docs/SKILL-AUTHORING.md +19 -6
- package/docs/WORKFLOW-LIFECYCLE.md +196 -44
- package/docs/evals/adversarial-review-2026-08-14.md +84 -0
- package/docs/evals/ultra-5-0-journey-results.md +62 -0
- package/docs/evals/ultra-5-0-live-acp-conformance.md +93 -0
- package/docs/evals/ultra-5-0-route-journeys.md +145 -0
- package/docs/evals/zcode-automation-2026-08-14.md +103 -0
- package/hooks/README.md +27 -4
- package/hooks/_common.py +1678 -61
- package/hooks/adapters/zcode.py +227 -0
- package/hooks/compact_context.py +176 -17
- package/hooks/mid_workflow_recall.py +27 -5
- package/hooks/post_edit_guard.py +133 -23
- package/hooks/session_context.py +707 -22
- package/package.json +7 -3
- package/skills/ultra-change/SKILL.md +69 -12
- package/skills/ultra-change/references/change-contract.md +93 -27
- package/skills/ultra-change/references/execution-grant.md +109 -0
- package/skills/ultra-change/references/primary-transfer.md +273 -0
- package/skills/ultra-change/scripts/validate_primary_transfer.cjs +1928 -0
- package/skills/ultra-delegate/SKILL.md +103 -8
- package/skills/ultra-delegate/patches/zcode-acp-0.1.0-zcode-0.16.5.patch +184 -0
- package/skills/ultra-delegate/references/delegation-contract.md +80 -1
- package/skills/ultra-delegate/scripts/acp-conformance.cjs +287 -0
- package/skills/ultra-delegate/scripts/acp-driver.cjs +762 -0
- package/skills/ultra-delegate/scripts/agent-execution-driver.cjs +320 -0
- package/skills/ultra-delegate/scripts/cli-driver.cjs +61 -0
- package/skills/ultra-delegate/scripts/delegate-worker.cjs +328 -0
- package/skills/ultra-delegate/scripts/delegate.cjs +690 -0
- package/skills/ultra-delegate/scripts/host-profile.cjs +267 -0
- package/skills/ultra-deliver/SKILL.md +98 -11
- package/skills/ultra-dev/SKILL.md +188 -42
- package/skills/ultra-grilling/SKILL.md +10 -7
- package/skills/ultra-grilling/references/reframing.md +7 -3
- package/skills/ultra-init/SKILL.md +56 -7
- package/skills/ultra-init/scripts/init_project.cjs +968 -21
- package/skills/ultra-plan/SKILL.md +91 -47
- package/skills/ultra-plan/references/task-evidence-v2.md +300 -0
- package/skills/ultra-plan/scripts/validate_task_evidence.cjs +859 -0
- package/skills/ultra-research/SKILL.md +70 -14
- package/skills/ultra-research/references/04-product-strategy.md +11 -2
- package/skills/ultra-research/references/21-features-scope.md +8 -0
- package/skills/ultra-research/references/22-success-metrics.md +4 -3
- package/skills/ultra-research/references/99-synthesis.md +19 -2
- package/skills/ultra-research/references/north-star-v2.md +168 -0
- package/skills/ultra-research/references/wayfinding.md +8 -0
- package/skills/ultra-research/scripts/validate_north_star.cjs +1174 -0
- package/skills/ultra-review/SKILL.md +201 -27
- package/skills/ultra-review/references/adversarial-evaluation.md +31 -0
- package/skills/ultra-review/references/spec.md +5 -0
- package/skills/ultra-review/references/unified-schema.md +146 -11
- package/skills/ultra-review/references/worker-packet.md +151 -8
- package/skills/ultra-review/scripts/review_wait.py +2653 -63
- package/skills/ultra-status/SKILL.md +129 -15
- package/skills/ultra-status/references/route-contract.md +100 -0
- package/skills/ultra-tdd/SKILL.md +11 -4
- package/skills/ultra-tdd/references/test-execution.md +6 -1
- package/skills/ultra-test/SKILL.md +161 -18
- package/skills/ultra-test/scripts/validate_review_transport.cjs +549 -0
- package/skills/ultra-test/scripts/worktree_digest.cjs +518 -30
- /package/{adapters/_shared → skills/ultra-delegate/scripts}/delegate-profiles/kimi-read-only.md +0 -0
- /package/{adapters/_shared → skills/ultra-delegate/scripts}/delegate-profiles/kimi-write.md +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Canonical
|
|
4
|
+
* Canonical package and invocation boundary.
|
|
5
5
|
*
|
|
6
6
|
* Skills carry semantic workflow. Hooks are optional acceleration and safety.
|
|
7
7
|
* There is no prompt projection, worker registry, MCP kernel, or runtime daemon.
|
|
@@ -27,7 +27,55 @@ const MODEL_INVOKED_SKILLS = Object.freeze([
|
|
|
27
27
|
]);
|
|
28
28
|
|
|
29
29
|
const ROUTER_SKILLS = Object.freeze(['ultra-status']);
|
|
30
|
-
const
|
|
30
|
+
const PUBLIC_SKILL_INVOCATION_MODES = Object.freeze({
|
|
31
|
+
'ultra-init': Object.freeze({
|
|
32
|
+
create: 'explicit-entry',
|
|
33
|
+
recover: 'explicit-entry',
|
|
34
|
+
}),
|
|
35
|
+
'ultra-research': Object.freeze({
|
|
36
|
+
baseline: 'explicit-entry',
|
|
37
|
+
'change-scoped': 'grant-continuable',
|
|
38
|
+
}),
|
|
39
|
+
'ultra-change': Object.freeze({
|
|
40
|
+
intake: 'explicit-entry',
|
|
41
|
+
'evidence-needed': 'explicit-entry',
|
|
42
|
+
reconcile: 'grant-continuable',
|
|
43
|
+
accepted: 'explicit-entry',
|
|
44
|
+
}),
|
|
45
|
+
'ultra-plan': Object.freeze({
|
|
46
|
+
decompose: 'grant-continuable',
|
|
47
|
+
recover: 'grant-continuable',
|
|
48
|
+
}),
|
|
49
|
+
'ultra-dev': Object.freeze({
|
|
50
|
+
execute: 'grant-continuable',
|
|
51
|
+
resume: 'grant-continuable',
|
|
52
|
+
reopen: 'grant-continuable',
|
|
53
|
+
}),
|
|
54
|
+
'ultra-test': Object.freeze({
|
|
55
|
+
audit: 'grant-continuable',
|
|
56
|
+
reaudit: 'grant-continuable',
|
|
57
|
+
}),
|
|
58
|
+
'ultra-deliver': Object.freeze({
|
|
59
|
+
reconcile: 'grant-continuable',
|
|
60
|
+
finalize: 'explicit-entry',
|
|
61
|
+
}),
|
|
62
|
+
'ultra-delegate': Object.freeze({
|
|
63
|
+
task: 'explicit-entry',
|
|
64
|
+
research: 'explicit-entry',
|
|
65
|
+
aggregate: 'explicit-entry',
|
|
66
|
+
}),
|
|
67
|
+
'ultra-status': Object.freeze({
|
|
68
|
+
route: 'router',
|
|
69
|
+
history: 'router',
|
|
70
|
+
installation: 'router',
|
|
71
|
+
delegation: 'router',
|
|
72
|
+
recovery: 'router',
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
|
+
const GRANT_CONTINUABLE_SKILLS = Object.freeze(USER_INVOKED_SKILLS.filter((name) => (
|
|
76
|
+
Object.values(PUBLIC_SKILL_INVOCATION_MODES[name]).includes('grant-continuable')
|
|
77
|
+
)));
|
|
78
|
+
const SUPPORTED_RUNTIMES = Object.freeze(['claude', 'opencode', 'codex', 'kimi', 'grok', 'zcode']);
|
|
31
79
|
|
|
32
80
|
const WORKFLOW_HOOK_FILES = Object.freeze([
|
|
33
81
|
'session_context.py',
|
|
@@ -52,16 +100,29 @@ function skillPolicy(name) {
|
|
|
52
100
|
if (!skillsForRuntime(SUPPORTED_RUNTIMES[0]).includes(name)) {
|
|
53
101
|
throw new Error(`unknown packaged Ultra skill: ${name}`);
|
|
54
102
|
}
|
|
55
|
-
|
|
103
|
+
if (MODEL_INVOKED_SKILLS.includes(name)) {
|
|
104
|
+
return {
|
|
105
|
+
userInvocable: false,
|
|
106
|
+
allowImplicitInvocation: true,
|
|
107
|
+
invocationClasses: ['model-discipline'],
|
|
108
|
+
modes: {},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const modes = PUBLIC_SKILL_INVOCATION_MODES[name];
|
|
112
|
+
const invocationClasses = [...new Set(Object.values(modes))];
|
|
56
113
|
return {
|
|
57
|
-
userInvocable:
|
|
58
|
-
allowImplicitInvocation:
|
|
114
|
+
userInvocable: true,
|
|
115
|
+
allowImplicitInvocation: invocationClasses.includes('grant-continuable'),
|
|
116
|
+
invocationClasses,
|
|
117
|
+
modes,
|
|
59
118
|
};
|
|
60
119
|
}
|
|
61
120
|
|
|
62
121
|
module.exports = {
|
|
122
|
+
GRANT_CONTINUABLE_SKILLS,
|
|
63
123
|
USER_INVOKED_SKILLS,
|
|
64
124
|
MODEL_INVOKED_SKILLS,
|
|
125
|
+
PUBLIC_SKILL_INVOCATION_MODES,
|
|
65
126
|
ROUTER_SKILLS,
|
|
66
127
|
SUPPORTED_RUNTIMES,
|
|
67
128
|
WORKFLOW_HOOK_FILES,
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const os = require('node:os');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { spawnSync } = require('node:child_process');
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
copyHooks,
|
|
10
|
+
copySkills,
|
|
11
|
+
inspectPlugin,
|
|
12
|
+
markManaged,
|
|
13
|
+
publishManagedTrees,
|
|
14
|
+
removeManaged,
|
|
15
|
+
writePluginProvenance,
|
|
16
|
+
} = require('./_shared/plugin-core.cjs');
|
|
17
|
+
const {
|
|
18
|
+
captureAbsent,
|
|
19
|
+
ensureDir,
|
|
20
|
+
isManaged,
|
|
21
|
+
managedMetadata,
|
|
22
|
+
pruneCreatedEmpty,
|
|
23
|
+
removeTree,
|
|
24
|
+
writeAtomic,
|
|
25
|
+
} = require('./_shared/file-ops.cjs');
|
|
26
|
+
const { zcodeBinary } = require('./_shared/host-profile.cjs');
|
|
27
|
+
|
|
28
|
+
const PLUGIN_NAME = 'ultra-builder-pro';
|
|
29
|
+
const INLINE_PLUGIN_ID = `${PLUGIN_NAME}@inline`;
|
|
30
|
+
|
|
31
|
+
function resolveTarget(ctx = {}) {
|
|
32
|
+
if (ctx.configDir) return path.resolve(ctx.configDir);
|
|
33
|
+
const configured = ctx.zcodeHome || ctx.env?.ZCODE_HOME || process.env.ZCODE_HOME;
|
|
34
|
+
return configured ? path.resolve(configured) : path.join(ctx.homeDir || os.homedir(), '.zcode');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveCliRoot(ctx = {}) {
|
|
38
|
+
return path.join(resolveTarget(ctx), 'cli');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function resolveConfigFile(ctx = {}) {
|
|
42
|
+
return path.join(resolveCliRoot(ctx), 'config.json');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolveMarketplaceRoot(ctx = {}) {
|
|
46
|
+
return path.join(resolveCliRoot(ctx), 'plugins', 'marketplaces', PLUGIN_NAME);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resolveMarketplaceFile(ctx = {}) {
|
|
50
|
+
return path.join(resolveMarketplaceRoot(ctx), 'marketplace.json');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolvePluginRoot(ctx = {}) {
|
|
54
|
+
return path.join(resolveMarketplaceRoot(ctx), 'plugin');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolveRepoRoot(ctx = {}) {
|
|
58
|
+
return ctx.repoRoot || path.resolve(__dirname, '..');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function assertPluginScope(ctx = {}) {
|
|
62
|
+
if (ctx.scope === 'local' && !ctx.configDir) {
|
|
63
|
+
throw new Error('ZCode plugins are user-scoped; use global scope or an isolated configDir');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function hookProcess(name) {
|
|
68
|
+
return {
|
|
69
|
+
type: 'process',
|
|
70
|
+
command: 'python3',
|
|
71
|
+
args: [`\${ZCODE_PLUGIN_ROOT}/hooks/adapters/zcode.py`, name],
|
|
72
|
+
timeoutMs: 10000,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function buildHooksManifest() {
|
|
77
|
+
return {
|
|
78
|
+
hooks: {
|
|
79
|
+
SessionStart: [
|
|
80
|
+
{ matcher: 'startup|resume|clear', hooks: [hookProcess('session_context.py')] },
|
|
81
|
+
{ matcher: 'compact', hooks: [hookProcess('compact_context.py')] },
|
|
82
|
+
],
|
|
83
|
+
PreToolUse: [
|
|
84
|
+
{ matcher: 'Write|Edit|ApplyPatch|Grep', hooks: [hookProcess('mid_workflow_recall.py')] },
|
|
85
|
+
{ matcher: 'Bash', hooks: [hookProcess('block_dangerous_commands.py')] },
|
|
86
|
+
],
|
|
87
|
+
PostToolUse: [
|
|
88
|
+
{ matcher: 'Write|Edit|ApplyPatch', hooks: [hookProcess('post_edit_guard.py')] },
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function loadConfig(file) {
|
|
95
|
+
if (!fs.existsSync(file)) return {};
|
|
96
|
+
const value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
97
|
+
if (!value || Array.isArray(value) || typeof value !== 'object') {
|
|
98
|
+
throw new Error(`invalid ZCode config: ${file}`);
|
|
99
|
+
}
|
|
100
|
+
if (value.plugins !== undefined && (
|
|
101
|
+
!value.plugins || Array.isArray(value.plugins) || typeof value.plugins !== 'object'
|
|
102
|
+
)) throw new Error(`invalid ZCode plugins config: ${file}`);
|
|
103
|
+
if (value.plugins?.dirs !== undefined && (
|
|
104
|
+
!Array.isArray(value.plugins.dirs)
|
|
105
|
+
|| !value.plugins.dirs.every((entry) => typeof entry === 'string' && entry)
|
|
106
|
+
)) throw new Error(`invalid ZCode plugins.dirs config: ${file}`);
|
|
107
|
+
if (value.plugins?.enabled !== undefined && typeof value.plugins.enabled !== 'boolean') {
|
|
108
|
+
throw new Error(`invalid ZCode plugins.enabled config: ${file}`);
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function configOwnership(file, pluginRoot) {
|
|
114
|
+
const exists = fs.existsSync(file);
|
|
115
|
+
const config = loadConfig(file);
|
|
116
|
+
const plugins = config.plugins;
|
|
117
|
+
return {
|
|
118
|
+
file_created: !exists,
|
|
119
|
+
plugins_created: plugins === undefined,
|
|
120
|
+
enabled_created: plugins === undefined || !Object.hasOwn(plugins, 'enabled'),
|
|
121
|
+
enabled_previous: plugins?.enabled,
|
|
122
|
+
dirs_created: plugins === undefined || !Object.hasOwn(plugins, 'dirs'),
|
|
123
|
+
dir_preexisting: Array.isArray(plugins?.dirs) && plugins.dirs.includes(pluginRoot),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function registeredConfig(config, pluginRoot) {
|
|
128
|
+
const plugins = config.plugins ? { ...config.plugins } : {};
|
|
129
|
+
const dirs = Array.isArray(plugins.dirs) ? [...plugins.dirs] : [];
|
|
130
|
+
if (!dirs.includes(pluginRoot)) dirs.push(pluginRoot);
|
|
131
|
+
plugins.enabled = true;
|
|
132
|
+
plugins.dirs = dirs;
|
|
133
|
+
return { ...config, plugins };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function unregisteredConfig(config, pluginRoot, ownership) {
|
|
137
|
+
if (!config.plugins || Array.isArray(config.plugins) || typeof config.plugins !== 'object') {
|
|
138
|
+
throw new Error('cannot unregister Ultra from an invalid ZCode plugins config');
|
|
139
|
+
}
|
|
140
|
+
const next = { ...config, plugins: { ...config.plugins } };
|
|
141
|
+
if (!ownership.dir_preexisting && Array.isArray(next.plugins.dirs)) {
|
|
142
|
+
next.plugins.dirs = next.plugins.dirs.filter((entry) => entry !== pluginRoot);
|
|
143
|
+
}
|
|
144
|
+
if (ownership.dirs_created && next.plugins.dirs?.length === 0) delete next.plugins.dirs;
|
|
145
|
+
|
|
146
|
+
if (ownership.enabled_created) {
|
|
147
|
+
if (next.plugins.enabled === true) delete next.plugins.enabled;
|
|
148
|
+
} else if (next.plugins.enabled === true && ownership.enabled_previous !== true) {
|
|
149
|
+
next.plugins.enabled = ownership.enabled_previous;
|
|
150
|
+
}
|
|
151
|
+
if (ownership.plugins_created && Object.keys(next.plugins).length === 0) delete next.plugins;
|
|
152
|
+
return next;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function buildStaging(repoRoot, staging, publishedRoot) {
|
|
156
|
+
const plugin = path.join(staging, 'plugin');
|
|
157
|
+
const skills = copySkills({ runtime: 'zcode', repoRoot, skillRoot: path.join(plugin, 'skills') });
|
|
158
|
+
copyHooks({
|
|
159
|
+
runtime: 'zcode', repoRoot, hookRoot: path.join(plugin, 'hooks'),
|
|
160
|
+
hooksManifest: buildHooksManifest(),
|
|
161
|
+
});
|
|
162
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
|
|
163
|
+
writeAtomic(path.join(plugin, '.zcode-plugin', 'plugin.json'), `${JSON.stringify({
|
|
164
|
+
name: PLUGIN_NAME,
|
|
165
|
+
version: pkg.version,
|
|
166
|
+
description: 'File-first Ultra Builder Pro workflows for ZCode.',
|
|
167
|
+
author: { name: pkg.author },
|
|
168
|
+
homepage: pkg.homepage,
|
|
169
|
+
repository: pkg.repository?.url || pkg.repository,
|
|
170
|
+
license: pkg.license,
|
|
171
|
+
skills: './skills',
|
|
172
|
+
hooks: './hooks/hooks.json',
|
|
173
|
+
}, null, 2)}\n`);
|
|
174
|
+
writeAtomic(path.join(staging, 'marketplace.json'), `${JSON.stringify({
|
|
175
|
+
name: PLUGIN_NAME,
|
|
176
|
+
plugins: [{
|
|
177
|
+
name: PLUGIN_NAME,
|
|
178
|
+
version: pkg.version,
|
|
179
|
+
description: 'File-first Ultra Builder Pro workflows for ZCode.',
|
|
180
|
+
source: './plugin',
|
|
181
|
+
}],
|
|
182
|
+
}, null, 2)}\n`);
|
|
183
|
+
markManaged(plugin, { adapter: 'zcode', plugin: PLUGIN_NAME, version: pkg.version });
|
|
184
|
+
writePluginProvenance({
|
|
185
|
+
adapter: 'zcode', repoRoot, stagingRoot: plugin, publishedRoot,
|
|
186
|
+
contracts: {
|
|
187
|
+
plugin_manifest: { root: 'plugin', path: '.zcode-plugin/plugin.json' },
|
|
188
|
+
hooks_manifest: { root: 'plugin', path: 'hooks/hooks.json' },
|
|
189
|
+
hook_adapter: { root: 'plugin', path: 'hooks/adapters/zcode.py' },
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
return { skills };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function shouldRunHostCli(ctx = {}) {
|
|
196
|
+
if (typeof ctx.runHostCli === 'boolean') return ctx.runHostCli;
|
|
197
|
+
return ctx.scope === 'global' && !ctx.configDir;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function zcodeCommand(ctx, args) {
|
|
201
|
+
const selected = ctx.zcodeBin || process.env.ZCODE_BIN || zcodeBinary();
|
|
202
|
+
const command = selected.endsWith('.cjs') || selected.endsWith('.js') ? process.execPath : selected;
|
|
203
|
+
const argv = command === selected ? args : [selected, ...args];
|
|
204
|
+
const result = spawnSync(command, argv, {
|
|
205
|
+
encoding: 'utf8',
|
|
206
|
+
timeout: ctx.hostCliTimeoutMs || 30000,
|
|
207
|
+
env: { ...process.env, HOME: ctx.homeDir || os.homedir() },
|
|
208
|
+
});
|
|
209
|
+
if (result.error) throw result.error;
|
|
210
|
+
if (result.status !== 0) {
|
|
211
|
+
throw new Error((result.stderr || result.stdout || '').trim() || `zcode ${args.join(' ')} failed`);
|
|
212
|
+
}
|
|
213
|
+
return result.stdout;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function nativePlugin(ctx) {
|
|
217
|
+
const value = JSON.parse(zcodeCommand(ctx, ['plugins', 'list', '--json']));
|
|
218
|
+
if (!Array.isArray(value.plugins)) throw new Error('zcode plugins list --json must include plugins');
|
|
219
|
+
return value.plugins.find((entry) => entry?.id === INLINE_PLUGIN_ID) || null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function install(ctx = {}) {
|
|
223
|
+
assertPluginScope(ctx);
|
|
224
|
+
const target = resolveMarketplaceRoot(ctx);
|
|
225
|
+
const pluginRoot = resolvePluginRoot(ctx);
|
|
226
|
+
const configRoot = resolveTarget(ctx);
|
|
227
|
+
const configFile = resolveConfigFile(ctx);
|
|
228
|
+
const repoRoot = resolveRepoRoot(ctx);
|
|
229
|
+
if (fs.existsSync(target) && !isManaged(target)) {
|
|
230
|
+
throw new Error(`refusing to replace unmanaged ZCode marketplace: ${target}`);
|
|
231
|
+
}
|
|
232
|
+
const previousMetadata = managedMetadata(target) || {};
|
|
233
|
+
const cleanupAbsent = previousMetadata.cleanup_absent || captureAbsent(configRoot, [
|
|
234
|
+
'.', 'cli', path.join('cli', 'plugins'), path.join('cli', 'plugins', 'marketplaces'),
|
|
235
|
+
path.join('cli', 'config.json'),
|
|
236
|
+
]);
|
|
237
|
+
const ownership = previousMetadata.config_ownership || configOwnership(configFile, pluginRoot);
|
|
238
|
+
const configExisted = fs.existsSync(configFile);
|
|
239
|
+
const configBefore = configExisted ? fs.readFileSync(configFile, 'utf8') : null;
|
|
240
|
+
const nextConfig = registeredConfig(loadConfig(configFile), pluginRoot);
|
|
241
|
+
ensureDir(path.dirname(target));
|
|
242
|
+
const staging = fs.mkdtempSync(path.join(path.dirname(target), `.${PLUGIN_NAME}-staging-`));
|
|
243
|
+
const recovery = fs.existsSync(target)
|
|
244
|
+
? fs.mkdtempSync(path.join(path.dirname(target), `.${PLUGIN_NAME}-recovery-`))
|
|
245
|
+
: null;
|
|
246
|
+
if (recovery) fs.cpSync(target, recovery, { recursive: true });
|
|
247
|
+
try {
|
|
248
|
+
const copied = buildStaging(repoRoot, staging, pluginRoot);
|
|
249
|
+
markManaged(staging, {
|
|
250
|
+
adapter: 'zcode', plugin: PLUGIN_NAME,
|
|
251
|
+
cleanup_absent: cleanupAbsent,
|
|
252
|
+
config_ownership: ownership,
|
|
253
|
+
});
|
|
254
|
+
publishManagedTrees([{ source: staging, target, label: 'ZCode marketplace' }]);
|
|
255
|
+
writeAtomic(configFile, `${JSON.stringify(nextConfig, null, 2)}\n`);
|
|
256
|
+
if (shouldRunHostCli(ctx)) {
|
|
257
|
+
const plugin = nativePlugin(ctx);
|
|
258
|
+
if (!plugin || plugin.enabled !== true || path.resolve(plugin.rootPath) !== path.resolve(pluginRoot)) {
|
|
259
|
+
throw new Error('ZCode did not activate the managed Ultra inline plugin');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (recovery) removeTree(recovery);
|
|
263
|
+
return {
|
|
264
|
+
target,
|
|
265
|
+
marketplaceRoot: target,
|
|
266
|
+
marketplaceFile: resolveMarketplaceFile(ctx),
|
|
267
|
+
pluginRoot,
|
|
268
|
+
skillRoot: path.join(pluginRoot, 'skills'),
|
|
269
|
+
hookRoot: path.join(pluginRoot, 'hooks'),
|
|
270
|
+
configFile,
|
|
271
|
+
copied,
|
|
272
|
+
};
|
|
273
|
+
} catch (error) {
|
|
274
|
+
const rollback = [];
|
|
275
|
+
try {
|
|
276
|
+
if (configExisted) writeAtomic(configFile, configBefore);
|
|
277
|
+
else if (fs.existsSync(configFile)) fs.unlinkSync(configFile);
|
|
278
|
+
} catch (rollbackError) { rollback.push(rollbackError); }
|
|
279
|
+
try {
|
|
280
|
+
if (fs.existsSync(target)) removeTree(target);
|
|
281
|
+
if (recovery) fs.renameSync(recovery, target);
|
|
282
|
+
} catch (rollbackError) { rollback.push(rollbackError); }
|
|
283
|
+
if (fs.existsSync(staging)) removeTree(staging);
|
|
284
|
+
pruneCreatedEmpty(configRoot, cleanupAbsent);
|
|
285
|
+
if (rollback.length) throw new AggregateError([error, ...rollback], 'ZCode install and rollback failed');
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function doctor(ctx = {}) {
|
|
291
|
+
assertPluginScope(ctx);
|
|
292
|
+
const pluginRoot = resolvePluginRoot(ctx);
|
|
293
|
+
const report = inspectPlugin({
|
|
294
|
+
adapter: 'zcode', repoRoot: resolveRepoRoot(ctx), pluginRoot,
|
|
295
|
+
skillRoot: path.join(pluginRoot, 'skills'), hookRoot: path.join(pluginRoot, 'hooks'),
|
|
296
|
+
manifestFile: path.join(pluginRoot, '.zcode-plugin', 'plugin.json'),
|
|
297
|
+
});
|
|
298
|
+
let marketplaceValid = false;
|
|
299
|
+
try {
|
|
300
|
+
const marketplace = JSON.parse(fs.readFileSync(resolveMarketplaceFile(ctx), 'utf8'));
|
|
301
|
+
marketplaceValid = marketplace?.name === PLUGIN_NAME
|
|
302
|
+
&& Array.isArray(marketplace.plugins)
|
|
303
|
+
&& marketplace.plugins.some((entry) => entry?.name === PLUGIN_NAME && entry?.source === './plugin');
|
|
304
|
+
} catch {}
|
|
305
|
+
report.checks.marketplace = { status: marketplaceValid ? 'pass' : 'fail' };
|
|
306
|
+
if (!marketplaceValid) report.issues.push({ code: 'ZCODE_MARKETPLACE_INVALID' });
|
|
307
|
+
|
|
308
|
+
let registered = false;
|
|
309
|
+
try {
|
|
310
|
+
const config = loadConfig(resolveConfigFile(ctx));
|
|
311
|
+
registered = config.plugins?.enabled !== false
|
|
312
|
+
&& Array.isArray(config.plugins?.dirs)
|
|
313
|
+
&& config.plugins.dirs.includes(pluginRoot);
|
|
314
|
+
} catch {}
|
|
315
|
+
report.checks.registration = { status: registered ? 'pass' : 'fail' };
|
|
316
|
+
if (!registered) report.issues.push({ code: 'ZCODE_PLUGIN_NOT_REGISTERED' });
|
|
317
|
+
|
|
318
|
+
if (shouldRunHostCli(ctx)) {
|
|
319
|
+
let discovered = false;
|
|
320
|
+
try {
|
|
321
|
+
const plugin = nativePlugin(ctx);
|
|
322
|
+
discovered = !!plugin && plugin.enabled === true
|
|
323
|
+
&& path.resolve(plugin.rootPath) === path.resolve(pluginRoot)
|
|
324
|
+
&& plugin.skillCount === 14;
|
|
325
|
+
} catch {}
|
|
326
|
+
report.checks.native_registry = { status: discovered ? 'pass' : 'fail' };
|
|
327
|
+
if (!discovered) report.issues.push({ code: 'ZCODE_NATIVE_DISCOVERY_FAILED' });
|
|
328
|
+
}
|
|
329
|
+
report.status = report.issues.length === 0 ? 'healthy' : 'degraded';
|
|
330
|
+
return report;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function uninstall(ctx = {}) {
|
|
334
|
+
assertPluginScope(ctx);
|
|
335
|
+
const target = resolveMarketplaceRoot(ctx);
|
|
336
|
+
const pluginRoot = resolvePluginRoot(ctx);
|
|
337
|
+
const configRoot = resolveTarget(ctx);
|
|
338
|
+
const metadata = managedMetadata(target) || {};
|
|
339
|
+
const ownership = metadata.config_ownership;
|
|
340
|
+
const cleanupAbsent = metadata.cleanup_absent || [];
|
|
341
|
+
if (fs.existsSync(target) && !ownership) {
|
|
342
|
+
throw new Error(`missing ZCode config ownership in ${target}`);
|
|
343
|
+
}
|
|
344
|
+
const configFile = resolveConfigFile(ctx);
|
|
345
|
+
const configBefore = fs.existsSync(configFile) ? fs.readFileSync(configFile, 'utf8') : null;
|
|
346
|
+
if (ownership) {
|
|
347
|
+
const next = unregisteredConfig(loadConfig(configFile), pluginRoot, ownership);
|
|
348
|
+
if (ownership.file_created && Object.keys(next).length === 0) fs.unlinkSync(configFile);
|
|
349
|
+
else writeAtomic(configFile, `${JSON.stringify(next, null, 2)}\n`);
|
|
350
|
+
}
|
|
351
|
+
try {
|
|
352
|
+
const removed = removeManaged(target, 'ZCode marketplace');
|
|
353
|
+
const cleaned = pruneCreatedEmpty(configRoot, cleanupAbsent);
|
|
354
|
+
return { target, marketplaceRoot: target, pluginRoot, removed: { marketplace: removed }, cleaned };
|
|
355
|
+
} catch (error) {
|
|
356
|
+
if (configBefore !== null) writeAtomic(configFile, configBefore);
|
|
357
|
+
throw error;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
module.exports = {
|
|
362
|
+
name: 'zcode',
|
|
363
|
+
PLUGIN_NAME,
|
|
364
|
+
INLINE_PLUGIN_ID,
|
|
365
|
+
buildHooksManifest,
|
|
366
|
+
resolveTarget,
|
|
367
|
+
resolveCliRoot,
|
|
368
|
+
resolveConfigFile,
|
|
369
|
+
resolveMarketplaceRoot,
|
|
370
|
+
resolveMarketplaceFile,
|
|
371
|
+
resolvePluginRoot,
|
|
372
|
+
install,
|
|
373
|
+
doctor,
|
|
374
|
+
uninstall,
|
|
375
|
+
};
|