@curdx/flow 3.0.0 → 3.1.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 (219) hide show
  1. package/CHANGELOG.md +21 -87
  2. package/LICENSE +1 -1
  3. package/README.md +28 -129
  4. package/dist/index.mjs +995 -0
  5. package/package.json +33 -44
  6. package/.claude-plugin/marketplace.json +0 -48
  7. package/.claude-plugin/plugin.json +0 -52
  8. package/agent-preamble/preamble.md +0 -314
  9. package/agents/flow-adversary.md +0 -203
  10. package/agents/flow-architect.md +0 -198
  11. package/agents/flow-brownfield-analyst.md +0 -143
  12. package/agents/flow-debugger.md +0 -321
  13. package/agents/flow-edge-hunter.md +0 -289
  14. package/agents/flow-executor.md +0 -269
  15. package/agents/flow-orchestrator.md +0 -145
  16. package/agents/flow-planner.md +0 -247
  17. package/agents/flow-product-designer.md +0 -159
  18. package/agents/flow-qa-engineer.md +0 -282
  19. package/agents/flow-researcher.md +0 -166
  20. package/agents/flow-reviewer.md +0 -304
  21. package/agents/flow-security-auditor.md +0 -401
  22. package/agents/flow-triage-analyst.md +0 -272
  23. package/agents/flow-ui-researcher.md +0 -230
  24. package/agents/flow-ux-designer.md +0 -221
  25. package/agents/flow-verifier.md +0 -350
  26. package/bin/curdx-flow +0 -5
  27. package/bin/curdx-flow-state +0 -104
  28. package/bin/curdx-flow.js +0 -54
  29. package/cli/README.md +0 -104
  30. package/cli/doctor-workflow.js +0 -483
  31. package/cli/doctor.js +0 -73
  32. package/cli/help.js +0 -59
  33. package/cli/install-bundled-mcps.js +0 -37
  34. package/cli/install-companions.js +0 -19
  35. package/cli/install-context7-config.js +0 -80
  36. package/cli/install-curdx-plugin.js +0 -96
  37. package/cli/install-language.js +0 -35
  38. package/cli/install-next-steps.js +0 -29
  39. package/cli/install-options.js +0 -9
  40. package/cli/install-paths.js +0 -52
  41. package/cli/install-recommended-plugins.js +0 -104
  42. package/cli/install-required-plugins.js +0 -57
  43. package/cli/install-self-update.js +0 -62
  44. package/cli/install-workflow.js +0 -209
  45. package/cli/install.js +0 -101
  46. package/cli/lib/claude-commands.js +0 -41
  47. package/cli/lib/claude-ops.js +0 -47
  48. package/cli/lib/claude.js +0 -183
  49. package/cli/lib/config.js +0 -24
  50. package/cli/lib/doctor-claude-settings.js +0 -1186
  51. package/cli/lib/doctor-report.js +0 -978
  52. package/cli/lib/doctor-runtime-environment.js +0 -196
  53. package/cli/lib/frontmatter.js +0 -44
  54. package/cli/lib/json-schema.js +0 -57
  55. package/cli/lib/logging.js +0 -25
  56. package/cli/lib/process.js +0 -60
  57. package/cli/lib/prompts.js +0 -135
  58. package/cli/lib/runtime.js +0 -107
  59. package/cli/lib/semver.js +0 -109
  60. package/cli/lib/version.js +0 -12
  61. package/cli/protocols-body.md +0 -22
  62. package/cli/protocols.js +0 -162
  63. package/cli/registry.js +0 -123
  64. package/cli/router.js +0 -49
  65. package/cli/uninstall-actions.js +0 -360
  66. package/cli/uninstall-workflow.js +0 -146
  67. package/cli/uninstall.js +0 -42
  68. package/cli/upgrade-workflow.js +0 -80
  69. package/cli/upgrade.js +0 -91
  70. package/cli/utils.js +0 -40
  71. package/gates/adversarial-review-gate.md +0 -219
  72. package/gates/coverage-audit-gate.md +0 -182
  73. package/gates/devex-gate.md +0 -254
  74. package/gates/edge-case-gate.md +0 -194
  75. package/gates/karpathy-gate.md +0 -130
  76. package/gates/security-gate.md +0 -218
  77. package/gates/tdd-gate.md +0 -182
  78. package/gates/test-quality-gate.md +0 -59
  79. package/gates/verification-gate.md +0 -179
  80. package/hooks/hooks.json +0 -130
  81. package/hooks/scripts/common.sh +0 -237
  82. package/hooks/scripts/config-change-guard.sh +0 -94
  83. package/hooks/scripts/flow-context-watch.sh +0 -94
  84. package/hooks/scripts/inject-karpathy.sh +0 -53
  85. package/hooks/scripts/quick-mode-guard.sh +0 -69
  86. package/hooks/scripts/session-start.sh +0 -94
  87. package/hooks/scripts/session-title.sh +0 -87
  88. package/hooks/scripts/stop-watcher.sh +0 -231
  89. package/hooks/scripts/subagent-artifact-guard.sh +0 -92
  90. package/hooks/scripts/subagent-statusline.sh +0 -111
  91. package/hooks/scripts/task-lifecycle-guard.sh +0 -106
  92. package/hooks/scripts/teammate-idle-guard.sh +0 -83
  93. package/knowledge/artifact-output-discipline.md +0 -24
  94. package/knowledge/artifact-summary-contracts.md +0 -50
  95. package/knowledge/atomic-commits.md +0 -262
  96. package/knowledge/claude-code-runtime-contracts.md +0 -240
  97. package/knowledge/epic-decomposition.md +0 -307
  98. package/knowledge/execution-strategies.md +0 -303
  99. package/knowledge/karpathy-guidelines.md +0 -219
  100. package/knowledge/planning-reviews.md +0 -211
  101. package/knowledge/poc-first-workflow.md +0 -223
  102. package/knowledge/review-feedback-intake.md +0 -57
  103. package/knowledge/spec-driven-development.md +0 -180
  104. package/knowledge/systematic-debugging.md +0 -378
  105. package/knowledge/two-stage-review.md +0 -249
  106. package/knowledge/wave-execution.md +0 -403
  107. package/monitors/monitors.json +0 -8
  108. package/monitors/scripts/flow-state-monitor.sh +0 -102
  109. package/output-styles/curdx-evidence-first.md +0 -34
  110. package/output-styles/curdx-fast-mode.md +0 -42
  111. package/output-styles/curdx-spec-mode.md +0 -46
  112. package/schemas/agent-frontmatter.schema.json +0 -66
  113. package/schemas/config.schema.json +0 -134
  114. package/schemas/gate-frontmatter.schema.json +0 -30
  115. package/schemas/hooks.schema.json +0 -115
  116. package/schemas/output-style-frontmatter.schema.json +0 -22
  117. package/schemas/plugin-manifest.schema.json +0 -436
  118. package/schemas/plugin-settings.schema.json +0 -29
  119. package/schemas/skill-frontmatter.schema.json +0 -177
  120. package/schemas/spec-frontmatter.schema.json +0 -42
  121. package/schemas/spec-state.schema.json +0 -165
  122. package/settings.json +0 -8
  123. package/skills/brownfield-index/SKILL.md +0 -53
  124. package/skills/brownfield-index/references/applicability.md +0 -12
  125. package/skills/brownfield-index/references/handoff.md +0 -8
  126. package/skills/brownfield-index/references/index-contract.md +0 -10
  127. package/skills/browser-qa/SKILL.md +0 -39
  128. package/skills/browser-qa/references/handoff.md +0 -6
  129. package/skills/browser-qa/references/prerequisites.md +0 -10
  130. package/skills/browser-qa/references/qa-contract.md +0 -20
  131. package/skills/cancel/SKILL.md +0 -41
  132. package/skills/cancel/references/destructive-mode.md +0 -17
  133. package/skills/cancel/references/reporting.md +0 -18
  134. package/skills/cancel/references/state-recovery.md +0 -30
  135. package/skills/cancel/references/target-resolution.md +0 -7
  136. package/skills/debug/SKILL.md +0 -45
  137. package/skills/debug/references/context-gathering.md +0 -11
  138. package/skills/debug/references/failure-guard.md +0 -25
  139. package/skills/debug/references/intake.md +0 -12
  140. package/skills/debug/references/phase-workflow.md +0 -34
  141. package/skills/debug/references/reporting.md +0 -20
  142. package/skills/epic/SKILL.md +0 -39
  143. package/skills/epic/references/epic-artifacts.md +0 -20
  144. package/skills/epic/references/epic-intake.md +0 -9
  145. package/skills/epic/references/slice-handoff.md +0 -16
  146. package/skills/fast/SKILL.md +0 -62
  147. package/skills/fast/references/applicability.md +0 -25
  148. package/skills/fast/references/clarification.md +0 -20
  149. package/skills/fast/references/execution-contract.md +0 -56
  150. package/skills/help/SKILL.md +0 -55
  151. package/skills/help/references/dispatch.md +0 -20
  152. package/skills/help/references/overview.md +0 -39
  153. package/skills/help/references/troubleshoot.md +0 -47
  154. package/skills/help/references/workflow.md +0 -37
  155. package/skills/implement/SKILL.md +0 -104
  156. package/skills/implement/references/error-recovery.md +0 -36
  157. package/skills/implement/references/linear-execution.md +0 -43
  158. package/skills/implement/references/native-task-sync.md +0 -107
  159. package/skills/implement/references/preflight.md +0 -43
  160. package/skills/implement/references/progress-contract.md +0 -36
  161. package/skills/implement/references/state-init.md +0 -36
  162. package/skills/implement/references/stop-hook-execution.md +0 -50
  163. package/skills/implement/references/strategy-router.md +0 -38
  164. package/skills/implement/references/subagent-execution.md +0 -57
  165. package/skills/implement/references/wave-execution.md +0 -180
  166. package/skills/init/SKILL.md +0 -49
  167. package/skills/init/references/gitignore-and-health.md +0 -26
  168. package/skills/init/references/next-steps.md +0 -22
  169. package/skills/init/references/preflight.md +0 -15
  170. package/skills/init/references/scaffold-contract.md +0 -27
  171. package/skills/review/SKILL.md +0 -82
  172. package/skills/review/references/optional-passes.md +0 -48
  173. package/skills/review/references/preflight.md +0 -38
  174. package/skills/review/references/report-contract.md +0 -49
  175. package/skills/review/references/reporting.md +0 -20
  176. package/skills/review/references/stage-execution.md +0 -32
  177. package/skills/security-audit/SKILL.md +0 -47
  178. package/skills/security-audit/references/audit-contract.md +0 -21
  179. package/skills/security-audit/references/gate-handoff.md +0 -8
  180. package/skills/security-audit/references/scope-and-depth.md +0 -9
  181. package/skills/spec/SKILL.md +0 -100
  182. package/skills/spec/references/artifact-landing.md +0 -31
  183. package/skills/spec/references/phase-execution.md +0 -50
  184. package/skills/spec/references/planning-review.md +0 -31
  185. package/skills/spec/references/preflight-and-routing.md +0 -46
  186. package/skills/spec/references/reporting.md +0 -21
  187. package/skills/start/SKILL.md +0 -84
  188. package/skills/start/references/branch-routing.md +0 -51
  189. package/skills/start/references/mode-semantics.md +0 -12
  190. package/skills/start/references/preflight.md +0 -13
  191. package/skills/start/references/reporting.md +0 -20
  192. package/skills/start/references/state-seeding.md +0 -44
  193. package/skills/start/references/workflow-handoff.md +0 -26
  194. package/skills/status/SKILL.md +0 -41
  195. package/skills/status/references/gather-contract.md +0 -30
  196. package/skills/status/references/health-rules.md +0 -27
  197. package/skills/status/references/output-contract.md +0 -25
  198. package/skills/status/references/preflight.md +0 -10
  199. package/skills/status/references/recovery-hints.md +0 -18
  200. package/skills/ui-sketch/SKILL.md +0 -39
  201. package/skills/ui-sketch/references/brief-intake.md +0 -10
  202. package/skills/ui-sketch/references/iteration-handoff.md +0 -5
  203. package/skills/ui-sketch/references/variant-contract.md +0 -15
  204. package/skills/verify/SKILL.md +0 -56
  205. package/skills/verify/references/evidence-workflow.md +0 -39
  206. package/skills/verify/references/output-contract.md +0 -23
  207. package/skills/verify/references/preflight.md +0 -11
  208. package/skills/verify/references/report-handoff.md +0 -35
  209. package/skills/verify/references/strict-mode.md +0 -12
  210. package/templates/CONTEXT.md.tmpl +0 -53
  211. package/templates/PROJECT.md.tmpl +0 -59
  212. package/templates/ROADMAP.md.tmpl +0 -50
  213. package/templates/STATE.md.tmpl +0 -49
  214. package/templates/config.json.tmpl +0 -51
  215. package/templates/design.md.tmpl +0 -83
  216. package/templates/progress.md.tmpl +0 -77
  217. package/templates/requirements.md.tmpl +0 -76
  218. package/templates/research.md.tmpl +0 -83
  219. package/templates/tasks.md.tmpl +0 -107
