ultra-builder-pro-cli 0.5.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 (206) hide show
  1. package/.ultra-template/delivery-report.json +9 -0
  2. package/.ultra-template/docs/research/README.md +53 -0
  3. package/.ultra-template/specs/architecture.md +250 -0
  4. package/.ultra-template/specs/discovery.md +248 -0
  5. package/.ultra-template/specs/product.md +223 -0
  6. package/.ultra-template/tasks/contexts/TEMPLATE.md +57 -0
  7. package/.ultra-template/tasks/tasks.json +33 -0
  8. package/.ultra-template/test-report.json +8 -0
  9. package/CHANGELOG.md +194 -0
  10. package/LICENSE +21 -0
  11. package/README.md +187 -0
  12. package/adapters/_shared/codex-assets.cjs +1128 -0
  13. package/adapters/_shared/codex-ultra-tools-entry.cjs +11 -0
  14. package/adapters/_shared/file-ops.cjs +115 -0
  15. package/adapters/_shared/frontmatter.cjs +52 -0
  16. package/adapters/_shared/handbook.cjs +144 -0
  17. package/adapters/_shared/md-to-toml.cjs +54 -0
  18. package/adapters/_shared/path-rewrite.cjs +79 -0
  19. package/adapters/_shared/rtk-detect.cjs +86 -0
  20. package/adapters/_shared/runtime-assets.cjs +70 -0
  21. package/adapters/_shared/settings-merge.cjs +76 -0
  22. package/adapters/_shared/tests/file-ops.test.cjs +54 -0
  23. package/adapters/_shared/tests/frontmatter.test.cjs +47 -0
  24. package/adapters/_shared/tests/handbook.test.cjs +86 -0
  25. package/adapters/_shared/tests/md-to-toml.test.cjs +48 -0
  26. package/adapters/_shared/tests/path-rewrite.test.cjs +60 -0
  27. package/adapters/_shared/tests/resolve-target.test.cjs +85 -0
  28. package/adapters/_shared/tests/rtk-detect.test.cjs +116 -0
  29. package/adapters/_shared/tests/runtime-assets.test.cjs +107 -0
  30. package/adapters/_shared/tests/settings-merge.test.cjs +85 -0
  31. package/adapters/_shared/tests/validate.test.cjs +30 -0
  32. package/adapters/_shared/validate.cjs +24 -0
  33. package/adapters/claude.js +182 -0
  34. package/adapters/codex.js +385 -0
  35. package/adapters/gemini.js +204 -0
  36. package/adapters/opencode.js +506 -0
  37. package/adapters/tests/claude.test.cjs +117 -0
  38. package/adapters/tests/codex-hook.test.cjs +90 -0
  39. package/adapters/tests/codex.test.cjs +356 -0
  40. package/adapters/tests/gemini.test.cjs +112 -0
  41. package/adapters/tests/opencode.test.cjs +246 -0
  42. package/agents/code-reviewer.md +102 -0
  43. package/agents/debugger.md +127 -0
  44. package/agents/review-code.md +125 -0
  45. package/agents/review-comments.md +98 -0
  46. package/agents/review-coordinator.md +146 -0
  47. package/agents/review-design.md +133 -0
  48. package/agents/review-errors.md +87 -0
  49. package/agents/review-tests.md +93 -0
  50. package/agents/tdd-runner.md +92 -0
  51. package/bin/handbook.js +54 -0
  52. package/bin/install.js +254 -0
  53. package/bin/orchestrator.js +214 -0
  54. package/commands/learn.md +41 -0
  55. package/commands/ultra-deliver.md +45 -0
  56. package/commands/ultra-dev.md +50 -0
  57. package/commands/ultra-init.md +51 -0
  58. package/commands/ultra-plan.md +49 -0
  59. package/commands/ultra-research.md +59 -0
  60. package/commands/ultra-status.md +46 -0
  61. package/commands/ultra-test.md +48 -0
  62. package/commands/ultra-think.md +45 -0
  63. package/docs/AGENT-CONTEXT.md +143 -0
  64. package/docs/ARCHITECTURE.md +197 -0
  65. package/docs/COMMIT-HASH-BACKFILL.md +101 -0
  66. package/docs/LEGACY-HERMES.md +1175 -0
  67. package/docs/PLAN.zh-CN.md +1683 -0
  68. package/docs/ROADMAP.md +102 -0
  69. package/docs/RUNTIME-COMPAT-MATRIX.md +98 -0
  70. package/docs/STATE-DB-ACCESS-POLICY.md +109 -0
  71. package/docs/USER-HANDBOOK-CONTRACT.md +61 -0
  72. package/hooks/README.md +24 -0
  73. package/hooks/active_task_context.py +44 -0
  74. package/hooks/adapters/codex.py +302 -0
  75. package/hooks/health_check.py +61 -0
  76. package/hooks/pre_stop_check.py +56 -0
  77. package/hooks/subagent_tracker.py +116 -0
  78. package/hooks/workflow_checkpoint.py +56 -0
  79. package/hooks/workflow_context.py +63 -0
  80. package/hooks/workflow_resume.py +50 -0
  81. package/mcp-server/lib/init-project.cjs +162 -0
  82. package/mcp-server/lib/llm-client.cjs +157 -0
  83. package/mcp-server/lib/llm-client.test.cjs +156 -0
  84. package/mcp-server/lib/plan-store.cjs +96 -0
  85. package/mcp-server/lib/plan-store.test.cjs +132 -0
  86. package/mcp-server/lib/prd-parser.cjs +110 -0
  87. package/mcp-server/lib/prd-parser.test.cjs +171 -0
  88. package/mcp-server/lib/pricing.cjs +53 -0
  89. package/mcp-server/lib/projector.cjs +158 -0
  90. package/mcp-server/lib/state-db.cjs +113 -0
  91. package/mcp-server/lib/state-db.test.cjs +116 -0
  92. package/mcp-server/lib/state-ops.cjs +725 -0
  93. package/mcp-server/lib/task-expander.cjs +123 -0
  94. package/mcp-server/lib/task-expander.test.cjs +253 -0
  95. package/mcp-server/lib/telemetry.cjs +72 -0
  96. package/mcp-server/lib/topo.cjs +131 -0
  97. package/mcp-server/lib/topo.test.cjs +158 -0
  98. package/mcp-server/server.cjs +482 -0
  99. package/mcp-server/tests/circuit-breaker.test.cjs +125 -0
  100. package/mcp-server/tests/commit-hash-flow.test.cjs +117 -0
  101. package/mcp-server/tests/concurrency-update.test.cjs +96 -0
  102. package/mcp-server/tests/concurrency.test.cjs +112 -0
  103. package/mcp-server/tests/db-ops.test.cjs +117 -0
  104. package/mcp-server/tests/init-project.test.cjs +141 -0
  105. package/mcp-server/tests/migrate.test.cjs +144 -0
  106. package/mcp-server/tests/projector.test.cjs +130 -0
  107. package/mcp-server/tests/server-contract.test.cjs +750 -0
  108. package/mcp-server/tests/staleness.test.cjs +190 -0
  109. package/mcp-server/tests/state-ops.test.cjs +286 -0
  110. package/mcp-server/tests/tagged-tasks.test.cjs +163 -0
  111. package/mcp-server/tests/telemetry.test.cjs +205 -0
  112. package/orchestrator/auto-merge.cjs +109 -0
  113. package/orchestrator/code-graph-watcher.cjs +121 -0
  114. package/orchestrator/daemon.cjs +112 -0
  115. package/orchestrator/dispatch-rules.cjs +93 -0
  116. package/orchestrator/parallel-orchestrator.cjs +195 -0
  117. package/orchestrator/planner/expand-prompt.cjs +62 -0
  118. package/orchestrator/planner/expand-prompt.test.cjs +52 -0
  119. package/orchestrator/planner/plan-builder.cjs +116 -0
  120. package/orchestrator/planner/plan-builder.test.cjs +165 -0
  121. package/orchestrator/planner/prd-prompt.cjs +44 -0
  122. package/orchestrator/planner/prd-prompt.test.cjs +30 -0
  123. package/orchestrator/recovery.cjs +70 -0
  124. package/orchestrator/session-runner.cjs +224 -0
  125. package/orchestrator/skill-miner.cjs +133 -0
  126. package/orchestrator/tests/auto-merge.test.cjs +216 -0
  127. package/orchestrator/tests/code-graph-watcher.test.cjs +183 -0
  128. package/orchestrator/tests/daemon.test.cjs +260 -0
  129. package/orchestrator/tests/dispatch-rules.test.cjs +122 -0
  130. package/orchestrator/tests/parallel-orchestrator.test.cjs +233 -0
  131. package/orchestrator/tests/phase-8b-gate.test.cjs +183 -0
  132. package/orchestrator/tests/recovery.test.cjs +190 -0
  133. package/orchestrator/tests/session-runner.test.cjs +314 -0
  134. package/orchestrator/tests/skill-miner.test.cjs +147 -0
  135. package/orchestrator/tests/worktree-manager.test.cjs +148 -0
  136. package/orchestrator/worktree-manager.cjs +131 -0
  137. package/output-styles/coding-vibes.md +67 -0
  138. package/output-styles/structural-thinking.md +61 -0
  139. package/package.json +109 -0
  140. package/settings.json +8 -0
  141. package/skills/cc-collab/SKILL.md +41 -0
  142. package/skills/code-review-expert/SKILL.md +118 -0
  143. package/skills/code-review-expert/references/code-quality-checklist.md +112 -0
  144. package/skills/code-review-expert/references/integration-checklist.md +46 -0
  145. package/skills/code-review-expert/references/removal-plan.md +52 -0
  146. package/skills/code-review-expert/references/security-checklist.md +100 -0
  147. package/skills/code-review-expert/references/solid-checklist.md +65 -0
  148. package/skills/codex-collab/SKILL.md +52 -0
  149. package/skills/codex-collab/evals/evals.json +40 -0
  150. package/skills/gemini-collab/SKILL.md +51 -0
  151. package/skills/gemini-collab/evals/evals.json +42 -0
  152. package/skills/integration-rules/SKILL.md +71 -0
  153. package/skills/learn/SKILL.md +117 -0
  154. package/skills/security-rules/SKILL.md +60 -0
  155. package/skills/testing-rules/SKILL.md +56 -0
  156. package/skills/ultra-deliver/SKILL.md +171 -0
  157. package/skills/ultra-dev/SKILL.md +324 -0
  158. package/skills/ultra-init/SKILL.md +148 -0
  159. package/skills/ultra-plan/SKILL.md +314 -0
  160. package/skills/ultra-research/SKILL.md +156 -0
  161. package/skills/ultra-research/steps/step-00-problem-validation.md +154 -0
  162. package/skills/ultra-research/steps/step-01-opportunity-discovery.md +172 -0
  163. package/skills/ultra-research/steps/step-02-market-assessment.md +195 -0
  164. package/skills/ultra-research/steps/step-03-competitive-landscape.md +197 -0
  165. package/skills/ultra-research/steps/step-04-product-strategy.md +196 -0
  166. package/skills/ultra-research/steps/step-05-assumptions-validation.md +234 -0
  167. package/skills/ultra-research/steps/step-10-user-personas.md +169 -0
  168. package/skills/ultra-research/steps/step-11-user-scenarios.md +181 -0
  169. package/skills/ultra-research/steps/step-20-user-stories.md +156 -0
  170. package/skills/ultra-research/steps/step-21-features-scope.md +157 -0
  171. package/skills/ultra-research/steps/step-22-success-metrics.md +195 -0
  172. package/skills/ultra-research/steps/step-30-architecture-context.md +154 -0
  173. package/skills/ultra-research/steps/step-31-solution-strategy.md +172 -0
  174. package/skills/ultra-research/steps/step-32-building-blocks.md +188 -0
  175. package/skills/ultra-research/steps/step-40-deployment.md +123 -0
  176. package/skills/ultra-research/steps/step-41-quality-risks.md +135 -0
  177. package/skills/ultra-research/steps/step-99-synthesis.md +188 -0
  178. package/skills/ultra-review/SKILL.md +437 -0
  179. package/skills/ultra-review/references/unified-schema.md +127 -0
  180. package/skills/ultra-review/scripts/review_verdict_update.py +99 -0
  181. package/skills/ultra-review/scripts/review_wait.py +120 -0
  182. package/skills/ultra-status/SKILL.md +162 -0
  183. package/skills/ultra-test/SKILL.md +195 -0
  184. package/skills/ultra-think/SKILL.md +109 -0
  185. package/skills/ultra-verify/SKILL.md +111 -0
  186. package/skills/ultra-verify/evals/evals.json +51 -0
  187. package/skills/ultra-verify/references/confidence-system.md +38 -0
  188. package/skills/ultra-verify/references/cross-verify-modes.md +73 -0
  189. package/skills/ultra-verify/scripts/verify_wait.py +131 -0
  190. package/spec/cli-protocol.md +118 -0
  191. package/spec/mcp-tools.yaml +658 -0
  192. package/spec/schemas/command-manifest.schema.json +47 -0
  193. package/spec/schemas/context-file.v4.5.schema.json +54 -0
  194. package/spec/schemas/mcp-tools.schema.json +136 -0
  195. package/spec/schemas/skill-manifest.schema.json +94 -0
  196. package/spec/schemas/state-db.sql +154 -0
  197. package/spec/schemas/tasks.v4.5.schema.json +66 -0
  198. package/ultra-tools/cli.cjs +121 -0
  199. package/ultra-tools/commands/db.cjs +175 -0
  200. package/ultra-tools/commands/legacy-memory.cjs +159 -0
  201. package/ultra-tools/commands/migrate.cjs +354 -0
  202. package/ultra-tools/commands/session.cjs +180 -0
  203. package/ultra-tools/commands/status.cjs +158 -0
  204. package/ultra-tools/commands/task.cjs +95 -0
  205. package/ultra-tools/legacy-memory.test.cjs +80 -0
  206. package/ultra-tools/status-command.test.cjs +132 -0
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ const { test } = require('node:test');
4
+ const assert = require('node:assert/strict');
5
+
6
+ const fs = require('node:fs');
7
+ const os = require('node:os');
8
+ const path = require('node:path');
9
+
10
+ const {
11
+ SENTINEL_VERSION,
12
+ readJsonSafe,
13
+ withSentinelBlock,
14
+ removeSentinelBlock,
15
+ hasSentinelBlock,
16
+ mergeArrayField,
17
+ } = require('../settings-merge.cjs');
18
+
19
+ test('withSentinelBlock writes under the sentinel key only; user config untouched', () => {
20
+ const existing = { user_prop: 'keep me', hooks: [{ name: 'user-hook' }] };
21
+ const out = withSentinelBlock(existing, 'ultra_builder_pro', {
22
+ mcp: { ubp: { command: 'node' } },
23
+ });
24
+
25
+ assert.equal(out.user_prop, 'keep me');
26
+ assert.deepEqual(out.hooks, [{ name: 'user-hook' }]);
27
+ assert.equal(out.ultra_builder_pro.__sentinel, SENTINEL_VERSION);
28
+ assert.equal(out.ultra_builder_pro.__generated_by, 'ultra-builder-pro-cli');
29
+ assert.deepEqual(out.ultra_builder_pro.mcp.ubp, { command: 'node' });
30
+ });
31
+
32
+ test('removeSentinelBlock leaves unrelated keys intact; noop if absent', () => {
33
+ const with_ = { a: 1, mine: { __sentinel: 1, x: 2 } };
34
+ const stripped = removeSentinelBlock(with_, 'mine');
35
+ assert.deepEqual(stripped, { a: 1 });
36
+ assert.ok(!hasSentinelBlock(stripped, 'mine'));
37
+
38
+ const noop = removeSentinelBlock({ a: 1 }, 'mine');
39
+ assert.deepEqual(noop, { a: 1 });
40
+ });
41
+
42
+ test('mergeArrayField dedupes; readJsonSafe handles missing/empty/malformed', () => {
43
+ const existing = { hooks: [{ name: 'user' }, { name: 'ubp-one' }] };
44
+ const out = mergeArrayField(existing, 'hooks', [
45
+ { name: 'ubp-one' }, // dup — skipped
46
+ { name: 'ubp-two' },
47
+ ]);
48
+ assert.deepEqual(out.hooks, [
49
+ { name: 'user' },
50
+ { name: 'ubp-one' },
51
+ { name: 'ubp-two' },
52
+ ]);
53
+
54
+ // readJsonSafe: missing file → empty object
55
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ubp-merge-'));
56
+ try {
57
+ assert.deepEqual(readJsonSafe(path.join(tmp, 'none.json')), {});
58
+
59
+ const p = path.join(tmp, 'existing.json');
60
+ fs.writeFileSync(p, '{"a":1}');
61
+ assert.deepEqual(readJsonSafe(p), { a: 1 });
62
+
63
+ // empty file treated as empty object
64
+ fs.writeFileSync(p, ' \n ');
65
+ assert.deepEqual(readJsonSafe(p), {});
66
+
67
+ // malformed → throws
68
+ fs.writeFileSync(p, '{not json');
69
+ assert.throws(() => readJsonSafe(p), /cannot parse/);
70
+
71
+ // P2 #7 / D45: rescue mode backs up corrupt file and returns {} instead
72
+ // of throwing — lets `install` finish even if the user's settings.json
73
+ // is syntactically broken.
74
+ fs.writeFileSync(p, '{still not json');
75
+ const rescued = readJsonSafe(p, { rescue: true });
76
+ assert.deepEqual(rescued, {});
77
+ const backups = fs.readdirSync(tmp).filter((f) => f.startsWith('existing.json.bak-'));
78
+ assert.ok(backups.length > 0, `expected a .bak-* file in ${tmp}, saw ${fs.readdirSync(tmp).join(',')}`);
79
+ } finally {
80
+ fs.rmSync(tmp, { recursive: true, force: true });
81
+ }
82
+
83
+ // withSentinelBlock rejects bad key
84
+ assert.throws(() => withSentinelBlock({}, '', {}), /non-empty string/);
85
+ });
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ // Phase 4 backlog P3 #13 / D45 — configDir validator.
4
+
5
+ const { test } = require('node:test');
6
+ const assert = require('node:assert/strict');
7
+
8
+ const { validateConfigDir } = require('../validate.cjs');
9
+
10
+ test('validateConfigDir: null/undefined → ok with configDir=null', () => {
11
+ assert.deepEqual(validateConfigDir(null), { ok: true, configDir: null });
12
+ assert.deepEqual(validateConfigDir(undefined), { ok: true, configDir: null });
13
+ });
14
+
15
+ test('validateConfigDir: normal path → ok', () => {
16
+ const out = validateConfigDir('/tmp/work');
17
+ assert.equal(out.ok, true);
18
+ assert.equal(out.configDir, '/tmp/work');
19
+ });
20
+
21
+ test('validateConfigDir: rejects NUL byte', () => {
22
+ const out = validateConfigDir('/tmp/foo\u0000bar');
23
+ assert.equal(out.ok, false);
24
+ assert.match(out.error, /NUL bytes/);
25
+ });
26
+
27
+ test('validateConfigDir: rejects non-string', () => {
28
+ assert.equal(validateConfigDir(42).ok, false);
29
+ assert.equal(validateConfigDir({}).ok, false);
30
+ });
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ // Argument-boundary validators shared by bin/install.js (and anything else
4
+ // that accepts user-supplied paths). Keep small and pure: input → verdict.
5
+
6
+ function validateConfigDir(configDir) {
7
+ if (configDir === undefined || configDir === null) {
8
+ return { ok: true, configDir: null };
9
+ }
10
+ if (typeof configDir !== 'string') {
11
+ return { ok: false, error: '--config-dir must be a string' };
12
+ }
13
+ if (configDir.includes('\0')) {
14
+ // P3 #13 / D45 — NUL bytes in a path are a classic path-traversal
15
+ // smuggling vector. Node's child_process layer also rejects NUL in
16
+ // argv, but defense-in-depth is cheap here.
17
+ return { ok: false, error: '--config-dir must not contain NUL bytes (path traversal)' };
18
+ }
19
+ return { ok: true, configDir };
20
+ }
21
+
22
+ module.exports = {
23
+ validateConfigDir,
24
+ };
@@ -0,0 +1,182 @@
1
+ 'use strict';
2
+
3
+ /** Build a Claude Code native Ultra Builder Pro plugin. */
4
+
5
+ const fs = require('node:fs');
6
+ const os = require('node:os');
7
+ const path = require('node:path');
8
+
9
+ const {
10
+ copyTree,
11
+ ensureDir,
12
+ isManaged,
13
+ markManaged,
14
+ removeTree,
15
+ writeAtomic,
16
+ } = require('./_shared/file-ops.cjs');
17
+ const { buildMcpRuntime } = require('./_shared/codex-assets.cjs');
18
+ const {
19
+ CORE_PUBLIC_SKILLS,
20
+ WORKFLOW_HOOK_FILES,
21
+ skillsForRuntime,
22
+ } = require('./_shared/runtime-assets.cjs');
23
+
24
+ const PLUGIN_NAME = 'ultra-builder-pro';
25
+ const MCP_SERVER_NAME = PLUGIN_NAME;
26
+ const SOURCE_TAG = 'ubp';
27
+ const SENTINEL_KEY = '_ubp_manifest';
28
+ const COMMAND_NAMES = CORE_PUBLIC_SKILLS.filter((name) => name !== 'ultra-review');
29
+
30
+ function resolveTarget(ctx = {}) {
31
+ if (ctx.configDir) return ctx.configDir;
32
+ if (ctx.scope === 'global') {
33
+ return process.env.CLAUDE_CONFIG_DIR || path.join(ctx.homeDir || os.homedir(), '.claude');
34
+ }
35
+ return path.join(ctx.cwd || process.cwd(), '.claude');
36
+ }
37
+
38
+ function resolvePluginRoot(ctx = {}) {
39
+ if (ctx.pluginRoot) return ctx.pluginRoot;
40
+ return path.join(resolveTarget(ctx), 'skills', PLUGIN_NAME);
41
+ }
42
+
43
+ function resolveRepoRoot(ctx = {}) {
44
+ return ctx.repoRoot || path.resolve(__dirname, '..');
45
+ }
46
+
47
+ function copyNamedDirectories(repoRoot, target, parent, names) {
48
+ const copied = [];
49
+ for (const name of names) {
50
+ const source = path.join(repoRoot, parent, name);
51
+ if (!fs.existsSync(source)) throw new Error(`missing allowlisted Claude asset: ${parent}/${name}`);
52
+ const files = copyTree(source, path.join(target, parent, name));
53
+ copied.push(...files.map((rel) => path.join(name, rel)));
54
+ }
55
+ return copied;
56
+ }
57
+
58
+ function copyCommands(repoRoot, target) {
59
+ const commandRoot = path.join(target, 'commands');
60
+ ensureDir(commandRoot);
61
+ const copied = [];
62
+ for (const name of COMMAND_NAMES) {
63
+ const file = `${name}.md`;
64
+ const source = path.join(repoRoot, 'commands', file);
65
+ if (!fs.existsSync(source)) throw new Error(`missing allowlisted Claude command: ${file}`);
66
+ fs.copyFileSync(source, path.join(commandRoot, file));
67
+ copied.push(file);
68
+ }
69
+ return copied;
70
+ }
71
+
72
+ function hookCommand(name, ...args) {
73
+ return {
74
+ type: 'command',
75
+ command: [
76
+ `python3 "\${CLAUDE_PLUGIN_ROOT}/hooks/${name}"`,
77
+ ...args.map((arg) => JSON.stringify(arg)),
78
+ ].join(' '),
79
+ timeout: 10,
80
+ };
81
+ }
82
+
83
+ function buildHooksManifest() {
84
+ return {
85
+ description: 'Ultra workflow lifecycle hooks; inactive outside projects with an active .ultra workflow.',
86
+ hooks: {
87
+ SessionStart: [
88
+ {
89
+ matcher: 'startup|resume|clear',
90
+ hooks: [hookCommand('health_check.py'), hookCommand('workflow_context.py')],
91
+ },
92
+ { matcher: 'compact', hooks: [hookCommand('workflow_resume.py')] },
93
+ ],
94
+ PreToolUse: [
95
+ { matcher: 'Edit|Write', hooks: [hookCommand('active_task_context.py')] },
96
+ ],
97
+ PreCompact: [
98
+ { hooks: [hookCommand('workflow_checkpoint.py')] },
99
+ ],
100
+ Stop: [
101
+ { hooks: [hookCommand('pre_stop_check.py')] },
102
+ ],
103
+ SubagentStart: [
104
+ { hooks: [hookCommand('subagent_tracker.py', 'start')] },
105
+ ],
106
+ SubagentStop: [
107
+ { hooks: [hookCommand('subagent_tracker.py', 'stop')] },
108
+ ],
109
+ },
110
+ };
111
+ }
112
+
113
+ function install(ctx = {}) {
114
+ const target = resolvePluginRoot(ctx);
115
+ const repoRoot = resolveRepoRoot(ctx);
116
+ if (fs.existsSync(target)) {
117
+ const entries = fs.readdirSync(target);
118
+ if (entries.length > 0 && !isManaged(target)) {
119
+ throw new Error(`refusing to replace unmanaged Claude plugin: ${target}`);
120
+ }
121
+ if (entries.length > 0) removeTree(target);
122
+ }
123
+ ensureDir(target);
124
+
125
+ const report = { target, copied: {}, config: { updated: false } };
126
+ report.copied.commands = copyCommands(repoRoot, target);
127
+ report.copied.skills = copyNamedDirectories(repoRoot, target, 'skills', skillsForRuntime('claude'));
128
+ report.copied.agents = copyTree(path.join(repoRoot, 'agents'), path.join(target, 'agents'));
129
+
130
+ ensureDir(path.join(target, 'hooks'));
131
+ report.copied.hooks = [];
132
+ for (const name of WORKFLOW_HOOK_FILES) {
133
+ fs.copyFileSync(path.join(repoRoot, 'hooks', name), path.join(target, 'hooks', name));
134
+ report.copied.hooks.push(name);
135
+ }
136
+ writeAtomic(path.join(target, 'hooks', 'hooks.json'), JSON.stringify(buildHooksManifest(), null, 2) + '\n');
137
+
138
+ const runtime = buildMcpRuntime(repoRoot, target, { runtime: 'claude' });
139
+ writeAtomic(path.join(target, '.mcp.json'), JSON.stringify({
140
+ mcpServers: {
141
+ [MCP_SERVER_NAME]: {
142
+ command: process.execPath,
143
+ args: [runtime.launcher],
144
+ },
145
+ },
146
+ }, null, 2) + '\n');
147
+
148
+ const pkg = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
149
+ writeAtomic(path.join(target, '.claude-plugin', 'plugin.json'), JSON.stringify({
150
+ name: PLUGIN_NAME,
151
+ version: pkg.version,
152
+ description: 'Claude Code native Ultra Builder Pro workflows, agents, hooks, and MCP task state.',
153
+ author: { name: typeof pkg.author === 'string' ? pkg.author : 'Ultra Builder Pro contributors' },
154
+ repository: pkg.homepage,
155
+ license: pkg.license || 'MIT',
156
+ }, null, 2) + '\n');
157
+ markManaged(target, { adapter: 'claude', plugin: PLUGIN_NAME });
158
+ return report;
159
+ }
160
+
161
+ function uninstall(ctx = {}) {
162
+ const target = resolvePluginRoot(ctx);
163
+ const report = { target, removed: {}, config: { updated: false } };
164
+ if (fs.existsSync(target) && isManaged(target)) {
165
+ removeTree(target);
166
+ report.removed.plugin = true;
167
+ }
168
+ return report;
169
+ }
170
+
171
+ module.exports = {
172
+ name: 'claude',
173
+ PLUGIN_NAME,
174
+ SENTINEL_KEY,
175
+ SOURCE_TAG,
176
+ MCP_SERVER_NAME,
177
+ buildHooksManifest,
178
+ resolveTarget,
179
+ resolvePluginRoot,
180
+ install,
181
+ uninstall,
182
+ };