ultra-builder-pro-cli 0.25.1 → 0.26.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.
Files changed (243) hide show
  1. package/.ultra-template/changes/abandoned/.gitkeep +0 -0
  2. package/.ultra-template/contexts/TEMPLATE.md +94 -0
  3. package/.ultra-template/decisions/.gitkeep +1 -0
  4. package/.ultra-template/evidence/.gitkeep +1 -0
  5. package/.ultra-template/north-star.md +46 -0
  6. package/.ultra-template/research/.gitkeep +1 -0
  7. package/.ultra-template/specs/discovery.md +2 -2
  8. package/.ultra-template/tasks.json +3 -0
  9. package/.ultra-template/test-report.json +27 -0
  10. package/CHANGELOG.md +107 -1
  11. package/README.md +217 -579
  12. package/adapters/_shared/delegate-profiles/kimi-read-only.md +15 -0
  13. package/adapters/_shared/delegate-profiles/kimi-write.md +17 -0
  14. package/adapters/_shared/file-ops.cjs +76 -2
  15. package/adapters/_shared/host-profile.cjs +97 -0
  16. package/adapters/_shared/plugin-core.cjs +204 -0
  17. package/adapters/_shared/runtime-assets.cjs +31 -96
  18. package/adapters/claude.js +92 -291
  19. package/adapters/codex.js +309 -842
  20. package/adapters/grok.js +143 -1469
  21. package/adapters/kimi.js +124 -1053
  22. package/adapters/opencode.js +230 -742
  23. package/bin/delegate-worker.cjs +328 -0
  24. package/bin/delegate.cjs +463 -0
  25. package/bin/install.js +38 -11
  26. package/docs/ARCHITECTURE.md +125 -216
  27. package/docs/ARTIFACT-AUTHORITY.md +90 -268
  28. package/docs/DECISIONS.md +77 -87
  29. package/docs/PHILOSOPHY.md +208 -0
  30. package/docs/PLUGIN-ISOLATION-CONTRACT.md +65 -39
  31. package/docs/RUNTIME-COMPAT-MATRIX.md +140 -155
  32. package/docs/SKILL-AUTHORING.md +94 -73
  33. package/docs/WORKFLOW-LIFECYCLE.md +97 -189
  34. package/hooks/README.md +19 -37
  35. package/hooks/_common.py +113 -0
  36. package/hooks/adapters/codex.py +9 -16
  37. package/hooks/adapters/grok.py +5 -5
  38. package/hooks/adapters/kimi.py +8 -14
  39. package/hooks/block_dangerous_commands.py +201 -0
  40. package/hooks/compact_context.py +65 -0
  41. package/hooks/mid_workflow_recall.py +25 -0
  42. package/hooks/post_edit_guard.py +105 -0
  43. package/hooks/session_context.py +30 -0
  44. package/package.json +17 -52
  45. package/skills/ultra-change/SKILL.md +115 -50
  46. package/skills/ultra-change/references/change-contract.md +68 -3
  47. package/skills/ultra-delegate/SKILL.md +89 -0
  48. package/skills/ultra-delegate/references/delegation-contract.md +59 -0
  49. package/skills/ultra-delegate/scripts/delegate_wait.py +86 -0
  50. package/skills/ultra-deliver/SKILL.md +98 -42
  51. package/skills/ultra-deliver/references/baseline-reconciliation.md +39 -20
  52. package/skills/ultra-dev/SKILL.md +115 -62
  53. package/skills/ultra-dev/references/debugging.md +22 -0
  54. package/skills/ultra-domain-modeling/SKILL.md +79 -0
  55. package/skills/ultra-grilling/SKILL.md +118 -0
  56. package/skills/ultra-grilling/references/reframing.md +72 -0
  57. package/skills/ultra-init/SKILL.md +102 -37
  58. package/skills/ultra-init/references/brownfield-adoption.md +33 -83
  59. package/skills/ultra-init/scripts/init_project.cjs +84 -0
  60. package/skills/ultra-plan/SKILL.md +101 -68
  61. package/skills/ultra-research/SKILL.md +68 -48
  62. package/skills/ultra-research/references/00-problem-validation.md +5 -5
  63. package/skills/ultra-research/references/01-opportunity-discovery.md +3 -3
  64. package/skills/ultra-research/references/02-market-assessment.md +5 -4
  65. package/skills/ultra-research/references/03-competitive-landscape.md +3 -3
  66. package/skills/ultra-research/references/04-product-strategy.md +3 -3
  67. package/skills/ultra-research/references/05-assumptions-validation.md +5 -5
  68. package/skills/ultra-research/references/10-user-personas.md +3 -2
  69. package/skills/ultra-research/references/11-user-scenarios.md +3 -3
  70. package/skills/ultra-research/references/20-user-stories.md +4 -3
  71. package/skills/ultra-research/references/21-features-scope.md +3 -2
  72. package/skills/ultra-research/references/22-success-metrics.md +3 -3
  73. package/skills/ultra-research/references/30-architecture-context.md +3 -3
  74. package/skills/ultra-research/references/31-solution-strategy.md +3 -3
  75. package/skills/ultra-research/references/32-building-blocks.md +3 -3
  76. package/skills/ultra-research/references/40-deployment.md +3 -3
  77. package/skills/ultra-research/references/41-quality-risks.md +3 -3
  78. package/skills/ultra-research/references/99-synthesis.md +7 -7
  79. package/skills/ultra-review/SKILL.md +84 -32
  80. package/skills/ultra-review/references/code.md +17 -0
  81. package/skills/ultra-review/references/comments.md +12 -0
  82. package/skills/ultra-review/references/design.md +12 -0
  83. package/skills/ultra-review/references/errors.md +13 -0
  84. package/skills/ultra-review/references/spec.md +13 -0
  85. package/skills/ultra-review/references/tests.md +12 -0
  86. package/skills/ultra-review/references/worker-packet.md +40 -0
  87. package/skills/ultra-review/scripts/review_wait.py +26 -15
  88. package/skills/ultra-status/SKILL.md +61 -36
  89. package/skills/ultra-tdd/SKILL.md +81 -0
  90. package/skills/ultra-tdd/references/templates/README.md +20 -0
  91. package/skills/ultra-tdd/references/templates/feature-flag-default-audit.sh +79 -0
  92. package/skills/ultra-tdd/references/templates/persistence-real.ts +78 -0
  93. package/skills/ultra-tdd/references/templates/testcontainer-postgres.py +78 -0
  94. package/skills/ultra-tdd/references/templates/testcontainer-postgres.ts +73 -0
  95. package/skills/ultra-tdd/references/templates/vertical-slice.ts +66 -0
  96. package/skills/ultra-tdd/references/test-execution.md +18 -0
  97. package/skills/ultra-test/SKILL.md +75 -30
  98. package/skills/ultra-test/references/export-syntax.md +18 -0
  99. package/skills/ultra-test/scripts/worktree_digest.cjs +54 -0
  100. package/skills/ultra-think/SKILL.md +49 -27
  101. package/skills/ultra-think/references/autonomy-boundary.md +48 -0
  102. package/skills/ultra-think/references/interaction-boundary.md +18 -33
  103. package/.ultra-template/docs/research/README.md +0 -55
  104. package/.ultra-template/reports/templates/delivery-report.json +0 -17
  105. package/.ultra-template/reports/templates/test-report.json +0 -18
  106. package/.ultra-template/tasks/tasks.json +0 -15
  107. package/.ultra-template/templates/task-context.md +0 -19
  108. package/adapters/_shared/codex-assets.cjs +0 -725
  109. package/adapters/_shared/codex-ultra-tools-entry.cjs +0 -11
  110. package/adapters/_shared/interaction-contract.cjs +0 -129
  111. package/adapters/_shared/path-rewrite.cjs +0 -79
  112. package/adapters/_shared/runtime-native.cjs +0 -132
  113. package/adapters/_shared/settings-merge.cjs +0 -76
  114. package/agents/code-reviewer.md +0 -42
  115. package/agents/debugger.md +0 -40
  116. package/agents/review-code.md +0 -49
  117. package/agents/review-comments.md +0 -38
  118. package/agents/review-coordinator.md +0 -56
  119. package/agents/review-design.md +0 -40
  120. package/agents/review-errors.md +0 -43
  121. package/agents/review-spec.md +0 -39
  122. package/agents/review-tests.md +0 -40
  123. package/agents/tdd-runner.md +0 -38
  124. package/bin/orchestrator.js +0 -383
  125. package/commands/ultra-change.md +0 -10
  126. package/commands/ultra-deliver.md +0 -10
  127. package/commands/ultra-dev.md +0 -10
  128. package/commands/ultra-doctor.md +0 -10
  129. package/commands/ultra-init.md +0 -10
  130. package/commands/ultra-plan.md +0 -10
  131. package/commands/ultra-research.md +0 -10
  132. package/commands/ultra-review.md +0 -10
  133. package/commands/ultra-status.md +0 -10
  134. package/commands/ultra-test.md +0 -10
  135. package/commands/ultra-think.md +0 -10
  136. package/docs/AGENT-CONTEXT.md +0 -191
  137. package/docs/COMMIT-HASH-BACKFILL.md +0 -51
  138. package/docs/LEGACY-CLI-CROSSWALK.md +0 -175
  139. package/docs/ROADMAP.md +0 -101
  140. package/docs/STATE-DB-ACCESS-POLICY.md +0 -161
  141. package/hooks/active_task_context.py +0 -152
  142. package/hooks/context_envelope.py +0 -100
  143. package/hooks/health_check.py +0 -196
  144. package/hooks/pre_stop_check.py +0 -66
  145. package/hooks/runtime_paths.py +0 -396
  146. package/hooks/subagent_tracker.py +0 -88
  147. package/hooks/workflow_checkpoint.py +0 -76
  148. package/hooks/workflow_context.py +0 -48
  149. package/hooks/workflow_resume.py +0 -84
  150. package/mcp-server/hook-context.cjs +0 -38
  151. package/mcp-server/hook-event.cjs +0 -34
  152. package/mcp-server/lib/archive-journal.cjs +0 -1119
  153. package/mcp-server/lib/archive-mutation-worker.py +0 -365
  154. package/mcp-server/lib/artifact-registry.cjs +0 -1232
  155. package/mcp-server/lib/baseline-workflow.cjs +0 -1193
  156. package/mcp-server/lib/canonical-json.cjs +0 -19
  157. package/mcp-server/lib/change-authority.cjs +0 -26
  158. package/mcp-server/lib/change-packet.cjs +0 -790
  159. package/mcp-server/lib/change-workflow.cjs +0 -65
  160. package/mcp-server/lib/context-envelope.cjs +0 -581
  161. package/mcp-server/lib/context-paths.cjs +0 -142
  162. package/mcp-server/lib/decision-records.cjs +0 -423
  163. package/mcp-server/lib/delivery-transaction.cjs +0 -471
  164. package/mcp-server/lib/doctor-backup-worker.cjs +0 -328
  165. package/mcp-server/lib/doctor.cjs +0 -627
  166. package/mcp-server/lib/git-bootstrap.cjs +0 -651
  167. package/mcp-server/lib/init-project.cjs +0 -1021
  168. package/mcp-server/lib/kernel-change-workflow.cjs +0 -2029
  169. package/mcp-server/lib/legacy-change-workflow.cjs +0 -3032
  170. package/mcp-server/lib/managed-file-write.cjs +0 -172
  171. package/mcp-server/lib/plan-checkpoint.cjs +0 -177
  172. package/mcp-server/lib/plan-publication-worker.cjs +0 -830
  173. package/mcp-server/lib/plan-store.cjs +0 -794
  174. package/mcp-server/lib/prd-parser.cjs +0 -83
  175. package/mcp-server/lib/pricing.cjs +0 -44
  176. package/mcp-server/lib/project-context.cjs +0 -178
  177. package/mcp-server/lib/projector.cjs +0 -386
  178. package/mcp-server/lib/provider-refs.cjs +0 -42
  179. package/mcp-server/lib/runtime-paths.cjs +0 -1714
  180. package/mcp-server/lib/runtime-state.cjs +0 -222
  181. package/mcp-server/lib/safe-project-file.cjs +0 -385
  182. package/mcp-server/lib/stage-checkpoints.cjs +0 -463
  183. package/mcp-server/lib/state-authority.cjs +0 -95
  184. package/mcp-server/lib/state-db.cjs +0 -1580
  185. package/mcp-server/lib/state-ops.cjs +0 -1428
  186. package/mcp-server/lib/task-contract.cjs +0 -404
  187. package/mcp-server/lib/task-expander.cjs +0 -105
  188. package/mcp-server/lib/task-ledger.cjs +0 -1964
  189. package/mcp-server/lib/telemetry.cjs +0 -85
  190. package/mcp-server/lib/topo.cjs +0 -131
  191. package/mcp-server/lib/ultra-facade.cjs +0 -2684
  192. package/mcp-server/lib/worker-packet.cjs +0 -358
  193. package/mcp-server/server.cjs +0 -437
  194. package/orchestrator/auto-merge.cjs +0 -128
  195. package/orchestrator/daemon.cjs +0 -264
  196. package/orchestrator/dispatch-rules.cjs +0 -78
  197. package/orchestrator/parallel-orchestrator.cjs +0 -903
  198. package/orchestrator/planner/plan-builder.cjs +0 -125
  199. package/orchestrator/recovery.cjs +0 -188
  200. package/orchestrator/session-close-journal-worker.cjs +0 -5
  201. package/orchestrator/session-close-journal.cjs +0 -1014
  202. package/orchestrator/session-runner.cjs +0 -1153
  203. package/orchestrator/worktree-manager.cjs +0 -282
  204. package/settings.json +0 -7
  205. package/skills/cc-collab/SKILL.md +0 -42
  206. package/skills/code-review-expert/SKILL.md +0 -43
  207. package/skills/code-review-expert/references/correctness-reliability.md +0 -30
  208. package/skills/code-review-expert/references/design-boundaries.md +0 -26
  209. package/skills/code-review-expert/references/removal-plan.md +0 -24
  210. package/skills/codex-collab/SKILL.md +0 -49
  211. package/skills/codex-collab/evals/evals.json +0 -35
  212. package/skills/integration-rules/SKILL.md +0 -32
  213. package/skills/security-rules/SKILL.md +0 -38
  214. package/skills/testing-rules/SKILL.md +0 -32
  215. package/skills/ultra-doctor/SKILL.md +0 -41
  216. package/skills/ultra-plan/references/semantic-preflight.md +0 -20
  217. package/skills/ultra-verify/SKILL.md +0 -46
  218. package/skills/ultra-verify/evals/evals.json +0 -46
  219. package/skills/ultra-verify/references/cross-verify-modes.md +0 -28
  220. package/skills/ultra-verify/references/evidence-status.md +0 -21
  221. package/skills/ultra-verify/scripts/verify_wait.py +0 -94
  222. package/spec/cli-protocol.md +0 -102
  223. package/spec/mcp-tools.yaml +0 -312
  224. package/spec/schemas/baseline-reconciliation.v1.schema.json +0 -73
  225. package/spec/schemas/change-delta.v1.schema.json +0 -153
  226. package/spec/schemas/command-manifest.schema.json +0 -29
  227. package/spec/schemas/context-file.v4.5.schema.json +0 -54
  228. package/spec/schemas/delivery-report.v1.schema.json +0 -66
  229. package/spec/schemas/documentation-reconciliation.v1.schema.json +0 -106
  230. package/spec/schemas/mcp-tools.schema.json +0 -136
  231. package/spec/schemas/skill-manifest.schema.json +0 -24
  232. package/spec/schemas/state-db.sql +0 -641
  233. package/spec/schemas/task-ledger.v2.schema.json +0 -296
  234. package/spec/schemas/tasks.v4.5.schema.json +0 -96
  235. package/spec/schemas/test-report.v1.schema.json +0 -225
  236. package/ultra-tools/cli.cjs +0 -129
  237. package/ultra-tools/commands/db.cjs +0 -226
  238. package/ultra-tools/commands/legacy-memory.cjs +0 -159
  239. package/ultra-tools/commands/migrate.cjs +0 -1386
  240. package/ultra-tools/commands/session.cjs +0 -213
  241. package/ultra-tools/commands/status.cjs +0 -559
  242. package/ultra-tools/commands/system.cjs +0 -498
  243. package/ultra-tools/commands/task.cjs +0 -105
