ultra-builder-pro-cli 0.23.0 → 0.24.1
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/docs/research/README.md +4 -4
- package/.ultra-template/tasks/tasks.json +4 -2
- package/CHANGELOG.md +72 -0
- package/README.md +55 -39
- package/adapters/_shared/codex-assets.cjs +121 -65
- package/adapters/_shared/interaction-contract.cjs +7 -1
- package/adapters/_shared/provenance.cjs +7 -2
- package/adapters/_shared/runtime-assets.cjs +3 -2
- package/adapters/_shared/runtime-native.cjs +132 -0
- package/adapters/claude.js +60 -30
- package/adapters/codex.js +178 -81
- package/adapters/grok.js +372 -0
- package/adapters/kimi.js +7 -3
- package/adapters/opencode.js +210 -32
- package/agents/code-reviewer.md +4 -2
- package/agents/debugger.md +4 -1
- package/agents/review-code.md +7 -3
- package/agents/review-comments.md +7 -4
- package/agents/review-coordinator.md +11 -7
- package/agents/review-design.md +8 -4
- package/agents/review-errors.md +8 -4
- package/agents/review-spec.md +8 -5
- package/agents/review-tests.md +7 -4
- package/agents/tdd-runner.md +5 -2
- package/bin/install.js +9 -4
- package/bin/orchestrator.js +4 -13
- package/docs/AGENT-CONTEXT.md +143 -229
- package/docs/ARCHITECTURE.md +205 -494
- package/docs/ARTIFACT-AUTHORITY.md +35 -28
- package/docs/LEGACY-CLI-CROSSWALK.md +1 -1
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +12 -9
- package/docs/ROADMAP.md +85 -110
- package/docs/RUNTIME-COMPAT-MATRIX.md +139 -175
- package/docs/SKILL-AUTHORING.md +3 -3
- package/docs/STATE-DB-ACCESS-POLICY.md +13 -8
- package/docs/WORKFLOW-LIFECYCLE.md +189 -448
- package/hooks/README.md +11 -8
- package/hooks/active_task_context.py +16 -16
- package/hooks/adapters/grok.py +134 -0
- package/hooks/{context_spine.py → context_envelope.py} +26 -22
- package/hooks/health_check.py +29 -34
- package/hooks/pre_stop_check.py +22 -12
- package/hooks/subagent_tracker.py +1 -1
- package/hooks/workflow_checkpoint.py +14 -13
- package/hooks/workflow_context.py +11 -12
- package/hooks/workflow_resume.py +14 -13
- package/mcp-server/{breadcrumb.cjs → hook-context.cjs} +7 -9
- package/mcp-server/lib/baseline-workflow.cjs +108 -82
- package/mcp-server/lib/canonical-json.cjs +19 -0
- package/mcp-server/lib/change-authority.cjs +26 -0
- package/mcp-server/lib/change-workflow.cjs +630 -45
- package/mcp-server/lib/context-envelope.cjs +508 -0
- package/mcp-server/lib/decision-records.cjs +337 -0
- package/mcp-server/lib/doctor.cjs +91 -33
- package/mcp-server/lib/init-project.cjs +78 -39
- package/mcp-server/lib/managed-file-write.cjs +116 -0
- package/mcp-server/lib/plan-checkpoint.cjs +177 -0
- package/mcp-server/lib/project-context.cjs +178 -0
- package/mcp-server/lib/stage-checkpoints.cjs +309 -0
- package/mcp-server/lib/state-db.cjs +155 -24
- package/mcp-server/lib/state-ops.cjs +21 -3
- package/mcp-server/lib/task-ledger.cjs +522 -13
- package/mcp-server/lib/ultra-facade.cjs +757 -442
- package/mcp-server/lib/worker-packet.cjs +358 -0
- package/mcp-server/server.cjs +200 -1103
- package/orchestrator/daemon.cjs +27 -9
- package/orchestrator/parallel-orchestrator.cjs +111 -76
- package/orchestrator/session-runner.cjs +135 -34
- package/package.json +10 -3
- package/skills/ultra-change/SKILL.md +10 -8
- package/skills/ultra-deliver/SKILL.md +17 -19
- package/skills/ultra-dev/SKILL.md +20 -11
- package/skills/ultra-doctor/SKILL.md +4 -4
- package/skills/ultra-init/SKILL.md +6 -5
- package/skills/ultra-init/references/brownfield-adoption.md +20 -14
- package/skills/ultra-plan/SKILL.md +13 -9
- package/skills/ultra-research/SKILL.md +18 -9
- package/skills/ultra-review/SKILL.md +14 -9
- package/skills/ultra-review/references/unified-schema.md +2 -0
- package/skills/ultra-review/scripts/review_wait.py +8 -0
- package/skills/ultra-status/SKILL.md +2 -2
- package/skills/ultra-test/SKILL.md +10 -6
- package/skills/ultra-think/SKILL.md +8 -7
- package/skills/ultra-think/references/interaction-boundary.md +56 -0
- package/spec/cli-protocol.md +80 -199
- package/spec/mcp-tools.yaml +43 -2806
- package/spec/schemas/state-db.sql +107 -4
- package/spec/schemas/{task-ledger.v1.schema.json → task-ledger.v2.schema.json} +70 -6
- package/ultra-tools/commands/status.cjs +369 -243
- package/mcp-server/lib/context-spine.cjs +0 -1208
- package/mcp-server/lib/decision-dialogue.cjs +0 -1128
- package/mcp-server/lib/project-breadcrumb.cjs +0 -173
- package/mcp-server/lib/spec-learning.cjs +0 -284
- package/mcp-server/lib/workflow-state.cjs +0 -3129
- package/skills/ultra-think/references/decision-dialogue.md +0 -155
|
@@ -30,44 +30,6 @@ const {
|
|
|
30
30
|
|
|
31
31
|
const PLUGIN_NAME = 'ultra-builder-pro';
|
|
32
32
|
const MANAGED_MARKER = 'Managed by Ultra Builder Pro Codex adapter.';
|
|
33
|
-
const CODEX_NATIVE_MCP_REPLACEMENTS = Object.freeze({
|
|
34
|
-
'review.run': {
|
|
35
|
-
surface: 'native_custom_agents',
|
|
36
|
-
replacement: '$ultra-builder-pro:ultra-review with the installed review-* agents',
|
|
37
|
-
},
|
|
38
|
-
'review.verdict': {
|
|
39
|
-
surface: 'native_custom_agents',
|
|
40
|
-
replacement: 'review-coordinator synthesis using the Ultra unified review artifact',
|
|
41
|
-
},
|
|
42
|
-
'impact.radius': {
|
|
43
|
-
surface: 'codex_code_discovery',
|
|
44
|
-
replacement: 'indexed code graph tools when current, otherwise targeted repository reads',
|
|
45
|
-
},
|
|
46
|
-
'impact.changes': {
|
|
47
|
-
surface: 'codex_code_discovery',
|
|
48
|
-
replacement: 'git diff plus indexed code graph tools when current',
|
|
49
|
-
},
|
|
50
|
-
'impact.dependents': {
|
|
51
|
-
surface: 'codex_code_discovery',
|
|
52
|
-
replacement: 'indexed caller/dependency tracing when current, otherwise targeted repository search',
|
|
53
|
-
},
|
|
54
|
-
'skill.resolve': {
|
|
55
|
-
surface: 'plugin_skill_discovery',
|
|
56
|
-
replacement: 'Codex plugin skill discovery with explicit $ultra-builder-pro:<skill> invocation',
|
|
57
|
-
},
|
|
58
|
-
'skill.manifest': {
|
|
59
|
-
surface: 'plugin_skill_discovery',
|
|
60
|
-
replacement: 'the installed plugin skill SKILL.md and agents/openai.yaml contract',
|
|
61
|
-
},
|
|
62
|
-
'ask.question': {
|
|
63
|
-
surface: 'request_user_input',
|
|
64
|
-
replacement: 'use request_user_input when available, otherwise ask one concise direct question',
|
|
65
|
-
},
|
|
66
|
-
'ask.menu': {
|
|
67
|
-
surface: 'request_user_input',
|
|
68
|
-
replacement: 'use request_user_input when available, otherwise present one concise direct choice',
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
33
|
const COMMAND_NAMES = Object.freeze(CORE_PUBLIC_SKILLS.filter((name) => name !== 'ultra-review'));
|
|
72
34
|
const SKILL_REFERENCE_NAMES = Object.freeze([
|
|
73
35
|
...COMMAND_NAMES,
|
|
@@ -77,6 +39,11 @@ const SKILL_REFERENCE_NAMES = Object.freeze([
|
|
|
77
39
|
]);
|
|
78
40
|
const CODEX_PRIMARY_SKILLS = new Set([...COMMAND_NAMES, 'ultra-review']);
|
|
79
41
|
const TEXT_EXTENSIONS = new Set(['.md', '.json', '.py', '.sh', '.txt', '.yaml', '.yml']);
|
|
42
|
+
const NATIVE_PACKAGES = Object.freeze([
|
|
43
|
+
'better-sqlite3',
|
|
44
|
+
'bindings',
|
|
45
|
+
'file-uri-to-path',
|
|
46
|
+
]);
|
|
80
47
|
|
|
81
48
|
function titleCase(name) {
|
|
82
49
|
const special = { 'cc-collab': 'Claude Code Collaboration' };
|
|
@@ -273,7 +240,7 @@ function buildHooksManifest(pluginRoot) {
|
|
|
273
240
|
};
|
|
274
241
|
}
|
|
275
242
|
|
|
276
|
-
function copyHooks(repoRoot, pluginRoot) {
|
|
243
|
+
function copyHooks(repoRoot, pluginRoot, publishedRoot = pluginRoot) {
|
|
277
244
|
const sourceRoot = path.join(repoRoot, 'hooks');
|
|
278
245
|
const targetRoot = path.join(pluginRoot, 'hooks');
|
|
279
246
|
ensureDir(targetRoot);
|
|
@@ -287,7 +254,7 @@ function copyHooks(repoRoot, pluginRoot) {
|
|
|
287
254
|
);
|
|
288
255
|
writeAtomic(
|
|
289
256
|
path.join(targetRoot, 'hooks.json'),
|
|
290
|
-
JSON.stringify(buildHooksManifest(
|
|
257
|
+
JSON.stringify(buildHooksManifest(publishedRoot), null, 2) + '\n',
|
|
291
258
|
);
|
|
292
259
|
}
|
|
293
260
|
|
|
@@ -322,6 +289,8 @@ function buildMcpRuntime(repoRoot, pluginRoot, { runtime = 'codex' } = {}) {
|
|
|
322
289
|
buildRoot,
|
|
323
290
|
'--no-cache',
|
|
324
291
|
'--quiet',
|
|
292
|
+
'-e',
|
|
293
|
+
'better-sqlite3',
|
|
325
294
|
], {
|
|
326
295
|
cwd: repoRoot,
|
|
327
296
|
encoding: 'utf8',
|
|
@@ -347,6 +316,8 @@ function buildMcpRuntime(repoRoot, pluginRoot, { runtime = 'codex' } = {}) {
|
|
|
347
316
|
cliBuildRoot,
|
|
348
317
|
'--no-cache',
|
|
349
318
|
'--quiet',
|
|
319
|
+
'-e',
|
|
320
|
+
'better-sqlite3',
|
|
350
321
|
], {
|
|
351
322
|
cwd: repoRoot,
|
|
352
323
|
encoding: 'utf8',
|
|
@@ -386,6 +357,8 @@ function buildMcpRuntime(repoRoot, pluginRoot, { runtime = 'codex' } = {}) {
|
|
|
386
357
|
workerBuildRoot,
|
|
387
358
|
'--no-cache',
|
|
388
359
|
'--quiet',
|
|
360
|
+
'-e',
|
|
361
|
+
'better-sqlite3',
|
|
389
362
|
], {
|
|
390
363
|
cwd: repoRoot,
|
|
391
364
|
encoding: 'utf8',
|
|
@@ -416,6 +389,52 @@ function buildMcpRuntime(repoRoot, pluginRoot, { runtime = 'codex' } = {}) {
|
|
|
416
389
|
fs.copyFileSync(sourceFile, path.join(runtimeRoot, supportFile));
|
|
417
390
|
}
|
|
418
391
|
|
|
392
|
+
const nodeModulesRoot = path.join(runtimeRoot, 'node_modules');
|
|
393
|
+
for (const packageName of NATIVE_PACKAGES) {
|
|
394
|
+
let packageFile;
|
|
395
|
+
try {
|
|
396
|
+
packageFile = require.resolve(`${packageName}/package.json`, { paths: [repoRoot] });
|
|
397
|
+
} catch (error) {
|
|
398
|
+
throw new Error(`Ultra native runtime dependency ${packageName} is unavailable: ${error.message}`);
|
|
399
|
+
}
|
|
400
|
+
copyTree(
|
|
401
|
+
path.dirname(packageFile),
|
|
402
|
+
path.join(nodeModulesRoot, packageName),
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
const nativeRelative = path.join(
|
|
406
|
+
'node_modules',
|
|
407
|
+
'better-sqlite3',
|
|
408
|
+
'build',
|
|
409
|
+
'Release',
|
|
410
|
+
'better_sqlite3.node',
|
|
411
|
+
);
|
|
412
|
+
const nativeFile = path.join(runtimeRoot, nativeRelative);
|
|
413
|
+
if (!fs.existsSync(nativeFile) || !fs.statSync(nativeFile).isFile()) {
|
|
414
|
+
throw new Error(`Ultra native SQLite driver is missing after staging: ${nativeFile}`);
|
|
415
|
+
}
|
|
416
|
+
const betterSqlitePackage = JSON.parse(fs.readFileSync(
|
|
417
|
+
path.join(nodeModulesRoot, 'better-sqlite3', 'package.json'),
|
|
418
|
+
'utf8',
|
|
419
|
+
));
|
|
420
|
+
writeAtomic(path.join(runtimeRoot, 'native-runtime.json'), JSON.stringify({
|
|
421
|
+
schema_version: 1,
|
|
422
|
+
platform: process.platform,
|
|
423
|
+
arch: process.arch,
|
|
424
|
+
modules: process.versions.modules,
|
|
425
|
+
node_version: process.versions.node,
|
|
426
|
+
package: {
|
|
427
|
+
name: 'better-sqlite3',
|
|
428
|
+
version: betterSqlitePackage.version,
|
|
429
|
+
},
|
|
430
|
+
native_module: nativeRelative.split(path.sep).join('/'),
|
|
431
|
+
native_sha256: crypto.createHash('sha256').update(fs.readFileSync(nativeFile)).digest('hex'),
|
|
432
|
+
}, null, 2) + '\n');
|
|
433
|
+
fs.copyFileSync(
|
|
434
|
+
path.join(repoRoot, 'adapters', '_shared', 'runtime-native.cjs'),
|
|
435
|
+
path.join(runtimeRoot, 'runtime-native.cjs'),
|
|
436
|
+
);
|
|
437
|
+
|
|
419
438
|
const runtimeBootstrap = runtime === 'kimi' ? `
|
|
420
439
|
const fs = require('node:fs');
|
|
421
440
|
const pluginRoot = path.resolve(__dirname, '..');
|
|
@@ -443,10 +462,12 @@ if (!process.env.UBP_ROOT_DIR) {
|
|
|
443
462
|
const launcher = `'use strict';
|
|
444
463
|
|
|
445
464
|
const path = require('node:path');
|
|
465
|
+
const { inspectRuntimeNative } = require('./runtime-native.cjs');
|
|
446
466
|
|
|
447
467
|
process.env.UBP_RUNTIME_ROOT = path.resolve(__dirname, '..');
|
|
448
468
|
${runtimeBootstrap}
|
|
449
469
|
|
|
470
|
+
inspectRuntimeNative(__dirname);
|
|
450
471
|
const { main } = require('./index.cjs');
|
|
451
472
|
|
|
452
473
|
main().catch((error) => {
|
|
@@ -456,8 +477,8 @@ main().catch((error) => {
|
|
|
456
477
|
`;
|
|
457
478
|
writeAtomic(path.join(runtimeRoot, 'launch.cjs'), launcher);
|
|
458
479
|
fs.copyFileSync(
|
|
459
|
-
path.join(repoRoot, 'mcp-server', '
|
|
460
|
-
path.join(runtimeRoot, '
|
|
480
|
+
path.join(repoRoot, 'mcp-server', 'hook-context.cjs'),
|
|
481
|
+
path.join(runtimeRoot, 'hook-context.cjs'),
|
|
461
482
|
);
|
|
462
483
|
fs.copyFileSync(
|
|
463
484
|
path.join(repoRoot, 'mcp-server', 'hook-event.cjs'),
|
|
@@ -470,11 +491,7 @@ main().catch((error) => {
|
|
|
470
491
|
|
|
471
492
|
const sourceToolsFile = path.join(repoRoot, 'spec', 'mcp-tools.yaml');
|
|
472
493
|
const upstreamManifest = yaml.load(fs.readFileSync(sourceToolsFile, 'utf8'));
|
|
473
|
-
const {
|
|
474
|
-
LEGACY_TOOLS,
|
|
475
|
-
PUBLIC_TOOLS,
|
|
476
|
-
REGISTERED_TOOLS,
|
|
477
|
-
} = require(path.join(repoRoot, 'mcp-server', 'server.cjs'));
|
|
494
|
+
const { REGISTERED_TOOLS } = require(path.join(repoRoot, 'mcp-server', 'server.cjs'));
|
|
478
495
|
const registered = new Set(REGISTERED_TOOLS);
|
|
479
496
|
const liveFamilies = new Set(
|
|
480
497
|
upstreamManifest.tools.filter((tool) => registered.has(tool.name)).map((tool) => tool.family),
|
|
@@ -495,15 +512,6 @@ main().catch((error) => {
|
|
|
495
512
|
path.join(specRoot, 'interaction-contract.json'),
|
|
496
513
|
JSON.stringify(interactionContract(runtime), null, 2) + '\n',
|
|
497
514
|
);
|
|
498
|
-
if (runtime === 'codex') {
|
|
499
|
-
fs.copyFileSync(sourceToolsFile, path.join(specRoot, 'upstream-mcp-tools.yaml'));
|
|
500
|
-
writeAtomic(path.join(specRoot, 'codex-capability-map.json'), JSON.stringify({
|
|
501
|
-
runtime: 'codex',
|
|
502
|
-
live_mcp_tools: PUBLIC_TOOLS,
|
|
503
|
-
compatibility_mcp_tools: LEGACY_TOOLS,
|
|
504
|
-
codex_native_replacements: CODEX_NATIVE_MCP_REPLACEMENTS,
|
|
505
|
-
}, null, 2) + '\n');
|
|
506
|
-
}
|
|
507
515
|
const sourceSchema = path.join(repoRoot, 'spec', 'schemas', 'state-db.sql');
|
|
508
516
|
const targetSchema = path.join(specRoot, 'schemas', 'state-db.sql');
|
|
509
517
|
ensureDir(path.dirname(targetSchema));
|
|
@@ -521,9 +529,49 @@ main().catch((error) => {
|
|
|
521
529
|
launcher: path.join(runtimeRoot, 'launch.cjs'),
|
|
522
530
|
bundle: path.join(runtimeRoot, 'index.cjs'),
|
|
523
531
|
ultraTools: path.join(runtimeRoot, 'ultra-tools.cjs'),
|
|
532
|
+
nativeManifest: path.join(runtimeRoot, 'native-runtime.json'),
|
|
524
533
|
};
|
|
525
534
|
}
|
|
526
535
|
|
|
536
|
+
function mcpCommand(launcher) {
|
|
537
|
+
if (process.platform === 'win32') {
|
|
538
|
+
return { command: 'node.exe', args: [launcher] };
|
|
539
|
+
}
|
|
540
|
+
return { command: '/usr/bin/env', args: ['node', launcher] };
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function inspectInstalledRuntime(runtimeRoot) {
|
|
544
|
+
const report = {
|
|
545
|
+
status: 'healthy',
|
|
546
|
+
checks: {},
|
|
547
|
+
issues: [],
|
|
548
|
+
};
|
|
549
|
+
try {
|
|
550
|
+
const detail = require(path.join(runtimeRoot, 'runtime-native.cjs'))
|
|
551
|
+
.inspectRuntimeNative(runtimeRoot);
|
|
552
|
+
report.checks.native_runtime = { status: 'pass', ...detail };
|
|
553
|
+
} catch (error) {
|
|
554
|
+
report.status = 'degraded';
|
|
555
|
+
report.checks.native_runtime = { status: 'fail' };
|
|
556
|
+
report.issues.push({
|
|
557
|
+
code: error.code || 'RUNTIME_NATIVE_MISSING',
|
|
558
|
+
message: error.message,
|
|
559
|
+
...(error.details || {}),
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
return report;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function applyNativeDoctor(report, runtimeRoot) {
|
|
566
|
+
const native = inspectInstalledRuntime(runtimeRoot);
|
|
567
|
+
report.checks = { ...(report.checks || {}), ...native.checks };
|
|
568
|
+
report.issues = [...(report.issues || []), ...native.issues];
|
|
569
|
+
if (report.status !== 'missing') {
|
|
570
|
+
report.status = report.issues.length === 0 ? 'healthy' : 'degraded';
|
|
571
|
+
}
|
|
572
|
+
return report;
|
|
573
|
+
}
|
|
574
|
+
|
|
527
575
|
function pluginContentHash(pluginRoot, baseVersion) {
|
|
528
576
|
const hash = crypto.createHash('sha256');
|
|
529
577
|
hash.update(baseVersion);
|
|
@@ -535,13 +583,14 @@ function pluginContentHash(pluginRoot, baseVersion) {
|
|
|
535
583
|
return hash.digest('hex').slice(0, 12);
|
|
536
584
|
}
|
|
537
585
|
|
|
538
|
-
function buildPlugin({ repoRoot, pluginRoot }) {
|
|
586
|
+
function buildPlugin({ repoRoot, pluginRoot, publishedRoot = pluginRoot }) {
|
|
539
587
|
if (fs.existsSync(pluginRoot)) {
|
|
540
588
|
const marker = path.join(pluginRoot, '.ubp-managed');
|
|
541
|
-
|
|
589
|
+
const entries = fs.readdirSync(pluginRoot);
|
|
590
|
+
if (entries.length > 0 && !fs.existsSync(marker)) {
|
|
542
591
|
throw new Error(`refusing to replace unmanaged plugin directory: ${pluginRoot}`);
|
|
543
592
|
}
|
|
544
|
-
removeTree(pluginRoot);
|
|
593
|
+
if (entries.length > 0) removeTree(pluginRoot);
|
|
545
594
|
}
|
|
546
595
|
ensureDir(pluginRoot);
|
|
547
596
|
|
|
@@ -555,14 +604,18 @@ function buildPlugin({ repoRoot, pluginRoot }) {
|
|
|
555
604
|
installedSkills.push(name);
|
|
556
605
|
}
|
|
557
606
|
|
|
558
|
-
copyHooks(repoRoot, pluginRoot);
|
|
607
|
+
copyHooks(repoRoot, pluginRoot, publishedRoot);
|
|
559
608
|
const mcpRuntime = buildMcpRuntime(repoRoot, pluginRoot);
|
|
609
|
+
const command = mcpCommand(path.join(
|
|
610
|
+
publishedRoot,
|
|
611
|
+
path.relative(pluginRoot, mcpRuntime.launcher),
|
|
612
|
+
));
|
|
560
613
|
writeAtomic(path.join(pluginRoot, '.mcp.json'), JSON.stringify({
|
|
561
614
|
mcpServers: {
|
|
562
615
|
[PLUGIN_NAME]: {
|
|
563
616
|
type: 'stdio',
|
|
564
|
-
command:
|
|
565
|
-
args:
|
|
617
|
+
command: command.command,
|
|
618
|
+
args: command.args,
|
|
566
619
|
},
|
|
567
620
|
},
|
|
568
621
|
}, null, 2) + '\n');
|
|
@@ -576,7 +629,7 @@ function buildPlugin({ repoRoot, pluginRoot }) {
|
|
|
576
629
|
const manifest = {
|
|
577
630
|
name: PLUGIN_NAME,
|
|
578
631
|
version,
|
|
579
|
-
description: 'Codex-native Ultra Builder Pro workflows
|
|
632
|
+
description: 'Codex-native Ultra Builder Pro workflows with a persistent safety kernel.',
|
|
580
633
|
author: { name: typeof pkg.author === 'string' ? pkg.author : 'Ultra Builder Pro contributors' },
|
|
581
634
|
homepage: pkg.homepage,
|
|
582
635
|
repository: 'https://github.com/rocky2431/ultra-builder-pro-cli',
|
|
@@ -587,7 +640,7 @@ function buildPlugin({ repoRoot, pluginRoot }) {
|
|
|
587
640
|
interface: {
|
|
588
641
|
displayName: 'Ultra Builder Pro',
|
|
589
642
|
shortDescription: 'Codex-native engineering workflows and verification gates.',
|
|
590
|
-
longDescription: 'Complete Codex adaptation of Ultra Builder Pro skills, command workflows, custom agents, lifecycle hooks, and project-local
|
|
643
|
+
longDescription: 'Complete Codex adaptation of Ultra Builder Pro skills, command workflows, custom agents, lifecycle hooks, and a project-local persistence and safety kernel.',
|
|
591
644
|
developerName: typeof pkg.author === 'string' ? pkg.author : 'Ultra Builder Pro contributors',
|
|
592
645
|
category: 'Developer Tools',
|
|
593
646
|
capabilities: ['Interactive', 'Write'],
|
|
@@ -602,7 +655,7 @@ function buildPlugin({ repoRoot, pluginRoot }) {
|
|
|
602
655
|
};
|
|
603
656
|
writeAtomic(path.join(pluginRoot, '.codex-plugin', 'plugin.json'), JSON.stringify(manifest, null, 2) + '\n');
|
|
604
657
|
writeAtomic(path.join(pluginRoot, '.ubp-managed'), JSON.stringify({ source: 'ubp', adapter: 'codex', version }, null, 2) + '\n');
|
|
605
|
-
return { root:
|
|
658
|
+
return { root: publishedRoot, version, skills: installedSkills.sort() };
|
|
606
659
|
}
|
|
607
660
|
|
|
608
661
|
function tomlMultiline(value) {
|
|
@@ -663,6 +716,9 @@ module.exports = {
|
|
|
663
716
|
buildCommandMap,
|
|
664
717
|
buildHooksManifest,
|
|
665
718
|
buildMcpRuntime,
|
|
719
|
+
inspectInstalledRuntime,
|
|
720
|
+
applyNativeDoctor,
|
|
721
|
+
mcpCommand,
|
|
666
722
|
buildPlugin,
|
|
667
723
|
buildAgentToml,
|
|
668
724
|
installAgents,
|
|
@@ -23,10 +23,15 @@ const SURFACES = Object.freeze({
|
|
|
23
23
|
fallback: 'direct_user_interaction',
|
|
24
24
|
availability: 'interactive_non_auto_mode',
|
|
25
25
|
}),
|
|
26
|
+
grok: Object.freeze({
|
|
27
|
+
primary: 'AskUserQuestion',
|
|
28
|
+
fallback: 'direct_user_interaction',
|
|
29
|
+
availability: 'interactive_session',
|
|
30
|
+
}),
|
|
26
31
|
});
|
|
27
32
|
|
|
28
33
|
const GENERIC_QUESTION_GUIDANCE =
|
|
29
|
-
/Use the host-native structured question surface declared by the installed interaction\s+contract(?: when it exists)?\./g;
|
|
34
|
+
/Use the host-native (?:structured )?question surface declared by the installed interaction\s+contract(?: when it exists)?(?:;?\s+prefer its structured form when the host exposes one)?\./g;
|
|
30
35
|
|
|
31
36
|
function interactionContract(runtime) {
|
|
32
37
|
const question = SURFACES[runtime];
|
|
@@ -106,6 +111,7 @@ function interactionPrompt(runtime) {
|
|
|
106
111
|
codex: 'Codex',
|
|
107
112
|
opencode: 'OpenCode',
|
|
108
113
|
kimi: 'Kimi Code',
|
|
114
|
+
grok: 'Grok Build',
|
|
109
115
|
}[runtime];
|
|
110
116
|
const primary = runtime === 'opencode' ? `\`${surface.primary}\`` : surface.primary;
|
|
111
117
|
return `Use ${host} ${primary} when ${surface.availability.replaceAll('_', ' ')} permits it.`;
|
|
@@ -187,7 +187,8 @@ function assetRefsForTree(rootName, root, { exclude = [] } = {}) {
|
|
|
187
187
|
function writeProvenance({
|
|
188
188
|
file, adapter, packageInfo, repository, sourceCommit: commit = null,
|
|
189
189
|
sourceDirty: dirty = null, sourceWorktreeDigest: worktreeDigest = null,
|
|
190
|
-
roots: inputRoots,
|
|
190
|
+
roots: inputRoots, assetRoots: inputAssetRoots = inputRoots,
|
|
191
|
+
assets: inputAssets, contracts: inputContracts = {},
|
|
191
192
|
}) {
|
|
192
193
|
if (!file || !adapter || !packageInfo?.name || !packageInfo?.version || !repository) {
|
|
193
194
|
throw new Error('provenance file, adapter, package, version, and repository are required');
|
|
@@ -199,9 +200,13 @@ function writeProvenance({
|
|
|
199
200
|
throw new Error('provenance source worktree digest must be a SHA-256 digest or null');
|
|
200
201
|
}
|
|
201
202
|
const roots = normalizeRoots(inputRoots);
|
|
203
|
+
const assetRoots = normalizeRoots(inputAssetRoots);
|
|
204
|
+
for (const root of Object.keys(roots)) {
|
|
205
|
+
if (!assetRoots[root]) throw new Error(`missing provenance asset root: ${root}`);
|
|
206
|
+
}
|
|
202
207
|
const refs = normalizeAssetRefs(inputAssets, roots);
|
|
203
208
|
const assets = refs.map((ref) => {
|
|
204
|
-
const { target } = resolveRef(
|
|
209
|
+
const { target } = resolveRef(assetRoots, ref);
|
|
205
210
|
const stat = fs.statSync(target);
|
|
206
211
|
if (!stat.isFile()) throw new Error(`provenance asset is not a file: ${target}`);
|
|
207
212
|
return { ...ref, size_bytes: stat.size, sha256: hashFile(target) };
|
|
@@ -56,13 +56,14 @@ const INTERNAL_AGENT_SKILLS = Object.freeze([
|
|
|
56
56
|
'testing-rules',
|
|
57
57
|
]);
|
|
58
58
|
|
|
59
|
-
const SUPPORTED_RUNTIMES = Object.freeze(['claude', 'opencode', 'codex', 'kimi']);
|
|
59
|
+
const SUPPORTED_RUNTIMES = Object.freeze(['claude', 'opencode', 'codex', 'kimi', 'grok']);
|
|
60
60
|
|
|
61
61
|
const COLLAB_SKILLS_BY_RUNTIME = Object.freeze({
|
|
62
62
|
claude: Object.freeze(['codex-collab', 'ultra-verify']),
|
|
63
63
|
codex: Object.freeze(['cc-collab', 'ultra-verify']),
|
|
64
64
|
opencode: Object.freeze(['cc-collab', 'codex-collab', 'ultra-verify']),
|
|
65
65
|
kimi: Object.freeze(['cc-collab', 'codex-collab', 'ultra-verify']),
|
|
66
|
+
grok: Object.freeze(['cc-collab', 'codex-collab', 'ultra-verify']),
|
|
66
67
|
});
|
|
67
68
|
|
|
68
69
|
const MCP_DEPENDENT_SKILLS = Object.freeze([
|
|
@@ -80,7 +81,7 @@ const MCP_DEPENDENT_SKILLS = Object.freeze([
|
|
|
80
81
|
|
|
81
82
|
const WORKFLOW_HOOK_FILES = Object.freeze([
|
|
82
83
|
'active_task_context.py',
|
|
83
|
-
'
|
|
84
|
+
'context_envelope.py',
|
|
84
85
|
'health_check.py',
|
|
85
86
|
'pre_stop_check.py',
|
|
86
87
|
'runtime_paths.py',
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
|
|
7
|
+
const MANIFEST_FILE = 'native-runtime.json';
|
|
8
|
+
|
|
9
|
+
class RuntimeNativeError extends Error {
|
|
10
|
+
constructor(code, message, details = {}) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'RuntimeNativeError';
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.details = details;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function sha256(file) {
|
|
19
|
+
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function readManifest(runtimeRoot) {
|
|
23
|
+
const file = path.join(path.resolve(runtimeRoot), MANIFEST_FILE);
|
|
24
|
+
if (!fs.existsSync(file)) {
|
|
25
|
+
throw new RuntimeNativeError(
|
|
26
|
+
'RUNTIME_NATIVE_MISSING',
|
|
27
|
+
`Ultra native runtime manifest is missing: ${file}`,
|
|
28
|
+
{ path: file },
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
let manifest;
|
|
32
|
+
try {
|
|
33
|
+
manifest = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
34
|
+
} catch (cause) {
|
|
35
|
+
throw new RuntimeNativeError(
|
|
36
|
+
'RUNTIME_NATIVE_MISSING',
|
|
37
|
+
`Ultra native runtime manifest is invalid: ${file}`,
|
|
38
|
+
{ path: file, cause: cause.message },
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
if (manifest?.schema_version !== 1
|
|
42
|
+
|| typeof manifest.native_module !== 'string'
|
|
43
|
+
|| !/^[0-9a-f]{64}$/i.test(manifest.native_sha256 || '')) {
|
|
44
|
+
throw new RuntimeNativeError(
|
|
45
|
+
'RUNTIME_NATIVE_MISSING',
|
|
46
|
+
`Ultra native runtime manifest is incomplete: ${file}`,
|
|
47
|
+
{ path: file },
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return { file, manifest };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function inspectRuntimeNative(runtimeRoot, { load = true } = {}) {
|
|
54
|
+
const root = path.resolve(runtimeRoot);
|
|
55
|
+
const { file, manifest } = readManifest(root);
|
|
56
|
+
const expected = {
|
|
57
|
+
platform: process.platform,
|
|
58
|
+
arch: process.arch,
|
|
59
|
+
modules: process.versions.modules,
|
|
60
|
+
};
|
|
61
|
+
const actual = {
|
|
62
|
+
platform: manifest.platform,
|
|
63
|
+
arch: manifest.arch,
|
|
64
|
+
modules: String(manifest.modules || ''),
|
|
65
|
+
};
|
|
66
|
+
if (actual.platform !== expected.platform
|
|
67
|
+
|| actual.arch !== expected.arch
|
|
68
|
+
|| actual.modules !== expected.modules) {
|
|
69
|
+
throw new RuntimeNativeError(
|
|
70
|
+
'RUNTIME_ABI_MISMATCH',
|
|
71
|
+
`Ultra native runtime requires ${actual.platform}/${actual.arch} ABI ${
|
|
72
|
+
actual.modules
|
|
73
|
+
}, but the active Node is ${expected.platform}/${expected.arch} ABI ${expected.modules}`,
|
|
74
|
+
{ expected: actual, actual: expected, manifest: file },
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const nativeFile = path.resolve(root, manifest.native_module);
|
|
78
|
+
if (nativeFile !== root && !nativeFile.startsWith(`${root}${path.sep}`)) {
|
|
79
|
+
throw new RuntimeNativeError(
|
|
80
|
+
'RUNTIME_NATIVE_MISSING',
|
|
81
|
+
'Ultra native module path escapes the runtime root',
|
|
82
|
+
{ path: manifest.native_module },
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (!fs.existsSync(nativeFile) || !fs.statSync(nativeFile).isFile()) {
|
|
86
|
+
throw new RuntimeNativeError(
|
|
87
|
+
'RUNTIME_NATIVE_MISSING',
|
|
88
|
+
`Ultra native SQLite driver is missing: ${nativeFile}`,
|
|
89
|
+
{ path: nativeFile },
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const digest = sha256(nativeFile);
|
|
93
|
+
if (digest !== manifest.native_sha256) {
|
|
94
|
+
throw new RuntimeNativeError(
|
|
95
|
+
'RUNTIME_NATIVE_MISSING',
|
|
96
|
+
`Ultra native SQLite driver digest does not match its manifest: ${nativeFile}`,
|
|
97
|
+
{ path: nativeFile, expected: manifest.native_sha256, actual: digest },
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (load) {
|
|
101
|
+
let Database;
|
|
102
|
+
try {
|
|
103
|
+
Database = require(require.resolve('better-sqlite3', {
|
|
104
|
+
paths: [path.join(root, 'node_modules')],
|
|
105
|
+
}));
|
|
106
|
+
const db = new Database(':memory:');
|
|
107
|
+
db.prepare('SELECT 1 AS ok').get();
|
|
108
|
+
db.close();
|
|
109
|
+
} catch (cause) {
|
|
110
|
+
throw new RuntimeNativeError(
|
|
111
|
+
'RUNTIME_ABI_MISMATCH',
|
|
112
|
+
`Ultra native SQLite driver cannot load in the active Node runtime: ${cause.message}`,
|
|
113
|
+
{ path: nativeFile, cause: cause.code || cause.message },
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
status: 'healthy',
|
|
119
|
+
manifest: file,
|
|
120
|
+
platform: actual.platform,
|
|
121
|
+
arch: actual.arch,
|
|
122
|
+
modules: actual.modules,
|
|
123
|
+
native_module: nativeFile,
|
|
124
|
+
native_sha256: digest,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = {
|
|
129
|
+
MANIFEST_FILE,
|
|
130
|
+
RuntimeNativeError,
|
|
131
|
+
inspectRuntimeNative,
|
|
132
|
+
};
|