sumulige-claude 1.5.1 → 1.5.2
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/.claude/hooks/hook-registry.json +0 -15
- package/.claude/rules/coding-style.md +18 -7
- package/.claude/rules/hooks.md +15 -4
- package/.claude/rules/performance.md +15 -5
- package/.claude/rules/security.md +140 -4
- package/.claude/rules/testing.md +138 -9
- package/.claude/rules/web-design-standard.md +16 -5
- package/.claude/skills/algorithmic-art/metadata.yaml +28 -0
- package/.claude/skills/api-tester/SKILL.md +61 -0
- package/.claude/skills/api-tester/examples/basic.md +3 -0
- package/.claude/skills/api-tester/metadata.yaml +30 -0
- package/.claude/skills/api-tester/templates/default.md +3 -0
- package/.claude/skills/brand-guidelines/metadata.yaml +26 -0
- package/.claude/skills/canvas-design/metadata.yaml +27 -0
- package/.claude/skills/code-reviewer-123/SKILL.md +61 -0
- package/.claude/skills/code-reviewer-123/examples/basic.md +3 -0
- package/.claude/skills/code-reviewer-123/metadata.yaml +30 -0
- package/.claude/skills/code-reviewer-123/templates/default.md +3 -0
- package/.claude/skills/doc-coauthoring/metadata.yaml +27 -0
- package/.claude/skills/docx/metadata.yaml +30 -0
- package/.claude/skills/frontend-design/metadata.yaml +28 -0
- package/.claude/skills/internal-comms/metadata.yaml +28 -0
- package/.claude/skills/mcp-builder/metadata.yaml +26 -0
- package/.claude/skills/my-skill/SKILL.md +61 -0
- package/.claude/skills/my-skill/examples/basic.md +3 -0
- package/.claude/skills/my-skill/metadata.yaml +30 -0
- package/.claude/skills/my-skill/templates/default.md +3 -0
- package/.claude/skills/pdf/metadata.yaml +29 -0
- package/.claude/skills/pptx/metadata.yaml +29 -0
- package/.claude/skills/react-best-practices/metadata.yaml +26 -0
- package/.claude/skills/react-node-practices/SKILL.md +409 -0
- package/.claude/skills/react-node-practices/metadata.yaml +56 -0
- package/.claude/skills/skill-creator/metadata.yaml +25 -0
- package/.claude/skills/slack-gif-creator/metadata.yaml +28 -0
- package/.claude/skills/test-skill-name/SKILL.md +61 -0
- package/.claude/skills/test-skill-name/examples/basic.md +3 -0
- package/.claude/skills/test-skill-name/metadata.yaml +30 -0
- package/.claude/skills/test-skill-name/templates/default.md +3 -0
- package/.claude/skills/test-workflow/metadata.yaml +32 -0
- package/.claude/skills/theme-factory/metadata.yaml +26 -0
- package/.claude/skills/threejs-fundamentals/metadata.yaml +27 -0
- package/.claude/skills/web-artifacts-builder/metadata.yaml +30 -0
- package/.claude/skills/web-design-guidelines/metadata.yaml +26 -0
- package/.claude/skills/webapp-testing/metadata.yaml +26 -0
- package/.claude/skills/xlsx/metadata.yaml +29 -0
- package/LICENSE +21 -0
- package/cli.js +1 -1
- package/package.json +25 -3
- package/.claude/.kickoff-hint.txt +0 -52
- package/.claude/.sumulige-claude-version +0 -1
- package/.claude/.version +0 -1
- package/.claude/AGENTS.md +0 -42
- package/.claude/ANCHORS.md +0 -40
- package/.claude/CLAUDE.md +0 -138
- package/.claude/MEMORY.md +0 -69
- package/.claude/PROJECT_LOG.md +0 -101
- package/.claude/THINKING_CHAIN_GUIDE.md +0 -287
- package/.claude/USAGE.md +0 -175
- package/.claude/boris-optimizations.md +0 -167
- package/.claude/handoffs/INDEX.md +0 -21
- package/.claude/handoffs/LATEST.md +0 -76
- package/.claude/handoffs/handoff_2026-01-22T13-07-04-757Z.md +0 -76
- package/.claude/quality-gate.json +0 -82
- package/.claude/rag/skill-index.json +0 -135
- package/.claude/settings.json +0 -99
- package/.claude/settings.local.json +0 -175
- package/.claude/templates/PROJECT_KICKOFF.md +0 -89
- package/.claude/templates/PROJECT_PROPOSAL.md +0 -227
- package/.claude/templates/TASK_PLAN.md +0 -121
- package/.claude/templates/hooks/README.md +0 -302
- package/.claude/templates/hooks/hook.sh.template +0 -94
- package/.claude/templates/hooks/user-prompt-submit.cjs.template +0 -116
- package/.claude/templates/hooks/user-response-submit.cjs.template +0 -94
- package/.claude/templates/hooks/validate.js +0 -173
- package/.claude/templates/tasks/develop.md +0 -69
- package/.claude/templates/tasks/research.md +0 -64
- package/.claude/templates/tasks/test.md +0 -96
- package/.claude/thinking-routes/.last-sync +0 -1
- package/.claude/thinking-routes/QUICKREF.md +0 -98
- package/.claude/workflow/document-scanner.js +0 -426
- package/.claude/workflow/knowledge-engine.js +0 -941
- package/.claude/workflow/notebooklm/browser.js +0 -1028
- package/.claude/workflow/phases/phase1-research.js +0 -578
- package/.claude/workflow/phases/phase1-research.ts +0 -465
- package/.claude/workflow/phases/phase2-approve.js +0 -722
- package/.claude/workflow/phases/phase3-plan.js +0 -1200
- package/.claude/workflow/phases/phase4-develop.js +0 -894
- package/.claude/workflow/search-cache.js +0 -230
- package/.claude/workflow/templates/approval.md +0 -315
- package/.claude/workflow/templates/development.md +0 -377
- package/.claude/workflow/templates/planning.md +0 -328
- package/.claude/workflow/templates/research.md +0 -250
- package/.claude/workflow/types.js +0 -37
- package/.claude/workflow/web-search.js +0 -278
- package/.claude-plugin/marketplace.json +0 -71
- package/.github/workflows/sync-skills.yml +0 -74
- package/.versionrc +0 -25
- package/AGENTS.md +0 -580
- package/CHANGELOG.md +0 -481
- package/CLAUDE-template.md +0 -114
- package/DEV_TOOLS_GUIDE.md +0 -190
- package/PROJECT_STRUCTURE.md +0 -266
- package/Q&A.md +0 -325
- package/config/defaults.json +0 -34
- package/config/official-skills.json +0 -183
- package/config/quality-gate.json +0 -67
- package/config/skill-categories.json +0 -40
- package/config/version-manifest.json +0 -85
- package/demos/power-3d-scatter.html +0 -683
- package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +0 -36
- package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +0 -36
- package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +0 -36
- package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +0 -36
- package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +0 -36
- package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +0 -36
- package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +0 -36
- package/development/knowledge-base/.index.clean.json +0 -1
- package/development/knowledge-base/.index.json +0 -486
- package/development/knowledge-base/test-best-practices.md +0 -29
- package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +0 -226
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +0 -345
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +0 -284
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +0 -14
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +0 -35
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +0 -34
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +0 -5
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +0 -60
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +0 -25
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +0 -70
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +0 -48
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +0 -20
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +0 -21
- package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +0 -226
- package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +0 -345
- package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +0 -284
- package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +0 -14
- package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +0 -178
- package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +0 -377
- package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +0 -442
- package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +0 -800
- package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +0 -625
- package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +0 -830
- package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +0 -957
- package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +0 -381
- package/development/todos/.state.json +0 -19
- package/development/todos/INDEX.md +0 -63
- package/development/todos/active/_README.md +0 -49
- package/development/todos/archived/_README.md +0 -11
- package/development/todos/backlog/_README.md +0 -11
- package/development/todos/backlog/mcp-integration.md +0 -35
- package/development/todos/completed/_README.md +0 -11
- package/development/todos/completed/boris-optimizations.md +0 -39
- package/development/todos/completed/develop/local-knowledge-index.md +0 -85
- package/development/todos/completed/develop/todo-system.md +0 -47
- package/development/todos/completed/develop/web-search-integration.md +0 -83
- package/development/todos/completed/test/phase1-e2e-test.md +0 -103
- package/docs/DEVELOPMENT.md +0 -461
- package/docs/MARKETPLACE.md +0 -352
- package/docs/RELEASE.md +0 -93
- package/jest.config.js +0 -63
- package/lib/commands.js +0 -3588
- package/lib/config-manager.js +0 -441
- package/lib/config-schema.js +0 -408
- package/lib/config-validator.js +0 -330
- package/lib/config.js +0 -122
- package/lib/errors.js +0 -305
- package/lib/incremental-sync.js +0 -274
- package/lib/marketplace.js +0 -487
- package/lib/migrations.js +0 -154
- package/lib/permission-audit.js +0 -255
- package/lib/quality-gate.js +0 -431
- package/lib/quality-rules.js +0 -373
- package/lib/utils.js +0 -150
- package/lib/version-check.js +0 -169
- package/lib/version-manifest.js +0 -171
- package/project-paradigm.md +0 -313
- package/prompts/how-to-find.md +0 -163
- package/prompts/linus-architect.md +0 -71
- package/prompts/software-architect.md +0 -173
- package/prompts/web-designer.md +0 -249
- package/scripts/fix-hooks.mjs +0 -97
- package/scripts/sync-external.mjs +0 -298
- package/scripts/sync-to-home.sh +0 -108
- package/scripts/update-registry.mjs +0 -325
- package/sources.yaml +0 -83
- package/tests/README.md +0 -263
- package/tests/commands.test.js +0 -1086
- package/tests/config-manager.test.js +0 -677
- package/tests/config-schema.test.js +0 -425
- package/tests/config-validator.test.js +0 -436
- package/tests/config.test.js +0 -100
- package/tests/errors.test.js +0 -477
- package/tests/manual/phase1-e2e.sh +0 -389
- package/tests/manual/phase2-test-cases.md +0 -311
- package/tests/manual/phase3-test-cases.md +0 -309
- package/tests/manual/phase4-test-cases.md +0 -414
- package/tests/manual/test-cases.md +0 -417
- package/tests/marketplace.test.js +0 -420
- package/tests/migrations.test.js +0 -187
- package/tests/quality-gate.test.js +0 -679
- package/tests/quality-rules.test.js +0 -619
- package/tests/sync-external.test.js +0 -214
- package/tests/update-registry.test.js +0 -251
- package/tests/utils.test.js +0 -171
- package/tests/version-check.test.js +0 -75
- package/tests/web-search.test.js +0 -392
- package/thinkinglens-silent.md +0 -138
package/lib/config-manager.js
DELETED
|
@@ -1,441 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration Manager
|
|
3
|
-
*
|
|
4
|
-
* Advanced configuration management with:
|
|
5
|
-
* - Automatic backup before changes
|
|
6
|
-
* - Environment variable expansion
|
|
7
|
-
* - Rollback capability
|
|
8
|
-
* - Change history tracking
|
|
9
|
-
*
|
|
10
|
-
* @module lib/config-manager
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const fs = require('fs');
|
|
14
|
-
const path = require('path');
|
|
15
|
-
const crypto = require('crypto');
|
|
16
|
-
const { ConfigValidator } = require('./config-validator');
|
|
17
|
-
const { ConfigError } = require('./errors');
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Configuration Manager class
|
|
21
|
-
*/
|
|
22
|
-
class ConfigManager {
|
|
23
|
-
/**
|
|
24
|
-
* @param {Object} options - Manager options
|
|
25
|
-
* @param {string} options.configDir - Configuration directory
|
|
26
|
-
* @param {string} options.configFile - Configuration file path
|
|
27
|
-
* @param {string} options.backupDir - Backup directory
|
|
28
|
-
* @param {number} options.maxBackups - Maximum number of backups to keep
|
|
29
|
-
* @param {boolean} options.strict - Strict validation mode
|
|
30
|
-
*/
|
|
31
|
-
constructor(options = {}) {
|
|
32
|
-
this.configDir = options.configDir || path.join(process.env.HOME, '.claude');
|
|
33
|
-
this.configFile = options.configFile || path.join(this.configDir, 'config.json');
|
|
34
|
-
this.backupDir = options.backupDir || path.join(this.configDir, 'backups');
|
|
35
|
-
this.maxBackups = options.maxBackups || 10;
|
|
36
|
-
|
|
37
|
-
this.validator = new ConfigValidator({
|
|
38
|
-
strict: options.strict !== false
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
this._ensureDirectories();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Load configuration with validation
|
|
46
|
-
* @param {Object} options - Load options
|
|
47
|
-
* @returns {Object} Configuration object
|
|
48
|
-
*/
|
|
49
|
-
load(options = {}) {
|
|
50
|
-
const {
|
|
51
|
-
useDefaults = true,
|
|
52
|
-
expandEnv = true,
|
|
53
|
-
strict = null
|
|
54
|
-
} = options;
|
|
55
|
-
|
|
56
|
-
if (!fs.existsSync(this.configFile)) {
|
|
57
|
-
if (useDefaults) {
|
|
58
|
-
return this._getDefaults();
|
|
59
|
-
}
|
|
60
|
-
throw new ConfigError('Configuration file not found', [], [
|
|
61
|
-
`Create file at: ${this.configFile}`,
|
|
62
|
-
'Or run: smc init'
|
|
63
|
-
]);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const content = fs.readFileSync(this.configFile, 'utf-8');
|
|
67
|
-
let config;
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
config = JSON.parse(content);
|
|
71
|
-
} catch (e) {
|
|
72
|
-
const validation = this.validator.validateFile(this.configFile);
|
|
73
|
-
throw new ConfigError(
|
|
74
|
-
'Invalid JSON in configuration file',
|
|
75
|
-
validation.errors,
|
|
76
|
-
validation.fixes
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Expand environment variables
|
|
81
|
-
if (expandEnv) {
|
|
82
|
-
config = this._expandEnvVars(config);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Validate against schema
|
|
86
|
-
const effectiveStrict = strict !== null ? strict : this.validator.strict;
|
|
87
|
-
if (effectiveStrict) {
|
|
88
|
-
const validation = this.validator.validate(config);
|
|
89
|
-
if (!validation.valid) {
|
|
90
|
-
throw new ConfigError(
|
|
91
|
-
'Configuration validation failed',
|
|
92
|
-
validation.errors,
|
|
93
|
-
validation.fixes
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return config;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Save configuration with backup
|
|
103
|
-
* @param {Object} config - Configuration to save
|
|
104
|
-
* @param {Object} options - Save options
|
|
105
|
-
* @returns {Object} Save result
|
|
106
|
-
*/
|
|
107
|
-
save(config, options = {}) {
|
|
108
|
-
const { backup = true, validate = true } = options;
|
|
109
|
-
|
|
110
|
-
// Validate before saving
|
|
111
|
-
if (validate) {
|
|
112
|
-
const validation = this.validator.validate(config);
|
|
113
|
-
if (!validation.valid) {
|
|
114
|
-
throw new ConfigError(
|
|
115
|
-
'Cannot save invalid configuration',
|
|
116
|
-
validation.errors,
|
|
117
|
-
validation.fixes
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Create backup
|
|
123
|
-
let backupPath = null;
|
|
124
|
-
if (backup && fs.existsSync(this.configFile)) {
|
|
125
|
-
backupPath = this._createBackup();
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Write new config
|
|
129
|
-
const content = JSON.stringify(config, null, 2);
|
|
130
|
-
fs.writeFileSync(this.configFile, content, 'utf-8');
|
|
131
|
-
|
|
132
|
-
// Record change
|
|
133
|
-
this._recordChange('save', {
|
|
134
|
-
hash: this._hash(content),
|
|
135
|
-
backup: backupPath
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
return { success: true, backup: backupPath };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Rollback to previous configuration
|
|
143
|
-
* @param {string|null} version - Backup version (null for latest)
|
|
144
|
-
* @returns {Object} Rollback result
|
|
145
|
-
*/
|
|
146
|
-
rollback(version = null) {
|
|
147
|
-
const backups = this.listBackups();
|
|
148
|
-
|
|
149
|
-
if (backups.length === 0) {
|
|
150
|
-
throw new ConfigError('No backups available', [], [
|
|
151
|
-
'Backups are created when you save config changes',
|
|
152
|
-
'Enable backup option when saving'
|
|
153
|
-
]);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const targetBackup = version
|
|
157
|
-
? backups.find(b => b.version === version)
|
|
158
|
-
: backups[0];
|
|
159
|
-
|
|
160
|
-
if (!targetBackup) {
|
|
161
|
-
throw new ConfigError(`Backup version ${version} not found`, [], [
|
|
162
|
-
'Available versions: ' + backups.map(b => b.version).join(', ')
|
|
163
|
-
]);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const backupPath = path.join(this.backupDir, targetBackup.file);
|
|
167
|
-
const content = fs.readFileSync(backupPath, 'utf-8');
|
|
168
|
-
|
|
169
|
-
// Create backup of current before rollback
|
|
170
|
-
if (fs.existsSync(this.configFile)) {
|
|
171
|
-
this._createBackup('pre-rollback');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
fs.writeFileSync(this.configFile, content, 'utf-8');
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
success: true,
|
|
178
|
-
restoredFrom: targetBackup.version,
|
|
179
|
-
timestamp: targetBackup.timestamp
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* List available backups
|
|
185
|
-
* @returns {Array} List of backup info objects
|
|
186
|
-
*/
|
|
187
|
-
listBackups() {
|
|
188
|
-
if (!fs.existsSync(this.backupDir)) {
|
|
189
|
-
return [];
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const files = fs.readdirSync(this.backupDir)
|
|
193
|
-
.filter(f => f.startsWith('config-') && f.endsWith('.json'))
|
|
194
|
-
.map(f => {
|
|
195
|
-
const stat = fs.statSync(path.join(this.backupDir, f));
|
|
196
|
-
return {
|
|
197
|
-
file: f,
|
|
198
|
-
version: f.replace('config-', '').replace('.json', ''),
|
|
199
|
-
timestamp: stat.mtime,
|
|
200
|
-
size: stat.size
|
|
201
|
-
};
|
|
202
|
-
})
|
|
203
|
-
.sort((a, b) => b.timestamp - a.timestamp)
|
|
204
|
-
.slice(0, this.maxBackups);
|
|
205
|
-
|
|
206
|
-
return files;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Get configuration diff
|
|
211
|
-
* @param {string|Object} left - Left config (file path or object)
|
|
212
|
-
* @param {string|Object} right - Right config (file path or object, null for current)
|
|
213
|
-
* @returns {Array} Array of change objects
|
|
214
|
-
*/
|
|
215
|
-
diff(left, right = null) {
|
|
216
|
-
const leftConfig = typeof left === 'string'
|
|
217
|
-
? JSON.parse(fs.readFileSync(left, 'utf-8'))
|
|
218
|
-
: left;
|
|
219
|
-
|
|
220
|
-
const rightConfig = right
|
|
221
|
-
? (typeof right === 'string'
|
|
222
|
-
? JSON.parse(fs.readFileSync(right, 'utf-8'))
|
|
223
|
-
: right)
|
|
224
|
-
: this.load({ validate: false });
|
|
225
|
-
|
|
226
|
-
return this._computeDiff(leftConfig, rightConfig);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Create backup of current config
|
|
231
|
-
* @param {string} suffix - Optional suffix for backup name
|
|
232
|
-
* @returns {string} Backup file path
|
|
233
|
-
*/
|
|
234
|
-
_createBackup(suffix = null) {
|
|
235
|
-
const timestamp = new Date().toISOString()
|
|
236
|
-
.replace(/[:.]/g, '-')
|
|
237
|
-
.replace('T', '_')
|
|
238
|
-
.split('.')[0];
|
|
239
|
-
|
|
240
|
-
const version = `${timestamp}${suffix ? '-' + suffix : ''}`;
|
|
241
|
-
const filename = `config-${version}.json`;
|
|
242
|
-
const backupPath = path.join(this.backupDir, filename);
|
|
243
|
-
|
|
244
|
-
fs.copyFileSync(this.configFile, backupPath);
|
|
245
|
-
|
|
246
|
-
// Clean old backups
|
|
247
|
-
this._cleanOldBackups();
|
|
248
|
-
|
|
249
|
-
return backupPath;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Remove old backups beyond maxBackups limit
|
|
254
|
-
*/
|
|
255
|
-
_cleanOldBackups() {
|
|
256
|
-
const backups = this.listBackups();
|
|
257
|
-
if (backups.length <= this.maxBackups) return;
|
|
258
|
-
|
|
259
|
-
const toRemove = backups.slice(this.maxBackups);
|
|
260
|
-
for (const backup of toRemove) {
|
|
261
|
-
fs.unlinkSync(path.join(this.backupDir, backup.file));
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Expand environment variables in config values
|
|
267
|
-
* Supports ${VAR} and ${VAR:default} syntax
|
|
268
|
-
* @param {*} value - Value to expand
|
|
269
|
-
* @returns {*} Expanded value
|
|
270
|
-
*/
|
|
271
|
-
_expandEnvVars(value) {
|
|
272
|
-
if (typeof value === 'string') {
|
|
273
|
-
return value.replace(/\$\{([^:}]+)(?::([^}]*))?\}/g, (_, name, defaultValue) => {
|
|
274
|
-
return process.env[name] !== undefined
|
|
275
|
-
? process.env[name]
|
|
276
|
-
: (defaultValue !== undefined ? defaultValue : '');
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (Array.isArray(value)) {
|
|
281
|
-
return value.map(item => this._expandEnvVars(item));
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (value && typeof value === 'object') {
|
|
285
|
-
const result = {};
|
|
286
|
-
for (const [key, val] of Object.entries(value)) {
|
|
287
|
-
result[key] = this._expandEnvVars(val);
|
|
288
|
-
}
|
|
289
|
-
return result;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return value;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Ensure necessary directories exist
|
|
297
|
-
*/
|
|
298
|
-
_ensureDirectories() {
|
|
299
|
-
[this.configDir, this.backupDir].forEach(dir => {
|
|
300
|
-
if (!fs.existsSync(dir)) {
|
|
301
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Get default configuration
|
|
308
|
-
* @returns {Object} Default config
|
|
309
|
-
*/
|
|
310
|
-
_getDefaults() {
|
|
311
|
-
const defaultsPath = path.join(__dirname, '../config/defaults.json');
|
|
312
|
-
if (fs.existsSync(defaultsPath)) {
|
|
313
|
-
return JSON.parse(fs.readFileSync(defaultsPath, 'utf-8'));
|
|
314
|
-
}
|
|
315
|
-
return {
|
|
316
|
-
version: '1.0.7',
|
|
317
|
-
model: 'claude-opus-4.5',
|
|
318
|
-
agents: {
|
|
319
|
-
conductor: { role: 'Task coordination and decomposition' },
|
|
320
|
-
architect: { role: 'Architecture design and decisions' },
|
|
321
|
-
builder: { role: 'Code implementation and testing' },
|
|
322
|
-
reviewer: { role: 'Code review and quality check' },
|
|
323
|
-
librarian: { role: 'Documentation and knowledge' }
|
|
324
|
-
},
|
|
325
|
-
skills: ['anthropics/skills', 'numman-ali/n-skills'],
|
|
326
|
-
hooks: { preTask: [], postTask: [] },
|
|
327
|
-
thinkingLens: {
|
|
328
|
-
enabled: true,
|
|
329
|
-
autoSync: true,
|
|
330
|
-
syncInterval: 20
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Compute SHA256 hash of content
|
|
337
|
-
* @param {string} content - Content to hash
|
|
338
|
-
* @returns {string} Hex hash
|
|
339
|
-
*/
|
|
340
|
-
_hash(content) {
|
|
341
|
-
return crypto.createHash('sha256').update(content).digest('hex');
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Record configuration change
|
|
346
|
-
* @param {string} action - Action type
|
|
347
|
-
* @param {Object} details - Change details
|
|
348
|
-
*/
|
|
349
|
-
_recordChange(action, details = {}) {
|
|
350
|
-
const historyPath = path.join(this.configDir, 'config-history.jsonl');
|
|
351
|
-
const entry = {
|
|
352
|
-
timestamp: new Date().toISOString(),
|
|
353
|
-
action,
|
|
354
|
-
...details
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
const line = JSON.stringify(entry) + '\n';
|
|
358
|
-
fs.appendFileSync(historyPath, line, 'utf-8');
|
|
359
|
-
|
|
360
|
-
// Keep only last 100 entries
|
|
361
|
-
try {
|
|
362
|
-
const history = fs.readFileSync(historyPath, 'utf-8').trim().split('\n');
|
|
363
|
-
if (history.length > 100) {
|
|
364
|
-
const recent = history.slice(-100);
|
|
365
|
-
fs.writeFileSync(historyPath, recent.join('\n') + '\n', 'utf-8');
|
|
366
|
-
}
|
|
367
|
-
} catch {
|
|
368
|
-
// First write, ignore
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Get configuration history
|
|
374
|
-
* @param {number} limit - Maximum entries to return
|
|
375
|
-
* @returns {Array} History entries
|
|
376
|
-
*/
|
|
377
|
-
getHistory(limit = 20) {
|
|
378
|
-
const historyPath = path.join(this.configDir, 'config-history.jsonl');
|
|
379
|
-
if (!fs.existsSync(historyPath)) {
|
|
380
|
-
return [];
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
const content = fs.readFileSync(historyPath, 'utf-8');
|
|
384
|
-
const lines = content.trim().split('\n');
|
|
385
|
-
const entries = lines
|
|
386
|
-
.slice(-limit)
|
|
387
|
-
.map(line => {
|
|
388
|
-
try {
|
|
389
|
-
return JSON.parse(line);
|
|
390
|
-
} catch {
|
|
391
|
-
return null;
|
|
392
|
-
}
|
|
393
|
-
})
|
|
394
|
-
.filter(Boolean);
|
|
395
|
-
|
|
396
|
-
return entries.reverse();
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Compute diff between two configs
|
|
401
|
-
* @param {Object} left - Left config
|
|
402
|
-
* @param {Object} right - Right config
|
|
403
|
-
* @returns {Array} Array of changes
|
|
404
|
-
*/
|
|
405
|
-
_computeDiff(left, right) {
|
|
406
|
-
const changes = [];
|
|
407
|
-
|
|
408
|
-
const compare = (l, r, path = '') => {
|
|
409
|
-
const allKeys = new Set([
|
|
410
|
-
...(l ? Object.keys(l) : []),
|
|
411
|
-
...(r ? Object.keys(r) : [])
|
|
412
|
-
]);
|
|
413
|
-
|
|
414
|
-
for (const key of allKeys) {
|
|
415
|
-
const keyPath = path ? `${path}.${key}` : key;
|
|
416
|
-
const lv = l?.[key];
|
|
417
|
-
const rv = r?.[key];
|
|
418
|
-
|
|
419
|
-
if (JSON.stringify(lv) !== JSON.stringify(rv)) {
|
|
420
|
-
if (typeof lv === 'object' && typeof rv === 'object' &&
|
|
421
|
-
lv !== null && rv !== null && !Array.isArray(lv) && !Array.isArray(rv)) {
|
|
422
|
-
compare(lv, rv, keyPath);
|
|
423
|
-
} else {
|
|
424
|
-
changes.push({
|
|
425
|
-
path: keyPath,
|
|
426
|
-
from: lv,
|
|
427
|
-
to: rv,
|
|
428
|
-
type: lv === undefined ? 'added' :
|
|
429
|
-
rv === undefined ? 'removed' : 'changed'
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
compare(left, right);
|
|
437
|
-
return changes;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
module.exports = { ConfigManager };
|