mustard-claude 2.0.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 (72) hide show
  1. package/README.md +198 -0
  2. package/bin/mustard.js +5 -0
  3. package/dist/analyzers/llm.d.ts +13 -0
  4. package/dist/analyzers/llm.js +339 -0
  5. package/dist/analyzers/llm.js.map +1 -0
  6. package/dist/analyzers/semantic.d.ts +13 -0
  7. package/dist/analyzers/semantic.js +215 -0
  8. package/dist/analyzers/semantic.js.map +1 -0
  9. package/dist/cli.d.ts +1 -0
  10. package/dist/cli.js +42 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/commands/init.d.ts +5 -0
  13. package/dist/commands/init.js +377 -0
  14. package/dist/commands/init.js.map +1 -0
  15. package/dist/commands/sync.d.ts +5 -0
  16. package/dist/commands/sync.js +235 -0
  17. package/dist/commands/sync.js.map +1 -0
  18. package/dist/commands/update.d.ts +8 -0
  19. package/dist/commands/update.js +237 -0
  20. package/dist/commands/update.js.map +1 -0
  21. package/dist/generators/claude-md-llm.d.ts +5 -0
  22. package/dist/generators/claude-md-llm.js +101 -0
  23. package/dist/generators/claude-md-llm.js.map +1 -0
  24. package/dist/generators/claude-md-template.d.ts +5 -0
  25. package/dist/generators/claude-md-template.js +273 -0
  26. package/dist/generators/claude-md-template.js.map +1 -0
  27. package/dist/generators/commands.d.ts +17 -0
  28. package/dist/generators/commands.js +845 -0
  29. package/dist/generators/commands.js.map +1 -0
  30. package/dist/generators/context.d.ts +11 -0
  31. package/dist/generators/context.js +621 -0
  32. package/dist/generators/context.js.map +1 -0
  33. package/dist/generators/hooks.d.ts +5 -0
  34. package/dist/generators/hooks.js +128 -0
  35. package/dist/generators/hooks.js.map +1 -0
  36. package/dist/generators/index.d.ts +11 -0
  37. package/dist/generators/index.js +541 -0
  38. package/dist/generators/index.js.map +1 -0
  39. package/dist/generators/prompts.d.ts +13 -0
  40. package/dist/generators/prompts.js +579 -0
  41. package/dist/generators/prompts.js.map +1 -0
  42. package/dist/generators/registry.d.ts +5 -0
  43. package/dist/generators/registry.js +93 -0
  44. package/dist/generators/registry.js.map +1 -0
  45. package/dist/scanners/dependencies.d.ts +7 -0
  46. package/dist/scanners/dependencies.js +195 -0
  47. package/dist/scanners/dependencies.js.map +1 -0
  48. package/dist/scanners/index.d.ts +6 -0
  49. package/dist/scanners/index.js +37 -0
  50. package/dist/scanners/index.js.map +1 -0
  51. package/dist/scanners/samples.d.ts +8 -0
  52. package/dist/scanners/samples.js +193 -0
  53. package/dist/scanners/samples.js.map +1 -0
  54. package/dist/scanners/stack.d.ts +5 -0
  55. package/dist/scanners/stack.js +294 -0
  56. package/dist/scanners/stack.js.map +1 -0
  57. package/dist/scanners/structure.d.ts +5 -0
  58. package/dist/scanners/structure.js +274 -0
  59. package/dist/scanners/structure.js.map +1 -0
  60. package/dist/services/grepai.d.ts +25 -0
  61. package/dist/services/grepai.js +89 -0
  62. package/dist/services/grepai.js.map +1 -0
  63. package/dist/services/ollama.d.ts +22 -0
  64. package/dist/services/ollama.js +86 -0
  65. package/dist/services/ollama.js.map +1 -0
  66. package/dist/services/package-manager.d.ts +95 -0
  67. package/dist/services/package-manager.js +164 -0
  68. package/dist/services/package-manager.js.map +1 -0
  69. package/dist/types.d.ts +233 -0
  70. package/dist/types.js +5 -0
  71. package/dist/types.js.map +1 -0
  72. package/package.json +56 -0
