coder-config 0.40.1
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/LICENSE +21 -0
- package/README.md +553 -0
- package/cli.js +431 -0
- package/config-loader.js +294 -0
- package/hooks/activity-track.sh +56 -0
- package/hooks/codex-workstream.sh +44 -0
- package/hooks/gemini-workstream.sh +44 -0
- package/hooks/workstream-inject.sh +20 -0
- package/lib/activity.js +283 -0
- package/lib/apply.js +344 -0
- package/lib/cli.js +267 -0
- package/lib/config.js +171 -0
- package/lib/constants.js +55 -0
- package/lib/env.js +114 -0
- package/lib/index.js +47 -0
- package/lib/init.js +122 -0
- package/lib/mcps.js +139 -0
- package/lib/memory.js +201 -0
- package/lib/projects.js +138 -0
- package/lib/registry.js +83 -0
- package/lib/utils.js +129 -0
- package/lib/workstreams.js +652 -0
- package/package.json +80 -0
- package/scripts/capture-screenshots.js +142 -0
- package/scripts/postinstall.js +122 -0
- package/scripts/release.sh +71 -0
- package/scripts/sync-version.js +77 -0
- package/scripts/tauri-prepare.js +328 -0
- package/shared/mcp-registry.json +76 -0
- package/ui/dist/assets/index-DbZ3_HBD.js +3204 -0
- package/ui/dist/assets/index-DjLdm3Mr.css +32 -0
- package/ui/dist/icons/icon-192.svg +16 -0
- package/ui/dist/icons/icon-512.svg +16 -0
- package/ui/dist/index.html +39 -0
- package/ui/dist/manifest.json +25 -0
- package/ui/dist/sw.js +24 -0
- package/ui/dist/tutorial/claude-settings.png +0 -0
- package/ui/dist/tutorial/header.png +0 -0
- package/ui/dist/tutorial/mcp-registry.png +0 -0
- package/ui/dist/tutorial/memory-view.png +0 -0
- package/ui/dist/tutorial/permissions.png +0 -0
- package/ui/dist/tutorial/plugins-view.png +0 -0
- package/ui/dist/tutorial/project-explorer.png +0 -0
- package/ui/dist/tutorial/projects-view.png +0 -0
- package/ui/dist/tutorial/sidebar.png +0 -0
- package/ui/dist/tutorial/tutorial-view.png +0 -0
- package/ui/dist/tutorial/workstreams-view.png +0 -0
- package/ui/routes/activity.js +58 -0
- package/ui/routes/commands.js +74 -0
- package/ui/routes/configs.js +329 -0
- package/ui/routes/env.js +40 -0
- package/ui/routes/file-explorer.js +668 -0
- package/ui/routes/index.js +41 -0
- package/ui/routes/mcp-discovery.js +235 -0
- package/ui/routes/memory.js +385 -0
- package/ui/routes/package.json +3 -0
- package/ui/routes/plugins.js +466 -0
- package/ui/routes/projects.js +198 -0
- package/ui/routes/registry.js +30 -0
- package/ui/routes/rules.js +74 -0
- package/ui/routes/search.js +125 -0
- package/ui/routes/settings.js +381 -0
- package/ui/routes/subprojects.js +208 -0
- package/ui/routes/tool-sync.js +127 -0
- package/ui/routes/updates.js +339 -0
- package/ui/routes/workstreams.js +224 -0
- package/ui/server.cjs +773 -0
- package/ui/terminal-server.cjs +160 -0
package/config-loader.js
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Claude Code Configuration Loader
|
|
5
|
+
*
|
|
6
|
+
* Uses standard JSON format throughout - no custom YAML.
|
|
7
|
+
* Copy/paste MCP configs from anywhere.
|
|
8
|
+
*
|
|
9
|
+
* Files:
|
|
10
|
+
* ~/.claude-config/mcp-registry.json - All available MCPs (copy/paste friendly)
|
|
11
|
+
* project/.claude/mcps.json - Which MCPs this project uses
|
|
12
|
+
* project/.claude/rules/*.md - Project rules
|
|
13
|
+
* project/.claude/commands/*.md - Project commands
|
|
14
|
+
* project/.mcp.json - Generated output for Claude Code
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
// Import from modular lib
|
|
21
|
+
const { VERSION, TOOL_PATHS } = require('./lib/constants');
|
|
22
|
+
const { loadJson, saveJson, loadEnvFile, interpolate, resolveEnvVars, copyDirRecursive } = require('./lib/utils');
|
|
23
|
+
const { findProjectRoot, findAllConfigs, mergeConfigs, getConfigPath, collectFilesFromHierarchy, findAllConfigsForTool } = require('./lib/config');
|
|
24
|
+
const { apply, applyForAntigravity, applyForGemini, detectInstalledTools, applyForTools } = require('./lib/apply');
|
|
25
|
+
const { list, add, remove } = require('./lib/mcps');
|
|
26
|
+
const { registryList, registryAdd, registryRemove } = require('./lib/registry');
|
|
27
|
+
const { init, show } = require('./lib/init');
|
|
28
|
+
const { memoryList, memoryInit, memoryAdd, memorySearch } = require('./lib/memory');
|
|
29
|
+
const { envList, envSet, envUnset } = require('./lib/env');
|
|
30
|
+
const { getProjectsRegistryPath, loadProjectsRegistry, saveProjectsRegistry, projectList, projectAdd, projectRemove } = require('./lib/projects');
|
|
31
|
+
const { getWorkstreamsPath, loadWorkstreams, saveWorkstreams, workstreamList, workstreamCreate, workstreamUpdate, workstreamDelete, workstreamUse, workstreamActive, workstreamAddProject, workstreamRemoveProject, workstreamInject, workstreamDetect, workstreamGet, getActiveWorkstream, countWorkstreamsForProject, workstreamInstallHook, workstreamInstallHookGemini, workstreamInstallHookCodex, workstreamDeactivate, workstreamCheckPath } = require('./lib/workstreams');
|
|
32
|
+
const { getActivityPath, getDefaultActivity, loadActivity, saveActivity, detectProjectRoot, activityLog, activitySummary, generateWorkstreamName, activitySuggestWorkstreams, activityClear } = require('./lib/activity');
|
|
33
|
+
const { runCli } = require('./lib/cli');
|
|
34
|
+
|
|
35
|
+
class ClaudeConfigManager {
|
|
36
|
+
constructor() {
|
|
37
|
+
this.installDir = process.env.CLAUDE_CONFIG_HOME || path.join(process.env.HOME || '', '.claude-config');
|
|
38
|
+
|
|
39
|
+
// Look for registry in multiple places
|
|
40
|
+
const possiblePaths = [
|
|
41
|
+
path.join(__dirname, 'shared', 'mcp-registry.json'),
|
|
42
|
+
path.join(__dirname, 'mcp-registry.json'),
|
|
43
|
+
path.join(this.installDir, 'shared', 'mcp-registry.json')
|
|
44
|
+
];
|
|
45
|
+
this.registryPath = possiblePaths.find(p => fs.existsSync(p)) || possiblePaths[0];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Utils
|
|
49
|
+
loadJson(filePath) { return loadJson(filePath); }
|
|
50
|
+
saveJson(filePath, data) { return saveJson(filePath, data); }
|
|
51
|
+
loadEnvFile(envPath) { return loadEnvFile(envPath); }
|
|
52
|
+
interpolate(obj, env) { return interpolate(obj, env); }
|
|
53
|
+
resolveEnvVars(obj, env) { return resolveEnvVars(obj, env); }
|
|
54
|
+
copyDirRecursive(src, dest) { return copyDirRecursive(src, dest); }
|
|
55
|
+
|
|
56
|
+
// Config
|
|
57
|
+
findProjectRoot(startDir) { return findProjectRoot(startDir); }
|
|
58
|
+
findAllConfigs(startDir) { return findAllConfigs(startDir); }
|
|
59
|
+
mergeConfigs(configs) { return mergeConfigs(configs); }
|
|
60
|
+
getConfigPath(projectDir) { return getConfigPath(this.installDir, projectDir); }
|
|
61
|
+
collectFilesFromHierarchy(configLocations, subdir) { return collectFilesFromHierarchy(configLocations, subdir); }
|
|
62
|
+
findAllConfigsForTool(toolId, startDir) { return findAllConfigsForTool(toolId, startDir); }
|
|
63
|
+
|
|
64
|
+
getAllRules(startDir = process.cwd()) {
|
|
65
|
+
const configLocations = findAllConfigs(startDir);
|
|
66
|
+
return collectFilesFromHierarchy(configLocations, 'rules');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getAllCommands(startDir = process.cwd()) {
|
|
70
|
+
const configLocations = findAllConfigs(startDir);
|
|
71
|
+
return collectFilesFromHierarchy(configLocations, 'commands');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Apply
|
|
75
|
+
apply(projectDir) { return apply(this.registryPath, projectDir); }
|
|
76
|
+
applyForAntigravity(projectDir) { return applyForAntigravity(this.registryPath, projectDir); }
|
|
77
|
+
applyForGemini(projectDir) { return applyForGemini(this.registryPath, projectDir); }
|
|
78
|
+
detectInstalledTools() { return detectInstalledTools(); }
|
|
79
|
+
getToolPaths() { return TOOL_PATHS; }
|
|
80
|
+
applyForTools(projectDir, tools) { return applyForTools(this.registryPath, projectDir, tools); }
|
|
81
|
+
|
|
82
|
+
// MCPs
|
|
83
|
+
list() { return list(this.registryPath); }
|
|
84
|
+
add(mcpNames) { return add(this.registryPath, this.installDir, mcpNames); }
|
|
85
|
+
remove(mcpNames) { return remove(this.installDir, mcpNames); }
|
|
86
|
+
|
|
87
|
+
// Registry
|
|
88
|
+
registryList() { return registryList(this.registryPath); }
|
|
89
|
+
registryAdd(name, configJson) { return registryAdd(this.registryPath, name, configJson); }
|
|
90
|
+
registryRemove(name) { return registryRemove(this.registryPath, name); }
|
|
91
|
+
|
|
92
|
+
// Init
|
|
93
|
+
init(projectDir) { return init(this.registryPath, projectDir); }
|
|
94
|
+
show(projectDir) { return show(projectDir); }
|
|
95
|
+
|
|
96
|
+
// Memory
|
|
97
|
+
memoryList(projectDir) { return memoryList(projectDir); }
|
|
98
|
+
memoryInit(projectDir) { return memoryInit(projectDir); }
|
|
99
|
+
memoryAdd(type, content, projectDir) { return memoryAdd(type, content, projectDir); }
|
|
100
|
+
memorySearch(query, projectDir) { return memorySearch(query, projectDir); }
|
|
101
|
+
|
|
102
|
+
// Env
|
|
103
|
+
envList(projectDir) { return envList(projectDir); }
|
|
104
|
+
envSet(key, value, projectDir) { return envSet(key, value, projectDir); }
|
|
105
|
+
envUnset(key, projectDir) { return envUnset(key, projectDir); }
|
|
106
|
+
|
|
107
|
+
// Projects
|
|
108
|
+
getProjectsRegistryPath() { return getProjectsRegistryPath(this.installDir); }
|
|
109
|
+
loadProjectsRegistry() { return loadProjectsRegistry(this.installDir); }
|
|
110
|
+
saveProjectsRegistry(registry) { return saveProjectsRegistry(this.installDir, registry); }
|
|
111
|
+
projectList() { return projectList(this.installDir); }
|
|
112
|
+
projectAdd(projectPath, name) { return projectAdd(this.installDir, projectPath, name); }
|
|
113
|
+
projectRemove(nameOrPath) { return projectRemove(this.installDir, nameOrPath); }
|
|
114
|
+
|
|
115
|
+
// Workstreams
|
|
116
|
+
getWorkstreamsPath() { return getWorkstreamsPath(this.installDir); }
|
|
117
|
+
loadWorkstreams() { return loadWorkstreams(this.installDir); }
|
|
118
|
+
saveWorkstreams(data) { return saveWorkstreams(this.installDir, data); }
|
|
119
|
+
workstreamList() { return workstreamList(this.installDir); }
|
|
120
|
+
workstreamCreate(name, projects, rules) { return workstreamCreate(this.installDir, name, projects, rules); }
|
|
121
|
+
workstreamUpdate(idOrName, updates) { return workstreamUpdate(this.installDir, idOrName, updates); }
|
|
122
|
+
workstreamDelete(idOrName) { return workstreamDelete(this.installDir, idOrName); }
|
|
123
|
+
workstreamUse(idOrName) { return workstreamUse(this.installDir, idOrName); }
|
|
124
|
+
workstreamActive() { return workstreamActive(this.installDir); }
|
|
125
|
+
workstreamAddProject(idOrName, projectPath) { return workstreamAddProject(this.installDir, idOrName, projectPath); }
|
|
126
|
+
workstreamRemoveProject(idOrName, projectPath) { return workstreamRemoveProject(this.installDir, idOrName, projectPath); }
|
|
127
|
+
workstreamInject(silent) { return workstreamInject(this.installDir, silent); }
|
|
128
|
+
workstreamDetect(dir) { return workstreamDetect(this.installDir, dir); }
|
|
129
|
+
workstreamGet(id) { return workstreamGet(this.installDir, id); }
|
|
130
|
+
getActiveWorkstream() { return getActiveWorkstream(this.installDir); }
|
|
131
|
+
countWorkstreamsForProject(projectPath) { return countWorkstreamsForProject(this.installDir, projectPath); }
|
|
132
|
+
workstreamInstallHook() { return workstreamInstallHook(); }
|
|
133
|
+
workstreamInstallHookGemini() { return workstreamInstallHookGemini(); }
|
|
134
|
+
workstreamInstallHookCodex() { return workstreamInstallHookCodex(); }
|
|
135
|
+
workstreamDeactivate() { return workstreamDeactivate(); }
|
|
136
|
+
workstreamCheckPath(targetPath, silent) { return workstreamCheckPath(this.installDir, targetPath, silent); }
|
|
137
|
+
|
|
138
|
+
// Activity
|
|
139
|
+
getActivityPath() { return getActivityPath(this.installDir); }
|
|
140
|
+
loadActivity() { return loadActivity(this.installDir); }
|
|
141
|
+
getDefaultActivity() { return getDefaultActivity(); }
|
|
142
|
+
saveActivity(data) { return saveActivity(this.installDir, data); }
|
|
143
|
+
detectProjectRoot(filePath) { return detectProjectRoot(filePath); }
|
|
144
|
+
activityLog(files, sessionId) { return activityLog(this.installDir, files, sessionId); }
|
|
145
|
+
activitySummary() { return activitySummary(this.installDir); }
|
|
146
|
+
generateWorkstreamName(projects) { return generateWorkstreamName(projects); }
|
|
147
|
+
activitySuggestWorkstreams() { return activitySuggestWorkstreams(this.installDir); }
|
|
148
|
+
activityClear(olderThanDays) { return activityClear(this.installDir, olderThanDays); }
|
|
149
|
+
|
|
150
|
+
// Update - check npm for updates or update from local source
|
|
151
|
+
async update(args = []) {
|
|
152
|
+
const https = require('https');
|
|
153
|
+
const { execSync } = require('child_process');
|
|
154
|
+
|
|
155
|
+
// Parse args
|
|
156
|
+
const checkOnly = args.includes('--check') || args.includes('-c');
|
|
157
|
+
const sourcePath = args.find(a => !a.startsWith('-'));
|
|
158
|
+
|
|
159
|
+
// If source path provided, use local update
|
|
160
|
+
if (sourcePath) {
|
|
161
|
+
return this._updateFromLocal(sourcePath);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check npm for updates
|
|
165
|
+
console.log('Checking for updates...');
|
|
166
|
+
|
|
167
|
+
const npmVersion = await this._fetchNpmVersion(https);
|
|
168
|
+
if (!npmVersion) {
|
|
169
|
+
console.error('Failed to check npm registry');
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const isNewer = this._isNewerVersion(npmVersion, VERSION);
|
|
174
|
+
|
|
175
|
+
if (!isNewer) {
|
|
176
|
+
console.log(`✓ You're up to date (v${VERSION})`);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log(`\nUpdate available: v${VERSION} → v${npmVersion}`);
|
|
181
|
+
|
|
182
|
+
if (checkOnly) {
|
|
183
|
+
console.log('\nRun "claude-config update" to install the update.');
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Perform npm update
|
|
188
|
+
console.log('\nUpdating via npm...');
|
|
189
|
+
try {
|
|
190
|
+
execSync('npm install -g @regression-io/claude-config@latest', {
|
|
191
|
+
stdio: 'inherit',
|
|
192
|
+
timeout: 120000
|
|
193
|
+
});
|
|
194
|
+
console.log(`\n✅ Updated to v${npmVersion}`);
|
|
195
|
+
console.log('Run "claude-config ui" to restart the UI with the new version.');
|
|
196
|
+
return true;
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.error('Update failed:', error.message);
|
|
199
|
+
console.log('\nTry manually: npm install -g @regression-io/claude-config@latest');
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Fetch latest version from npm registry
|
|
205
|
+
_fetchNpmVersion(https) {
|
|
206
|
+
return new Promise((resolve) => {
|
|
207
|
+
const url = 'https://registry.npmjs.org/@regression-io/claude-config/latest';
|
|
208
|
+
https.get(url, (res) => {
|
|
209
|
+
let data = '';
|
|
210
|
+
res.on('data', chunk => data += chunk);
|
|
211
|
+
res.on('end', () => {
|
|
212
|
+
try {
|
|
213
|
+
const parsed = JSON.parse(data);
|
|
214
|
+
resolve(parsed.version || null);
|
|
215
|
+
} catch (e) {
|
|
216
|
+
resolve(null);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}).on('error', () => resolve(null));
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Compare semver versions
|
|
224
|
+
_isNewerVersion(source, installed) {
|
|
225
|
+
if (!source || !installed) return false;
|
|
226
|
+
const parseVersion = (v) => v.split('.').map(n => parseInt(n, 10) || 0);
|
|
227
|
+
const s = parseVersion(source);
|
|
228
|
+
const i = parseVersion(installed);
|
|
229
|
+
for (let j = 0; j < Math.max(s.length, i.length); j++) {
|
|
230
|
+
const sv = s[j] || 0;
|
|
231
|
+
const iv = i[j] || 0;
|
|
232
|
+
if (sv > iv) return true;
|
|
233
|
+
if (sv < iv) return false;
|
|
234
|
+
}
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Update from local source directory
|
|
239
|
+
_updateFromLocal(sourcePath) {
|
|
240
|
+
if (!fs.existsSync(sourcePath)) {
|
|
241
|
+
console.error(`Source not found: ${sourcePath}`);
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const files = [
|
|
246
|
+
'config-loader.js',
|
|
247
|
+
'shared/mcp-registry.json',
|
|
248
|
+
'shell/claude-config.zsh'
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
let updated = 0;
|
|
252
|
+
for (const file of files) {
|
|
253
|
+
const src = path.join(sourcePath, file);
|
|
254
|
+
const dest = path.join(this.installDir, file);
|
|
255
|
+
|
|
256
|
+
if (fs.existsSync(src)) {
|
|
257
|
+
const destDir = path.dirname(dest);
|
|
258
|
+
if (!fs.existsSync(destDir)) {
|
|
259
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
260
|
+
}
|
|
261
|
+
fs.copyFileSync(src, dest);
|
|
262
|
+
console.log(`✓ Updated ${file}`);
|
|
263
|
+
updated++;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (updated > 0) {
|
|
268
|
+
console.log(`\n✅ Updated ${updated} item(s)`);
|
|
269
|
+
console.log('Restart your shell or run: source ~/.zshrc');
|
|
270
|
+
} else {
|
|
271
|
+
console.log('No files found to update');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return updated > 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Version
|
|
278
|
+
version() {
|
|
279
|
+
console.log(`claude-config v${VERSION}`);
|
|
280
|
+
console.log(`Install: ${this.installDir}`);
|
|
281
|
+
console.log(`Registry: ${this.registryPath}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// =============================================================================
|
|
286
|
+
// CLI
|
|
287
|
+
// =============================================================================
|
|
288
|
+
|
|
289
|
+
if (require.main === module) {
|
|
290
|
+
const manager = new ClaudeConfigManager();
|
|
291
|
+
runCli(manager);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
module.exports = ClaudeConfigManager;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Activity Tracking Hook for Claude Code
|
|
3
|
+
# Install: Add to ~/.claude/hooks/post-response.sh
|
|
4
|
+
#
|
|
5
|
+
# This hook tracks file activity to help suggest workstreams based on usage patterns.
|
|
6
|
+
# It extracts file paths from Claude's response and logs them to the activity tracker.
|
|
7
|
+
|
|
8
|
+
# Exit silently if claude-config isn't available
|
|
9
|
+
if ! command -v claude-config &> /dev/null; then
|
|
10
|
+
exit 0
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
# Generate session ID from current terminal session
|
|
14
|
+
SESSION_ID="${CLAUDE_SESSION_ID:-$(date +%Y%m%d)-$$}"
|
|
15
|
+
|
|
16
|
+
# The hook receives the response as stdin
|
|
17
|
+
# We extract file paths mentioned in tool calls (Read, Edit, Write, Grep, Glob results)
|
|
18
|
+
response=$(cat)
|
|
19
|
+
|
|
20
|
+
# Extract file paths from the response
|
|
21
|
+
# Look for patterns like:
|
|
22
|
+
# - "file_path": "/path/to/file"
|
|
23
|
+
# - Read file: /path/to/file
|
|
24
|
+
# - Edit file: /path/to/file
|
|
25
|
+
# - paths from glob/grep results
|
|
26
|
+
|
|
27
|
+
files=$(echo "$response" | grep -oE '(/[a-zA-Z0-9_/.~-]+\.[a-zA-Z0-9]+)' | sort -u | head -50)
|
|
28
|
+
|
|
29
|
+
if [ -n "$files" ]; then
|
|
30
|
+
# Convert to JSON array
|
|
31
|
+
json_files="["
|
|
32
|
+
first=true
|
|
33
|
+
while IFS= read -r file; do
|
|
34
|
+
# Skip common non-file patterns
|
|
35
|
+
[[ "$file" == *"/api/"* ]] && continue
|
|
36
|
+
[[ "$file" == *"/ws/"* ]] && continue
|
|
37
|
+
[[ "$file" == *".com"* ]] && continue
|
|
38
|
+
[[ "$file" == *".org"* ]] && continue
|
|
39
|
+
|
|
40
|
+
if [ "$first" = true ]; then
|
|
41
|
+
first=false
|
|
42
|
+
else
|
|
43
|
+
json_files+=","
|
|
44
|
+
fi
|
|
45
|
+
json_files+="\"$file\""
|
|
46
|
+
done <<< "$files"
|
|
47
|
+
json_files+="]"
|
|
48
|
+
|
|
49
|
+
# Log to activity tracker via API (run in background to not block)
|
|
50
|
+
curl -s -X POST "http://localhost:3333/api/activity/log" \
|
|
51
|
+
-H "Content-Type: application/json" \
|
|
52
|
+
-d "{\"files\": $json_files, \"sessionId\": \"$SESSION_ID\"}" > /dev/null 2>&1 &
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# Pass through the response unchanged
|
|
56
|
+
echo "$response"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Codex CLI Workstream Injection Hook
|
|
3
|
+
# Install: claude-config workstream install-hook --codex
|
|
4
|
+
#
|
|
5
|
+
# This hook injects workstream context at session start, restricting
|
|
6
|
+
# the agent to work within specified directories.
|
|
7
|
+
|
|
8
|
+
# Exit silently if claude-config isn't available
|
|
9
|
+
if ! command -v claude-config &> /dev/null; then
|
|
10
|
+
echo '{"decision": "allow"}'
|
|
11
|
+
exit 0
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
# Read input from stdin (Codex passes JSON)
|
|
15
|
+
input=$(cat)
|
|
16
|
+
|
|
17
|
+
# Check for active workstream via env var
|
|
18
|
+
if [ -z "$CLAUDE_WORKSTREAM" ]; then
|
|
19
|
+
# No workstream active, allow without context
|
|
20
|
+
echo '{"decision": "allow"}'
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Get workstream injection content
|
|
25
|
+
context=$(claude-config workstream inject --silent 2>/dev/null)
|
|
26
|
+
|
|
27
|
+
if [ -n "$context" ]; then
|
|
28
|
+
# Escape the context for JSON
|
|
29
|
+
escaped_context=$(echo "$context" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')
|
|
30
|
+
|
|
31
|
+
# Return with additional context to inject
|
|
32
|
+
cat <<EOF
|
|
33
|
+
{
|
|
34
|
+
"decision": "allow",
|
|
35
|
+
"hookSpecificOutput": {
|
|
36
|
+
"additionalContext": $escaped_context
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
EOF
|
|
40
|
+
else
|
|
41
|
+
echo '{"decision": "allow"}'
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
exit 0
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Gemini CLI Workstream Injection Hook
|
|
3
|
+
# Install: Add to ~/.gemini/settings.json hooks configuration
|
|
4
|
+
#
|
|
5
|
+
# This hook injects workstream context at session start, restricting
|
|
6
|
+
# the agent to work within specified directories.
|
|
7
|
+
|
|
8
|
+
# Exit silently if claude-config isn't available
|
|
9
|
+
if ! command -v claude-config &> /dev/null; then
|
|
10
|
+
echo '{"decision": "allow"}'
|
|
11
|
+
exit 0
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
# Read input from stdin (Gemini passes JSON)
|
|
15
|
+
input=$(cat)
|
|
16
|
+
|
|
17
|
+
# Check for active workstream via env var
|
|
18
|
+
if [ -z "$CLAUDE_WORKSTREAM" ]; then
|
|
19
|
+
# No workstream active, allow without context
|
|
20
|
+
echo '{"decision": "allow"}'
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Get workstream injection content
|
|
25
|
+
context=$(claude-config workstream inject --silent 2>/dev/null)
|
|
26
|
+
|
|
27
|
+
if [ -n "$context" ]; then
|
|
28
|
+
# Escape the context for JSON
|
|
29
|
+
escaped_context=$(echo "$context" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')
|
|
30
|
+
|
|
31
|
+
# Return with additional context to inject
|
|
32
|
+
cat <<EOF
|
|
33
|
+
{
|
|
34
|
+
"decision": "allow",
|
|
35
|
+
"hookSpecificOutput": {
|
|
36
|
+
"additionalContext": $escaped_context
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
EOF
|
|
40
|
+
else
|
|
41
|
+
echo '{"decision": "allow"}'
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
exit 0
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Workstream Rule Injection Hook for Claude Code
|
|
3
|
+
#
|
|
4
|
+
# This hook injects the active workstream's rules into every Claude session.
|
|
5
|
+
#
|
|
6
|
+
# Installation:
|
|
7
|
+
# 1. Copy this file to ~/.claude/hooks/pre-prompt.sh (or add to existing)
|
|
8
|
+
# 2. Make it executable: chmod +x ~/.claude/hooks/pre-prompt.sh
|
|
9
|
+
# 3. Set active workstream: claude-config workstream use "My Workstream"
|
|
10
|
+
#
|
|
11
|
+
# The rules will be prepended to Claude's context automatically.
|
|
12
|
+
|
|
13
|
+
# Check if claude-config is available
|
|
14
|
+
if ! command -v claude-config &> /dev/null; then
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Inject workstream rules silently
|
|
19
|
+
# The --silent flag suppresses "No active workstream" messages
|
|
20
|
+
claude-config workstream inject --silent
|