sinapse-ai 1.9.0 → 1.9.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/.claude/rules/mandatory-delegation.md +1 -1
- package/.codex/delegation-matrix.json +4 -3
- package/.codex/delegation-parity.json +4 -3
- package/.codex/instructions.md +2 -2
- package/.sinapse-ai/constitution.md +2 -2
- package/.sinapse-ai/core/doctor/checks/git-hooks.js +76 -10
- package/.sinapse-ai/core/execution/subagent-dispatcher.js +1 -1
- package/.sinapse-ai/core/synapse/engine.js +15 -0
- package/.sinapse-ai/data/entity-registry.yaml +13 -13
- package/.sinapse-ai/development/agents/snps-orqx.md +4 -4
- package/.sinapse-ai/git-hooks/lib/secret-scanner-core.js +76 -4
- package/.sinapse-ai/git-hooks/pre-push +7 -1
- package/.sinapse-ai/install-manifest.yaml +9 -9
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +1247 -0
- package/bin/commands/uninstall.js +2 -2
- package/bin/utils/secret-scanner-core.js +76 -4
- package/docs/agent-reference-guide.md +1 -1
- package/docs/framework/architecture-overview.md +4 -4
- package/docs/framework/guiding-principles.md +9 -9
- package/docs/getting-started.md +1 -1
- package/docs/guides/agent-reference.md +1 -1
- package/docs/guides/codex-config.md +4 -5
- package/docs/pt/architecture/sub-orqx-pattern.md +20 -18
- package/package.json +8 -2
- package/packages/installer/src/installer/git-hooks-installer.js +3 -1
- package/packages/installer/src/wizard/ide-config-generator.js +9 -1
- package/packages/installer/src/wizard/index.js +3 -4
- package/scripts/regenerate-orqx-stubs.ps1 +0 -1
- package/scripts/sync-counts.js +10 -2
- package/scripts/sync-squad-yaml-components.js +108 -6
- package/scripts/validate-squad-orqx.js +19 -9
- package/sinapse/agents/sinapse-orqx.md +4 -4
- package/sinapse/agents/snps-orqx.md +4 -4
- package/sinapse/knowledge-base/routing-catalog.md +1 -1
- package/sinapse/tasks/diagnose-and-route.md +1 -1
- package/sinapse/tasks/squad-status-report.md +1 -1
- package/squads/claude-code-mastery/agents/claude-mastery-chief.md +1 -1
- package/squads/claude-code-mastery/agents/hooks-architect.md +60 -68
- package/squads/claude-code-mastery/knowledge-base/swarm-orchestration-patterns.md +1 -1
- package/squads/claude-code-mastery/tasks/audit-setup.md +1 -1
- package/squads/claude-code-mastery/workflows/optimization-cycle.yaml +4 -4
- package/squads/claude-code-mastery/workflows/project-setup-cycle.yaml +4 -4
- package/squads/squad-animations/README.md +1 -1
- package/squads/squad-cloning/README.md +1 -1
- package/squads/squad-commercial/README.md +1 -1
- package/squads/squad-content/README.md +1 -1
- package/squads/squad-copy/README.md +1 -1
- package/squads/squad-council/README.md +1 -1
- package/squads/squad-courses/README.md +1 -1
- package/squads/squad-cybersecurity/README.md +1 -1
- package/squads/squad-design/README.md +1 -1
- package/squads/squad-finance/README.md +1 -1
- package/squads/squad-growth/README.md +1 -1
- package/squads/squad-paidmedia/README.md +1 -1
- package/squads/squad-product/README.md +1 -1
- package/squads/squad-research/README.md +1 -1
- package/squads/squad-storytelling/README.md +1 -1
- package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +0 -265
- package/.sinapse-ai/core/permissions/__tests__/permission-mode.test.js +0 -293
- package/.sinapse-ai/infrastructure/tests/project-status-loader.test.js +0 -569
- package/.sinapse-ai/infrastructure/tests/regression-suite-v2.md +0 -622
- package/.sinapse-ai/infrastructure/tests/validate-module.js +0 -98
- package/.sinapse-ai/infrastructure/tests/worktree-manager.test.js +0 -620
- package/.sinapse-ai/workflow-intelligence/__tests__/confidence-scorer.test.js +0 -335
- package/.sinapse-ai/workflow-intelligence/__tests__/integration.test.js +0 -340
- package/.sinapse-ai/workflow-intelligence/__tests__/suggestion-engine.test.js +0 -438
- package/.sinapse-ai/workflow-intelligence/__tests__/wave-analyzer.test.js +0 -448
- package/.sinapse-ai/workflow-intelligence/__tests__/workflow-registry.test.js +0 -303
- package/packages/installer/src/__tests__/performance-benchmark.js +0 -383
- package/packages/installer/tests/integration/environment-configuration.test.js +0 -332
- package/packages/installer/tests/integration/wizard-detection.test.js +0 -352
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +0 -402
- package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +0 -193
- package/packages/installer/tests/unit/config-validator.test.js +0 -315
- package/packages/installer/tests/unit/detection/detect-project-type.test.js +0 -539
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +0 -675
- package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +0 -192
- package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +0 -192
- package/packages/installer/tests/unit/env-template.test.js +0 -187
- package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +0 -310
- package/packages/installer/tests/unit/git-hooks-installer.test.js +0 -262
- package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +0 -231
- package/packages/installer/tests/unit/merger/env-merger.test.js +0 -191
- package/packages/installer/tests/unit/merger/markdown-merger.test.js +0 -262
- package/packages/installer/tests/unit/merger/strategies.test.js +0 -154
- package/packages/installer/tests/unit/merger/yaml-merger.test.js +0 -328
- package/packages/sinapse-install/tests/unit/chrome-brain.smoke.test.js +0 -66
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Verification Tests for Active SINAPSE Memory Modules
|
|
3
|
-
*
|
|
4
|
-
* Tests active memory modules:
|
|
5
|
-
* 1. Feedback Loop (gotchas-memory.js)
|
|
6
|
-
* 2. Custom Rules per Project (semantic-merge-engine.js)
|
|
7
|
-
*
|
|
8
|
-
* @created 2026-01-29
|
|
9
|
-
* @updated 2026-02-09 - Removed orphan modules tests (Story MIS-2)
|
|
10
|
-
* @updated 2026-06-15 - Status note added (see below)
|
|
11
|
-
*
|
|
12
|
-
* STATUS — IMPORTANT (do not mistake this for a passing CI gate):
|
|
13
|
-
* This is a `.verify.js` standalone script (run manually via `node`), NOT a
|
|
14
|
-
* Jest spec — jest.config.js only matches `*.test.js`/`*.spec.js`, so it never
|
|
15
|
-
* runs in CI. The "Feedback Loop" section below asserts the Story 9.4 surface
|
|
16
|
-
* (FeedbackType enum + trackUserFeedback / getAccuracyMetrics / getSuggestedRules)
|
|
17
|
-
* which is NOT yet implemented in gotchas-memory.js — those assertions are a
|
|
18
|
-
* forward spec for pending work, not a regression check. The Custom Rules
|
|
19
|
-
* section reflects shipped behavior. Do not "fix" this by inventing the feedback
|
|
20
|
-
* feature without its story (Constitution Art. IV — No Invention).
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
const path = require('path');
|
|
24
|
-
const fs = require('fs');
|
|
25
|
-
|
|
26
|
-
// Test helpers
|
|
27
|
-
const testResults = {
|
|
28
|
-
passed: 0,
|
|
29
|
-
failed: 0,
|
|
30
|
-
errors: [],
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
function test(name, fn) {
|
|
34
|
-
try {
|
|
35
|
-
fn();
|
|
36
|
-
console.log(` ✅ ${name}`);
|
|
37
|
-
testResults.passed++;
|
|
38
|
-
} catch (error) {
|
|
39
|
-
console.log(` ❌ ${name}`);
|
|
40
|
-
console.log(` Error: ${error.message}`);
|
|
41
|
-
testResults.failed++;
|
|
42
|
-
testResults.errors.push({ name, error: error.message });
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function assertEqual(actual, expected, message) {
|
|
47
|
-
if (actual !== expected) {
|
|
48
|
-
throw new Error(`${message}: expected ${expected}, got ${actual}`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function assertTrue(condition, message) {
|
|
53
|
-
if (!condition) {
|
|
54
|
-
throw new Error(message || 'Assertion failed');
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function assertDefined(value, message) {
|
|
59
|
-
if (value === undefined || value === null) {
|
|
60
|
-
throw new Error(message || 'Value is undefined or null');
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// ============================================================================
|
|
65
|
-
// TEST SUITE 1: FEEDBACK LOOP
|
|
66
|
-
// ============================================================================
|
|
67
|
-
|
|
68
|
-
console.log('\n🔄 Testing Feedback Loop...\n');
|
|
69
|
-
|
|
70
|
-
test('GotchasMemory module loads with FeedbackType', () => {
|
|
71
|
-
const { GotchasMemory, FeedbackType } = require('../gotchas-memory');
|
|
72
|
-
|
|
73
|
-
assertDefined(GotchasMemory, 'GotchasMemory should be defined');
|
|
74
|
-
assertDefined(FeedbackType, 'FeedbackType should be defined');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('FeedbackType has required types', () => {
|
|
78
|
-
const { FeedbackType } = require('../gotchas-memory');
|
|
79
|
-
|
|
80
|
-
assertEqual(FeedbackType.HELPFUL, 'helpful', 'HELPFUL type');
|
|
81
|
-
assertEqual(FeedbackType.NOT_HELPFUL, 'not_helpful', 'NOT_HELPFUL type');
|
|
82
|
-
assertEqual(FeedbackType.FALSE_POSITIVE, 'false_positive', 'FALSE_POSITIVE type');
|
|
83
|
-
assertEqual(FeedbackType.MISSED, 'missed', 'MISSED type');
|
|
84
|
-
assertEqual(FeedbackType.IMPROVED, 'improved', 'IMPROVED type');
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('GotchasMemory has trackUserFeedback method', () => {
|
|
88
|
-
const { GotchasMemory } = require('../gotchas-memory');
|
|
89
|
-
|
|
90
|
-
// GotchasMemory constructor takes rootPath as first arg (string)
|
|
91
|
-
const memory = new GotchasMemory(process.cwd());
|
|
92
|
-
assertDefined(memory.trackUserFeedback, 'trackUserFeedback method should exist');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('GotchasMemory has getAccuracyMetrics method', () => {
|
|
96
|
-
const { GotchasMemory } = require('../gotchas-memory');
|
|
97
|
-
|
|
98
|
-
const memory = new GotchasMemory(process.cwd());
|
|
99
|
-
assertDefined(memory.getAccuracyMetrics, 'getAccuracyMetrics method should exist');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test('GotchasMemory has getSuggestedRules method', () => {
|
|
103
|
-
const { GotchasMemory } = require('../gotchas-memory');
|
|
104
|
-
|
|
105
|
-
const memory = new GotchasMemory(process.cwd());
|
|
106
|
-
assertDefined(memory.getSuggestedRules, 'getSuggestedRules method should exist');
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('GotchasMemory can track feedback', () => {
|
|
110
|
-
const { GotchasMemory, FeedbackType } = require('../gotchas-memory');
|
|
111
|
-
|
|
112
|
-
const memory = new GotchasMemory(process.cwd());
|
|
113
|
-
const result = memory.trackUserFeedback({
|
|
114
|
-
gotchaId: 'test-gotcha-1',
|
|
115
|
-
feedbackType: FeedbackType.HELPFUL,
|
|
116
|
-
comment: 'Test feedback',
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
assertDefined(result, 'Result should be defined');
|
|
120
|
-
// Result structure may vary
|
|
121
|
-
assertTrue(typeof result === 'object', 'Result should be an object');
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test('GotchasMemory can get accuracy metrics', () => {
|
|
125
|
-
const { GotchasMemory } = require('../gotchas-memory');
|
|
126
|
-
|
|
127
|
-
const memory = new GotchasMemory(process.cwd());
|
|
128
|
-
const metrics = memory.getAccuracyMetrics();
|
|
129
|
-
|
|
130
|
-
assertDefined(metrics, 'Metrics should be defined');
|
|
131
|
-
assertTrue(typeof metrics === 'object', 'Metrics should be an object');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
// ============================================================================
|
|
135
|
-
// TEST SUITE 2: CUSTOM RULES PER PROJECT
|
|
136
|
-
// ============================================================================
|
|
137
|
-
|
|
138
|
-
console.log('\n⚙️ Testing Custom Rules per Project...\n');
|
|
139
|
-
|
|
140
|
-
test('SemanticMergeEngine loads CustomRulesLoader', () => {
|
|
141
|
-
const {
|
|
142
|
-
SemanticMergeEngine,
|
|
143
|
-
CustomRulesLoader,
|
|
144
|
-
} = require('../../execution/semantic-merge-engine');
|
|
145
|
-
|
|
146
|
-
assertDefined(SemanticMergeEngine, 'SemanticMergeEngine should be defined');
|
|
147
|
-
assertDefined(CustomRulesLoader, 'CustomRulesLoader should be defined');
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test('CustomRulesLoader instantiates correctly', () => {
|
|
151
|
-
const { CustomRulesLoader } = require('../../execution/semantic-merge-engine');
|
|
152
|
-
|
|
153
|
-
const loader = new CustomRulesLoader(process.cwd());
|
|
154
|
-
assertDefined(loader, 'Loader should instantiate');
|
|
155
|
-
assertDefined(loader.loadCustomRules, 'loadCustomRules method should exist');
|
|
156
|
-
assertDefined(loader.getMergedRules, 'getMergedRules method should exist');
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test('CustomRulesLoader has getDefaultRules method', () => {
|
|
160
|
-
const { CustomRulesLoader } = require('../../execution/semantic-merge-engine');
|
|
161
|
-
|
|
162
|
-
const loader = new CustomRulesLoader(process.cwd());
|
|
163
|
-
const defaults = loader.getDefaultRules();
|
|
164
|
-
|
|
165
|
-
assertDefined(defaults, 'Defaults should be defined');
|
|
166
|
-
assertDefined(defaults.compatibility, 'Defaults should have compatibility');
|
|
167
|
-
assertDefined(defaults.file_patterns, 'Defaults should have file_patterns');
|
|
168
|
-
assertDefined(defaults.languages, 'Defaults should have languages');
|
|
169
|
-
assertDefined(defaults.ai, 'Defaults should have ai config');
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
test('CustomRulesLoader can get merged rules', () => {
|
|
173
|
-
const { CustomRulesLoader } = require('../../execution/semantic-merge-engine');
|
|
174
|
-
|
|
175
|
-
const loader = new CustomRulesLoader(process.cwd());
|
|
176
|
-
const rules = loader.getMergedRules();
|
|
177
|
-
|
|
178
|
-
assertDefined(rules, 'Rules should be defined');
|
|
179
|
-
assertDefined(rules.file_patterns, 'Rules should have file_patterns');
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test('CustomRulesLoader can categorize files', () => {
|
|
183
|
-
const { CustomRulesLoader } = require('../../execution/semantic-merge-engine');
|
|
184
|
-
|
|
185
|
-
const loader = new CustomRulesLoader(process.cwd());
|
|
186
|
-
|
|
187
|
-
// Test matchesPattern directly
|
|
188
|
-
const skipPatterns = ['node_modules/**', '.git/**', '*.log'];
|
|
189
|
-
const matchesNodeModules = loader.matchesPattern('node_modules/package/index.js', skipPatterns);
|
|
190
|
-
assertTrue(matchesNodeModules, 'node_modules/package/index.js should match node_modules/**');
|
|
191
|
-
|
|
192
|
-
// Test default category
|
|
193
|
-
const defaultCategory = loader.getFileCategory('src/utils/helper.js');
|
|
194
|
-
// This depends on rules, but should be defined
|
|
195
|
-
assertDefined(defaultCategory, 'Category should be defined');
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
test('CustomRulesLoader has cache functionality', () => {
|
|
199
|
-
const { CustomRulesLoader } = require('../../execution/semantic-merge-engine');
|
|
200
|
-
|
|
201
|
-
const loader = new CustomRulesLoader(process.cwd());
|
|
202
|
-
|
|
203
|
-
// First call loads
|
|
204
|
-
loader.getMergedRules();
|
|
205
|
-
|
|
206
|
-
// Check cache is valid
|
|
207
|
-
const isValid = loader.isCacheValid();
|
|
208
|
-
assertTrue(isValid, 'Cache should be valid after loading');
|
|
209
|
-
|
|
210
|
-
// Clear cache
|
|
211
|
-
loader.clearCache();
|
|
212
|
-
const isInvalid = !loader.isCacheValid();
|
|
213
|
-
assertTrue(isInvalid, 'Cache should be invalid after clearing');
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
test('SemanticMergeEngine integrates with CustomRulesLoader', () => {
|
|
217
|
-
const { SemanticMergeEngine } = require('../../execution/semantic-merge-engine');
|
|
218
|
-
|
|
219
|
-
const engine = new SemanticMergeEngine({ rootPath: process.cwd() });
|
|
220
|
-
|
|
221
|
-
assertDefined(engine.rulesLoader, 'Engine should have rulesLoader');
|
|
222
|
-
assertDefined(engine.getRules, 'Engine should have getRules method');
|
|
223
|
-
assertDefined(engine.reloadRules, 'Engine should have reloadRules method');
|
|
224
|
-
assertDefined(engine.getFileCategory, 'Engine should have getFileCategory method');
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
test('SemanticMergeEngine can get rules', () => {
|
|
228
|
-
const { SemanticMergeEngine } = require('../../execution/semantic-merge-engine');
|
|
229
|
-
|
|
230
|
-
const engine = new SemanticMergeEngine({ rootPath: process.cwd() });
|
|
231
|
-
const rules = engine.getRules();
|
|
232
|
-
|
|
233
|
-
assertDefined(rules, 'Rules should be defined');
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
test('merge-rules.yaml exists in .sinapse', () => {
|
|
237
|
-
const rulesPath = path.join(process.cwd(), '.sinapse', 'merge-rules.yaml');
|
|
238
|
-
const exists = fs.existsSync(rulesPath);
|
|
239
|
-
|
|
240
|
-
assertTrue(exists, 'merge-rules.yaml should exist in .sinapse');
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
// ============================================================================
|
|
244
|
-
// TEST SUMMARY
|
|
245
|
-
// ============================================================================
|
|
246
|
-
|
|
247
|
-
console.log('\n' + '='.repeat(60));
|
|
248
|
-
console.log('TEST SUMMARY');
|
|
249
|
-
console.log('='.repeat(60));
|
|
250
|
-
console.log(`\n ✅ Passed: ${testResults.passed}`);
|
|
251
|
-
console.log(` ❌ Failed: ${testResults.failed}`);
|
|
252
|
-
console.log(` 📊 Total: ${testResults.passed + testResults.failed}`);
|
|
253
|
-
|
|
254
|
-
if (testResults.errors.length > 0) {
|
|
255
|
-
console.log('\n Errors:');
|
|
256
|
-
testResults.errors.forEach((e) => {
|
|
257
|
-
console.log(` - ${e.name}: ${e.error}`);
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
console.log('\n' + '='.repeat(60));
|
|
262
|
-
|
|
263
|
-
// Exit with error code if tests failed
|
|
264
|
-
process.exit(testResults.failed > 0 ? 1 : 0);
|
|
265
|
-
|
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Permission Mode Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for the permission mode system (Epic 6)
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { PermissionMode } = require('../permission-mode');
|
|
8
|
-
const { OperationGuard } = require('../operation-guard');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const fs = require('fs').promises;
|
|
11
|
-
const os = require('os');
|
|
12
|
-
|
|
13
|
-
describe('PermissionMode', () => {
|
|
14
|
-
let tempDir;
|
|
15
|
-
let mode;
|
|
16
|
-
|
|
17
|
-
beforeEach(async () => {
|
|
18
|
-
// Create temp directory for tests
|
|
19
|
-
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'sinapse-test-'));
|
|
20
|
-
await fs.mkdir(path.join(tempDir, '.sinapse'), { recursive: true });
|
|
21
|
-
mode = new PermissionMode(tempDir);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
afterEach(async () => {
|
|
25
|
-
// Cleanup temp directory
|
|
26
|
-
try {
|
|
27
|
-
await fs.rm(tempDir, { recursive: true });
|
|
28
|
-
} catch {
|
|
29
|
-
// Ignore cleanup errors
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe('load()', () => {
|
|
34
|
-
it('should default to "ask" mode when no config exists', async () => {
|
|
35
|
-
const result = await mode.load();
|
|
36
|
-
expect(result).toBe('ask');
|
|
37
|
-
expect(mode.currentMode).toBe('ask');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should load mode from config file', async () => {
|
|
41
|
-
await fs.writeFile(
|
|
42
|
-
path.join(tempDir, '.sinapse', 'config.yaml'),
|
|
43
|
-
'permissions:\n mode: auto\n',
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const result = await mode.load();
|
|
47
|
-
expect(result).toBe('auto');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should fallback to "ask" for invalid mode in config', async () => {
|
|
51
|
-
await fs.writeFile(
|
|
52
|
-
path.join(tempDir, '.sinapse', 'config.yaml'),
|
|
53
|
-
'permissions:\n mode: invalid_mode\n',
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
const result = await mode.load();
|
|
57
|
-
expect(result).toBe('ask');
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('setMode()', () => {
|
|
62
|
-
it('should set mode and persist to config', async () => {
|
|
63
|
-
const result = await mode.setMode('auto');
|
|
64
|
-
|
|
65
|
-
expect(result.mode).toBe('auto');
|
|
66
|
-
expect(mode.currentMode).toBe('auto');
|
|
67
|
-
|
|
68
|
-
// Verify persisted
|
|
69
|
-
const configContent = await fs.readFile(path.join(tempDir, '.sinapse', 'config.yaml'), 'utf-8');
|
|
70
|
-
expect(configContent).toContain('mode: auto');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should handle alias "yolo" for "auto"', async () => {
|
|
74
|
-
const result = await mode.setMode('yolo');
|
|
75
|
-
expect(result.mode).toBe('auto');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should handle alias "safe" for "explore"', async () => {
|
|
79
|
-
const result = await mode.setMode('safe');
|
|
80
|
-
expect(result.mode).toBe('explore');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should throw error for invalid mode', async () => {
|
|
84
|
-
await expect(mode.setMode('invalid')).rejects.toThrow('Invalid mode');
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe('getBadge()', () => {
|
|
89
|
-
it('should return correct badge for each mode', async () => {
|
|
90
|
-
mode.currentMode = 'explore';
|
|
91
|
-
expect(mode.getBadge()).toBe('[🔍 Explore]');
|
|
92
|
-
|
|
93
|
-
mode.currentMode = 'ask';
|
|
94
|
-
expect(mode.getBadge()).toBe('[⚠️ Ask]');
|
|
95
|
-
|
|
96
|
-
mode.currentMode = 'auto';
|
|
97
|
-
expect(mode.getBadge()).toBe('[⚡ Auto]');
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
describe('canPerform()', () => {
|
|
102
|
-
it('should allow all reads in all modes', () => {
|
|
103
|
-
for (const modeName of ['explore', 'ask', 'auto']) {
|
|
104
|
-
mode.currentMode = modeName;
|
|
105
|
-
const result = mode.canPerform('read');
|
|
106
|
-
expect(result.allowed).toBe(true);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should block writes in explore mode', () => {
|
|
111
|
-
mode.currentMode = 'explore';
|
|
112
|
-
const result = mode.canPerform('write');
|
|
113
|
-
expect(result.allowed).toBe(false);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('should require confirmation for writes in ask mode', () => {
|
|
117
|
-
mode.currentMode = 'ask';
|
|
118
|
-
const result = mode.canPerform('write');
|
|
119
|
-
expect(result.allowed).toBe('confirm');
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('should allow writes in auto mode', () => {
|
|
123
|
-
mode.currentMode = 'auto';
|
|
124
|
-
const result = mode.canPerform('write');
|
|
125
|
-
expect(result.allowed).toBe(true);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe('cycleMode()', () => {
|
|
130
|
-
it('should cycle through modes correctly', async () => {
|
|
131
|
-
mode.currentMode = 'explore';
|
|
132
|
-
mode._loaded = true;
|
|
133
|
-
|
|
134
|
-
let result = await mode.cycleMode();
|
|
135
|
-
expect(result.mode).toBe('ask');
|
|
136
|
-
|
|
137
|
-
result = await mode.cycleMode();
|
|
138
|
-
expect(result.mode).toBe('auto');
|
|
139
|
-
|
|
140
|
-
result = await mode.cycleMode();
|
|
141
|
-
expect(result.mode).toBe('explore');
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
describe('isAutonomous()', () => {
|
|
146
|
-
it('should return true only for auto mode', () => {
|
|
147
|
-
mode.currentMode = 'auto';
|
|
148
|
-
expect(mode.isAutonomous()).toBe(true);
|
|
149
|
-
|
|
150
|
-
mode.currentMode = 'ask';
|
|
151
|
-
expect(mode.isAutonomous()).toBe(false);
|
|
152
|
-
|
|
153
|
-
mode.currentMode = 'explore';
|
|
154
|
-
expect(mode.isAutonomous()).toBe(false);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
describe('isReadOnly()', () => {
|
|
159
|
-
it('should return true only for explore mode', () => {
|
|
160
|
-
mode.currentMode = 'explore';
|
|
161
|
-
expect(mode.isReadOnly()).toBe(true);
|
|
162
|
-
|
|
163
|
-
mode.currentMode = 'ask';
|
|
164
|
-
expect(mode.isReadOnly()).toBe(false);
|
|
165
|
-
|
|
166
|
-
mode.currentMode = 'auto';
|
|
167
|
-
expect(mode.isReadOnly()).toBe(false);
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
describe('OperationGuard', () => {
|
|
173
|
-
let mode;
|
|
174
|
-
let guard;
|
|
175
|
-
|
|
176
|
-
beforeEach(() => {
|
|
177
|
-
mode = new PermissionMode();
|
|
178
|
-
mode._loaded = true;
|
|
179
|
-
guard = new OperationGuard(mode);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
describe('classifyOperation()', () => {
|
|
183
|
-
it('should classify Read tool as read', () => {
|
|
184
|
-
expect(guard.classifyOperation('Read', {})).toBe('read');
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it('should classify Write tool as write', () => {
|
|
188
|
-
expect(guard.classifyOperation('Write', {})).toBe('write');
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it('should classify Edit tool as write', () => {
|
|
192
|
-
expect(guard.classifyOperation('Edit', {})).toBe('write');
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it('should classify Glob tool as read', () => {
|
|
196
|
-
expect(guard.classifyOperation('Glob', {})).toBe('read');
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should classify Grep tool as read', () => {
|
|
200
|
-
expect(guard.classifyOperation('Grep', {})).toBe('read');
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
describe('classifyBashCommand()', () => {
|
|
205
|
-
it('should classify git status as read', () => {
|
|
206
|
-
expect(guard.classifyBashCommand('git status')).toBe('read');
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('should classify ls as read', () => {
|
|
210
|
-
expect(guard.classifyBashCommand('ls -la')).toBe('read');
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it('should classify git push as write', () => {
|
|
214
|
-
expect(guard.classifyBashCommand('git push origin main')).toBe('write');
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('should classify rm -rf as delete', () => {
|
|
218
|
-
expect(guard.classifyBashCommand('rm -rf node_modules')).toBe('delete');
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('should classify git reset --hard as delete', () => {
|
|
222
|
-
expect(guard.classifyBashCommand('git reset --hard HEAD')).toBe('delete');
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('should classify npm install as write', () => {
|
|
226
|
-
expect(guard.classifyBashCommand('npm install lodash')).toBe('write');
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('should classify mkdir as write', () => {
|
|
230
|
-
expect(guard.classifyBashCommand('mkdir new_dir')).toBe('write');
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
describe('guard()', () => {
|
|
235
|
-
it('should allow read operations in all modes', async () => {
|
|
236
|
-
for (const modeName of ['explore', 'ask', 'auto']) {
|
|
237
|
-
mode.currentMode = modeName;
|
|
238
|
-
const result = await guard.guard('Read', { file_path: 'test.js' });
|
|
239
|
-
expect(result.proceed).toBe(true);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it('should block write in explore mode', async () => {
|
|
244
|
-
mode.currentMode = 'explore';
|
|
245
|
-
const result = await guard.guard('Write', { file_path: 'test.js' });
|
|
246
|
-
expect(result.proceed).toBe(false);
|
|
247
|
-
expect(result.blocked).toBe(true);
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it('should request confirmation for write in ask mode', async () => {
|
|
251
|
-
mode.currentMode = 'ask';
|
|
252
|
-
const result = await guard.guard('Write', { file_path: 'test.js' });
|
|
253
|
-
expect(result.proceed).toBe(false);
|
|
254
|
-
expect(result.needsConfirmation).toBe(true);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it('should allow write in auto mode', async () => {
|
|
258
|
-
mode.currentMode = 'auto';
|
|
259
|
-
const result = await guard.guard('Write', { file_path: 'test.js' });
|
|
260
|
-
expect(result.proceed).toBe(true);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it('should block destructive bash commands in explore mode', async () => {
|
|
264
|
-
mode.currentMode = 'explore';
|
|
265
|
-
const result = await guard.guard('Bash', { command: 'rm -rf temp' });
|
|
266
|
-
expect(result.proceed).toBe(false);
|
|
267
|
-
expect(result.blocked).toBe(true);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('should allow safe bash commands in explore mode', async () => {
|
|
271
|
-
mode.currentMode = 'explore';
|
|
272
|
-
const result = await guard.guard('Bash', { command: 'git status' });
|
|
273
|
-
expect(result.proceed).toBe(true);
|
|
274
|
-
});
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
describe('getStats()', () => {
|
|
278
|
-
it('should track operation statistics', async () => {
|
|
279
|
-
mode.currentMode = 'auto';
|
|
280
|
-
|
|
281
|
-
await guard.guard('Read', {});
|
|
282
|
-
await guard.guard('Write', {});
|
|
283
|
-
await guard.guard('Bash', { command: 'rm -rf x' });
|
|
284
|
-
|
|
285
|
-
const stats = guard.getStats();
|
|
286
|
-
expect(stats.total).toBe(3);
|
|
287
|
-
expect(stats.byOperation.read).toBe(1);
|
|
288
|
-
expect(stats.byOperation.write).toBe(1);
|
|
289
|
-
expect(stats.byOperation.delete).toBe(1);
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|