ultra-builder-pro-cli 0.20.0 → 0.21.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 +26 -5
- package/.ultra-template/specs/discovery.md +1 -1
- package/.ultra-template/tasks/tasks.json +1 -1
- package/CHANGELOG.md +66 -0
- package/README.md +54 -24
- 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 +30 -28
- package/bin/orchestrator.js +64 -24
- package/docs/AGENT-CONTEXT.md +74 -43
- package/docs/ARCHITECTURE.md +63 -31
- package/docs/ARTIFACT-AUTHORITY.md +234 -0
- package/docs/DECISIONS.md +10 -4
- package/docs/LEGACY-CLI-CROSSWALK.md +6 -4
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +4 -3
- package/docs/ROADMAP.md +19 -5
- package/docs/RUNTIME-COMPAT-MATRIX.md +24 -13
- package/docs/STATE-DB-ACCESS-POLICY.md +11 -9
- package/docs/WORKFLOW-LIFECYCLE.md +70 -39
- package/hooks/README.md +6 -5
- package/hooks/active_task_context.py +97 -12
- 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 +1126 -0
- package/mcp-server/lib/baseline-workflow.cjs +9 -4
- 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 +111 -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 +373 -11
- package/mcp-server/lib/git-bootstrap.cjs +459 -47
- package/mcp-server/lib/init-project.cjs +190 -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 +174 -40
- 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-db.cjs +263 -9
- package/mcp-server/lib/state-ops.cjs +248 -49
- package/mcp-server/lib/telemetry.cjs +12 -3
- package/mcp-server/lib/workflow-state.cjs +610 -58
- package/mcp-server/server.cjs +278 -33
- 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 +26 -8
- package/skills/ultra-deliver/SKILL.md +23 -11
- package/skills/ultra-dev/SKILL.md +33 -6
- package/skills/ultra-doctor/SKILL.md +6 -2
- package/skills/ultra-init/SKILL.md +4 -2
- package/skills/ultra-plan/SKILL.md +49 -16
- package/skills/ultra-research/SKILL.md +30 -27
- package/skills/ultra-review/SKILL.md +9 -5
- package/skills/ultra-status/SKILL.md +9 -4
- package/skills/ultra-test/SKILL.md +10 -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 +18 -7
- package/spec/mcp-tools.yaml +502 -44
- package/spec/schemas/change-delta.v1.schema.json +153 -0
- 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 +50 -13
- 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 +995 -80
- 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/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
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
|
|
6
|
+
const CONTEXT_ROOT_RELATIVE = '.ultra/tasks/contexts';
|
|
7
|
+
const GENERATED_BY = 'ultra-projector';
|
|
8
|
+
|
|
9
|
+
class ContextPathError extends Error {
|
|
10
|
+
constructor(message, details = undefined) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'ContextPathError';
|
|
13
|
+
this.code = 'CONTEXT_PATH_INVALID';
|
|
14
|
+
if (details !== undefined) this.details = details;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function invalid(value, reason) {
|
|
19
|
+
throw new ContextPathError(`invalid task context path ${JSON.stringify(value)}: ${reason}`, {
|
|
20
|
+
path: value == null ? null : String(value),
|
|
21
|
+
reason,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function assertNoSymlinkAncestors(root, relative, original) {
|
|
26
|
+
let current = root;
|
|
27
|
+
const components = relative.split('/');
|
|
28
|
+
for (let index = 0; index < components.length; index += 1) {
|
|
29
|
+
current = path.join(current, components[index]);
|
|
30
|
+
let stat;
|
|
31
|
+
try {
|
|
32
|
+
stat = fs.lstatSync(current);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (error.code === 'ENOENT') continue;
|
|
35
|
+
invalid(original, `cannot inspect ${components.slice(0, index + 1).join('/')}`);
|
|
36
|
+
}
|
|
37
|
+
if (stat.isSymbolicLink()) {
|
|
38
|
+
invalid(original, `symbolic link ancestor is not allowed: ${components.slice(0, index + 1).join('/')}`);
|
|
39
|
+
}
|
|
40
|
+
if (index < components.length - 1 && !stat.isDirectory()) {
|
|
41
|
+
invalid(original, `non-directory ancestor: ${components.slice(0, index + 1).join('/')}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function assertRealContainment(root, file, original) {
|
|
47
|
+
if (!fs.existsSync(root)) return;
|
|
48
|
+
const realRoot = fs.realpathSync(root);
|
|
49
|
+
let existing = file;
|
|
50
|
+
while (!fs.existsSync(existing) && existing !== root) existing = path.dirname(existing);
|
|
51
|
+
const realExisting = fs.realpathSync(existing);
|
|
52
|
+
if (realExisting !== realRoot && !realExisting.startsWith(`${realRoot}${path.sep}`)) {
|
|
53
|
+
invalid(original, 'resolved path escapes the project root');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolveContextPath(rootDir, value, {
|
|
58
|
+
taskId,
|
|
59
|
+
allowLegacyAliases = false,
|
|
60
|
+
} = {}) {
|
|
61
|
+
const root = path.resolve(rootDir || '.');
|
|
62
|
+
const defaultValue = taskId == null
|
|
63
|
+
? null
|
|
64
|
+
: `${CONTEXT_ROOT_RELATIVE}/task-${taskId}.md`;
|
|
65
|
+
const original = value || defaultValue;
|
|
66
|
+
if (typeof original !== 'string' || original.trim() === '' || original.includes('\0')) {
|
|
67
|
+
invalid(original, 'a non-empty path is required');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let raw;
|
|
71
|
+
if (path.isAbsolute(original)) {
|
|
72
|
+
raw = path.relative(root, path.resolve(original)).split(path.sep).join('/');
|
|
73
|
+
if (raw === '..' || raw.startsWith('../')) invalid(original, 'absolute path escapes the project');
|
|
74
|
+
} else {
|
|
75
|
+
raw = original.trim().replaceAll('\\', '/').replace(/^\.\//, '');
|
|
76
|
+
}
|
|
77
|
+
if (raw.split('/').includes('..')) invalid(original, 'parent traversal is not allowed');
|
|
78
|
+
|
|
79
|
+
if (allowLegacyAliases && raw.startsWith('contexts/')) {
|
|
80
|
+
raw = `.ultra/tasks/${raw}`;
|
|
81
|
+
} else if (allowLegacyAliases && !raw.includes('/')) {
|
|
82
|
+
raw = `${CONTEXT_ROOT_RELATIVE}/${raw}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const relative = path.posix.normalize(raw);
|
|
86
|
+
if (!relative.startsWith(`${CONTEXT_ROOT_RELATIVE}/`)
|
|
87
|
+
|| relative === `${CONTEXT_ROOT_RELATIVE}/`
|
|
88
|
+
|| relative.endsWith('/')) {
|
|
89
|
+
invalid(original, `path must be a file below ${CONTEXT_ROOT_RELATIVE}`);
|
|
90
|
+
}
|
|
91
|
+
const file = path.resolve(root, ...relative.split('/'));
|
|
92
|
+
const contextRoot = path.resolve(root, ...CONTEXT_ROOT_RELATIVE.split('/'));
|
|
93
|
+
if (!file.startsWith(`${contextRoot}${path.sep}`)) {
|
|
94
|
+
invalid(original, `path escapes ${CONTEXT_ROOT_RELATIVE}`);
|
|
95
|
+
}
|
|
96
|
+
assertNoSymlinkAncestors(root, relative, original);
|
|
97
|
+
assertRealContainment(root, file, original);
|
|
98
|
+
return { root, contextRoot, relative, file };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isGeneratedContextContents(contents) {
|
|
102
|
+
return new RegExp(`^generated_by:\\s*${GENERATED_BY}\\s*$`, 'm').test(String(contents));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = {
|
|
106
|
+
CONTEXT_ROOT_RELATIVE,
|
|
107
|
+
GENERATED_BY,
|
|
108
|
+
ContextPathError,
|
|
109
|
+
isGeneratedContextContents,
|
|
110
|
+
resolveContextPath,
|
|
111
|
+
};
|