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.
Files changed (109) hide show
  1. package/.ultra-template/docs/research/README.md +26 -5
  2. package/.ultra-template/specs/discovery.md +1 -1
  3. package/.ultra-template/tasks/tasks.json +10 -3
  4. package/.ultra-template/templates/task-context.md +19 -0
  5. package/CHANGELOG.md +106 -0
  6. package/README.md +215 -41
  7. package/adapters/_shared/codex-assets.cjs +69 -11
  8. package/adapters/_shared/interaction-contract.cjs +36 -3
  9. package/adapters/_shared/runtime-assets.cjs +41 -1
  10. package/adapters/claude.js +7 -5
  11. package/adapters/codex.js +1 -1
  12. package/adapters/kimi.js +2 -8
  13. package/adapters/opencode.js +43 -30
  14. package/bin/orchestrator.js +64 -24
  15. package/docs/AGENT-CONTEXT.md +89 -49
  16. package/docs/ARCHITECTURE.md +112 -45
  17. package/docs/ARTIFACT-AUTHORITY.md +274 -0
  18. package/docs/COMMIT-HASH-BACKFILL.md +33 -90
  19. package/docs/DECISIONS.md +14 -6
  20. package/docs/LEGACY-CLI-CROSSWALK.md +16 -6
  21. package/docs/PLUGIN-ISOLATION-CONTRACT.md +6 -4
  22. package/docs/ROADMAP.md +20 -6
  23. package/docs/RUNTIME-COMPAT-MATRIX.md +29 -16
  24. package/docs/STATE-DB-ACCESS-POLICY.md +43 -13
  25. package/docs/WORKFLOW-LIFECYCLE.md +105 -50
  26. package/hooks/README.md +10 -8
  27. package/hooks/active_task_context.py +105 -18
  28. package/hooks/context_spine.py +16 -4
  29. package/hooks/health_check.py +5 -4
  30. package/hooks/pre_stop_check.py +5 -2
  31. package/hooks/runtime_paths.py +396 -0
  32. package/hooks/subagent_tracker.py +5 -2
  33. package/hooks/workflow_checkpoint.py +16 -3
  34. package/hooks/workflow_context.py +11 -3
  35. package/hooks/workflow_resume.py +7 -4
  36. package/mcp-server/breadcrumb.cjs +20 -2
  37. package/mcp-server/lib/archive-journal.cjs +1026 -111
  38. package/mcp-server/lib/archive-mutation-worker.py +365 -0
  39. package/mcp-server/lib/artifact-registry.cjs +1127 -0
  40. package/mcp-server/lib/baseline-workflow.cjs +69 -28
  41. package/mcp-server/lib/change-packet.cjs +818 -0
  42. package/mcp-server/lib/change-workflow.cjs +932 -176
  43. package/mcp-server/lib/context-paths.cjs +142 -0
  44. package/mcp-server/lib/context-spine.cjs +511 -45
  45. package/mcp-server/lib/decision-dialogue.cjs +345 -3
  46. package/mcp-server/lib/delivery-transaction.cjs +471 -0
  47. package/mcp-server/lib/doctor-backup-worker.cjs +328 -0
  48. package/mcp-server/lib/doctor.cjs +401 -11
  49. package/mcp-server/lib/git-bootstrap.cjs +458 -47
  50. package/mcp-server/lib/init-project.cjs +240 -95
  51. package/mcp-server/lib/plan-publication-worker.cjs +830 -0
  52. package/mcp-server/lib/plan-store.cjs +700 -5
  53. package/mcp-server/lib/project-breadcrumb.cjs +23 -4
  54. package/mcp-server/lib/projector.cjs +211 -42
  55. package/mcp-server/lib/runtime-paths.cjs +1687 -0
  56. package/mcp-server/lib/safe-project-file.cjs +385 -0
  57. package/mcp-server/lib/spec-learning.cjs +28 -12
  58. package/mcp-server/lib/state-authority.cjs +19 -3
  59. package/mcp-server/lib/state-db.cjs +263 -9
  60. package/mcp-server/lib/state-ops.cjs +254 -50
  61. package/mcp-server/lib/task-ledger.cjs +1342 -0
  62. package/mcp-server/lib/telemetry.cjs +12 -3
  63. package/mcp-server/lib/workflow-state.cjs +610 -58
  64. package/mcp-server/server.cjs +423 -42
  65. package/orchestrator/daemon.cjs +2 -10
  66. package/orchestrator/parallel-orchestrator.cjs +314 -50
  67. package/orchestrator/planner/plan-builder.cjs +1 -1
  68. package/orchestrator/recovery.cjs +125 -7
  69. package/orchestrator/session-close-journal-worker.cjs +5 -0
  70. package/orchestrator/session-close-journal.cjs +1014 -0
  71. package/orchestrator/session-runner.cjs +679 -69
  72. package/orchestrator/worktree-manager.cjs +186 -35
  73. package/package.json +2 -3
  74. package/skills/cc-collab/SKILL.md +4 -1
  75. package/skills/codex-collab/SKILL.md +5 -2
  76. package/skills/ultra-change/SKILL.md +34 -8
  77. package/skills/ultra-deliver/SKILL.md +26 -11
  78. package/skills/ultra-dev/SKILL.md +52 -10
  79. package/skills/ultra-doctor/SKILL.md +16 -3
  80. package/skills/ultra-init/SKILL.md +22 -11
  81. package/skills/ultra-plan/SKILL.md +58 -18
  82. package/skills/ultra-research/SKILL.md +37 -35
  83. package/skills/ultra-review/SKILL.md +14 -8
  84. package/skills/ultra-status/SKILL.md +18 -9
  85. package/skills/ultra-test/SKILL.md +11 -5
  86. package/skills/ultra-think/SKILL.md +6 -2
  87. package/skills/ultra-think/references/decision-dialogue.md +17 -6
  88. package/skills/ultra-verify/SKILL.md +4 -1
  89. package/spec/cli-protocol.md +21 -7
  90. package/spec/mcp-tools.yaml +629 -44
  91. package/spec/schemas/change-delta.v1.schema.json +153 -0
  92. package/spec/schemas/context-file.v4.5.schema.json +1 -1
  93. package/spec/schemas/documentation-reconciliation.v1.schema.json +106 -0
  94. package/spec/schemas/mcp-tools.schema.json +1 -1
  95. package/spec/schemas/state-db.sql +51 -14
  96. package/spec/schemas/task-ledger.v1.schema.json +217 -0
  97. package/spec/schemas/tasks.v4.5.schema.json +1 -1
  98. package/ultra-tools/cli.cjs +3 -2
  99. package/ultra-tools/commands/db.cjs +76 -25
  100. package/ultra-tools/commands/migrate.cjs +1000 -82
  101. package/ultra-tools/commands/session.cjs +34 -8
  102. package/ultra-tools/commands/status.cjs +32 -6
  103. package/ultra-tools/commands/system.cjs +129 -22
  104. package/.ultra-template/tasks/contexts/TEMPLATE.md +0 -19
  105. package/commands/learn.md +0 -10
  106. package/orchestrator/skill-miner.cjs +0 -133
  107. package/output-styles/coding-vibes.md +0 -67
  108. package/output-styles/structural-thinking.md +0 -61
  109. package/skills/learn/SKILL.md +0 -58
