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,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
|