claude-context 1.2.4

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.
@@ -0,0 +1,302 @@
1
+ /**
2
+ * Generation module for Claude Context Engineering
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const { glob } = require('glob');
8
+ const chalk = require('chalk');
9
+
10
+ /**
11
+ * Generate or regenerate documentation
12
+ */
13
+ async function generate(projectRoot, options = {}) {
14
+ const claudeDir = path.join(projectRoot, '.claude');
15
+ const results = {
16
+ success: true,
17
+ generated: []
18
+ };
19
+
20
+ console.log(chalk.blue('\nšŸ“ Generating documentation...\n'));
21
+
22
+ // Generate CODE_TO_WORKFLOW_MAP
23
+ if (options.codeMap) {
24
+ await generateCodeMap(claudeDir, projectRoot);
25
+ results.generated.push('CODE_TO_WORKFLOW_MAP.md');
26
+ console.log(chalk.green('āœ“ Generated CODE_TO_WORKFLOW_MAP.md'));
27
+ }
28
+
29
+ // Rebuild indexes
30
+ if (options.indexes) {
31
+ await generateIndexes(claudeDir);
32
+ results.generated.push('indexes');
33
+ console.log(chalk.green('āœ“ Rebuilt indexes'));
34
+ }
35
+
36
+ // Regenerate semantic anchors
37
+ if (options.anchors) {
38
+ await generateAnchors(claudeDir, projectRoot);
39
+ results.generated.push('anchors.json');
40
+ console.log(chalk.green('āœ“ Generated semantic anchors'));
41
+ }
42
+
43
+ // If no specific option, show help
44
+ if (!options.codeMap && !options.indexes && !options.anchors) {
45
+ console.log(chalk.yellow('No generation option specified. Available options:'));
46
+ console.log(' --code-map Regenerate CODE_TO_WORKFLOW_MAP.md');
47
+ console.log(' --indexes Rebuild all indexes');
48
+ console.log(' --anchors Regenerate semantic anchors');
49
+ results.success = false;
50
+ }
51
+
52
+ if (results.generated.length > 0) {
53
+ console.log(chalk.green(`\nāœ“ Generated ${results.generated.length} item(s)\n`));
54
+ }
55
+
56
+ return results;
57
+ }
58
+
59
+ /**
60
+ * Generate CODE_TO_WORKFLOW_MAP.md
61
+ */
62
+ async function generateCodeMap(claudeDir, projectRoot) {
63
+ const mapPath = path.join(claudeDir, 'context', 'CODE_TO_WORKFLOW_MAP.md');
64
+ const workflowDir = path.join(claudeDir, 'context', 'workflows');
65
+
66
+ // Ensure context directory exists
67
+ const contextDir = path.join(claudeDir, 'context');
68
+ if (!fs.existsSync(contextDir)) {
69
+ fs.mkdirSync(contextDir, { recursive: true });
70
+ }
71
+
72
+ const codeToWorkflow = {};
73
+
74
+ if (fs.existsSync(workflowDir)) {
75
+ const workflowFiles = await glob('*.md', { cwd: workflowDir });
76
+
77
+ for (const file of workflowFiles) {
78
+ const content = fs.readFileSync(path.join(workflowDir, file), 'utf8');
79
+
80
+ // Extract workflow name
81
+ const nameMatch = content.match(/^#\s+(.+)$/m);
82
+ const workflowName = nameMatch ? nameMatch[1] : file.replace('.md', '');
83
+
84
+ // Find file references
85
+ const fileRefPattern = /`([^`]+\.[a-z]+)(?::\d+)?`/g;
86
+ let match;
87
+
88
+ while ((match = fileRefPattern.exec(content)) !== null) {
89
+ const filePath = match[1];
90
+ if (!codeToWorkflow[filePath]) {
91
+ codeToWorkflow[filePath] = [];
92
+ }
93
+ if (!codeToWorkflow[filePath].includes(workflowName)) {
94
+ codeToWorkflow[filePath].push(workflowName);
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ // Generate markdown
101
+ let output = `# Code to Workflow Map
102
+
103
+ > Auto-generated by \`npx claude-context generate --code-map\`
104
+ > Last updated: ${new Date().toISOString().split('T')[0]}
105
+
106
+ This maps source files to their documenting workflows.
107
+
108
+ ## Quick Reference
109
+
110
+ | File | Workflows |
111
+ |------|-----------|
112
+ `;
113
+
114
+ const sortedFiles = Object.keys(codeToWorkflow).sort();
115
+ for (const file of sortedFiles) {
116
+ const workflows = codeToWorkflow[file].join(', ');
117
+ output += `| \`${file}\` | ${workflows} |\n`;
118
+ }
119
+
120
+ if (sortedFiles.length === 0) {
121
+ output += `| *No mappings yet* | Run @context-engineer to document workflows |\n`;
122
+ }
123
+
124
+ output += `
125
+
126
+ ---
127
+
128
+ ## Usage
129
+
130
+ When modifying a file listed above, check its workflow documentation:
131
+
132
+ 1. Find the file in the table above
133
+ 2. Open the linked workflow in \`.claude/context/workflows/\`
134
+ 3. Update any affected sections after your changes
135
+
136
+ ## Maintenance
137
+
138
+ Regenerate this file after adding new workflows:
139
+
140
+ \`\`\`bash
141
+ npx claude-context generate --code-map
142
+ \`\`\`
143
+ `;
144
+
145
+ fs.writeFileSync(mapPath, output);
146
+ }
147
+
148
+ /**
149
+ * Generate/rebuild indexes
150
+ */
151
+ async function generateIndexes(claudeDir) {
152
+ const indexDir = path.join(claudeDir, 'indexes');
153
+
154
+ if (!fs.existsSync(indexDir)) {
155
+ fs.mkdirSync(indexDir, { recursive: true });
156
+ }
157
+
158
+ // Generate workflow index
159
+ const workflowDir = path.join(claudeDir, 'context', 'workflows');
160
+ if (fs.existsSync(workflowDir)) {
161
+ const workflowFiles = await glob('*.md', { cwd: workflowDir });
162
+
163
+ let workflowIndex = `# Workflow Index
164
+
165
+ > Auto-generated by \`npx claude-context generate --indexes\`
166
+
167
+ | Workflow | Description |
168
+ |----------|-------------|
169
+ `;
170
+
171
+ for (const file of workflowFiles.sort()) {
172
+ const content = fs.readFileSync(path.join(workflowDir, file), 'utf8');
173
+ const nameMatch = content.match(/^#\s+(.+)$/m);
174
+ const descMatch = content.match(/^>\s*(.+)$/m) || content.match(/\n\n([^#\n]+)/);
175
+
176
+ const name = nameMatch ? nameMatch[1] : file.replace('.md', '');
177
+ const desc = descMatch ? descMatch[1].substring(0, 60) + '...' : 'No description';
178
+
179
+ workflowIndex += `| [${name}](../context/workflows/${file}) | ${desc} |\n`;
180
+ }
181
+
182
+ fs.writeFileSync(path.join(indexDir, 'WORKFLOW_INDEX.md'), workflowIndex);
183
+ }
184
+
185
+ // Generate agent index
186
+ const agentDir = path.join(claudeDir, 'agents');
187
+ if (fs.existsSync(agentDir)) {
188
+ const agentFiles = await glob('*.md', { cwd: agentDir });
189
+
190
+ let agentIndex = `# Agent Index
191
+
192
+ > Auto-generated by \`npx claude-context generate --indexes\`
193
+
194
+ | Agent | Specialty |
195
+ |-------|-----------|
196
+ `;
197
+
198
+ for (const file of agentFiles.sort()) {
199
+ const content = fs.readFileSync(path.join(agentDir, file), 'utf8');
200
+ const nameMatch = content.match(/^#\s+(.+)$/m);
201
+ const roleMatch = content.match(/##\s+Role\s*\n+([^\n#]+)/i);
202
+
203
+ const name = nameMatch ? nameMatch[1] : file.replace('.md', '');
204
+ const role = roleMatch ? roleMatch[1].substring(0, 50) : 'Specialized agent';
205
+
206
+ agentIndex += `| @${file.replace('.md', '')} | ${role} |\n`;
207
+ }
208
+
209
+ fs.writeFileSync(path.join(indexDir, 'AGENT_INDEX.md'), agentIndex);
210
+ }
211
+
212
+ // Generate command index
213
+ const commandDir = path.join(claudeDir, 'commands');
214
+ if (fs.existsSync(commandDir)) {
215
+ const commandFiles = await glob('*.md', { cwd: commandDir });
216
+
217
+ let commandIndex = `# Command Index
218
+
219
+ > Auto-generated by \`npx claude-context generate --indexes\`
220
+
221
+ | Command | Description |
222
+ |---------|-------------|
223
+ `;
224
+
225
+ for (const file of commandFiles.sort()) {
226
+ const content = fs.readFileSync(path.join(commandDir, file), 'utf8');
227
+ const nameMatch = content.match(/^#\s+(.+)$/m);
228
+ const descMatch = content.match(/^>\s*(.+)$/m);
229
+
230
+ const name = nameMatch ? nameMatch[1] : '/' + file.replace('.md', '');
231
+ const desc = descMatch ? descMatch[1].substring(0, 60) : 'No description';
232
+
233
+ commandIndex += `| /${file.replace('.md', '')} | ${desc} |\n`;
234
+ }
235
+
236
+ fs.writeFileSync(path.join(indexDir, 'COMMAND_INDEX.md'), commandIndex);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Generate semantic anchors
242
+ */
243
+ async function generateAnchors(claudeDir, projectRoot) {
244
+ const anchorsPath = path.join(claudeDir, 'sync', 'anchors.json');
245
+ const syncDir = path.join(claudeDir, 'sync');
246
+
247
+ if (!fs.existsSync(syncDir)) {
248
+ fs.mkdirSync(syncDir, { recursive: true });
249
+ }
250
+
251
+ const anchors = {};
252
+
253
+ // Find function definitions in source files
254
+ const sourcePatterns = ['**/*.js', '**/*.ts', '**/*.py', '**/*.go', '**/*.rb'];
255
+ const ignorePatterns = ['node_modules/**', '.git/**', 'dist/**', 'build/**', 'vendor/**'];
256
+
257
+ for (const pattern of sourcePatterns) {
258
+ const files = await glob(pattern, {
259
+ cwd: projectRoot,
260
+ ignore: ignorePatterns,
261
+ nodir: true
262
+ });
263
+
264
+ for (const file of files.slice(0, 100)) { // Limit for performance
265
+ try {
266
+ const content = fs.readFileSync(path.join(projectRoot, file), 'utf8');
267
+ const lines = content.split('\n');
268
+
269
+ // Find function/method definitions
270
+ const funcPatterns = [
271
+ /(?:function|async function)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/g, // JS function
272
+ /(?:const|let|var)\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(?:async\s*)?\(/g, // JS arrow
273
+ /def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/g, // Python
274
+ /func\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/g, // Go
275
+ /def\s+([a-zA-Z_][a-zA-Z0-9_?!]*)/g // Ruby
276
+ ];
277
+
278
+ for (const pattern of funcPatterns) {
279
+ let match;
280
+ while ((match = pattern.exec(content)) !== null) {
281
+ const funcName = match[1];
282
+ const lineNum = content.substring(0, match.index).split('\n').length;
283
+
284
+ const anchor = `${file}::${funcName}()`;
285
+ anchors[anchor] = {
286
+ file,
287
+ function: funcName,
288
+ line: lineNum,
289
+ updated: new Date().toISOString()
290
+ };
291
+ }
292
+ }
293
+ } catch {
294
+ // Skip unreadable files
295
+ }
296
+ }
297
+ }
298
+
299
+ fs.writeFileSync(anchorsPath, JSON.stringify(anchors, null, 2));
300
+ }
301
+
302
+ module.exports = { generate };
package/lib/hooks.js ADDED
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Git hooks management for Claude Context Engineering
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const chalk = require('chalk');
8
+
9
+ const PRE_COMMIT_HOOK = `#!/bin/sh
10
+ # Claude Context Engineering - Pre-commit hook
11
+ # Validates documentation before commits
12
+
13
+ echo "šŸ” Running context validation..."
14
+
15
+ # Check for unreplaced placeholders in staged files
16
+ STAGED_MD=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\\.md$' || true)
17
+
18
+ if [ -n "$STAGED_MD" ]; then
19
+ for file in $STAGED_MD; do
20
+ if grep -q '{{[A-Z_]*}}' "$file" 2>/dev/null; then
21
+ echo "āš ļø Warning: Unreplaced placeholder found in $file"
22
+ fi
23
+ done
24
+ fi
25
+
26
+ # Optional: Run full validation (uncomment to enable)
27
+ # npx claude-context validate --all --strict
28
+
29
+ exit 0
30
+ `;
31
+
32
+ const POST_COMMIT_HOOK = `#!/bin/sh
33
+ # Claude Context Engineering - Post-commit hook
34
+ # Updates indexes after commits
35
+
36
+ echo "šŸ“ Updating context indexes..."
37
+
38
+ # Regenerate CODE_TO_WORKFLOW_MAP if code files changed
39
+ CHANGED_CODE=$(git diff --name-only HEAD~1 HEAD | grep -E '\\.(js|ts|py|go|rb|java|cs|php)$' || true)
40
+
41
+ if [ -n "$CHANGED_CODE" ]; then
42
+ echo "Code files changed, consider running: npx claude-context generate --code-map"
43
+ fi
44
+
45
+ exit 0
46
+ `;
47
+
48
+ /**
49
+ * Manage git hooks
50
+ */
51
+ async function hooks(projectRoot, action, options = {}) {
52
+ const gitDir = path.join(projectRoot, '.git');
53
+ const hooksDir = path.join(gitDir, 'hooks');
54
+
55
+ const results = {
56
+ success: true,
57
+ installed: [],
58
+ uninstalled: [],
59
+ errors: []
60
+ };
61
+
62
+ // Check if git repo exists
63
+ if (!fs.existsSync(gitDir)) {
64
+ console.error(chalk.red('Error: Not a git repository.'));
65
+ results.success = false;
66
+ results.errors.push('Not a git repository');
67
+ return results;
68
+ }
69
+
70
+ // Ensure hooks directory exists
71
+ if (!fs.existsSync(hooksDir)) {
72
+ fs.mkdirSync(hooksDir, { recursive: true });
73
+ }
74
+
75
+ const hooksToManage = [];
76
+ if (options.preCommit || (!options.preCommit && !options.postCommit)) {
77
+ hooksToManage.push({ name: 'pre-commit', content: PRE_COMMIT_HOOK });
78
+ }
79
+ if (options.postCommit || (!options.preCommit && !options.postCommit)) {
80
+ hooksToManage.push({ name: 'post-commit', content: POST_COMMIT_HOOK });
81
+ }
82
+
83
+ if (action === 'install') {
84
+ console.log(chalk.blue('\nšŸ“¦ Installing git hooks...\n'));
85
+
86
+ for (const hook of hooksToManage) {
87
+ const hookPath = path.join(hooksDir, hook.name);
88
+
89
+ // Backup existing hook if present
90
+ if (fs.existsSync(hookPath)) {
91
+ const backupPath = `${hookPath}.backup`;
92
+ fs.copyFileSync(hookPath, backupPath);
93
+ console.log(chalk.yellow(` Backed up existing ${hook.name} to ${hook.name}.backup`));
94
+ }
95
+
96
+ // Write new hook
97
+ fs.writeFileSync(hookPath, hook.content);
98
+
99
+ // Make executable (Unix)
100
+ try {
101
+ fs.chmodSync(hookPath, '755');
102
+ } catch {
103
+ // Windows doesn't need chmod
104
+ }
105
+
106
+ results.installed.push(hook.name);
107
+ console.log(chalk.green(` āœ“ Installed ${hook.name} hook`));
108
+ }
109
+
110
+ } else if (action === 'uninstall') {
111
+ console.log(chalk.blue('\nšŸ—‘ļø Uninstalling git hooks...\n'));
112
+
113
+ for (const hook of hooksToManage) {
114
+ const hookPath = path.join(hooksDir, hook.name);
115
+
116
+ if (fs.existsSync(hookPath)) {
117
+ // Check if it's our hook
118
+ const content = fs.readFileSync(hookPath, 'utf8');
119
+ if (content.includes('Claude Context Engineering')) {
120
+ fs.unlinkSync(hookPath);
121
+
122
+ // Restore backup if exists
123
+ const backupPath = `${hookPath}.backup`;
124
+ if (fs.existsSync(backupPath)) {
125
+ fs.renameSync(backupPath, hookPath);
126
+ console.log(chalk.yellow(` Restored ${hook.name} from backup`));
127
+ }
128
+
129
+ results.uninstalled.push(hook.name);
130
+ console.log(chalk.green(` āœ“ Uninstalled ${hook.name} hook`));
131
+ } else {
132
+ console.log(chalk.yellow(` ⚠ Skipped ${hook.name} (not a claude-context hook)`));
133
+ }
134
+ } else {
135
+ console.log(chalk.gray(` - ${hook.name} not installed`));
136
+ }
137
+ }
138
+ }
139
+
140
+ // Summary
141
+ if (action === 'install') {
142
+ console.log(chalk.green(`\nāœ“ Installed ${results.installed.length} hook(s)\n`));
143
+ } else {
144
+ console.log(chalk.green(`\nāœ“ Uninstalled ${results.uninstalled.length} hook(s)\n`));
145
+ }
146
+
147
+ return results;
148
+ }
149
+
150
+ module.exports = { hooks };
package/lib/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Claude Context CLI - Main exports
3
+ */
4
+
5
+ const { validate } = require('./validate');
6
+ const { sync } = require('./sync');
7
+ const { hooks } = require('./hooks');
8
+ const { diagnose } = require('./diagnose');
9
+ const { generate } = require('./generate');
10
+
11
+ module.exports = {
12
+ validate,
13
+ sync,
14
+ hooks,
15
+ diagnose,
16
+ generate
17
+ };