@@ -0,0 +1,215 @@
1
+ import * as grepai from '../services/grepai.js';
2
+ /**
3
+ * Semantic search patterns for different aspects of a codebase
4
+ */
5
+ const SEARCH_QUERIES = {
6
+ architecture: [
7
+ 'service layer business logic implementation',
8
+ 'repository pattern data access',
9
+ 'API endpoint route handler',
10
+ 'controller action method'
11
+ ],
12
+ patterns: [
13
+ 'dependency injection constructor',
14
+ 'DTO data transfer object mapping',
15
+ 'middleware request pipeline',
16
+ 'validation rules validator'
17
+ ],
18
+ entities: [
19
+ 'entity class definition database',
20
+ 'database table schema model',
21
+ 'domain model aggregate root'
22
+ ],
23
+ frontend: [
24
+ 'React component with hooks and state',
25
+ 'custom hook data fetching',
26
+ 'form validation submit handler',
27
+ 'context provider state management'
28
+ ]
29
+ };
30
+ /**
31
+ * Discover patterns using semantic search
32
+ */
33
+ export async function discoverPatterns(options = {}) {
34
+ const { verbose = false, stacks = [] } = options;
35
+ const results = {
36
+ services: [],
37
+ repositories: [],
38
+ endpoints: [],
39
+ components: [],
40
+ hooks: [],
41
+ entities: [],
42
+ callGraph: null
43
+ };
44
+ // Backend patterns
45
+ const hasBackend = stacks.some(s => ['dotnet', 'node', 'python', 'java', 'go'].includes(s.name));
46
+ if (hasBackend) {
47
+ // Search for services
48
+ const serviceResults = await grepai.search('service layer business logic implementation', { limit: 5 });
49
+ results.services = serviceResults.results ?? [];
50
+ // Search for repositories
51
+ const repoResults = await grepai.search('repository pattern data access', { limit: 5 });
52
+ results.repositories = repoResults.results ?? [];
53
+ // Search for endpoints
54
+ const endpointResults = await grepai.search('API endpoint route handler', { limit: 5 });
55
+ results.endpoints = endpointResults.results ?? [];
56
+ // Search for entities
57
+ const entityResults = await grepai.search('entity class definition database model', { limit: 10 });
58
+ results.entities = (entityResults.results ?? [])
59
+ .map(r => {
60
+ const name = extractEntityName(r);
61
+ if (!name)
62
+ return null;
63
+ return {
64
+ name,
65
+ file: r.file_path,
66
+ type: inferEntityType(r)
67
+ };
68
+ })
69
+ .filter((e) => e !== null);
70
+ }
71
+ // Frontend patterns
72
+ const hasFrontend = stacks.some(s => ['react', 'nextjs', 'vue', 'angular'].includes(s.name));
73
+ if (hasFrontend) {
74
+ // Search for components
75
+ const componentResults = await grepai.search('React component with hooks and state', { limit: 5 });
76
+ results.components = componentResults.results ?? [];
77
+ // Search for hooks
78
+ const hookResults = await grepai.search('custom hook data fetching useQuery', { limit: 5 });
79
+ results.hooks = hookResults.results ?? [];
80
+ }
81
+ // Build call graph for main service if found
82
+ if (results.services.length > 0 && results.services[0]) {
83
+ const mainService = extractSymbolName(results.services[0]);
84
+ if (mainService) {
85
+ results.callGraph = await grepai.traceGraph(mainService, { depth: 2 });
86
+ }
87
+ }
88
+ return results;
89
+ }
90
+ /**
91
+ * Discover entities in the codebase
92
+ */
93
+ export async function discoverEntities(stacks = []) {
94
+ const entities = [];
95
+ // Search for entity definitions
96
+ const entityResults = await grepai.search('entity class definition table schema', { limit: 20 });
97
+ if (entityResults.results) {
98
+ for (const result of entityResults.results) {
99
+ const name = extractEntityName(result);
100
+ if (name && !entities.find(e => e.name === name)) {
101
+ entities.push({
102
+ name,
103
+ file: result.file_path,
104
+ type: inferEntityType(result)
105
+ });
106
+ }
107
+ }
108
+ }
109
+ return entities;
110
+ }
111
+ /**
112
+ * Get code samples for each pattern type
113
+ */
114
+ export async function getCodeSamples(patterns, options = {}) {
115
+ const { maxLines = 50 } = options;
116
+ const samples = {};
117
+ // Get service sample
118
+ if (patterns.services && patterns.services.length > 0 && patterns.services[0]) {
119
+ samples.service = {
120
+ file: patterns.services[0].file_path,
121
+ content: truncateContent(patterns.services[0].content, maxLines),
122
+ type: 'service'
123
+ };
124
+ }
125
+ // Get endpoint sample
126
+ if (patterns.endpoints && patterns.endpoints.length > 0 && patterns.endpoints[0]) {
127
+ samples.endpoint = {
128
+ file: patterns.endpoints[0].file_path,
129
+ content: truncateContent(patterns.endpoints[0].content, maxLines),
130
+ type: 'endpoint'
131
+ };
132
+ }
133
+ // Get hook sample
134
+ if (patterns.hooks && patterns.hooks.length > 0 && patterns.hooks[0]) {
135
+ samples.hook = {
136
+ file: patterns.hooks[0].file_path,
137
+ content: truncateContent(patterns.hooks[0].content, maxLines),
138
+ type: 'hook'
139
+ };
140
+ }
141
+ // Get component sample
142
+ if (patterns.components && patterns.components.length > 0 && patterns.components[0]) {
143
+ samples.component = {
144
+ file: patterns.components[0].file_path,
145
+ content: truncateContent(patterns.components[0].content, maxLines),
146
+ type: 'component'
147
+ };
148
+ }
149
+ return samples;
150
+ }
151
+ /**
152
+ * Extract symbol name from search result
153
+ */
154
+ function extractSymbolName(result) {
155
+ if (!result || !result.content)
156
+ return null;
157
+ // Try to extract class name
158
+ const classMatch = result.content.match(/class\s+(\w+)/);
159
+ if (classMatch?.[1])
160
+ return classMatch[1];
161
+ // Try to extract function name
162
+ const funcMatch = result.content.match(/(?:function|const|let|var)\s+(\w+)/);
163
+ if (funcMatch?.[1])
164
+ return funcMatch[1];
165
+ return null;
166
+ }
167
+ /**
168
+ * Extract entity name from search result
169
+ */
170
+ function extractEntityName(result) {
171
+ if (!result || !result.content)
172
+ return null;
173
+ // .NET entity
174
+ const classMatch = result.content.match(/class\s+(\w+)(?:\s*:\s*\w+)?/);
175
+ if (classMatch?.[1])
176
+ return classMatch[1];
177
+ // Drizzle schema
178
+ const drizzleMatch = result.content.match(/export\s+const\s+(\w+)\s*=/);
179
+ if (drizzleMatch?.[1])
180
+ return drizzleMatch[1];
181
+ // Python model
182
+ const pythonMatch = result.content.match(/class\s+(\w+)\s*\(/);
183
+ if (pythonMatch?.[1])
184
+ return pythonMatch[1];
185
+ return null;
186
+ }
187
+ /**
188
+ * Infer entity type from content
189
+ */
190
+ function inferEntityType(result) {
191
+ if (!result || !result.file_path)
192
+ return 'unknown';
193
+ const path = result.file_path.toLowerCase();
194
+ if (path.includes('schema') || path.includes('drizzle'))
195
+ return 'drizzle';
196
+ if (path.includes('entities') || path.includes('entity'))
197
+ return 'entity';
198
+ if (path.includes('models') || path.includes('model'))
199
+ return 'model';
200
+ if (path.includes('domain'))
201
+ return 'domain';
202
+ return 'unknown';
203
+ }
204
+ /**
205
+ * Truncate content to max lines
206
+ */
207
+ function truncateContent(content, maxLines) {
208
+ if (!content)
209
+ return '';
210
+ const lines = content.split('\n');
211
+ if (lines.length <= maxLines)
212
+ return content;
213
+ return lines.slice(0, maxLines).join('\n') + '\n// ...';
214
+ }
215
+ //# sourceMappingURL=semantic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic.js","sourceRoot":"","sources":["../../src/analyzers/semantic.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function run(): void;
package/dist/cli.js ADDED
@@ -0,0 +1,42 @@
1
+ import { Command } from 'commander';
2
+ import { initCommand } from './commands/init.js';
3
+ import { updateCommand } from './commands/update.js';
4
+ import { syncCommand } from './commands/sync.js';
5
+ export function run() {
6
+ const program = new Command();
7
+ program
8
+ .name('mustard')
9
+ .description('Framework-agnostic CLI for Claude Code project setup')
10
+ .version('2.0.0');
11
+ program
12
+ .command('init')
13
+ .description('Initialize .claude/ structure for the current project')
14
+ .option('-f, --force', 'Overwrite existing .claude/ directory')
15
+ .option('-y, --yes', 'Skip confirmation prompts')
16
+ .option('--no-ollama', 'Skip Ollama analysis (use template-based generation)')
17
+ .option('--no-grepai', 'Skip grepai semantic search')
18
+ .option('-v, --verbose', 'Show detailed output')
19
+ .action(initCommand);
20
+ program
21
+ .command('update')
22
+ .description('Update Mustard core files (preserves client customizations)')
23
+ .option('-f, --force', 'Skip backup and confirmation')
24
+ .option('--no-ollama', 'Skip Ollama analysis')
25
+ .option('--no-grepai', 'Skip grepai semantic search')
26
+ .option('-v, --verbose', 'Show detailed output')
27
+ .option('--include-claude-md', 'Also update CLAUDE.md (normally preserved)')
28
+ .action(updateCommand);
29
+ program
30
+ .command('sync')
31
+ .description('Sync prompts and context with current codebase state')
32
+ .option('--prompts', 'Only sync prompts')
33
+ .option('--context', 'Only sync context files')
34
+ .option('--registry', 'Only sync entity registry')
35
+ .option('--no-ollama', 'Skip Ollama analysis')
36
+ .option('--no-grepai', 'Skip grepai semantic search')
37
+ .option('-v, --verbose', 'Show detailed output')
38
+ .option('-f, --force', 'Skip confirmation')
39
+ .action(syncCommand);
40
+ program.parse();
41
+ }
42
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,UAAU,GAAG;IACjB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,SAAS,CAAC;SACf,WAAW,CAAC,sDAAsD,CAAC;SACnE,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,uDAAuD,CAAC;SACpE,MAAM,CAAC,aAAa,EAAE,uCAAuC,CAAC;SAC9D,MAAM,CAAC,WAAW,EAAE,2BAA2B,CAAC;SAChD,MAAM,CAAC,aAAa,EAAE,sDAAsD,CAAC;SAC7E,MAAM,CAAC,aAAa,EAAE,6BAA6B,CAAC;SACpD,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC;SAC/C,MAAM,CAAC,WAAW,CAAC,CAAC;IAEvB,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,6DAA6D,CAAC;SAC1E,MAAM,CAAC,aAAa,EAAE,8BAA8B,CAAC;SACrD,MAAM,CAAC,aAAa,EAAE,sBAAsB,CAAC;SAC7C,MAAM,CAAC,aAAa,EAAE,6BAA6B,CAAC;SACpD,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC;SAC/C,MAAM,CAAC,qBAAqB,EAAE,4CAA4C,CAAC;SAC3E,MAAM,CAAC,aAAa,CAAC,CAAC;IAEzB,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,sDAAsD,CAAC;SACnE,MAAM,CAAC,WAAW,EAAE,mBAAmB,CAAC;SACxC,MAAM,CAAC,WAAW,EAAE,yBAAyB,CAAC;SAC9C,MAAM,CAAC,YAAY,EAAE,2BAA2B,CAAC;SACjD,MAAM,CAAC,aAAa,EAAE,sBAAsB,CAAC;SAC7C,MAAM,CAAC,aAAa,EAAE,6BAA6B,CAAC;SACpD,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC;SAC/C,MAAM,CAAC,aAAa,EAAE,mBAAmB,CAAC;SAC1C,MAAM,CAAC,WAAW,CAAC,CAAC;IAEvB,OAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { InitOptions } from '../types.js';
2
+ /**
3
+ * Main init command
4
+ */
5
+ export declare function initCommand(options: InitOptions): Promise<void>;
@@ -0,0 +1,377 @@
1
+ import { existsSync } from 'fs';
2
+ import { rename, rm, cp } from 'fs/promises';
3
+ import { join, resolve } from 'path';
4
+ import chalk from 'chalk';
5
+ import ora from 'ora';
6
+ import inquirer from 'inquirer';
7
+ import { scanProject } from '../scanners/index.js';
8
+ import { scanCodeSamples } from '../scanners/samples.js';
9
+ import * as ollamaService from '../services/ollama.js';
10
+ import * as grepaiService from '../services/grepai.js';
11
+ import * as semanticAnalyzer from '../analyzers/semantic.js';
12
+ import * as llmAnalyzer from '../analyzers/llm.js';
13
+ import { generateAll } from '../generators/index.js';
14
+ /**
15
+ * Main init command
16
+ */
17
+ export async function initCommand(options) {
18
+ const projectPath = resolve(process.cwd());
19
+ console.log(chalk.bold('\n🌿 Mustard CLI v2.0\n'));
20
+ // Check for existing .claude directory
21
+ const claudePath = join(projectPath, '.claude');
22
+ const claudeMdPath = join(claudePath, 'CLAUDE.md');
23
+ let overwriteClaudeMd = true;
24
+ if (existsSync(claudePath) && !options.force) {
25
+ if (options.yes) {
26
+ // With --yes, preserve existing files (no backup, just update)
27
+ console.log(chalk.gray(' .claude/ exists - updating files...'));
28
+ }
29
+ else {
30
+ const handleExisting = await promptExistingClaude();
31
+ if (handleExisting === 'cancel') {
32
+ console.log(chalk.yellow('\n⚠️ Cancelled.\n'));
33
+ return;
34
+ }
35
+ if (handleExisting === 'backup') {
36
+ await backupExistingClaude(claudePath);
37
+ }
38
+ // 'overwrite' continues without backup
39
+ }
40
+ }
41
+ // Check if CLAUDE.md already exists and prompt user
42
+ if (existsSync(claudeMdPath) && !options.force) {
43
+ if (options.yes) {
44
+ // With --yes, default to NOT overwriting existing CLAUDE.md
45
+ overwriteClaudeMd = false;
46
+ console.log(chalk.yellow('⚠️ CLAUDE.md already exists - preserving existing file'));
47
+ }
48
+ else {
49
+ overwriteClaudeMd = await promptOverwriteClaudeMd();
50
+ if (!overwriteClaudeMd) {
51
+ console.log(chalk.gray(' CLAUDE.md will be preserved'));
52
+ }
53
+ }
54
+ }
55
+ // Check dependencies
56
+ const deps = await checkDependencies(options);
57
+ // Phase 1: Basic scan
58
+ const scanSpinner = ora('Scanning project...').start();
59
+ let projectInfo;
60
+ try {
61
+ projectInfo = await scanProject(projectPath, { verbose: options.verbose });
62
+ scanSpinner.succeed('Project scanned');
63
+ }
64
+ catch (error) {
65
+ scanSpinner.fail('Scan failed');
66
+ const message = error instanceof Error ? error.message : 'Unknown error';
67
+ console.error(chalk.red(message));
68
+ return;
69
+ }
70
+ // Display detected info
71
+ displayProjectInfo(projectInfo);
72
+ // Confirm with user
73
+ const confirmed = await promptConfirmation(projectInfo, options.yes);
74
+ if (!confirmed) {
75
+ console.log(chalk.yellow('\n⚠️ Cancelled.\n'));
76
+ return;
77
+ }
78
+ // Phase 2: Semantic analysis (if grepai available)
79
+ let patterns = { services: [], repositories: [], endpoints: [], components: [], hooks: [], entities: [], callGraph: null };
80
+ if (deps.grepai && options.grepai !== false) {
81
+ const semanticSpinner = ora('Analyzing codebase semantically (grepai)...').start();
82
+ try {
83
+ patterns = await semanticAnalyzer.discoverPatterns({
84
+ stacks: projectInfo.stacks,
85
+ verbose: options.verbose
86
+ });
87
+ // Discover entities
88
+ const entities = await semanticAnalyzer.discoverEntities(projectInfo.stacks);
89
+ patterns.entities = entities;
90
+ semanticSpinner.succeed(`Found ${patterns.entities.length} entities`);
91
+ }
92
+ catch (error) {
93
+ semanticSpinner.warn('Semantic analysis limited');
94
+ if (options.verbose) {
95
+ const message = error instanceof Error ? error.message : 'Unknown error';
96
+ console.log(chalk.gray(` ${message}`));
97
+ }
98
+ }
99
+ }
100
+ // Phase 3: LLM analysis (if Ollama available)
101
+ let analysis = {
102
+ architecture: projectInfo.structure?.architecture ?? { type: 'unknown', confidence: 'low' },
103
+ patterns: [],
104
+ rules: [],
105
+ entities: patterns.entities
106
+ };
107
+ // Get code samples for context generation
108
+ let codeSamples = {};
109
+ if (deps.grepai && options.grepai !== false) {
110
+ try {
111
+ codeSamples = await semanticAnalyzer.getCodeSamples(patterns);
112
+ }
113
+ catch {
114
+ // Ignore - samples are optional
115
+ }
116
+ }
117
+ // Fallback: scan filesystem for code samples if grepai didn't find any
118
+ if (Object.keys(codeSamples).length === 0) {
119
+ const sampleSpinner = ora('Scanning for code samples...').start();
120
+ try {
121
+ codeSamples = await scanCodeSamples(projectPath, projectInfo.stacks, { verbose: options.verbose });
122
+ const found = Object.keys(codeSamples).length;
123
+ if (found > 0) {
124
+ sampleSpinner.succeed(`Found ${found} code sample${found > 1 ? 's' : ''}`);
125
+ }
126
+ else {
127
+ sampleSpinner.warn('No code samples found');
128
+ }
129
+ }
130
+ catch {
131
+ sampleSpinner.warn('Code sample scan failed');
132
+ }
133
+ }
134
+ if (deps.ollama && options.ollama !== false) {
135
+ const llmSpinner = ora('Analyzing code patterns (Ollama)...').start();
136
+ try {
137
+ const samplesList = Object.values(codeSamples).filter((s) => Boolean(s));
138
+ if (samplesList.length > 0) {
139
+ const llmResult = await llmAnalyzer.analyzeCode(samplesList, {
140
+ model: deps.ollamaModel ?? undefined
141
+ });
142
+ analysis = { ...analysis, ...llmResult };
143
+ llmSpinner.succeed('Code patterns analyzed');
144
+ }
145
+ else {
146
+ llmSpinner.warn('No code samples for analysis');
147
+ }
148
+ }
149
+ catch (error) {
150
+ llmSpinner.warn('LLM analysis limited');
151
+ if (options.verbose) {
152
+ const message = error instanceof Error ? error.message : 'Unknown error';
153
+ console.log(chalk.gray(` ${message}`));
154
+ }
155
+ }
156
+ }
157
+ // Phase 4: Generate files
158
+ const genSpinner = ora('Generating .claude/ structure...').start();
159
+ try {
160
+ const files = await generateAll(projectPath, projectInfo, analysis, {
161
+ useOllama: deps.ollama && options.ollama !== false,
162
+ model: deps.ollamaModel ?? undefined,
163
+ hasGrepai: deps.grepai,
164
+ verbose: options.verbose,
165
+ overwriteClaudeMd,
166
+ codeSamples
167
+ });
168
+ genSpinner.succeed(`Generated ${files.length} files`);
169
+ // Display generated files
170
+ console.log(chalk.gray('\n Generated files:'));
171
+ const grouped = groupFiles(files);
172
+ for (const [dir, dirFiles] of Object.entries(grouped)) {
173
+ if (dir === '') {
174
+ for (const file of dirFiles) {
175
+ console.log(chalk.gray(` .claude/${file}`));
176
+ }
177
+ }
178
+ else {
179
+ console.log(chalk.gray(` .claude/${dir}/`));
180
+ for (const file of dirFiles) {
181
+ console.log(chalk.gray(` ${file}`));
182
+ }
183
+ }
184
+ }
185
+ }
186
+ catch (error) {
187
+ genSpinner.fail('Generation failed');
188
+ const message = error instanceof Error ? error.message : 'Unknown error';
189
+ console.error(chalk.red(message));
190
+ if (options.verbose && error instanceof Error) {
191
+ console.error(error.stack);
192
+ }
193
+ return;
194
+ }
195
+ // Done!
196
+ console.log(chalk.green.bold('\n✅ Done!\n'));
197
+ console.log(chalk.white('Use these commands to get started:'));
198
+ console.log(chalk.cyan(' /mtd-pipeline-feature <name>') + chalk.gray(' - Start a new feature'));
199
+ console.log(chalk.cyan(' /mtd-pipeline-bugfix <error>') + chalk.gray(' - Fix a bug'));
200
+ console.log(chalk.cyan(' /mtd-validate-status') + chalk.gray(' - Check project status'));
201
+ console.log();
202
+ }
203
+ /**
204
+ * Check available dependencies (Ollama, grepai)
205
+ */
206
+ async function checkDependencies(options) {
207
+ const deps = {
208
+ ollama: false,
209
+ ollamaModel: null,
210
+ grepai: false
211
+ };
212
+ // Check Ollama
213
+ if (options.ollama !== false) {
214
+ const ollamaSpinner = ora('Checking Ollama...').start();
215
+ const ollamaAvailable = await ollamaService.checkOllamaAvailable();
216
+ if (ollamaAvailable) {
217
+ const models = await ollamaService.getAvailableModels();
218
+ if (models.length > 0) {
219
+ deps.ollama = true;
220
+ deps.ollamaModel = await ollamaService.selectBestModel(models);
221
+ ollamaSpinner.succeed(`Ollama: ${deps.ollamaModel}`);
222
+ }
223
+ else {
224
+ ollamaSpinner.warn('Ollama running but no models');
225
+ }
226
+ }
227
+ else {
228
+ ollamaSpinner.warn('Ollama not available (using templates)');
229
+ }
230
+ }
231
+ // Check grepai
232
+ if (options.grepai !== false) {
233
+ const grepaiSpinner = ora('Checking grepai...').start();
234
+ const grepaiAvailable = await grepaiService.checkGrepaiAvailable();
235
+ if (grepaiAvailable) {
236
+ deps.grepai = true;
237
+ grepaiSpinner.succeed('grepai: available');
238
+ }
239
+ else {
240
+ grepaiSpinner.warn('grepai not available (limited semantic search)');
241
+ }
242
+ }
243
+ return deps;
244
+ }
245
+ /**
246
+ * Prompt user about existing .claude directory
247
+ */
248
+ async function promptExistingClaude() {
249
+ const { action } = await inquirer.prompt([
250
+ {
251
+ type: 'list',
252
+ name: 'action',
253
+ message: '.claude/ directory already exists. What would you like to do?',
254
+ choices: [
255
+ { name: 'Backup and overwrite', value: 'backup' },
256
+ { name: 'Overwrite without backup', value: 'overwrite' },
257
+ { name: 'Cancel', value: 'cancel' }
258
+ ]
259
+ }
260
+ ]);
261
+ return action;
262
+ }
263
+ /**
264
+ * Prompt user about overwriting existing CLAUDE.md
265
+ */
266
+ async function promptOverwriteClaudeMd() {
267
+ const { overwrite } = await inquirer.prompt([
268
+ {
269
+ type: 'confirm',
270
+ name: 'overwrite',
271
+ message: 'CLAUDE.md already exists. Do you want to overwrite it?',
272
+ default: false
273
+ }
274
+ ]);
275
+ return overwrite;
276
+ }
277
+ /**
278
+ * Backup existing .claude directory
279
+ */
280
+ async function backupExistingClaude(claudePath) {
281
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
282
+ const backupPath = `${claudePath}.backup.${timestamp}`;
283
+ const spinner = ora('Creating backup...').start();
284
+ try {
285
+ // Try rename first (faster)
286
+ await rename(claudePath, backupPath);
287
+ spinner.succeed(`Backup created: ${backupPath}`);
288
+ }
289
+ catch (error) {
290
+ // On Windows, rename can fail if files are locked
291
+ // Fall back to copy + delete
292
+ const err = error;
293
+ if (err.code === 'EPERM' || err.code === 'EBUSY') {
294
+ try {
295
+ await cp(claudePath, backupPath, { recursive: true });
296
+ await rm(claudePath, { recursive: true, force: true });
297
+ spinner.succeed(`Backup created: ${backupPath}`);
298
+ }
299
+ catch (copyError) {
300
+ spinner.fail('Backup failed');
301
+ throw copyError;
302
+ }
303
+ }
304
+ else {
305
+ spinner.fail('Backup failed');
306
+ throw error;
307
+ }
308
+ }
309
+ }
310
+ /**
311
+ * Display detected project info
312
+ */
313
+ function displayProjectInfo(projectInfo) {
314
+ console.log(chalk.bold('\n📋 Detected:\n'));
315
+ // Project type
316
+ console.log(chalk.white(` Type: ${chalk.cyan(projectInfo.type)}`));
317
+ // Stacks
318
+ console.log(chalk.white(' Stacks:'));
319
+ for (const stack of projectInfo.stacks) {
320
+ const version = stack.version ? ` ${stack.version}` : '';
321
+ const path = stack.path !== '.' ? ` (${stack.path})` : '';
322
+ console.log(chalk.gray(` • ${stack.name}${version}${path}`));
323
+ }
324
+ // Architecture
325
+ if (projectInfo.structure?.architecture?.type !== 'unknown') {
326
+ console.log(chalk.white(` Architecture: ${chalk.cyan(projectInfo.structure.architecture.type)}`));
327
+ }
328
+ // Package manager
329
+ if (projectInfo.packageManager) {
330
+ console.log(chalk.white(` Package manager: ${chalk.cyan(projectInfo.packageManager)}`));
331
+ }
332
+ // Naming patterns
333
+ if (projectInfo.patterns) {
334
+ console.log(chalk.white(' Naming:'));
335
+ console.log(chalk.gray(` • Classes: ${projectInfo.patterns.classes}`));
336
+ console.log(chalk.gray(` • Folders: ${projectInfo.patterns.folders}`));
337
+ }
338
+ console.log();
339
+ }
340
+ /**
341
+ * Prompt user for confirmation
342
+ */
343
+ async function promptConfirmation(projectInfo, skipPrompt = false) {
344
+ if (skipPrompt) {
345
+ return true;
346
+ }
347
+ const { confirmed } = await inquirer.prompt([
348
+ {
349
+ type: 'confirm',
350
+ name: 'confirmed',
351
+ message: 'Generate .claude/ structure with these settings?',
352
+ default: true
353
+ }
354
+ ]);
355
+ return confirmed;
356
+ }
357
+ /**
358
+ * Group files by directory for display
359
+ */
360
+ function groupFiles(files) {
361
+ const grouped = {};
362
+ for (const file of files) {
363
+ const parts = file.split('/');
364
+ if (parts.length === 1) {
365
+ grouped[''] = grouped[''] ?? [];
366
+ grouped[''].push(file);
367
+ }
368
+ else {
369
+ const dir = parts[0];
370
+ const name = parts.slice(1).join('/');
371
+ grouped[dir] = grouped[dir] ?? [];
372
+ grouped[dir].push(name);
373
+ }
374
+ }
375
+ return grouped;
376
+ }
377
+ //# sourceMappingURL=init.js.map