@uluops/setup 0.2.0 → 0.6.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 (253) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +109 -89
  3. package/assets/auto-tracker-save.mjs +142 -0
  4. package/assets/claude-code/agents/anxiety-reader-agent.md +464 -0
  5. package/assets/{agents → claude-code/agents}/api-contract-validator-agent.md +9 -228
  6. package/assets/{agents → claude-code/agents}/aristotle-analyst-agent.md +51 -4
  7. package/assets/{agents → claude-code/agents}/aristotle-explorer-agent.md +6 -2
  8. package/assets/{agents → claude-code/agents}/aristotle-forecaster-agent.md +15 -230
  9. package/assets/{agents → claude-code/agents}/aristotle-validator-agent.md +12 -252
  10. package/assets/{agents → claude-code/agents}/assumption-excavator-agent.md +21 -247
  11. package/assets/{agents → claude-code/agents}/code-auditor-agent.md +12 -255
  12. package/assets/{agents → claude-code/agents}/code-optimizer-agent.md +15 -236
  13. package/assets/{agents → claude-code/agents}/code-validator-agent.md +31 -300
  14. package/assets/claude-code/agents/docs-validator-agent.md +472 -0
  15. package/assets/{agents → claude-code/agents}/frontend-validator-agent.md +15 -258
  16. package/assets/{agents → claude-code/agents}/mcp-validator-agent.md +8 -252
  17. package/assets/{agents → claude-code/agents}/pre-implementation-architect-agent.md +8 -224
  18. package/assets/{agents → claude-code/agents}/prompt-engineer-agent.md +57 -290
  19. package/assets/{agents → claude-code/agents}/prompt-pattern-analyzer-agent.md +10 -225
  20. package/assets/{agents → claude-code/agents}/prompt-quality-validator-agent.md +11 -249
  21. package/assets/{agents → claude-code/agents}/public-interface-validator-agent.md +15 -268
  22. package/assets/claude-code/agents/release-readiness-agent.md +495 -0
  23. package/assets/{agents → claude-code/agents}/security-analyst-agent.md +236 -480
  24. package/assets/{agents → claude-code/agents}/test-architect-agent.md +16 -259
  25. package/assets/{agents → claude-code/agents}/type-safety-validator-agent.md +23 -266
  26. package/assets/{agents → claude-code/agents}/workflow-synthesis-agent.md +23 -226
  27. package/assets/claude-code/commands/agents/anxiety-reader.md +157 -0
  28. package/assets/{commands → claude-code/commands}/agents/api-contract.md +156 -135
  29. package/assets/{commands → claude-code/commands}/agents/architect.md +156 -135
  30. package/assets/claude-code/commands/agents/aristotle-analyst.md +157 -0
  31. package/assets/claude-code/commands/agents/aristotle-explorer.md +157 -0
  32. package/assets/claude-code/commands/agents/aristotle-forecaster.md +157 -0
  33. package/assets/claude-code/commands/agents/aristotle-validator.md +157 -0
  34. package/assets/{commands → claude-code/commands}/agents/assumption-excavator.md +49 -6
  35. package/assets/{commands → claude-code/commands}/agents/audit.md +156 -136
  36. package/assets/{commands → claude-code/commands}/agents/docs-validate.md +156 -133
  37. package/assets/{commands → claude-code/commands}/agents/frontend.md +156 -135
  38. package/assets/{commands → claude-code/commands}/agents/mcp-validate.md +156 -136
  39. package/assets/{commands → claude-code/commands}/agents/optimize.md +156 -133
  40. package/assets/{commands → claude-code/commands}/agents/pattern-analyzer.md +150 -126
  41. package/assets/{commands → claude-code/commands}/agents/prompt-quality.md +155 -134
  42. package/assets/claude-code/commands/agents/prompt-validate.md +155 -0
  43. package/assets/{commands → claude-code/commands}/agents/public-interface.md +156 -134
  44. package/assets/{commands → claude-code/commands}/agents/release.md +156 -135
  45. package/assets/{commands → claude-code/commands}/agents/security.md +156 -137
  46. package/assets/{commands → claude-code/commands}/agents/test-review.md +156 -136
  47. package/assets/{commands → claude-code/commands}/agents/type-safety.md +156 -135
  48. package/assets/{commands → claude-code/commands}/agents/validate.md +156 -134
  49. package/assets/claude-code/commands/agents/workflow-synthesis.md +157 -0
  50. package/assets/claude-code/commands/pipelines/aristotle.md +143 -0
  51. package/assets/claude-code/commands/pipelines/ship.md +188 -0
  52. package/assets/claude-code/commands/workflows/post-implementation.md +60 -0
  53. package/assets/claude-code/commands/workflows/pre-implementation.md +46 -0
  54. package/assets/claude-code/commands/workflows/prompt-audit.md +44 -0
  55. package/assets/codex/agents/anxiety-reader-agent.toml +462 -0
  56. package/assets/codex/agents/api-contract-validator-agent.toml +738 -0
  57. package/assets/codex/agents/aristotle-analyst-agent.toml +750 -0
  58. package/assets/codex/agents/aristotle-explorer-agent.toml +155 -0
  59. package/assets/codex/agents/aristotle-forecaster-agent.toml +449 -0
  60. package/assets/codex/agents/aristotle-validator-agent.toml +424 -0
  61. package/assets/codex/agents/assumption-excavator-agent.toml +1126 -0
  62. package/assets/codex/agents/code-auditor-agent.toml +815 -0
  63. package/assets/codex/agents/code-optimizer-agent.toml +652 -0
  64. package/assets/codex/agents/code-validator-agent.toml +573 -0
  65. package/assets/codex/agents/docs-validator-agent.toml +468 -0
  66. package/assets/codex/agents/frontend-validator-agent.toml +598 -0
  67. package/assets/codex/agents/mcp-validator-agent.toml +580 -0
  68. package/assets/codex/agents/pre-implementation-architect-agent.toml +817 -0
  69. package/assets/codex/agents/prompt-engineer-agent.toml +922 -0
  70. package/assets/codex/agents/prompt-pattern-analyzer-agent.toml +689 -0
  71. package/assets/codex/agents/prompt-quality-validator-agent.toml +777 -0
  72. package/assets/codex/agents/public-interface-validator-agent.toml +695 -0
  73. package/assets/codex/agents/release-readiness-agent.toml +491 -0
  74. package/assets/codex/agents/security-analyst-agent.toml +847 -0
  75. package/assets/codex/agents/test-architect-agent.toml +615 -0
  76. package/assets/codex/agents/type-safety-validator-agent.toml +686 -0
  77. package/assets/codex/agents/workflow-synthesis-agent.toml +631 -0
  78. package/assets/gemini-cli/agents/anxiety-reader-agent.md +470 -0
  79. package/assets/gemini-cli/agents/api-contract-validator-agent.md +747 -0
  80. package/assets/gemini-cli/agents/aristotle-analyst-agent.md +758 -0
  81. package/assets/gemini-cli/agents/aristotle-explorer-agent.md +163 -0
  82. package/assets/gemini-cli/agents/aristotle-forecaster-agent.md +457 -0
  83. package/assets/gemini-cli/agents/aristotle-validator-agent.md +432 -0
  84. package/assets/gemini-cli/agents/assumption-excavator-agent.md +1134 -0
  85. package/assets/gemini-cli/agents/code-auditor-agent.md +827 -0
  86. package/assets/gemini-cli/agents/code-optimizer-agent.md +661 -0
  87. package/assets/gemini-cli/agents/code-validator-agent.md +582 -0
  88. package/assets/gemini-cli/agents/docs-validator-agent.md +477 -0
  89. package/assets/gemini-cli/agents/frontend-validator-agent.md +610 -0
  90. package/assets/gemini-cli/agents/mcp-validator-agent.md +589 -0
  91. package/assets/gemini-cli/agents/pre-implementation-architect-agent.md +826 -0
  92. package/assets/gemini-cli/agents/prompt-engineer-agent.md +931 -0
  93. package/assets/gemini-cli/agents/prompt-pattern-analyzer-agent.md +698 -0
  94. package/assets/gemini-cli/agents/prompt-quality-validator-agent.md +786 -0
  95. package/assets/gemini-cli/agents/public-interface-validator-agent.md +707 -0
  96. package/assets/gemini-cli/agents/release-readiness-agent.md +500 -0
  97. package/assets/gemini-cli/agents/security-analyst-agent.md +859 -0
  98. package/assets/gemini-cli/agents/test-architect-agent.md +624 -0
  99. package/assets/gemini-cli/agents/type-safety-validator-agent.md +695 -0
  100. package/assets/gemini-cli/agents/workflow-synthesis-agent.md +639 -0
  101. package/assets/gemini-cli/commands/agents/anxiety-reader.toml +155 -0
  102. package/assets/gemini-cli/commands/agents/api-contract.toml +154 -0
  103. package/assets/gemini-cli/commands/agents/architect.toml +154 -0
  104. package/assets/gemini-cli/commands/agents/aristotle-analyst.toml +155 -0
  105. package/assets/gemini-cli/commands/agents/aristotle-explorer.toml +155 -0
  106. package/assets/gemini-cli/commands/agents/aristotle-forecaster.toml +155 -0
  107. package/assets/gemini-cli/commands/agents/aristotle-validator.toml +155 -0
  108. package/assets/gemini-cli/commands/agents/assumption-excavator.toml +155 -0
  109. package/assets/gemini-cli/commands/agents/audit.toml +154 -0
  110. package/assets/gemini-cli/commands/agents/docs-validate.toml +154 -0
  111. package/assets/gemini-cli/commands/agents/frontend.toml +154 -0
  112. package/assets/gemini-cli/commands/agents/mcp-validate.toml +154 -0
  113. package/assets/gemini-cli/commands/agents/optimize.toml +154 -0
  114. package/assets/gemini-cli/commands/agents/pattern-analyzer.toml +148 -0
  115. package/assets/gemini-cli/commands/agents/prompt-quality.toml +153 -0
  116. package/assets/gemini-cli/commands/agents/prompt-validate.toml +153 -0
  117. package/assets/gemini-cli/commands/agents/public-interface.toml +154 -0
  118. package/assets/gemini-cli/commands/agents/release.toml +154 -0
  119. package/assets/gemini-cli/commands/agents/security.toml +154 -0
  120. package/assets/gemini-cli/commands/agents/test-review.toml +154 -0
  121. package/assets/gemini-cli/commands/agents/type-safety.toml +154 -0
  122. package/assets/gemini-cli/commands/agents/validate.toml +154 -0
  123. package/assets/gemini-cli/commands/agents/workflow-synthesis.toml +155 -0
  124. package/assets/gemini-cli/commands/pipelines/aristotle.toml +139 -0
  125. package/assets/gemini-cli/commands/pipelines/ship.toml +184 -0
  126. package/assets/gemini-cli/commands/workflows/post-implementation.toml +56 -0
  127. package/assets/gemini-cli/commands/workflows/pre-implementation.toml +42 -0
  128. package/assets/gemini-cli/commands/workflows/prompt-audit.toml +40 -0
  129. package/assets/opencode/agents/anxiety-reader-agent.md +472 -0
  130. package/assets/opencode/agents/api-contract-validator-agent.md +749 -0
  131. package/assets/opencode/agents/aristotle-analyst-agent.md +760 -0
  132. package/assets/opencode/agents/aristotle-explorer-agent.md +164 -0
  133. package/assets/opencode/agents/aristotle-forecaster-agent.md +459 -0
  134. package/assets/opencode/agents/aristotle-validator-agent.md +434 -0
  135. package/assets/opencode/agents/assumption-excavator-agent.md +1136 -0
  136. package/assets/opencode/agents/code-auditor-agent.md +826 -0
  137. package/assets/opencode/agents/code-optimizer-agent.md +663 -0
  138. package/assets/opencode/agents/code-validator-agent.md +584 -0
  139. package/assets/opencode/agents/docs-validator-agent.md +479 -0
  140. package/assets/opencode/agents/frontend-validator-agent.md +609 -0
  141. package/assets/opencode/agents/mcp-validator-agent.md +591 -0
  142. package/assets/opencode/agents/pre-implementation-architect-agent.md +828 -0
  143. package/assets/opencode/agents/prompt-engineer-agent.md +933 -0
  144. package/assets/opencode/agents/prompt-pattern-analyzer-agent.md +700 -0
  145. package/assets/opencode/agents/prompt-quality-validator-agent.md +788 -0
  146. package/assets/opencode/agents/public-interface-validator-agent.md +706 -0
  147. package/assets/opencode/agents/release-readiness-agent.md +502 -0
  148. package/assets/opencode/agents/security-analyst-agent.md +858 -0
  149. package/assets/opencode/agents/test-architect-agent.md +626 -0
  150. package/assets/opencode/agents/type-safety-validator-agent.md +697 -0
  151. package/assets/opencode/agents/workflow-synthesis-agent.md +641 -0
  152. package/dist/cli.js +22 -380
  153. package/dist/commands/helpers.d.ts +73 -0
  154. package/dist/commands/helpers.js +274 -0
  155. package/dist/commands/setup.d.ts +13 -0
  156. package/dist/commands/setup.js +93 -0
  157. package/dist/commands/uninstall.d.ts +3 -0
  158. package/dist/commands/uninstall.js +126 -0
  159. package/dist/commands/verify.d.ts +1 -0
  160. package/dist/commands/verify.js +28 -0
  161. package/dist/harnesses/claude-code.d.ts +8 -0
  162. package/dist/harnesses/claude-code.js +74 -0
  163. package/dist/harnesses/codex.d.ts +15 -0
  164. package/dist/harnesses/codex.js +54 -0
  165. package/dist/harnesses/gemini-cli.d.ts +12 -0
  166. package/dist/harnesses/gemini-cli.js +80 -0
  167. package/dist/harnesses/index.d.ts +27 -0
  168. package/dist/harnesses/index.js +54 -0
  169. package/dist/harnesses/opencode.d.ts +14 -0
  170. package/dist/harnesses/opencode.js +139 -0
  171. package/dist/harnesses/types.d.ts +106 -0
  172. package/dist/harnesses/types.js +26 -0
  173. package/dist/lib/agent-transform.d.ts +12 -0
  174. package/dist/lib/agent-transform.js +129 -0
  175. package/dist/lib/asset-catalog.d.ts +9 -0
  176. package/dist/lib/asset-catalog.js +56 -0
  177. package/dist/lib/atomic-write.d.ts +11 -0
  178. package/dist/lib/atomic-write.js +28 -0
  179. package/dist/lib/config-merger.d.ts +9 -2
  180. package/dist/lib/config-merger.js +44 -7
  181. package/dist/lib/display.d.ts +14 -0
  182. package/dist/lib/display.js +66 -0
  183. package/dist/lib/file-ops.d.ts +11 -0
  184. package/dist/lib/file-ops.js +40 -4
  185. package/dist/lib/hash.d.ts +1 -0
  186. package/dist/lib/hash.js +2 -1
  187. package/dist/lib/health.d.ts +2 -0
  188. package/dist/lib/health.js +10 -0
  189. package/dist/lib/manifest.d.ts +51 -5
  190. package/dist/lib/manifest.js +146 -13
  191. package/dist/lib/paths.d.ts +30 -3
  192. package/dist/lib/paths.js +98 -12
  193. package/dist/lib/settings-merger.d.ts +31 -8
  194. package/dist/lib/settings-merger.js +87 -24
  195. package/dist/lib/version.d.ts +2 -0
  196. package/dist/lib/version.js +10 -0
  197. package/dist/steps/agents.d.ts +4 -1
  198. package/dist/steps/agents.js +48 -9
  199. package/dist/steps/auth.js +26 -10
  200. package/dist/steps/cli.d.ts +53 -0
  201. package/dist/steps/cli.js +90 -0
  202. package/dist/steps/commands.d.ts +6 -1
  203. package/dist/steps/commands.js +36 -9
  204. package/dist/steps/detect.d.ts +3 -0
  205. package/dist/steps/detect.js +11 -0
  206. package/dist/steps/mcp.d.ts +6 -2
  207. package/dist/steps/mcp.js +39 -22
  208. package/dist/steps/metrics.d.ts +26 -10
  209. package/dist/steps/metrics.js +108 -108
  210. package/dist/steps/shell.d.ts +2 -0
  211. package/dist/steps/shell.js +26 -9
  212. package/dist/steps/signup.d.ts +7 -4
  213. package/dist/steps/signup.js +29 -20
  214. package/dist/steps/verify.d.ts +2 -2
  215. package/dist/steps/verify.js +118 -112
  216. package/package.json +40 -14
  217. package/assets/agents/docs-validator-agent.md +0 -490
  218. package/assets/agents/release-readiness-agent.md +0 -482
  219. package/assets/commands/agents/aristotle-analyst.md +0 -115
  220. package/assets/commands/agents/aristotle-explorer.md +0 -92
  221. package/assets/commands/agents/aristotle-forecaster.md +0 -114
  222. package/assets/commands/agents/aristotle-validator.md +0 -114
  223. package/assets/commands/agents/prompt-validate.md +0 -135
  224. package/assets/commands/agents/workflow-synthesis.md +0 -101
  225. package/assets/commands/workflows/aristotle.md +0 -543
  226. package/assets/commands/workflows/post-implementation.md +0 -577
  227. package/assets/commands/workflows/pre-implementation.md +0 -670
  228. package/assets/commands/workflows/prompt-audit.md +0 -754
  229. package/assets/commands/workflows/ship.md +0 -721
  230. package/dist/test/auth.test.d.ts +0 -1
  231. package/dist/test/auth.test.js +0 -43
  232. package/dist/test/config-io.test.d.ts +0 -1
  233. package/dist/test/config-io.test.js +0 -56
  234. package/dist/test/config-merger.test.d.ts +0 -1
  235. package/dist/test/config-merger.test.js +0 -94
  236. package/dist/test/detect.test.d.ts +0 -1
  237. package/dist/test/detect.test.js +0 -25
  238. package/dist/test/file-ops.test.d.ts +0 -1
  239. package/dist/test/file-ops.test.js +0 -100
  240. package/dist/test/hash.test.d.ts +0 -1
  241. package/dist/test/hash.test.js +0 -14
  242. package/dist/test/manifest.test.d.ts +0 -1
  243. package/dist/test/manifest.test.js +0 -78
  244. package/dist/test/paths.test.d.ts +0 -1
  245. package/dist/test/paths.test.js +0 -30
  246. package/dist/test/settings-merger.test.d.ts +0 -1
  247. package/dist/test/settings-merger.test.js +0 -167
  248. package/dist/test/shell-profile.test.d.ts +0 -1
  249. package/dist/test/shell-profile.test.js +0 -40
  250. package/dist/test/shell.test.d.ts +0 -1
  251. package/dist/test/shell.test.js +0 -71
  252. package/dist/test/signup.test.d.ts +0 -1
  253. package/dist/test/signup.test.js +0 -83
