@cleocode/core 2026.3.44 → 2026.3.46
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/admin/import-tasks.d.ts +10 -2
- package/dist/admin/import-tasks.d.ts.map +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/index.js +1890 -504
- package/dist/index.js.map +4 -4
- package/dist/init.d.ts.map +1 -1
- package/dist/injection.d.ts +1 -1
- package/dist/injection.d.ts.map +1 -1
- package/dist/internal.d.ts +3 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/nexus/index.d.ts +2 -0
- package/dist/nexus/index.d.ts.map +1 -1
- package/dist/nexus/transfer-types.d.ts +123 -0
- package/dist/nexus/transfer-types.d.ts.map +1 -0
- package/dist/nexus/transfer.d.ts +31 -0
- package/dist/nexus/transfer.d.ts.map +1 -0
- package/dist/routing/capability-matrix.d.ts +6 -4
- package/dist/routing/capability-matrix.d.ts.map +1 -1
- package/dist/scaffold.d.ts +16 -9
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/skills/agents/install.d.ts.map +1 -1
- package/dist/skills/routing-table.d.ts +17 -16
- package/dist/skills/routing-table.d.ts.map +1 -1
- package/dist/skills/skill-paths.d.ts.map +1 -1
- package/dist/store/brain-sqlite.d.ts +4 -1
- package/dist/store/brain-sqlite.d.ts.map +1 -1
- package/dist/store/nexus-sqlite.d.ts +4 -1
- package/dist/store/nexus-sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.d.ts +4 -1
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/tasks-schema.d.ts +3 -3
- package/dist/store/tasks-schema.d.ts.map +1 -1
- package/dist/store/validation-schemas.d.ts +5 -4
- package/dist/store/validation-schemas.d.ts.map +1 -1
- package/dist/system/health.d.ts.map +1 -1
- package/dist/ui/index.d.ts +0 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/package.json +9 -4
- package/schemas/adr-frontmatter.schema.json +72 -0
- package/schemas/agent-configs.schema.json +120 -0
- package/schemas/agent-registry.json +243 -0
- package/schemas/agent-registry.schema.json +132 -0
- package/schemas/archive/research-manifest.schema.json +257 -0
- package/schemas/archive.schema.json +450 -0
- package/schemas/brain-decision.schema.json +69 -0
- package/schemas/brain-learning.schema.json +57 -0
- package/schemas/brain-pattern.schema.json +72 -0
- package/schemas/config.schema.json +2606 -0
- package/schemas/context-state.schema.json +137 -0
- package/schemas/contribution.schema.json +722 -0
- package/schemas/critical-path.schema.json +246 -0
- package/schemas/deps-cache.schema.json +97 -0
- package/schemas/doctor-output.schema.json +283 -0
- package/schemas/error.schema.json +161 -0
- package/schemas/export-package.schema.json +375 -0
- package/schemas/global-config.schema.json +219 -0
- package/schemas/grade.schema.json +49 -0
- package/schemas/log.schema.json +250 -0
- package/schemas/metrics.schema.json +328 -0
- package/schemas/migrations.schema.json +150 -0
- package/schemas/nexus-registry.schema.json +90 -0
- package/schemas/operation-constitution.schema.json +438 -0
- package/schemas/output.schema.json +164 -0
- package/schemas/project-context.schema.json +164 -0
- package/schemas/project-info.schema.json +180 -0
- package/schemas/projects-registry.schema.json +107 -0
- package/schemas/protocol-frontmatter.schema.json +72 -0
- package/schemas/rcasd-consensus-report.schema.json +10 -0
- package/schemas/rcasd-evidence.schema.json +42 -0
- package/schemas/rcasd-gate-result.schema.json +46 -0
- package/schemas/rcasd-hitl-resolution.schema.json +10 -0
- package/schemas/rcasd-index.schema.json +10 -0
- package/schemas/rcasd-manifest.schema.json +10 -0
- package/schemas/rcasd-research-output.schema.json +10 -0
- package/schemas/rcasd-spec-frontmatter.schema.json +10 -0
- package/schemas/rcasd-stage-transition.schema.json +38 -0
- package/schemas/releases.schema.json +267 -0
- package/schemas/skills-manifest.schema.json +91 -0
- package/schemas/skillsmp.schema.json +208 -0
- package/schemas/spec-index.schema.json +196 -0
- package/schemas/system-flow-atlas.schema.json +125 -0
- package/src/__tests__/injection-chain.test.ts +11 -10
- package/src/__tests__/injection-mvi-tiers.test.ts +4 -2
- package/src/admin/import-tasks.ts +53 -29
- package/src/agents/__tests__/capacity.test.d.ts +7 -0
- package/src/agents/__tests__/capacity.test.d.ts.map +1 -0
- package/src/agents/__tests__/capacity.test.js +173 -0
- package/src/agents/__tests__/capacity.test.js.map +1 -0
- package/src/agents/__tests__/registry.test.d.ts +8 -0
- package/src/agents/__tests__/registry.test.d.ts.map +1 -0
- package/src/agents/__tests__/registry.test.js +348 -0
- package/src/agents/__tests__/registry.test.js.map +1 -0
- package/src/agents/__tests__/retry.test.d.ts +7 -0
- package/src/agents/__tests__/retry.test.d.ts.map +1 -0
- package/src/agents/__tests__/retry.test.js +225 -0
- package/src/agents/__tests__/retry.test.js.map +1 -0
- package/src/bootstrap.ts +3 -1
- package/src/init.ts +63 -18
- package/src/injection.ts +11 -5
- package/src/intelligence/__tests__/impact.test.d.ts +15 -0
- package/src/intelligence/__tests__/impact.test.d.ts.map +1 -0
- package/src/intelligence/__tests__/impact.test.js +384 -0
- package/src/intelligence/__tests__/impact.test.js.map +1 -0
- package/src/intelligence/__tests__/patterns.test.d.ts +8 -0
- package/src/intelligence/__tests__/patterns.test.d.ts.map +1 -0
- package/src/intelligence/__tests__/patterns.test.js +370 -0
- package/src/intelligence/__tests__/patterns.test.js.map +1 -0
- package/src/intelligence/__tests__/prediction.test.d.ts +8 -0
- package/src/intelligence/__tests__/prediction.test.d.ts.map +1 -0
- package/src/intelligence/__tests__/prediction.test.js +314 -0
- package/src/intelligence/__tests__/prediction.test.js.map +1 -0
- package/src/internal.ts +7 -1
- package/src/nexus/__tests__/nexus-e2e.test.d.ts +12 -0
- package/src/nexus/__tests__/nexus-e2e.test.d.ts.map +1 -0
- package/src/nexus/__tests__/nexus-e2e.test.js +1220 -0
- package/src/nexus/__tests__/nexus-e2e.test.js.map +1 -0
- package/src/nexus/__tests__/transfer.test.d.ts +8 -0
- package/src/nexus/__tests__/transfer.test.d.ts.map +1 -0
- package/src/nexus/__tests__/transfer.test.js +372 -0
- package/src/nexus/__tests__/transfer.test.js.map +1 -0
- package/src/nexus/__tests__/transfer.test.ts +446 -0
- package/src/nexus/index.ts +14 -0
- package/src/nexus/transfer-types.ts +129 -0
- package/src/nexus/transfer.ts +314 -0
- package/src/routing/capability-matrix.ts +1435 -205
- package/src/scaffold.ts +18 -11
- package/src/skills/__tests__/routing-table.test.ts +53 -33
- package/src/skills/agents/install.ts +9 -1
- package/src/skills/routing-table.ts +39 -253
- package/src/skills/skill-paths.ts +3 -2
- package/src/store/__tests__/project-detect.test.ts +1 -1
- package/src/store/brain-sqlite.ts +7 -3
- package/src/store/nexus-sqlite.ts +7 -3
- package/src/store/sqlite.ts +9 -3
- package/src/store/tasks-schema.ts +1 -1
- package/src/system/health.ts +18 -7
- package/src/ui/index.ts +0 -6
- package/src/validation/operation-gate-validators.ts +2 -2
- package/templates/CLEO-INJECTION.md +120 -0
- package/templates/README.md +29 -0
- package/templates/agent-registry.json +305 -0
- package/templates/cleo-gitignore +74 -0
- package/templates/config.template.json +187 -0
- package/templates/git-hooks/commit-msg +149 -0
- package/templates/git-hooks/pre-commit +40 -0
- package/templates/git-hooks/pre-push +79 -0
- package/templates/github/ISSUE_TEMPLATE/bug_report.yml +143 -0
- package/templates/github/ISSUE_TEMPLATE/config.yml +8 -0
- package/templates/github/ISSUE_TEMPLATE/feature_request.yml +125 -0
- package/templates/github/ISSUE_TEMPLATE/help_question.yml +99 -0
- package/templates/global-config.template.json +56 -0
- package/templates/hooks/precompact-safestop.sh +89 -0
- package/templates/issue-templates/bug_report.yml +143 -0
- package/templates/issue-templates/config.yml +8 -0
- package/templates/issue-templates/feature_request.yml +125 -0
- package/templates/issue-templates/help_question.yml +99 -0
- package/templates/skillsmp.json.example +28 -0
- package/templates/skillsmp.json.example.md +214 -0
- package/dist/ui/injection-legacy.d.ts +0 -26
- package/dist/ui/injection-legacy.d.ts.map +0 -1
- package/src/ui/__tests__/injection-registry.test.d.ts +0 -11
- package/src/ui/__tests__/injection-registry.test.d.ts.map +0 -1
- package/src/ui/__tests__/injection-registry.test.js +0 -46
- package/src/ui/__tests__/injection-registry.test.js.map +0 -1
- package/src/ui/__tests__/injection-registry.test.ts +0 -57
- package/src/ui/injection-legacy.ts +0 -44
package/src/scaffold.ts
CHANGED
|
@@ -523,7 +523,7 @@ export async function ensureProjectContext(
|
|
|
523
523
|
try {
|
|
524
524
|
const schemaPath = join(
|
|
525
525
|
dirname(fileURLToPath(import.meta.url)),
|
|
526
|
-
'
|
|
526
|
+
'../schemas/project-context.schema.json',
|
|
527
527
|
);
|
|
528
528
|
if (existsSync(schemaPath)) {
|
|
529
529
|
const AjvModule = await import('ajv');
|
|
@@ -1037,8 +1037,16 @@ export function checkMemoryBridge(projectRoot: string): CheckResult {
|
|
|
1037
1037
|
* Required subdirectories under the global ~/.cleo/ home.
|
|
1038
1038
|
* These are infrastructure directories managed by CLEO itself,
|
|
1039
1039
|
* not project-specific data.
|
|
1040
|
+
*
|
|
1041
|
+
* Truly global:
|
|
1042
|
+
* - logs — global log output
|
|
1043
|
+
* - templates — CLEO-INJECTION.md symlink target
|
|
1044
|
+
*
|
|
1045
|
+
* Note: nexus.db lives directly in ~/.cleo/, not a subdir.
|
|
1046
|
+
* Schemas are read at runtime from getPackageRoot()/schemas/ — no copy needed.
|
|
1047
|
+
* Project-level dirs (adrs/, rcasd/, agent-outputs/, backups/) live in .cleo/ only.
|
|
1040
1048
|
*/
|
|
1041
|
-
export const REQUIRED_GLOBAL_SUBDIRS = ['
|
|
1049
|
+
export const REQUIRED_GLOBAL_SUBDIRS = ['logs', 'templates'] as const;
|
|
1042
1050
|
|
|
1043
1051
|
/**
|
|
1044
1052
|
* Ensure the global ~/.cleo/ home directory and its required
|
|
@@ -1111,9 +1119,13 @@ export async function ensureGlobalTemplates(): Promise<ScaffoldResult> {
|
|
|
1111
1119
|
}
|
|
1112
1120
|
|
|
1113
1121
|
/**
|
|
1114
|
-
* Perform a complete global scaffold operation: ensure home
|
|
1115
|
-
*
|
|
1116
|
-
*
|
|
1122
|
+
* Perform a complete global scaffold operation: ensure home and templates
|
|
1123
|
+
* are all present and current. This is the single entry point for global
|
|
1124
|
+
* infrastructure scaffolding.
|
|
1125
|
+
*
|
|
1126
|
+
* Schemas are NOT copied here — they are read at runtime from the npm
|
|
1127
|
+
* package path (getPackageRoot() + '/schemas/'). Use ensureGlobalSchemas()
|
|
1128
|
+
* explicitly from init or upgrade if a copy is needed for a specific workflow.
|
|
1117
1129
|
*
|
|
1118
1130
|
* Used by:
|
|
1119
1131
|
* - MCP startup (via startupHealthCheck in health.ts)
|
|
@@ -1122,17 +1134,12 @@ export async function ensureGlobalTemplates(): Promise<ScaffoldResult> {
|
|
|
1122
1134
|
*/
|
|
1123
1135
|
export async function ensureGlobalScaffold(): Promise<{
|
|
1124
1136
|
home: ScaffoldResult;
|
|
1125
|
-
schemas: { installed: number; updated: number; total: number };
|
|
1126
1137
|
templates: ScaffoldResult;
|
|
1127
1138
|
}> {
|
|
1128
|
-
// Lazy import to avoid circular dependency
|
|
1129
|
-
const { ensureGlobalSchemas } = await import('./schema-management.js');
|
|
1130
|
-
|
|
1131
1139
|
const home = await ensureGlobalHome();
|
|
1132
|
-
const schemas = ensureGlobalSchemas();
|
|
1133
1140
|
const templates = await ensureGlobalTemplates();
|
|
1134
1141
|
|
|
1135
|
-
return { home,
|
|
1142
|
+
return { home, templates };
|
|
1136
1143
|
}
|
|
1137
1144
|
|
|
1138
1145
|
// ── Global check* functions (read-only diagnostics) ──────────────────
|
|
@@ -3,44 +3,16 @@ import {
|
|
|
3
3
|
getOperationsByChannel,
|
|
4
4
|
getPreferredChannel,
|
|
5
5
|
getRoutingForDomain,
|
|
6
|
-
ROUTING_TABLE,
|
|
7
6
|
} from '../routing-table.js';
|
|
8
7
|
|
|
9
8
|
describe('routing-table', () => {
|
|
10
|
-
describe('
|
|
11
|
-
it('
|
|
12
|
-
|
|
13
|
-
expect(domains.has('memory')).toBe(true);
|
|
14
|
-
expect(domains.has('tasks')).toBe(true);
|
|
15
|
-
expect(domains.has('session')).toBe(true);
|
|
16
|
-
expect(domains.has('admin')).toBe(true);
|
|
17
|
-
expect(domains.has('tools')).toBe(true);
|
|
18
|
-
expect(domains.has('check')).toBe(true);
|
|
19
|
-
expect(domains.has('pipeline')).toBe(true);
|
|
20
|
-
expect(domains.has('orchestrate')).toBe(true);
|
|
21
|
-
expect(domains.has('nexus')).toBe(true);
|
|
22
|
-
expect(domains.has('sticky')).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('every entry has required fields', () => {
|
|
26
|
-
for (const entry of ROUTING_TABLE) {
|
|
27
|
-
expect(entry.domain).toBeTruthy();
|
|
28
|
-
expect(entry.operation).toBeTruthy();
|
|
29
|
-
expect(['mcp', 'cli', 'either']).toContain(entry.preferredChannel);
|
|
30
|
-
expect(entry.reason).toBeTruthy();
|
|
31
|
-
}
|
|
9
|
+
describe('getPreferredChannel (via capability matrix)', () => {
|
|
10
|
+
it('returns mcp for memory.find', () => {
|
|
11
|
+
expect(getPreferredChannel('memory', 'find')).toBe('mcp');
|
|
32
12
|
});
|
|
33
13
|
|
|
34
|
-
it('
|
|
35
|
-
|
|
36
|
-
const unique = new Set(keys);
|
|
37
|
-
expect(unique.size).toBe(keys.length);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('getPreferredChannel', () => {
|
|
42
|
-
it('returns mcp for memory.brain.search', () => {
|
|
43
|
-
expect(getPreferredChannel('memory', 'brain.search')).toBe('mcp');
|
|
14
|
+
it('returns mcp for memory.fetch', () => {
|
|
15
|
+
expect(getPreferredChannel('memory', 'fetch')).toBe('mcp');
|
|
44
16
|
});
|
|
45
17
|
|
|
46
18
|
it('returns cli for pipeline.release.ship', () => {
|
|
@@ -54,15 +26,56 @@ describe('routing-table', () => {
|
|
|
54
26
|
it('returns either for unknown operations', () => {
|
|
55
27
|
expect(getPreferredChannel('nonexistent', 'op')).toBe('either');
|
|
56
28
|
});
|
|
29
|
+
|
|
30
|
+
it('returns mcp for tasks.show', () => {
|
|
31
|
+
expect(getPreferredChannel('tasks', 'show')).toBe('mcp');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('returns mcp for session.status', () => {
|
|
35
|
+
expect(getPreferredChannel('session', 'status')).toBe('mcp');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('returns mcp for admin.dash', () => {
|
|
39
|
+
expect(getPreferredChannel('admin', 'dash')).toBe('mcp');
|
|
40
|
+
});
|
|
57
41
|
});
|
|
58
42
|
|
|
59
43
|
describe('getRoutingForDomain', () => {
|
|
44
|
+
it('returns entries covering all 10 canonical domains', () => {
|
|
45
|
+
const domains = [
|
|
46
|
+
'memory',
|
|
47
|
+
'tasks',
|
|
48
|
+
'session',
|
|
49
|
+
'admin',
|
|
50
|
+
'tools',
|
|
51
|
+
'check',
|
|
52
|
+
'pipeline',
|
|
53
|
+
'orchestrate',
|
|
54
|
+
'nexus',
|
|
55
|
+
'sticky',
|
|
56
|
+
];
|
|
57
|
+
for (const domain of domains) {
|
|
58
|
+
const entries = getRoutingForDomain(domain);
|
|
59
|
+
expect(entries.length).toBeGreaterThan(0);
|
|
60
|
+
expect(entries.every((e) => e.domain === domain)).toBe(true);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
60
64
|
it('returns all memory domain entries', () => {
|
|
61
65
|
const entries = getRoutingForDomain('memory');
|
|
62
66
|
expect(entries.length).toBeGreaterThan(0);
|
|
63
67
|
expect(entries.every((e) => e.domain === 'memory')).toBe(true);
|
|
64
68
|
});
|
|
65
69
|
|
|
70
|
+
it('every entry has required fields with valid channel', () => {
|
|
71
|
+
const entries = getRoutingForDomain('tasks');
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
expect(entry.domain).toBeTruthy();
|
|
74
|
+
expect(entry.operation).toBeTruthy();
|
|
75
|
+
expect(['mcp', 'cli', 'either']).toContain(entry.preferredChannel);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
66
79
|
it('returns empty array for unknown domain', () => {
|
|
67
80
|
expect(getRoutingForDomain('nonexistent')).toEqual([]);
|
|
68
81
|
});
|
|
@@ -86,5 +99,12 @@ describe('routing-table', () => {
|
|
|
86
99
|
const cliCount = getOperationsByChannel('cli').length;
|
|
87
100
|
expect(mcpCount).toBeGreaterThan(cliCount);
|
|
88
101
|
});
|
|
102
|
+
|
|
103
|
+
it('has no duplicate domain+operation pairs within a channel', () => {
|
|
104
|
+
const mcpOps = getOperationsByChannel('mcp');
|
|
105
|
+
const keys = mcpOps.map((e) => `${e.domain}.${e.operation}`);
|
|
106
|
+
const unique = new Set(keys);
|
|
107
|
+
expect(unique.size).toBe(keys.length);
|
|
108
|
+
});
|
|
89
109
|
});
|
|
90
110
|
});
|
|
@@ -9,10 +9,18 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { existsSync, mkdirSync, readdirSync, readlinkSync, symlinkSync, unlinkSync } from 'node:fs';
|
|
12
|
+
import { platform } from 'node:os';
|
|
12
13
|
import { basename, join } from 'node:path';
|
|
13
14
|
import { getClaudeAgentsDir } from '../../paths.js';
|
|
14
15
|
import { getAgentsDir } from './config.js';
|
|
15
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Symlink type for directory symlinks.
|
|
19
|
+
* On Windows, use 'junction' (no admin privileges required).
|
|
20
|
+
* On Unix, use 'dir'.
|
|
21
|
+
*/
|
|
22
|
+
const DIR_SYMLINK_TYPE: 'junction' | 'dir' = platform() === 'win32' ? 'junction' : 'dir';
|
|
23
|
+
|
|
16
24
|
// ============================================================================
|
|
17
25
|
// Agent Installation
|
|
18
26
|
// ============================================================================
|
|
@@ -65,7 +73,7 @@ export function installAgent(agentDir: string): {
|
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
try {
|
|
68
|
-
symlinkSync(agentDir, targetPath,
|
|
76
|
+
symlinkSync(agentDir, targetPath, DIR_SYMLINK_TYPE);
|
|
69
77
|
return { installed: true, path: targetPath };
|
|
70
78
|
} catch (err) {
|
|
71
79
|
return { installed: false, path: targetPath, error: `Symlink failed: ${err}` };
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dynamic Skill Routing Table
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* is
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* and are preferred for rapid, repeated queries. CLI is preferred for
|
|
9
|
-
* operations that benefit from human-readable output or shell integration.
|
|
4
|
+
* Thin wrapper over the merged capability matrix. PreferredChannel data
|
|
5
|
+
* is now stored as part of OperationCapability in the capability matrix
|
|
6
|
+
* (packages/core/src/routing/capability-matrix.ts), which is the single
|
|
7
|
+
* SSoT for both execution mode and channel preference.
|
|
10
8
|
*
|
|
11
9
|
* @task T5240
|
|
12
10
|
* @epic T5149
|
|
11
|
+
* @see packages/core/src/routing/capability-matrix.ts
|
|
13
12
|
*/
|
|
14
13
|
|
|
14
|
+
import { getCapabilityMatrix } from '../routing/capability-matrix.js';
|
|
15
|
+
|
|
16
|
+
export type { PreferredChannel } from '../routing/capability-matrix.js';
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* Routing entry describing the preferred channel for an operation.
|
|
20
|
+
*
|
|
21
|
+
* Derived from OperationCapability in the capability matrix.
|
|
22
|
+
* Use this type when consuming domain-level routing results from
|
|
23
|
+
* getRoutingForDomain() or getOperationsByChannel().
|
|
17
24
|
*/
|
|
18
25
|
export interface RoutingEntry {
|
|
19
26
|
/** Domain name (e.g. 'tasks', 'memory', 'session') */
|
|
20
27
|
domain: string;
|
|
21
|
-
/** Operation name (e.g. '
|
|
28
|
+
/** Operation name (e.g. 'show', 'find') */
|
|
22
29
|
operation: string;
|
|
23
30
|
/** Preferred channel for token efficiency */
|
|
24
31
|
preferredChannel: 'mcp' | 'cli' | 'either';
|
|
@@ -26,277 +33,56 @@ export interface RoutingEntry {
|
|
|
26
33
|
reason: string;
|
|
27
34
|
}
|
|
28
35
|
|
|
29
|
-
/**
|
|
30
|
-
* Static routing table for all canonical operations.
|
|
31
|
-
*
|
|
32
|
-
* Operations are grouped by domain with channel preferences based on:
|
|
33
|
-
* - MCP: lower overhead (no CLI startup), direct DB access, structured JSON
|
|
34
|
-
* - CLI: human-readable output, shell integration, interactive prompts
|
|
35
|
-
* - Either: no significant difference or context-dependent
|
|
36
|
-
*/
|
|
37
|
-
export const ROUTING_TABLE: RoutingEntry[] = [
|
|
38
|
-
// === Memory domain -- MCP strongly preferred (direct DB, no CLI startup) ===
|
|
39
|
-
{
|
|
40
|
-
domain: 'memory',
|
|
41
|
-
operation: 'brain.search',
|
|
42
|
-
preferredChannel: 'mcp',
|
|
43
|
-
reason: 'Low overhead search, direct DB access',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
domain: 'memory',
|
|
47
|
-
operation: 'brain.fetch',
|
|
48
|
-
preferredChannel: 'mcp',
|
|
49
|
-
reason: 'Direct DB access, structured response',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
domain: 'memory',
|
|
53
|
-
operation: 'brain.timeline',
|
|
54
|
-
preferredChannel: 'mcp',
|
|
55
|
-
reason: 'Complex multi-table query, direct DB',
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
domain: 'memory',
|
|
59
|
-
operation: 'brain.observe',
|
|
60
|
-
preferredChannel: 'mcp',
|
|
61
|
-
reason: 'Direct DB write, no CLI overhead',
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
domain: 'memory',
|
|
65
|
-
operation: 'find',
|
|
66
|
-
preferredChannel: 'mcp',
|
|
67
|
-
reason: 'Alias for brain.search',
|
|
68
|
-
},
|
|
69
|
-
{ domain: 'memory', operation: 'show', preferredChannel: 'mcp', reason: 'Alias for brain.fetch' },
|
|
70
|
-
{
|
|
71
|
-
domain: 'memory',
|
|
72
|
-
operation: 'decision.store',
|
|
73
|
-
preferredChannel: 'mcp',
|
|
74
|
-
reason: 'Direct DB write',
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
domain: 'memory',
|
|
78
|
-
operation: 'decision.find',
|
|
79
|
-
preferredChannel: 'mcp',
|
|
80
|
-
reason: 'Direct DB query',
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
domain: 'memory',
|
|
84
|
-
operation: 'learning.store',
|
|
85
|
-
preferredChannel: 'mcp',
|
|
86
|
-
reason: 'Direct DB write',
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
domain: 'memory',
|
|
90
|
-
operation: 'learning.find',
|
|
91
|
-
preferredChannel: 'mcp',
|
|
92
|
-
reason: 'Direct DB query',
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
domain: 'memory',
|
|
96
|
-
operation: 'pattern.store',
|
|
97
|
-
preferredChannel: 'mcp',
|
|
98
|
-
reason: 'Direct DB write',
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
domain: 'memory',
|
|
102
|
-
operation: 'pattern.find',
|
|
103
|
-
preferredChannel: 'mcp',
|
|
104
|
-
reason: 'Direct DB query',
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
// === Tasks domain -- MCP preferred for queries, CLI for complex ops ===
|
|
108
|
-
{
|
|
109
|
-
domain: 'tasks',
|
|
110
|
-
operation: 'show',
|
|
111
|
-
preferredChannel: 'mcp',
|
|
112
|
-
reason: 'Structured JSON response',
|
|
113
|
-
},
|
|
114
|
-
{ domain: 'tasks', operation: 'find', preferredChannel: 'mcp', reason: 'Low overhead search' },
|
|
115
|
-
{ domain: 'tasks', operation: 'list', preferredChannel: 'mcp', reason: 'Paginated response' },
|
|
116
|
-
{
|
|
117
|
-
domain: 'tasks',
|
|
118
|
-
operation: 'next',
|
|
119
|
-
preferredChannel: 'mcp',
|
|
120
|
-
reason: 'Algorithm runs in-process',
|
|
121
|
-
},
|
|
122
|
-
{ domain: 'tasks', operation: 'current', preferredChannel: 'mcp', reason: 'Simple DB lookup' },
|
|
123
|
-
{
|
|
124
|
-
domain: 'tasks',
|
|
125
|
-
operation: 'plan',
|
|
126
|
-
preferredChannel: 'mcp',
|
|
127
|
-
reason: 'Composite view, in-process',
|
|
128
|
-
},
|
|
129
|
-
{ domain: 'tasks', operation: 'add', preferredChannel: 'mcp', reason: 'Atomic DB write' },
|
|
130
|
-
{ domain: 'tasks', operation: 'update', preferredChannel: 'mcp', reason: 'Atomic DB write' },
|
|
131
|
-
{
|
|
132
|
-
domain: 'tasks',
|
|
133
|
-
operation: 'complete',
|
|
134
|
-
preferredChannel: 'mcp',
|
|
135
|
-
reason: 'Atomic DB write with hooks',
|
|
136
|
-
},
|
|
137
|
-
{ domain: 'tasks', operation: 'start', preferredChannel: 'mcp', reason: 'Atomic DB write' },
|
|
138
|
-
{ domain: 'tasks', operation: 'stop', preferredChannel: 'mcp', reason: 'Atomic DB write' },
|
|
139
|
-
|
|
140
|
-
// === Session domain -- MCP preferred (stateful, in-process) ===
|
|
141
|
-
{ domain: 'session', operation: 'status', preferredChannel: 'mcp', reason: 'Quick state lookup' },
|
|
142
|
-
{ domain: 'session', operation: 'start', preferredChannel: 'mcp', reason: 'State transition' },
|
|
143
|
-
{
|
|
144
|
-
domain: 'session',
|
|
145
|
-
operation: 'end',
|
|
146
|
-
preferredChannel: 'mcp',
|
|
147
|
-
reason: 'State transition with hooks',
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
domain: 'session',
|
|
151
|
-
operation: 'handoff.show',
|
|
152
|
-
preferredChannel: 'mcp',
|
|
153
|
-
reason: 'Structured handoff data',
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
domain: 'session',
|
|
157
|
-
operation: 'briefing.show',
|
|
158
|
-
preferredChannel: 'mcp',
|
|
159
|
-
reason: 'Composite cold-start',
|
|
160
|
-
},
|
|
161
|
-
|
|
162
|
-
// === Admin domain -- either (human interaction common) ===
|
|
163
|
-
{ domain: 'admin', operation: 'version', preferredChannel: 'either', reason: 'Simple lookup' },
|
|
164
|
-
{ domain: 'admin', operation: 'health', preferredChannel: 'either', reason: 'Diagnostics' },
|
|
165
|
-
{ domain: 'admin', operation: 'dash', preferredChannel: 'mcp', reason: 'Composite view' },
|
|
166
|
-
{ domain: 'admin', operation: 'help', preferredChannel: 'mcp', reason: 'Operation discovery' },
|
|
167
|
-
{
|
|
168
|
-
domain: 'admin',
|
|
169
|
-
operation: 'map',
|
|
170
|
-
preferredChannel: 'mcp',
|
|
171
|
-
reason: 'Structured codebase analysis',
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
// === Tools domain -- MCP preferred (structured responses) ===
|
|
175
|
-
{
|
|
176
|
-
domain: 'tools',
|
|
177
|
-
operation: 'skill.list',
|
|
178
|
-
preferredChannel: 'mcp',
|
|
179
|
-
reason: 'Structured catalog',
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
domain: 'tools',
|
|
183
|
-
operation: 'skill.show',
|
|
184
|
-
preferredChannel: 'mcp',
|
|
185
|
-
reason: 'Structured skill data',
|
|
186
|
-
},
|
|
187
|
-
{ domain: 'tools', operation: 'skill.find', preferredChannel: 'mcp', reason: 'Search response' },
|
|
188
|
-
{
|
|
189
|
-
domain: 'tools',
|
|
190
|
-
operation: 'skill.install',
|
|
191
|
-
preferredChannel: 'either',
|
|
192
|
-
reason: 'May need shell access',
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
domain: 'tools',
|
|
196
|
-
operation: 'provider.list',
|
|
197
|
-
preferredChannel: 'mcp',
|
|
198
|
-
reason: 'Structured list',
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
domain: 'tools',
|
|
202
|
-
operation: 'provider.detect',
|
|
203
|
-
preferredChannel: 'mcp',
|
|
204
|
-
reason: 'Detection result',
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
domain: 'tools',
|
|
208
|
-
operation: 'adapter.list',
|
|
209
|
-
preferredChannel: 'mcp',
|
|
210
|
-
reason: 'Structured list',
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
domain: 'tools',
|
|
214
|
-
operation: 'adapter.show',
|
|
215
|
-
preferredChannel: 'mcp',
|
|
216
|
-
reason: 'Structured manifest',
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
domain: 'tools',
|
|
220
|
-
operation: 'adapter.activate',
|
|
221
|
-
preferredChannel: 'mcp',
|
|
222
|
-
reason: 'State transition',
|
|
223
|
-
},
|
|
224
|
-
|
|
225
|
-
// === Check domain -- MCP preferred (validation results) ===
|
|
226
|
-
{
|
|
227
|
-
domain: 'check',
|
|
228
|
-
operation: 'validate',
|
|
229
|
-
preferredChannel: 'mcp',
|
|
230
|
-
reason: 'Structured validation',
|
|
231
|
-
},
|
|
232
|
-
{
|
|
233
|
-
domain: 'check',
|
|
234
|
-
operation: 'compliance',
|
|
235
|
-
preferredChannel: 'mcp',
|
|
236
|
-
reason: 'Compliance report',
|
|
237
|
-
},
|
|
238
|
-
|
|
239
|
-
// === Pipeline domain -- MCP preferred (lifecycle gates) ===
|
|
240
|
-
{
|
|
241
|
-
domain: 'pipeline',
|
|
242
|
-
operation: 'lifecycle.status',
|
|
243
|
-
preferredChannel: 'mcp',
|
|
244
|
-
reason: 'Gate status',
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
domain: 'pipeline',
|
|
248
|
-
operation: 'release.ship',
|
|
249
|
-
preferredChannel: 'cli',
|
|
250
|
-
reason: 'Complex multi-step with git ops',
|
|
251
|
-
},
|
|
252
|
-
|
|
253
|
-
// === Orchestrate domain -- MCP preferred (agent coordination) ===
|
|
254
|
-
{ domain: 'orchestrate', operation: 'spawn', preferredChannel: 'mcp', reason: 'Agent spawning' },
|
|
255
|
-
{ domain: 'orchestrate', operation: 'plan', preferredChannel: 'mcp', reason: 'Decomposition' },
|
|
256
|
-
|
|
257
|
-
// === Nexus domain -- either (cross-project queries) ===
|
|
258
|
-
{
|
|
259
|
-
domain: 'nexus',
|
|
260
|
-
operation: 'search',
|
|
261
|
-
preferredChannel: 'either',
|
|
262
|
-
reason: 'Cross-project search',
|
|
263
|
-
},
|
|
264
|
-
{ domain: 'nexus', operation: 'sync', preferredChannel: 'either', reason: 'Cross-project sync' },
|
|
265
|
-
|
|
266
|
-
// === Sticky domain -- MCP preferred (quick ephemeral notes) ===
|
|
267
|
-
{ domain: 'sticky', operation: 'add', preferredChannel: 'mcp', reason: 'Quick DB write' },
|
|
268
|
-
{ domain: 'sticky', operation: 'list', preferredChannel: 'mcp', reason: 'Quick DB read' },
|
|
269
|
-
{ domain: 'sticky', operation: 'show', preferredChannel: 'mcp', reason: 'Quick DB read' },
|
|
270
|
-
];
|
|
271
|
-
|
|
272
36
|
/**
|
|
273
37
|
* Look up the preferred channel for a given domain + operation.
|
|
274
38
|
*
|
|
39
|
+
* Reads from the merged capability matrix (single SSoT).
|
|
40
|
+
*
|
|
275
41
|
* @param domain - Domain name
|
|
276
42
|
* @param operation - Operation name
|
|
277
43
|
* @returns Preferred channel ('mcp', 'cli', or 'either' as fallback)
|
|
278
44
|
*/
|
|
279
45
|
export function getPreferredChannel(domain: string, operation: string): 'mcp' | 'cli' | 'either' {
|
|
280
|
-
const entry =
|
|
46
|
+
const entry = getCapabilityMatrix().find(
|
|
47
|
+
(cap) => cap.domain === domain && cap.operation === operation,
|
|
48
|
+
);
|
|
281
49
|
return entry?.preferredChannel ?? 'either';
|
|
282
50
|
}
|
|
283
51
|
|
|
284
52
|
/**
|
|
285
53
|
* Get routing entries for a specific domain.
|
|
286
54
|
*
|
|
55
|
+
* Derives entries from the capability matrix.
|
|
56
|
+
*
|
|
287
57
|
* @param domain - Domain name
|
|
288
58
|
* @returns All routing entries for the domain
|
|
289
59
|
*/
|
|
290
60
|
export function getRoutingForDomain(domain: string): RoutingEntry[] {
|
|
291
|
-
return
|
|
61
|
+
return getCapabilityMatrix()
|
|
62
|
+
.filter((cap) => cap.domain === domain)
|
|
63
|
+
.map((cap) => ({
|
|
64
|
+
domain: cap.domain,
|
|
65
|
+
operation: cap.operation,
|
|
66
|
+
preferredChannel: cap.preferredChannel,
|
|
67
|
+
reason: '',
|
|
68
|
+
}));
|
|
292
69
|
}
|
|
293
70
|
|
|
294
71
|
/**
|
|
295
72
|
* Get all operations that prefer a specific channel.
|
|
296
73
|
*
|
|
74
|
+
* Derives entries from the capability matrix.
|
|
75
|
+
*
|
|
297
76
|
* @param channel - Channel preference to filter by
|
|
298
77
|
* @returns Matching routing entries
|
|
299
78
|
*/
|
|
300
79
|
export function getOperationsByChannel(channel: 'mcp' | 'cli' | 'either'): RoutingEntry[] {
|
|
301
|
-
return
|
|
80
|
+
return getCapabilityMatrix()
|
|
81
|
+
.filter((cap) => cap.preferredChannel === channel)
|
|
82
|
+
.map((cap) => ({
|
|
83
|
+
domain: cap.domain,
|
|
84
|
+
operation: cap.operation,
|
|
85
|
+
preferredChannel: cap.preferredChannel,
|
|
86
|
+
reason: '',
|
|
87
|
+
}));
|
|
302
88
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { existsSync, lstatSync, readlinkSync, realpathSync } from 'node:fs';
|
|
17
17
|
import { delimiter, join, resolve } from 'node:path';
|
|
18
|
-
import {
|
|
18
|
+
import { getCanonicalSkillsDir } from '@cleocode/caamp';
|
|
19
19
|
|
|
20
20
|
/** Source type classification for a skill directory. */
|
|
21
21
|
export type SkillSourceType = 'embedded' | 'caamp' | 'project-link' | 'global-link';
|
|
@@ -31,10 +31,11 @@ export interface SkillSearchPath {
|
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Get the CAAMP canonical skill location.
|
|
34
|
+
* Delegates to caamp's getCanonicalSkillsDir() which is XDG-aware.
|
|
34
35
|
* @task T4552
|
|
35
36
|
*/
|
|
36
37
|
function getCaampCanonical(): string {
|
|
37
|
-
return
|
|
38
|
+
return getCanonicalSkillsDir();
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
/**
|
|
@@ -14,7 +14,7 @@ import { detectProjectType } from '../project-detect.js';
|
|
|
14
14
|
|
|
15
15
|
// ─── Schema validator setup ───────────────────────────────────────────────────
|
|
16
16
|
const schemaPath = fileURLToPath(
|
|
17
|
-
new URL('
|
|
17
|
+
new URL('../../../schemas/project-context.schema.json', import.meta.url),
|
|
18
18
|
);
|
|
19
19
|
const schema = JSON.parse(readFileSync(schemaPath, 'utf-8'));
|
|
20
20
|
const Ajv = (AjvModule as any).default ?? AjvModule;
|
|
@@ -50,13 +50,17 @@ export function getBrainDbPath(cwd?: string): string {
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Resolve the path to the drizzle-brain migrations folder.
|
|
53
|
-
* Works from both src/ (dev via tsx) and dist/ (compiled).
|
|
53
|
+
* Works from both src/ (dev via tsx) and dist/ (compiled via esbuild bundle).
|
|
54
|
+
*
|
|
55
|
+
* - Source layout: __dirname = src/store/ → need ../../migrations/drizzle-brain
|
|
56
|
+
* - Bundled layout: __dirname = dist/ → need ../migrations/drizzle-brain
|
|
54
57
|
*/
|
|
55
58
|
export function resolveBrainMigrationsFolder(): string {
|
|
56
59
|
const __filename = fileURLToPath(import.meta.url);
|
|
57
60
|
const __dirname = dirname(__filename);
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
const isBundled = __dirname.endsWith('/dist') || __dirname.endsWith('\\dist');
|
|
62
|
+
const pkgRoot = isBundled ? join(__dirname, '..') : join(__dirname, '..', '..');
|
|
63
|
+
return join(pkgRoot, 'migrations', 'drizzle-brain');
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
/**
|
|
@@ -46,13 +46,17 @@ export function getNexusDbPath(): string {
|
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Resolve the path to the drizzle-nexus migrations folder.
|
|
49
|
-
* Works from both src/ (dev via tsx) and dist/ (compiled).
|
|
49
|
+
* Works from both src/ (dev via tsx) and dist/ (compiled via esbuild bundle).
|
|
50
|
+
*
|
|
51
|
+
* - Source layout: __dirname = src/store/ → need ../../migrations/drizzle-nexus
|
|
52
|
+
* - Bundled layout: __dirname = dist/ → need ../migrations/drizzle-nexus
|
|
50
53
|
*/
|
|
51
54
|
export function resolveNexusMigrationsFolder(): string {
|
|
52
55
|
const __filename = fileURLToPath(import.meta.url);
|
|
53
56
|
const __dirname = dirname(__filename);
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
const isBundled = __dirname.endsWith('/dist') || __dirname.endsWith('\\dist');
|
|
58
|
+
const pkgRoot = isBundled ? join(__dirname, '..') : join(__dirname, '..', '..');
|
|
59
|
+
return join(pkgRoot, 'migrations', 'drizzle-nexus');
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
/**
|
package/src/store/sqlite.ts
CHANGED
|
@@ -357,13 +357,19 @@ export async function getDb(cwd?: string): Promise<NodeSQLiteDatabase<typeof sch
|
|
|
357
357
|
|
|
358
358
|
/**
|
|
359
359
|
* Resolve the path to the drizzle migrations folder.
|
|
360
|
-
* Works from both src/ (dev via tsx) and dist/ (compiled).
|
|
360
|
+
* Works from both src/ (dev via tsx) and dist/ (compiled via esbuild bundle).
|
|
361
|
+
*
|
|
362
|
+
* - Source layout: __dirname = src/store/ → need ../../migrations/drizzle-tasks
|
|
363
|
+
* - Bundled layout: __dirname = dist/ → need ../migrations/drizzle-tasks
|
|
361
364
|
*/
|
|
362
365
|
export function resolveMigrationsFolder(): string {
|
|
363
366
|
const __filename = fileURLToPath(import.meta.url);
|
|
364
367
|
const __dirname = dirname(__filename);
|
|
365
|
-
//
|
|
366
|
-
|
|
368
|
+
// When esbuild bundles into dist/index.js, __dirname is dist/ (1 level deep).
|
|
369
|
+
// When running from source via tsx, __dirname is src/store/ (2 levels deep).
|
|
370
|
+
const isBundled = __dirname.endsWith('/dist') || __dirname.endsWith('\\dist');
|
|
371
|
+
const pkgRoot = isBundled ? join(__dirname, '..') : join(__dirname, '..', '..');
|
|
372
|
+
return join(pkgRoot, 'migrations', 'drizzle-tasks');
|
|
367
373
|
}
|
|
368
374
|
|
|
369
375
|
/**
|
|
@@ -127,7 +127,7 @@ export const TASK_RELATION_TYPES = [
|
|
|
127
127
|
export const LIFECYCLE_TRANSITION_TYPES = ['automatic', 'manual', 'forced'] as const;
|
|
128
128
|
|
|
129
129
|
/** External task link types matching DB constraint on external_task_links.link_type. */
|
|
130
|
-
export const EXTERNAL_LINK_TYPES = ['created', 'matched', 'manual'] as const;
|
|
130
|
+
export const EXTERNAL_LINK_TYPES = ['created', 'matched', 'manual', 'transferred'] as const;
|
|
131
131
|
|
|
132
132
|
/** Sync direction types matching DB constraint on external_task_links.sync_direction. */
|
|
133
133
|
export const SYNC_DIRECTIONS = ['inbound', 'outbound', 'bidirectional'] as const;
|