@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.
Files changed (183) hide show
  1. package/dist/admin/export-tasks.d.ts.map +1 -1
  2. package/dist/admin/import-tasks.d.ts +10 -2
  3. package/dist/admin/import-tasks.d.ts.map +1 -1
  4. package/dist/agents/agent-schema.d.ts +358 -0
  5. package/dist/agents/agent-schema.d.ts.map +1 -0
  6. package/dist/agents/capacity.d.ts +57 -0
  7. package/dist/agents/capacity.d.ts.map +1 -0
  8. package/dist/agents/index.d.ts +17 -0
  9. package/dist/agents/index.d.ts.map +1 -0
  10. package/dist/agents/registry.d.ts +115 -0
  11. package/dist/agents/registry.d.ts.map +1 -0
  12. package/dist/agents/retry.d.ts +83 -0
  13. package/dist/agents/retry.d.ts.map +1 -0
  14. package/dist/hooks/index.d.ts +4 -1
  15. package/dist/hooks/index.d.ts.map +1 -1
  16. package/dist/hooks/payload-schemas.d.ts +214 -0
  17. package/dist/hooks/payload-schemas.d.ts.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +16937 -2371
  21. package/dist/index.js.map +4 -4
  22. package/dist/inject/index.d.ts.map +1 -1
  23. package/dist/intelligence/impact.d.ts +51 -0
  24. package/dist/intelligence/impact.d.ts.map +1 -0
  25. package/dist/intelligence/index.d.ts +15 -0
  26. package/dist/intelligence/index.d.ts.map +1 -0
  27. package/dist/intelligence/patterns.d.ts +66 -0
  28. package/dist/intelligence/patterns.d.ts.map +1 -0
  29. package/dist/intelligence/prediction.d.ts +51 -0
  30. package/dist/intelligence/prediction.d.ts.map +1 -0
  31. package/dist/intelligence/types.d.ts +221 -0
  32. package/dist/intelligence/types.d.ts.map +1 -0
  33. package/dist/internal.d.ts +12 -1
  34. package/dist/internal.d.ts.map +1 -1
  35. package/dist/issue/template-parser.d.ts +8 -2
  36. package/dist/issue/template-parser.d.ts.map +1 -1
  37. package/dist/lifecycle/pipeline.d.ts +2 -2
  38. package/dist/lifecycle/pipeline.d.ts.map +1 -1
  39. package/dist/lifecycle/state-machine.d.ts +1 -1
  40. package/dist/lifecycle/state-machine.d.ts.map +1 -1
  41. package/dist/memory/brain-lifecycle.d.ts.map +1 -1
  42. package/dist/memory/brain-retrieval.d.ts.map +1 -1
  43. package/dist/memory/brain-row-types.d.ts +40 -6
  44. package/dist/memory/brain-row-types.d.ts.map +1 -1
  45. package/dist/memory/brain-search.d.ts.map +1 -1
  46. package/dist/memory/brain-similarity.d.ts.map +1 -1
  47. package/dist/memory/claude-mem-migration.d.ts.map +1 -1
  48. package/dist/nexus/discover.d.ts.map +1 -1
  49. package/dist/nexus/index.d.ts +2 -0
  50. package/dist/nexus/index.d.ts.map +1 -1
  51. package/dist/nexus/transfer-types.d.ts +123 -0
  52. package/dist/nexus/transfer-types.d.ts.map +1 -0
  53. package/dist/nexus/transfer.d.ts +31 -0
  54. package/dist/nexus/transfer.d.ts.map +1 -0
  55. package/dist/orchestration/bootstrap.d.ts.map +1 -1
  56. package/dist/orchestration/skill-ops.d.ts +4 -4
  57. package/dist/orchestration/skill-ops.d.ts.map +1 -1
  58. package/dist/otel/index.d.ts +1 -1
  59. package/dist/otel/index.d.ts.map +1 -1
  60. package/dist/sessions/briefing.d.ts.map +1 -1
  61. package/dist/sessions/handoff.d.ts.map +1 -1
  62. package/dist/sessions/index.d.ts +1 -1
  63. package/dist/sessions/index.d.ts.map +1 -1
  64. package/dist/sessions/types.d.ts +8 -42
  65. package/dist/sessions/types.d.ts.map +1 -1
  66. package/dist/signaldock/signaldock-transport.d.ts +1 -1
  67. package/dist/signaldock/signaldock-transport.d.ts.map +1 -1
  68. package/dist/skills/injection/subagent.d.ts +3 -3
  69. package/dist/skills/injection/subagent.d.ts.map +1 -1
  70. package/dist/skills/manifests/contribution.d.ts +2 -2
  71. package/dist/skills/manifests/contribution.d.ts.map +1 -1
  72. package/dist/skills/orchestrator/spawn.d.ts +6 -6
  73. package/dist/skills/orchestrator/spawn.d.ts.map +1 -1
  74. package/dist/skills/orchestrator/startup.d.ts +1 -1
  75. package/dist/skills/orchestrator/startup.d.ts.map +1 -1
  76. package/dist/skills/orchestrator/validator.d.ts +2 -2
  77. package/dist/skills/orchestrator/validator.d.ts.map +1 -1
  78. package/dist/skills/precedence-types.d.ts +24 -1
  79. package/dist/skills/precedence-types.d.ts.map +1 -1
  80. package/dist/skills/types.d.ts +70 -4
  81. package/dist/skills/types.d.ts.map +1 -1
  82. package/dist/store/brain-sqlite.d.ts +4 -1
  83. package/dist/store/brain-sqlite.d.ts.map +1 -1
  84. package/dist/store/export.d.ts +5 -4
  85. package/dist/store/export.d.ts.map +1 -1
  86. package/dist/store/nexus-sqlite.d.ts +4 -1
  87. package/dist/store/nexus-sqlite.d.ts.map +1 -1
  88. package/dist/store/sqlite.d.ts +4 -1
  89. package/dist/store/sqlite.d.ts.map +1 -1
  90. package/dist/store/tasks-schema.d.ts +14 -4
  91. package/dist/store/tasks-schema.d.ts.map +1 -1
  92. package/dist/store/typed-query.d.ts +12 -0
  93. package/dist/store/typed-query.d.ts.map +1 -0
  94. package/dist/store/validation-schemas.d.ts +2423 -50
  95. package/dist/store/validation-schemas.d.ts.map +1 -1
  96. package/dist/system/inject-generate.d.ts.map +1 -1
  97. package/dist/validation/doctor/checks.d.ts +5 -0
  98. package/dist/validation/doctor/checks.d.ts.map +1 -1
  99. package/dist/validation/engine.d.ts +10 -10
  100. package/dist/validation/engine.d.ts.map +1 -1
  101. package/dist/validation/index.d.ts +6 -2
  102. package/dist/validation/index.d.ts.map +1 -1
  103. package/dist/validation/protocol-common.d.ts +10 -2
  104. package/dist/validation/protocol-common.d.ts.map +1 -1
  105. package/migrations/drizzle-tasks/20260320013731_wave0-schema-hardening/migration.sql +84 -0
  106. package/migrations/drizzle-tasks/20260320013731_wave0-schema-hardening/snapshot.json +4060 -0
  107. package/migrations/drizzle-tasks/20260320020000_agent-dimension/migration.sql +35 -0
  108. package/migrations/drizzle-tasks/20260320020000_agent-dimension/snapshot.json +4312 -0
  109. package/package.json +2 -2
  110. package/src/admin/export-tasks.ts +2 -5
  111. package/src/admin/import-tasks.ts +53 -29
  112. package/src/agents/__tests__/capacity.test.ts +219 -0
  113. package/src/agents/__tests__/registry.test.ts +457 -0
  114. package/src/agents/__tests__/retry.test.ts +289 -0
  115. package/src/agents/agent-schema.ts +107 -0
  116. package/src/agents/capacity.ts +151 -0
  117. package/src/agents/index.ts +68 -0
  118. package/src/agents/registry.ts +449 -0
  119. package/src/agents/retry.ts +255 -0
  120. package/src/hooks/index.ts +20 -1
  121. package/src/hooks/payload-schemas.ts +199 -0
  122. package/src/index.ts +69 -0
  123. package/src/inject/index.ts +14 -14
  124. package/src/intelligence/__tests__/impact.test.ts +453 -0
  125. package/src/intelligence/__tests__/patterns.test.ts +450 -0
  126. package/src/intelligence/__tests__/prediction.test.ts +418 -0
  127. package/src/intelligence/impact.ts +638 -0
  128. package/src/intelligence/index.ts +47 -0
  129. package/src/intelligence/patterns.ts +621 -0
  130. package/src/intelligence/prediction.ts +621 -0
  131. package/src/intelligence/types.ts +273 -0
  132. package/src/internal.ts +89 -2
  133. package/src/issue/template-parser.ts +65 -4
  134. package/src/lifecycle/pipeline.ts +14 -7
  135. package/src/lifecycle/state-machine.ts +6 -2
  136. package/src/memory/brain-lifecycle.ts +5 -11
  137. package/src/memory/brain-retrieval.ts +44 -38
  138. package/src/memory/brain-row-types.ts +43 -6
  139. package/src/memory/brain-search.ts +53 -32
  140. package/src/memory/brain-similarity.ts +9 -8
  141. package/src/memory/claude-mem-migration.ts +4 -3
  142. package/src/nexus/__tests__/nexus-e2e.test.ts +1481 -0
  143. package/src/nexus/__tests__/transfer.test.ts +446 -0
  144. package/src/nexus/discover.ts +1 -0
  145. package/src/nexus/index.ts +14 -0
  146. package/src/nexus/transfer-types.ts +129 -0
  147. package/src/nexus/transfer.ts +314 -0
  148. package/src/orchestration/bootstrap.ts +11 -17
  149. package/src/orchestration/skill-ops.ts +52 -32
  150. package/src/otel/index.ts +48 -4
  151. package/src/sessions/__tests__/briefing.test.ts +31 -2
  152. package/src/sessions/briefing.ts +27 -42
  153. package/src/sessions/handoff.ts +52 -86
  154. package/src/sessions/index.ts +5 -1
  155. package/src/sessions/types.ts +9 -43
  156. package/src/signaldock/signaldock-transport.ts +5 -2
  157. package/src/skills/injection/subagent.ts +10 -16
  158. package/src/skills/manifests/contribution.ts +5 -13
  159. package/src/skills/orchestrator/__tests__/spawn-tier.test.ts +44 -30
  160. package/src/skills/orchestrator/spawn.ts +18 -31
  161. package/src/skills/orchestrator/startup.ts +78 -65
  162. package/src/skills/orchestrator/validator.ts +26 -31
  163. package/src/skills/precedence-types.ts +24 -1
  164. package/src/skills/types.ts +72 -5
  165. package/src/store/__tests__/test-db-helper.d.ts +4 -4
  166. package/src/store/__tests__/test-db-helper.js +5 -16
  167. package/src/store/__tests__/test-db-helper.ts +5 -18
  168. package/src/store/brain-sqlite.ts +7 -3
  169. package/src/store/chain-schema.ts +1 -1
  170. package/src/store/export.ts +22 -12
  171. package/src/store/nexus-sqlite.ts +7 -3
  172. package/src/store/sqlite.ts +9 -3
  173. package/src/store/tasks-schema.ts +65 -8
  174. package/src/store/typed-query.ts +17 -0
  175. package/src/store/validation-schemas.ts +347 -23
  176. package/src/system/inject-generate.ts +9 -23
  177. package/src/validation/doctor/checks.ts +24 -2
  178. package/src/validation/engine.ts +11 -11
  179. package/src/validation/index.ts +131 -3
  180. package/src/validation/protocol-common.ts +54 -3
  181. package/dist/tasks/reparent.d.ts +0 -38
  182. package/dist/tasks/reparent.d.ts.map +0 -1
  183. package/src/tasks/reparent.ts +0 -134