@@ -1,44 +1,32 @@
1
1
  'use strict';
2
2
 
3
- /** Build a Claude Code native Ultra Builder Pro plugin. */
4
-
5
3
  const fs = require('node:fs');
6
4
  const os = require('node:os');
7
5
  const path = require('node:path');
8
6
  const { spawnSync } = require('node:child_process');
9
7
 
10
8
  const {
11
- copyTree,
12
- ensureDir,
13
- isManaged,
9
+ copyHooks,
10
+ copySkills,
11
+ inspectPlugin,
14
12
  markManaged,
13
+ publishManagedTrees,
14
+ removeManaged,
15
+ writePluginProvenance,
16
+ } = require('./_shared/plugin-core.cjs');
17
+ const {
18
+ captureAbsent,
19
+ ensureDir,
20
+ managedMetadata,
21
+ pruneCreatedEmpty,
15
22
  removeTree,
16
23
  writeAtomic,
17
24
  } = require('./_shared/file-ops.cjs');
18
- const {
19
- applyNativeDoctor,
20
- buildMcpRuntime,
21
- mcpCommand,
22
- } = require('./_shared/codex-assets.cjs');
23
- const { parse: parseFrontmatter, serialize: serializeFrontmatter } = require('./_shared/frontmatter.cjs');
24
- const { adaptInteractionGuidance } = require('./_shared/interaction-contract.cjs');
25
- const provenance = require('./_shared/provenance.cjs');
26
- const {
27
- CORE_PUBLIC_SKILLS,
28
- WORKFLOW_HOOK_FILES,
29
- skillPolicy,
30
- skillsForRuntime,
31
- } = require('./_shared/runtime-assets.cjs');
32
25
 
