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.
- package/README.md +198 -0
- package/bin/mustard.js +5 -0
- package/dist/analyzers/llm.d.ts +13 -0
- package/dist/analyzers/llm.js +339 -0
- package/dist/analyzers/llm.js.map +1 -0
- package/dist/analyzers/semantic.d.ts +13 -0
- package/dist/analyzers/semantic.js +215 -0
- package/dist/analyzers/semantic.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +42 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +377 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/sync.d.ts +5 -0
- package/dist/commands/sync.js +235 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/update.d.ts +8 -0
- package/dist/commands/update.js +237 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/generators/claude-md-llm.d.ts +5 -0
- package/dist/generators/claude-md-llm.js +101 -0
- package/dist/generators/claude-md-llm.js.map +1 -0
- package/dist/generators/claude-md-template.d.ts +5 -0
- package/dist/generators/claude-md-template.js +273 -0
- package/dist/generators/claude-md-template.js.map +1 -0
- package/dist/generators/commands.d.ts +17 -0
- package/dist/generators/commands.js +845 -0
- package/dist/generators/commands.js.map +1 -0
- package/dist/generators/context.d.ts +11 -0
- package/dist/generators/context.js +621 -0
- package/dist/generators/context.js.map +1 -0
- package/dist/generators/hooks.d.ts +5 -0
- package/dist/generators/hooks.js +128 -0
- package/dist/generators/hooks.js.map +1 -0
- package/dist/generators/index.d.ts +11 -0
- package/dist/generators/index.js +541 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/prompts.d.ts +13 -0
- package/dist/generators/prompts.js +579 -0
- package/dist/generators/prompts.js.map +1 -0
- package/dist/generators/registry.d.ts +5 -0
- package/dist/generators/registry.js +93 -0
- package/dist/generators/registry.js.map +1 -0
- package/dist/scanners/dependencies.d.ts +7 -0
- package/dist/scanners/dependencies.js +195 -0
- package/dist/scanners/dependencies.js.map +1 -0
- package/dist/scanners/index.d.ts +6 -0
- package/dist/scanners/index.js +37 -0
- package/dist/scanners/index.js.map +1 -0
- package/dist/scanners/samples.d.ts +8 -0
- package/dist/scanners/samples.js +193 -0
- package/dist/scanners/samples.js.map +1 -0
- package/dist/scanners/stack.d.ts +5 -0
- package/dist/scanners/stack.js +294 -0
- package/dist/scanners/stack.js.map +1 -0
- package/dist/scanners/structure.d.ts +5 -0
- package/dist/scanners/structure.js +274 -0
- package/dist/scanners/structure.js.map +1 -0
- package/dist/services/grepai.d.ts +25 -0
- package/dist/services/grepai.js +89 -0
- package/dist/services/grepai.js.map +1 -0
- package/dist/services/ollama.d.ts +22 -0
- package/dist/services/ollama.js +86 -0
- package/dist/services/ollama.js.map +1 -0
- package/dist/services/package-manager.d.ts +95 -0
- package/dist/services/package-manager.js +164 -0
- package/dist/services/package-manager.js.map +1 -0
- package/dist/types.d.ts +233 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- 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":"AAAA,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAC;AAGhD;;GAEG;AACH,MAAM,cAAc,GAAG;IACrB,YAAY,EAAE;QACZ,6CAA6C;QAC7C,gCAAgC;QAChC,4BAA4B;QAC5B,0BAA0B;KAC3B;IACD,QAAQ,EAAE;QACR,kCAAkC;QAClC,kCAAkC;QAClC,6BAA6B;QAC7B,4BAA4B;KAC7B;IACD,QAAQ,EAAE;QACR,kCAAkC;QAClC,6BAA6B;QAC7B,6BAA6B;KAC9B;IACD,QAAQ,EAAE;QACR,sCAAsC;QACtC,2BAA2B;QAC3B,gCAAgC;QAChC,mCAAmC;KACpC;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,UAA2B,EAAE;IAClE,MAAM,EAAE,OAAO,GAAG,KAAK,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAEjD,MAAM,OAAO,GAAuB;QAClC,QAAQ,EAAE,EAAE;QACZ,YAAY,EAAE,EAAE;QAChB,SAAS,EAAE,EAAE;QACb,UAAU,EAAE,EAAE;QACd,KAAK,EAAE,EAAE;QACT,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,IAAI;KAChB,CAAC;IAEF,mBAAmB;IACnB,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACjG,IAAI,UAAU,EAAE,CAAC;QACf,sBAAsB;QACtB,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,6CAA6C,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACxG,OAAO,CAAC,QAAQ,GAAG,cAAc,CAAC,OAAO,IAAI,EAAE,CAAC;QAEhD,0BAA0B;QAC1B,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,gCAAgC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACxF,OAAO,CAAC,YAAY,GAAG,WAAW,CAAC,OAAO,IAAI,EAAE,CAAC;QAEjD,uBAAuB;QACvB,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACxF,OAAO,CAAC,SAAS,GAAG,eAAe,CAAC,OAAO,IAAI,EAAE,CAAC;QAElD,sBAAsB;QACtB,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,wCAAwC,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QACnG,OAAO,CAAC,QAAQ,GAAG,CAAC,aAAa,CAAC,OAAO,IAAI,EAAE,CAAC;aAC7C,GAAG,CAAC,CAAC,CAAC,EAAE;YACP,MAAM,IAAI,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC;YAClC,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YACvB,OAAO;gBACL,IAAI;gBACJ,IAAI,EAAE,CAAC,CAAC,SAAS;gBACjB,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;aACzB,CAAC;QACJ,CAAC,CAAC;aACD,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,oBAAoB;IACpB,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7F,IAAI,WAAW,EAAE,CAAC;QAChB,wBAAwB;QACxB,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,sCAAsC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACnG,OAAO,CAAC,UAAU,GAAG,gBAAgB,CAAC,OAAO,IAAI,EAAE,CAAC;QAEpD,mBAAmB;QACnB,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,oCAAoC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QAC5F,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC,OAAO,IAAI,EAAE,CAAC;IAC5C,CAAC;IAED,6CAA6C;IAC7C,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,MAAM,WAAW,GAAG,iBAAiB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3D,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,CAAC,SAAS,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,SAAkB,EAAE;IACzD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,gCAAgC;IAChC,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,sCAAsC,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IAEjG,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;QAC1B,KAAK,MAAM,MAAM,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;YACvC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;gBACjD,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI;oBACJ,IAAI,EAAE,MAAM,CAAC,SAAS;oBACtB,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC;iBAC9B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,QAA4B,EAAE,UAA6B,EAAE;IAChG,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAClC,MAAM,OAAO,GAAgB,EAAE,CAAC;IAEhC,qBAAqB;IACrB,IAAI,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9E,OAAO,CAAC,OAAO,GAAG;YAChB,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;YACpC,OAAO,EAAE,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC;YAChE,IAAI,EAAE,SAAS;SAChB,CAAC;IACJ,CAAC;IAED,sBAAsB;IACtB,IAAI,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QACjF,OAAO,CAAC,QAAQ,GAAG;YACjB,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;YACrC,OAAO,EAAE,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC;YACjE,IAAI,EAAE,UAAU;SACjB,CAAC;IACJ,CAAC;IAED,kBAAkB;IAClB,IAAI,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,OAAO,CAAC,IAAI,GAAG;YACb,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;YACjC,OAAO,EAAE,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC;YAC7D,IAAI,EAAE,MAAM;SACb,CAAC;IACJ,CAAC;IAED,uBAAuB;IACvB,IAAI,QAAQ,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QACpF,OAAO,CAAC,SAAS,GAAG;YAClB,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;YACtC,OAAO,EAAE,eAAe,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC;YAClE,IAAI,EAAE,WAAW;SAClB,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,MAAoB;IAC7C,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE5C,4BAA4B;IAC5B,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACzD,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;IAE1C,+BAA+B;IAC/B,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;IAC7E,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC;IAExC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,MAAoB;IAC7C,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE5C,cAAc;IACd,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACxE,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;IAE1C,iBAAiB;IACjB,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;IACxE,IAAI,YAAY,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;IAE9C,eAAe;IACf,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC/D,IAAI,WAAW,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC;IAE5C,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,MAAoB;IAC3C,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC;IAEnD,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;IAE5C,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1E,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC1E,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IACtE,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAE7C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,OAAe,EAAE,QAAgB;IACxD,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,OAAO,CAAC;IAC7C,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC;AAC1D,CAAC"}
|
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
|
package/dist/cli.js.map
ADDED
|
@@ -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,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
|