@@ -1,6 +1,14 @@
1
1
  /**
2
2
  * Skills Precedence Types - Phase 3B of T5238
3
- * Re-exports CAAMP precedence types and adds CLEO-specific wrapper types.
3
+ *
4
+ * Re-exports CAAMP precedence types and adds CLEO-specific wrapper types
5
+ * for skill path resolution and installation context.
6
+ *
7
+ * These types complement the skill types in `./types.ts`:
8
+ * - `./types.ts` defines the CLEO skill domain model (Skill, SkillFrontmatter, etc.)
9
+ * - This file defines CAAMP precedence integration types used when resolving
10
+ * which skill version takes priority across providers and scopes.
11
+ *
4
12
  * @task T5238
5
13
  */
6
14
 
@@ -21,23 +29,38 @@ export {
21
29
 
22
30
  import type { SkillsPrecedence } from '@cleocode/caamp';
23
31
 
32
+ /** Configuration for skill precedence resolution across providers. */
24
33
  export interface SkillsPrecedenceConfig {
34
+ /** Default precedence mode when no provider-specific override exists. */
25
35
  defaultPrecedence?: SkillsPrecedence;
36
+ /** Per-provider precedence overrides (provider ID -> precedence). */
26
37
  providerOverrides?: Record<string, SkillsPrecedence>;
27
38
  }
28
39
 
40
+ /** A resolved skill path with full provenance metadata. */
29
41
  export interface ResolvedSkillPath {
42
+ /** Absolute filesystem path to the skill directory. */
30
43
  path: string;
44
+ /** Where the skill was sourced from. */
31
45
  source: 'vendor' | 'agents' | 'marketplace';
46
+ /** Whether this is a global or project-scoped skill. */
32
47
  scope: 'global' | 'project';
48
+ /** The precedence mode that selected this path. */
33
49
  precedence: SkillsPrecedence;
50
+ /** The provider that owns this skill path. */
34
51
  providerId: string;
35
52
  }
36
53
 
54
+ /** Context for a skill installation operation. */
37
55
  export interface SkillInstallationContext {
56
+ /** Name of the skill being installed. */
38
57
  skillName: string;
58
+ /** Source URL or path to install from. */
39
59
  source: string;
60
+ /** Provider IDs to install the skill for. */
40
61
  targetProviders: string[];
62
+ /** Precedence mode for the installation. */
41
63
  precedenceMode?: SkillsPrecedence;
64
+ /** Project root for project-scoped installations. */
42
65
  projectRoot?: string;
43
66
  }
@@ -1,13 +1,46 @@
1
1
  /**
2
2
  * Type definitions for the skills system.
3
- * Re-exports overlapping CAAMP types for downstream consumers.
3
+ *
4
+ * ## Dual Type System: CAAMP vs CLEO Skills
5
+ *
6
+ * The skill type system has two layers reflecting the CAAMP/CLEO split:
7
+ *
8
+ * ### CAAMP types (re-exported as `CaampSkillMetadata`, `CtSkillEntry`, etc.)
9
+ * - Defined in `@cleocode/caamp` -- the cross-agent skill standard.
10
+ * - **`CaampSkillMetadata`**: Minimal metadata parsed from SKILL.md YAML header
11
+ * by CAAMP's `parseSkillFile()`. Contains: name, description, version, license,
12
+ * compatibility, metadata, allowedTools.
13
+ * - **`CtSkillEntry`**: A discovered skill with location + metadata. Used by CAAMP's
14
+ * discovery and install pipeline.
15
+ * - Use CAAMP types when interacting with CAAMP APIs (skill discovery, install,
16
+ * provider lock files, skill precedence resolution).
17
+ *
18
+ * ### CLEO types (`Skill`, `SkillFrontmatter`, `SkillSummary`, `SkillManifest`)
19
+ * - Defined here -- CLEO's extended skill domain model.
20
+ * - **`SkillFrontmatter`**: Superset of CaampSkillMetadata. Adds CLEO-specific
21
+ * fields: tags, triggers, dispatchPriority, model, invocable, command, protocol.
22
+ * Parsed by CLEO's own SKILL.md reader with richer YAML support.
23
+ * - **`Skill`**: Full in-memory skill object with filesystem paths and parsed content.
24
+ * - **`SkillSummary`**: Lightweight projection for listings (cached in SkillManifest).
25
+ * - Use CLEO types for skill dispatch, orchestration, CLI display, and manifest caching.
26
+ *
27
+ * ### Relationship
28
+ * - `SkillFrontmatter` is a functional superset of `CaampSkillMetadata`:
29
+ * every field in CaampSkillMetadata exists in SkillFrontmatter (name, description,
30
+ * version, allowedTools). SkillFrontmatter adds CLEO-specific extension fields.
31
+ * - A `Skill` (CLEO) wraps a `SkillFrontmatter` with filesystem context, whereas
32
+ * a `CtSkillEntry` (CAAMP) wraps a `CaampSkillMetadata` with install metadata.
33
+ * - When converting between layers, map `CaampSkillMetadata` -> `SkillFrontmatter`
34
+ * (all CAAMP fields carry over; CLEO-specific fields default to undefined).
35
+ *
4
36
  * @epic T4454
5
37
  * @task T4516
6
38
  */
7
39
 
8
40
  import type { TaskRef, TaskRefPriority } from '@cleocode/contracts';
9
41
 
10
- // Re-export CAAMP types where they overlap with CLEO's domain
42
+ // Re-export CAAMP types where they overlap with CLEO's domain.
43
+ // See module JSDoc above for guidance on which type to use where.
11
44
  export type {
12
45
  CtDispatchMatrix,
13
46
  CtManifest,
@@ -25,23 +58,52 @@ export type {
25
58
  // Skill Types
26
59
  // ============================================================================
27
60
 
28
- /** Skill frontmatter parsed from SKILL.md YAML header. */
61
+ /**
62
+ * Skill frontmatter parsed from SKILL.md YAML header.
63
+ *
64
+ * This is CLEO's extended skill metadata -- a functional superset of CAAMP's
65
+ * `SkillMetadata`. All fields from `CaampSkillMetadata` (name, description,
66
+ * version, allowedTools) are present here. CLEO adds: tags, triggers,
67
+ * dispatchPriority, model, invocable, command, protocol.
68
+ *
69
+ * Use this type when working with CLEO's skill loading, dispatch, and
70
+ * orchestration systems. Use `CaampSkillMetadata` when interfacing directly
71
+ * with CAAMP's discovery/install APIs.
72
+ */
29
73
  export interface SkillFrontmatter {
74
+ /** Skill name (lowercase, hyphens). Maps to CaampSkillMetadata.name. */
30
75
  name: string;
76
+ /** Human-readable description. Maps to CaampSkillMetadata.description. */
31
77
  description: string;
78
+ /** Semantic version string. Maps to CaampSkillMetadata.version. */
32
79
  version?: string;
80
+ /** Skill author (CLEO extension). */
33
81
  author?: string;
82
+ /** Classification tags for search/filter (CLEO extension). */
34
83
  tags?: string[];
84
+ /** Trigger patterns for auto-dispatch (CLEO extension). */
35
85
  triggers?: string[];
86
+ /** Priority for dispatch selection (CLEO extension). */
36
87
  dispatchPriority?: number;
88
+ /** Preferred LLM model (CLEO extension). */
37
89
  model?: string;
90
+ /** Allowed tool names. Maps to CaampSkillMetadata.allowedTools. */
38
91
  allowedTools?: string[];
92
+ /** Whether the skill can be invoked directly (CLEO extension). */
39
93
  invocable?: boolean;
94
+ /** CLI command for direct invocation (CLEO extension). */
40
95
  command?: string;
96
+ /** RCASD-IVTR+C protocol type (CLEO extension). */
41
97
  protocol?: SkillProtocolType;
42
98
  }
43
99
 
44
- /** Skill definition loaded from disk. */
100
+ /**
101
+ * Skill definition loaded from disk.
102
+ *
103
+ * CLEO-specific type that wraps a {@link SkillFrontmatter} with filesystem
104
+ * context. For CAAMP's equivalent, see `CtSkillEntry` which wraps
105
+ * `CaampSkillMetadata` with install/discovery metadata instead.
106
+ */
45
107
  export interface Skill {
46
108
  name: string;
47
109
  dirName: string;
@@ -51,7 +113,12 @@ export interface Skill {
51
113
  content?: string;
52
114
  }
53
115
 
54
- /** Lightweight skill summary for manifest/listing. */
116
+ /**
117
+ * Lightweight skill summary for manifest/listing.
118
+ *
119
+ * Projected from {@link Skill} for efficient caching in {@link SkillManifest}.
120
+ * Contains only the fields needed for CLI display and dispatch selection.
121
+ */
55
122
  export interface SkillSummary {
56
123
  name: string;
57
124
  dirName: string;
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @task T5244
9
9
  */
10
- import type { Task, TaskFile } from '@cleocode/contracts';
10
+ import type { Task } from '@cleocode/contracts';
11
11
  import type { DataAccessor } from '../data-accessor.js';
12
12
  /** Result of creating a test database environment. */
13
13
  export interface TestDbEnv {
@@ -32,16 +32,16 @@ export interface TestDbEnv {
32
32
  */
33
33
  export declare function createTestDb(): Promise<TestDbEnv>;
34
34
  /**
35
- * Build a TaskFile structure from a list of task partials.
35
+ * Build full Task objects from a list of task partials.
36
36
  * Useful for seeding test data via accessor.upsertSingleTask().
37
37
  */
38
- export declare function makeTaskFile(
38
+ export declare function makeTasks(
39
39
  tasks: Array<
40
40
  Partial<Task> & {
41
41
  id: string;
42
42
  }
43
43
  >,
44
- ): TaskFile;
44
+ ): Task[];
45
45
  /**
46
46
  * Seed tasks into the test database via the accessor.
47
47
  *
@@ -40,11 +40,11 @@ export async function createTestDb() {
40
40
  };
41
41
  }
42
42
  /**
43
- * Build a TaskFile structure from a list of task partials.
43
+ * Build full Task objects from a list of task partials.
44
44
  * Useful for seeding test data via accessor.upsertSingleTask().
45
45
  */
46
- export function makeTaskFile(tasks) {
47
- const fullTasks = tasks.map((t) => ({
46
+ export function makeTasks(tasks) {
47
+ return tasks.map((t) => ({
48
48
  title: t.title ?? `Task ${t.id}`,
49
49
  description: t.description ?? undefined,
50
50
  status: t.status ?? 'pending',
@@ -52,17 +52,6 @@ export function makeTaskFile(tasks) {
52
52
  createdAt: t.createdAt ?? new Date().toISOString(),
53
53
  ...t,
54
54
  }));
55
- return {
56
- version: '2.10.0',
57
- project: { name: 'test', phases: {} },
58
- lastUpdated: new Date().toISOString(),
59
- _meta: {
60
- schemaVersion: '2.10.0',
61
- checksum: '0000000000000000',
62
- configVersion: '1.0.0',
63
- },
64
- tasks: fullTasks,
65
- };
66
55
  }
67
56
  /**
68
57
  * Seed tasks into the test database via the accessor.
@@ -79,8 +68,8 @@ export async function seedTasks(accessor, tasks) {
79
68
  return;
80
69
  }
81
70
  // Build full Task objects from partials
82
- const tf = makeTaskFile(tasks);
83
- const fullTasks = tf.tasks;
71
+ const fullTasks = makeTasks(tasks);
72
+
84
73
  // Pass 1: Upsert all tasks without dependencies so FK targets exist
85
74
  for (const task of fullTasks) {
86
75
  await accessor.upsertSingleTask({ ...task, depends: undefined });
@@ -11,7 +11,7 @@
11
11
  import { mkdtempSync, rmSync } from 'node:fs';
12
12
  import { tmpdir } from 'node:os';
13
13
  import { join } from 'node:path';
14
- import type { Task, TaskFile } from '@cleocode/contracts';
14
+ import type { Task } from '@cleocode/contracts';
15
15
  import type { DataAccessor } from '../data-accessor.js';
16
16
  import { resetDbState } from '../sqlite.js';
17
17
  import { createSqliteDataAccessor } from '../sqlite-data-accessor.js';
@@ -59,11 +59,11 @@ export async function createTestDb(): Promise<TestDbEnv> {
59
59
  }
60
60
 
61
61
  /**
62
- * Build a TaskFile structure from a list of task partials.
62
+ * Build full Task objects from a list of task partials.
63
63
  * Useful for seeding test data via accessor.upsertSingleTask().
64
64
  */
65
- export function makeTaskFile(tasks: Array<Partial<Task> & { id: string }>): TaskFile {
66
- const fullTasks: Task[] = tasks.map(
65
+ export function makeTasks(tasks: Array<Partial<Task> & { id: string }>): Task[] {
66
+ return tasks.map(
67
67
  (t) =>
68
68
  ({
69
69
  title: t.title ?? `Task ${t.id}`,
@@ -74,18 +74,6 @@ export function makeTaskFile(tasks: Array<Partial<Task> & { id: string }>): Task
74
74
  ...t,
75
75
  }) as Task,
76
76
  );
77
-
78
- return {
79
- version: '2.10.0',
80
- project: { name: 'test', phases: {} },
81
- lastUpdated: new Date().toISOString(),
82
- _meta: {
83
- schemaVersion: '2.10.0',
84
- checksum: '0000000000000000',
85
- configVersion: '1.0.0',
86
- },
87
- tasks: fullTasks,
88
- };
89
77
  }
90
78
 
91
79
  /**
@@ -108,8 +96,7 @@ export async function seedTasks(
108
96
  }
109
97
 
110
98
  // Build full Task objects from partials
111
- const tf = makeTaskFile(tasks);
112
- const fullTasks = tf.tasks;
99
+ const fullTasks = makeTasks(tasks);
113
100
 
114
101
  // Pass 1: Upsert all tasks without dependencies so FK targets exist
115
102
  for (const task of fullTasks) {
@@ -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
- // Both src/store/ and dist/store/ are 2 levels deep from package root
59
- return join(__dirname, '..', '..', 'migrations', 'drizzle-brain');
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,7 +46,7 @@ export const warpChainInstances = sqliteTable(
46
46
  id: text('id').primaryKey(),
47
47
  chainId: text('chain_id')
48
48
  .notNull()
49
- .references(() => warpChains.id),
49
+ .references(() => warpChains.id, { onDelete: 'cascade' }),
50
50
  epicId: text('epic_id').notNull(),
51
51
  variables: text('variables'), // JSON
52
52
  stageToTask: text('stage_to_task'), // JSON
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { createHash } from 'node:crypto';
10
- import type { Task, TaskFile } from '@cleocode/contracts';
10
+ import type { Task } from '@cleocode/contracts';
11
11
 
12
12
  /** Export format version. */
13
13
  const EXPORT_FORMAT_VERSION = '1.0.0';
@@ -137,17 +137,17 @@ export function buildRelationshipGraph(tasks: Task[]): RelationshipGraph {
137
137
  */
138
138
  export function buildExportPackage(
139
139
  tasks: Task[],
140
- taskData: TaskFile,
141
140
  options: {
142
141
  mode: string;
143
142
  rootTaskIds: string[];
144
143
  includeChildren: boolean;
145
144
  cleoVersion?: string;
146
145
  filters?: unknown;
146
+ projectName?: string;
147
147
  },
148
148
  ): ExportPackage {
149
149
  const now = new Date().toISOString();
150
- const projectName = taskData.project?.name ?? 'unknown';
150
+ const projectName = options.projectName ?? 'unknown';
151
151
  const maxId = tasks.reduce((max, t) => {
152
152
  const num = parseInt(t.id.replace('T', ''), 10);
153
153
  return num > max ? num : max;
@@ -188,41 +188,51 @@ export function buildExportPackage(
188
188
  /**
189
189
  * Export a single task.
190
190
  */
191
- export function exportSingle(taskId: string, taskData: TaskFile): ExportPackage | null {
192
- const task = taskData.tasks.find((t) => t.id === taskId);
191
+ export function exportSingle(
192
+ taskId: string,
193
+ allTasks: Task[],
194
+ projectName?: string,
195
+ ): ExportPackage | null {
196
+ const task = allTasks.find((t) => t.id === taskId);
193
197
  if (!task) return null;
194
198
 
195
- return buildExportPackage([task], taskData, {
199
+ return buildExportPackage([task], {
196
200
  mode: 'single',
197
201
  rootTaskIds: [taskId],
198
202
  includeChildren: false,
203
+ projectName,
199
204
  });
200
205
  }
201
206
 
202
207
  /**
203
208
  * Export a subtree (task + all descendants).
204
209
  */
205
- export function exportSubtree(rootId: string, taskData: TaskFile): ExportPackage | null {
206
- const root = taskData.tasks.find((t) => t.id === rootId);
210
+ export function exportSubtree(
211
+ rootId: string,
212
+ allTasks: Task[],
213
+ projectName?: string,
214
+ ): ExportPackage | null {
215
+ const root = allTasks.find((t) => t.id === rootId);
207
216
  if (!root) return null;
208
217
 
209
- // Collect all descendants
218
+ // Collect all descendants via BFS
210
219
  const collected = new Map<string, Task>();
211
220
  const queue = [rootId];
212
221
 
213
222
  while (queue.length > 0) {
214
223
  const id = queue.shift()!;
215
- const task = taskData.tasks.find((t) => t.id === id);
224
+ const task = allTasks.find((t) => t.id === id);
216
225
  if (!task || collected.has(id)) continue;
217
226
  collected.set(id, task);
218
- const children = taskData.tasks.filter((t) => t.parentId === id);
227
+ const children = allTasks.filter((t) => t.parentId === id);
219
228
  queue.push(...children.map((c) => c.id));
220
229
  }
221
230
 
222
231
  const tasks = [...collected.values()];
223
- return buildExportPackage(tasks, taskData, {
232
+ return buildExportPackage(tasks, {
224
233
  mode: 'subtree',
225
234
  rootTaskIds: [rootId],
226
235
  includeChildren: true,
236
+ projectName,
227
237
  });
228
238
  }
@@ -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
- // Both src/store/ and dist/store/ are 2 levels deep from package root
55
- return join(__dirname, '..', '..', 'migrations', 'drizzle-nexus');
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
  /**
@@ -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
- // Both src/store/ and dist/store/ are 2 levels deep from package root
366
- return join(__dirname, '..', '..', 'migrations', 'drizzle-tasks');
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
  /**
@@ -16,6 +16,7 @@ import {
16
16
  primaryKey,
17
17
  sqliteTable,
18
18
  text,
19
+ unique,
19
20
  } from 'drizzle-orm/sqlite-core';
20
21
  import {
21
22
  ADR_STATUSES,
@@ -27,6 +28,22 @@ import {
27
28
  TASK_STATUSES,
28
29
  } from './status-registry.js';
29
30
 
31
+ export type {
32
+ AgentErrorLogRow,
33
+ AgentErrorType,
34
+ AgentInstanceRow,
35
+ AgentInstanceStatus,
36
+ AgentType,
37
+ NewAgentErrorLogRow,
38
+ NewAgentInstanceRow,
39
+ } from '../agents/agent-schema.js';
40
+ // Re-export agent schema tables so drizzle-kit picks them up for migrations.
41
+ export {
42
+ AGENT_INSTANCE_STATUSES,
43
+ AGENT_TYPES,
44
+ agentErrorLog,
45
+ agentInstances,
46
+ } from '../agents/agent-schema.js';
30
47
  export type {
31
48
  NewWarpChainInstanceRow,
32
49
  NewWarpChainRow,
@@ -95,6 +112,26 @@ export const TOKEN_USAGE_CONFIDENCE = ['real', 'high', 'estimated', 'coarse'] as
95
112
  /** Transport types for token telemetry. */
96
113
  export const TOKEN_USAGE_TRANSPORTS = ['cli', 'mcp', 'api', 'agent', 'unknown'] as const;
97
114
 
115
+ /** Task relation types matching DB CHECK constraint on task_relations.relation_type. */
116
+ export const TASK_RELATION_TYPES = [
117
+ 'related',
118
+ 'blocks',
119
+ 'duplicates',
120
+ 'absorbs',
121
+ 'fixes',
122
+ 'extends',
123
+ 'supersedes',
124
+ ] as const;
125
+
126
+ /** Lifecycle transition types matching DB CHECK constraint on lifecycle_transitions.transition_type. */
127
+ export const LIFECYCLE_TRANSITION_TYPES = ['automatic', 'manual', 'forced'] as const;
128
+
129
+ /** External task link types matching DB constraint on external_task_links.link_type. */
130
+ export const EXTERNAL_LINK_TYPES = ['created', 'matched', 'manual', 'transferred'] as const;
131
+
132
+ /** Sync direction types matching DB constraint on external_task_links.sync_direction. */
133
+ export const SYNC_DIRECTIONS = ['inbound', 'outbound', 'bidirectional'] as const;
134
+
98
135
  // === TASKS TABLE ===
99
136
 
100
137
  export const tasks = sqliteTable(
@@ -160,6 +197,7 @@ export const tasks = sqliteTable(
160
197
  index('idx_tasks_phase').on(table.phase),
161
198
  index('idx_tasks_type').on(table.type),
162
199
  index('idx_tasks_priority').on(table.priority),
200
+ index('idx_tasks_session_id').on(table.sessionId),
163
201
  ],
164
202
  );
165
203
 
@@ -193,13 +231,16 @@ export const taskRelations = sqliteTable(
193
231
  .notNull()
194
232
  .references(() => tasks.id, { onDelete: 'cascade' }),
195
233
  relationType: text('relation_type', {
196
- enum: ['related', 'blocks', 'duplicates', 'absorbs', 'fixes', 'extends', 'supersedes'],
234
+ enum: TASK_RELATION_TYPES,
197
235
  })
198
236
  .notNull()
199
237
  .default('related'),
200
238
  reason: text('reason'),
201
239
  },
202
- (table) => [primaryKey({ columns: [table.taskId, table.relatedTo] })],
240
+ (table) => [
241
+ primaryKey({ columns: [table.taskId, table.relatedTo] }),
242
+ index('idx_task_relations_related_to').on(table.relatedTo),
243
+ ],
203
244
  );
204
245
 
205
246
  // === SESSIONS ===
@@ -225,8 +266,12 @@ export const sessions = sqliteTable(
225
266
  startedAt: text('started_at').notNull().default(sql`(datetime('now'))`),
226
267
  endedAt: text('ended_at'),
227
268
  // Session chain columns (T4959)
228
- previousSessionId: text('previous_session_id'),
229
- nextSessionId: text('next_session_id'),
269
+ previousSessionId: text('previous_session_id').references((): AnySQLiteColumn => sessions.id, {
270
+ onDelete: 'set null',
271
+ }),
272
+ nextSessionId: text('next_session_id').references((): AnySQLiteColumn => sessions.id, {
273
+ onDelete: 'set null',
274
+ }),
230
275
  agentIdentifier: text('agent_identifier'),
231
276
  handoffConsumedAt: text('handoff_consumed_at'),
232
277
  handoffConsumedBy: text('handoff_consumed_by'),
@@ -242,6 +287,7 @@ export const sessions = sqliteTable(
242
287
  index('idx_sessions_status').on(table.status),
243
288
  index('idx_sessions_previous').on(table.previousSessionId),
244
289
  index('idx_sessions_agent_identifier').on(table.agentIdentifier),
290
+ index('idx_sessions_started_at').on(table.startedAt),
245
291
  ],
246
292
  );
247
293
 
@@ -325,6 +371,7 @@ export const lifecycleStages = sqliteTable(
325
371
  index('idx_lifecycle_stages_pipeline_id').on(table.pipelineId),
326
372
  index('idx_lifecycle_stages_stage_name').on(table.stageName),
327
373
  index('idx_lifecycle_stages_status').on(table.status),
374
+ index('idx_lifecycle_stages_validated_by').on(table.validatedBy),
328
375
  ],
329
376
  );
330
377
 
@@ -381,7 +428,7 @@ export const lifecycleTransitions = sqliteTable(
381
428
  fromStageId: text('from_stage_id').notNull(),
382
429
  toStageId: text('to_stage_id').notNull(),
383
430
  transitionType: text('transition_type', {
384
- enum: ['automatic', 'manual', 'forced'],
431
+ enum: LIFECYCLE_TRANSITION_TYPES,
385
432
  })
386
433
  .notNull()
387
434
  .default('automatic'),
@@ -524,6 +571,7 @@ export const auditLog = sqliteTable(
524
571
  index('idx_audit_log_domain').on(table.domain),
525
572
  index('idx_audit_log_request_id').on(table.requestId),
526
573
  index('idx_audit_log_project_hash').on(table.projectHash),
574
+ index('idx_audit_log_actor').on(table.actor),
527
575
  ],
528
576
  );
529
577
 
@@ -567,6 +615,7 @@ export const tokenUsage = sqliteTable(
567
615
  index('idx_token_usage_transport').on(table.transport),
568
616
  index('idx_token_usage_domain_operation').on(table.domain, table.operation),
569
617
  index('idx_token_usage_method').on(table.method),
618
+ index('idx_token_usage_gateway').on(table.gateway),
570
619
  ],
571
620
  );
572
621
 
@@ -603,7 +652,10 @@ export const architectureDecisions = sqliteTable(
603
652
  keywords: text('keywords'),
604
653
  topics: text('topics'),
605
654
  },
606
- (table) => [index('idx_arch_decisions_status').on(table.status)],
655
+ (table) => [
656
+ index('idx_arch_decisions_status').on(table.status),
657
+ index('idx_arch_decisions_amends_id').on(table.amendsId),
658
+ ],
607
659
  );
608
660
 
609
661
  // === ADR JUNCTION TABLES (ADR-017 §5.3) ===
@@ -673,11 +725,11 @@ export const externalTaskLinks = sqliteTable(
673
725
  externalTitle: text('external_title'),
674
726
  /** How this link was established. */
675
727
  linkType: text('link_type', {
676
- enum: ['created', 'matched', 'manual'],
728
+ enum: EXTERNAL_LINK_TYPES,
677
729
  }).notNull(),
678
730
  /** Direction of the sync that created this link. */
679
731
  syncDirection: text('sync_direction', {
680
- enum: ['inbound', 'outbound', 'bidirectional'],
732
+ enum: SYNC_DIRECTIONS,
681
733
  })
682
734
  .notNull()
683
735
  .default('inbound'),
@@ -692,6 +744,11 @@ export const externalTaskLinks = sqliteTable(
692
744
  index('idx_ext_links_task_id').on(table.taskId),
693
745
  index('idx_ext_links_provider_external').on(table.providerId, table.externalId),
694
746
  index('idx_ext_links_provider_id').on(table.providerId),
747
+ unique('uq_ext_links_task_provider_external').on(
748
+ table.taskId,
749
+ table.providerId,
750
+ table.externalId,
751
+ ),
695
752
  ],
696
753
  );
697
754
 
@@ -0,0 +1,17 @@
1
+ import type { SQLInputValue, StatementSync } from 'node:sqlite';
2
+
3
+ /**
4
+ * Type-safe wrapper for {@link StatementSync.all} — centralizes the
5
+ * `as unknown as` cast required by node:sqlite's untyped return type.
6
+ */
7
+ export function typedAll<T>(stmt: StatementSync, ...params: SQLInputValue[]): T[] {
8
+ return stmt.all(...params) as unknown as T[];
9
+ }
10
+
11
+ /**
12
+ * Type-safe wrapper for {@link StatementSync.get} — centralizes the
13
+ * `as unknown as` cast required by node:sqlite's untyped return type.
14
+ */
15
+ export function typedGet<T>(stmt: StatementSync, ...params: SQLInputValue[]): T | undefined {
16
+ return stmt.get(...params) as unknown as T | undefined;
17
+ }