@@ -0,0 +1,142 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ const CONTEXT_ROOT_RELATIVE = '.ultra/.runtime/projections/contexts';
7
+ const LEGACY_CONTEXT_ROOT_RELATIVE = '.ultra/tasks/contexts';
8
+ const GENERATED_BY = 'ultra-projector';
9
+
10
+ class ContextPathError extends Error {
11
+ constructor(message, details = undefined) {
12
+ super(message);
13
+ this.name = 'ContextPathError';
14
+ this.code = 'CONTEXT_PATH_INVALID';
15
+ if (details !== undefined) this.details = details;
16
+ }
17
+ }
18
+
19
+ function invalid(value, reason) {
20
+ throw new ContextPathError(`invalid task context path ${JSON.stringify(value)}: ${reason}`, {
21
+ path: value == null ? null : String(value),
22
+ reason,
23
+ });
24
+ }
25
+
26
+ function assertNoSymlinkAncestors(root, relative, original) {
27
+ let current = root;
28
+ const components = relative.split('/');
29
+ for (let index = 0; index < components.length; index += 1) {
30
+ current = path.join(current, components[index]);
31
+ let stat;
32
+ try {
33
+ stat = fs.lstatSync(current);
34
+ } catch (error) {
35
+ if (error.code === 'ENOENT') continue;
36
+ invalid(original, `cannot inspect ${components.slice(0, index + 1).join('/')}`);
37
+ }
38
+ if (stat.isSymbolicLink()) {
39
+ invalid(original, `symbolic link ancestor is not allowed: ${components.slice(0, index + 1).join('/')}`);
40
+ }
41
+ if (index < components.length - 1 && !stat.isDirectory()) {
42
+ invalid(original, `non-directory ancestor: ${components.slice(0, index + 1).join('/')}`);
43
+ }
44
+ }
45
+ }
46
+
47
+ function assertRealContainment(root, file, original) {
48
+ if (!fs.existsSync(root)) return;
49
+ const realRoot = fs.realpathSync(root);
50
+ let existing = file;
51
+ while (!fs.existsSync(existing) && existing !== root) existing = path.dirname(existing);
52
+ const realExisting = fs.realpathSync(existing);
53
+ if (realExisting !== realRoot && !realExisting.startsWith(`${realRoot}${path.sep}`)) {
54
+ invalid(original, 'resolved path escapes the project root');
55
+ }
56
+ }
57
+
58
+ function resolveContextPath(rootDir, value, {
59
+ taskId,
60
+ allowLegacyAliases = false,
61
+ } = {}) {
62
+ const root = path.resolve(rootDir || '.');
63
+ const defaultValue = taskId == null
64
+ ? null
65
+ : `${CONTEXT_ROOT_RELATIVE}/task-${taskId}.md`;
66
+ const original = value || defaultValue;
67
+ if (typeof original !== 'string' || original.trim() === '' || original.includes('\0')) {
68
+ invalid(original, 'a non-empty path is required');
69
+ }
70
+
71
+ let raw;
72
+ if (path.isAbsolute(original)) {
73
+ raw = path.relative(root, path.resolve(original)).split(path.sep).join('/');
74
+ if (raw === '..' || raw.startsWith('../')) invalid(original, 'absolute path escapes the project');
75
+ } else {
76
+ raw = original.trim().replaceAll('\\', '/').replace(/^\.\//, '');
77
+ }
78
+ if (raw.split('/').includes('..')) invalid(original, 'parent traversal is not allowed');
79
+
80
+ if (allowLegacyAliases && raw.startsWith('contexts/')) {
81
+ raw = `${LEGACY_CONTEXT_ROOT_RELATIVE}/${raw.slice('contexts/'.length)}`;
82
+ } else if (allowLegacyAliases && !raw.includes('/')) {
83
+ raw = `${LEGACY_CONTEXT_ROOT_RELATIVE}/${raw}`;
84
+ }
85
+ if (raw.startsWith(`${LEGACY_CONTEXT_ROOT_RELATIVE}/`)) {
86
+ raw = `${CONTEXT_ROOT_RELATIVE}/${raw.slice(LEGACY_CONTEXT_ROOT_RELATIVE.length + 1)}`;
87
+ }
88
+
89
+ const relative = path.posix.normalize(raw);
90
+ if (!relative.startsWith(`${CONTEXT_ROOT_RELATIVE}/`)
91
+ || relative === `${CONTEXT_ROOT_RELATIVE}/`
92
+ || relative.endsWith('/')) {
93
+ invalid(original, `path must be a file below ${CONTEXT_ROOT_RELATIVE}`);
94
+ }
95
+ const file = path.resolve(root, ...relative.split('/'));
96
+ const contextRoot = path.resolve(root, ...CONTEXT_ROOT_RELATIVE.split('/'));
97
+ if (!file.startsWith(`${contextRoot}${path.sep}`)) {
98
+ invalid(original, `path escapes ${CONTEXT_ROOT_RELATIVE}`);
99
+ }
100
+ assertNoSymlinkAncestors(root, relative, original);
101
+ assertRealContainment(root, file, original);
102
+ return { root, contextRoot, relative, file };
103
+ }
104
+
105
+ function resolveLegacyContextPath(rootDir, value, { taskId } = {}) {
106
+ const root = path.resolve(rootDir || '.');
107
+ const original = value || `${LEGACY_CONTEXT_ROOT_RELATIVE}/task-${taskId}.md`;
108
+ if (typeof original !== 'string' || original.trim() === '' || original.includes('\0')) {
109
+ invalid(original, 'a non-empty legacy path is required');
110
+ }
111
+ if (path.isAbsolute(original)) invalid(original, 'legacy paths must be project-relative');
112
+ const raw = original.trim().replaceAll('\\', '/').replace(/^\.\//, '');
113
+ if (raw.split('/').includes('..')) invalid(original, 'parent traversal is not allowed');
114
+ const relative = path.posix.normalize(raw);
115
+ if (!relative.startsWith(`${LEGACY_CONTEXT_ROOT_RELATIVE}/`)
116
+ || relative === `${LEGACY_CONTEXT_ROOT_RELATIVE}/`
117
+ || relative.endsWith('/')) {
118
+ invalid(original, `legacy path must be a file below ${LEGACY_CONTEXT_ROOT_RELATIVE}`);
119
+ }
120
+ const file = path.resolve(root, ...relative.split('/'));
121
+ const contextRoot = path.resolve(root, ...LEGACY_CONTEXT_ROOT_RELATIVE.split('/'));
122
+ if (!file.startsWith(`${contextRoot}${path.sep}`)) {
123
+ invalid(original, `legacy path escapes ${LEGACY_CONTEXT_ROOT_RELATIVE}`);
124
+ }
125
+ assertNoSymlinkAncestors(root, relative, original);
126
+ assertRealContainment(root, file, original);
127
+ return { root, contextRoot, relative, file };
128
+ }
129
+
130
+ function isGeneratedContextContents(contents) {
131
+ return new RegExp(`^generated_by:\\s*${GENERATED_BY}\\s*$`, 'm').test(String(contents));
132
+ }
133
+
134
+ module.exports = {
135
+ CONTEXT_ROOT_RELATIVE,
136
+ LEGACY_CONTEXT_ROOT_RELATIVE,
137
+ GENERATED_BY,
138
+ ContextPathError,
139
+ isGeneratedContextContents,
140
+ resolveContextPath,
141
+ resolveLegacyContextPath,
142
+ };