@regression-io/claude-config 0.24.9 → 0.30.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/config-loader.js
CHANGED
|
@@ -19,7 +19,7 @@ const fs = require('fs');
|
|
|
19
19
|
const path = require('path');
|
|
20
20
|
const { execSync } = require('child_process');
|
|
21
21
|
|
|
22
|
-
const VERSION = '0.
|
|
22
|
+
const VERSION = '0.30.1';
|
|
23
23
|
|
|
24
24
|
// Tool-specific path configurations
|
|
25
25
|
const TOOL_PATHS = {
|
|
@@ -1803,6 +1803,329 @@ class ClaudeConfigManager {
|
|
|
1803
1803
|
console.log(`✓ Removed project: ${removed.name}`);
|
|
1804
1804
|
return true;
|
|
1805
1805
|
}
|
|
1806
|
+
|
|
1807
|
+
// ===========================================================================
|
|
1808
|
+
// WORKSTREAMS
|
|
1809
|
+
// ===========================================================================
|
|
1810
|
+
|
|
1811
|
+
/**
|
|
1812
|
+
* Get workstreams file path
|
|
1813
|
+
*/
|
|
1814
|
+
getWorkstreamsPath() {
|
|
1815
|
+
return path.join(this.installDir, 'workstreams.json');
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
/**
|
|
1819
|
+
* Load workstreams
|
|
1820
|
+
*/
|
|
1821
|
+
loadWorkstreams() {
|
|
1822
|
+
const wsPath = this.getWorkstreamsPath();
|
|
1823
|
+
if (fs.existsSync(wsPath)) {
|
|
1824
|
+
try {
|
|
1825
|
+
return JSON.parse(fs.readFileSync(wsPath, 'utf8'));
|
|
1826
|
+
} catch (e) {
|
|
1827
|
+
return { workstreams: [], activeId: null, lastUsedByProject: {} };
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
return { workstreams: [], activeId: null, lastUsedByProject: {} };
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
/**
|
|
1834
|
+
* Save workstreams
|
|
1835
|
+
*/
|
|
1836
|
+
saveWorkstreams(data) {
|
|
1837
|
+
const wsPath = this.getWorkstreamsPath();
|
|
1838
|
+
const dir = path.dirname(wsPath);
|
|
1839
|
+
if (!fs.existsSync(dir)) {
|
|
1840
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1841
|
+
}
|
|
1842
|
+
fs.writeFileSync(wsPath, JSON.stringify(data, null, 2) + '\n');
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
/**
|
|
1846
|
+
* List all workstreams
|
|
1847
|
+
*/
|
|
1848
|
+
workstreamList() {
|
|
1849
|
+
const data = this.loadWorkstreams();
|
|
1850
|
+
|
|
1851
|
+
if (data.workstreams.length === 0) {
|
|
1852
|
+
console.log('\nNo workstreams defined.');
|
|
1853
|
+
console.log('Create one with: claude-config workstream create "Name"\n');
|
|
1854
|
+
return data.workstreams;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
console.log('\n📋 Workstreams:\n');
|
|
1858
|
+
for (const ws of data.workstreams) {
|
|
1859
|
+
const active = ws.id === data.activeId ? '● ' : '○ ';
|
|
1860
|
+
console.log(`${active}${ws.name}`);
|
|
1861
|
+
if (ws.projects && ws.projects.length > 0) {
|
|
1862
|
+
console.log(` Projects: ${ws.projects.map(p => path.basename(p)).join(', ')}`);
|
|
1863
|
+
}
|
|
1864
|
+
if (ws.rules) {
|
|
1865
|
+
const preview = ws.rules.substring(0, 60).replace(/\n/g, ' ');
|
|
1866
|
+
console.log(` Rules: ${preview}${ws.rules.length > 60 ? '...' : ''}`);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
console.log('');
|
|
1870
|
+
return data.workstreams;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
/**
|
|
1874
|
+
* Create a new workstream
|
|
1875
|
+
*/
|
|
1876
|
+
workstreamCreate(name, projects = [], rules = '') {
|
|
1877
|
+
if (!name) {
|
|
1878
|
+
console.error('Usage: claude-config workstream create "Name"');
|
|
1879
|
+
return null;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
const data = this.loadWorkstreams();
|
|
1883
|
+
|
|
1884
|
+
// Check for duplicate name
|
|
1885
|
+
if (data.workstreams.some(ws => ws.name.toLowerCase() === name.toLowerCase())) {
|
|
1886
|
+
console.error(`Workstream "${name}" already exists`);
|
|
1887
|
+
return null;
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
const workstream = {
|
|
1891
|
+
id: Date.now().toString(36) + Math.random().toString(36).substr(2, 5),
|
|
1892
|
+
name,
|
|
1893
|
+
projects: projects.map(p => path.resolve(p.replace(/^~/, process.env.HOME || ''))),
|
|
1894
|
+
rules: rules || '',
|
|
1895
|
+
createdAt: new Date().toISOString(),
|
|
1896
|
+
updatedAt: new Date().toISOString()
|
|
1897
|
+
};
|
|
1898
|
+
|
|
1899
|
+
data.workstreams.push(workstream);
|
|
1900
|
+
|
|
1901
|
+
// If first workstream, make it active
|
|
1902
|
+
if (!data.activeId) {
|
|
1903
|
+
data.activeId = workstream.id;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
this.saveWorkstreams(data);
|
|
1907
|
+
console.log(`✓ Created workstream: ${name}`);
|
|
1908
|
+
return workstream;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
/**
|
|
1912
|
+
* Update a workstream
|
|
1913
|
+
*/
|
|
1914
|
+
workstreamUpdate(idOrName, updates) {
|
|
1915
|
+
const data = this.loadWorkstreams();
|
|
1916
|
+
const ws = data.workstreams.find(
|
|
1917
|
+
w => w.id === idOrName || w.name.toLowerCase() === idOrName.toLowerCase()
|
|
1918
|
+
);
|
|
1919
|
+
|
|
1920
|
+
if (!ws) {
|
|
1921
|
+
console.error(`Workstream not found: ${idOrName}`);
|
|
1922
|
+
return null;
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
if (updates.name !== undefined) ws.name = updates.name;
|
|
1926
|
+
if (updates.projects !== undefined) {
|
|
1927
|
+
ws.projects = updates.projects.map(p =>
|
|
1928
|
+
path.resolve(p.replace(/^~/, process.env.HOME || ''))
|
|
1929
|
+
);
|
|
1930
|
+
}
|
|
1931
|
+
if (updates.rules !== undefined) ws.rules = updates.rules;
|
|
1932
|
+
ws.updatedAt = new Date().toISOString();
|
|
1933
|
+
|
|
1934
|
+
this.saveWorkstreams(data);
|
|
1935
|
+
console.log(`✓ Updated workstream: ${ws.name}`);
|
|
1936
|
+
return ws;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
/**
|
|
1940
|
+
* Delete a workstream
|
|
1941
|
+
*/
|
|
1942
|
+
workstreamDelete(idOrName) {
|
|
1943
|
+
const data = this.loadWorkstreams();
|
|
1944
|
+
const idx = data.workstreams.findIndex(
|
|
1945
|
+
w => w.id === idOrName || w.name.toLowerCase() === idOrName.toLowerCase()
|
|
1946
|
+
);
|
|
1947
|
+
|
|
1948
|
+
if (idx === -1) {
|
|
1949
|
+
console.error(`Workstream not found: ${idOrName}`);
|
|
1950
|
+
return false;
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
const removed = data.workstreams.splice(idx, 1)[0];
|
|
1954
|
+
|
|
1955
|
+
// If removed active workstream, select first remaining
|
|
1956
|
+
if (data.activeId === removed.id) {
|
|
1957
|
+
data.activeId = data.workstreams[0]?.id || null;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
this.saveWorkstreams(data);
|
|
1961
|
+
console.log(`✓ Deleted workstream: ${removed.name}`);
|
|
1962
|
+
return true;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
/**
|
|
1966
|
+
* Set active workstream
|
|
1967
|
+
*/
|
|
1968
|
+
workstreamUse(idOrName) {
|
|
1969
|
+
const data = this.loadWorkstreams();
|
|
1970
|
+
|
|
1971
|
+
if (!idOrName) {
|
|
1972
|
+
// Show current active
|
|
1973
|
+
const active = data.workstreams.find(w => w.id === data.activeId);
|
|
1974
|
+
if (active) {
|
|
1975
|
+
console.log(`Active workstream: ${active.name}`);
|
|
1976
|
+
} else {
|
|
1977
|
+
console.log('No active workstream');
|
|
1978
|
+
}
|
|
1979
|
+
return active || null;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
const ws = data.workstreams.find(
|
|
1983
|
+
w => w.id === idOrName || w.name.toLowerCase() === idOrName.toLowerCase()
|
|
1984
|
+
);
|
|
1985
|
+
|
|
1986
|
+
if (!ws) {
|
|
1987
|
+
console.error(`Workstream not found: ${idOrName}`);
|
|
1988
|
+
return null;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
data.activeId = ws.id;
|
|
1992
|
+
this.saveWorkstreams(data);
|
|
1993
|
+
console.log(`✓ Switched to workstream: ${ws.name}`);
|
|
1994
|
+
return ws;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
/**
|
|
1998
|
+
* Get active workstream
|
|
1999
|
+
*/
|
|
2000
|
+
workstreamActive() {
|
|
2001
|
+
const data = this.loadWorkstreams();
|
|
2002
|
+
return data.workstreams.find(w => w.id === data.activeId) || null;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
/**
|
|
2006
|
+
* Add project to workstream
|
|
2007
|
+
*/
|
|
2008
|
+
workstreamAddProject(idOrName, projectPath) {
|
|
2009
|
+
const data = this.loadWorkstreams();
|
|
2010
|
+
const ws = data.workstreams.find(
|
|
2011
|
+
w => w.id === idOrName || w.name.toLowerCase() === idOrName.toLowerCase()
|
|
2012
|
+
);
|
|
2013
|
+
|
|
2014
|
+
if (!ws) {
|
|
2015
|
+
console.error(`Workstream not found: ${idOrName}`);
|
|
2016
|
+
return null;
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
const absPath = path.resolve(projectPath.replace(/^~/, process.env.HOME || ''));
|
|
2020
|
+
|
|
2021
|
+
if (!ws.projects.includes(absPath)) {
|
|
2022
|
+
ws.projects.push(absPath);
|
|
2023
|
+
ws.updatedAt = new Date().toISOString();
|
|
2024
|
+
this.saveWorkstreams(data);
|
|
2025
|
+
console.log(`✓ Added ${path.basename(absPath)} to ${ws.name}`);
|
|
2026
|
+
} else {
|
|
2027
|
+
console.log(`Project already in workstream: ${path.basename(absPath)}`);
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
return ws;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
/**
|
|
2034
|
+
* Remove project from workstream
|
|
2035
|
+
*/
|
|
2036
|
+
workstreamRemoveProject(idOrName, projectPath) {
|
|
2037
|
+
const data = this.loadWorkstreams();
|
|
2038
|
+
const ws = data.workstreams.find(
|
|
2039
|
+
w => w.id === idOrName || w.name.toLowerCase() === idOrName.toLowerCase()
|
|
2040
|
+
);
|
|
2041
|
+
|
|
2042
|
+
if (!ws) {
|
|
2043
|
+
console.error(`Workstream not found: ${idOrName}`);
|
|
2044
|
+
return null;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
const absPath = path.resolve(projectPath.replace(/^~/, process.env.HOME || ''));
|
|
2048
|
+
const idx = ws.projects.indexOf(absPath);
|
|
2049
|
+
|
|
2050
|
+
if (idx !== -1) {
|
|
2051
|
+
ws.projects.splice(idx, 1);
|
|
2052
|
+
ws.updatedAt = new Date().toISOString();
|
|
2053
|
+
this.saveWorkstreams(data);
|
|
2054
|
+
console.log(`✓ Removed ${path.basename(absPath)} from ${ws.name}`);
|
|
2055
|
+
} else {
|
|
2056
|
+
console.log(`Project not in workstream: ${path.basename(absPath)}`);
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
return ws;
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
/**
|
|
2063
|
+
* Inject active workstream rules into Claude context
|
|
2064
|
+
* Called by pre-prompt hook
|
|
2065
|
+
*/
|
|
2066
|
+
workstreamInject(silent = false) {
|
|
2067
|
+
const active = this.workstreamActive();
|
|
2068
|
+
|
|
2069
|
+
if (!active) {
|
|
2070
|
+
if (!silent) console.log('No active workstream');
|
|
2071
|
+
return null;
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
if (!active.rules || active.rules.trim() === '') {
|
|
2075
|
+
if (!silent) console.log(`Workstream "${active.name}" has no rules defined`);
|
|
2076
|
+
return null;
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
// Output rules to stdout for hook to capture
|
|
2080
|
+
const header = `## Active Workstream: ${active.name}\n\n`;
|
|
2081
|
+
const output = header + active.rules;
|
|
2082
|
+
|
|
2083
|
+
if (!silent) {
|
|
2084
|
+
console.log(output);
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
return output;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
/**
|
|
2091
|
+
* Detect workstream from current directory
|
|
2092
|
+
*/
|
|
2093
|
+
workstreamDetect(dir = process.cwd()) {
|
|
2094
|
+
const data = this.loadWorkstreams();
|
|
2095
|
+
const absDir = path.resolve(dir.replace(/^~/, process.env.HOME || ''));
|
|
2096
|
+
|
|
2097
|
+
// Find workstreams that contain this directory or a parent
|
|
2098
|
+
const matches = data.workstreams.filter(ws =>
|
|
2099
|
+
ws.projects.some(p => absDir.startsWith(p) || p.startsWith(absDir))
|
|
2100
|
+
);
|
|
2101
|
+
|
|
2102
|
+
if (matches.length === 0) {
|
|
2103
|
+
return null;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
if (matches.length === 1) {
|
|
2107
|
+
return matches[0];
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// Multiple matches - check lastUsedByProject
|
|
2111
|
+
if (data.lastUsedByProject && data.lastUsedByProject[absDir]) {
|
|
2112
|
+
const lastUsed = matches.find(ws => ws.id === data.lastUsedByProject[absDir]);
|
|
2113
|
+
if (lastUsed) return lastUsed;
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
// Return most recently updated
|
|
2117
|
+
return matches.sort((a, b) =>
|
|
2118
|
+
new Date(b.updatedAt) - new Date(a.updatedAt)
|
|
2119
|
+
)[0];
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
/**
|
|
2123
|
+
* Get workstream by ID
|
|
2124
|
+
*/
|
|
2125
|
+
workstreamGet(id) {
|
|
2126
|
+
const data = this.loadWorkstreams();
|
|
2127
|
+
return data.workstreams.find(w => w.id === id) || null;
|
|
2128
|
+
}
|
|
1806
2129
|
}
|
|
1807
2130
|
|
|
1808
2131
|
// =============================================================================
|
|
@@ -1903,6 +2226,42 @@ if (require.main === module) {
|
|
|
1903
2226
|
}
|
|
1904
2227
|
break;
|
|
1905
2228
|
|
|
2229
|
+
// Workstreams
|
|
2230
|
+
case 'workstream':
|
|
2231
|
+
case 'ws':
|
|
2232
|
+
if (args[1] === 'create' || args[1] === 'new') {
|
|
2233
|
+
manager.workstreamCreate(args[2]);
|
|
2234
|
+
} else if (args[1] === 'delete' || args[1] === 'rm') {
|
|
2235
|
+
manager.workstreamDelete(args[2]);
|
|
2236
|
+
} else if (args[1] === 'use' || args[1] === 'switch') {
|
|
2237
|
+
manager.workstreamUse(args[2]);
|
|
2238
|
+
} else if (args[1] === 'add-project') {
|
|
2239
|
+
manager.workstreamAddProject(args[2], args[3]);
|
|
2240
|
+
} else if (args[1] === 'remove-project') {
|
|
2241
|
+
manager.workstreamRemoveProject(args[2], args[3]);
|
|
2242
|
+
} else if (args[1] === 'inject') {
|
|
2243
|
+
const silent = args.includes('--silent') || args.includes('-s');
|
|
2244
|
+
manager.workstreamInject(silent);
|
|
2245
|
+
} else if (args[1] === 'detect') {
|
|
2246
|
+
const ws = manager.workstreamDetect(args[2] || process.cwd());
|
|
2247
|
+
if (ws) {
|
|
2248
|
+
console.log(ws.name);
|
|
2249
|
+
}
|
|
2250
|
+
} else if (args[1] === 'active') {
|
|
2251
|
+
const ws = manager.workstreamActive();
|
|
2252
|
+
if (ws) {
|
|
2253
|
+
console.log(`Active: ${ws.name}`);
|
|
2254
|
+
if (ws.projects.length > 0) {
|
|
2255
|
+
console.log(`Projects: ${ws.projects.map(p => path.basename(p)).join(', ')}`);
|
|
2256
|
+
}
|
|
2257
|
+
} else {
|
|
2258
|
+
console.log('No active workstream');
|
|
2259
|
+
}
|
|
2260
|
+
} else {
|
|
2261
|
+
manager.workstreamList();
|
|
2262
|
+
}
|
|
2263
|
+
break;
|
|
2264
|
+
|
|
1906
2265
|
// Maintenance
|
|
1907
2266
|
case 'update':
|
|
1908
2267
|
manager.update(args[1]);
|
|
@@ -1956,6 +2315,17 @@ Project Commands (for UI):
|
|
|
1956
2315
|
project add [path] --name X Add with custom display name
|
|
1957
2316
|
project remove <name|path> Remove project from registry
|
|
1958
2317
|
|
|
2318
|
+
Workstream Commands:
|
|
2319
|
+
workstream List all workstreams
|
|
2320
|
+
workstream create "Name" Create new workstream
|
|
2321
|
+
workstream delete <name> Delete workstream
|
|
2322
|
+
workstream use <name> Set active workstream
|
|
2323
|
+
workstream active Show current active workstream
|
|
2324
|
+
workstream add-project <ws> <path> Add project to workstream
|
|
2325
|
+
workstream remove-project <ws> <path> Remove project from workstream
|
|
2326
|
+
workstream inject [--silent] Output active workstream rules (for hooks)
|
|
2327
|
+
workstream detect [path] Detect workstream for directory
|
|
2328
|
+
|
|
1959
2329
|
Registry Commands:
|
|
1960
2330
|
registry-add <name> '<json>' Add MCP to global registry
|
|
1961
2331
|
registry-remove <name> Remove MCP from registry
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@regression-io/claude-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.1",
|
|
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",
|