@regression-io/claude-config 0.36.18 → 0.37.3
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/config-loader.js +5 -1
- package/lib/cli.js +12 -3
- package/lib/constants.js +1 -1
- package/lib/workstreams.js +141 -10
- package/package.json +1 -1
- package/ui/dist/assets/{index-CulBzdDG.js → index-9YD-9K6l.js} +46 -46
- package/ui/dist/index.html +1 -1
- package/ui/routes/workstreams.js +10 -0
package/config-loader.js
CHANGED
|
@@ -28,7 +28,7 @@ const { init, show } = require('./lib/init');
|
|
|
28
28
|
const { memoryList, memoryInit, memoryAdd, memorySearch } = require('./lib/memory');
|
|
29
29
|
const { envList, envSet, envUnset } = require('./lib/env');
|
|
30
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 } = require('./lib/workstreams');
|
|
31
|
+
const { getWorkstreamsPath, loadWorkstreams, saveWorkstreams, workstreamList, workstreamCreate, workstreamUpdate, workstreamDelete, workstreamUse, workstreamActive, workstreamAddProject, workstreamRemoveProject, workstreamInject, workstreamDetect, workstreamGet, getActiveWorkstream, countWorkstreamsForProject, workstreamInstallHook, workstreamDeactivate } = require('./lib/workstreams');
|
|
32
32
|
const { getActivityPath, getDefaultActivity, loadActivity, saveActivity, detectProjectRoot, activityLog, activitySummary, generateWorkstreamName, activitySuggestWorkstreams, activityClear } = require('./lib/activity');
|
|
33
33
|
const { getSmartSyncPath, loadSmartSyncPrefs, saveSmartSyncPrefs, smartSyncRememberChoice, smartSyncDismissNudge, smartSyncUpdateSettings, smartSyncDetect, smartSyncCheckNudge, smartSyncHandleAction, smartSyncStatus } = require('./lib/smart-sync');
|
|
34
34
|
const { runCli } = require('./lib/cli');
|
|
@@ -128,6 +128,10 @@ class ClaudeConfigManager {
|
|
|
128
128
|
workstreamInject(silent) { return workstreamInject(this.installDir, silent); }
|
|
129
129
|
workstreamDetect(dir) { return workstreamDetect(this.installDir, dir); }
|
|
130
130
|
workstreamGet(id) { return workstreamGet(this.installDir, id); }
|
|
131
|
+
getActiveWorkstream() { return getActiveWorkstream(this.installDir); }
|
|
132
|
+
countWorkstreamsForProject(projectPath) { return countWorkstreamsForProject(this.installDir, projectPath); }
|
|
133
|
+
workstreamInstallHook() { return workstreamInstallHook(); }
|
|
134
|
+
workstreamDeactivate() { return workstreamDeactivate(); }
|
|
131
135
|
|
|
132
136
|
// Activity
|
|
133
137
|
getActivityPath() { return getActivityPath(this.installDir); }
|
package/lib/cli.js
CHANGED
|
@@ -112,12 +112,16 @@ function runCli(manager) {
|
|
|
112
112
|
const ws = manager.workstreamActive();
|
|
113
113
|
if (ws) {
|
|
114
114
|
console.log(`Active: ${ws.name}`);
|
|
115
|
-
if (ws.projects.length > 0) {
|
|
115
|
+
if (ws.projects && ws.projects.length > 0) {
|
|
116
116
|
console.log(`Projects: ${ws.projects.map(p => path.basename(p)).join(', ')}`);
|
|
117
117
|
}
|
|
118
118
|
} else {
|
|
119
119
|
console.log('No active workstream');
|
|
120
120
|
}
|
|
121
|
+
} else if (args[1] === 'install-hook') {
|
|
122
|
+
manager.workstreamInstallHook();
|
|
123
|
+
} else if (args[1] === 'deactivate') {
|
|
124
|
+
manager.workstreamDeactivate();
|
|
121
125
|
} else {
|
|
122
126
|
manager.workstreamList();
|
|
123
127
|
}
|
|
@@ -189,12 +193,17 @@ Workstream Commands:
|
|
|
189
193
|
workstream List all workstreams
|
|
190
194
|
workstream create "Name" Create new workstream
|
|
191
195
|
workstream delete <name> Delete workstream
|
|
192
|
-
workstream use <name> Set active workstream
|
|
196
|
+
workstream use <name> Set active workstream (global)
|
|
193
197
|
workstream active Show current active workstream
|
|
198
|
+
workstream deactivate Show how to deactivate workstream
|
|
194
199
|
workstream add <ws> <path> Add project to workstream
|
|
195
200
|
workstream remove <ws> <path> Remove project from workstream
|
|
196
|
-
workstream inject [--silent]
|
|
201
|
+
workstream inject [--silent] Output restriction + context (for hooks)
|
|
197
202
|
workstream detect [path] Detect workstream for directory
|
|
203
|
+
workstream install-hook Install pre-prompt hook for injection
|
|
204
|
+
|
|
205
|
+
Per-session activation (enables parallel work):
|
|
206
|
+
export CLAUDE_WORKSTREAM=<name-or-id>
|
|
198
207
|
|
|
199
208
|
Registry Commands:
|
|
200
209
|
registry List MCPs in global registry
|
package/lib/constants.js
CHANGED
package/lib/workstreams.js
CHANGED
|
@@ -188,11 +188,10 @@ function workstreamUse(installDir, idOrName) {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
/**
|
|
191
|
-
* Get active workstream
|
|
191
|
+
* Get active workstream (uses env var or file-based activeId)
|
|
192
192
|
*/
|
|
193
193
|
function workstreamActive(installDir) {
|
|
194
|
-
|
|
195
|
-
return data.workstreams.find(w => w.id === data.activeId) || null;
|
|
194
|
+
return getActiveWorkstream(installDir);
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
/**
|
|
@@ -253,23 +252,81 @@ function workstreamRemoveProject(installDir, idOrName, projectPath) {
|
|
|
253
252
|
}
|
|
254
253
|
|
|
255
254
|
/**
|
|
256
|
-
*
|
|
255
|
+
* Get active workstream - checks env var first, then falls back to file
|
|
256
|
+
*/
|
|
257
|
+
function getActiveWorkstream(installDir) {
|
|
258
|
+
const data = loadWorkstreams(installDir);
|
|
259
|
+
|
|
260
|
+
// Check env var first (per-session activation)
|
|
261
|
+
const envWorkstream = process.env.CLAUDE_WORKSTREAM;
|
|
262
|
+
if (envWorkstream) {
|
|
263
|
+
const ws = data.workstreams.find(
|
|
264
|
+
w => w.id === envWorkstream || w.name.toLowerCase() === envWorkstream.toLowerCase()
|
|
265
|
+
);
|
|
266
|
+
if (ws) return ws;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Fall back to file-based activeId
|
|
270
|
+
return data.workstreams.find(w => w.id === data.activeId) || null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Inject active workstream context into Claude - includes restriction and context
|
|
257
275
|
*/
|
|
258
276
|
function workstreamInject(installDir, silent = false) {
|
|
259
|
-
const active =
|
|
277
|
+
const active = getActiveWorkstream(installDir);
|
|
260
278
|
|
|
261
279
|
if (!active) {
|
|
262
280
|
if (!silent) console.log('No active workstream');
|
|
263
281
|
return null;
|
|
264
282
|
}
|
|
265
283
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
284
|
+
// Build the injection output
|
|
285
|
+
const lines = [];
|
|
286
|
+
|
|
287
|
+
// Header
|
|
288
|
+
lines.push(`## Active Workstream: ${active.name}`);
|
|
289
|
+
lines.push('');
|
|
290
|
+
|
|
291
|
+
// Restriction section (always include if there are projects)
|
|
292
|
+
if (active.projects && active.projects.length > 0) {
|
|
293
|
+
lines.push('### Restriction');
|
|
294
|
+
lines.push('');
|
|
295
|
+
lines.push('You are working within a scoped workstream. You may ONLY access files within these directories:');
|
|
296
|
+
lines.push('');
|
|
297
|
+
for (const p of active.projects) {
|
|
298
|
+
const displayPath = p.replace(process.env.HOME || '', '~');
|
|
299
|
+
lines.push(`- ${displayPath}`);
|
|
300
|
+
}
|
|
301
|
+
lines.push('');
|
|
302
|
+
lines.push('**Do NOT read, write, search, or reference files outside these directories.**');
|
|
303
|
+
lines.push('');
|
|
269
304
|
}
|
|
270
305
|
|
|
271
|
-
|
|
272
|
-
const
|
|
306
|
+
// Context section (user-defined context/rules)
|
|
307
|
+
const context = active.context || active.rules || '';
|
|
308
|
+
if (context.trim()) {
|
|
309
|
+
lines.push('### Context');
|
|
310
|
+
lines.push('');
|
|
311
|
+
lines.push(context.trim());
|
|
312
|
+
lines.push('');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Repositories table
|
|
316
|
+
if (active.projects && active.projects.length > 0) {
|
|
317
|
+
lines.push('### Repositories in this Workstream');
|
|
318
|
+
lines.push('');
|
|
319
|
+
lines.push('| Repository | Path |');
|
|
320
|
+
lines.push('|------------|------|');
|
|
321
|
+
for (const p of active.projects) {
|
|
322
|
+
const name = path.basename(p);
|
|
323
|
+
const displayPath = p.replace(process.env.HOME || '', '~');
|
|
324
|
+
lines.push(`| ${name} | ${displayPath} |`);
|
|
325
|
+
}
|
|
326
|
+
lines.push('');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const output = lines.join('\n');
|
|
273
330
|
|
|
274
331
|
if (!silent) {
|
|
275
332
|
console.log(output);
|
|
@@ -315,6 +372,76 @@ function workstreamGet(installDir, id) {
|
|
|
315
372
|
return data.workstreams.find(w => w.id === id) || null;
|
|
316
373
|
}
|
|
317
374
|
|
|
375
|
+
/**
|
|
376
|
+
* Count how many workstreams include a given project path
|
|
377
|
+
*/
|
|
378
|
+
function countWorkstreamsForProject(installDir, projectPath) {
|
|
379
|
+
const data = loadWorkstreams(installDir);
|
|
380
|
+
const absPath = path.resolve(projectPath.replace(/^~/, process.env.HOME || ''));
|
|
381
|
+
return data.workstreams.filter(ws =>
|
|
382
|
+
ws.projects && ws.projects.includes(absPath)
|
|
383
|
+
).length;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Install the pre-prompt hook for workstream injection
|
|
388
|
+
*/
|
|
389
|
+
function workstreamInstallHook() {
|
|
390
|
+
const hookDir = path.join(process.env.HOME || '', '.claude', 'hooks');
|
|
391
|
+
const hookPath = path.join(hookDir, 'pre-prompt.sh');
|
|
392
|
+
|
|
393
|
+
// Ensure hooks directory exists
|
|
394
|
+
if (!fs.existsSync(hookDir)) {
|
|
395
|
+
fs.mkdirSync(hookDir, { recursive: true });
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const hookContent = `#!/bin/bash
|
|
399
|
+
# Claude Code pre-prompt hook for workstream injection
|
|
400
|
+
# Installed by claude-config
|
|
401
|
+
|
|
402
|
+
# Check for active workstream via env var or file
|
|
403
|
+
if [ -n "$CLAUDE_WORKSTREAM" ] || claude-config workstream active >/dev/null 2>&1; then
|
|
404
|
+
claude-config workstream inject --silent
|
|
405
|
+
fi
|
|
406
|
+
`;
|
|
407
|
+
|
|
408
|
+
// Check if hook already exists
|
|
409
|
+
if (fs.existsSync(hookPath)) {
|
|
410
|
+
const existing = fs.readFileSync(hookPath, 'utf8');
|
|
411
|
+
if (existing.includes('claude-config workstream inject')) {
|
|
412
|
+
console.log('✓ Workstream hook already installed');
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
// Append to existing hook
|
|
416
|
+
fs.appendFileSync(hookPath, '\n' + hookContent);
|
|
417
|
+
console.log('✓ Appended workstream injection to existing pre-prompt hook');
|
|
418
|
+
} else {
|
|
419
|
+
fs.writeFileSync(hookPath, hookContent);
|
|
420
|
+
fs.chmodSync(hookPath, '755');
|
|
421
|
+
console.log('✓ Installed pre-prompt hook at ~/.claude/hooks/pre-prompt.sh');
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
console.log('\nWorkstream injection is now active. When a workstream is active,');
|
|
425
|
+
console.log('Claude will see the restriction and context at the start of each prompt.');
|
|
426
|
+
console.log('\nTo activate a workstream for this session:');
|
|
427
|
+
console.log(' export CLAUDE_WORKSTREAM=<name-or-id>');
|
|
428
|
+
console.log('\nOr use the global active workstream:');
|
|
429
|
+
console.log(' claude-config workstream use <name>');
|
|
430
|
+
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Deactivate workstream (output shell command to unset env var)
|
|
436
|
+
*/
|
|
437
|
+
function workstreamDeactivate() {
|
|
438
|
+
console.log('To deactivate the workstream for this session, run:');
|
|
439
|
+
console.log(' unset CLAUDE_WORKSTREAM');
|
|
440
|
+
console.log('\nOr to clear the global active workstream:');
|
|
441
|
+
console.log(' claude-config workstream use --clear');
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
|
|
318
445
|
module.exports = {
|
|
319
446
|
getWorkstreamsPath,
|
|
320
447
|
loadWorkstreams,
|
|
@@ -330,4 +457,8 @@ module.exports = {
|
|
|
330
457
|
workstreamInject,
|
|
331
458
|
workstreamDetect,
|
|
332
459
|
workstreamGet,
|
|
460
|
+
getActiveWorkstream,
|
|
461
|
+
countWorkstreamsForProject,
|
|
462
|
+
workstreamInstallHook,
|
|
463
|
+
workstreamDeactivate,
|
|
333
464
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@regression-io/claude-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.3",
|
|
4
4
|
"description": "Configuration management UI for Claude Code and Antigravity - manage MCPs, rules, commands, memory, and project folders",
|
|
5
5
|
"author": "regression.io",
|
|
6
6
|
"main": "config-loader.js",
|