@curdx/flow 2.3.11 → 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 (210) hide show
  1. package/CHANGELOG.md +21 -34
  2. package/LICENSE +1 -1
  3. package/README.md +28 -79
  4. package/dist/index.mjs +995 -0
  5. package/package.json +33 -42
  6. package/.claude-plugin/marketplace.json +0 -48
  7. package/.claude-plugin/plugin.json +0 -70
  8. package/agent-preamble/preamble.md +0 -314
  9. package/agents/flow-adversary.md +0 -202
  10. package/agents/flow-architect.md +0 -197
  11. package/agents/flow-brownfield-analyst.md +0 -142
  12. package/agents/flow-debugger.md +0 -321
  13. package/agents/flow-edge-hunter.md +0 -288
  14. package/agents/flow-executor.md +0 -269
  15. package/agents/flow-orchestrator.md +0 -145
  16. package/agents/flow-planner.md +0 -246
  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 -165
  20. package/agents/flow-reviewer.md +0 -303
  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 -229
  24. package/agents/flow-ux-designer.md +0 -221
  25. package/agents/flow-verifier.md +0 -349
  26. package/bin/curdx-flow +0 -5
  27. package/bin/curdx-flow.js +0 -54
  28. package/cli/README.md +0 -104
  29. package/cli/doctor-workflow.js +0 -483
  30. package/cli/doctor.js +0 -73
  31. package/cli/help.js +0 -59
  32. package/cli/install-bundled-mcps.js +0 -37
  33. package/cli/install-companions.js +0 -19
  34. package/cli/install-context7-config.js +0 -80
  35. package/cli/install-curdx-plugin.js +0 -96
  36. package/cli/install-language.js +0 -35
  37. package/cli/install-next-steps.js +0 -29
  38. package/cli/install-options.js +0 -9
  39. package/cli/install-paths.js +0 -52
  40. package/cli/install-recommended-plugins.js +0 -104
  41. package/cli/install-required-plugins.js +0 -57
  42. package/cli/install-self-update.js +0 -62
  43. package/cli/install-workflow.js +0 -209
  44. package/cli/install.js +0 -101
  45. package/cli/lib/claude-commands.js +0 -41
  46. package/cli/lib/claude-ops.js +0 -47
  47. package/cli/lib/claude.js +0 -183
  48. package/cli/lib/config.js +0 -24
  49. package/cli/lib/doctor-claude-settings.js +0 -1186
  50. package/cli/lib/doctor-report.js +0 -978
  51. package/cli/lib/doctor-runtime-environment.js +0 -196
  52. package/cli/lib/frontmatter.js +0 -44
  53. package/cli/lib/json-schema.js +0 -57
  54. package/cli/lib/logging.js +0 -25
  55. package/cli/lib/process.js +0 -60
  56. package/cli/lib/prompts.js +0 -135
  57. package/cli/lib/runtime.js +0 -107
  58. package/cli/lib/semver.js +0 -109
  59. package/cli/lib/version.js +0 -12
  60. package/cli/protocols-body.md +0 -22
  61. package/cli/protocols.js +0 -162
  62. package/cli/registry.js +0 -123
  63. package/cli/router.js +0 -49
  64. package/cli/uninstall-actions.js +0 -360
  65. package/cli/uninstall-workflow.js +0 -146
  66. package/cli/uninstall.js +0 -42
  67. package/cli/upgrade-workflow.js +0 -80
  68. package/cli/upgrade.js +0 -91
  69. package/cli/utils.js +0 -40
  70. package/gates/adversarial-review-gate.md +0 -219
  71. package/gates/coverage-audit-gate.md +0 -182
  72. package/gates/devex-gate.md +0 -254
  73. package/gates/edge-case-gate.md +0 -194
  74. package/gates/karpathy-gate.md +0 -130
  75. package/gates/security-gate.md +0 -218
  76. package/gates/tdd-gate.md +0 -182
  77. package/gates/test-quality-gate.md +0 -59
  78. package/gates/verification-gate.md +0 -179
  79. package/hooks/hooks.json +0 -58
  80. package/hooks/scripts/common.sh +0 -46
  81. package/hooks/scripts/inject-karpathy.sh +0 -53
  82. package/hooks/scripts/quick-mode-guard.sh +0 -68
  83. package/hooks/scripts/session-start.sh +0 -90
  84. package/hooks/scripts/stop-watcher.sh +0 -230
  85. package/hooks/scripts/subagent-artifact-guard.sh +0 -159
  86. package/hooks/scripts/subagent-statusline.sh +0 -105
  87. package/knowledge/artifact-output-discipline.md +0 -24
  88. package/knowledge/artifact-summary-contracts.md +0 -50
  89. package/knowledge/atomic-commits.md +0 -262
  90. package/knowledge/claude-code-runtime-contracts.md +0 -219
  91. package/knowledge/epic-decomposition.md +0 -307
  92. package/knowledge/execution-strategies.md +0 -303
  93. package/knowledge/karpathy-guidelines.md +0 -219
  94. package/knowledge/planning-reviews.md +0 -211
  95. package/knowledge/poc-first-workflow.md +0 -223
  96. package/knowledge/review-feedback-intake.md +0 -57
  97. package/knowledge/spec-driven-development.md +0 -180
  98. package/knowledge/systematic-debugging.md +0 -378
  99. package/knowledge/two-stage-review.md +0 -249
  100. package/knowledge/wave-execution.md +0 -403
  101. package/monitors/monitors.json +0 -8
  102. package/monitors/scripts/flow-state-monitor.sh +0 -99
  103. package/output-styles/curdx-evidence-first.md +0 -34
  104. package/schemas/agent-frontmatter.schema.json +0 -63
  105. package/schemas/config.schema.json +0 -134
  106. package/schemas/gate-frontmatter.schema.json +0 -30
  107. package/schemas/hooks.schema.json +0 -115
  108. package/schemas/output-style-frontmatter.schema.json +0 -22
  109. package/schemas/plugin-manifest.schema.json +0 -436
  110. package/schemas/plugin-settings.schema.json +0 -29
  111. package/schemas/skill-frontmatter.schema.json +0 -177
  112. package/schemas/spec-frontmatter.schema.json +0 -42
  113. package/schemas/spec-state.schema.json +0 -147
  114. package/settings.json +0 -7
  115. package/skills/brownfield-index/SKILL.md +0 -53
  116. package/skills/brownfield-index/references/applicability.md +0 -12
  117. package/skills/brownfield-index/references/handoff.md +0 -8
  118. package/skills/brownfield-index/references/index-contract.md +0 -10
  119. package/skills/browser-qa/SKILL.md +0 -39
  120. package/skills/browser-qa/references/handoff.md +0 -6
  121. package/skills/browser-qa/references/prerequisites.md +0 -10
  122. package/skills/browser-qa/references/qa-contract.md +0 -20
  123. package/skills/cancel/SKILL.md +0 -41
  124. package/skills/cancel/references/destructive-mode.md +0 -17
  125. package/skills/cancel/references/reporting.md +0 -18
  126. package/skills/cancel/references/state-recovery.md +0 -30
  127. package/skills/cancel/references/target-resolution.md +0 -7
  128. package/skills/debug/SKILL.md +0 -45
  129. package/skills/debug/references/context-gathering.md +0 -11
  130. package/skills/debug/references/failure-guard.md +0 -25
  131. package/skills/debug/references/intake.md +0 -12
  132. package/skills/debug/references/phase-workflow.md +0 -34
  133. package/skills/debug/references/reporting.md +0 -20
  134. package/skills/epic/SKILL.md +0 -39
  135. package/skills/epic/references/epic-artifacts.md +0 -20
  136. package/skills/epic/references/epic-intake.md +0 -9
  137. package/skills/epic/references/slice-handoff.md +0 -16
  138. package/skills/fast/SKILL.md +0 -62
  139. package/skills/fast/references/applicability.md +0 -25
  140. package/skills/fast/references/clarification.md +0 -20
  141. package/skills/fast/references/execution-contract.md +0 -56
  142. package/skills/help/SKILL.md +0 -55
  143. package/skills/help/references/dispatch.md +0 -20
  144. package/skills/help/references/overview.md +0 -39
  145. package/skills/help/references/troubleshoot.md +0 -47
  146. package/skills/help/references/workflow.md +0 -37
  147. package/skills/implement/SKILL.md +0 -96
  148. package/skills/implement/references/error-recovery.md +0 -36
  149. package/skills/implement/references/linear-execution.md +0 -32
  150. package/skills/implement/references/preflight.md +0 -43
  151. package/skills/implement/references/progress-contract.md +0 -32
  152. package/skills/implement/references/state-init.md +0 -33
  153. package/skills/implement/references/stop-hook-execution.md +0 -36
  154. package/skills/implement/references/strategy-router.md +0 -38
  155. package/skills/implement/references/subagent-execution.md +0 -43
  156. package/skills/implement/references/wave-execution.md +0 -162
  157. package/skills/init/SKILL.md +0 -49
  158. package/skills/init/references/gitignore-and-health.md +0 -26
  159. package/skills/init/references/next-steps.md +0 -22
  160. package/skills/init/references/preflight.md +0 -15
  161. package/skills/init/references/scaffold-contract.md +0 -27
  162. package/skills/review/SKILL.md +0 -82
  163. package/skills/review/references/optional-passes.md +0 -48
  164. package/skills/review/references/preflight.md +0 -38
  165. package/skills/review/references/report-contract.md +0 -49
  166. package/skills/review/references/reporting.md +0 -20
  167. package/skills/review/references/stage-execution.md +0 -32
  168. package/skills/security-audit/SKILL.md +0 -47
  169. package/skills/security-audit/references/audit-contract.md +0 -21
  170. package/skills/security-audit/references/gate-handoff.md +0 -8
  171. package/skills/security-audit/references/scope-and-depth.md +0 -9
  172. package/skills/spec/SKILL.md +0 -100
  173. package/skills/spec/references/artifact-landing.md +0 -31
  174. package/skills/spec/references/phase-execution.md +0 -50
  175. package/skills/spec/references/planning-review.md +0 -31
  176. package/skills/spec/references/preflight-and-routing.md +0 -46
  177. package/skills/spec/references/reporting.md +0 -21
  178. package/skills/start/SKILL.md +0 -84
  179. package/skills/start/references/branch-routing.md +0 -51
  180. package/skills/start/references/mode-semantics.md +0 -12
  181. package/skills/start/references/preflight.md +0 -13
  182. package/skills/start/references/reporting.md +0 -20
  183. package/skills/start/references/state-seeding.md +0 -44
  184. package/skills/start/references/workflow-handoff.md +0 -26
  185. package/skills/status/SKILL.md +0 -41
  186. package/skills/status/references/gather-contract.md +0 -27
  187. package/skills/status/references/health-rules.md +0 -27
  188. package/skills/status/references/output-contract.md +0 -24
  189. package/skills/status/references/preflight.md +0 -10
  190. package/skills/status/references/recovery-hints.md +0 -18
  191. package/skills/ui-sketch/SKILL.md +0 -39
  192. package/skills/ui-sketch/references/brief-intake.md +0 -10
  193. package/skills/ui-sketch/references/iteration-handoff.md +0 -5
  194. package/skills/ui-sketch/references/variant-contract.md +0 -15
  195. package/skills/verify/SKILL.md +0 -56
  196. package/skills/verify/references/evidence-workflow.md +0 -39
  197. package/skills/verify/references/output-contract.md +0 -23
  198. package/skills/verify/references/preflight.md +0 -11
  199. package/skills/verify/references/report-handoff.md +0 -35
  200. package/skills/verify/references/strict-mode.md +0 -12
  201. package/templates/CONTEXT.md.tmpl +0 -53
  202. package/templates/PROJECT.md.tmpl +0 -59
  203. package/templates/ROADMAP.md.tmpl +0 -50
  204. package/templates/STATE.md.tmpl +0 -49
  205. package/templates/config.json.tmpl +0 -51
  206. package/templates/design.md.tmpl +0 -83
  207. package/templates/progress.md.tmpl +0 -77
  208. package/templates/requirements.md.tmpl +0 -76
  209. package/templates/research.md.tmpl +0 -83
  210. 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
- }