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.
Files changed (88) hide show
  1. package/.claude/rules/mandatory-delegation.md +1 -1
  2. package/.codex/delegation-matrix.json +4 -3
  3. package/.codex/delegation-parity.json +4 -3
  4. package/.codex/instructions.md +2 -2
  5. package/.sinapse-ai/constitution.md +2 -2
  6. package/.sinapse-ai/core/doctor/checks/git-hooks.js +76 -10
  7. package/.sinapse-ai/core/execution/subagent-dispatcher.js +1 -1
  8. package/.sinapse-ai/core/synapse/engine.js +15 -0
  9. package/.sinapse-ai/data/entity-registry.yaml +13 -13
  10. package/.sinapse-ai/development/agents/snps-orqx.md +4 -4
  11. package/.sinapse-ai/git-hooks/lib/secret-scanner-core.js +76 -4
  12. package/.sinapse-ai/git-hooks/pre-push +7 -1
  13. package/.sinapse-ai/install-manifest.yaml +9 -9
  14. package/AGENTS.md +2 -2
  15. package/CHANGELOG.md +1247 -0
  16. package/bin/commands/uninstall.js +2 -2
  17. package/bin/utils/secret-scanner-core.js +76 -4
  18. package/docs/agent-reference-guide.md +1 -1
  19. package/docs/framework/architecture-overview.md +4 -4
  20. package/docs/framework/guiding-principles.md +9 -9
  21. package/docs/getting-started.md +1 -1
  22. package/docs/guides/agent-reference.md +1 -1
  23. package/docs/guides/codex-config.md +4 -5
  24. package/docs/pt/architecture/sub-orqx-pattern.md +20 -18
  25. package/package.json +8 -2
  26. package/packages/installer/src/installer/git-hooks-installer.js +3 -1
  27. package/packages/installer/src/wizard/ide-config-generator.js +9 -1
  28. package/packages/installer/src/wizard/index.js +3 -4
  29. package/scripts/regenerate-orqx-stubs.ps1 +0 -1
  30. package/scripts/sync-counts.js +10 -2
  31. package/scripts/sync-squad-yaml-components.js +108 -6
  32. package/scripts/validate-squad-orqx.js +19 -9
  33. package/sinapse/agents/sinapse-orqx.md +4 -4
  34. package/sinapse/agents/snps-orqx.md +4 -4
  35. package/sinapse/knowledge-base/routing-catalog.md +1 -1
  36. package/sinapse/tasks/diagnose-and-route.md +1 -1
  37. package/sinapse/tasks/squad-status-report.md +1 -1
  38. package/squads/claude-code-mastery/agents/claude-mastery-chief.md +1 -1
  39. package/squads/claude-code-mastery/agents/hooks-architect.md +60 -68
  40. package/squads/claude-code-mastery/knowledge-base/swarm-orchestration-patterns.md +1 -1
  41. package/squads/claude-code-mastery/tasks/audit-setup.md +1 -1
  42. package/squads/claude-code-mastery/workflows/optimization-cycle.yaml +4 -4
  43. package/squads/claude-code-mastery/workflows/project-setup-cycle.yaml +4 -4
  44. package/squads/squad-animations/README.md +1 -1
  45. package/squads/squad-cloning/README.md +1 -1
  46. package/squads/squad-commercial/README.md +1 -1
  47. package/squads/squad-content/README.md +1 -1
  48. package/squads/squad-copy/README.md +1 -1
  49. package/squads/squad-council/README.md +1 -1
  50. package/squads/squad-courses/README.md +1 -1
  51. package/squads/squad-cybersecurity/README.md +1 -1
  52. package/squads/squad-design/README.md +1 -1
  53. package/squads/squad-finance/README.md +1 -1
  54. package/squads/squad-growth/README.md +1 -1
  55. package/squads/squad-paidmedia/README.md +1 -1
  56. package/squads/squad-product/README.md +1 -1
  57. package/squads/squad-research/README.md +1 -1
  58. package/squads/squad-storytelling/README.md +1 -1
  59. package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +0 -265
  60. package/.sinapse-ai/core/permissions/__tests__/permission-mode.test.js +0 -293
  61. package/.sinapse-ai/infrastructure/tests/project-status-loader.test.js +0 -569
  62. package/.sinapse-ai/infrastructure/tests/regression-suite-v2.md +0 -622
  63. package/.sinapse-ai/infrastructure/tests/validate-module.js +0 -98
  64. package/.sinapse-ai/infrastructure/tests/worktree-manager.test.js +0 -620
  65. package/.sinapse-ai/workflow-intelligence/__tests__/confidence-scorer.test.js +0 -335
  66. package/.sinapse-ai/workflow-intelligence/__tests__/integration.test.js +0 -340
  67. package/.sinapse-ai/workflow-intelligence/__tests__/suggestion-engine.test.js +0 -438
  68. package/.sinapse-ai/workflow-intelligence/__tests__/wave-analyzer.test.js +0 -448
  69. package/.sinapse-ai/workflow-intelligence/__tests__/workflow-registry.test.js +0 -303
  70. package/packages/installer/src/__tests__/performance-benchmark.js +0 -383
  71. package/packages/installer/tests/integration/environment-configuration.test.js +0 -332
  72. package/packages/installer/tests/integration/wizard-detection.test.js +0 -352
  73. package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +0 -402
  74. package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +0 -193
  75. package/packages/installer/tests/unit/config-validator.test.js +0 -315
  76. package/packages/installer/tests/unit/detection/detect-project-type.test.js +0 -539
  77. package/packages/installer/tests/unit/doctor/doctor-checks.test.js +0 -675
  78. package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +0 -192
  79. package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +0 -192
  80. package/packages/installer/tests/unit/env-template.test.js +0 -187
  81. package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +0 -310
  82. package/packages/installer/tests/unit/git-hooks-installer.test.js +0 -262
  83. package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +0 -231
  84. package/packages/installer/tests/unit/merger/env-merger.test.js +0 -191
  85. package/packages/installer/tests/unit/merger/markdown-merger.test.js +0 -262
  86. package/packages/installer/tests/unit/merger/strategies.test.js +0 -154
  87. package/packages/installer/tests/unit/merger/yaml-merger.test.js +0 -328
  88. 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
-