@@ -1,196 +0,0 @@
1
- const ENV_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max", "auto"];
2
- const PINNED_MODEL_ENV_FAMILIES = [
3
- {
4
- modelVar: "ANTHROPIC_DEFAULT_OPUS_MODEL",
5
- capsVar: "ANTHROPIC_DEFAULT_OPUS_MODEL_SUPPORTED_CAPABILITIES",
6
- label: "Opus",
7
- },
8
- {
9
- modelVar: "ANTHROPIC_DEFAULT_SONNET_MODEL",
10
- capsVar: "ANTHROPIC_DEFAULT_SONNET_MODEL_SUPPORTED_CAPABILITIES",
11
- label: "Sonnet",
12
- },
13
- {
14
- modelVar: "ANTHROPIC_DEFAULT_HAIKU_MODEL",
15
- capsVar: "ANTHROPIC_DEFAULT_HAIKU_MODEL_SUPPORTED_CAPABILITIES",
16
- label: "Haiku",
17
- },
18
- {
19
- modelVar: "ANTHROPIC_CUSTOM_MODEL_OPTION",
20
- capsVar: "ANTHROPIC_CUSTOM_MODEL_OPTION_SUPPORTED_CAPABILITIES",
21
- label: "Custom model option",
22
- },
23
- ];
24
-
25
- function envFlagEnabled(value) {
26
- if (value === true || value === 1) return true;
27
- if (typeof value !== "string") return false;
28
- return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
29
- }
30
-
31
- function normalizedEnvValue(value) {
32
- if (typeof value !== "string") return null;
33
- const normalized = value.trim();
34
- return normalized.length > 0 ? normalized : null;
35
- }
36
-
37
- function stripExtendedContextSuffix(modelId) {
38
- return modelId.replace(/\[1m\]$/i, "");
39
- }
40
-
41
- function looksProviderSpecificModelId(modelId) {
42
- const normalized = stripExtendedContextSuffix(modelId);
43
- if (normalized.includes(":") || normalized.includes("/")) return true;
44
- if (/^(?:us\.)?anthropic\./i.test(normalized)) return true;
45
- if (!/^claude-(?:opus|sonnet|haiku)-/i.test(normalized)) return true;
46
- return false;
47
- }
48
-
49
- function positiveIntegerFromEnv(value) {
50
- const normalized = normalizedEnvValue(value);
51
- if (!normalized) return null;
52
- if (!/^[0-9]+$/.test(normalized)) return Number.NaN;
53
- const parsed = Number(normalized);
54
- return parsed > 0 ? parsed : Number.NaN;
55
- }
56
-
57
- export function inspectRuntimeEnvironment(env = process.env) {
58
- const entries = [];
59
- const inCi = envFlagEnabled(env.CI);
60
-
61
- if (envFlagEnabled(env.CLAUDE_CODE_SIMPLE)) {
62
- entries.push({
63
- level: "err",
64
- text: "CLAUDE_CODE_SIMPLE enabled (bare/simple mode)",
65
- details: [
66
- "official docs: this disables auto-discovery of hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md",
67
- "CurDX-Flow cannot load correctly in this mode; unset it before launching Claude Code",
68
- ],
69
- });
70
- }
71
-
72
- if (envFlagEnabled(env.CLAUDE_CODE_SIMPLE_SYSTEM_PROMPT)) {
73
- entries.push({
74
- level: "warn",
75
- text: "CLAUDE_CODE_SIMPLE_SYSTEM_PROMPT enabled",
76
- details: [
77
- "official docs: discovery still works, but Claude runs with the minimal system prompt and collapsed tool descriptions",
78
- "CurDX-Flow may still load, but planning/review behavior can degrade versus the normal Claude Code prompt",
79
- ],
80
- });
81
- }
82
-
83
- const effortLevel = normalizedEnvValue(env.CLAUDE_CODE_EFFORT_LEVEL);
84
- if (effortLevel && !ENV_EFFORT_LEVELS.includes(effortLevel)) {
85
- entries.push({
86
- level: "warn",
87
- text: `CLAUDE_CODE_EFFORT_LEVEL invalid (${effortLevel})`,
88
- details: [
89
- `expected one of: ${ENV_EFFORT_LEVELS.join(", ")}`,
90
- "invalid effort env values can make sessions harder to reason about; remove or correct it",
91
- ],
92
- });
93
- } else if (effortLevel === "low" || effortLevel === "medium") {
94
- entries.push({
95
- level: "warn",
96
- text: `CLAUDE_CODE_EFFORT_LEVEL ${effortLevel}`,
97
- details: [
98
- "this takes precedence over /effort and settings effortLevel",
99
- "CurDX-Flow planning, verification, and review-heavy turns usually work better at high or xhigh",
100
- ],
101
- });
102
- } else if (effortLevel) {
103
- entries.push({
104
- level: "info",
105
- text: `CLAUDE_CODE_EFFORT_LEVEL ${effortLevel}`,
106
- details: [
107
- "session effort is pinned through the environment for this process",
108
- ],
109
- });
110
- }
111
-
112
- if (envFlagEnabled(env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS)) {
113
- entries.push({
114
- level: "info",
115
- text: "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS enabled",
116
- details: [
117
- "official docs: this enables experimental team surfaces such as SendMessage / TeamCreate / TeamDelete",
118
- "CurDX-Flow does not depend on these runtime-gated tools, but this explains why teammate features may appear in this session",
119
- ],
120
- });
121
- }
122
-
123
- const syncPluginInstall = envFlagEnabled(env.CLAUDE_CODE_SYNC_PLUGIN_INSTALL);
124
- const syncPluginInstallTimeout = positiveIntegerFromEnv(
125
- env.CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS
126
- );
127
- const pluginSeedDir = normalizedEnvValue(env.CLAUDE_CODE_PLUGIN_SEED_DIR);
128
-
129
- if (normalizedEnvValue(env.CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS)) {
130
- if (Number.isNaN(syncPluginInstallTimeout)) {
131
- entries.push({
132
- level: "warn",
133
- text: "CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS invalid",
134
- details: [
135
- "expected a positive integer timeout in milliseconds",
136
- "invalid timeout values can make headless plugin-install behavior harder to reason about",
137
- ],
138
- });
139
- } else if (!syncPluginInstall) {
140
- entries.push({
141
- level: "warn",
142
- text: "CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS set without CLAUDE_CODE_SYNC_PLUGIN_INSTALL",
143
- details: [
144
- "official docs: the timeout only applies when synchronous plugin installation is enabled",
145
- "set CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1 or remove the timeout override",
146
- ],
147
- });
148
- }
149
- }
150
-
151
- if (pluginSeedDir) {
152
- entries.push({
153
- level: "info",
154
- text: "CLAUDE_CODE_PLUGIN_SEED_DIR configured",
155
- details: [
156
- `seed dir: ${pluginSeedDir}`,
157
- "official docs: pre-populated plugin seeds let containers and CI start with marketplaces/plugins already available",
158
- ],
159
- });
160
- }
161
-
162
- if (inCi && !syncPluginInstall && !pluginSeedDir) {
163
- entries.push({
164
- level: "warn",
165
- text: "CI environment without synchronous or seeded plugin availability",
166
- details: [
167
- "prefer claude --bare -p for CI so runs do not inherit local hooks, skills, plugins, MCP discovery, or CLAUDE.md unexpectedly",
168
- "official docs: in non-interactive/headless mode, marketplace plugins may install in the background and miss the first turn",
169
- "set CLAUDE_CODE_SYNC_PLUGIN_INSTALL=1 for headless runs that depend on plugin availability on turn one",
170
- "or pre-populate plugins with CLAUDE_CODE_PLUGIN_SEED_DIR in containers/CI images",
171
- "if the run needs project assets in bare mode, pass them explicitly with --plugin-dir, --settings, or --mcp-config",
172
- ],
173
- });
174
- }
175
-
176
- for (const family of PINNED_MODEL_ENV_FAMILIES) {
177
- const modelId = normalizedEnvValue(env[family.modelVar]);
178
- if (!modelId) continue;
179
-
180
- const caps = normalizedEnvValue(env[family.capsVar]);
181
- if (!looksProviderSpecificModelId(modelId) || caps) continue;
182
-
183
- entries.push({
184
- level: "warn",
185
- text: `${family.modelVar} uses a provider-specific/custom model id`,
186
- details: [
187
- `${family.label} pinned to: ${modelId}`,
188
- `${family.capsVar} is unset`,
189
- "official docs: custom/provider model IDs can disable Claude Code feature detection for effort and thinking",
190
- `declare ${family.capsVar} when pinning custom Bedrock / Vertex / Foundry / gateway model IDs`,
191
- ],
192
- });
193
- }
194
-
195
- return { entries };
196
- }
@@ -1,44 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import YAML from "yaml";
3
-
4
- function formatYamlErrors(errors) {
5
- return errors.map((error) => error.message).join("; ");
6
- }
7
-
8
- export function extractFrontmatterBlock(text, sourceLabel = "frontmatter") {
9
- const match = text.match(/^---\n([\s\S]*?)\n---(?:\n|$)/);
10
- if (!match) {
11
- throw new Error(`${sourceLabel}: missing YAML frontmatter`);
12
- }
13
-
14
- return match[1];
15
- }
16
-
17
- export function parseFrontmatterBlock(block, sourceLabel = "frontmatter") {
18
- const document = YAML.parseDocument(block, {
19
- strict: true,
20
- uniqueKeys: true,
21
- });
22
-
23
- if (document.errors.length > 0) {
24
- throw new Error(`${sourceLabel}: invalid YAML (${formatYamlErrors(document.errors)})`);
25
- }
26
-
27
- const value = document.toJS();
28
- if (value == null) return {};
29
- if (!value || typeof value !== "object" || Array.isArray(value)) {
30
- throw new Error(`${sourceLabel}: frontmatter must parse to an object`);
31
- }
32
-
33
- return value;
34
- }
35
-
36
- export function readFrontmatter(filePath) {
37
- const text = readFileSync(filePath, "utf-8");
38
- const block = extractFrontmatterBlock(text, filePath);
39
- return parseFrontmatterBlock(block, filePath);
40
- }
41
-
42
- export function readFrontmatterFields(filePath) {
43
- return Object.keys(readFrontmatter(filePath));
44
- }
@@ -1,57 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- import Ajv from "ajv";
4
-
5
- const ajv = new Ajv({
6
- allErrors: true,
7
- allowUnionTypes: true,
8
- strict: false,
9
- });
10
-
11
- const validatorCache = new Map();
12
-
13
- function formatParams(params = {}) {
14
- if (params.missingProperty) return ` missingProperty=${params.missingProperty}`;
15
- if (params.additionalProperty) return ` additionalProperty=${params.additionalProperty}`;
16
- return "";
17
- }
18
-
19
- export function readJsonFile(filePath) {
20
- return JSON.parse(readFileSync(filePath, "utf-8"));
21
- }
22
-
23
- export function formatAjvErrors(errors = []) {
24
- return errors.map((error) => {
25
- const where = error.instancePath || error.schemaPath || "(root)";
26
- return `${where}: ${error.message}${formatParams(error.params)}`;
27
- });
28
- }
29
-
30
- function getSchemaValidator(schemaPath) {
31
- const absPath = resolve(schemaPath);
32
- if (validatorCache.has(absPath)) return validatorCache.get(absPath);
33
-
34
- const schema = readJsonFile(absPath);
35
- const validate = ajv.compile(schema);
36
- const entry = { absPath, schema, validate };
37
- validatorCache.set(absPath, entry);
38
- return entry;
39
- }
40
-
41
- export function validateAgainstSchemaFile(schemaPath, data) {
42
- const { validate } = getSchemaValidator(schemaPath);
43
- const valid = Boolean(validate(data));
44
- return {
45
- valid,
46
- errors: valid ? [] : formatAjvErrors(validate.errors),
47
- };
48
- }
49
-
50
- export function validateSchemaFile(schemaPath) {
51
- const schema = readJsonFile(resolve(schemaPath));
52
- const valid = ajv.validateSchema(schema);
53
- return {
54
- valid,
55
- errors: valid ? [] : formatAjvErrors(ajv.errors),
56
- };
57
- }
@@ -1,25 +0,0 @@
1
- const isTTY = process.stdout.isTTY && process.env.TERM !== "dumb";
2
- const c = (code) => (s) => isTTY ? `\x1b[${code}m${s}\x1b[0m` : String(s);
3
-
4
- export const color = {
5
- red: c("31"),
6
- green: c("32"),
7
- yellow: c("33"),
8
- blue: c("34"),
9
- magenta: c("35"),
10
- cyan: c("36"),
11
- dim: c("2"),
12
- bold: c("1"),
13
- underline: c("4"),
14
- };
15
-
16
- export const log = {
17
- info: (msg) => console.log(`${color.cyan("ℹ")} ${msg}`),
18
- ok: (msg) => console.log(`${color.green("✓")} ${msg}`),
19
- warn: (msg) => console.log(`${color.yellow("⚠")} ${msg}`),
20
- err: (msg) => console.error(`${color.red("✗")} ${msg}`),
21
- step: (n, total, msg) =>
22
- console.log(`${color.dim(`[${n}/${total}]`)} ${msg}`),
23
- blank: () => console.log(""),
24
- title: (msg) => console.log(`\n${color.bold(msg)}\n`),
25
- };
@@ -1,60 +0,0 @@
1
- import { spawn, spawnSync } from "node:child_process";
2
-
3
- /**
4
- * Run a command, stream output live. Returns { code, stdout, stderr }.
5
- */
6
- export function run(cmd, args = [], opts = {}) {
7
- return new Promise((resolve) => {
8
- const child = spawn(cmd, args, {
9
- stdio: opts.silent ? ["ignore", "pipe", "pipe"] : "inherit",
10
- env: { ...process.env, ...opts.env },
11
- cwd: opts.cwd || process.cwd(),
12
- shell: false,
13
- });
14
-
15
- let stdout = "";
16
- let stderr = "";
17
- if (opts.silent) {
18
- child.stdout.on("data", (d) => (stdout += d.toString()));
19
- child.stderr.on("data", (d) => (stderr += d.toString()));
20
- }
21
-
22
- child.on("close", (code) => resolve({ code, stdout, stderr }));
23
- child.on("error", (err) =>
24
- resolve({ code: -1, stdout: "", stderr: err.message })
25
- );
26
- });
27
- }
28
-
29
- /**
30
- * Sync run — for quick checks (e.g. "which claude").
31
- */
32
- export function runSync(cmd, args = [], opts = {}) {
33
- const res = spawnSync(cmd, args, {
34
- encoding: "utf-8",
35
- shell: false,
36
- cwd: opts.cwd,
37
- env: opts.env ? { ...process.env, ...opts.env } : process.env,
38
- });
39
- return {
40
- code: res.status ?? -1,
41
- stdout: res.stdout ?? "",
42
- stderr: res.stderr ?? "",
43
- };
44
- }
45
-
46
- export function has(cmd) {
47
- const res = runSync("which", [cmd]);
48
- return res.code === 0 && res.stdout.trim().length > 0;
49
- }
50
-
51
- export function resultOutput(result) {
52
- return (result?.stderr || "").trim() || (result?.stdout || "").trim();
53
- }
54
-
55
- export function resultLastLine(result) {
56
- const output = resultOutput(result);
57
- if (!output) return "";
58
- const lines = output.split(/\r?\n/).filter(Boolean);
59
- return lines.at(-1) || "";
60
- }
@@ -1,135 +0,0 @@
1
- import { createInterface } from "node:readline";
2
-
3
- import { color } from "./logging.js";
4
-
5
- let _clack = null;
6
-
7
- async function getClack() {
8
- if (!_clack) {
9
- _clack = await import("@clack/prompts");
10
- }
11
- return _clack;
12
- }
13
-
14
- async function handleCancel(value, message = "Operation cancelled") {
15
- const clack = await getClack();
16
- if (clack.isCancel(value)) {
17
- clack.cancel(message);
18
- process.exit(0);
19
- }
20
- return false;
21
- }
22
-
23
- export async function select(options) {
24
- const clack = await getClack();
25
- const result = await clack.select({
26
- message: options.message,
27
- options: options.options,
28
- initialValue: options.initialValue,
29
- });
30
- await handleCancel(result);
31
- return result;
32
- }
33
-
34
- export async function multiselectClack(options) {
35
- const clack = await getClack();
36
- const result = await clack.multiselect({
37
- message: options.message,
38
- options: options.options,
39
- initialValues: options.initialValues || [],
40
- required: options.required !== undefined ? options.required : false,
41
- });
42
- await handleCancel(result);
43
- return result;
44
- }
45
-
46
- export async function text(options) {
47
- const clack = await getClack();
48
- const result = await clack.text({
49
- message: options.message,
50
- placeholder: options.placeholder,
51
- defaultValue: options.defaultValue,
52
- validate: options.validate,
53
- });
54
- await handleCancel(result);
55
- return result;
56
- }
57
-
58
- export async function spinner() {
59
- const clack = await getClack();
60
- return clack.spinner();
61
- }
62
-
63
- export async function intro(message) {
64
- const clack = await getClack();
65
- clack.intro(message);
66
- }
67
-
68
- export async function outro(message) {
69
- const clack = await getClack();
70
- clack.outro(message);
71
- }
72
-
73
- export async function note(message, title) {
74
- const clack = await getClack();
75
- clack.note(message, title);
76
- }
77
-
78
- export function confirm(message, defaultYes = true) {
79
- return new Promise((resolve) => {
80
- const rl = createInterface({
81
- input: process.stdin,
82
- output: process.stdout,
83
- });
84
- const hint = defaultYes ? "[Y/n]" : "[y/N]";
85
- rl.question(`${color.cyan("?")} ${message} ${color.dim(hint)} `, (ans) => {
86
- rl.close();
87
- const v = ans.trim().toLowerCase();
88
- if (v === "") return resolve(defaultYes);
89
- resolve(v === "y" || v === "yes");
90
- });
91
- });
92
- }
93
-
94
- export function multiSelect(message, choices, defaults = null) {
95
- return new Promise((resolve) => {
96
- const defaultSet = new Set(defaults ?? choices.map((_, i) => i));
97
- console.log(`${color.cyan("?")} ${message}`);
98
- choices.forEach((ch, i) => {
99
- const checked = defaultSet.has(i)
100
- ? color.green("[x]")
101
- : color.dim("[ ]");
102
- console.log(
103
- ` ${checked} ${color.bold(String(i + 1))}. ${ch.label}${ch.hint ? color.dim(` — ${ch.hint}`) : ""}`
104
- );
105
- });
106
- console.log(
107
- color.dim(
108
- " (comma-separated selection, e.g. 1,3 | a=all | n=none | Enter=default)"
109
- )
110
- );
111
-
112
- const rl = createInterface({
113
- input: process.stdin,
114
- output: process.stdout,
115
- });
116
- rl.question(` > `, (ans) => {
117
- rl.close();
118
- const v = ans.trim().toLowerCase();
119
- let selected;
120
- if (v === "") {
121
- selected = [...defaultSet];
122
- } else if (v === "a" || v === "all") {
123
- selected = choices.map((_, i) => i);
124
- } else if (v === "n" || v === "none") {
125
- selected = [];
126
- } else {
127
- selected = v
128
- .split(/[,\s]+/)
129
- .map((x) => parseInt(x, 10) - 1)
130
- .filter((i) => Number.isInteger(i) && i >= 0 && i < choices.length);
131
- }
132
- resolve(selected.map((i) => choices[i].value));
133
- });
134
- });
135
- }
@@ -1,107 +0,0 @@
1
- import {
2
- existsSync,
3
- lstatSync,
4
- mkdirSync,
5
- readlinkSync,
6
- symlinkSync,
7
- unlinkSync,
8
- } from "node:fs";
9
- import { homedir } from "node:os";
10
- import { join } from "node:path";
11
-
12
- import { has } from "./process.js";
13
-
14
- const HOME = homedir();
15
-
16
- const BUN_CANDIDATES = [
17
- join(HOME, ".bun", "bin", "bun"),
18
- "/opt/homebrew/bin/bun",
19
- "/usr/local/bin/bun",
20
- "/home/linuxbrew/.linuxbrew/bin/bun",
21
- ];
22
-
23
- const UV_CANDIDATES = [
24
- join(HOME, ".local", "bin", "uv"),
25
- join(HOME, ".cargo", "bin", "uv"),
26
- "/opt/homebrew/bin/uv",
27
- "/usr/local/bin/uv",
28
- ];
29
-
30
- const SYMLINK_TARGET_DIRS = [
31
- join(HOME, ".local", "bin"),
32
- join(HOME, ".npm-global", "bin"),
33
- ];
34
-
35
- function findRuntime(candidates) {
36
- for (const p of candidates) if (existsSync(p)) return p;
37
- return null;
38
- }
39
-
40
- function findSymlinkDir() {
41
- const pathDirs = (process.env.PATH || "").split(":").filter(Boolean);
42
- for (const d of SYMLINK_TARGET_DIRS) {
43
- if (pathDirs.includes(d)) {
44
- try {
45
- if (!existsSync(d)) mkdirSync(d, { recursive: true });
46
- return d;
47
- } catch {
48
- // continue
49
- }
50
- }
51
- }
52
- return null;
53
- }
54
-
55
- function getRuntimeStatus(cmd, candidates, { repair = false } = {}) {
56
- if (has(cmd)) return { status: "ok" };
57
- const realPath = findRuntime(candidates);
58
- if (!realPath) return { status: "missing" };
59
-
60
- const linkDir = findSymlinkDir();
61
- if (!linkDir) return { status: "path-unwritable", path: realPath };
62
-
63
- if (!repair) {
64
- return { status: "linkable", path: realPath, link: join(linkDir, cmd) };
65
- }
66
-
67
- const linkPath = join(linkDir, cmd);
68
- if (existsSync(linkPath)) {
69
- try {
70
- const stat = lstatSync(linkPath);
71
- if (stat.isSymbolicLink() && readlinkSync(linkPath) === realPath) {
72
- return { status: "ok", path: realPath, link: linkPath };
73
- }
74
- unlinkSync(linkPath);
75
- } catch {
76
- // ignore
77
- }
78
- }
79
- try {
80
- symlinkSync(realPath, linkPath);
81
- return { status: "linked", path: realPath, link: linkPath };
82
- } catch {
83
- return { status: "path-unwritable", path: realPath };
84
- }
85
- }
86
-
87
- export function inspectRuntimeInPath(cmd, candidates) {
88
- return getRuntimeStatus(cmd, candidates, { repair: false });
89
- }
90
-
91
- export function ensureRuntimeInPath(cmd, candidates) {
92
- return getRuntimeStatus(cmd, candidates, { repair: true });
93
- }
94
-
95
- export function inspectClaudeMemRuntimes() {
96
- return {
97
- bun: inspectRuntimeInPath("bun", BUN_CANDIDATES),
98
- uv: inspectRuntimeInPath("uv", UV_CANDIDATES),
99
- };
100
- }
101
-
102
- export function ensureClaudeMemRuntimes() {
103
- return {
104
- bun: ensureRuntimeInPath("bun", BUN_CANDIDATES),
105
- uv: ensureRuntimeInPath("uv", UV_CANDIDATES),
106
- };
107
- }