ultra-builder-pro-cli 0.26.2 → 3.0.2
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 +64 -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 +163 -1
- package/README.md +108 -38
- package/adapters/_shared/host-profile.cjs +68 -12
- package/adapters/_shared/plugin-core.cjs +4 -5
- package/adapters/_shared/runtime-assets.cjs +11 -2
- package/adapters/zcode.js +375 -0
- package/bin/delegate-worker.cjs +20 -0
- package/bin/delegate.cjs +94 -25
- package/bin/install.js +11 -6
- package/docs/ARCHITECTURE.md +84 -23
- package/docs/ARTIFACT-AUTHORITY.md +167 -22
- package/docs/DECISIONS.md +131 -17
- package/docs/PHILOSOPHY.md +58 -7
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +6 -0
- package/docs/RUNTIME-COMPAT-MATRIX.md +106 -13
- package/docs/SKILL-AUTHORING.md +4 -3
- package/docs/WORKFLOW-LIFECYCLE.md +126 -28
- package/docs/evals/adversarial-review-2026-08-14.md +84 -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 +6 -3
- package/skills/ultra-change/SKILL.md +40 -9
- 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 +24 -2
- package/skills/ultra-delegate/references/delegation-contract.md +21 -1
- package/skills/ultra-deliver/SKILL.md +85 -11
- package/skills/ultra-dev/SKILL.md +171 -41
- package/skills/ultra-grilling/SKILL.md +10 -7
- package/skills/ultra-grilling/references/reframing.md +7 -3
- package/skills/ultra-init/SKILL.md +44 -7
- package/skills/ultra-init/scripts/init_project.cjs +968 -21
- package/skills/ultra-plan/SKILL.md +70 -44
- 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 +38 -10
- 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 +95 -11
- package/skills/ultra-tdd/SKILL.md +11 -4
- package/skills/ultra-tdd/references/test-execution.md +6 -1
- package/skills/ultra-test/SKILL.md +145 -17
- package/skills/ultra-test/scripts/validate_review_transport.cjs +549 -0
- package/skills/ultra-test/scripts/worktree_digest.cjs +518 -30
|
@@ -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
|
+
};
|
package/bin/delegate-worker.cjs
CHANGED
|
@@ -70,11 +70,24 @@ function parseJson(value) {
|
|
|
70
70
|
try { return JSON.parse(text); } catch { return null; }
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
function parseFencedJson(value) {
|
|
74
|
+
if (typeof value !== 'string') return [];
|
|
75
|
+
const parsed = [];
|
|
76
|
+
const fences = /```(?:json)?\s*([\s\S]*?)\s*```/gi;
|
|
77
|
+
for (const match of value.matchAll(fences)) {
|
|
78
|
+
try { parsed.push(JSON.parse(match[1])); } catch {}
|
|
79
|
+
}
|
|
80
|
+
return parsed;
|
|
81
|
+
}
|
|
82
|
+
|
|
73
83
|
function collectResultCandidates(value, candidates, depth = 0) {
|
|
74
84
|
if (depth > 12 || value === null || value === undefined) return;
|
|
75
85
|
if (typeof value === 'string') {
|
|
76
86
|
const parsed = parseJson(value);
|
|
77
87
|
if (parsed !== null) collectResultCandidates(parsed, candidates, depth + 1);
|
|
88
|
+
for (const fenced of parseFencedJson(value)) {
|
|
89
|
+
collectResultCandidates(fenced, candidates, depth + 1);
|
|
90
|
+
}
|
|
78
91
|
return;
|
|
79
92
|
}
|
|
80
93
|
if (Array.isArray(value)) {
|
|
@@ -93,6 +106,7 @@ function extractModelResult(spec) {
|
|
|
93
106
|
for (const file of [spec.host_output, spec.stdout]) {
|
|
94
107
|
if (!file || !fs.existsSync(file) || fs.statSync(file).size === 0) continue;
|
|
95
108
|
const text = fs.readFileSync(file, 'utf8');
|
|
109
|
+
collectResultCandidates(text, candidates);
|
|
96
110
|
const full = parseJson(text);
|
|
97
111
|
if (full !== null) collectResultCandidates(full, candidates);
|
|
98
112
|
for (const line of text.split('\n')) {
|
|
@@ -167,6 +181,12 @@ function mechanical(spec, exitCode, signal) {
|
|
|
167
181
|
return {
|
|
168
182
|
delegation_id: spec.delegation_id,
|
|
169
183
|
host: spec.host,
|
|
184
|
+
transport_maturity: spec.transport_maturity ?? null,
|
|
185
|
+
transport_surface: spec.transport_surface ?? null,
|
|
186
|
+
// The explicit experimental acknowledgment travels to every terminal
|
|
187
|
+
// result — finished, failed, cancelled, or interrupted — so no result can
|
|
188
|
+
// imply an undocumented surface was silently launched.
|
|
189
|
+
...(spec.experimental_ack === true ? { experimental_ack: true } : {}),
|
|
170
190
|
instruction_digest: spec.instruction_digest,
|
|
171
191
|
permission_digest: spec.permission_digest,
|
|
172
192
|
output_schema_digest: spec.output_schema_digest,
|
package/bin/delegate.cjs
CHANGED
|
@@ -5,7 +5,7 @@ const fs = require('node:fs');
|
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
const { execFileSync, spawn } = require('node:child_process');
|
|
7
7
|
|
|
8
|
-
const { hostProfile } = require('../adapters/_shared/host-profile.cjs');
|
|
8
|
+
const { hostProfile, transportSurface } = require('../adapters/_shared/host-profile.cjs');
|
|
9
9
|
const { writeAtomic } = require('../adapters/_shared/file-ops.cjs');
|
|
10
10
|
|
|
11
11
|
const PERMISSION_SCHEMA = 'ultra-delegation-permission-v1';
|
|
@@ -21,28 +21,28 @@ const RESULT_JSON_SCHEMA = Object.freeze({
|
|
|
21
21
|
'questions', 'residual_risks',
|
|
22
22
|
],
|
|
23
23
|
properties: {
|
|
24
|
-
$schema: { const: RESULT_SCHEMA },
|
|
25
|
-
status: { enum: ['finished', 'blocked', 'failed'] },
|
|
26
|
-
summary: { type: 'string'
|
|
24
|
+
$schema: { type: 'string', const: RESULT_SCHEMA },
|
|
25
|
+
status: { type: 'string', enum: ['finished', 'blocked', 'failed'] },
|
|
26
|
+
summary: { type: 'string' },
|
|
27
27
|
changed_files: {
|
|
28
|
-
type: 'array',
|
|
28
|
+
type: 'array', items: { type: 'string' },
|
|
29
29
|
},
|
|
30
30
|
checks: {
|
|
31
31
|
type: 'array',
|
|
32
32
|
items: {
|
|
33
33
|
type: 'object',
|
|
34
34
|
additionalProperties: false,
|
|
35
|
-
required: ['command', 'status'],
|
|
35
|
+
required: ['command', 'status', 'output_ref'],
|
|
36
36
|
properties: {
|
|
37
|
-
command: { type: 'string'
|
|
38
|
-
status: { enum: ['passed', 'failed', 'not_run'] },
|
|
39
|
-
output_ref: { type: 'string'
|
|
37
|
+
command: { type: 'string' },
|
|
38
|
+
status: { type: 'string', enum: ['passed', 'failed', 'not_run'] },
|
|
39
|
+
output_ref: { type: 'string' },
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
42
|
},
|
|
43
|
-
evidence: { type: 'array', items: { type: 'string'
|
|
44
|
-
questions: { type: 'array', items: { type: 'string'
|
|
45
|
-
residual_risks: { type: 'array', items: { type: 'string'
|
|
43
|
+
evidence: { type: 'array', items: { type: 'string' } },
|
|
44
|
+
questions: { type: 'array', items: { type: 'string' } },
|
|
45
|
+
residual_risks: { type: 'array', items: { type: 'string' } },
|
|
46
46
|
},
|
|
47
47
|
});
|
|
48
48
|
|
|
@@ -115,8 +115,10 @@ function validateWorktree(worktree) {
|
|
|
115
115
|
|
|
116
116
|
function readPermission(file, worktree) {
|
|
117
117
|
let payload;
|
|
118
|
+
let source;
|
|
118
119
|
try {
|
|
119
|
-
|
|
120
|
+
source = fs.readFileSync(file, 'utf8');
|
|
121
|
+
payload = JSON.parse(source);
|
|
120
122
|
} catch {
|
|
121
123
|
throw new Error('permission must be valid JSON');
|
|
122
124
|
}
|
|
@@ -167,24 +169,40 @@ function readPermission(file, worktree) {
|
|
|
167
169
|
if (payload.external_effects.length !== 0) {
|
|
168
170
|
throw new Error('permission external_effects must be empty; the primary host performs separately authorized effects');
|
|
169
171
|
}
|
|
170
|
-
return {
|
|
172
|
+
return {
|
|
173
|
+
writableRoots,
|
|
174
|
+
writableRootPaths,
|
|
175
|
+
source,
|
|
176
|
+
digest: crypto.createHash('sha256').update(source).digest('hex'),
|
|
177
|
+
};
|
|
171
178
|
}
|
|
172
179
|
|
|
173
|
-
function delegatePrompt({
|
|
180
|
+
function delegatePrompt({
|
|
181
|
+
instructionSource, instructionDigest, permissionSource, permissionDigest,
|
|
182
|
+
worktree, readOnly,
|
|
183
|
+
}) {
|
|
174
184
|
return [
|
|
175
185
|
'Execute one bounded Ultra Builder Pro delegation in the isolated Git worktree.',
|
|
176
|
-
|
|
177
|
-
`
|
|
186
|
+
'The launcher embedded the digest-bound packet below. Do not access packet paths outside the worktree.',
|
|
187
|
+
`Instruction SHA-256: ${instructionDigest}`,
|
|
188
|
+
'--- instruction.md ---',
|
|
189
|
+
instructionSource.trimEnd(),
|
|
190
|
+
'--- end instruction.md ---',
|
|
191
|
+
`Permission SHA-256: ${permissionDigest}`,
|
|
192
|
+
'--- permission.json ---',
|
|
193
|
+
permissionSource.trimEnd(),
|
|
194
|
+
'--- end permission.json ---',
|
|
178
195
|
`Use this worktree as the only filesystem boundary: ${worktree}`,
|
|
179
196
|
readOnly
|
|
180
197
|
? 'This is read-only delegation. Do not create, modify, or delete any project file.'
|
|
181
198
|
: 'Modify only paths covered by permission.writable_roots.',
|
|
182
199
|
'Do not perform external effects, edit the primary checkout, or change Ultra task authority.',
|
|
200
|
+
'Batch independent reads in the earliest tool turn. Do not emit a preliminary result, and reserve a final turn for the one terminal JSON response.',
|
|
183
201
|
`Return exactly one JSON object as the final response using $schema ${RESULT_SCHEMA}.`,
|
|
184
|
-
'The exact fields are status, summary, changed_files, checks, evidence, questions, and residual_risks.',
|
|
202
|
+
'The exact fields are $schema, status, summary, changed_files, checks, evidence, questions, and residual_risks.',
|
|
185
203
|
'status is finished, blocked, or failed. blocked requires at least one question.',
|
|
186
204
|
'changed_files must exactly list every repository-relative path changed in the worktree.',
|
|
187
|
-
'checks items use command, status (passed, failed, or not_run), and
|
|
205
|
+
'checks items use command, status (passed, failed, or not_run), and output_ref. Use a short evidence reference when no file exists.',
|
|
188
206
|
'Do not write the receipt yourself. The launcher validates structured output, process exit, permissions, and the actual Git diff before publication.',
|
|
189
207
|
].join('\n');
|
|
190
208
|
}
|
|
@@ -193,6 +211,10 @@ function selectedBinary(runtime, fallback) {
|
|
|
193
211
|
return process.env[`UBP_DELEGATE_${runtime.toUpperCase()}_BIN`] || fallback;
|
|
194
212
|
}
|
|
195
213
|
|
|
214
|
+
function selectedModel(runtime, explicit) {
|
|
215
|
+
return explicit || process.env[`UBP_DELEGATE_${runtime.toUpperCase()}_MODEL`] || null;
|
|
216
|
+
}
|
|
217
|
+
|
|
196
218
|
function parsePairs(argv, allowed) {
|
|
197
219
|
const values = {};
|
|
198
220
|
for (let index = 0; index < argv.length; index += 2) {
|
|
@@ -208,8 +230,12 @@ function parsePairs(argv, allowed) {
|
|
|
208
230
|
}
|
|
209
231
|
|
|
210
232
|
function parseRun(argv) {
|
|
211
|
-
|
|
212
|
-
|
|
233
|
+
// --ack-experimental is the one valueless flag: launching an experimental
|
|
234
|
+
// transport must be a visible choice, never a silent default.
|
|
235
|
+
const ackExperimental = argv.includes('--ack-experimental');
|
|
236
|
+
const pairs = argv.filter((arg) => arg !== '--ack-experimental');
|
|
237
|
+
const values = parsePairs(pairs, new Set([
|
|
238
|
+
'--to', '--instruction', '--permission', '--worktree', '--timeout', '--model',
|
|
213
239
|
]));
|
|
214
240
|
for (const key of ['--to', '--instruction', '--permission', '--worktree']) {
|
|
215
241
|
if (!values[key]) throw new Error(`delegate run requires ${key}`);
|
|
@@ -221,7 +247,9 @@ function parseRun(argv) {
|
|
|
221
247
|
return {
|
|
222
248
|
to: values['--to'], instruction: values['--instruction'],
|
|
223
249
|
permission: values['--permission'], worktree: values['--worktree'],
|
|
250
|
+
model: values['--model'] || null,
|
|
224
251
|
timeoutMs: Math.ceil(timeout * 1000),
|
|
252
|
+
ackExperimental,
|
|
225
253
|
};
|
|
226
254
|
}
|
|
227
255
|
|
|
@@ -238,6 +266,28 @@ function run(argv, { projectRoot = process.cwd() } = {}) {
|
|
|
238
266
|
const args = parseRun(argv);
|
|
239
267
|
projectRoot = fs.realpathSync(projectRoot);
|
|
240
268
|
const profile = hostProfile(args.to);
|
|
269
|
+
if (args.model && !profile.supportsModelSelection) {
|
|
270
|
+
throw new Error(`delegate --model is not supported by host ${args.to}`);
|
|
271
|
+
}
|
|
272
|
+
const model = profile.supportsModelSelection ? selectedModel(args.to, args.model) : null;
|
|
273
|
+
const binary = selectedBinary(args.to, profile.binary);
|
|
274
|
+
const transportMaturity = profile.transportMaturity;
|
|
275
|
+
const transportSurfaceNote = transportSurface(args.to, binary);
|
|
276
|
+
if (transportMaturity === 'experimental' && !args.ackExperimental) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
`delegate --to ${args.to} would launch ${transportSurfaceNote}. `
|
|
279
|
+
+ 'This transport is experimental (verified-local only; the provider publishes no '
|
|
280
|
+
+ 'stability contract), and it is not the documented ZCode Desktop interactive '
|
|
281
|
+
+ 'surface. Acknowledge explicitly with --ack-experimental to proceed, or pick a '
|
|
282
|
+
+ 'documented transport.',
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
if (transportMaturity === 'experimental') {
|
|
286
|
+
process.stderr.write(
|
|
287
|
+
`ultra-delegate: warning: host '${args.to}' runs ${transportSurfaceNote}; `
|
|
288
|
+
+ `recovery: ubp delegate cancel --delegation <dir>\n`,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
241
291
|
const instruction = projectPath(projectRoot, args.instruction, 'instruction');
|
|
242
292
|
const permission = projectPath(projectRoot, args.permission, 'permission');
|
|
243
293
|
const worktree = projectPath(projectRoot, args.worktree, 'worktree', { directory: true });
|
|
@@ -248,6 +298,7 @@ function run(argv, { projectRoot = process.cwd() } = {}) {
|
|
|
248
298
|
}
|
|
249
299
|
const baseHead = validateWorktree(worktree);
|
|
250
300
|
const permissionContract = readPermission(permission, worktree);
|
|
301
|
+
const instructionSource = fs.readFileSync(instruction, 'utf8');
|
|
251
302
|
const delegationRoot = path.dirname(instruction);
|
|
252
303
|
const delegationId = path.basename(delegationRoot);
|
|
253
304
|
const result = path.join(delegationRoot, 'result.json');
|
|
@@ -272,8 +323,8 @@ function run(argv, { projectRoot = process.cwd() } = {}) {
|
|
|
272
323
|
throw error;
|
|
273
324
|
}
|
|
274
325
|
const startedAt = new Date().toISOString();
|
|
275
|
-
const instructionDigest = sha256(
|
|
276
|
-
const permissionDigest =
|
|
326
|
+
const instructionDigest = crypto.createHash('sha256').update(instructionSource).digest('hex');
|
|
327
|
+
const permissionDigest = permissionContract.digest;
|
|
277
328
|
try {
|
|
278
329
|
fs.writeFileSync(lockFd, `${JSON.stringify({ pid: process.pid, started_at: startedAt })}\n`);
|
|
279
330
|
fs.closeSync(lockFd);
|
|
@@ -287,15 +338,25 @@ function run(argv, { projectRoot = process.cwd() } = {}) {
|
|
|
287
338
|
schemaFile: outputSchema,
|
|
288
339
|
schemaJson: JSON.stringify(RESULT_JSON_SCHEMA),
|
|
289
340
|
hostOutput,
|
|
341
|
+
model,
|
|
290
342
|
};
|
|
291
343
|
const prompt = delegatePrompt({
|
|
292
|
-
|
|
344
|
+
instructionSource,
|
|
345
|
+
instructionDigest,
|
|
346
|
+
permissionSource: permissionContract.source,
|
|
347
|
+
permissionDigest,
|
|
348
|
+
worktree,
|
|
349
|
+
readOnly: profileOptions.readOnly,
|
|
293
350
|
});
|
|
294
351
|
const workerSpec = {
|
|
295
352
|
$schema: 'ultra-delegation-worker-spec-v1',
|
|
296
353
|
delegation_id: delegationId,
|
|
297
354
|
host: args.to,
|
|
298
|
-
|
|
355
|
+
model,
|
|
356
|
+
command: binary,
|
|
357
|
+
transport_maturity: transportMaturity,
|
|
358
|
+
transport_surface: transportSurfaceNote,
|
|
359
|
+
experimental_ack: transportMaturity === 'experimental' ? args.ackExperimental : undefined,
|
|
299
360
|
args: profile.delegateArgv(prompt, worktree, profileOptions),
|
|
300
361
|
cwd: worktree,
|
|
301
362
|
instruction,
|
|
@@ -341,6 +402,10 @@ function run(argv, { projectRoot = process.cwd() } = {}) {
|
|
|
341
402
|
status: 'started',
|
|
342
403
|
delegation_id: delegationId,
|
|
343
404
|
host: args.to,
|
|
405
|
+
model,
|
|
406
|
+
transport_maturity: transportMaturity,
|
|
407
|
+
transport_surface: transportSurfaceNote,
|
|
408
|
+
...(transportMaturity === 'experimental' ? { experimental_ack: args.ackExperimental } : {}),
|
|
344
409
|
worker_pid: worker.pid,
|
|
345
410
|
instruction_digest: instructionDigest,
|
|
346
411
|
permission_digest: permissionDigest,
|
|
@@ -415,6 +480,10 @@ function failureFromSpec(spec, failureType, summary) {
|
|
|
415
480
|
failure_type: failureType,
|
|
416
481
|
delegation_id: spec.delegation_id,
|
|
417
482
|
host: spec.host,
|
|
483
|
+
transport_maturity: spec.transport_maturity ?? null,
|
|
484
|
+
transport_surface: spec.transport_surface ?? null,
|
|
485
|
+
// Terminal recovery results carry the same transport truth as live ones.
|
|
486
|
+
...(spec.experimental_ack === true ? { experimental_ack: true } : {}),
|
|
418
487
|
instruction_digest: spec.instruction_digest,
|
|
419
488
|
permission_digest: spec.permission_digest,
|
|
420
489
|
output_schema_digest: spec.output_schema_digest,
|