@@ -0,0 +1,274 @@
1
+ import chalk from "chalk";
2
+ import { join } from "node:path";
3
+ import { readdir } from "node:fs/promises";
4
+ import { detect } from "../steps/detect.js";
5
+ import { signup } from "../steps/signup.js";
6
+ import { resolveApiKey } from "../steps/auth.js";
7
+ import { installMcp } from "../steps/mcp.js";
8
+ import { installAgents } from "../steps/agents.js";
9
+ import { installCommands } from "../steps/commands.js";
10
+ import { installMetrics } from "../steps/metrics.js";
11
+ import { installCli, CLI_PACKAGE } from "../steps/cli.js";
12
+ import { writeShellExport } from "../steps/shell.js";
13
+ import { probeHookSupport } from "../lib/settings-merger.js";
14
+ import { findProjectRoot, ASSETS_DIR } from "../lib/paths.js";
15
+ import { getHealthTimeout } from "../lib/health.js";
16
+ import { ok, warn, fail, info } from "../lib/display.js";
17
+ /** Resolve API key via flag, env, file, signup, or interactive prompt. Returns env detection + key. */
18
+ export async function initContext(opts) {
19
+ const env = await detect();
20
+ let apiKey;
21
+ try {
22
+ if (opts.signup) {
23
+ info("Create your UluOps account\n");
24
+ const auth = await signup();
25
+ apiKey = auth.apiKey;
26
+ ok(`Account created (${auth.email})`);
27
+ ok(`API key generated`);
28
+ }
29
+ else {
30
+ const auth = await resolveApiKey({
31
+ apiKeyFlag: opts.apiKey,
32
+ skipValidation: opts.skipValidation,
33
+ interactive: !opts.yes && !opts.apiKey && !process.env["ULUOPS_API_KEY"],
34
+ });
35
+ apiKey = auth.apiKey;
36
+ if (auth.email)
37
+ ok(`Key validated (${auth.email})`);
38
+ else if (opts.skipValidation)
39
+ ok("Key accepted (validation skipped)");
40
+ else
41
+ ok("Key validated");
42
+ }
43
+ }
44
+ catch (err) {
45
+ fail(err instanceof Error ? err.message : String(err));
46
+ process.exit(1);
47
+ }
48
+ return { env, apiKey };
49
+ }
50
+ /** Write MCP server entries to harness config and report warnings. */
51
+ export async function configureMcpStep(profile, apiKey, opts) {
52
+ const res = await installMcp(profile, apiKey, opts.scope, opts.dryRun);
53
+ ok(`MCP config → ${res.configPath} (2 servers)`);
54
+ for (const w of res.packageWarnings)
55
+ warn(w);
56
+ return res;
57
+ }
58
+ /** Copy agent definitions from assets to harness directory. */
59
+ export async function installAgentsDefs(profile, opts, prev) {
60
+ const res = await installAgents(profile, opts.localDefs, opts.dryRun, prev);
61
+ const parts = [];
62
+ if (res.copied > 0)
63
+ parts.push(`${res.copied} copied`);
64
+ if (res.skipped > 0)
65
+ parts.push(`${res.skipped} unchanged`);
66
+ if (res.removed > 0)
67
+ parts.push(`${res.removed} removed`);
68
+ const dest = opts.localDefs
69
+ ? "./uluops/agents/"
70
+ : `${profile.paths.agentsDir.replace(process.env["HOME"] ?? "", "~")}/`;
71
+ ok(`${res.files.length} agents → ${dest}${parts.length ? ` (${parts.join(", ")})` : ""}`);
72
+ return res;
73
+ }
74
+ /** Copy slash-command definitions from assets (Claude Code only). */
75
+ export async function installCommandsDefs(profile, opts, prev) {
76
+ const res = await installCommands(profile, opts.localDefs, opts.dryRun, prev);
77
+ if (res.skippedReason === "not-supported") {
78
+ info(chalk.dim(`Commands not yet supported for ${profile.displayName} (coming soon)`));
79
+ return res;
80
+ }
81
+ const total = res.agentCommands + res.workflowCommands + res.pipelineCommands;
82
+ const parts = [];
83
+ if (total > 0)
84
+ parts.push(`${total} copied`);
85
+ if (res.skipped > 0)
86
+ parts.push(`${res.skipped} unchanged`);
87
+ if (res.removed > 0)
88
+ parts.push(`${res.removed} removed`);
89
+ const dest = opts.localDefs
90
+ ? "./uluops/commands/"
91
+ : `${profile.paths.commandsDir.replace(process.env["HOME"] ?? "", "~")}/`;
92
+ ok(`${res.files.length} commands → ${dest}${parts.length ? ` (${parts.join(", ")})` : ""}`);
93
+ return res;
94
+ }
95
+ /** Install agent-metrics hook and tool files (Claude Code only). */
96
+ export async function configureMetricsStep(profile, opts) {
97
+ if (!profile.hooks) {
98
+ info(chalk.dim(`Metrics hooks not supported for ${profile.displayName}`));
99
+ return { toolFilesCopied: 0, hookConfigured: false, hooksInstalledVersion: null };
100
+ }
101
+ const probe = probeHookSupport();
102
+ if (probe.warning)
103
+ warn(probe.warning);
104
+ const res = await installMetrics(profile, opts.dryRun);
105
+ if (res.hookConfigured) {
106
+ const parts = [];
107
+ if (res.toolFilesCopied > 0)
108
+ parts.push(`${res.toolFilesCopied} files`);
109
+ parts.push("hook configured");
110
+ const toolPath = profile.paths.toolsDir?.replace(process.env["HOME"] ?? "", "~");
111
+ ok(`Agent metrics → ${toolPath}/ (${parts.join(", ")})`);
112
+ }
113
+ else {
114
+ warn("Agent metrics hook not configured (tool files not found)");
115
+ }
116
+ return res;
117
+ }
118
+ /**
119
+ * Decide whether to install `@uluops/cli` globally and do it (or not).
120
+ *
121
+ * Decision matrix:
122
+ * - `--no-cli` (opts.cli === false) → skip, no prompt
123
+ * - `--with-cli` (opts.withCli === true) → install, no prompt
124
+ * - Neither flag + non-interactive (--yes / --api-key / no TTY) → skip
125
+ * - Neither flag + interactive → prompt (default Y)
126
+ *
127
+ * Returns null when the step did not run (skipped). Returns a `CliInstallResult`
128
+ * when an install attempt was made, with details for the manifest.
129
+ */
130
+ export async function configureCliStep(opts) {
131
+ if (opts.cli === false) {
132
+ info(chalk.dim(`Skipped global ${CLI_PACKAGE} install (--no-cli)`));
133
+ return null;
134
+ }
135
+ let shouldInstall;
136
+ if (opts.withCli === true) {
137
+ shouldInstall = true;
138
+ }
139
+ else {
140
+ const nonInteractive = opts.yes || !!opts.apiKey || !process.stdin.isTTY;
141
+ if (nonInteractive) {
142
+ info(chalk.dim(`Skipped global ${CLI_PACKAGE} install (non-interactive — pass --with-cli to install)`));
143
+ return null;
144
+ }
145
+ const { confirm } = await import("@inquirer/prompts");
146
+ shouldInstall = await confirm({
147
+ message: `Install ${CLI_PACKAGE} globally (provides the ${chalk.cyan("ulu")} command)?`,
148
+ default: true,
149
+ });
150
+ if (!shouldInstall) {
151
+ info(chalk.dim(`Skipped global ${CLI_PACKAGE} install`));
152
+ return null;
153
+ }
154
+ }
155
+ const res = await installCli({
156
+ dryRun: opts.dryRun,
157
+ executor: opts.executor,
158
+ });
159
+ if (opts.dryRun && !res.alreadyPresent) {
160
+ ok(`Would install ${CLI_PACKAGE} globally`);
161
+ return res;
162
+ }
163
+ if (res.alreadyPresent) {
164
+ ok(`${CLI_PACKAGE} already installed${res.version ? ` (${res.version})` : ""} — no change`);
165
+ return res;
166
+ }
167
+ if (res.installed) {
168
+ ok(`${CLI_PACKAGE} installed globally${res.version ? ` (${res.version})` : ""}`);
169
+ return res;
170
+ }
171
+ warn(`Could not install ${CLI_PACKAGE} globally — try ${chalk.cyan(`npm install -g ${CLI_PACKAGE}`)} manually`);
172
+ if (res.error) {
173
+ const oneLine = res.error.split("\n")[0]?.slice(0, 120) ?? "";
174
+ if (oneLine)
175
+ info(chalk.dim(` ${oneLine}`));
176
+ }
177
+ return res;
178
+ }
179
+ /** Ping tracker and registry health endpoints. */
180
+ export async function runHealthCheck(opts) {
181
+ if (!opts.skipValidation && !opts.dryRun) {
182
+ try {
183
+ const [trackerOk, registryOk] = await Promise.all([
184
+ checkEndpoint("https://api.uluops.ai/api/v1/health"),
185
+ checkEndpoint("https://api.uluops.ai/api/v1/registry/health"),
186
+ ]);
187
+ if (trackerOk && registryOk)
188
+ ok("Health check passed — both APIs reachable");
189
+ else
190
+ warn("Some APIs unreachable (MCP tools may have limited functionality)");
191
+ }
192
+ catch {
193
+ warn("Health check skipped (network issue)");
194
+ }
195
+ }
196
+ }
197
+ /** Optionally write ULUOPS_API_KEY export to shell profile. */
198
+ export async function configureShell(env, apiKey, opts) {
199
+ let modified = false;
200
+ if (opts.shell && env.shellProfile) {
201
+ if (!opts.yes && !opts.dryRun) {
202
+ const confirmed = await confirmShellWrite(env.shellProfile);
203
+ if (!confirmed) {
204
+ warn("Skipped writing API key to shell profile");
205
+ return false;
206
+ }
207
+ }
208
+ await writeShellExport(env.shellProfile, apiKey, opts.dryRun);
209
+ ok(`ULUOPS_API_KEY added to ${env.shellProfile}`);
210
+ warn("API key stored in plaintext in shell profile. Consider rotating if shared machine.");
211
+ modified = true;
212
+ }
213
+ else if (opts.shell) {
214
+ warn("--shell requested but no supported shell detected ($SHELL). Skipping.");
215
+ }
216
+ return modified;
217
+ }
218
+ /** Interactive y/N confirmation before writing API key to shell profile. */
219
+ export async function confirmShellWrite(profilePath) {
220
+ const readline = await import("node:readline/promises");
221
+ const rl = readline.createInterface({
222
+ input: process.stdin,
223
+ output: process.stdout,
224
+ });
225
+ const answer = await rl.question(`Write ULUOPS_API_KEY to ${profilePath}? (y/N) `);
226
+ rl.close();
227
+ return answer.trim().toLowerCase() === "y";
228
+ }
229
+ /** Warn if existing agent files will be overwritten and prompt for confirmation. */
230
+ export async function checkConflicts(profile, localDefs) {
231
+ const destDir = localDefs
232
+ ? join(await findProjectRoot(), "uluops", "agents")
233
+ : profile.paths.agentsDir;
234
+ const srcDir = join(ASSETS_DIR, profile.name, "agents");
235
+ let existingFiles;
236
+ let assetFiles;
237
+ try {
238
+ existingFiles = await readdir(destDir);
239
+ assetFiles = await readdir(srcDir);
240
+ }
241
+ catch {
242
+ return;
243
+ }
244
+ const conflicts = assetFiles.filter((f) => existingFiles.includes(f));
245
+ if (conflicts.length === 0)
246
+ return;
247
+ warn(`Found ${conflicts.length} existing agents that match UluOps definitions:`);
248
+ for (const f of conflicts.slice(0, 5)) {
249
+ info(` ${f}`);
250
+ }
251
+ if (conflicts.length > 5) {
252
+ info(` ... and ${conflicts.length - 5} more`);
253
+ }
254
+ console.log();
255
+ info("These will be overwritten.");
256
+ console.log();
257
+ const { confirm } = await import("@inquirer/prompts");
258
+ const proceed = await confirm({ message: "Continue?", default: true });
259
+ if (!proceed) {
260
+ process.exit(0);
261
+ }
262
+ }
263
+ /** Fetch a URL and return true if the response is OK, false on any failure. */
264
+ async function checkEndpoint(url) {
265
+ try {
266
+ const res = await fetch(url, {
267
+ signal: AbortSignal.timeout(getHealthTimeout()),
268
+ });
269
+ return res.ok;
270
+ }
271
+ catch {
272
+ return false;
273
+ }
274
+ }
@@ -0,0 +1,13 @@
1
+ export declare function runSetup(opts: {
2
+ apiKey?: string;
3
+ signup: boolean;
4
+ scope: "global" | "local";
5
+ localDefs: boolean;
6
+ shell: boolean;
7
+ skipValidation: boolean;
8
+ dryRun: boolean;
9
+ yes: boolean;
10
+ harness: string;
11
+ withCli?: boolean;
12
+ cli?: boolean;
13
+ }): Promise<void>;
@@ -0,0 +1,93 @@
1
+ import chalk from "chalk";
2
+ import { join } from "node:path";
3
+ import { loadManifest, saveManifest, } from "../lib/manifest.js";
4
+ import { findProjectRoot } from "../lib/paths.js";
5
+ import { info, printSetupSummary } from "../lib/display.js";
6
+ import { getVersion } from "../lib/version.js";
7
+ import { getProfile } from "../harnesses/index.js";
8
+ import { initContext, checkConflicts, configureMcpStep, installAgentsDefs, installCommandsDefs, configureMetricsStep, configureCliStep, runHealthCheck, configureShell, } from "./helpers.js";
9
+ export async function runSetup(opts) {
10
+ const version = await getVersion();
11
+ const profile = getProfile(opts.harness);
12
+ console.log();
13
+ console.log(` ${chalk.dim("⟨u⟩")} ${chalk.cyan.bold("ulu")}${chalk.bold("·ops")}`);
14
+ console.log(` ${chalk.dim("operating intelligence as infrastructure")}`);
15
+ console.log();
16
+ console.log(` Setup v${version} — ${chalk.bold(profile.displayName)}`);
17
+ console.log();
18
+ if (opts.dryRun) {
19
+ info(chalk.dim("(dry run — no changes will be made)\n"));
20
+ }
21
+ const { env, apiKey } = await initContext(opts);
22
+ console.log();
23
+ // Load existing manifest for update detection
24
+ const existingManifest = await loadManifest();
25
+ const existingHarness = existingManifest?.harnesses[profile.name];
26
+ if (existingManifest && existingManifest.version !== version) {
27
+ info(`Updating ${chalk.dim(existingManifest.version)} → ${chalk.green(version)}`);
28
+ console.log();
29
+ }
30
+ else if (existingHarness) {
31
+ info(chalk.dim(`Already at v${version} — checking for changes`));
32
+ console.log();
33
+ }
34
+ // Check for conflicts on first install for this harness
35
+ if (!existingHarness && !opts.yes && !opts.dryRun) {
36
+ await checkConflicts(profile, opts.localDefs);
37
+ }
38
+ const mcpResult = await configureMcpStep(profile, apiKey, opts);
39
+ const agentsResult = await installAgentsDefs(profile, opts, existingHarness?.agents);
40
+ const commandsResult = await installCommandsDefs(profile, opts, existingHarness?.commands);
41
+ const metricsResult = await configureMetricsStep(profile, opts);
42
+ const cliResult = await configureCliStep({
43
+ withCli: opts.withCli,
44
+ cli: opts.cli,
45
+ yes: opts.yes,
46
+ apiKey: opts.apiKey,
47
+ dryRun: opts.dryRun,
48
+ });
49
+ await runHealthCheck(opts);
50
+ const shellModified = await configureShell(env, apiKey, opts);
51
+ // Save manifest
52
+ if (!opts.dryRun) {
53
+ const now = new Date().toISOString();
54
+ const harnessEntry = {
55
+ installedAt: now,
56
+ setupVersion: version,
57
+ mcpScope: opts.scope,
58
+ mcpConfigPath: mcpResult.configPath,
59
+ defsScope: opts.localDefs ? "local" : "global",
60
+ defsPath: opts.localDefs
61
+ ? join(await findProjectRoot(), "uluops")
62
+ : profile.paths.home,
63
+ agents: agentsResult.files,
64
+ commands: commandsResult.files,
65
+ hooksInstalled: metricsResult.hookConfigured,
66
+ hooksInstalledVersion: metricsResult.hooksInstalledVersion,
67
+ };
68
+ const manifest = existingManifest ?? {
69
+ version,
70
+ installedAt: now,
71
+ shellModified: false,
72
+ harnesses: {},
73
+ };
74
+ manifest.version = version;
75
+ manifest.installedAt = now;
76
+ manifest.shellModified = shellModified || manifest.shellModified;
77
+ manifest.harnesses[profile.name] = harnessEntry;
78
+ // Only flip cliInstalled to true when WE installed it (not when user-installed).
79
+ // Once true, persist across re-runs so uninstall remains symmetric — until the
80
+ // user explicitly removes it with --no-cli + uninstall, this manifest owns it.
81
+ if (cliResult && cliResult.installed && !cliResult.alreadyPresent) {
82
+ manifest.cliInstalled = true;
83
+ manifest.cliInstalledVersion = cliResult.version;
84
+ }
85
+ await saveManifest(manifest);
86
+ }
87
+ await printSetupSummary({
88
+ profile,
89
+ agentCount: agentsResult.files.length,
90
+ commandCount: commandsResult.files.length,
91
+ apiKey,
92
+ });
93
+ }
@@ -0,0 +1,3 @@
1
+ export declare function runUninstall(opts: {
2
+ dryRun: boolean;
3
+ }): Promise<void>;
@@ -0,0 +1,126 @@
1
+ import chalk from "chalk";
2
+ import { loadManifest, deleteManifest, validateManifest, } from "../lib/manifest.js";
3
+ import { uninstallMcp } from "../steps/mcp.js";
4
+ import { uninstallAgents } from "../steps/agents.js";
5
+ import { uninstallCommands } from "../steps/commands.js";
6
+ import { removeShellExport } from "../steps/shell.js";
7
+ import { uninstallMetrics } from "../steps/metrics.js";
8
+ import { uninstallCli, CLI_PACKAGE } from "../steps/cli.js";
9
+ import { ok, warn, fail, info } from "../lib/display.js";
10
+ import { getVersion } from "../lib/version.js";
11
+ import { getProfile } from "../harnesses/index.js";
12
+ export async function runUninstall(opts) {
13
+ const version = await getVersion();
14
+ console.log();
15
+ console.log(` ${chalk.dim("⟨u⟩")} ${chalk.cyan.bold("ulu")}${chalk.bold("·ops")} ${chalk.red("Uninstall")} v${version}`);
16
+ console.log();
17
+ if (opts.dryRun) {
18
+ info(chalk.dim("(dry run — no changes will be made)\n"));
19
+ }
20
+ const manifest = await loadManifest();
21
+ if (!manifest) {
22
+ warn("No manifest found — nothing to uninstall.");
23
+ return;
24
+ }
25
+ const validation = await validateManifest(manifest);
26
+ if (!validation.valid) {
27
+ fail("Manifest references paths that no longer exist:");
28
+ for (const err of validation.errors)
29
+ info(` ${err}`);
30
+ console.log();
31
+ info("Uninstall may be incomplete. Proceeding with what's available.");
32
+ console.log();
33
+ }
34
+ if (validation.warnings.length > 0) {
35
+ for (const w of validation.warnings)
36
+ warn(w);
37
+ console.log();
38
+ }
39
+ for (const [harnessName, hm] of Object.entries(manifest.harnesses)) {
40
+ let profile;
41
+ try {
42
+ profile = getProfile(harnessName);
43
+ }
44
+ catch {
45
+ warn(`Unknown harness "${harnessName}" in manifest — skipping`);
46
+ continue;
47
+ }
48
+ info(chalk.bold(profile.displayName));
49
+ if (!opts.dryRun) {
50
+ const agentCount = await uninstallAgents(hm.agents, hm.defsPath);
51
+ ok(`Removed ${agentCount} agent(s)`);
52
+ }
53
+ else {
54
+ ok(`Would remove ${hm.agents.length} agent(s)`);
55
+ }
56
+ if (hm.commands.length > 0) {
57
+ if (!opts.dryRun) {
58
+ const cmdCount = await uninstallCommands(hm.commands, hm.defsPath);
59
+ ok(`Removed ${cmdCount} command(s)`);
60
+ }
61
+ else {
62
+ ok(`Would remove ${hm.commands.length} command(s)`);
63
+ }
64
+ }
65
+ if (!opts.dryRun) {
66
+ try {
67
+ await uninstallMcp(profile, hm.mcpConfigPath);
68
+ ok(`Removed MCP servers from ${hm.mcpConfigPath}`);
69
+ }
70
+ catch {
71
+ warn(`Could not remove MCP servers from ${hm.mcpConfigPath}`);
72
+ }
73
+ }
74
+ else {
75
+ ok(`Would remove MCP servers from ${hm.mcpConfigPath}`);
76
+ }
77
+ if (hm.hooksInstalled) {
78
+ if (!opts.dryRun) {
79
+ await uninstallMetrics(profile, false);
80
+ ok("Removed agent-metrics hook and tool files");
81
+ }
82
+ else {
83
+ ok("Would remove agent-metrics hook and tool files");
84
+ }
85
+ }
86
+ console.log();
87
+ }
88
+ // Remove global @uluops/cli if WE installed it (per manifest).
89
+ // We never auto-remove a user-installed CLI — manifest.cliInstalled gates this.
90
+ if (manifest.cliInstalled) {
91
+ const res = await uninstallCli({ dryRun: opts.dryRun });
92
+ if (opts.dryRun) {
93
+ ok(`Would remove ${CLI_PACKAGE} (global)`);
94
+ }
95
+ else if (res.removed) {
96
+ ok(`Removed ${CLI_PACKAGE} (global)`);
97
+ }
98
+ else {
99
+ warn(`Could not remove ${CLI_PACKAGE} (global) — try \`npm uninstall -g ${CLI_PACKAGE}\` manually`);
100
+ if (res.error) {
101
+ const oneLine = res.error.split("\n")[0]?.slice(0, 120) ?? "";
102
+ if (oneLine)
103
+ info(` ${oneLine}`);
104
+ }
105
+ }
106
+ }
107
+ // Remove shell export
108
+ if (manifest.shellModified) {
109
+ const { getShellProfile } = await import("../lib/paths.js");
110
+ const shellProfile = getShellProfile();
111
+ if (shellProfile && !opts.dryRun) {
112
+ await removeShellExport(shellProfile.path);
113
+ ok(`Removed export from ${shellProfile.path}`);
114
+ }
115
+ else if (shellProfile) {
116
+ ok(`Would remove export from ${shellProfile.path}`);
117
+ }
118
+ }
119
+ if (!opts.dryRun) {
120
+ await deleteManifest();
121
+ ok("Manifest deleted");
122
+ }
123
+ console.log();
124
+ info("UluOps has been removed. Restart your harness to complete.");
125
+ console.log();
126
+ }
@@ -0,0 +1 @@
1
+ export declare function runVerify(): Promise<void>;
@@ -0,0 +1,28 @@
1
+ import chalk from "chalk";
2
+ import { verify } from "../steps/verify.js";
3
+ import { ok, fail, info } from "../lib/display.js";
4
+ import { getVersion } from "../lib/version.js";
5
+ export async function runVerify() {
6
+ const version = await getVersion();
7
+ console.log();
8
+ console.log(` ${chalk.dim("⟨u⟩")} ${chalk.cyan.bold("ulu")}${chalk.bold("·ops")} Installation Check v${version}`);
9
+ console.log();
10
+ const result = await verify();
11
+ for (const check of result.checks) {
12
+ if (check.passed) {
13
+ ok(check.label);
14
+ }
15
+ else {
16
+ fail(`${check.label}${check.detail ? ` — ${check.detail}` : ""}`);
17
+ }
18
+ }
19
+ console.log();
20
+ if (result.ok) {
21
+ info(chalk.green("All checks passed."));
22
+ }
23
+ else {
24
+ info(chalk.red("Some checks failed. Run npx @uluops/setup to fix."));
25
+ }
26
+ console.log();
27
+ process.exit(result.ok ? 0 : 1);
28
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Claude Code Harness Profile
3
+ *
4
+ * Wraps existing config-merger.ts and settings-merger.ts logic
5
+ * behind the HarnessProfile abstraction.
6
+ */
7
+ import { type HarnessProfile } from "./types.js";
8
+ export declare const claudeCodeProfile: HarnessProfile;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Claude Code Harness Profile
3
+ *
4
+ * Wraps existing config-merger.ts and settings-merger.ts logic
5
+ * behind the HarnessProfile abstraction.
6
+ */
7
+ import { join } from "node:path";
8
+ import { ULUOPS_SERVERS, } from "./types.js";
9
+ import { readConfig, mergeUluopsMcp, removeUluopsMcp, writeConfig, } from "../lib/config-merger.js";
10
+ import { readSettings, writeSettings, mergeUluopsHook, removeUluopsHook, hasUluopsHook, } from "../lib/settings-merger.js";
11
+ import { getClaudeHome, getClaudeJsonPath } from "../lib/paths.js";
12
+ class ClaudeCodeMcpConfig {
13
+ async read(path) {
14
+ return readConfig(path);
15
+ }
16
+ merge(config, apiKey) {
17
+ return mergeUluopsMcp(config, apiKey);
18
+ }
19
+ remove(config) {
20
+ return removeUluopsMcp(config);
21
+ }
22
+ async write(path, config) {
23
+ await writeConfig(path, config);
24
+ }
25
+ check(config) {
26
+ const servers = config["mcpServers"];
27
+ if (!servers)
28
+ return false;
29
+ return ULUOPS_SERVERS.every((name) => name in servers);
30
+ }
31
+ }
32
+ class ClaudeCodeHooks {
33
+ async install(settingsPath, hookCommand, dryRun) {
34
+ if (dryRun)
35
+ return true;
36
+ const settings = await readSettings(settingsPath);
37
+ // Claude Code uses SubagentStop as the default event for auto-save
38
+ const merged = mergeUluopsHook(settings, hookCommand);
39
+ await writeSettings(settingsPath, merged);
40
+ return true;
41
+ }
42
+ async remove(settingsPath, dryRun) {
43
+ if (dryRun)
44
+ return;
45
+ const settings = await readSettings(settingsPath);
46
+ const cleaned = removeUluopsHook(settings);
47
+ await writeSettings(settingsPath, cleaned);
48
+ }
49
+ async check(settingsPath) {
50
+ const settings = await readSettings(settingsPath);
51
+ return hasUluopsHook(settings);
52
+ }
53
+ }
54
+ const home = getClaudeHome();
55
+ export const claudeCodeProfile = {
56
+ name: "claude-code",
57
+ displayName: "Claude Code",
58
+ status: "stable",
59
+ homeDir: home,
60
+ agentFormat: "markdown",
61
+ factoryTarget: "claude-code",
62
+ agentExtension: ".md",
63
+ paths: {
64
+ home,
65
+ globalMcpConfig: getClaudeJsonPath(),
66
+ localMcpConfig: ".mcp.json",
67
+ agentsDir: join(home, "agents"),
68
+ commandsDir: join(home, "commands"),
69
+ settingsPath: join(home, "settings.json"),
70
+ toolsDir: join(home, "tools", "agent-metrics"),
71
+ },
72
+ mcpConfig: new ClaudeCodeMcpConfig(),
73
+ hooks: new ClaudeCodeHooks(),
74
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Codex Harness Profile (Scaffold)
3
+ *
4
+ * Paths and metadata are verified from vendor docs.
5
+ * Codex uses TOML config with `mcp_servers` key (nested tables).
6
+ * Agent definitions are TOML, not markdown.
7
+ * Skills use a different path ($HOME/.agents/skills/) than agents (~/.codex/agents/).
8
+ *
9
+ * NOT YET TESTED with UluOps agents. McpConfigStrategy throws
10
+ * until integration testing is complete.
11
+ *
12
+ * Will require `smol-toml` dependency when fully implemented.
13
+ */
14
+ import type { HarnessProfile } from "./types.js";
15
+ export declare const codexProfile: HarnessProfile;