33
26
  const PLUGIN_NAME = 'ultra-builder-pro';
34
- const MCP_SERVER_NAME = PLUGIN_NAME;
35
- const SOURCE_TAG = 'ubp';
36
- const SENTINEL_KEY = '_ubp_manifest';
37
- const COMMAND_NAMES = CORE_PUBLIC_SKILLS;
38
- const PROVENANCE_FILE = 'provenance.json';
39
27
 
40
28
  function resolveTarget(ctx = {}) {
41
- if (ctx.configDir) return ctx.configDir;
29
+ if (ctx.configDir) return path.resolve(ctx.configDir);
42
30
  if (ctx.scope === 'global') {
43
31
  return process.env.CLAUDE_CONFIG_DIR || path.join(ctx.homeDir || os.homedir(), '.claude');
44
32
  }
@@ -46,332 +34,145 @@ function resolveTarget(ctx = {}) {
46
34
  }
47
35
 
48
36
  function resolvePluginRoot(ctx = {}) {
49
- if (ctx.pluginRoot) return ctx.pluginRoot;
50
- return path.join(resolveTarget(ctx), 'skills', PLUGIN_NAME);
37
+ return ctx.pluginRoot || path.join(resolveTarget(ctx), 'skills', PLUGIN_NAME);
51
38
  }
52
39
 
53
40
  function resolveRepoRoot(ctx = {}) {
54
41
  return ctx.repoRoot || path.resolve(__dirname, '..');
55
42
  }
56
43
 
57
- function shouldRunHostCli(ctx = {}) {
58
- if (typeof ctx.runHostCli === 'boolean') return ctx.runHostCli;
59
- return ctx.scope === 'global' && !ctx.configDir;
60
- }
61
-
62
- function runClaudeCli(args, ctx = {}) {
63
- const result = spawnSync(ctx.claudeBin || 'claude', args, {
64
- encoding: 'utf8',
65
- timeout: ctx.hostCliTimeoutMs || 30000,
66
- killSignal: 'SIGKILL',
67
- stdio: ['ignore', 'pipe', 'pipe'],
68
- env: {
69
- ...process.env,
70
- CLAUDE_CONFIG_DIR: resolveTarget(ctx),
71
- },
72
- });
73
- if (result.error) {
74
- if (result.error.code === 'ETIMEDOUT') {
75
- const error = new Error(`claude ${args.join(' ')} timed out`);
76
- error.code = 'HOST_CLI_TIMEOUT';
77
- throw error;
78
- }
79
- throw result.error;
80
- }
81
- if (result.status !== 0) {
82
- const detail = (result.stderr || result.stdout || '').trim();
83
- throw new Error(`claude ${args.join(' ')} failed${detail ? `: ${detail}` : ''}`);
84
- }
85
- return result.stdout;
86
- }
87
-
88
- function claudeMcpRow(output, identity) {
89
- for (const rawLine of output.split(/\r?\n/)) {
90
- const line = rawLine.trim();
91
- if (line.slice(0, identity.length) !== identity) continue;
92
- if (line[identity.length] !== ':') continue;
93
- return line;
94
- }
95
- return '';
96
- }
97
-
98
- function inspectClaudeHost(ctx = {}) {
99
- const target = resolvePluginRoot(ctx);
100
- const manifest = JSON.parse(
101
- fs.readFileSync(path.join(target, '.claude-plugin', 'plugin.json'), 'utf8'),
102
- );
103
- const plugins = JSON.parse(runClaudeCli(['plugin', 'list', '--json'], ctx));
104
- if (!Array.isArray(plugins)) throw new Error('claude plugin list --json did not return an array');
105
- const plugin = plugins.find((entry) => entry?.id === `${PLUGIN_NAME}@skills-dir`);
106
- const pluginOk = !!plugin
107
- && plugin.enabled === true
108
- && plugin.version === manifest.version
109
- && path.resolve(plugin.installPath || '') === path.resolve(target);
110
- const mcpOutput = pluginOk
111
- ? runClaudeCli(['mcp', 'list'], ctx).replace(/\u001b\[[0-9;]*m/g, '')
112
- : '';
113
- const mcpLine = claudeMcpRow(
114
- mcpOutput,
115
- `plugin:${PLUGIN_NAME}:${MCP_SERVER_NAME}`,
116
- );
117
- const mcpOk = pluginOk
118
- && !!plugin.mcpServers?.[MCP_SERVER_NAME]
119
- && /\bConnected\b/i.test(mcpLine);
120
- let detailsOk = false;
121
- if (pluginOk) {
122
- runClaudeCli(['plugin', 'details', `${PLUGIN_NAME}@skills-dir`], ctx);
123
- detailsOk = true;
124
- }
125
- return { plugin, pluginOk, mcpOk, mcpOutput, detailsOk };
126
- }
127
-
128
- function copySkills(repoRoot, target, names) {
129
- const copied = [];
130
- for (const name of names) {
131
- const source = path.join(repoRoot, 'skills', name);
132
- if (!fs.existsSync(source)) throw new Error(`missing allowlisted Claude skill: ${name}`);
133
- const files = copyTree(source, path.join(target, 'skills', name), {
134
- transform(buf, rel) {
135
- if (rel !== 'SKILL.md') {
136
- return rel.endsWith('.md')
137
- ? Buffer.from(adaptInteractionGuidance(buf.toString('utf8'), 'claude'))
138
- : buf;
139
- }
140
- const { fm, body } = parseFrontmatter(buf.toString('utf8'));
141
- if (!fm) throw new Error(`missing frontmatter in Claude skill: ${name}`);
142
- let adaptedBody = adaptInteractionGuidance(body, 'claude');
143
- if (name === 'ultra-review') {
144
- adaptedBody = adaptedBody.replace(
145
- /(?:the current host's native|the host-native) bounded-worker\s+mechanism/g,
146
- 'Claude Code Task workers using the installed review agent definitions',
147
- );
148
- }
149
- const policy = skillPolicy(name);
150
- return Buffer.from(serializeFrontmatter({
151
- name,
152
- description: fm.description,
153
- 'user-invocable': policy.userInvocable,
154
- ...(policy.userInvocable ? { 'disable-model-invocation': true } : {}),
155
- }, adaptedBody));
156
- },
157
- });
158
- copied.push(...files.map((rel) => path.join(name, rel)));
159
- }
160
- return copied;
161
- }
162
-
163
- function copyCommands(repoRoot, target) {
164
- const commandRoot = path.join(target, 'commands');
165
- ensureDir(commandRoot);
166
- const copied = [];
167
- for (const name of COMMAND_NAMES) {
168
- const file = `${name}.md`;
169
- const source = path.join(repoRoot, 'commands', file);
170
- if (!fs.existsSync(source)) throw new Error(`missing allowlisted Claude command: ${file}`);
171
- fs.copyFileSync(source, path.join(commandRoot, file));
172
- copied.push(file);
173
- }
174
- return copied;
175
- }
176
-
177
- function hookCommand(name, ...args) {
44
+ function hookCommand(name) {
178
45
  return {
179
46
  type: 'command',
180
- command: [
181
- `python3 "\${CLAUDE_PLUGIN_ROOT}/hooks/${name}"`,
182
- ...args.map((arg) => JSON.stringify(arg)),
183
- ].join(' '),
47
+ command: `python3 "\${CLAUDE_PLUGIN_ROOT}/hooks/${name}"`,
184
48
  timeout: 10,
185
49
  };
186
50
  }
187
51
 
188
52
  function buildHooksManifest() {
189
53
  return {
190
- description: 'Ultra workflow lifecycle hooks; inactive outside projects with an active .ultra workflow.',
54
+ description: 'Ultra file-first context sensors and narrow effect protection.',
191
55
  hooks: {
192
56
  SessionStart: [
193
- {
194
- matcher: 'startup|resume|clear',
195
- hooks: [hookCommand('health_check.py'), hookCommand('workflow_context.py')],
196
- },
197
- { matcher: 'compact', hooks: [hookCommand('workflow_resume.py')] },
57
+ { matcher: 'startup|resume|clear', hooks: [hookCommand('session_context.py')] },
58
+ { matcher: 'compact', hooks: [hookCommand('compact_context.py')] },
198
59
  ],
199
60
  PreToolUse: [
200
- { matcher: 'Edit|Write', hooks: [hookCommand('active_task_context.py')] },
201
- ],
202
- PreCompact: [
203
- { hooks: [hookCommand('workflow_checkpoint.py')] },
61
+ { matcher: 'Write|Edit|Grep', hooks: [hookCommand('mid_workflow_recall.py')] },
62
+ { matcher: 'Bash', hooks: [hookCommand('block_dangerous_commands.py')] },
204
63
  ],
205
- Stop: [
206
- { hooks: [hookCommand('pre_stop_check.py')] },
207
- ],
208
- SubagentStart: [
209
- { hooks: [hookCommand('subagent_tracker.py', 'start')] },
210
- ],
211
- SubagentStop: [
212
- { hooks: [hookCommand('subagent_tracker.py', 'stop')] },
64
+ PostToolUse: [
65
+ { matcher: 'Write|Edit', hooks: [hookCommand('post_edit_guard.py')] },
213
66
  ],
67
+ PreCompact: [{ hooks: [hookCommand('compact_context.py')] }],
214
68
  },
215
69
  };
216
70
  }
217
71
 
218
- function buildStaging(repoRoot, staging, target) {
219
- const report = { target, copied: {}, config: { updated: false } };
220
- report.copied.commands = copyCommands(repoRoot, staging);
221
- report.copied.skills = copySkills(repoRoot, staging, skillsForRuntime('claude'));
222
- report.copied.agents = copyTree(path.join(repoRoot, 'agents'), path.join(staging, 'agents'));
223
-
224
- ensureDir(path.join(staging, 'hooks'));
225
- report.copied.hooks = [];
226
- for (const name of WORKFLOW_HOOK_FILES) {
227
- fs.copyFileSync(path.join(repoRoot, 'hooks', name), path.join(staging, 'hooks', name));
228
- report.copied.hooks.push(name);
229
- }
230
- writeAtomic(path.join(staging, 'hooks', 'hooks.json'), JSON.stringify(buildHooksManifest(), null, 2) + '\n');
231
-
232
- buildMcpRuntime(repoRoot, staging, { runtime: 'claude' });
233
- const command = mcpCommand(path.join(target, 'runtime', 'launch.cjs'));
234
- writeAtomic(path.join(staging, '.mcp.json'), JSON.stringify({
235
- mcpServers: {
236
- [MCP_SERVER_NAME]: {
237
- command: command.command,
238
- args: command.args,
239
- },
240
- },
241
- }, null, 2) + '\n');
242
-
72
+ function buildStaging(repoRoot, staging, publishedRoot) {
73
+ const skillRoot = path.join(staging, 'skills');
74
+ const hookRoot = path.join(staging, 'hooks');
75
+ const hooks = buildHooksManifest();
76
+ const skills = copySkills({ runtime: 'claude', repoRoot, skillRoot });
77
+ copyHooks({ runtime: 'claude', repoRoot, hookRoot, hooksManifest: hooks });
243
78
  const pkg = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
244
- writeAtomic(path.join(staging, '.claude-plugin', 'plugin.json'), JSON.stringify({
79
+ writeAtomic(path.join(staging, '.claude-plugin', 'plugin.json'), `${JSON.stringify({
245
80
  name: PLUGIN_NAME,
246
81
  version: pkg.version,
247
- description: 'Claude Code native Ultra Builder Pro workflows with a persistent safety kernel.',
248
- author: { name: typeof pkg.author === 'string' ? pkg.author : 'Ultra Builder Pro contributors' },
82
+ description: 'File-first Ultra Builder Pro workflows for Claude Code.',
83
+ author: { name: pkg.author },
249
84
  repository: pkg.homepage,
250
- license: pkg.license || 'MIT',
251
- }, null, 2) + '\n');
252
- markManaged(staging, { adapter: 'claude', plugin: PLUGIN_NAME });
253
- const source = provenance.packageSource(repoRoot);
254
- const provenanceFile = path.join(staging, PROVENANCE_FILE);
255
- report.provenance = provenance.writeProvenance({
256
- file: provenanceFile,
257
- adapter: 'claude',
258
- ...source,
259
- roots: { plugin: target },
260
- assetRoots: { plugin: staging },
261
- assets: provenance.assetRefsForTree('plugin', staging, {
262
- exclude: ['.ubp-managed', PROVENANCE_FILE],
263
- }),
85
+ license: pkg.license,
86
+ }, null, 2)}\n`);
87
+ markManaged(staging, { adapter: 'claude', plugin: PLUGIN_NAME, version: pkg.version });
88
+ writePluginProvenance({
89
+ adapter: 'claude', repoRoot, stagingRoot: staging, publishedRoot,
264
90
  contracts: {
265
91
  plugin_manifest: { root: 'plugin', path: '.claude-plugin/plugin.json' },
266
- mcp_registration: { root: 'plugin', path: '.mcp.json' },
267
- mcp_launcher: { root: 'plugin', path: 'runtime/launch.cjs' },
268
- native_runtime: { root: 'plugin', path: 'runtime/native-runtime.json' },
269
- context_envelope_helper: { root: 'plugin', path: 'runtime/hook-context.cjs' },
270
- hook_event_helper: { root: 'plugin', path: 'runtime/hook-event.cjs' },
271
92
  hooks_manifest: { root: 'plugin', path: 'hooks/hooks.json' },
272
- checkpoint_hook: { root: 'plugin', path: 'hooks/workflow_checkpoint.py' },
273
- resume_hook: { root: 'plugin', path: 'hooks/workflow_resume.py' },
274
93
  },
275
94
  });
276
- report.provenance.file = path.join(target, PROVENANCE_FILE);
277
- return report;
95
+ return { skills };
278
96
  }
279
97
 
280
98
  function install(ctx = {}) {
281
- const target = resolvePluginRoot(ctx);
282
99
  const repoRoot = resolveRepoRoot(ctx);
283
- if (fs.existsSync(target)) {
284
- const entries = fs.readdirSync(target);
285
- if (entries.length > 0 && !isManaged(target)) {
286
- throw new Error(`refusing to replace unmanaged Claude plugin: ${target}`);
287
- }
288
- }
289
- ensureDir(path.dirname(target));
290
- const staging = fs.mkdtempSync(path.join(path.dirname(target), `.${PLUGIN_NAME}-staging-`));
291
- const backup = `${staging}-previous`;
292
- let movedPrevious = false;
293
- let published = false;
100
+ const pluginRoot = resolvePluginRoot(ctx);
101
+ const target = resolveTarget(ctx);
102
+ const cleanupAbsent = managedMetadata(pluginRoot)?.cleanup_absent
103
+ || captureAbsent(target, ['.', 'skills']);
104
+ ensureDir(path.dirname(pluginRoot));
105
+ const staging = fs.mkdtempSync(path.join(path.dirname(pluginRoot), `.${PLUGIN_NAME}-staging-`));
294
106
  try {
295
- const report = buildStaging(repoRoot, staging, target);
296
- if (fs.existsSync(target)) {
297
- fs.renameSync(target, backup);
298
- movedPrevious = true;
299
- }
300
- fs.renameSync(staging, target);
301
- published = true;
302
- if (movedPrevious) removeTree(backup);
303
- return report;
107
+ const copied = buildStaging(repoRoot, staging, pluginRoot);
108
+ markManaged(staging, { cleanup_absent: cleanupAbsent });
109
+ publishManagedTrees([{ source: staging, target: pluginRoot, label: 'Claude plugin' }]);
110
+ return {
111
+ target: pluginRoot,
112
+ pluginRoot,
113
+ skillRoot: path.join(pluginRoot, 'skills'),
114
+ hookRoot: path.join(pluginRoot, 'hooks'),
115
+ copied,
116
+ };
304
117
  } catch (error) {
305
- if (published && fs.existsSync(target)) removeTree(target);
306
- else if (fs.existsSync(staging)) removeTree(staging);
307
- if (movedPrevious && fs.existsSync(backup)) fs.renameSync(backup, target);
118
+ if (fs.existsSync(staging)) removeTree(staging);
119
+ pruneCreatedEmpty(target, cleanupAbsent);
308
120
  throw error;
309
121
  }
310
122
  }
311
123
 
124
+ function shouldRunHostCli(ctx = {}) {
125
+ if (typeof ctx.runHostCli === 'boolean') return ctx.runHostCli;
126
+ return ctx.scope === 'global' && !ctx.configDir;
127
+ }
128
+
312
129
  function doctor(ctx = {}) {
313
130
  const repoRoot = resolveRepoRoot(ctx);
314
- const source = provenance.packageSource(repoRoot);
315
- const report = provenance.inspectProvenance({
316
- file: path.join(resolvePluginRoot(ctx), PROVENANCE_FILE),
317
- expectedAdapter: 'claude',
318
- expectedPackageVersion: source.packageInfo.version,
131
+ const pluginRoot = resolvePluginRoot(ctx);
132
+ const report = inspectPlugin({
133
+ adapter: 'claude',
134
+ repoRoot,
135
+ pluginRoot,
136
+ skillRoot: path.join(pluginRoot, 'skills'),
137
+ hookRoot: path.join(pluginRoot, 'hooks'),
138
+ manifestFile: path.join(pluginRoot, '.claude-plugin', 'plugin.json'),
319
139
  });
320
140
  if (shouldRunHostCli(ctx)) {
321
- try {
322
- const host = inspectClaudeHost(ctx);
323
- report.checks.host_plugin = {
324
- status: host.pluginOk && host.detailsOk ? 'pass' : 'fail',
325
- };
326
- report.checks.host_mcp = { status: host.mcpOk ? 'pass' : 'fail' };
327
- if (!host.pluginOk || !host.detailsOk) {
328
- report.issues.push({
329
- code: 'HOST_PLUGIN_NOT_DISCOVERED',
330
- plugin_id: `${PLUGIN_NAME}@skills-dir`,
331
- });
332
- }
333
- if (!host.mcpOk) {
334
- report.issues.push({
335
- code: 'HOST_MCP_NOT_DISCOVERED',
336
- plugin_id: `${PLUGIN_NAME}@skills-dir`,
337
- server: MCP_SERVER_NAME,
338
- });
339
- }
340
- } catch (error) {
341
- report.checks.host_plugin = { status: 'fail' };
342
- report.checks.host_mcp = { status: 'fail' };
343
- report.issues.push({
344
- code: error.code === 'HOST_CLI_TIMEOUT'
345
- ? 'HOST_CLI_TIMEOUT'
346
- : 'HOST_CLI_INSPECTION_FAILED',
347
- message: error.message,
348
- });
141
+ const result = spawnSync(ctx.claudeBin || 'claude', ['plugin', 'list', '--json'], {
142
+ encoding: 'utf8', timeout: ctx.hostCliTimeoutMs || 30000,
143
+ env: { ...process.env, CLAUDE_CONFIG_DIR: resolveTarget(ctx) },
144
+ });
145
+ let discovered = false;
146
+ if (result.status === 0) {
147
+ try {
148
+ const plugins = JSON.parse(result.stdout);
149
+ discovered = Array.isArray(plugins) && plugins.some((entry) => (
150
+ entry?.id === `${PLUGIN_NAME}@skills-dir` && entry.enabled === true
151
+ ));
152
+ } catch {}
349
153
  }
154
+ report.checks.host_plugin = { status: discovered ? 'pass' : 'fail' };
155
+ if (!discovered) report.issues.push({ code: 'HOST_PLUGIN_NOT_DISCOVERED' });
156
+ report.status = report.issues.length === 0 ? 'healthy' : 'degraded';
350
157
  }
351
- return applyNativeDoctor(report, path.join(resolvePluginRoot(ctx), 'runtime'));
158
+ return report;
352
159
  }
353
160
 
354
161
  function uninstall(ctx = {}) {
355
- const target = resolvePluginRoot(ctx);
356
- const report = { target, removed: {}, config: { updated: false } };
357
- if (fs.existsSync(target) && isManaged(target)) {
358
- removeTree(target);
359
- report.removed.plugin = true;
360
- }
361
- return report;
162
+ const pluginRoot = resolvePluginRoot(ctx);
163
+ const cleanupAbsent = managedMetadata(pluginRoot)?.cleanup_absent || [];
164
+ const removed = removeManaged(pluginRoot, 'Claude plugin');
165
+ const cleaned = pruneCreatedEmpty(resolveTarget(ctx), cleanupAbsent);
166
+ return { target: pluginRoot, pluginRoot, removed, cleaned };
362
167
  }
363
168
 
364
169
  module.exports = {
365
170
  name: 'claude',
366
171
  PLUGIN_NAME,
367
- SENTINEL_KEY,
368
- SOURCE_TAG,
369
- MCP_SERVER_NAME,
370
172
  buildHooksManifest,
371
- inspectClaudeHost,
372
173
  resolveTarget,
373
174
  resolvePluginRoot,
374
175
  install,
375
- uninstall,
376
176
  doctor,
177
+ uninstall,
377
178
  };