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/README.md +121 -0
- package/bin/claude-context.js +144 -0
- package/lib/diagnose.js +337 -0
- package/lib/generate.js +302 -0
- package/lib/hooks.js +150 -0
- package/lib/index.js +17 -0
- package/lib/sync.js +263 -0
- package/lib/validate.js +292 -0
- package/package.json +48 -0
package/lib/generate.js
ADDED
|
@@ -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
|
+
};
|