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,128 @@
1
+ /**
2
+ * Generate hook files
3
+ */
4
+ export function generateHooks(projectInfo, options = {}) {
5
+ const hooks = {
6
+ // Pipeline enforcement hook (always)
7
+ 'enforce-pipeline.js': generateEnforcePipelineHook()
8
+ };
9
+ // grepai enforcement hook (if grepai available)
10
+ if (options.hasGrepai !== false) {
11
+ hooks['enforce-grepai.js'] = generateEnforceGrepaiHook();
12
+ }
13
+ return hooks;
14
+ }
15
+ function generateEnforcePipelineHook() {
16
+ return `/**
17
+ * Hook: enforce-pipeline
18
+ *
19
+ * Enforces that code changes go through the proper pipeline.
20
+ * Triggers on Edit/Write to code files.
21
+ *
22
+ * Exceptions:
23
+ * - .md files (documentation)
24
+ * - .json files (config)
25
+ * - .yaml/.yml files (config)
26
+ * - Files in .claude/, mustard/, spec/ directories
27
+ */
28
+
29
+ export default {
30
+ name: 'enforce-pipeline',
31
+
32
+ // Hook configuration
33
+ hooks: {
34
+ preToolCall: {
35
+ tools: ['Edit', 'Write'],
36
+ handler: 'checkPipeline'
37
+ }
38
+ },
39
+
40
+ /**
41
+ * Check if there's an active pipeline before allowing code edits
42
+ */
43
+ checkPipeline(context) {
44
+ const { toolName, parameters } = context;
45
+ const filePath = parameters.file_path || '';
46
+
47
+ // Skip non-code files
48
+ if (isExemptFile(filePath)) {
49
+ return { allowed: true };
50
+ }
51
+
52
+ // Check for active pipeline
53
+ // Note: This is a reminder hook - actual enforcement is done by Claude
54
+ // following the CLAUDE.md instructions
55
+
56
+ return {
57
+ allowed: true,
58
+ message: \`📋 REMINDER: Ensure you're following the pipeline for code changes.
59
+ Use /mtd-pipeline-feature or /mtd-pipeline-bugfix to start a proper pipeline.\`
60
+ };
61
+ }
62
+ };
63
+
64
+ function isExemptFile(filePath) {
65
+ const exemptExtensions = ['.md', '.json', '.yaml', '.yml', '.txt', '.env.example'];
66
+ const exemptDirs = ['.claude', 'mustard', 'spec', 'node_modules', 'bin', 'obj'];
67
+
68
+ // Check extension
69
+ if (exemptExtensions.some(ext => filePath.endsWith(ext))) {
70
+ return true;
71
+ }
72
+
73
+ // Check directory
74
+ if (exemptDirs.some(dir => filePath.includes(\`/\${dir}/\`) || filePath.includes(\`\\\\\${dir}\\\\\`))) {
75
+ return true;
76
+ }
77
+
78
+ return false;
79
+ }
80
+ `;
81
+ }
82
+ function generateEnforceGrepaiHook() {
83
+ return `/**
84
+ * Hook: enforce-grepai
85
+ *
86
+ * Encourages use of grepai for semantic code search.
87
+ * Triggers on Grep/Glob tools.
88
+ *
89
+ * Note: This is an advisory hook, not a blocker.
90
+ */
91
+
92
+ export default {
93
+ name: 'enforce-grepai',
94
+
95
+ // Hook configuration
96
+ hooks: {
97
+ preToolCall: {
98
+ tools: ['Grep', 'Glob'],
99
+ handler: 'suggestGrepai'
100
+ }
101
+ },
102
+
103
+ /**
104
+ * Suggest using grepai for better semantic search
105
+ */
106
+ suggestGrepai(context) {
107
+ const { toolName, parameters } = context;
108
+
109
+ // Allow but remind
110
+ return {
111
+ allowed: true,
112
+ message: \`💡 SUGGESTION: Consider using grepai for semantic search.
113
+
114
+ grepai provides:
115
+ - Semantic understanding of code intent
116
+ - Better results for complex queries
117
+ - Call graph tracing
118
+
119
+ Example:
120
+ grepai_search({ query: "your search" })
121
+ grepai_trace_callers({ symbol: "FunctionName" })
122
+ \`
123
+ };
124
+ }
125
+ };
126
+ `;
127
+ }
128
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/generators/hooks.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,WAAwB,EAAE,UAA4B,EAAE;IACpF,MAAM,KAAK,GAAmB;QAC5B,qCAAqC;QACrC,qBAAqB,EAAE,2BAA2B,EAAE;KACrD,CAAC;IAEF,gDAAgD;IAChD,IAAI,OAAO,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;QAChC,KAAK,CAAC,mBAAmB,CAAC,GAAG,yBAAyB,EAAE,CAAC;IAC3D,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,2BAA2B;IAClC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgER,CAAC;AACF,CAAC;AAED,SAAS,yBAAyB;IAChC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CR,CAAC;AACF,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ProjectInfo, Analysis, GeneratorOptions } from '../types.js';
2
+ /**
3
+ * Main generator orchestrator
4
+ */
5
+ export declare function generateAll(projectPath: string, projectInfo: ProjectInfo, analysis: Analysis, options?: GeneratorOptions): Promise<string[]>;
6
+ /**
7
+ * Generate only core files (for update command)
8
+ * Preserves: CLAUDE.md, context/*.md (except README), docs/*
9
+ * Updates: commands/, prompts/, hooks/, core/, scripts/, settings.json, entity-registry.json
10
+ */
11
+ export declare function generateCoreOnly(projectPath: string, projectInfo: ProjectInfo, analysis: Analysis, options?: GeneratorOptions): Promise<string[]>;
@@ -0,0 +1,541 @@
1
+ import { existsSync } from 'fs';
2
+ import { mkdir, writeFile, copyFile, readFile } from 'fs/promises';
3
+ import { join, dirname } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { generateClaudeMd as generateClaudeMdLLM } from './claude-md-llm.js';
6
+ import { generateClaudeMd as generateClaudeMdTemplate } from './claude-md-template.js';
7
+ import { generatePrompts } from './prompts.js';
8
+ import { generateCommands, MUSTARD_COMMANDS_FOLDER } from './commands.js';
9
+ import { generateHooks } from './hooks.js';
10
+ import { generateRegistry } from './registry.js';
11
+ import { generateContext } from './context.js';
12
+ /**
13
+ * Deep merge two objects, with source taking priority
14
+ */
15
+ function deepMerge(target, source) {
16
+ const result = { ...target };
17
+ for (const key of Object.keys(source)) {
18
+ const sourceValue = source[key];
19
+ const targetValue = target[key];
20
+ if (sourceValue !== null &&
21
+ typeof sourceValue === 'object' &&
22
+ !Array.isArray(sourceValue) &&
23
+ targetValue !== null &&
24
+ typeof targetValue === 'object' &&
25
+ !Array.isArray(targetValue)) {
26
+ result[key] = deepMerge(targetValue, sourceValue);
27
+ }
28
+ else if (sourceValue !== undefined) {
29
+ result[key] = sourceValue;
30
+ }
31
+ }
32
+ return result;
33
+ }
34
+ /**
35
+ * Main generator orchestrator
36
+ */
37
+ export async function generateAll(projectPath, projectInfo, analysis, options = {}) {
38
+ const { useOllama = true, model, verbose = false, overwriteClaudeMd = true, codeSamples = {} } = options;
39
+ const log = (msg) => { if (verbose)
40
+ console.log(` ${msg}`); };
41
+ const claudePath = join(projectPath, '.claude');
42
+ const generatedFiles = [];
43
+ // Create .claude directory structure
44
+ await mkdir(join(claudePath, 'prompts'), { recursive: true });
45
+ await mkdir(join(claudePath, 'commands', MUSTARD_COMMANDS_FOLDER), { recursive: true });
46
+ await mkdir(join(claudePath, 'hooks'), { recursive: true });
47
+ await mkdir(join(claudePath, 'core'), { recursive: true });
48
+ await mkdir(join(claudePath, 'docs'), { recursive: true });
49
+ await mkdir(join(claudePath, 'context'), { recursive: true });
50
+ // Generate CLAUDE.md (only if allowed or doesn't exist)
51
+ const claudeMdPath = join(claudePath, 'CLAUDE.md');
52
+ const claudeMdExists = existsSync(claudeMdPath);
53
+ log('Generating CLAUDE.md...');
54
+ if (!claudeMdExists || overwriteClaudeMd) {
55
+ let claudeMdContent = null;
56
+ if (useOllama) {
57
+ claudeMdContent = await generateClaudeMdLLM(projectInfo, analysis, { model, verbose });
58
+ }
59
+ if (!claudeMdContent) {
60
+ claudeMdContent = generateClaudeMdTemplate(projectInfo, analysis);
61
+ }
62
+ await writeFile(claudeMdPath, claudeMdContent);
63
+ generatedFiles.push('CLAUDE.md');
64
+ }
65
+ // Generate prompts
66
+ log('Generating prompts (may use Ollama)...');
67
+ const prompts = await generatePrompts(projectInfo, analysis, { useOllama, model });
68
+ for (const [name, content] of Object.entries(prompts)) {
69
+ await writeFile(join(claudePath, 'prompts', `${name}.md`), content);
70
+ generatedFiles.push(`prompts/${name}.md`);
71
+ }
72
+ // Generate prompts index
73
+ const promptsIndex = generatePromptsIndex(Object.keys(prompts));
74
+ await writeFile(join(claudePath, 'prompts', '_index.md'), promptsIndex);
75
+ generatedFiles.push('prompts/_index.md');
76
+ // Generate commands (in mustard/ subfolder)
77
+ log('Generating commands...');
78
+ const commands = generateCommands(projectInfo);
79
+ for (const [name, content] of Object.entries(commands)) {
80
+ await writeFile(join(claudePath, 'commands', MUSTARD_COMMANDS_FOLDER, `${name}.md`), content);
81
+ generatedFiles.push(`commands/${MUSTARD_COMMANDS_FOLDER}/${name}.md`);
82
+ }
83
+ // Generate hooks
84
+ log('Generating hooks...');
85
+ const hooks = generateHooks(projectInfo, options);
86
+ for (const [name, content] of Object.entries(hooks)) {
87
+ await writeFile(join(claudePath, 'hooks', name), content);
88
+ generatedFiles.push(`hooks/${name}`);
89
+ }
90
+ // Generate entity registry
91
+ log('Generating entity registry...');
92
+ const registry = generateRegistry(projectInfo, analysis);
93
+ await writeFile(join(claudePath, 'entity-registry.json'), JSON.stringify(registry, null, 2));
94
+ generatedFiles.push('entity-registry.json');
95
+ // Generate core files (enforcement, pipeline - NOT naming-conventions)
96
+ log('Generating core files...');
97
+ await generateCoreFiles(claudePath, projectInfo);
98
+ generatedFiles.push('core/enforcement.md', 'core/pipeline.md');
99
+ // Generate context folder with README
100
+ await generateContextFolder(claudePath);
101
+ generatedFiles.push('context/README.md');
102
+ // Copy settings.json from template
103
+ log('Copying settings.json...');
104
+ await copySettingsJson(claudePath);
105
+ generatedFiles.push('settings.json');
106
+ // Copy scripts folder
107
+ log('Copying scripts...');
108
+ await copyScripts(claudePath);
109
+ generatedFiles.push('scripts/statusline.js');
110
+ // Generate auto-populated context files (architecture.md, patterns.md, naming.md)
111
+ log('Generating context files (may use Ollama)...');
112
+ const contextFiles = await generateContext(claudePath, projectInfo, analysis, codeSamples, {
113
+ useOllama,
114
+ model,
115
+ verbose
116
+ });
117
+ generatedFiles.push(...contextFiles);
118
+ return generatedFiles;
119
+ }
120
+ /**
121
+ * Generate prompts index file
122
+ */
123
+ function generatePromptsIndex(promptNames) {
124
+ return `# Prompts Index
125
+
126
+ This directory contains specialized prompts for agents.
127
+
128
+ ## Available Prompts
129
+
130
+ ${promptNames.map(name => `- [${name}.md](./${name}.md)`).join('\n')}
131
+
132
+ ## How to Use
133
+
134
+ Prompts are automatically loaded by the pipeline when needed.
135
+ To delegate tasks, use:
136
+
137
+ \`\`\`javascript
138
+ Task({
139
+ subagent_type: "general-purpose",
140
+ model: "opus",
141
+ prompt: \`
142
+ [appropriate prompt content]
143
+
144
+ # TASK
145
+ [task description]
146
+ \`
147
+ })
148
+ \`\`\`
149
+ `;
150
+ }
151
+ /**
152
+ * Generate context folder with README
153
+ */
154
+ async function generateContextFolder(claudePath) {
155
+ const contextReadme = `# Project Context
156
+
157
+ Place markdown files here to provide context to Claude during implementations.
158
+
159
+ ## Purpose
160
+
161
+ Files in this folder are loaded into memory MCP at the start of \`/mtd-pipeline-feature\` or \`/mtd-pipeline-bugfix\` pipelines.
162
+ This gives Claude instant access to project specifications, architecture decisions, and patterns.
163
+
164
+ ## Supported Files
165
+
166
+ Any \`.md\` file placed in this folder will be automatically loaded.
167
+
168
+ **Suggested files:**
169
+ - \`project-spec.md\` - Project overview and specifications
170
+ - \`architecture.md\` - Architecture decisions and patterns
171
+ - \`business-rules.md\` - Domain-specific rules and logic
172
+ - \`api-guidelines.md\` - API design guidelines
173
+ - \`tips.md\` - Project-specific tips for Claude
174
+ - \`service-example.md\` - Code example for services
175
+ - \`component-example.md\` - Code example for components
176
+
177
+ ## Rules
178
+
179
+ 1. **Markdown only** - Only \`.md\` files are loaded
180
+ 2. **Keep files focused** - One topic per file
181
+ 3. **Use headers** - Claude uses headers to understand structure
182
+ 4. **Max 500 lines** - Longer files are truncated
183
+ 5. **Max 20 files** - Total limit for loaded files
184
+
185
+ ## How It Works
186
+
187
+ Files are automatically loaded at the start of \`/mtd-pipeline-feature\` or \`/mtd-pipeline-bugfix\` pipelines.
188
+ Each file is stored as a \`UserContext:{filename}\` entity in memory MCP.
189
+
190
+ ## Example: architecture.md
191
+
192
+ \`\`\`markdown
193
+ # Architecture
194
+
195
+ ## Layers
196
+ - Database: Drizzle ORM with PostgreSQL
197
+ - Backend: .NET 9 with FastEndpoints
198
+ - Frontend: React 19 with TanStack Query
199
+
200
+ ## Patterns
201
+ - Repository pattern for data access
202
+ - Services for business logic
203
+ - DTOs for API contracts
204
+ \`\`\`
205
+
206
+ ## Manual Refresh
207
+
208
+ To force a context refresh, use:
209
+
210
+ \`\`\`
211
+ /mtd-sync-context --refresh
212
+ \`\`\`
213
+
214
+ ## See Also
215
+
216
+ - [/mtd-sync-context](../commands/mustard/mtd-sync-context.md) - Manual context loading
217
+ - [/mtd-pipeline-feature](../commands/mustard/mtd-pipeline-feature.md) - Feature pipeline
218
+ - [pipeline.md](../core/pipeline.md) - Pipeline documentation
219
+ `;
220
+ await writeFile(join(claudePath, 'context', 'README.md'), contextReadme);
221
+ // Create .gitkeep for empty folder preservation
222
+ await writeFile(join(claudePath, 'context', '.gitkeep'), '');
223
+ }
224
+ /**
225
+ * Generate core documentation files
226
+ * Note: naming-conventions moved to prompts/naming.md
227
+ */
228
+ async function generateCoreFiles(claudePath, projectInfo) {
229
+ // Enforcement rules (stack-agnostic)
230
+ const enforcement = `# Enforcement Rules
231
+
232
+ > Mustard v3.0 (stack-agnostic)
233
+
234
+ ## Enforcement Levels
235
+
236
+ | Level | Rule | Description | Details |
237
+ |-------|------|-------------|---------|
238
+ | L0 | Delegation | Main Claude does NOT implement code | This file |
239
+ | L1 | grepai | Prefer grepai for semantic search | This file |
240
+ | L2 | Pipeline | Pipeline required for features/bugs | This file |
241
+ | L3 | Naming | Follow naming conventions | \`prompts/naming.md\` |
242
+ | L4 | Validation | Code must pass static validation | \`prompts/review.md\` |
243
+ | L5 | Build | Project must compile | \`prompts/review.md\` |
244
+ | L6 | Registry | Sync registry after creating entities | This file |
245
+
246
+ ## Details
247
+
248
+ ### L0 - Delegation
249
+ Main Claude coordinates but does not implement. Always delegates via Task tool.
250
+
251
+ ### L1 - grepai
252
+ Use grepai for semantic search instead of Grep/Glob when possible.
253
+
254
+ ### L2 - Pipeline
255
+ Features and bugfixes must follow the pipeline: Explore -> Spec -> Implement -> Review.
256
+
257
+ ### L3 - Naming
258
+ Follow naming conventions defined in [prompts/naming.md](../prompts/naming.md).
259
+
260
+ ### L4/L5 - Validation & Build
261
+ Validation and build commands depend on the project stack. See [prompts/review.md](../prompts/review.md).
262
+
263
+ ### L6 - Registry
264
+ After creating/modifying entities, run \`/mtd-sync-registry\`.
265
+ `;
266
+ await writeFile(join(claudePath, 'core', 'enforcement.md'), enforcement);
267
+ // Pipeline documentation
268
+ const pipeline = `# Development Pipeline
269
+
270
+ ## Flow
271
+
272
+ \`\`\`
273
+ /mtd-pipeline-feature or /mtd-pipeline-bugfix
274
+
275
+
276
+ EXPLORE (analysis)
277
+
278
+
279
+ SPEC (approval)
280
+
281
+
282
+ IMPLEMENT
283
+ (delegation)
284
+
285
+
286
+ REVIEW
287
+
288
+
289
+ COMPLETED
290
+ \`\`\`
291
+
292
+ ## Commands
293
+
294
+ | Command | Description |
295
+ |---------|-------------|
296
+ | /mtd-pipeline-feature <name> | Starts feature pipeline |
297
+ | /mtd-pipeline-bugfix <error> | Starts bugfix pipeline |
298
+ | /mtd-pipeline-approve | Approves spec for implementation |
299
+ | /mtd-pipeline-complete | Finalizes pipeline |
300
+ | /mtd-pipeline-resume | Resumes active pipeline |
301
+ `;
302
+ await writeFile(join(claudePath, 'core', 'pipeline.md'), pipeline);
303
+ }
304
+ /**
305
+ * Copy settings.json from template to target .claude directory
306
+ */
307
+ async function copySettingsJson(claudePath) {
308
+ const __filename = fileURLToPath(import.meta.url);
309
+ const __dirname = dirname(__filename);
310
+ const templatePath = join(__dirname, '..', '..', '..', 'claude', 'settings.json');
311
+ const targetPath = join(claudePath, 'settings.json');
312
+ if (existsSync(templatePath)) {
313
+ await copyFile(templatePath, targetPath);
314
+ }
315
+ else {
316
+ // Fallback: generate minimal settings.json
317
+ const minimalSettings = {
318
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
319
+ "hooks": {
320
+ "PreToolUse": [
321
+ {
322
+ "matcher": "Edit|Write",
323
+ "hooks": [
324
+ {
325
+ "type": "command",
326
+ "command": "node .claude/hooks/enforce-pipeline.js",
327
+ "timeout": 5
328
+ }
329
+ ]
330
+ }
331
+ ]
332
+ }
333
+ };
334
+ await writeFile(targetPath, JSON.stringify(minimalSettings, null, 2));
335
+ }
336
+ }
337
+ /**
338
+ * Copy scripts folder from template to target .claude directory
339
+ */
340
+ async function copyScripts(claudePath) {
341
+ const __filename = fileURLToPath(import.meta.url);
342
+ const __dirname = dirname(__filename);
343
+ const templateDir = join(__dirname, '..', '..', '..', 'claude', 'scripts');
344
+ const targetDir = join(claudePath, 'scripts');
345
+ await mkdir(targetDir, { recursive: true });
346
+ // Copy statusline.js
347
+ const statuslinePath = join(templateDir, 'statusline.js');
348
+ if (existsSync(statuslinePath)) {
349
+ await copyFile(statuslinePath, join(targetDir, 'statusline.js'));
350
+ }
351
+ }
352
+ /**
353
+ * Merge settings.json - preserves client customizations while updating core structure
354
+ */
355
+ async function mergeSettingsJson(claudePath) {
356
+ const __filename = fileURLToPath(import.meta.url);
357
+ const __dirname = dirname(__filename);
358
+ const templatePath = join(__dirname, '..', '..', '..', 'claude', 'settings.json');
359
+ const targetPath = join(claudePath, 'settings.json');
360
+ // Get template settings
361
+ let templateSettings = {};
362
+ if (existsSync(templatePath)) {
363
+ const content = await readFile(templatePath, 'utf-8');
364
+ templateSettings = JSON.parse(content);
365
+ }
366
+ else {
367
+ templateSettings = {
368
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
369
+ "hooks": {
370
+ "PreToolUse": [
371
+ {
372
+ "matcher": "Edit|Write",
373
+ "hooks": [
374
+ {
375
+ "type": "command",
376
+ "command": "node .claude/hooks/enforce-pipeline.js",
377
+ "timeout": 5
378
+ }
379
+ ]
380
+ }
381
+ ]
382
+ }
383
+ };
384
+ }
385
+ // Get existing settings if any
386
+ let existingSettings = {};
387
+ if (existsSync(targetPath)) {
388
+ try {
389
+ const content = await readFile(targetPath, 'utf-8');
390
+ existingSettings = JSON.parse(content);
391
+ }
392
+ catch {
393
+ // Invalid JSON, use template
394
+ }
395
+ }
396
+ // Merge: template as base, existing takes priority
397
+ const merged = deepMerge(templateSettings, existingSettings);
398
+ await writeFile(targetPath, JSON.stringify(merged, null, 2));
399
+ }
400
+ /**
401
+ * Generate only core files (for update command)
402
+ * Preserves: CLAUDE.md, context/*.md (except README), docs/*
403
+ * Updates: commands/, prompts/, hooks/, core/, scripts/, settings.json, entity-registry.json
404
+ */
405
+ export async function generateCoreOnly(projectPath, projectInfo, analysis, options = {}) {
406
+ const { useOllama = false, model, verbose = false, overwriteClaudeMd = false } = options;
407
+ const log = (msg) => { if (verbose)
408
+ console.log(` ${msg}`); };
409
+ const claudePath = join(projectPath, '.claude');
410
+ const generatedFiles = [];
411
+ // Ensure directory structure exists
412
+ await mkdir(join(claudePath, 'commands', MUSTARD_COMMANDS_FOLDER), { recursive: true });
413
+ await mkdir(join(claudePath, 'hooks'), { recursive: true });
414
+ await mkdir(join(claudePath, 'core'), { recursive: true });
415
+ await mkdir(join(claudePath, 'scripts'), { recursive: true });
416
+ // Generate CLAUDE.md only if explicitly requested
417
+ if (overwriteClaudeMd) {
418
+ log('Generating CLAUDE.md...');
419
+ const claudeMdPath = join(claudePath, 'CLAUDE.md');
420
+ let claudeMdContent = null;
421
+ if (useOllama) {
422
+ claudeMdContent = await generateClaudeMdLLM(projectInfo, analysis, { model, verbose });
423
+ }
424
+ if (!claudeMdContent) {
425
+ claudeMdContent = generateClaudeMdTemplate(projectInfo, analysis);
426
+ }
427
+ await writeFile(claudeMdPath, claudeMdContent);
428
+ generatedFiles.push('CLAUDE.md');
429
+ }
430
+ // NOTE: Prompts are NOT regenerated during update - only during init
431
+ // This preserves user customizations to prompt files
432
+ // Generate commands (in mustard/ subfolder - user commands in commands/ are preserved)
433
+ log('Generating commands...');
434
+ const commands = generateCommands(projectInfo);
435
+ for (const [name, content] of Object.entries(commands)) {
436
+ await writeFile(join(claudePath, 'commands', MUSTARD_COMMANDS_FOLDER, `${name}.md`), content);
437
+ generatedFiles.push(`commands/${MUSTARD_COMMANDS_FOLDER}/${name}.md`);
438
+ }
439
+ // Generate hooks
440
+ log('Generating hooks...');
441
+ const hooks = generateHooks(projectInfo, options);
442
+ for (const [name, content] of Object.entries(hooks)) {
443
+ await writeFile(join(claudePath, 'hooks', name), content);
444
+ generatedFiles.push(`hooks/${name}`);
445
+ }
446
+ // Generate entity registry
447
+ log('Generating entity registry...');
448
+ const registry = generateRegistry(projectInfo, analysis);
449
+ await writeFile(join(claudePath, 'entity-registry.json'), JSON.stringify(registry, null, 2));
450
+ generatedFiles.push('entity-registry.json');
451
+ // Generate core files (enforcement, pipeline - NOT naming-conventions)
452
+ log('Generating core files...');
453
+ await generateCoreFiles(claudePath, projectInfo);
454
+ generatedFiles.push('core/enforcement.md', 'core/pipeline.md');
455
+ // Update context/README.md only (preserve other context files)
456
+ log('Updating context/README.md...');
457
+ await mkdir(join(claudePath, 'context'), { recursive: true });
458
+ await generateContextReadme(claudePath);
459
+ generatedFiles.push('context/README.md');
460
+ // Merge settings.json (preserve client hooks)
461
+ log('Merging settings.json...');
462
+ await mergeSettingsJson(claudePath);
463
+ generatedFiles.push('settings.json');
464
+ // Copy scripts
465
+ log('Copying scripts...');
466
+ await copyScripts(claudePath);
467
+ generatedFiles.push('scripts/statusline.js');
468
+ return generatedFiles;
469
+ }
470
+ /**
471
+ * Generate only context/README.md (for update command)
472
+ */
473
+ async function generateContextReadme(claudePath) {
474
+ const contextReadme = `# Project Context
475
+
476
+ Place markdown files here to provide context to Claude during implementations.
477
+
478
+ ## Purpose
479
+
480
+ Files in this folder are loaded into memory MCP at the start of \`/mtd-pipeline-feature\` or \`/mtd-pipeline-bugfix\` pipelines.
481
+ This gives Claude instant access to project specifications, architecture decisions, and patterns.
482
+
483
+ ## Supported Files
484
+
485
+ Any \`.md\` file placed in this folder will be automatically loaded.
486
+
487
+ **Suggested files:**
488
+ - \`project-spec.md\` - Project overview and specifications
489
+ - \`architecture.md\` - Architecture decisions and patterns
490
+ - \`business-rules.md\` - Domain-specific rules and logic
491
+ - \`api-guidelines.md\` - API design guidelines
492
+ - \`tips.md\` - Project-specific tips for Claude
493
+ - \`service-example.md\` - Code example for services
494
+ - \`component-example.md\` - Code example for components
495
+
496
+ ## Rules
497
+
498
+ 1. **Markdown only** - Only \`.md\` files are loaded
499
+ 2. **Keep files focused** - One topic per file
500
+ 3. **Use headers** - Claude uses headers to understand structure
501
+ 4. **Max 500 lines** - Longer files are truncated
502
+ 5. **Max 20 files** - Total limit for loaded files
503
+
504
+ ## How It Works
505
+
506
+ Files are automatically loaded at the start of \`/mtd-pipeline-feature\` or \`/mtd-pipeline-bugfix\` pipelines.
507
+ Each file is stored as a \`UserContext:{filename}\` entity in memory MCP.
508
+
509
+ ## Example: architecture.md
510
+
511
+ \`\`\`markdown
512
+ # Architecture
513
+
514
+ ## Layers
515
+ - Database: Drizzle ORM with PostgreSQL
516
+ - Backend: .NET 9 with FastEndpoints
517
+ - Frontend: React 19 with TanStack Query
518
+
519
+ ## Patterns
520
+ - Repository pattern for data access
521
+ - Services for business logic
522
+ - DTOs for API contracts
523
+ \`\`\`
524
+
525
+ ## Manual Refresh
526
+
527
+ To force a context refresh, use:
528
+
529
+ \`\`\`
530
+ /mtd-sync-context --refresh
531
+ \`\`\`
532
+
533
+ ## See Also
534
+
535
+ - [/mtd-sync-context](../commands/mustard/mtd-sync-context.md) - Manual context loading
536
+ - [/mtd-pipeline-feature](../commands/mustard/mtd-pipeline-feature.md) - Feature pipeline
537
+ - [pipeline.md](../core/pipeline.md) - Pipeline documentation
538
+ `;
539
+ await writeFile(join(claudePath, 'context', 'README.md'), contextReadme);
540
+ }
541
+ //# sourceMappingURL=index.js.map