@cleocode/core 2026.4.30 → 2026.4.31
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/dist/bootstrap.d.ts +35 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/code/index.d.ts +8 -4
- package/dist/code/index.d.ts.map +1 -1
- package/dist/code/parser.d.ts +22 -9
- package/dist/code/parser.d.ts.map +1 -1
- package/dist/hooks/handlers/session-hooks.d.ts +11 -4
- package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
- package/dist/hooks/payload-schemas.d.ts +6 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3859 -3008
- package/dist/index.js.map +4 -4
- package/dist/internal.d.ts +10 -7
- package/dist/internal.d.ts.map +1 -1
- package/dist/lib/tree-sitter-languages.d.ts +11 -7
- package/dist/lib/tree-sitter-languages.d.ts.map +1 -1
- package/dist/memory/auto-extract.d.ts +27 -15
- package/dist/memory/auto-extract.d.ts.map +1 -1
- package/dist/memory/brain-backfill.d.ts +59 -0
- package/dist/memory/brain-backfill.d.ts.map +1 -0
- package/dist/memory/brain-purge.d.ts +51 -0
- package/dist/memory/brain-purge.d.ts.map +1 -0
- package/dist/memory/brain-retrieval.d.ts.map +1 -1
- package/dist/memory/brain-search.d.ts.map +1 -1
- package/dist/memory/decisions.d.ts.map +1 -1
- package/dist/memory/engine-compat.d.ts +71 -0
- package/dist/memory/engine-compat.d.ts.map +1 -1
- package/dist/memory/graph-auto-populate.d.ts +65 -0
- package/dist/memory/graph-auto-populate.d.ts.map +1 -0
- package/dist/memory/graph-queries.d.ts +127 -0
- package/dist/memory/graph-queries.d.ts.map +1 -0
- package/dist/memory/learnings.d.ts +2 -0
- package/dist/memory/learnings.d.ts.map +1 -1
- package/dist/memory/patterns.d.ts +2 -0
- package/dist/memory/patterns.d.ts.map +1 -1
- package/dist/memory/quality-scoring.d.ts +90 -0
- package/dist/memory/quality-scoring.d.ts.map +1 -0
- package/dist/sessions/session-memory-bridge.d.ts +16 -10
- package/dist/sessions/session-memory-bridge.d.ts.map +1 -1
- package/dist/store/brain-accessor.d.ts +7 -0
- package/dist/store/brain-accessor.d.ts.map +1 -1
- package/dist/store/brain-schema.d.ts +185 -11
- package/dist/store/brain-schema.d.ts.map +1 -1
- package/dist/store/brain-sqlite.d.ts.map +1 -1
- package/dist/store/nexus-schema.d.ts +480 -2
- package/dist/store/nexus-schema.d.ts.map +1 -1
- package/dist/store/tasks-schema.d.ts +9 -9
- package/dist/store/validation-schemas.d.ts +44 -28
- package/dist/store/validation-schemas.d.ts.map +1 -1
- package/dist/system/dependencies.d.ts +43 -0
- package/dist/system/dependencies.d.ts.map +1 -0
- package/dist/system/health.d.ts +3 -0
- package/dist/system/health.d.ts.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/package.json +19 -19
- package/src/bootstrap.ts +124 -0
- package/src/code/index.ts +20 -4
- package/src/code/parser.ts +310 -110
- package/src/hooks/handlers/__tests__/hook-automation-e2e.test.ts +19 -45
- package/src/hooks/handlers/__tests__/session-hooks.test.ts +42 -54
- package/src/hooks/handlers/session-hooks.ts +11 -33
- package/src/index.ts +14 -0
- package/src/internal.ts +37 -7
- package/src/lib/tree-sitter-languages.ts +11 -7
- package/src/memory/__tests__/auto-extract.test.ts +20 -82
- package/src/memory/__tests__/embedding-pipeline.test.ts +389 -0
- package/src/memory/auto-extract.ts +34 -120
- package/src/memory/brain-backfill.ts +471 -0
- package/src/memory/brain-purge.ts +315 -0
- package/src/memory/brain-retrieval.ts +43 -2
- package/src/memory/brain-search.ts +23 -6
- package/src/memory/decisions.ts +76 -3
- package/src/memory/engine-compat.ts +168 -0
- package/src/memory/graph-auto-populate.ts +173 -0
- package/src/memory/graph-queries.ts +424 -0
- package/src/memory/learnings.ts +55 -7
- package/src/memory/patterns.ts +66 -13
- package/src/memory/quality-scoring.ts +173 -0
- package/src/sessions/__tests__/session-memory-bridge.test.ts +27 -49
- package/src/sessions/session-memory-bridge.ts +19 -47
- package/src/store/__tests__/brain-accessor-pageindex.test.ts +93 -22
- package/src/store/brain-accessor.ts +48 -2
- package/src/store/brain-schema.ts +165 -13
- package/src/store/brain-sqlite.ts +35 -0
- package/src/store/nexus-schema.ts +257 -3
- package/src/system/dependencies.ts +534 -0
- package/src/system/health.ts +126 -22
- package/src/tasks/complete.ts +40 -0
|
@@ -1,77 +1,65 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const mocks = vi.hoisted(() => ({
|
|
4
|
+
maybeRefreshMemoryBridge: vi.fn(),
|
|
5
|
+
gradeSession: vi.fn(),
|
|
6
|
+
loadConfig: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
vi.mock('../memory-bridge-refresh.js', () => ({
|
|
10
|
+
maybeRefreshMemoryBridge: mocks.maybeRefreshMemoryBridge,
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock('../../../sessions/session-grade.js', () => ({
|
|
14
|
+
gradeSession: mocks.gradeSession,
|
|
15
|
+
}));
|
|
4
16
|
|
|
5
|
-
vi.mock('../../../
|
|
6
|
-
|
|
17
|
+
vi.mock('../../../config.js', () => ({
|
|
18
|
+
loadConfig: mocks.loadConfig,
|
|
7
19
|
}));
|
|
8
20
|
|
|
9
21
|
import { handleSessionEnd, handleSessionStart } from '../session-hooks.js';
|
|
10
22
|
|
|
11
23
|
describe('session hook handlers', () => {
|
|
12
24
|
beforeEach(() => {
|
|
13
|
-
|
|
25
|
+
mocks.maybeRefreshMemoryBridge.mockReset();
|
|
26
|
+
mocks.maybeRefreshMemoryBridge.mockResolvedValue(undefined);
|
|
27
|
+
mocks.gradeSession.mockResolvedValue(undefined);
|
|
28
|
+
mocks.loadConfig.mockResolvedValue({ brain: { autoCapture: false } });
|
|
14
29
|
});
|
|
15
30
|
|
|
16
|
-
it('
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
31
|
+
it('handleSessionStart refreshes the memory bridge', async () => {
|
|
32
|
+
await handleSessionStart('/tmp/project', {
|
|
33
|
+
sessionId: 'ses-1',
|
|
34
|
+
timestamp: '2026-03-04T00:00:00.000Z',
|
|
35
|
+
name: 'Test Session',
|
|
36
|
+
scope: 'T5306',
|
|
37
|
+
});
|
|
20
38
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
sessionId: 'ses-1',
|
|
24
|
-
timestamp: '2026-03-04T00:00:00.000Z',
|
|
25
|
-
name: 'Test Session',
|
|
26
|
-
scope: 'T5306',
|
|
27
|
-
}),
|
|
28
|
-
).resolves.toBeUndefined();
|
|
39
|
+
expect(mocks.maybeRefreshMemoryBridge).toHaveBeenCalledTimes(1);
|
|
40
|
+
expect(mocks.maybeRefreshMemoryBridge).toHaveBeenCalledWith('/tmp/project');
|
|
29
41
|
});
|
|
30
42
|
|
|
31
|
-
it('
|
|
32
|
-
|
|
43
|
+
it('handleSessionEnd refreshes the memory bridge', async () => {
|
|
44
|
+
await handleSessionEnd('/tmp/project', {
|
|
45
|
+
sessionId: 'ses-2',
|
|
46
|
+
timestamp: '2026-03-04T00:30:00.000Z',
|
|
47
|
+
duration: 1800,
|
|
48
|
+
tasksCompleted: ['T5306', 'T5307'],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(mocks.maybeRefreshMemoryBridge).toHaveBeenCalledTimes(1);
|
|
52
|
+
expect(mocks.maybeRefreshMemoryBridge).toHaveBeenCalledWith('/tmp/project');
|
|
53
|
+
});
|
|
33
54
|
|
|
55
|
+
it('handleSessionEnd resolves normally with no tasks', async () => {
|
|
34
56
|
await expect(
|
|
35
57
|
handleSessionEnd('/tmp/project', {
|
|
36
|
-
sessionId: 'ses-
|
|
58
|
+
sessionId: 'ses-3',
|
|
37
59
|
timestamp: '2026-03-04T00:30:00.000Z',
|
|
38
|
-
duration:
|
|
60
|
+
duration: 900,
|
|
39
61
|
tasksCompleted: [],
|
|
40
62
|
}),
|
|
41
63
|
).resolves.toBeUndefined();
|
|
42
64
|
});
|
|
43
|
-
|
|
44
|
-
it('rethrows non-schema errors', async () => {
|
|
45
|
-
observeBrainMock.mockRejectedValue(new Error('database is locked'));
|
|
46
|
-
|
|
47
|
-
await expect(
|
|
48
|
-
handleSessionStart('/tmp/project', {
|
|
49
|
-
sessionId: 'ses-1',
|
|
50
|
-
timestamp: '2026-03-04T00:00:00.000Z',
|
|
51
|
-
name: 'Test Session',
|
|
52
|
-
scope: 'T5306',
|
|
53
|
-
}),
|
|
54
|
-
).rejects.toThrow('database is locked');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('records session context when observe succeeds', async () => {
|
|
58
|
-
observeBrainMock.mockResolvedValue(undefined);
|
|
59
|
-
|
|
60
|
-
await handleSessionEnd('/tmp/project', {
|
|
61
|
-
sessionId: 'ses-2',
|
|
62
|
-
timestamp: '2026-03-04T00:45:00.000Z',
|
|
63
|
-
duration: 2700,
|
|
64
|
-
tasksCompleted: ['T5306', 'T5307'],
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
expect(observeBrainMock).toHaveBeenCalledTimes(1);
|
|
68
|
-
expect(observeBrainMock).toHaveBeenCalledWith(
|
|
69
|
-
'/tmp/project',
|
|
70
|
-
expect.objectContaining({
|
|
71
|
-
title: 'Session end: ses-2',
|
|
72
|
-
type: 'change',
|
|
73
|
-
sourceSessionId: 'ses-2',
|
|
74
|
-
}),
|
|
75
|
-
);
|
|
76
|
-
});
|
|
77
65
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Session Hook Handlers - Phase 2D of T5237
|
|
3
3
|
*
|
|
4
|
-
* Handlers that capture session lifecycle events
|
|
4
|
+
* Handlers that capture session lifecycle events.
|
|
5
5
|
* Auto-registers on module load.
|
|
6
6
|
*
|
|
7
7
|
* T138: Triggers memory bridge refresh on session start and end.
|
|
@@ -10,65 +10,43 @@
|
|
|
10
10
|
* T5158: Auto-snapshots SQLite databases (tasks.db + brain.db) via
|
|
11
11
|
* VACUUM INTO on SessionEnd to preserve a recovery point now that
|
|
12
12
|
* the databases are no longer tracked in project git (ADR-013).
|
|
13
|
+
* T527: Removed duplicate session observeBrain writes — session data already
|
|
14
|
+
* lives in the sessions table; writing it again to brain_observations
|
|
15
|
+
* was pure noise.
|
|
13
16
|
*/
|
|
14
17
|
|
|
15
18
|
import { hooks } from '../registry.js';
|
|
16
19
|
import type { SessionEndPayload, SessionStartPayload } from '../types.js';
|
|
17
|
-
import { isMissingBrainSchemaError } from './handler-helpers.js';
|
|
18
20
|
import { maybeRefreshMemoryBridge } from './memory-bridge-refresh.js';
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
|
-
* Handle SessionStart -
|
|
23
|
+
* Handle SessionStart - refresh memory bridge on session start.
|
|
22
24
|
*
|
|
23
25
|
* T138: Refresh memory bridge on session start.
|
|
24
26
|
* T139: Regenerate bridge with session scope context.
|
|
27
|
+
* T527: Removed duplicate observeBrain write — session data is already
|
|
28
|
+
* persisted in the sessions table.
|
|
25
29
|
*/
|
|
26
30
|
export async function handleSessionStart(
|
|
27
31
|
projectRoot: string,
|
|
28
|
-
|
|
32
|
+
_payload: SessionStartPayload,
|
|
29
33
|
): Promise<void> {
|
|
30
|
-
const { observeBrain } = await import('../../memory/brain-retrieval.js');
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
await observeBrain(projectRoot, {
|
|
34
|
-
text: `Session started: ${payload.name}\nScope: ${JSON.stringify(payload.scope)}\nAgent: ${payload.agent || 'unknown'}`,
|
|
35
|
-
title: `Session start: ${payload.name}`,
|
|
36
|
-
type: 'discovery',
|
|
37
|
-
sourceSessionId: payload.sessionId,
|
|
38
|
-
sourceType: 'agent',
|
|
39
|
-
});
|
|
40
|
-
} catch (err) {
|
|
41
|
-
if (!isMissingBrainSchemaError(err)) throw err;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
34
|
// T138/T139: Refresh memory bridge after session starts (best-effort)
|
|
45
35
|
await maybeRefreshMemoryBridge(projectRoot);
|
|
46
36
|
}
|
|
47
37
|
|
|
48
38
|
/**
|
|
49
|
-
* Handle SessionEnd -
|
|
39
|
+
* Handle SessionEnd - run post-session tasks and refresh memory bridge.
|
|
50
40
|
*
|
|
51
41
|
* T138: Refresh memory bridge after session ends.
|
|
52
42
|
* T144: Extract transcript observations via cross-provider adapter.
|
|
43
|
+
* T527: Removed duplicate observeBrain write — session data is already
|
|
44
|
+
* persisted in the sessions table.
|
|
53
45
|
*/
|
|
54
46
|
export async function handleSessionEnd(
|
|
55
47
|
projectRoot: string,
|
|
56
48
|
payload: SessionEndPayload,
|
|
57
49
|
): Promise<void> {
|
|
58
|
-
const { observeBrain } = await import('../../memory/brain-retrieval.js');
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
await observeBrain(projectRoot, {
|
|
62
|
-
text: `Session ended: ${payload.sessionId}\nDuration: ${payload.duration}s\nTasks completed: ${payload.tasksCompleted.join(', ') || 'none'}`,
|
|
63
|
-
title: `Session end: ${payload.sessionId}`,
|
|
64
|
-
type: 'change',
|
|
65
|
-
sourceSessionId: payload.sessionId,
|
|
66
|
-
sourceType: 'agent',
|
|
67
|
-
});
|
|
68
|
-
} catch (err) {
|
|
69
|
-
if (!isMissingBrainSchemaError(err)) throw err;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
50
|
// Auto-grade session and feed insights to brain.db (best-effort)
|
|
73
51
|
try {
|
|
74
52
|
const { gradeSession } = await import('../../sessions/session-grade.js');
|
package/src/index.ts
CHANGED
|
@@ -299,6 +299,20 @@ export {
|
|
|
299
299
|
timelineBrain,
|
|
300
300
|
} from './memory/brain-retrieval.js';
|
|
301
301
|
export { searchBrain } from './memory/brain-search.js';
|
|
302
|
+
export type {
|
|
303
|
+
DecisionQualityInput,
|
|
304
|
+
LearningQualityInput,
|
|
305
|
+
ObservationQualityInput,
|
|
306
|
+
PatternQualityInput,
|
|
307
|
+
} from './memory/quality-scoring.js';
|
|
308
|
+
// Quality scoring (T531) — exported for backfill (T530) and future hooks
|
|
309
|
+
export {
|
|
310
|
+
computeDecisionQuality,
|
|
311
|
+
computeLearningQuality,
|
|
312
|
+
computeObservationQuality,
|
|
313
|
+
computePatternQuality,
|
|
314
|
+
QUALITY_SCORE_THRESHOLD,
|
|
315
|
+
} from './memory/quality-scoring.js';
|
|
302
316
|
// Migration (flat re-exports for backward compatibility)
|
|
303
317
|
export {
|
|
304
318
|
compareSemver,
|
package/src/internal.ts
CHANGED
|
@@ -22,6 +22,16 @@ export * from './index.js';
|
|
|
22
22
|
// Extended flat exports (required by @cleocode/cleo)
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
|
|
25
|
+
// Code analysis (Smart Explore) — canonical source: @cleocode/nexus
|
|
26
|
+
export {
|
|
27
|
+
batchParse,
|
|
28
|
+
isTreeSitterAvailable,
|
|
29
|
+
parseFile,
|
|
30
|
+
type SmartSearchOptions,
|
|
31
|
+
smartOutline,
|
|
32
|
+
smartSearch,
|
|
33
|
+
smartUnfold,
|
|
34
|
+
} from '@cleocode/nexus';
|
|
25
35
|
export { exportTasks } from './admin/export.js';
|
|
26
36
|
export { exportTasksPackage } from './admin/export-tasks.js';
|
|
27
37
|
// Admin
|
|
@@ -36,17 +46,17 @@ export type { AuditEntry } from './audit.js';
|
|
|
36
46
|
// Backfill
|
|
37
47
|
export type { BackfillOptions, BackfillResult, BackfillTaskChange } from './backfill/index.js';
|
|
38
48
|
export { backfillTasks, generateAcFromDescription } from './backfill/index.js';
|
|
39
|
-
export type {
|
|
49
|
+
export type {
|
|
50
|
+
BootstrapContext,
|
|
51
|
+
BootstrapOptions,
|
|
52
|
+
BootstrapVerificationResult,
|
|
53
|
+
} from './bootstrap.js';
|
|
40
54
|
// Bootstrap (global setup)
|
|
41
55
|
export {
|
|
42
56
|
bootstrapGlobalCleo,
|
|
43
57
|
installSkillsGlobally,
|
|
58
|
+
verifyBootstrapComplete,
|
|
44
59
|
} from './bootstrap.js';
|
|
45
|
-
export { smartOutline } from './code/outline.js';
|
|
46
|
-
// Code analysis (Smart Explore)
|
|
47
|
-
export { batchParse, isTreeSitterAvailable, parseFile } from './code/parser.js';
|
|
48
|
-
export { type SmartSearchOptions, smartSearch } from './code/search.js';
|
|
49
|
-
export { smartUnfold } from './code/unfold.js';
|
|
50
60
|
export type { ViolationLogEntry } from './compliance/protocol-enforcement.js';
|
|
51
61
|
// Compliance
|
|
52
62
|
export { ProtocolEnforcer, protocolEnforcer } from './compliance/protocol-enforcement.js';
|
|
@@ -63,7 +73,6 @@ export { validatePayload } from './hooks/payload-schemas.js';
|
|
|
63
73
|
// Hooks
|
|
64
74
|
export type { HookEvent, ProviderHookEvent } from './hooks/provider-hooks.js';
|
|
65
75
|
export { isProviderHookEvent } from './hooks/types.js';
|
|
66
|
-
|
|
67
76
|
// Init (additional)
|
|
68
77
|
export { isAutoInitEnabled } from './init.js';
|
|
69
78
|
export {
|
|
@@ -146,6 +155,8 @@ export {
|
|
|
146
155
|
} from './lifecycle/index.js';
|
|
147
156
|
export { STAGE_DEFINITIONS } from './lifecycle/stages.js';
|
|
148
157
|
export { instantiateTessera, showTessera } from './lifecycle/tessera-engine.js';
|
|
158
|
+
export type { BrainBackfillResult } from './memory/brain-backfill.js';
|
|
159
|
+
export { backfillBrainGraph } from './memory/brain-backfill.js';
|
|
149
160
|
// Memory — brain lifecycle (temporal decay + consolidation)
|
|
150
161
|
export type { ConsolidationResult, DecayResult } from './memory/brain-lifecycle.js';
|
|
151
162
|
export { applyTemporalDecay, consolidateMemories } from './memory/brain-lifecycle.js';
|
|
@@ -159,6 +170,8 @@ export type {
|
|
|
159
170
|
BrainMaintenanceResult,
|
|
160
171
|
} from './memory/brain-maintenance.js';
|
|
161
172
|
export { runBrainMaintenance } from './memory/brain-maintenance.js';
|
|
173
|
+
export type { PurgeResult } from './memory/brain-purge.js';
|
|
174
|
+
export { purgeBrainNoise } from './memory/brain-purge.js';
|
|
162
175
|
export type {
|
|
163
176
|
PopulateEmbeddingsOptions,
|
|
164
177
|
PopulateEmbeddingsResult,
|
|
@@ -174,9 +187,13 @@ export {
|
|
|
174
187
|
memoryFetch,
|
|
175
188
|
memoryFind,
|
|
176
189
|
memoryGraphAdd,
|
|
190
|
+
memoryGraphContext,
|
|
177
191
|
memoryGraphNeighbors,
|
|
192
|
+
memoryGraphRelated,
|
|
178
193
|
memoryGraphRemove,
|
|
179
194
|
memoryGraphShow,
|
|
195
|
+
memoryGraphStatsFull,
|
|
196
|
+
memoryGraphTrace,
|
|
180
197
|
memoryLearningFind,
|
|
181
198
|
memoryLearningStats,
|
|
182
199
|
memoryLearningStore,
|
|
@@ -193,6 +210,13 @@ export {
|
|
|
193
210
|
memoryTimeline,
|
|
194
211
|
memoryUnlink,
|
|
195
212
|
} from './memory/engine-compat.js';
|
|
213
|
+
// Memory — graph traversal query functions (T535)
|
|
214
|
+
export type {
|
|
215
|
+
GraphStats,
|
|
216
|
+
NodeContext,
|
|
217
|
+
RelatedNode,
|
|
218
|
+
TraceNode,
|
|
219
|
+
} from './memory/graph-queries.js';
|
|
196
220
|
// Memory — pipeline manifests
|
|
197
221
|
export {
|
|
198
222
|
pipelineManifestAppend,
|
|
@@ -491,6 +515,12 @@ export type { BackupEntry, BackupResult, RestoreResult } from './system/backup.j
|
|
|
491
515
|
export { listSystemBackups, restoreBackup } from './system/backup.js';
|
|
492
516
|
export type { CleanupResult } from './system/cleanup.js';
|
|
493
517
|
export { cleanupSystem } from './system/cleanup.js';
|
|
518
|
+
// System — dependency registry
|
|
519
|
+
export {
|
|
520
|
+
checkAllDependencies,
|
|
521
|
+
checkDependency,
|
|
522
|
+
getDependencySpecs,
|
|
523
|
+
} from './system/dependencies.js';
|
|
494
524
|
export type { DiagnosticsResult, HealthResult } from './system/health.js';
|
|
495
525
|
export { getSystemDiagnostics, getSystemHealth, startupHealthCheck } from './system/health.js';
|
|
496
526
|
export type {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tree-sitter language detection and grammar resolution.
|
|
3
3
|
*
|
|
4
|
-
* Maps file extensions to tree-sitter
|
|
5
|
-
* the Smart Explore code analysis pipeline.
|
|
4
|
+
* Maps file extensions to tree-sitter language identifiers used by
|
|
5
|
+
* the Smart Explore code analysis pipeline (outline, search, unfold).
|
|
6
|
+
* Grammar loading is handled internally by parser.ts via native Node bindings.
|
|
6
7
|
*
|
|
7
|
-
* @task
|
|
8
|
+
* @task T509
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
/** Supported tree-sitter language identifiers. */
|
|
@@ -60,9 +61,9 @@ const GRAMMAR_PACKAGE_MAP: Record<TreeSitterLanguage, string> = {
|
|
|
60
61
|
};
|
|
61
62
|
|
|
62
63
|
/**
|
|
63
|
-
* Detect tree-sitter language from a file path.
|
|
64
|
+
* Detect tree-sitter language from a file path or file name.
|
|
64
65
|
*
|
|
65
|
-
* @param filePath - Path to a source file
|
|
66
|
+
* @param filePath - Path to a source file (or bare file name)
|
|
66
67
|
* @returns The detected language, or undefined if unsupported
|
|
67
68
|
*/
|
|
68
69
|
export function detectLanguage(filePath: string): TreeSitterLanguage | undefined {
|
|
@@ -74,6 +75,9 @@ export function detectLanguage(filePath: string): TreeSitterLanguage | undefined
|
|
|
74
75
|
/**
|
|
75
76
|
* Get the npm grammar package name for a language.
|
|
76
77
|
*
|
|
78
|
+
* The package name matches the native Node binding that parser.ts loads
|
|
79
|
+
* via {@link createRequire}. Useful for diagnostics and install hints.
|
|
80
|
+
*
|
|
77
81
|
* @param language - A tree-sitter language identifier
|
|
78
82
|
* @returns The npm package name containing the grammar
|
|
79
83
|
*/
|
|
@@ -81,8 +85,8 @@ export function grammarPackage(language: TreeSitterLanguage): string {
|
|
|
81
85
|
return GRAMMAR_PACKAGE_MAP[language];
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
/** All supported file extensions. */
|
|
88
|
+
/** All supported file extensions (without leading dot). */
|
|
85
89
|
export const SUPPORTED_EXTENSIONS = Object.keys(EXTENSION_MAP);
|
|
86
90
|
|
|
87
|
-
/** All supported languages. */
|
|
91
|
+
/** All supported languages (deduplicated). */
|
|
88
92
|
export const SUPPORTED_LANGUAGES: TreeSitterLanguage[] = [...new Set(Object.values(EXTENSION_MAP))];
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unit tests for auto-extract memory pipeline.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* extractTaskCompletionMemory and extractSessionEndMemory are disabled no-ops
|
|
5
|
+
* per T523 CA1 specification. Tests verify the no-op contract holds.
|
|
6
|
+
*
|
|
7
|
+
* @task T526
|
|
8
|
+
* @epic T523
|
|
5
9
|
*/
|
|
6
10
|
|
|
7
11
|
import type { Task } from '@cleocode/contracts';
|
|
@@ -21,7 +25,7 @@ vi.mock('../decisions.js', () => ({
|
|
|
21
25
|
storeDecision: vi.fn().mockResolvedValue(undefined),
|
|
22
26
|
}));
|
|
23
27
|
|
|
24
|
-
// Mock getAccessor
|
|
28
|
+
// Mock getAccessor — should never be called by disabled functions
|
|
25
29
|
vi.mock('../../store/data-accessor.js', () => ({
|
|
26
30
|
getAccessor: vi.fn(),
|
|
27
31
|
}));
|
|
@@ -80,24 +84,16 @@ beforeEach(() => {
|
|
|
80
84
|
});
|
|
81
85
|
|
|
82
86
|
describe('extractTaskCompletionMemory', () => {
|
|
83
|
-
it('
|
|
87
|
+
it('is a no-op — does not write learnings', async () => {
|
|
84
88
|
const task = makeTask({ id: 'T001', title: 'Fix auth bug', description: 'Auth was broken' });
|
|
85
89
|
setupAccessor([task]);
|
|
86
90
|
|
|
87
91
|
await extractTaskCompletionMemory('/mock/root', task);
|
|
88
92
|
|
|
89
|
-
expect(storeLearning).
|
|
90
|
-
'/mock/root',
|
|
91
|
-
expect.objectContaining({
|
|
92
|
-
insight: `Completed: ${task.title} — ${task.description}`,
|
|
93
|
-
source: `task-completion:${task.id}`,
|
|
94
|
-
confidence: 0.7,
|
|
95
|
-
actionable: true,
|
|
96
|
-
}),
|
|
97
|
-
);
|
|
93
|
+
expect(storeLearning).not.toHaveBeenCalled();
|
|
98
94
|
});
|
|
99
95
|
|
|
100
|
-
it('
|
|
96
|
+
it('is a no-op — does not write dependency learnings', async () => {
|
|
101
97
|
const task = makeTask({
|
|
102
98
|
id: 'T002',
|
|
103
99
|
title: 'Deploy feature',
|
|
@@ -108,28 +104,10 @@ describe('extractTaskCompletionMemory', () => {
|
|
|
108
104
|
|
|
109
105
|
await extractTaskCompletionMemory('/mock/root', task);
|
|
110
106
|
|
|
111
|
-
|
|
112
|
-
const depCall = calls.find((c) =>
|
|
113
|
-
(c[1] as { insight: string }).insight.includes('depended on'),
|
|
114
|
-
);
|
|
115
|
-
expect(depCall).toBeDefined();
|
|
116
|
-
expect((depCall![1] as { insight: string }).insight).toContain('T001, T003');
|
|
117
|
-
expect((depCall![1] as { source: string }).source).toBe('task-completion:T002');
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('does not create dependency learning when task has no dependencies', async () => {
|
|
121
|
-
const task = makeTask({ id: 'T003', title: 'Solo task', depends: [] });
|
|
122
|
-
setupAccessor([task]);
|
|
123
|
-
|
|
124
|
-
await extractTaskCompletionMemory('/mock/root', task);
|
|
125
|
-
|
|
126
|
-
const depCall = (storeLearning as ReturnType<typeof vi.fn>).mock.calls.find((c) =>
|
|
127
|
-
(c[1] as { insight: string }).insight.includes('depended on'),
|
|
128
|
-
);
|
|
129
|
-
expect(depCall).toBeUndefined();
|
|
107
|
+
expect(storeLearning).not.toHaveBeenCalled();
|
|
130
108
|
});
|
|
131
109
|
|
|
132
|
-
it('
|
|
110
|
+
it('is a no-op — does not write label patterns', async () => {
|
|
133
111
|
const completedTasks = [
|
|
134
112
|
makeTask({ id: 'T010', title: 'A', labels: ['bug'] }),
|
|
135
113
|
makeTask({ id: 'T011', title: 'B', labels: ['bug'] }),
|
|
@@ -140,17 +118,11 @@ describe('extractTaskCompletionMemory', () => {
|
|
|
140
118
|
|
|
141
119
|
await extractTaskCompletionMemory('/mock/root', trigger);
|
|
142
120
|
|
|
143
|
-
expect(storePattern).
|
|
144
|
-
|
|
145
|
-
expect.objectContaining({
|
|
146
|
-
type: 'success',
|
|
147
|
-
impact: 'medium',
|
|
148
|
-
}),
|
|
149
|
-
);
|
|
121
|
+
expect(storePattern).not.toHaveBeenCalled();
|
|
122
|
+
expect(getAccessor).not.toHaveBeenCalled();
|
|
150
123
|
});
|
|
151
124
|
|
|
152
|
-
it('
|
|
153
|
-
(storeLearning as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error('db error'));
|
|
125
|
+
it('resolves to undefined without throwing', async () => {
|
|
154
126
|
setupAccessor([]);
|
|
155
127
|
|
|
156
128
|
await expect(
|
|
@@ -160,7 +132,7 @@ describe('extractTaskCompletionMemory', () => {
|
|
|
160
132
|
});
|
|
161
133
|
|
|
162
134
|
describe('extractSessionEndMemory', () => {
|
|
163
|
-
it('
|
|
135
|
+
it('is a no-op — does not write a session decision', async () => {
|
|
164
136
|
const tasks = [
|
|
165
137
|
makeTask({ id: 'T001', title: 'Task one' }),
|
|
166
138
|
makeTask({ id: 'T002', title: 'Task two' }),
|
|
@@ -169,32 +141,10 @@ describe('extractSessionEndMemory', () => {
|
|
|
169
141
|
|
|
170
142
|
await extractSessionEndMemory('/mock/root', session, tasks);
|
|
171
143
|
|
|
172
|
-
expect(storeDecision).toHaveBeenCalledWith(
|
|
173
|
-
'/mock/root',
|
|
174
|
-
expect.objectContaining({
|
|
175
|
-
type: 'process',
|
|
176
|
-
confidence: 'medium',
|
|
177
|
-
}),
|
|
178
|
-
);
|
|
179
|
-
const call = (storeDecision as ReturnType<typeof vi.fn>).mock.calls[0][1] as {
|
|
180
|
-
decision: string;
|
|
181
|
-
rationale: string;
|
|
182
|
-
};
|
|
183
|
-
expect(call.decision).toContain('S-001');
|
|
184
|
-
expect(call.decision).toContain('T001');
|
|
185
|
-
expect(call.decision).toContain('T002');
|
|
186
|
-
expect(call.rationale).toContain('test scope');
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('does not create a decision when no tasks completed', async () => {
|
|
190
|
-
const session = makeSessionData({ tasksCompleted: [] });
|
|
191
|
-
|
|
192
|
-
await extractSessionEndMemory('/mock/root', session, []);
|
|
193
|
-
|
|
194
144
|
expect(storeDecision).not.toHaveBeenCalled();
|
|
195
145
|
});
|
|
196
146
|
|
|
197
|
-
it('
|
|
147
|
+
it('is a no-op — does not write per-task learnings', async () => {
|
|
198
148
|
const tasks = [
|
|
199
149
|
makeTask({ id: 'T001', title: 'Task one', description: 'Desc one' }),
|
|
200
150
|
makeTask({ id: 'T002', title: 'Task two', description: 'Desc two' }),
|
|
@@ -203,15 +153,10 @@ describe('extractSessionEndMemory', () => {
|
|
|
203
153
|
|
|
204
154
|
await extractSessionEndMemory('/mock/root', session, tasks);
|
|
205
155
|
|
|
206
|
-
|
|
207
|
-
expect(calls.length).toBe(2);
|
|
208
|
-
calls.forEach((c) => {
|
|
209
|
-
expect((c[1] as { source: string }).source).toBe('session-end:S-002');
|
|
210
|
-
expect((c[1] as { confidence: number }).confidence).toBe(0.7);
|
|
211
|
-
});
|
|
156
|
+
expect(storeLearning).not.toHaveBeenCalled();
|
|
212
157
|
});
|
|
213
158
|
|
|
214
|
-
it('
|
|
159
|
+
it('is a no-op — does not write workflow patterns', async () => {
|
|
215
160
|
const tasks = [
|
|
216
161
|
makeTask({ id: 'T001', title: 'A', labels: ['feature'] }),
|
|
217
162
|
makeTask({ id: 'T002', title: 'B', labels: ['feature'] }),
|
|
@@ -220,17 +165,10 @@ describe('extractSessionEndMemory', () => {
|
|
|
220
165
|
|
|
221
166
|
await extractSessionEndMemory('/mock/root', session, tasks);
|
|
222
167
|
|
|
223
|
-
expect(storePattern).
|
|
224
|
-
'/mock/root',
|
|
225
|
-
expect.objectContaining({
|
|
226
|
-
type: 'workflow',
|
|
227
|
-
impact: 'medium',
|
|
228
|
-
}),
|
|
229
|
-
);
|
|
168
|
+
expect(storePattern).not.toHaveBeenCalled();
|
|
230
169
|
});
|
|
231
170
|
|
|
232
|
-
it('
|
|
233
|
-
(storeDecision as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error('db fail'));
|
|
171
|
+
it('resolves to undefined without throwing', async () => {
|
|
234
172
|
const tasks = [makeTask({ id: 'T001', title: 'X' })];
|
|
235
173
|
const session = makeSessionData({ tasksCompleted: ['T001'] });
|
|
236
174
|
|