@cleocode/core 2026.3.43 → 2026.3.45
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/export-tasks.d.ts.map +1 -1
- package/dist/admin/import-tasks.d.ts +10 -2
- package/dist/admin/import-tasks.d.ts.map +1 -1
- package/dist/agents/agent-schema.d.ts +358 -0
- package/dist/agents/agent-schema.d.ts.map +1 -0
- package/dist/agents/capacity.d.ts +57 -0
- package/dist/agents/capacity.d.ts.map +1 -0
- package/dist/agents/index.d.ts +17 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/registry.d.ts +115 -0
- package/dist/agents/registry.d.ts.map +1 -0
- package/dist/agents/retry.d.ts +83 -0
- package/dist/agents/retry.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +4 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/payload-schemas.d.ts +214 -0
- package/dist/hooks/payload-schemas.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16937 -2371
- package/dist/index.js.map +4 -4
- package/dist/inject/index.d.ts.map +1 -1
- package/dist/intelligence/impact.d.ts +51 -0
- package/dist/intelligence/impact.d.ts.map +1 -0
- package/dist/intelligence/index.d.ts +15 -0
- package/dist/intelligence/index.d.ts.map +1 -0
- package/dist/intelligence/patterns.d.ts +66 -0
- package/dist/intelligence/patterns.d.ts.map +1 -0
- package/dist/intelligence/prediction.d.ts +51 -0
- package/dist/intelligence/prediction.d.ts.map +1 -0
- package/dist/intelligence/types.d.ts +221 -0
- package/dist/intelligence/types.d.ts.map +1 -0
- package/dist/internal.d.ts +12 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/issue/template-parser.d.ts +8 -2
- package/dist/issue/template-parser.d.ts.map +1 -1
- package/dist/lifecycle/pipeline.d.ts +2 -2
- package/dist/lifecycle/pipeline.d.ts.map +1 -1
- package/dist/lifecycle/state-machine.d.ts +1 -1
- package/dist/lifecycle/state-machine.d.ts.map +1 -1
- package/dist/memory/brain-lifecycle.d.ts.map +1 -1
- package/dist/memory/brain-retrieval.d.ts.map +1 -1
- package/dist/memory/brain-row-types.d.ts +40 -6
- package/dist/memory/brain-row-types.d.ts.map +1 -1
- package/dist/memory/brain-search.d.ts.map +1 -1
- package/dist/memory/brain-similarity.d.ts.map +1 -1
- package/dist/memory/claude-mem-migration.d.ts.map +1 -1
- package/dist/nexus/discover.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/orchestration/bootstrap.d.ts.map +1 -1
- package/dist/orchestration/skill-ops.d.ts +4 -4
- package/dist/orchestration/skill-ops.d.ts.map +1 -1
- package/dist/otel/index.d.ts +1 -1
- package/dist/otel/index.d.ts.map +1 -1
- package/dist/sessions/briefing.d.ts.map +1 -1
- package/dist/sessions/handoff.d.ts.map +1 -1
- package/dist/sessions/index.d.ts +1 -1
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/sessions/types.d.ts +8 -42
- package/dist/sessions/types.d.ts.map +1 -1
- package/dist/signaldock/signaldock-transport.d.ts +1 -1
- package/dist/signaldock/signaldock-transport.d.ts.map +1 -1
- package/dist/skills/injection/subagent.d.ts +3 -3
- package/dist/skills/injection/subagent.d.ts.map +1 -1
- package/dist/skills/manifests/contribution.d.ts +2 -2
- package/dist/skills/manifests/contribution.d.ts.map +1 -1
- package/dist/skills/orchestrator/spawn.d.ts +6 -6
- package/dist/skills/orchestrator/spawn.d.ts.map +1 -1
- package/dist/skills/orchestrator/startup.d.ts +1 -1
- package/dist/skills/orchestrator/startup.d.ts.map +1 -1
- package/dist/skills/orchestrator/validator.d.ts +2 -2
- package/dist/skills/orchestrator/validator.d.ts.map +1 -1
- package/dist/skills/precedence-types.d.ts +24 -1
- package/dist/skills/precedence-types.d.ts.map +1 -1
- package/dist/skills/types.d.ts +70 -4
- package/dist/skills/types.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/export.d.ts +5 -4
- package/dist/store/export.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 +14 -4
- package/dist/store/tasks-schema.d.ts.map +1 -1
- package/dist/store/typed-query.d.ts +12 -0
- package/dist/store/typed-query.d.ts.map +1 -0
- package/dist/store/validation-schemas.d.ts +2423 -50
- package/dist/store/validation-schemas.d.ts.map +1 -1
- package/dist/system/inject-generate.d.ts.map +1 -1
- package/dist/validation/doctor/checks.d.ts +5 -0
- package/dist/validation/doctor/checks.d.ts.map +1 -1
- package/dist/validation/engine.d.ts +10 -10
- package/dist/validation/engine.d.ts.map +1 -1
- package/dist/validation/index.d.ts +6 -2
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/protocol-common.d.ts +10 -2
- package/dist/validation/protocol-common.d.ts.map +1 -1
- package/migrations/drizzle-tasks/20260320013731_wave0-schema-hardening/migration.sql +84 -0
- package/migrations/drizzle-tasks/20260320013731_wave0-schema-hardening/snapshot.json +4060 -0
- package/migrations/drizzle-tasks/20260320020000_agent-dimension/migration.sql +35 -0
- package/migrations/drizzle-tasks/20260320020000_agent-dimension/snapshot.json +4312 -0
- package/package.json +2 -2
- package/src/admin/export-tasks.ts +2 -5
- package/src/admin/import-tasks.ts +53 -29
- package/src/agents/__tests__/capacity.test.ts +219 -0
- package/src/agents/__tests__/registry.test.ts +457 -0
- package/src/agents/__tests__/retry.test.ts +289 -0
- package/src/agents/agent-schema.ts +107 -0
- package/src/agents/capacity.ts +151 -0
- package/src/agents/index.ts +68 -0
- package/src/agents/registry.ts +449 -0
- package/src/agents/retry.ts +255 -0
- package/src/hooks/index.ts +20 -1
- package/src/hooks/payload-schemas.ts +199 -0
- package/src/index.ts +69 -0
- package/src/inject/index.ts +14 -14
- package/src/intelligence/__tests__/impact.test.ts +453 -0
- package/src/intelligence/__tests__/patterns.test.ts +450 -0
- package/src/intelligence/__tests__/prediction.test.ts +418 -0
- package/src/intelligence/impact.ts +638 -0
- package/src/intelligence/index.ts +47 -0
- package/src/intelligence/patterns.ts +621 -0
- package/src/intelligence/prediction.ts +621 -0
- package/src/intelligence/types.ts +273 -0
- package/src/internal.ts +89 -2
- package/src/issue/template-parser.ts +65 -4
- package/src/lifecycle/pipeline.ts +14 -7
- package/src/lifecycle/state-machine.ts +6 -2
- package/src/memory/brain-lifecycle.ts +5 -11
- package/src/memory/brain-retrieval.ts +44 -38
- package/src/memory/brain-row-types.ts +43 -6
- package/src/memory/brain-search.ts +53 -32
- package/src/memory/brain-similarity.ts +9 -8
- package/src/memory/claude-mem-migration.ts +4 -3
- package/src/nexus/__tests__/nexus-e2e.test.ts +1481 -0
- package/src/nexus/__tests__/transfer.test.ts +446 -0
- package/src/nexus/discover.ts +1 -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/orchestration/bootstrap.ts +11 -17
- package/src/orchestration/skill-ops.ts +52 -32
- package/src/otel/index.ts +48 -4
- package/src/sessions/__tests__/briefing.test.ts +31 -2
- package/src/sessions/briefing.ts +27 -42
- package/src/sessions/handoff.ts +52 -86
- package/src/sessions/index.ts +5 -1
- package/src/sessions/types.ts +9 -43
- package/src/signaldock/signaldock-transport.ts +5 -2
- package/src/skills/injection/subagent.ts +10 -16
- package/src/skills/manifests/contribution.ts +5 -13
- package/src/skills/orchestrator/__tests__/spawn-tier.test.ts +44 -30
- package/src/skills/orchestrator/spawn.ts +18 -31
- package/src/skills/orchestrator/startup.ts +78 -65
- package/src/skills/orchestrator/validator.ts +26 -31
- package/src/skills/precedence-types.ts +24 -1
- package/src/skills/types.ts +72 -5
- package/src/store/__tests__/test-db-helper.d.ts +4 -4
- package/src/store/__tests__/test-db-helper.js +5 -16
- package/src/store/__tests__/test-db-helper.ts +5 -18
- package/src/store/brain-sqlite.ts +7 -3
- package/src/store/chain-schema.ts +1 -1
- package/src/store/export.ts +22 -12
- package/src/store/nexus-sqlite.ts +7 -3
- package/src/store/sqlite.ts +9 -3
- package/src/store/tasks-schema.ts +65 -8
- package/src/store/typed-query.ts +17 -0
- package/src/store/validation-schemas.ts +347 -23
- package/src/system/inject-generate.ts +9 -23
- package/src/validation/doctor/checks.ts +24 -2
- package/src/validation/engine.ts +11 -11
- package/src/validation/index.ts +131 -3
- package/src/validation/protocol-common.ts +54 -3
- package/dist/tasks/reparent.d.ts +0 -38
- package/dist/tasks/reparent.d.ts.map +0 -1
- package/src/tasks/reparent.ts +0 -134
|
@@ -29,6 +29,22 @@ vi.mock('../../discovery.js', () => ({
|
|
|
29
29
|
mapSkillName: vi.fn(() => ({ canonical: 'task-executor', mapped: true })),
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
|
+
vi.mock('../../../store/data-accessor.js', () => ({
|
|
33
|
+
getAccessor: vi.fn().mockResolvedValue({
|
|
34
|
+
loadSingleTask: vi.fn().mockResolvedValue({
|
|
35
|
+
id: 'T100',
|
|
36
|
+
title: 'Test task',
|
|
37
|
+
status: 'active',
|
|
38
|
+
description: 'A test',
|
|
39
|
+
priority: 'medium',
|
|
40
|
+
labels: [],
|
|
41
|
+
depends: [],
|
|
42
|
+
}),
|
|
43
|
+
loadTasks: vi.fn().mockResolvedValue([]),
|
|
44
|
+
queryTasks: vi.fn().mockResolvedValue({ tasks: [], total: 0 }),
|
|
45
|
+
}),
|
|
46
|
+
}));
|
|
47
|
+
|
|
32
48
|
import { existsSync, readFileSync } from 'node:fs';
|
|
33
49
|
import { findSkill } from '../../discovery.js';
|
|
34
50
|
import { injectProtocol, orchestratorSpawnSkill } from '../../injection/subagent.js';
|
|
@@ -67,7 +83,7 @@ describe('orchestratorSpawnSkill tier passthrough', () => {
|
|
|
67
83
|
} as any);
|
|
68
84
|
});
|
|
69
85
|
|
|
70
|
-
it('passes tier 0 through — output excludes standard and orchestrator', () => {
|
|
86
|
+
it('passes tier 0 through — output excludes standard and orchestrator', async () => {
|
|
71
87
|
vi.mocked(readFileSync).mockImplementation((path: unknown) => {
|
|
72
88
|
const p = String(path);
|
|
73
89
|
if (p.includes('subagent-protocol-base')) return TIERED_PROTOCOL;
|
|
@@ -75,7 +91,7 @@ describe('orchestratorSpawnSkill tier passthrough', () => {
|
|
|
75
91
|
return '';
|
|
76
92
|
});
|
|
77
93
|
|
|
78
|
-
const result = orchestratorSpawnSkill(
|
|
94
|
+
const result = await orchestratorSpawnSkill(
|
|
79
95
|
'T100',
|
|
80
96
|
'test-skill',
|
|
81
97
|
{ TASK_ID: 'T100' },
|
|
@@ -89,7 +105,7 @@ describe('orchestratorSpawnSkill tier passthrough', () => {
|
|
|
89
105
|
expect(result).toContain('Test Skill');
|
|
90
106
|
});
|
|
91
107
|
|
|
92
|
-
it('passes tier 1 through — output includes minimal + standard', () => {
|
|
108
|
+
it('passes tier 1 through — output includes minimal + standard', async () => {
|
|
93
109
|
vi.mocked(readFileSync).mockImplementation((path: unknown) => {
|
|
94
110
|
const p = String(path);
|
|
95
111
|
if (p.includes('subagent-protocol-base')) return TIERED_PROTOCOL;
|
|
@@ -97,7 +113,7 @@ describe('orchestratorSpawnSkill tier passthrough', () => {
|
|
|
97
113
|
return '';
|
|
98
114
|
});
|
|
99
115
|
|
|
100
|
-
const result = orchestratorSpawnSkill(
|
|
116
|
+
const result = await orchestratorSpawnSkill(
|
|
101
117
|
'T100',
|
|
102
118
|
'test-skill',
|
|
103
119
|
{ TASK_ID: 'T100' },
|
|
@@ -110,7 +126,7 @@ describe('orchestratorSpawnSkill tier passthrough', () => {
|
|
|
110
126
|
expect(result).not.toContain('Orchestrator Section');
|
|
111
127
|
});
|
|
112
128
|
|
|
113
|
-
it('passes tier 2 through — output includes all tiers', () => {
|
|
129
|
+
it('passes tier 2 through — output includes all tiers', async () => {
|
|
114
130
|
vi.mocked(readFileSync).mockImplementation((path: unknown) => {
|
|
115
131
|
const p = String(path);
|
|
116
132
|
if (p.includes('subagent-protocol-base')) return TIERED_PROTOCOL;
|
|
@@ -118,7 +134,7 @@ describe('orchestratorSpawnSkill tier passthrough', () => {
|
|
|
118
134
|
return '';
|
|
119
135
|
});
|
|
120
136
|
|
|
121
|
-
const result = orchestratorSpawnSkill(
|
|
137
|
+
const result = await orchestratorSpawnSkill(
|
|
122
138
|
'T100',
|
|
123
139
|
'test-skill',
|
|
124
140
|
{ TASK_ID: 'T100' },
|
|
@@ -131,7 +147,7 @@ describe('orchestratorSpawnSkill tier passthrough', () => {
|
|
|
131
147
|
expect(result).toContain('Orchestrator Section');
|
|
132
148
|
});
|
|
133
149
|
|
|
134
|
-
it('omitting tier includes full protocol (backward compat)', () => {
|
|
150
|
+
it('omitting tier includes full protocol (backward compat)', async () => {
|
|
135
151
|
vi.mocked(readFileSync).mockImplementation((path: unknown) => {
|
|
136
152
|
const p = String(path);
|
|
137
153
|
if (p.includes('subagent-protocol-base')) return TIERED_PROTOCOL;
|
|
@@ -140,7 +156,7 @@ describe('orchestratorSpawnSkill tier passthrough', () => {
|
|
|
140
156
|
});
|
|
141
157
|
|
|
142
158
|
// No tier parameter — should pass through unfiltered
|
|
143
|
-
const result = orchestratorSpawnSkill(
|
|
159
|
+
const result = await orchestratorSpawnSkill(
|
|
144
160
|
'T100',
|
|
145
161
|
'test-skill',
|
|
146
162
|
{ TASK_ID: 'T100' },
|
|
@@ -153,12 +169,12 @@ describe('orchestratorSpawnSkill tier passthrough', () => {
|
|
|
153
169
|
expect(result).toContain('TIER:orchestrator');
|
|
154
170
|
});
|
|
155
171
|
|
|
156
|
-
it('throws when skill not found', () => {
|
|
172
|
+
it('throws when skill not found', async () => {
|
|
157
173
|
vi.mocked(findSkill).mockReturnValue(null);
|
|
158
174
|
|
|
159
|
-
expect(
|
|
160
|
-
orchestratorSpawnSkill('T100', 'nonexistent', { TASK_ID: 'T100' }, '/mock/project', 0)
|
|
161
|
-
|
|
175
|
+
await expect(
|
|
176
|
+
orchestratorSpawnSkill('T100', 'nonexistent', { TASK_ID: 'T100' }, '/mock/project', 0),
|
|
177
|
+
).rejects.toThrow(/Skill not found/);
|
|
162
178
|
});
|
|
163
179
|
});
|
|
164
180
|
|
|
@@ -168,29 +184,27 @@ describe('injectProtocol tier parameter', () => {
|
|
|
168
184
|
vi.mocked(existsSync).mockReturnValue(true);
|
|
169
185
|
});
|
|
170
186
|
|
|
171
|
-
it('tier undefined leaves protocol unfiltered', () => {
|
|
187
|
+
it('tier undefined leaves protocol unfiltered', async () => {
|
|
172
188
|
vi.mocked(readFileSync).mockImplementation((path: unknown) => {
|
|
173
189
|
const p = String(path);
|
|
174
190
|
if (p.includes('subagent-protocol-base')) return TIERED_PROTOCOL;
|
|
175
|
-
if (p.includes('tasks.json')) return MOCK_TASKS_JSON;
|
|
176
191
|
return '';
|
|
177
192
|
});
|
|
178
193
|
|
|
179
|
-
const result = injectProtocol('# Skill', 'T100', {}, '/mock/project', undefined);
|
|
194
|
+
const result = await injectProtocol('# Skill', 'T100', {}, '/mock/project', undefined);
|
|
180
195
|
|
|
181
196
|
expect(result).toContain('TIER:minimal');
|
|
182
197
|
expect(result).toContain('TIER:orchestrator');
|
|
183
198
|
});
|
|
184
199
|
|
|
185
|
-
it('tier 0 filters to minimal only', () => {
|
|
200
|
+
it('tier 0 filters to minimal only', async () => {
|
|
186
201
|
vi.mocked(readFileSync).mockImplementation((path: unknown) => {
|
|
187
202
|
const p = String(path);
|
|
188
203
|
if (p.includes('subagent-protocol-base')) return TIERED_PROTOCOL;
|
|
189
|
-
if (p.includes('tasks.json')) return MOCK_TASKS_JSON;
|
|
190
204
|
return '';
|
|
191
205
|
});
|
|
192
206
|
|
|
193
|
-
const result = injectProtocol('# Skill', 'T100', {}, '/mock/project', 0);
|
|
207
|
+
const result = await injectProtocol('# Skill', 'T100', {}, '/mock/project', 0);
|
|
194
208
|
|
|
195
209
|
expect(result).toContain('Minimal Section');
|
|
196
210
|
expect(result).not.toContain('Standard Section');
|
|
@@ -218,43 +232,43 @@ describe('buildPrompt tier passthrough', () => {
|
|
|
218
232
|
} as any);
|
|
219
233
|
});
|
|
220
234
|
|
|
221
|
-
it('buildPrompt with tier 0 filters protocol to minimal only', () => {
|
|
222
|
-
const result = buildPrompt('T100', 'TASK-EXECUTOR', '/mock/project', 0);
|
|
235
|
+
it('buildPrompt with tier 0 filters protocol to minimal only', async () => {
|
|
236
|
+
const result = await buildPrompt('T100', 'TASK-EXECUTOR', '/mock/project', 0);
|
|
223
237
|
expect(result.prompt).toContain('Minimal Section');
|
|
224
238
|
expect(result.prompt).not.toContain('Standard Section');
|
|
225
239
|
expect(result.prompt).not.toContain('Orchestrator Section');
|
|
226
240
|
expect(result.prompt).toContain('SUBAGENT PROTOCOL');
|
|
227
241
|
});
|
|
228
242
|
|
|
229
|
-
it('buildPrompt with tier 1 includes minimal + standard', () => {
|
|
230
|
-
const result = buildPrompt('T100', 'TASK-EXECUTOR', '/mock/project', 1);
|
|
243
|
+
it('buildPrompt with tier 1 includes minimal + standard', async () => {
|
|
244
|
+
const result = await buildPrompt('T100', 'TASK-EXECUTOR', '/mock/project', 1);
|
|
231
245
|
expect(result.prompt).toContain('Minimal Section');
|
|
232
246
|
expect(result.prompt).toContain('Standard Section');
|
|
233
247
|
expect(result.prompt).not.toContain('Orchestrator Section');
|
|
234
248
|
});
|
|
235
249
|
|
|
236
|
-
it('buildPrompt without tier includes full protocol', () => {
|
|
237
|
-
const result = buildPrompt('T100', 'TASK-EXECUTOR', '/mock/project');
|
|
250
|
+
it('buildPrompt without tier includes full protocol', async () => {
|
|
251
|
+
const result = await buildPrompt('T100', 'TASK-EXECUTOR', '/mock/project');
|
|
238
252
|
// No tier = unfiltered, so tier markers present
|
|
239
253
|
expect(result.prompt).toContain('TIER:minimal');
|
|
240
254
|
expect(result.prompt).toContain('TIER:orchestrator');
|
|
241
255
|
});
|
|
242
256
|
|
|
243
|
-
it('buildPrompt injects task context', () => {
|
|
244
|
-
const result = buildPrompt('T100', 'TASK-EXECUTOR', '/mock/project', 0);
|
|
257
|
+
it('buildPrompt injects task context', async () => {
|
|
258
|
+
const result = await buildPrompt('T100', 'TASK-EXECUTOR', '/mock/project', 0);
|
|
245
259
|
expect(result.prompt).toContain('Task Context');
|
|
246
260
|
expect(result.prompt).toContain('T100');
|
|
247
261
|
});
|
|
248
262
|
|
|
249
|
-
it('spawn passes tier through', () => {
|
|
250
|
-
const result = spawn('T100', 'TASK-EXECUTOR', '/mock/project', 0);
|
|
263
|
+
it('spawn passes tier through', async () => {
|
|
264
|
+
const result = await spawn('T100', 'TASK-EXECUTOR', '/mock/project', 0);
|
|
251
265
|
expect(result.prompt).toContain('Minimal Section');
|
|
252
266
|
expect(result.prompt).not.toContain('Orchestrator Section');
|
|
253
267
|
expect(result.spawnTimestamp).toBeDefined();
|
|
254
268
|
});
|
|
255
269
|
|
|
256
|
-
it('spawnBatch passes tier through', () => {
|
|
257
|
-
const result = spawnBatch(['T100'], 'TASK-EXECUTOR', '/mock/project', 0);
|
|
270
|
+
it('spawnBatch passes tier through', async () => {
|
|
271
|
+
const result = await spawnBatch(['T100'], 'TASK-EXECUTOR', '/mock/project', 0);
|
|
258
272
|
expect(result.succeeded).toBe(1);
|
|
259
273
|
expect(result.spawns[0].result!.prompt).toContain('Minimal Section');
|
|
260
274
|
expect(result.spawns[0].result!.prompt).not.toContain('Orchestrator Section');
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
* @task T4519
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
16
15
|
import type { Task } from '@cleocode/contracts';
|
|
17
16
|
import { ExitCode } from '@cleocode/contracts';
|
|
18
17
|
import { CleoError } from '../../errors.js';
|
|
19
|
-
import { getAgentOutputsDir
|
|
18
|
+
import { getAgentOutputsDir } from '../../paths.js';
|
|
19
|
+
import { getAccessor } from '../../store/data-accessor.js';
|
|
20
20
|
import { findSkill, mapSkillName } from '../discovery.js';
|
|
21
21
|
import { injectProtocol } from '../injection/subagent.js';
|
|
22
22
|
import type { TokenValues } from '../injection/token.js';
|
|
@@ -30,22 +30,14 @@ import type { SpawnPromptResult } from '../types.js';
|
|
|
30
30
|
* Build a fully-resolved prompt for spawning a subagent.
|
|
31
31
|
* @task T4519
|
|
32
32
|
*/
|
|
33
|
-
export function buildPrompt(
|
|
33
|
+
export async function buildPrompt(
|
|
34
34
|
taskId: string,
|
|
35
35
|
templateName: string = 'TASK-EXECUTOR',
|
|
36
36
|
cwd?: string,
|
|
37
37
|
tier?: 0 | 1 | 2,
|
|
38
|
-
): SpawnPromptResult {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
if (!existsSync(taskPath)) {
|
|
42
|
-
throw new CleoError(ExitCode.NOT_FOUND, 'Todo file not found. Run: cleo init');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Load task
|
|
46
|
-
const data = JSON.parse(readFileSync(taskPath, 'utf-8'));
|
|
47
|
-
const tasks: Task[] = data.tasks ?? [];
|
|
48
|
-
const task = tasks.find((t) => t.id === taskId);
|
|
38
|
+
): Promise<SpawnPromptResult> {
|
|
39
|
+
const acc = await getAccessor(cwd);
|
|
40
|
+
const task = await acc.loadSingleTask(taskId);
|
|
49
41
|
|
|
50
42
|
if (!task) {
|
|
51
43
|
throw new CleoError(ExitCode.NOT_FOUND, `Task ${taskId} not found`);
|
|
@@ -121,7 +113,7 @@ export function buildPrompt(
|
|
|
121
113
|
}
|
|
122
114
|
|
|
123
115
|
// Inject tokens and protocol into template
|
|
124
|
-
const promptContent = injectProtocol(skill.content, taskId, tokenValues, cwd, tier);
|
|
116
|
+
const promptContent = await injectProtocol(skill.content, taskId, tokenValues, cwd, tier);
|
|
125
117
|
|
|
126
118
|
return {
|
|
127
119
|
taskId,
|
|
@@ -138,13 +130,13 @@ export function buildPrompt(
|
|
|
138
130
|
* Generate full spawn command with metadata.
|
|
139
131
|
* @task T4519
|
|
140
132
|
*/
|
|
141
|
-
export function spawn(
|
|
133
|
+
export async function spawn(
|
|
142
134
|
taskId: string,
|
|
143
135
|
templateName: string = 'TASK-EXECUTOR',
|
|
144
136
|
cwd?: string,
|
|
145
137
|
tier?: 0 | 1 | 2,
|
|
146
|
-
): SpawnPromptResult & { spawnTimestamp: string } {
|
|
147
|
-
const result = buildPrompt(taskId, templateName, cwd, tier);
|
|
138
|
+
): Promise<SpawnPromptResult & { spawnTimestamp: string }> {
|
|
139
|
+
const result = await buildPrompt(taskId, templateName, cwd, tier);
|
|
148
140
|
|
|
149
141
|
return {
|
|
150
142
|
...result,
|
|
@@ -156,25 +148,20 @@ export function spawn(
|
|
|
156
148
|
* Check if tasks can be spawned in parallel (no inter-dependencies).
|
|
157
149
|
* @task T4519
|
|
158
150
|
*/
|
|
159
|
-
export function canParallelize(
|
|
151
|
+
export async function canParallelize(
|
|
160
152
|
taskIds: string[],
|
|
161
153
|
cwd?: string,
|
|
162
|
-
): {
|
|
154
|
+
): Promise<{
|
|
163
155
|
canParallelize: boolean;
|
|
164
156
|
conflicts: Array<Pick<Task, 'id'> & { dependsOn: string[] }>;
|
|
165
157
|
safeToSpawn: string[];
|
|
166
|
-
} {
|
|
158
|
+
}> {
|
|
167
159
|
if (taskIds.length === 0) {
|
|
168
160
|
return { canParallelize: true, conflicts: [], safeToSpawn: [] };
|
|
169
161
|
}
|
|
170
162
|
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
return { canParallelize: true, conflicts: [], safeToSpawn: taskIds };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const data = JSON.parse(readFileSync(taskPath, 'utf-8'));
|
|
177
|
-
const tasks: Task[] = data.tasks ?? [];
|
|
163
|
+
const acc = await getAccessor(cwd);
|
|
164
|
+
const tasks = await acc.loadTasks(taskIds);
|
|
178
165
|
const taskIdSet = new Set(taskIds);
|
|
179
166
|
|
|
180
167
|
const conflicts: Array<Pick<Task, 'id'> & { dependsOn: string[] }> = [];
|
|
@@ -229,17 +216,17 @@ export interface BatchSpawnResult {
|
|
|
229
216
|
* @task T4712
|
|
230
217
|
* @epic T4663
|
|
231
218
|
*/
|
|
232
|
-
export function spawnBatch(
|
|
219
|
+
export async function spawnBatch(
|
|
233
220
|
taskIds: string[],
|
|
234
221
|
templateName?: string,
|
|
235
222
|
cwd?: string,
|
|
236
223
|
tier?: 0 | 1 | 2,
|
|
237
|
-
): BatchSpawnResult {
|
|
224
|
+
): Promise<BatchSpawnResult> {
|
|
238
225
|
const spawns: BatchSpawnEntry[] = [];
|
|
239
226
|
|
|
240
227
|
for (const taskId of taskIds) {
|
|
241
228
|
try {
|
|
242
|
-
const result = spawn(taskId, templateName, cwd, tier);
|
|
229
|
+
const result = await spawn(taskId, templateName, cwd, tier);
|
|
243
230
|
spawns.push({ taskId, success: true, result });
|
|
244
231
|
} catch (err: unknown) {
|
|
245
232
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
import { existsSync, readFileSync } from 'node:fs';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
|
-
import type { Task, TaskRef, TaskRefPriority } from '@cleocode/contracts';
|
|
15
|
-
import { getCleoDirAbsolute
|
|
14
|
+
import type { Task, TaskRef, TaskRefPriority, TaskWorkState } from '@cleocode/contracts';
|
|
15
|
+
import { getCleoDirAbsolute } from '../../paths.js';
|
|
16
|
+
import { getAccessor } from '../../store/data-accessor.js';
|
|
16
17
|
import type {
|
|
17
18
|
DependencyAnalysis,
|
|
18
19
|
DependencyWave,
|
|
@@ -134,47 +135,65 @@ export interface SessionInitResult {
|
|
|
134
135
|
* Determines the recommended action based on current state.
|
|
135
136
|
* @task T4519
|
|
136
137
|
*/
|
|
137
|
-
export async function sessionInit(
|
|
138
|
-
const
|
|
139
|
-
const cleoDirAbs = getCleoDirAbsolute(cwd);
|
|
140
|
-
const focusPath = join(cleoDirAbs, 'focus.json');
|
|
138
|
+
export async function sessionInit(epicId?: string, cwd?: string): Promise<SessionInitResult> {
|
|
139
|
+
const acc = await getAccessor(cwd);
|
|
141
140
|
|
|
142
|
-
// Check active sessions
|
|
141
|
+
// Check active sessions from SQLite (ADR-006/ADR-020)
|
|
143
142
|
let activeSessions = 0;
|
|
144
143
|
let activeSessionId: string | null = null;
|
|
145
144
|
let activeScope: string | null = null;
|
|
146
145
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
146
|
+
try {
|
|
147
|
+
const sessions = await acc.loadSessions();
|
|
148
|
+
let active = sessions.filter((s) => s.status === 'active');
|
|
149
|
+
|
|
150
|
+
// If epicId provided, prefer sessions scoped to that epic
|
|
151
|
+
if (epicId && active.length > 0) {
|
|
152
|
+
const epicScoped = active.filter(
|
|
153
|
+
(s) => s.scope?.rootTaskId === epicId || s.scope?.epicId === epicId,
|
|
154
|
+
);
|
|
155
|
+
if (epicScoped.length > 0) {
|
|
156
|
+
active = epicScoped;
|
|
155
157
|
}
|
|
156
|
-
} catch {
|
|
157
|
-
// Invalid sessions file
|
|
158
158
|
}
|
|
159
|
+
|
|
160
|
+
activeSessions = active.length;
|
|
161
|
+
if (activeSessions > 0) {
|
|
162
|
+
activeSessionId = active[0].id ?? null;
|
|
163
|
+
activeScope = active[0].scope?.rootTaskId ?? active[0].scope?.epicId ?? null;
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
// DB unavailable
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// If epicId provided and no active session found, set activeScope to the epic
|
|
170
|
+
if (epicId && !activeScope) {
|
|
171
|
+
activeScope = epicId;
|
|
159
172
|
}
|
|
160
173
|
|
|
161
|
-
// Check focus
|
|
174
|
+
// Check focus state from SQLite meta KV
|
|
162
175
|
let hasFocus = false;
|
|
163
176
|
let focusedTask: string | null = null;
|
|
164
177
|
|
|
165
|
-
|
|
178
|
+
try {
|
|
179
|
+
const focus = await acc.getMetaValue<TaskWorkState>('focus_state');
|
|
180
|
+
focusedTask = focus?.currentTask ?? null;
|
|
181
|
+
hasFocus = !!focusedTask;
|
|
182
|
+
} catch {
|
|
183
|
+
// Focus unavailable
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Determine pending work: check if the epic has ready tasks via DataAccessor
|
|
187
|
+
let hasPending = false;
|
|
188
|
+
if (epicId) {
|
|
166
189
|
try {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
hasFocus = !!focusedTask;
|
|
190
|
+
const children = await acc.getChildren(epicId);
|
|
191
|
+
hasPending = children.some((t) => t.status === 'pending');
|
|
170
192
|
} catch {
|
|
171
|
-
//
|
|
193
|
+
// Tasks unavailable
|
|
172
194
|
}
|
|
173
195
|
}
|
|
174
196
|
|
|
175
|
-
// Determine pending work (simplified - check manifest for needs_followup)
|
|
176
|
-
const hasPending = false; // Will be populated by manifest checks
|
|
177
|
-
|
|
178
197
|
// Decision matrix
|
|
179
198
|
let recommendedAction: SessionInitResult['recommendedAction'];
|
|
180
199
|
let actionReason: string;
|
|
@@ -187,7 +206,9 @@ export async function sessionInit(_epicId?: string, cwd?: string): Promise<Sessi
|
|
|
187
206
|
actionReason = 'Active session without focus - query manifest and spawn next agent';
|
|
188
207
|
} else if (hasPending) {
|
|
189
208
|
recommendedAction = 'create_and_spawn';
|
|
190
|
-
actionReason =
|
|
209
|
+
actionReason = epicId
|
|
210
|
+
? `No session but epic ${epicId} has pending tasks - create session and spawn`
|
|
211
|
+
: 'No session but manifest has followups - create session and spawn';
|
|
191
212
|
} else {
|
|
192
213
|
recommendedAction = 'request_direction';
|
|
193
214
|
actionReason = 'No session, no pending work - await user direction';
|
|
@@ -275,9 +296,10 @@ export async function analyzeDependencies(
|
|
|
275
296
|
epicId: string,
|
|
276
297
|
cwd?: string,
|
|
277
298
|
): Promise<DependencyAnalysis> {
|
|
278
|
-
const
|
|
299
|
+
const acc = await getAccessor(cwd);
|
|
300
|
+
const epicTasks = await acc.getChildren(epicId);
|
|
279
301
|
|
|
280
|
-
if (
|
|
302
|
+
if (epicTasks.length === 0) {
|
|
281
303
|
return {
|
|
282
304
|
epicId,
|
|
283
305
|
totalTasks: 0,
|
|
@@ -290,10 +312,9 @@ export async function analyzeDependencies(
|
|
|
290
312
|
};
|
|
291
313
|
}
|
|
292
314
|
|
|
293
|
-
|
|
294
|
-
const tasks:
|
|
295
|
-
const
|
|
296
|
-
const doneIds = new Set(tasks.filter((t) => t.status === 'done').map((t) => t.id));
|
|
315
|
+
// Need full task set to resolve cross-epic dependency status
|
|
316
|
+
const { tasks: allTasks } = await acc.queryTasks({});
|
|
317
|
+
const doneIds = new Set(allTasks.filter((t) => t.status === 'done').map((t) => t.id));
|
|
297
318
|
const epicIds = new Set(epicTasks.map((t) => t.id));
|
|
298
319
|
|
|
299
320
|
// Compute waves iteratively
|
|
@@ -389,12 +410,10 @@ export async function getNextTask(
|
|
|
389
410
|
return { task: null, readyCount: 0 };
|
|
390
411
|
}
|
|
391
412
|
|
|
392
|
-
// Load full task details
|
|
393
|
-
const
|
|
394
|
-
const data = JSON.parse(readFileSync(taskPath, 'utf-8'));
|
|
395
|
-
const tasks: Task[] = data.tasks ?? [];
|
|
413
|
+
// Load full task details from SQLite
|
|
414
|
+
const acc = await getAccessor(cwd);
|
|
396
415
|
const nextId = analysis.readyToSpawn[0].id;
|
|
397
|
-
const task =
|
|
416
|
+
const task = await acc.loadSingleTask(nextId);
|
|
398
417
|
|
|
399
418
|
return { task, readyCount: analysis.readyToSpawn.length };
|
|
400
419
|
}
|
|
@@ -407,16 +426,13 @@ export async function getReadyTasks(epicId: string, cwd?: string): Promise<TaskR
|
|
|
407
426
|
const analysis = await analyzeDependencies(epicId, cwd);
|
|
408
427
|
const readyIds = new Set(analysis.readyToSpawn.map((t) => t.id));
|
|
409
428
|
|
|
410
|
-
//
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const data = JSON.parse(readFileSync(taskPath, 'utf-8'));
|
|
415
|
-
const tasks: Task[] = data.tasks ?? [];
|
|
429
|
+
// Load full task details from SQLite to check inter-ready dependencies
|
|
430
|
+
const acc = await getAccessor(cwd);
|
|
431
|
+
const readyTasksFull = await acc.loadTasks([...readyIds]);
|
|
416
432
|
|
|
417
433
|
return analysis.readyToSpawn
|
|
418
434
|
.filter((ready) => {
|
|
419
|
-
const task =
|
|
435
|
+
const task = readyTasksFull.find((t) => t.id === ready.id);
|
|
420
436
|
if (!task) return false;
|
|
421
437
|
const deps = task.depends ?? [];
|
|
422
438
|
// Only include if no deps are also in the ready set
|
|
@@ -438,31 +454,29 @@ export async function generateHitlSummary(
|
|
|
438
454
|
stopReason: string = 'context-limit',
|
|
439
455
|
cwd?: string,
|
|
440
456
|
): Promise<HitlSummary> {
|
|
441
|
-
const
|
|
442
|
-
const cleoDirAbs = getCleoDirAbsolute(cwd);
|
|
457
|
+
const acc = await getAccessor(cwd);
|
|
443
458
|
|
|
444
|
-
// Session info
|
|
459
|
+
// Session info from SQLite (ADR-006/ADR-020)
|
|
445
460
|
let sessionId: string | null = null;
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
sessionId =
|
|
461
|
+
try {
|
|
462
|
+
const activeSession = await acc.getActiveSession();
|
|
463
|
+
sessionId = activeSession?.id ?? null;
|
|
464
|
+
} catch {
|
|
465
|
+
// DB unavailable
|
|
449
466
|
}
|
|
450
467
|
|
|
451
|
-
// Focus info
|
|
468
|
+
// Focus info from SQLite meta KV
|
|
452
469
|
let focusedTask: string | null = null;
|
|
453
470
|
let progressNote: string | null = null;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
} catch {
|
|
461
|
-
// ignore
|
|
462
|
-
}
|
|
471
|
+
try {
|
|
472
|
+
const focus = await acc.getMetaValue<TaskWorkState>('focus_state');
|
|
473
|
+
focusedTask = focus?.currentTask ?? null;
|
|
474
|
+
progressNote = focus?.sessionNote ?? null;
|
|
475
|
+
} catch {
|
|
476
|
+
// Focus unavailable
|
|
463
477
|
}
|
|
464
478
|
|
|
465
|
-
// Task statistics
|
|
479
|
+
// Task statistics from SQLite via DataAccessor
|
|
466
480
|
let completed = 0,
|
|
467
481
|
pending = 0,
|
|
468
482
|
active = 0,
|
|
@@ -471,9 +485,8 @@ export async function generateHitlSummary(
|
|
|
471
485
|
let remainingTasks: Array<TaskRef & { priority?: string }> = [];
|
|
472
486
|
let readyToSpawn: TaskRefPriority[] = [];
|
|
473
487
|
|
|
474
|
-
if (
|
|
475
|
-
const
|
|
476
|
-
const tasks: Task[] = (data.tasks ?? []).filter((t: Task) => t.parentId === epicId);
|
|
488
|
+
if (epicId) {
|
|
489
|
+
const tasks = await acc.getChildren(epicId);
|
|
477
490
|
|
|
478
491
|
completed = tasks.filter((t) => t.status === 'done').length;
|
|
479
492
|
pending = tasks.filter((t) => t.status === 'pending').length;
|
|
@@ -11,13 +11,12 @@
|
|
|
11
11
|
|
|
12
12
|
import { existsSync, readFileSync } from 'node:fs';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
|
-
import type { Task } from '@cleocode/contracts';
|
|
15
14
|
import { MANIFEST_STATUSES } from '@cleocode/contracts';
|
|
16
15
|
import {
|
|
17
16
|
getAgentOutputsAbsolute,
|
|
18
17
|
getManifestPath as getManifestPathFromPaths,
|
|
19
|
-
getTaskPath,
|
|
20
18
|
} from '../../paths.js';
|
|
19
|
+
import { getAccessor } from '../../store/data-accessor.js';
|
|
21
20
|
import type { ComplianceResult, ManifestEntry, ManifestValidationResult } from '../types.js';
|
|
22
21
|
|
|
23
22
|
// ============================================================================
|
|
@@ -310,14 +309,14 @@ export function verifyCompliance(
|
|
|
310
309
|
* Validate orchestrator compliance (post-hoc behavioral checks).
|
|
311
310
|
* @task T4519
|
|
312
311
|
*/
|
|
313
|
-
export function validateOrchestratorCompliance(
|
|
312
|
+
export async function validateOrchestratorCompliance(
|
|
314
313
|
epicId?: string,
|
|
315
314
|
cwd?: string,
|
|
316
|
-
): {
|
|
315
|
+
): Promise<{
|
|
317
316
|
compliant: boolean;
|
|
318
317
|
violations: string[];
|
|
319
318
|
warnings: string[];
|
|
320
|
-
} {
|
|
319
|
+
}> {
|
|
321
320
|
const violations: string[] = [];
|
|
322
321
|
const warnings: string[] = [];
|
|
323
322
|
|
|
@@ -333,36 +332,32 @@ export function validateOrchestratorCompliance(
|
|
|
333
332
|
}
|
|
334
333
|
}
|
|
335
334
|
|
|
336
|
-
// Check dependency order (ORC-004) for completed tasks
|
|
335
|
+
// Check dependency order (ORC-004) for completed tasks via DataAccessor
|
|
337
336
|
if (epicId) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
violations.push(
|
|
358
|
-
`ORC-004_DEPENDENCY_ORDER: Task ${sorted[i].id} completed before dependency ${dep}`,
|
|
359
|
-
);
|
|
360
|
-
}
|
|
337
|
+
try {
|
|
338
|
+
const acc = await getAccessor(cwd);
|
|
339
|
+
const children = await acc.getChildren(epicId);
|
|
340
|
+
const tasks = children.filter((t) => t.status === 'done');
|
|
341
|
+
|
|
342
|
+
// Sort by updatedAt and check for dependency violations
|
|
343
|
+
const sorted = tasks.sort(
|
|
344
|
+
(a, b) => new Date(a.updatedAt ?? '').getTime() - new Date(b.updatedAt ?? '').getTime(),
|
|
345
|
+
);
|
|
346
|
+
const completionOrder = sorted.map((t) => t.id);
|
|
347
|
+
|
|
348
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
349
|
+
const deps = sorted[i].depends ?? [];
|
|
350
|
+
for (const dep of deps) {
|
|
351
|
+
const depIdx = completionOrder.indexOf(dep);
|
|
352
|
+
if (depIdx >= 0 && depIdx >= i) {
|
|
353
|
+
violations.push(
|
|
354
|
+
`ORC-004_DEPENDENCY_ORDER: Task ${sorted[i].id} completed before dependency ${dep}`,
|
|
355
|
+
);
|
|
361
356
|
}
|
|
362
357
|
}
|
|
363
|
-
} catch {
|
|
364
|
-
// Skip on parse error
|
|
365
358
|
}
|
|
359
|
+
} catch {
|
|
360
|
+
// Skip on DB error
|
|
366
361
|
}
|
|
367
362
|
}
|
|
368
363
|
|