@regression-io/claude-config 0.37.16 → 0.37.19
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 +1 -0
- package/config-loader.js +2 -1
- package/lib/cli.js +9 -0
- package/lib/constants.js +1 -1
- package/lib/workstreams.js +44 -0
- package/package.json +1 -1
- package/ui/routes/updates.js +67 -16
package/README.md
CHANGED
|
@@ -96,6 +96,7 @@ claude-config workstream add <ws> <path> # Add project to workstream
|
|
|
96
96
|
claude-config workstream remove <ws> <path> # Remove project from workstream
|
|
97
97
|
claude-config workstream inject [--silent] # Output restriction + context (for hooks)
|
|
98
98
|
claude-config workstream detect [path] # Detect workstream for directory
|
|
99
|
+
claude-config workstream check-path <path> # Check if path is within workstream (exit 0/1)
|
|
99
100
|
claude-config workstream install-hook # Install pre-prompt hook for injection
|
|
100
101
|
```
|
|
101
102
|
|
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, getActiveWorkstream, countWorkstreamsForProject, workstreamInstallHook, workstreamDeactivate } = require('./lib/workstreams');
|
|
31
|
+
const { getWorkstreamsPath, loadWorkstreams, saveWorkstreams, workstreamList, workstreamCreate, workstreamUpdate, workstreamDelete, workstreamUse, workstreamActive, workstreamAddProject, workstreamRemoveProject, workstreamInject, workstreamDetect, workstreamGet, getActiveWorkstream, countWorkstreamsForProject, workstreamInstallHook, workstreamDeactivate, workstreamCheckPath } = 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');
|
|
@@ -132,6 +132,7 @@ class ClaudeConfigManager {
|
|
|
132
132
|
countWorkstreamsForProject(projectPath) { return countWorkstreamsForProject(this.installDir, projectPath); }
|
|
133
133
|
workstreamInstallHook() { return workstreamInstallHook(); }
|
|
134
134
|
workstreamDeactivate() { return workstreamDeactivate(); }
|
|
135
|
+
workstreamCheckPath(targetPath, silent) { return workstreamCheckPath(this.installDir, targetPath, silent); }
|
|
135
136
|
|
|
136
137
|
// Activity
|
|
137
138
|
getActivityPath() { return getActivityPath(this.installDir); }
|
package/lib/cli.js
CHANGED
|
@@ -122,6 +122,15 @@ function runCli(manager) {
|
|
|
122
122
|
manager.workstreamInstallHook();
|
|
123
123
|
} else if (args[1] === 'deactivate') {
|
|
124
124
|
manager.workstreamDeactivate();
|
|
125
|
+
} else if (args[1] === 'check-path') {
|
|
126
|
+
const targetPath = args[2];
|
|
127
|
+
if (!targetPath) {
|
|
128
|
+
console.error('Usage: claude-config workstream check-path <path>');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
const silent = args.includes('--silent') || args.includes('-s');
|
|
132
|
+
const isValid = manager.workstreamCheckPath(targetPath, silent);
|
|
133
|
+
process.exit(isValid ? 0 : 1);
|
|
125
134
|
} else {
|
|
126
135
|
manager.workstreamList();
|
|
127
136
|
}
|
package/lib/constants.js
CHANGED
package/lib/workstreams.js
CHANGED
|
@@ -442,6 +442,49 @@ function workstreamDeactivate() {
|
|
|
442
442
|
return true;
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
+
/**
|
|
446
|
+
* Check if a path is within the active workstream's directories
|
|
447
|
+
* Used by pre-tool-call hooks for enforcement
|
|
448
|
+
* Returns true if path is valid, false otherwise
|
|
449
|
+
*/
|
|
450
|
+
function workstreamCheckPath(installDir, targetPath, silent = false) {
|
|
451
|
+
const active = getActiveWorkstream(installDir);
|
|
452
|
+
|
|
453
|
+
// No active workstream = all paths allowed
|
|
454
|
+
if (!active) {
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// No projects in workstream = all paths allowed
|
|
459
|
+
if (!active.projects || active.projects.length === 0) {
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Resolve the target path
|
|
464
|
+
const absPath = path.resolve(targetPath.replace(/^~/, process.env.HOME || ''));
|
|
465
|
+
|
|
466
|
+
// Check if path is within any of the workstream's directories
|
|
467
|
+
const isWithin = active.projects.some(projectPath => {
|
|
468
|
+
// Path is within if it starts with the project path
|
|
469
|
+
// Handle both exact match and subdirectories
|
|
470
|
+
return absPath === projectPath || absPath.startsWith(projectPath + path.sep);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
if (!silent) {
|
|
474
|
+
if (isWithin) {
|
|
475
|
+
console.log(`✓ Path is within workstream "${active.name}"`);
|
|
476
|
+
} else {
|
|
477
|
+
console.error(`✗ Path is outside workstream "${active.name}"`);
|
|
478
|
+
console.error(` Allowed directories:`);
|
|
479
|
+
for (const p of active.projects) {
|
|
480
|
+
console.error(` - ${p.replace(process.env.HOME || '', '~')}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return isWithin;
|
|
486
|
+
}
|
|
487
|
+
|
|
445
488
|
module.exports = {
|
|
446
489
|
getWorkstreamsPath,
|
|
447
490
|
loadWorkstreams,
|
|
@@ -461,4 +504,5 @@ module.exports = {
|
|
|
461
504
|
countWorkstreamsForProject,
|
|
462
505
|
workstreamInstallHook,
|
|
463
506
|
workstreamDeactivate,
|
|
507
|
+
workstreamCheckPath,
|
|
464
508
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@regression-io/claude-config",
|
|
3
|
-
"version": "0.37.
|
|
3
|
+
"version": "0.37.19",
|
|
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",
|
package/ui/routes/updates.js
CHANGED
|
@@ -36,7 +36,8 @@ function getVersionFromFile(filePath) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
* Fetch npm version
|
|
39
|
+
* Fetch npm version and verify it's installable
|
|
40
|
+
* Returns version only if the tarball is accessible (CDN propagated)
|
|
40
41
|
*/
|
|
41
42
|
function fetchNpmVersion() {
|
|
42
43
|
return new Promise((resolve) => {
|
|
@@ -47,7 +48,37 @@ function fetchNpmVersion() {
|
|
|
47
48
|
res.on('end', () => {
|
|
48
49
|
try {
|
|
49
50
|
const parsed = JSON.parse(data);
|
|
50
|
-
|
|
51
|
+
const version = parsed.version;
|
|
52
|
+
const tarballUrl = parsed.dist?.tarball;
|
|
53
|
+
|
|
54
|
+
if (!version || !tarballUrl) {
|
|
55
|
+
resolve(null);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Verify tarball is accessible (HEAD request)
|
|
60
|
+
const tarball = new URL(tarballUrl);
|
|
61
|
+
const options = {
|
|
62
|
+
hostname: tarball.hostname,
|
|
63
|
+
path: tarball.pathname,
|
|
64
|
+
method: 'HEAD',
|
|
65
|
+
timeout: 5000
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const verifyReq = https.request(options, (verifyRes) => {
|
|
69
|
+
// 200 = accessible, anything else = not yet propagated
|
|
70
|
+
if (verifyRes.statusCode === 200) {
|
|
71
|
+
resolve(version);
|
|
72
|
+
} else {
|
|
73
|
+
resolve(null);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
verifyReq.on('error', () => resolve(null));
|
|
77
|
+
verifyReq.on('timeout', () => {
|
|
78
|
+
verifyReq.destroy();
|
|
79
|
+
resolve(null);
|
|
80
|
+
});
|
|
81
|
+
verifyReq.end();
|
|
51
82
|
} catch (e) {
|
|
52
83
|
resolve(null);
|
|
53
84
|
}
|
|
@@ -112,21 +143,41 @@ async function checkForUpdates(manager, dirname) {
|
|
|
112
143
|
* Perform npm update
|
|
113
144
|
*/
|
|
114
145
|
async function performNpmUpdate(targetVersion) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
146
|
+
const maxRetries = 3;
|
|
147
|
+
const retryDelayMs = 5000;
|
|
148
|
+
|
|
149
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
150
|
+
try {
|
|
151
|
+
// Use npm install @latest instead of npm update for reliable updates
|
|
152
|
+
execSync('npm install -g @regression-io/claude-config@latest', {
|
|
153
|
+
stdio: 'pipe',
|
|
154
|
+
timeout: 120000
|
|
155
|
+
});
|
|
121
156
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
157
|
+
return {
|
|
158
|
+
success: true,
|
|
159
|
+
updateMethod: 'npm',
|
|
160
|
+
newVersion: targetVersion || 'latest',
|
|
161
|
+
message: 'Updated via npm. Please restart the UI to use the new version.'
|
|
162
|
+
};
|
|
163
|
+
} catch (error) {
|
|
164
|
+
const isEtarget = error.message.includes('ETARGET') || error.message.includes('No matching version');
|
|
165
|
+
|
|
166
|
+
// Retry on ETARGET (CDN propagation delay) if we have retries left
|
|
167
|
+
if (isEtarget && attempt < maxRetries) {
|
|
168
|
+
await new Promise(resolve => setTimeout(resolve, retryDelayMs));
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Final attempt failed or non-retryable error
|
|
173
|
+
if (isEtarget) {
|
|
174
|
+
return {
|
|
175
|
+
success: false,
|
|
176
|
+
error: `Version not yet available on npm CDN. Please try again in a minute. (${error.message})`
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return { success: false, error: error.message };
|
|
180
|
+
}
|
|
130
181
|
}
|
|
131
182
|
}
|
|
132
183
|
|