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.
package/lib/sync.js ADDED
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Synchronization 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
+ * Synchronize documentation with code
12
+ */
13
+ async function sync(projectRoot, options = {}) {
14
+ const claudeDir = path.join(projectRoot, '.claude');
15
+ const results = {
16
+ success: true,
17
+ driftDetected: false,
18
+ filesChecked: 0,
19
+ issuesFound: 0,
20
+ issuesFixed: 0
21
+ };
22
+
23
+ console.log(chalk.blue('\nšŸ”„ Checking documentation synchronization...\n'));
24
+
25
+ // Check for drift
26
+ if (options.check || options.fix || (!options.rebuildMap)) {
27
+ const driftResult = await checkDrift(claudeDir, projectRoot, options);
28
+ results.driftDetected = driftResult.hasDrift;
29
+ results.filesChecked = driftResult.filesChecked;
30
+ results.issuesFound = driftResult.issues.length;
31
+
32
+ if (options.fix && driftResult.issues.length > 0) {
33
+ const fixResult = await fixDrift(claudeDir, projectRoot, driftResult.issues);
34
+ results.issuesFixed = fixResult.fixed;
35
+ }
36
+
37
+ if (options.strict && results.driftDetected) {
38
+ results.success = false;
39
+ }
40
+ }
41
+
42
+ // Rebuild CODE_TO_WORKFLOW_MAP
43
+ if (options.rebuildMap) {
44
+ await rebuildCodeMap(claudeDir, projectRoot);
45
+ console.log(chalk.green('āœ“ CODE_TO_WORKFLOW_MAP.md regenerated'));
46
+ }
47
+
48
+ // Print summary
49
+ printSyncSummary(results, options);
50
+
51
+ return results;
52
+ }
53
+
54
+ /**
55
+ * Check for documentation drift
56
+ */
57
+ async function checkDrift(claudeDir, projectRoot, options) {
58
+ const result = {
59
+ hasDrift: false,
60
+ filesChecked: 0,
61
+ issues: []
62
+ };
63
+
64
+ // Load staleness tracking
65
+ const stalenessPath = path.join(claudeDir, 'sync', 'staleness.json');
66
+ let staleness = {};
67
+ if (fs.existsSync(stalenessPath)) {
68
+ try {
69
+ staleness = JSON.parse(fs.readFileSync(stalenessPath, 'utf8'));
70
+ } catch {
71
+ // Start fresh
72
+ }
73
+ }
74
+
75
+ // Check workflow files against code
76
+ const workflowDir = path.join(claudeDir, 'context', 'workflows');
77
+ if (fs.existsSync(workflowDir)) {
78
+ const workflowFiles = await glob('*.md', { cwd: workflowDir });
79
+
80
+ for (const file of workflowFiles) {
81
+ result.filesChecked++;
82
+ const content = fs.readFileSync(path.join(workflowDir, file), 'utf8');
83
+
84
+ // Find file:line references
85
+ const lineRefPattern = /`([^`]+):(\d+)`/g;
86
+ let match;
87
+
88
+ while ((match = lineRefPattern.exec(content)) !== null) {
89
+ const filePath = match[1];
90
+ const lineNum = parseInt(match[2], 10);
91
+ const targetPath = path.join(projectRoot, filePath);
92
+
93
+ if (fs.existsSync(targetPath)) {
94
+ const lines = fs.readFileSync(targetPath, 'utf8').split('\n');
95
+
96
+ if (lineNum > lines.length) {
97
+ result.hasDrift = true;
98
+ result.issues.push({
99
+ docFile: file,
100
+ codeFile: filePath,
101
+ oldLine: lineNum,
102
+ newLine: null,
103
+ type: 'line_exceeded'
104
+ });
105
+ }
106
+ } else {
107
+ result.hasDrift = true;
108
+ result.issues.push({
109
+ docFile: file,
110
+ codeFile: filePath,
111
+ oldLine: lineNum,
112
+ newLine: null,
113
+ type: 'file_missing'
114
+ });
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ if (result.issues.length > 0) {
121
+ console.log(chalk.yellow(`⚠ Found ${result.issues.length} drift issues:`));
122
+ for (const issue of result.issues.slice(0, 5)) {
123
+ if (issue.type === 'file_missing') {
124
+ console.log(chalk.yellow(` - ${issue.docFile}: File ${issue.codeFile} no longer exists`));
125
+ } else {
126
+ console.log(chalk.yellow(` - ${issue.docFile}: Line ${issue.oldLine} in ${issue.codeFile} is out of range`));
127
+ }
128
+ }
129
+ if (result.issues.length > 5) {
130
+ console.log(chalk.yellow(` ... and ${result.issues.length - 5} more`));
131
+ }
132
+ } else {
133
+ console.log(chalk.green('āœ“ No drift detected'));
134
+ }
135
+
136
+ return result;
137
+ }
138
+
139
+ /**
140
+ * Fix drift issues
141
+ */
142
+ async function fixDrift(claudeDir, projectRoot, issues) {
143
+ const result = { fixed: 0, failed: 0 };
144
+
145
+ // Group issues by doc file
146
+ const byDocFile = {};
147
+ for (const issue of issues) {
148
+ if (!byDocFile[issue.docFile]) {
149
+ byDocFile[issue.docFile] = [];
150
+ }
151
+ byDocFile[issue.docFile].push(issue);
152
+ }
153
+
154
+ for (const [docFile, fileIssues] of Object.entries(byDocFile)) {
155
+ const docPath = path.join(claudeDir, 'context', 'workflows', docFile);
156
+ if (!fs.existsSync(docPath)) continue;
157
+
158
+ let content = fs.readFileSync(docPath, 'utf8');
159
+ let modified = false;
160
+
161
+ for (const issue of fileIssues) {
162
+ if (issue.type === 'file_missing') {
163
+ // Comment out the reference
164
+ const pattern = new RegExp(`\`${issue.codeFile}:${issue.oldLine}\``, 'g');
165
+ const newContent = content.replace(pattern, `<!-- REMOVED: ${issue.codeFile}:${issue.oldLine} -->`);
166
+ if (newContent !== content) {
167
+ content = newContent;
168
+ modified = true;
169
+ result.fixed++;
170
+ }
171
+ }
172
+ }
173
+
174
+ if (modified) {
175
+ fs.writeFileSync(docPath, content);
176
+ }
177
+ }
178
+
179
+ console.log(chalk.green(`āœ“ Fixed ${result.fixed} issues`));
180
+
181
+ return result;
182
+ }
183
+
184
+ /**
185
+ * Rebuild CODE_TO_WORKFLOW_MAP.md
186
+ */
187
+ async function rebuildCodeMap(claudeDir, projectRoot) {
188
+ const mapPath = path.join(claudeDir, 'context', 'CODE_TO_WORKFLOW_MAP.md');
189
+ const workflowDir = path.join(claudeDir, 'context', 'workflows');
190
+
191
+ const codeToWorkflow = {};
192
+
193
+ if (fs.existsSync(workflowDir)) {
194
+ const workflowFiles = await glob('*.md', { cwd: workflowDir });
195
+
196
+ for (const file of workflowFiles) {
197
+ const content = fs.readFileSync(path.join(workflowDir, file), 'utf8');
198
+
199
+ // Extract workflow name from first heading
200
+ const nameMatch = content.match(/^#\s+(.+)$/m);
201
+ const workflowName = nameMatch ? nameMatch[1] : file.replace('.md', '');
202
+
203
+ // Find file references
204
+ const fileRefPattern = /`([^`]+\.[a-z]+)(?::\d+)?`/g;
205
+ let match;
206
+
207
+ while ((match = fileRefPattern.exec(content)) !== null) {
208
+ const filePath = match[1];
209
+ if (!codeToWorkflow[filePath]) {
210
+ codeToWorkflow[filePath] = [];
211
+ }
212
+ if (!codeToWorkflow[filePath].includes(workflowName)) {
213
+ codeToWorkflow[filePath].push(workflowName);
214
+ }
215
+ }
216
+ }
217
+ }
218
+
219
+ // Generate markdown
220
+ let output = `# Code to Workflow Map
221
+
222
+ > Auto-generated by \`npx claude-context generate --code-map\`
223
+ > Last updated: ${new Date().toISOString().split('T')[0]}
224
+
225
+ This maps source files to their documenting workflows.
226
+
227
+ | File | Workflows |
228
+ |------|-----------|
229
+ `;
230
+
231
+ const sortedFiles = Object.keys(codeToWorkflow).sort();
232
+ for (const file of sortedFiles) {
233
+ const workflows = codeToWorkflow[file].join(', ');
234
+ output += `| \`${file}\` | ${workflows} |\n`;
235
+ }
236
+
237
+ if (sortedFiles.length === 0) {
238
+ output += `| *No mappings yet* | - |\n`;
239
+ }
240
+
241
+ fs.writeFileSync(mapPath, output);
242
+ }
243
+
244
+ /**
245
+ * Print sync summary
246
+ */
247
+ function printSyncSummary(results, options) {
248
+ console.log('\n' + chalk.bold('Sync Summary:'));
249
+ console.log(` Files checked: ${results.filesChecked}`);
250
+ console.log(` Issues found: ${results.issuesFound}`);
251
+
252
+ if (options.fix) {
253
+ console.log(` Issues fixed: ${results.issuesFixed}`);
254
+ }
255
+
256
+ if (results.success) {
257
+ console.log(chalk.green('\nāœ“ Sync check complete!\n'));
258
+ } else {
259
+ console.log(chalk.red('\nāœ— Drift detected (strict mode).\n'));
260
+ }
261
+ }
262
+
263
+ module.exports = { sync };
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Validation 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
+ * Validate context engineering setup
12
+ */
13
+ async function validate(projectRoot, options = {}) {
14
+ const claudeDir = path.join(projectRoot, '.claude');
15
+ const results = {
16
+ success: true,
17
+ checks: [],
18
+ errors: [],
19
+ warnings: []
20
+ };
21
+
22
+ console.log(chalk.blue('\nšŸ” Validating context engineering setup...\n'));
23
+
24
+ // Determine which checks to run
25
+ const runAll = options.all || (!options.schema && !options.links && !options.placeholders && !options.structure && !options.lines);
26
+
27
+ // Schema validation
28
+ if (runAll || options.schema) {
29
+ const schemaResult = await validateSchemas(claudeDir, options);
30
+ results.checks.push(schemaResult);
31
+ if (!schemaResult.passed) {
32
+ results.errors.push(...schemaResult.errors);
33
+ results.success = false;
34
+ }
35
+ }
36
+
37
+ // Link validation
38
+ if (runAll || options.links) {
39
+ const linkResult = await validateLinks(claudeDir, projectRoot, options);
40
+ results.checks.push(linkResult);
41
+ if (!linkResult.passed) {
42
+ results.errors.push(...linkResult.errors);
43
+ results.success = false;
44
+ }
45
+ }
46
+
47
+ // Placeholder validation
48
+ if (runAll || options.placeholders) {
49
+ const placeholderResult = await validatePlaceholders(claudeDir, options);
50
+ results.checks.push(placeholderResult);
51
+ if (!placeholderResult.passed) {
52
+ results.warnings.push(...placeholderResult.errors);
53
+ // Placeholders are warnings, not failures
54
+ }
55
+ }
56
+
57
+ // Structure validation
58
+ if (runAll || options.structure) {
59
+ const structureResult = await validateStructure(claudeDir, options);
60
+ results.checks.push(structureResult);
61
+ if (!structureResult.passed) {
62
+ results.errors.push(...structureResult.errors);
63
+ results.success = false;
64
+ }
65
+ }
66
+
67
+ // Line number validation
68
+ if (runAll || options.lines) {
69
+ const lineResult = await validateLineNumbers(claudeDir, projectRoot, options);
70
+ results.checks.push(lineResult);
71
+ if (!lineResult.passed) {
72
+ results.warnings.push(...lineResult.errors);
73
+ }
74
+ }
75
+
76
+ // Print summary
77
+ printSummary(results);
78
+
79
+ return results;
80
+ }
81
+
82
+ /**
83
+ * Validate JSON schemas
84
+ */
85
+ async function validateSchemas(claudeDir, options) {
86
+ const result = { name: 'Schema Validation', passed: true, errors: [], count: 0 };
87
+
88
+ const schemaDir = path.join(claudeDir, 'schemas');
89
+ if (!fs.existsSync(schemaDir)) {
90
+ result.errors.push('No schemas directory found');
91
+ result.passed = false;
92
+ return result;
93
+ }
94
+
95
+ const jsonFiles = await glob('**/*.json', { cwd: claudeDir, nodir: true });
96
+
97
+ for (const file of jsonFiles) {
98
+ try {
99
+ const content = fs.readFileSync(path.join(claudeDir, file), 'utf8');
100
+ JSON.parse(content);
101
+ result.count++;
102
+ } catch (e) {
103
+ result.errors.push(`Invalid JSON in ${file}: ${e.message}`);
104
+ result.passed = false;
105
+ }
106
+ }
107
+
108
+ console.log(result.passed
109
+ ? chalk.green(`āœ“ Schema validation: ${result.count} JSON files valid`)
110
+ : chalk.red(`āœ— Schema validation: ${result.errors.length} errors`));
111
+
112
+ return result;
113
+ }
114
+
115
+ /**
116
+ * Validate internal links
117
+ */
118
+ async function validateLinks(claudeDir, projectRoot, options) {
119
+ const result = { name: 'Link Validation', passed: true, errors: [], count: 0 };
120
+
121
+ const mdFiles = await glob('**/*.md', { cwd: claudeDir, nodir: true });
122
+ const linkPattern = /\[([^\]]+)\]\(([^)]+)\)/g;
123
+
124
+ for (const file of mdFiles) {
125
+ const content = fs.readFileSync(path.join(claudeDir, file), 'utf8');
126
+ let match;
127
+
128
+ while ((match = linkPattern.exec(content)) !== null) {
129
+ const linkPath = match[2];
130
+
131
+ // Skip external links and anchors
132
+ if (linkPath.startsWith('http') || linkPath.startsWith('#') || linkPath.startsWith('mailto:')) {
133
+ continue;
134
+ }
135
+
136
+ result.count++;
137
+
138
+ // Resolve relative path
139
+ const targetPath = path.resolve(path.dirname(path.join(claudeDir, file)), linkPath.split('#')[0]);
140
+
141
+ if (!fs.existsSync(targetPath)) {
142
+ result.errors.push(`Broken link in ${file}: ${linkPath}`);
143
+ result.passed = false;
144
+ }
145
+ }
146
+ }
147
+
148
+ console.log(result.passed
149
+ ? chalk.green(`āœ“ Link validation: ${result.count} links checked`)
150
+ : chalk.red(`āœ— Link validation: ${result.errors.length} broken links`));
151
+
152
+ if (options.verbose && result.errors.length > 0) {
153
+ result.errors.forEach(e => console.log(chalk.yellow(` - ${e}`)));
154
+ }
155
+
156
+ return result;
157
+ }
158
+
159
+ /**
160
+ * Validate placeholders are replaced
161
+ */
162
+ async function validatePlaceholders(claudeDir, options) {
163
+ const result = { name: 'Placeholder Validation', passed: true, errors: [], count: 0 };
164
+
165
+ const mdFiles = await glob('**/*.md', { cwd: claudeDir, nodir: true });
166
+ const placeholderPattern = /\{\{([A-Z_]+)\}\}/g;
167
+
168
+ for (const file of mdFiles) {
169
+ const content = fs.readFileSync(path.join(claudeDir, file), 'utf8');
170
+ let match;
171
+
172
+ while ((match = placeholderPattern.exec(content)) !== null) {
173
+ result.errors.push(`Unreplaced placeholder in ${file}: ${match[0]}`);
174
+ result.count++;
175
+ result.passed = false;
176
+ }
177
+ }
178
+
179
+ console.log(result.passed
180
+ ? chalk.green(`āœ“ Placeholder validation: No unreplaced placeholders`)
181
+ : chalk.yellow(`⚠ Placeholder validation: ${result.count} unreplaced placeholders`));
182
+
183
+ return result;
184
+ }
185
+
186
+ /**
187
+ * Validate directory structure
188
+ */
189
+ async function validateStructure(claudeDir, options) {
190
+ const result = { name: 'Structure Validation', passed: true, errors: [], count: 0 };
191
+
192
+ const requiredDirs = [
193
+ 'agents',
194
+ 'commands',
195
+ 'context',
196
+ 'indexes'
197
+ ];
198
+
199
+ for (const dir of requiredDirs) {
200
+ const dirPath = path.join(claudeDir, dir);
201
+ if (fs.existsSync(dirPath)) {
202
+ result.count++;
203
+ } else {
204
+ result.errors.push(`Missing required directory: ${dir}`);
205
+ result.passed = false;
206
+ }
207
+ }
208
+
209
+ // Check for CLAUDE.md at project root
210
+ const claudeMdPath = path.join(path.dirname(claudeDir), 'CLAUDE.md');
211
+ if (fs.existsSync(claudeMdPath)) {
212
+ result.count++;
213
+ } else {
214
+ result.errors.push('Missing CLAUDE.md at project root');
215
+ result.passed = false;
216
+ }
217
+
218
+ console.log(result.passed
219
+ ? chalk.green(`āœ“ Structure validation: ${result.count} required items present`)
220
+ : chalk.red(`āœ— Structure validation: ${result.errors.length} missing items`));
221
+
222
+ return result;
223
+ }
224
+
225
+ /**
226
+ * Validate line number references
227
+ */
228
+ async function validateLineNumbers(claudeDir, projectRoot, options) {
229
+ const result = { name: 'Line Number Validation', passed: true, errors: [], count: 0, accurate: 0 };
230
+ const threshold = parseInt(options.threshold || '60', 10);
231
+
232
+ const mdFiles = await glob('**/*.md', { cwd: claudeDir, nodir: true });
233
+ const lineRefPattern = /`([^`]+):(\d+)`|([a-zA-Z0-9_\-/.]+):(\d+)/g;
234
+
235
+ for (const file of mdFiles) {
236
+ const content = fs.readFileSync(path.join(claudeDir, file), 'utf8');
237
+ let match;
238
+
239
+ while ((match = lineRefPattern.exec(content)) !== null) {
240
+ const filePath = match[1] || match[3];
241
+ const lineNum = parseInt(match[2] || match[4], 10);
242
+
243
+ // Skip if file path doesn't look like a source file
244
+ if (!filePath || filePath.includes(' ') || !filePath.includes('.')) {
245
+ continue;
246
+ }
247
+
248
+ result.count++;
249
+
250
+ const targetPath = path.join(projectRoot, filePath);
251
+ if (fs.existsSync(targetPath)) {
252
+ try {
253
+ const lines = fs.readFileSync(targetPath, 'utf8').split('\n');
254
+ if (lineNum <= lines.length) {
255
+ result.accurate++;
256
+ } else {
257
+ result.errors.push(`Line ${lineNum} exceeds file length in ${filePath} (${lines.length} lines)`);
258
+ }
259
+ } catch {
260
+ // Skip unreadable files
261
+ }
262
+ }
263
+ }
264
+ }
265
+
266
+ const accuracy = result.count > 0 ? Math.round((result.accurate / result.count) * 100) : 100;
267
+ result.passed = accuracy >= threshold;
268
+
269
+ console.log(result.passed
270
+ ? chalk.green(`āœ“ Line number validation: ${accuracy}% accurate (threshold: ${threshold}%)`)
271
+ : chalk.yellow(`⚠ Line number validation: ${accuracy}% accurate (below ${threshold}% threshold)`));
272
+
273
+ return result;
274
+ }
275
+
276
+ /**
277
+ * Print validation summary
278
+ */
279
+ function printSummary(results) {
280
+ console.log('\n' + chalk.bold('Summary:'));
281
+ console.log(` Checks run: ${results.checks.length}`);
282
+ console.log(` Errors: ${results.errors.length}`);
283
+ console.log(` Warnings: ${results.warnings.length}`);
284
+
285
+ if (results.success) {
286
+ console.log(chalk.green('\nāœ“ All validations passed!\n'));
287
+ } else {
288
+ console.log(chalk.red('\nāœ— Some validations failed.\n'));
289
+ }
290
+ }
291
+
292
+ module.exports = { validate };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "claude-context",
3
+ "version": "1.2.4",
4
+ "description": "CLI tools for Claude Context Engineering - validate, sync, diagnose, and manage your context system",
5
+ "keywords": [
6
+ "claude",
7
+ "claude-code",
8
+ "context-engineering",
9
+ "documentation",
10
+ "validation",
11
+ "cli"
12
+ ],
13
+ "author": "SireJeff",
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/SireJeff/claude-context-engineering-template.git",
18
+ "directory": "packages/claude-context"
19
+ },
20
+ "homepage": "https://github.com/SireJeff/claude-context-engineering-template#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/SireJeff/claude-context-engineering-template/issues"
23
+ },
24
+ "bin": {
25
+ "claude-context": "./bin/claude-context.js"
26
+ },
27
+ "main": "lib/index.js",
28
+ "files": [
29
+ "bin",
30
+ "lib"
31
+ ],
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ },
35
+ "dependencies": {
36
+ "commander": "^11.0.0",
37
+ "chalk": "^4.1.2",
38
+ "glob": "^10.3.0",
39
+ "minimatch": "^9.0.0"
40
+ },
41
+ "devDependencies": {
42
+ "jest": "^29.7.0"
43
+ },
44
+ "scripts": {
45
+ "test": "jest",
46
+ "test:coverage": "jest --coverage"
47
+ }
48
+ }