@cleocode/core 2026.3.45 → 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/bootstrap.d.ts.map +1 -1
- package/dist/index.js +1475 -372
- 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/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/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/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/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 +1 -1
- 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/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;
|
package/src/system/health.ts
CHANGED
|
@@ -1036,7 +1036,7 @@ export async function startupHealthCheck(projectRoot?: string): Promise<StartupH
|
|
|
1036
1036
|
!globalSchemaCheck.ok;
|
|
1037
1037
|
|
|
1038
1038
|
if (globalNeedsRepair) {
|
|
1039
|
-
// Auto-repair
|
|
1039
|
+
// Auto-repair home and templates via ensureGlobalScaffold (idempotent, safe)
|
|
1040
1040
|
const scaffoldResult = await ensureGlobalScaffold();
|
|
1041
1041
|
|
|
1042
1042
|
checks.push({
|
|
@@ -1049,12 +1049,23 @@ export async function startupHealthCheck(projectRoot?: string): Promise<StartupH
|
|
|
1049
1049
|
repaired: scaffoldResult.home.action !== 'skipped',
|
|
1050
1050
|
});
|
|
1051
1051
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1052
|
+
// Schemas are read at runtime from the package path; only copy if stale
|
|
1053
|
+
if (!globalSchemaCheck.ok) {
|
|
1054
|
+
const { ensureGlobalSchemas } = await import('../schema-management.js');
|
|
1055
|
+
const schemaResult = ensureGlobalSchemas();
|
|
1056
|
+
checks.push({
|
|
1057
|
+
check: 'global_schemas',
|
|
1058
|
+
status: 'pass',
|
|
1059
|
+
message: `Schemas: ${schemaResult.installed} installed, ${schemaResult.updated} updated of ${schemaResult.total}`,
|
|
1060
|
+
repaired: schemaResult.installed > 0 || schemaResult.updated > 0,
|
|
1061
|
+
});
|
|
1062
|
+
} else {
|
|
1063
|
+
checks.push({
|
|
1064
|
+
check: 'global_schemas',
|
|
1065
|
+
status: 'pass',
|
|
1066
|
+
message: `All ${globalSchemaCheck.installed} schemas current`,
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1058
1069
|
|
|
1059
1070
|
checks.push({
|
|
1060
1071
|
check: 'global_templates',
|
package/src/ui/index.ts
CHANGED
|
@@ -59,9 +59,3 @@ export {
|
|
|
59
59
|
// injection.ts -> CAAMP inject()/checkInjection()
|
|
60
60
|
// mcp-config.ts -> CAAMP detectAllProviders()/installMcpServer()
|
|
61
61
|
// injection-registry.ts -> CAAMP getInstructionFiles()
|
|
62
|
-
|
|
63
|
-
// Injection legacy utilities (kept for validation output support)
|
|
64
|
-
export {
|
|
65
|
-
getValidationKey,
|
|
66
|
-
INJECTION_VALIDATION_KEYS,
|
|
67
|
-
} from './injection-legacy.js';
|
|
@@ -326,7 +326,7 @@ export async function validateLayer2Semantic(context: OperationContext): Promise
|
|
|
326
326
|
// Session scope validation
|
|
327
327
|
if (context.domain === 'session' && context.operation === 'start') {
|
|
328
328
|
const scope = context.params?.scope as string | undefined;
|
|
329
|
-
if (scope && !scope.match(/^(epic|task
|
|
329
|
+
if (scope && scope !== 'global' && !scope.match(/^(epic|task):/)) {
|
|
330
330
|
violations.push({
|
|
331
331
|
layer: GateLayer.SEMANTIC,
|
|
332
332
|
severity: ErrorSeverity.ERROR,
|
|
@@ -334,7 +334,7 @@ export async function validateLayer2Semantic(context: OperationContext): Promise
|
|
|
334
334
|
message: `Invalid session scope format: ${scope}`,
|
|
335
335
|
field: 'scope',
|
|
336
336
|
constraint: 'Must be epic:<id>, task:<id>, or global',
|
|
337
|
-
fix: '
|
|
337
|
+
fix: 'Provide scope as "global" or "epic:TXXX"',
|
|
338
338
|
});
|
|
339
339
|
}
|
|
340
340
|
}
|