ultra-builder-pro-cli 0.20.0 → 0.22.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/docs/research/README.md +26 -5
- package/.ultra-template/specs/discovery.md +1 -1
- package/.ultra-template/tasks/tasks.json +10 -3
- package/.ultra-template/templates/task-context.md +19 -0
- package/CHANGELOG.md +106 -0
- package/README.md +215 -41
- package/adapters/_shared/codex-assets.cjs +69 -11
- package/adapters/_shared/interaction-contract.cjs +36 -3
- package/adapters/_shared/runtime-assets.cjs +41 -1
- package/adapters/claude.js +7 -5
- package/adapters/codex.js +1 -1
- package/adapters/kimi.js +2 -8
- package/adapters/opencode.js +43 -30
- package/bin/orchestrator.js +64 -24
- package/docs/AGENT-CONTEXT.md +89 -49
- package/docs/ARCHITECTURE.md +112 -45
- package/docs/ARTIFACT-AUTHORITY.md +274 -0
- package/docs/COMMIT-HASH-BACKFILL.md +33 -90
- package/docs/DECISIONS.md +14 -6
- package/docs/LEGACY-CLI-CROSSWALK.md +16 -6
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +6 -4
- package/docs/ROADMAP.md +20 -6
- package/docs/RUNTIME-COMPAT-MATRIX.md +29 -16
- package/docs/STATE-DB-ACCESS-POLICY.md +43 -13
- package/docs/WORKFLOW-LIFECYCLE.md +105 -50
- package/hooks/README.md +10 -8
- package/hooks/active_task_context.py +105 -18
- package/hooks/context_spine.py +16 -4
- package/hooks/health_check.py +5 -4
- package/hooks/pre_stop_check.py +5 -2
- package/hooks/runtime_paths.py +396 -0
- package/hooks/subagent_tracker.py +5 -2
- package/hooks/workflow_checkpoint.py +16 -3
- package/hooks/workflow_context.py +11 -3
- package/hooks/workflow_resume.py +7 -4
- package/mcp-server/breadcrumb.cjs +20 -2
- package/mcp-server/lib/archive-journal.cjs +1026 -111
- package/mcp-server/lib/archive-mutation-worker.py +365 -0
- package/mcp-server/lib/artifact-registry.cjs +1127 -0
- package/mcp-server/lib/baseline-workflow.cjs +69 -28
- package/mcp-server/lib/change-packet.cjs +818 -0
- package/mcp-server/lib/change-workflow.cjs +932 -176
- package/mcp-server/lib/context-paths.cjs +142 -0
- package/mcp-server/lib/context-spine.cjs +511 -45
- package/mcp-server/lib/decision-dialogue.cjs +345 -3
- package/mcp-server/lib/delivery-transaction.cjs +471 -0
- package/mcp-server/lib/doctor-backup-worker.cjs +328 -0
- package/mcp-server/lib/doctor.cjs +401 -11
- package/mcp-server/lib/git-bootstrap.cjs +458 -47
- package/mcp-server/lib/init-project.cjs +240 -95
- package/mcp-server/lib/plan-publication-worker.cjs +830 -0
- package/mcp-server/lib/plan-store.cjs +700 -5
- package/mcp-server/lib/project-breadcrumb.cjs +23 -4
- package/mcp-server/lib/projector.cjs +211 -42
- package/mcp-server/lib/runtime-paths.cjs +1687 -0
- package/mcp-server/lib/safe-project-file.cjs +385 -0
- package/mcp-server/lib/spec-learning.cjs +28 -12
- package/mcp-server/lib/state-authority.cjs +19 -3
- package/mcp-server/lib/state-db.cjs +263 -9
- package/mcp-server/lib/state-ops.cjs +254 -50
- package/mcp-server/lib/task-ledger.cjs +1342 -0
- package/mcp-server/lib/telemetry.cjs +12 -3
- package/mcp-server/lib/workflow-state.cjs +610 -58
- package/mcp-server/server.cjs +423 -42
- package/orchestrator/daemon.cjs +2 -10
- package/orchestrator/parallel-orchestrator.cjs +314 -50
- package/orchestrator/planner/plan-builder.cjs +1 -1
- package/orchestrator/recovery.cjs +125 -7
- package/orchestrator/session-close-journal-worker.cjs +5 -0
- package/orchestrator/session-close-journal.cjs +1014 -0
- package/orchestrator/session-runner.cjs +679 -69
- package/orchestrator/worktree-manager.cjs +186 -35
- package/package.json +2 -3
- package/skills/cc-collab/SKILL.md +4 -1
- package/skills/codex-collab/SKILL.md +5 -2
- package/skills/ultra-change/SKILL.md +34 -8
- package/skills/ultra-deliver/SKILL.md +26 -11
- package/skills/ultra-dev/SKILL.md +52 -10
- package/skills/ultra-doctor/SKILL.md +16 -3
- package/skills/ultra-init/SKILL.md +22 -11
- package/skills/ultra-plan/SKILL.md +58 -18
- package/skills/ultra-research/SKILL.md +37 -35
- package/skills/ultra-review/SKILL.md +14 -8
- package/skills/ultra-status/SKILL.md +18 -9
- package/skills/ultra-test/SKILL.md +11 -5
- package/skills/ultra-think/SKILL.md +6 -2
- package/skills/ultra-think/references/decision-dialogue.md +17 -6
- package/skills/ultra-verify/SKILL.md +4 -1
- package/spec/cli-protocol.md +21 -7
- package/spec/mcp-tools.yaml +629 -44
- package/spec/schemas/change-delta.v1.schema.json +153 -0
- package/spec/schemas/context-file.v4.5.schema.json +1 -1
- package/spec/schemas/documentation-reconciliation.v1.schema.json +106 -0
- package/spec/schemas/mcp-tools.schema.json +1 -1
- package/spec/schemas/state-db.sql +51 -14
- package/spec/schemas/task-ledger.v1.schema.json +217 -0
- package/spec/schemas/tasks.v4.5.schema.json +1 -1
- package/ultra-tools/cli.cjs +3 -2
- package/ultra-tools/commands/db.cjs +76 -25
- package/ultra-tools/commands/migrate.cjs +1000 -82
- package/ultra-tools/commands/session.cjs +34 -8
- package/ultra-tools/commands/status.cjs +32 -6
- package/ultra-tools/commands/system.cjs +129 -22
- package/.ultra-template/tasks/contexts/TEMPLATE.md +0 -19
- package/commands/learn.md +0 -10
- package/orchestrator/skill-miner.cjs +0 -133
- package/output-styles/coding-vibes.md +0 -67
- package/output-styles/structural-thinking.md +0 -61
- package/skills/learn/SKILL.md +0 -58
|
@@ -8,6 +8,8 @@ const { spawnSync } = require('node:child_process');
|
|
|
8
8
|
const yaml = require('js-yaml');
|
|
9
9
|
const {
|
|
10
10
|
CORE_PUBLIC_SKILLS,
|
|
11
|
+
RUNTIME_SUPPORT_FILES,
|
|
12
|
+
RUNTIME_WORKER_FILES,
|
|
11
13
|
WORKFLOW_HOOK_FILES,
|
|
12
14
|
skillPolicy,
|
|
13
15
|
skillsForRuntime,
|
|
@@ -21,7 +23,10 @@ const {
|
|
|
21
23
|
removeTree,
|
|
22
24
|
writeAtomic,
|
|
23
25
|
} = require('./file-ops.cjs');
|
|
24
|
-
const {
|
|
26
|
+
const {
|
|
27
|
+
adaptInteractionGuidance,
|
|
28
|
+
interactionContract,
|
|
29
|
+
} = require('./interaction-contract.cjs');
|
|
25
30
|
|
|
26
31
|
const PLUGIN_NAME = 'ultra-builder-pro';
|
|
27
32
|
const MANAGED_MARKER = 'Managed by Ultra Builder Pro Codex adapter.';
|
|
@@ -104,7 +109,7 @@ function adaptSkillAsset(input, targetName, _rel) {
|
|
|
104
109
|
}
|
|
105
110
|
|
|
106
111
|
function adaptHostText(input, skillName = '') {
|
|
107
|
-
let text =
|
|
112
|
+
let text = adaptInteractionGuidance(input, 'codex');
|
|
108
113
|
text = text.replaceAll('codex-collab', 'cc-collab');
|
|
109
114
|
text = text.replaceAll('CLAUDE.md', 'AGENTS.md');
|
|
110
115
|
text = text.replaceAll('$CLAUDE_PLUGIN_ROOT/skills', '~/plugins/ultra-builder-pro/skills');
|
|
@@ -122,11 +127,6 @@ function adaptHostText(input, skillName = '') {
|
|
|
122
127
|
text = adaptCodexPrimaryText(text, skillName);
|
|
123
128
|
}
|
|
124
129
|
|
|
125
|
-
if (skillName === 'learn') {
|
|
126
|
-
text = text.replaceAll("current host's user skill directory", '`~/.agents/skills`');
|
|
127
|
-
text += `\n\n## Codex packaging requirement\n\nEach learned pattern must be a valid skill directory, not a loose Markdown file. The generated\n\`SKILL.md\` must start with only \`name\` and \`description\` frontmatter. Also create\n\`agents/openai.yaml\` with \`policy.allow_implicit_invocation: false\`. A new Codex task is required\nbefore the learned skill appears in discovery.\n`;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
130
|
return text;
|
|
131
131
|
}
|
|
132
132
|
|
|
@@ -135,7 +135,6 @@ function adaptedDescription(sourceDescription, targetName) {
|
|
|
135
135
|
'cc-collab': 'Ask Claude Code for an independent read-only analysis while Codex owns verification and synthesis. Use only when the user explicitly requests CC or Claude Code collaboration.',
|
|
136
136
|
'ultra-verify': 'Run Codex-primary cross-model verification with a read-only Claude Code advisor. Use only when the user explicitly requests independent model verification.',
|
|
137
137
|
'ultra-dev': 'Execute one authoritative Ultra task with Codex-native implementation, verification, and review. Use when a dependency-ready slice is ready for development.',
|
|
138
|
-
'learn': 'Extract one verified reusable workflow from the current Codex task into a portable user skill. Use when the user explicitly asks to preserve the method.',
|
|
139
138
|
};
|
|
140
139
|
return special[targetName] || adaptHostText(String(sourceDescription || `${titleCase(targetName)} workflow for Codex.`), targetName)
|
|
141
140
|
.replace(/\s+/g, ' ')
|
|
@@ -295,6 +294,16 @@ function copyHooks(repoRoot, pluginRoot) {
|
|
|
295
294
|
function buildMcpRuntime(repoRoot, pluginRoot, { runtime = 'codex' } = {}) {
|
|
296
295
|
const source = path.join(repoRoot, 'mcp-server', 'server.cjs');
|
|
297
296
|
const cliSource = path.join(repoRoot, 'adapters', '_shared', 'codex-ultra-tools-entry.cjs');
|
|
297
|
+
const runtimeWorkerSources = new Map([
|
|
298
|
+
[
|
|
299
|
+
'session-close-journal-worker.cjs',
|
|
300
|
+
path.join(repoRoot, 'orchestrator', 'session-close-journal-worker.cjs'),
|
|
301
|
+
],
|
|
302
|
+
[
|
|
303
|
+
'doctor-backup-worker.cjs',
|
|
304
|
+
path.join(repoRoot, 'mcp-server', 'lib', 'doctor-backup-worker.cjs'),
|
|
305
|
+
],
|
|
306
|
+
]);
|
|
298
307
|
const runtimeRoot = path.join(pluginRoot, 'runtime');
|
|
299
308
|
const buildRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ubp-codex-mcp-build-'));
|
|
300
309
|
let nccCli;
|
|
@@ -359,6 +368,54 @@ function buildMcpRuntime(repoRoot, pluginRoot, { runtime = 'codex' } = {}) {
|
|
|
359
368
|
removeTree(cliBuildRoot);
|
|
360
369
|
}
|
|
361
370
|
|
|
371
|
+
for (const workerFile of RUNTIME_WORKER_FILES) {
|
|
372
|
+
const workerSource = runtimeWorkerSources.get(workerFile);
|
|
373
|
+
if (!workerSource) {
|
|
374
|
+
throw new Error(`Ultra runtime worker source is not registered: ${workerFile}`);
|
|
375
|
+
}
|
|
376
|
+
const workerBuildRoot = fs.mkdtempSync(path.join(
|
|
377
|
+
os.tmpdir(),
|
|
378
|
+
`ubp-${path.basename(workerFile, '.cjs')}-build-`,
|
|
379
|
+
));
|
|
380
|
+
try {
|
|
381
|
+
const bundled = spawnSync(process.execPath, [
|
|
382
|
+
nccCli,
|
|
383
|
+
'build',
|
|
384
|
+
workerSource,
|
|
385
|
+
'-o',
|
|
386
|
+
workerBuildRoot,
|
|
387
|
+
'--no-cache',
|
|
388
|
+
'--quiet',
|
|
389
|
+
], {
|
|
390
|
+
cwd: repoRoot,
|
|
391
|
+
encoding: 'utf8',
|
|
392
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
393
|
+
});
|
|
394
|
+
if (bundled.error) throw bundled.error;
|
|
395
|
+
if (bundled.status !== 0) {
|
|
396
|
+
const detail = (bundled.stderr || bundled.stdout || '').trim();
|
|
397
|
+
throw new Error(
|
|
398
|
+
`ncc failed to bundle Ultra runtime worker ${workerFile}${detail ? `: ${detail}` : ''}`,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
const workerOutput = path.join(workerBuildRoot, 'index.cjs');
|
|
402
|
+
if (!fs.existsSync(workerOutput)) {
|
|
403
|
+
throw new Error(`ncc did not emit Ultra runtime worker ${workerFile}`);
|
|
404
|
+
}
|
|
405
|
+
fs.copyFileSync(workerOutput, path.join(runtimeRoot, workerFile));
|
|
406
|
+
} finally {
|
|
407
|
+
removeTree(workerBuildRoot);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
for (const supportFile of RUNTIME_SUPPORT_FILES) {
|
|
412
|
+
const sourceFile = path.join(repoRoot, 'mcp-server', 'lib', supportFile);
|
|
413
|
+
if (!fs.existsSync(sourceFile) || !fs.statSync(sourceFile).isFile()) {
|
|
414
|
+
throw new Error(`Ultra runtime support asset is missing: ${supportFile}`);
|
|
415
|
+
}
|
|
416
|
+
fs.copyFileSync(sourceFile, path.join(runtimeRoot, supportFile));
|
|
417
|
+
}
|
|
418
|
+
|
|
362
419
|
const runtimeBootstrap = runtime === 'kimi' ? `
|
|
363
420
|
const fs = require('node:fs');
|
|
364
421
|
const pluginRoot = path.resolve(__dirname, '..');
|
|
@@ -381,9 +438,6 @@ if (!process.env.UBP_ROOT_DIR) {
|
|
|
381
438
|
}
|
|
382
439
|
process.env.UBP_ROOT_DIR = rootDir;
|
|
383
440
|
}
|
|
384
|
-
if (!process.env.UBP_DB_PATH) {
|
|
385
|
-
process.env.UBP_DB_PATH = path.join(process.env.UBP_ROOT_DIR, '.ultra', 'state.db');
|
|
386
|
-
}
|
|
387
441
|
` : '';
|
|
388
442
|
|
|
389
443
|
const launcher = `'use strict';
|
|
@@ -409,6 +463,10 @@ main().catch((error) => {
|
|
|
409
463
|
path.join(repoRoot, 'mcp-server', 'hook-event.cjs'),
|
|
410
464
|
path.join(runtimeRoot, 'hook-event.cjs'),
|
|
411
465
|
);
|
|
466
|
+
fs.copyFileSync(
|
|
467
|
+
path.join(repoRoot, 'mcp-server', 'lib', 'plan-publication-worker.cjs'),
|
|
468
|
+
path.join(runtimeRoot, 'plan-publication-worker.cjs'),
|
|
469
|
+
);
|
|
412
470
|
|
|
413
471
|
const sourceToolsFile = path.join(repoRoot, 'spec', 'mcp-tools.yaml');
|
|
414
472
|
const upstreamManifest = yaml.load(fs.readFileSync(sourceToolsFile, 'utf8'));
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { PUBLIC_CAPABILITY_GRAPH } = require('./runtime-assets.cjs');
|
|
4
|
+
|
|
3
5
|
const SURFACES = Object.freeze({
|
|
4
6
|
claude: Object.freeze({
|
|
5
7
|
primary: 'AskUserQuestion',
|
|
@@ -23,12 +25,16 @@ const SURFACES = Object.freeze({
|
|
|
23
25
|
}),
|
|
24
26
|
});
|
|
25
27
|
|
|
28
|
+
const GENERIC_QUESTION_GUIDANCE =
|
|
29
|
+
/Use the host-native structured question surface declared by the installed interaction\s+contract(?: when it exists)?\./g;
|
|
30
|
+
|
|
26
31
|
function interactionContract(runtime) {
|
|
27
32
|
const question = SURFACES[runtime];
|
|
28
33
|
if (!question) throw new Error(`unsupported interaction runtime: ${runtime}`);
|
|
29
34
|
return {
|
|
30
|
-
schema_version: '1.
|
|
35
|
+
schema_version: '1.2',
|
|
31
36
|
runtime,
|
|
37
|
+
public_capability_graph: PUBLIC_CAPABILITY_GRAPH,
|
|
32
38
|
authority: {
|
|
33
39
|
user: [
|
|
34
40
|
'product_intent',
|
|
@@ -54,10 +60,13 @@ function interactionContract(runtime) {
|
|
|
54
60
|
semantic_selection_flow: [
|
|
55
61
|
'inspect',
|
|
56
62
|
'suggest',
|
|
57
|
-
'
|
|
63
|
+
'host_native_ask',
|
|
58
64
|
'normalize',
|
|
59
65
|
'persist',
|
|
66
|
+
'apply',
|
|
67
|
+
'read_back',
|
|
60
68
|
],
|
|
69
|
+
adapter_authority: 'none',
|
|
61
70
|
dependent_decisions: 'one_at_a_time',
|
|
62
71
|
independent_low_load_questions: { maximum: 3 },
|
|
63
72
|
explicit_current_intent: 'normalize_without_reconfirmation',
|
|
@@ -67,9 +76,11 @@ function interactionContract(runtime) {
|
|
|
67
76
|
},
|
|
68
77
|
persistence: {
|
|
69
78
|
accepted_intent: 'trust_as_current_authority',
|
|
79
|
+
accepted_intent_recall: 'breadcrumb_and_decision_list',
|
|
70
80
|
user_interaction_proof: 'not_required',
|
|
71
81
|
raw_prompt_or_transcript: 'never_store',
|
|
72
82
|
pending_question: 'store_only_when_recovery_requires_it',
|
|
83
|
+
application_evidence: 'record_applied_refs_when_another_authority_changes',
|
|
73
84
|
},
|
|
74
85
|
routing: {
|
|
75
86
|
semantic_recommendation_owner: 'host_model',
|
|
@@ -80,4 +91,26 @@ function interactionContract(runtime) {
|
|
|
80
91
|
};
|
|
81
92
|
}
|
|
82
93
|
|
|
83
|
-
|
|
94
|
+
function interactionPrompt(runtime) {
|
|
95
|
+
const contract = interactionContract(runtime);
|
|
96
|
+
const surface = contract.interaction.question_surface;
|
|
97
|
+
const host = {
|
|
98
|
+
claude: 'Claude Code',
|
|
99
|
+
codex: 'Codex',
|
|
100
|
+
opencode: 'OpenCode',
|
|
101
|
+
kimi: 'Kimi Code',
|
|
102
|
+
}[runtime];
|
|
103
|
+
const primary = runtime === 'opencode' ? `\`${surface.primary}\`` : surface.primary;
|
|
104
|
+
return `Use ${host} ${primary} when ${surface.availability.replaceAll('_', ' ')} permits it.`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function adaptInteractionGuidance(input, runtime) {
|
|
108
|
+
return String(input).replace(GENERIC_QUESTION_GUIDANCE, interactionPrompt(runtime));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = {
|
|
112
|
+
SURFACES,
|
|
113
|
+
adaptInteractionGuidance,
|
|
114
|
+
interactionContract,
|
|
115
|
+
interactionPrompt,
|
|
116
|
+
};
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
const CORE_PUBLIC_SKILLS = Object.freeze([
|
|
11
|
-
'learn',
|
|
12
11
|
'ultra-init',
|
|
13
12
|
'ultra-research',
|
|
14
13
|
'ultra-plan',
|
|
@@ -22,6 +21,34 @@ const CORE_PUBLIC_SKILLS = Object.freeze([
|
|
|
22
21
|
'ultra-doctor',
|
|
23
22
|
]);
|
|
24
23
|
|
|
24
|
+
const PUBLIC_CAPABILITY_MODES = Object.freeze({
|
|
25
|
+
'ultra-init': 'setup',
|
|
26
|
+
'ultra-research': 'workflow',
|
|
27
|
+
'ultra-plan': 'workflow',
|
|
28
|
+
'ultra-dev': 'workflow',
|
|
29
|
+
'ultra-test': 'workflow',
|
|
30
|
+
'ultra-review': 'workflow',
|
|
31
|
+
'ultra-deliver': 'workflow',
|
|
32
|
+
'ultra-status': 'read_only',
|
|
33
|
+
'ultra-think': 'reasoning',
|
|
34
|
+
'ultra-change': 'workflow',
|
|
35
|
+
'ultra-doctor': 'diagnostic',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const PUBLIC_CAPABILITY_GRAPH = Object.freeze(Object.fromEntries(
|
|
39
|
+
CORE_PUBLIC_SKILLS.map((name) => [
|
|
40
|
+
name,
|
|
41
|
+
Object.freeze({
|
|
42
|
+
mode: PUBLIC_CAPABILITY_MODES[name],
|
|
43
|
+
activation: 'explicit_only',
|
|
44
|
+
next_capability_source: 'mcp_allowed_transitions',
|
|
45
|
+
recommendation_owner: 'host_model',
|
|
46
|
+
selection_owner: 'user',
|
|
47
|
+
automatic_invocation: false,
|
|
48
|
+
}),
|
|
49
|
+
]),
|
|
50
|
+
));
|
|
51
|
+
|
|
25
52
|
const INTERNAL_AGENT_SKILLS = Object.freeze([
|
|
26
53
|
'code-review-expert',
|
|
27
54
|
'security-rules',
|
|
@@ -56,12 +83,22 @@ const WORKFLOW_HOOK_FILES = Object.freeze([
|
|
|
56
83
|
'context_spine.py',
|
|
57
84
|
'health_check.py',
|
|
58
85
|
'pre_stop_check.py',
|
|
86
|
+
'runtime_paths.py',
|
|
59
87
|
'subagent_tracker.py',
|
|
60
88
|
'workflow_checkpoint.py',
|
|
61
89
|
'workflow_context.py',
|
|
62
90
|
'workflow_resume.py',
|
|
63
91
|
]);
|
|
64
92
|
|
|
93
|
+
const RUNTIME_WORKER_FILES = Object.freeze([
|
|
94
|
+
'session-close-journal-worker.cjs',
|
|
95
|
+
'doctor-backup-worker.cjs',
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
const RUNTIME_SUPPORT_FILES = Object.freeze([
|
|
99
|
+
'archive-mutation-worker.py',
|
|
100
|
+
]);
|
|
101
|
+
|
|
65
102
|
function skillsForRuntime(runtime) {
|
|
66
103
|
const collab = COLLAB_SKILLS_BY_RUNTIME[runtime];
|
|
67
104
|
if (!collab) throw new Error(`unsupported Ultra runtime: ${runtime}`);
|
|
@@ -84,11 +121,14 @@ function skillPolicy(name) {
|
|
|
84
121
|
|
|
85
122
|
module.exports = {
|
|
86
123
|
CORE_PUBLIC_SKILLS,
|
|
124
|
+
PUBLIC_CAPABILITY_GRAPH,
|
|
87
125
|
INTERNAL_AGENT_SKILLS,
|
|
88
126
|
SUPPORTED_RUNTIMES,
|
|
89
127
|
COLLAB_SKILLS_BY_RUNTIME,
|
|
90
128
|
MCP_DEPENDENT_SKILLS,
|
|
129
|
+
RUNTIME_SUPPORT_FILES,
|
|
91
130
|
WORKFLOW_HOOK_FILES,
|
|
131
|
+
RUNTIME_WORKER_FILES,
|
|
92
132
|
isSupportedRuntime,
|
|
93
133
|
skillPolicy,
|
|
94
134
|
skillsForRuntime,
|
package/adapters/claude.js
CHANGED
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
} = require('./_shared/file-ops.cjs');
|
|
17
17
|
const { buildMcpRuntime } = require('./_shared/codex-assets.cjs');
|
|
18
18
|
const { parse: parseFrontmatter, serialize: serializeFrontmatter } = require('./_shared/frontmatter.cjs');
|
|
19
|
+
const { adaptInteractionGuidance } = require('./_shared/interaction-contract.cjs');
|
|
19
20
|
const provenance = require('./_shared/provenance.cjs');
|
|
20
21
|
const {
|
|
21
22
|
CORE_PUBLIC_SKILLS,
|
|
@@ -55,13 +56,14 @@ function copySkills(repoRoot, target, names) {
|
|
|
55
56
|
if (!fs.existsSync(source)) throw new Error(`missing allowlisted Claude skill: ${name}`);
|
|
56
57
|
const files = copyTree(source, path.join(target, 'skills', name), {
|
|
57
58
|
transform(buf, rel) {
|
|
58
|
-
if (rel !== 'SKILL.md')
|
|
59
|
+
if (rel !== 'SKILL.md') {
|
|
60
|
+
return rel.endsWith('.md')
|
|
61
|
+
? Buffer.from(adaptInteractionGuidance(buf.toString('utf8'), 'claude'))
|
|
62
|
+
: buf;
|
|
63
|
+
}
|
|
59
64
|
const { fm, body } = parseFrontmatter(buf.toString('utf8'));
|
|
60
65
|
if (!fm) throw new Error(`missing frontmatter in Claude skill: ${name}`);
|
|
61
|
-
let adaptedBody = body;
|
|
62
|
-
if (name === 'learn') {
|
|
63
|
-
adaptedBody = adaptedBody.replaceAll("current host's user skill directory", '`~/.claude/skills`');
|
|
64
|
-
}
|
|
66
|
+
let adaptedBody = adaptInteractionGuidance(body, 'claude');
|
|
65
67
|
if (name === 'ultra-review') {
|
|
66
68
|
adaptedBody = adaptedBody.replace(
|
|
67
69
|
/the current host's native bounded-worker\s+mechanism/g,
|
package/adapters/codex.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* skills/** -> ~/plugins/ultra-builder-pro/skills/**
|
|
9
9
|
* agents/*.md -> ~/.codex/agents/*.toml
|
|
10
10
|
* hooks/*.py -> plugin hooks with a Codex wire adapter
|
|
11
|
-
* mcp-server -> plugin .mcp.json (project cwd owns .ultra/state.db)
|
|
11
|
+
* mcp-server -> plugin .mcp.json (project cwd owns .ultra/.runtime/state.db)
|
|
12
12
|
* plugin marketplace -> ~/.agents/plugins/marketplace.json
|
|
13
13
|
*
|
|
14
14
|
* Deprecated ~/.codex/prompts and ~/.codex/skills projection is intentionally
|
package/adapters/kimi.js
CHANGED
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
} = require('./_shared/file-ops.cjs');
|
|
17
17
|
const { buildMcpRuntime } = require('./_shared/codex-assets.cjs');
|
|
18
18
|
const { parse: parseFm, serialize: serializeFm } = require('./_shared/frontmatter.cjs');
|
|
19
|
+
const { adaptInteractionGuidance } = require('./_shared/interaction-contract.cjs');
|
|
19
20
|
const provenance = require('./_shared/provenance.cjs');
|
|
20
21
|
const {
|
|
21
22
|
CORE_PUBLIC_SKILLS,
|
|
@@ -66,7 +67,7 @@ function loadRegistry(file) {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
function kimiTextTransform(input, assetName = '') {
|
|
69
|
-
let text =
|
|
70
|
+
let text = adaptInteractionGuidance(input, 'kimi');
|
|
70
71
|
text = text.replaceAll('$CLAUDE_PLUGIN_ROOT', '$KIMI_PLUGIN_ROOT');
|
|
71
72
|
text = text.replaceAll('~/.claude/skills', '~/.kimi-code/skills');
|
|
72
73
|
text = text.replaceAll('~/.claude/hooks', '~/.kimi-code/hooks');
|
|
@@ -99,17 +100,10 @@ function kimiTextTransform(input, assetName = '') {
|
|
|
99
100
|
'Kimi `AgentSwarm` for parallel reviewers or one foreground Kimi `Agent` for a single reviewer, using the worker prompt files under `$KIMI_PLUGIN_ROOT/agents/`',
|
|
100
101
|
);
|
|
101
102
|
}
|
|
102
|
-
if (assetName === 'learn') {
|
|
103
|
-
text = text.replaceAll("current host's user skill directory", '`~/.kimi-code/skills`');
|
|
104
|
-
}
|
|
105
103
|
text = text.replace(
|
|
106
104
|
/(^|[\s`("'])\/ultra-(?!builder-pro:)([a-z][a-z0-9-]*)/gm,
|
|
107
105
|
'$1/ultra-builder-pro:ultra-$2',
|
|
108
106
|
);
|
|
109
|
-
text = text.replace(
|
|
110
|
-
/(^|[\s`("'])\/learn(?=$|[\s`),.;:])/gm,
|
|
111
|
-
'$1/ultra-builder-pro:learn',
|
|
112
|
-
);
|
|
113
107
|
text = text.replace(
|
|
114
108
|
/(^|[\s`("'])\/clear(?=$|[\s`),.;:])/gm,
|
|
115
109
|
'$1/new',
|
package/adapters/opencode.js
CHANGED
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
writeAtomic,
|
|
16
16
|
} = require('./_shared/file-ops.cjs');
|
|
17
17
|
const { buildMcpRuntime } = require('./_shared/codex-assets.cjs');
|
|
18
|
+
const { adaptInteractionGuidance } = require('./_shared/interaction-contract.cjs');
|
|
18
19
|
const provenance = require('./_shared/provenance.cjs');
|
|
19
20
|
const {
|
|
20
21
|
CORE_PUBLIC_SKILLS,
|
|
@@ -67,7 +68,7 @@ function lowercaseFrontmatterTransform(buf, relPath) {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
function openCodeTextTransform(input, assetName = '') {
|
|
70
|
-
let text =
|
|
71
|
+
let text = adaptInteractionGuidance(input, 'opencode');
|
|
71
72
|
text = text.replaceAll('$CLAUDE_PLUGIN_ROOT/skills', '~/.config/opencode/skills');
|
|
72
73
|
text = text.replaceAll('~/.claude/skills', '~/.config/opencode/skills');
|
|
73
74
|
text = text.replaceAll('~/.claude/hooks', '~/.config/opencode/.ultra-builder-pro/hooks');
|
|
@@ -96,9 +97,6 @@ function openCodeTextTransform(input, assetName = '') {
|
|
|
96
97
|
'the OpenCode `task` tool using the installed bounded review agents',
|
|
97
98
|
);
|
|
98
99
|
}
|
|
99
|
-
if (assetName === 'learn') {
|
|
100
|
-
text = text.replaceAll("current host's user skill directory", '`~/.config/opencode/skills`');
|
|
101
|
-
}
|
|
102
100
|
return text;
|
|
103
101
|
}
|
|
104
102
|
|
|
@@ -335,48 +333,41 @@ function preflightAssets(repoRoot, target) {
|
|
|
335
333
|
|
|
336
334
|
function pluginSource() {
|
|
337
335
|
return `// Managed by Ultra Builder Pro. Rebuild through the adapter; do not edit in place.
|
|
338
|
-
import fs from "node:fs";
|
|
339
336
|
import path from "node:path";
|
|
340
337
|
import { execFileSync } from "node:child_process";
|
|
341
338
|
import { fileURLToPath } from "node:url";
|
|
342
339
|
|
|
343
|
-
const
|
|
340
|
+
const TEAM_TASK_LEDGER = ".ultra/tasks/tasks.json";
|
|
341
|
+
const LIVE_TASK_PROJECTION = ".ultra/.runtime/projections/tasks.json";
|
|
344
342
|
const NODE_BINARY = ${JSON.stringify(process.execPath)};
|
|
345
343
|
const PLUGIN_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
346
344
|
const BREADCRUMB_CLI = path.resolve(
|
|
347
345
|
PLUGIN_DIR, "..", ${JSON.stringify(BUNDLE_DIR)}, "runtime", "breadcrumb.cjs",
|
|
348
346
|
);
|
|
349
347
|
|
|
350
|
-
function findUltraRoot(directory) {
|
|
351
|
-
let current = path.resolve(directory);
|
|
352
|
-
while (true) {
|
|
353
|
-
const stateDb = path.join(current, ".ultra", "state.db");
|
|
354
|
-
if (fs.existsSync(stateDb) && fs.statSync(stateDb).isFile()) return current;
|
|
355
|
-
const parent = path.dirname(current);
|
|
356
|
-
if (parent === current) return null;
|
|
357
|
-
current = parent;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
348
|
function readUltraContext(directory) {
|
|
362
|
-
const root = findUltraRoot(directory);
|
|
363
|
-
if (!root) return null;
|
|
364
349
|
try {
|
|
365
|
-
const raw = execFileSync(NODE_BINARY, [BREADCRUMB_CLI,
|
|
366
|
-
cwd:
|
|
350
|
+
const raw = execFileSync(NODE_BINARY, [BREADCRUMB_CLI, "--discover", directory], {
|
|
351
|
+
cwd: directory,
|
|
367
352
|
encoding: "utf8",
|
|
368
353
|
stdio: ["ignore", "pipe", "pipe"],
|
|
369
354
|
timeout: 5000,
|
|
370
355
|
});
|
|
371
356
|
const value = JSON.parse(raw);
|
|
357
|
+
const root = value?.root;
|
|
358
|
+
if (typeof root !== "string" || !root) return null;
|
|
372
359
|
// A canonical breadcrumb is injectable only while DB authority proves an active workflow.
|
|
373
360
|
if (!value?.breadcrumb || !value.breadcrumb.workflow
|
|
374
361
|
|| typeof value.text !== "string" || !value.text) {
|
|
375
362
|
return { root, breadcrumb: null, text: null };
|
|
376
363
|
}
|
|
377
364
|
return { root, breadcrumb: value.breadcrumb, text: value.text };
|
|
378
|
-
} catch {
|
|
379
|
-
|
|
365
|
+
} catch (error) {
|
|
366
|
+
const detail = String(error?.stderr || error?.message || error);
|
|
367
|
+
if (/RUNTIME_(?:STATE_CONFLICT|PATH_UNSAFE|ORPHAN_SIDECAR|ROOT_INVALID|AUTHORITY_MISMATCH)|both legacy .*runtime.*state\\.db/i.test(detail)) {
|
|
368
|
+
throw new Error(detail.trim());
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
380
371
|
}
|
|
381
372
|
}
|
|
382
373
|
|
|
@@ -392,11 +383,30 @@ function targetPaths(tool, args) {
|
|
|
392
383
|
return [...new Set(paths)];
|
|
393
384
|
}
|
|
394
385
|
|
|
395
|
-
function
|
|
386
|
+
function managedTaskRootForTarget(baseRoot, candidate) {
|
|
396
387
|
const target = path.isAbsolute(candidate)
|
|
397
388
|
? path.resolve(candidate)
|
|
398
|
-
: path.resolve(
|
|
399
|
-
|
|
389
|
+
: path.resolve(baseRoot, candidate);
|
|
390
|
+
if (target === path.resolve(baseRoot, TEAM_TASK_LEDGER)
|
|
391
|
+
|| target === path.resolve(baseRoot, LIVE_TASK_PROJECTION)) return baseRoot;
|
|
392
|
+
if (path.basename(target) !== "tasks.json") return null;
|
|
393
|
+
const parent = path.dirname(target);
|
|
394
|
+
if (path.basename(parent) === "tasks"
|
|
395
|
+
&& path.basename(path.dirname(parent)) === ".ultra") {
|
|
396
|
+
return path.dirname(path.dirname(parent));
|
|
397
|
+
}
|
|
398
|
+
if (path.basename(parent) === "projections"
|
|
399
|
+
&& path.basename(path.dirname(parent)) === ".runtime"
|
|
400
|
+
&& path.basename(path.dirname(path.dirname(parent))) === ".ultra") {
|
|
401
|
+
return path.dirname(path.dirname(path.dirname(parent)));
|
|
402
|
+
}
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function protectsManagedTaskFile(baseRoot, candidate) {
|
|
407
|
+
const targetRoot = managedTaskRootForTarget(baseRoot, candidate);
|
|
408
|
+
if (!targetRoot) return false;
|
|
409
|
+
return Boolean(readUltraContext(targetRoot));
|
|
400
410
|
}
|
|
401
411
|
|
|
402
412
|
export const UltraBuilderProPlugin = async ({ directory, worktree }) => {
|
|
@@ -417,13 +427,16 @@ export const UltraBuilderProPlugin = async ({ directory, worktree }) => {
|
|
|
417
427
|
},
|
|
418
428
|
"tool.execute.before": async (input, output) => {
|
|
419
429
|
refresh();
|
|
420
|
-
if (!active) return;
|
|
421
430
|
const tool = String(input?.tool ?? "").toLowerCase();
|
|
422
431
|
if (!["write", "edit", "apply_patch"].includes(tool)) return;
|
|
423
|
-
if (targetPaths(tool, output?.args).some(
|
|
432
|
+
if (targetPaths(tool, output?.args).some(
|
|
433
|
+
(candidate) => protectsManagedTaskFile(root, candidate),
|
|
434
|
+
)) {
|
|
424
435
|
throw new Error(
|
|
425
|
-
"Ultra Builder Pro refused a direct write to .
|
|
426
|
-
".ultra/
|
|
436
|
+
"Ultra Builder Pro refused a direct write to an MCP-managed task file. " +
|
|
437
|
+
".ultra/tasks/tasks.json is the MCP-published team checkpoint and " +
|
|
438
|
+
".ultra/.runtime/projections/tasks.json is the checkout-local DB view. " +
|
|
439
|
+
"Use the Ultra MCP task or task.ledger tools."
|
|
427
440
|
);
|
|
428
441
|
}
|
|
429
442
|
},
|