@cleocode/contracts 2026.5.130 → 2026.5.131

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 (171) hide show
  1. package/dist/__tests__/atomic-tools-contract.test.d.ts +14 -0
  2. package/dist/__tests__/atomic-tools-contract.test.d.ts.map +1 -0
  3. package/dist/__tests__/atomic-tools-contract.test.js +55 -0
  4. package/dist/__tests__/atomic-tools-contract.test.js.map +1 -0
  5. package/dist/__tests__/gateway-contract.test.d.ts +18 -0
  6. package/dist/__tests__/gateway-contract.test.d.ts.map +1 -0
  7. package/dist/__tests__/gateway-contract.test.js +187 -0
  8. package/dist/__tests__/gateway-contract.test.js.map +1 -0
  9. package/dist/__tests__/mvi.test.d.ts +17 -0
  10. package/dist/__tests__/mvi.test.d.ts.map +1 -0
  11. package/dist/__tests__/mvi.test.js +147 -0
  12. package/dist/__tests__/mvi.test.js.map +1 -0
  13. package/dist/__tests__/operation-def.test.js +1 -1
  14. package/dist/__tests__/operation-def.test.js.map +1 -1
  15. package/dist/boundary.d.ts +26 -0
  16. package/dist/boundary.d.ts.map +1 -1
  17. package/dist/boundary.js +42 -4
  18. package/dist/boundary.js.map +1 -1
  19. package/dist/branch-lock.d.ts +35 -1
  20. package/dist/branch-lock.d.ts.map +1 -1
  21. package/dist/branch-lock.js +23 -2
  22. package/dist/branch-lock.js.map +1 -1
  23. package/dist/daemon/__tests__/health-roundtrip.test.d.ts +13 -0
  24. package/dist/daemon/__tests__/health-roundtrip.test.d.ts.map +1 -0
  25. package/dist/daemon/__tests__/health-roundtrip.test.js +69 -0
  26. package/dist/daemon/__tests__/health-roundtrip.test.js.map +1 -0
  27. package/dist/daemon/health.d.ts +108 -0
  28. package/dist/daemon/health.d.ts.map +1 -0
  29. package/dist/daemon/health.js +95 -0
  30. package/dist/daemon/health.js.map +1 -0
  31. package/dist/daemon/index.d.ts +24 -0
  32. package/dist/daemon/index.d.ts.map +1 -0
  33. package/dist/daemon/index.js +22 -0
  34. package/dist/daemon/index.js.map +1 -0
  35. package/dist/daemon/subsystem.d.ts +121 -0
  36. package/dist/daemon/subsystem.d.ts.map +1 -0
  37. package/dist/daemon/subsystem.js +28 -0
  38. package/dist/daemon/subsystem.js.map +1 -0
  39. package/dist/daemon-ipc/__tests__/daemon-ipc-freeze.test.d.ts +15 -0
  40. package/dist/daemon-ipc/__tests__/daemon-ipc-freeze.test.d.ts.map +1 -0
  41. package/dist/daemon-ipc/__tests__/daemon-ipc-freeze.test.js +40 -0
  42. package/dist/daemon-ipc/__tests__/daemon-ipc-freeze.test.js.map +1 -0
  43. package/dist/daemon-ipc/index.d.ts +44 -0
  44. package/dist/daemon-ipc/index.d.ts.map +1 -0
  45. package/dist/daemon-ipc/index.js +43 -0
  46. package/dist/daemon-ipc/index.js.map +1 -0
  47. package/dist/db-inventory.json +8 -8
  48. package/dist/dispatch/identity.d.ts +1 -1
  49. package/dist/dispatch/identity.d.ts.map +1 -1
  50. package/dist/dispatch/identity.js +3 -0
  51. package/dist/dispatch/identity.js.map +1 -1
  52. package/dist/dispatch/operation-def.d.ts +16 -0
  53. package/dist/dispatch/operation-def.d.ts.map +1 -1
  54. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  55. package/dist/dispatch/operations-registry.js +133 -0
  56. package/dist/dispatch/operations-registry.js.map +1 -1
  57. package/dist/exit-codes.d.ts +1 -1
  58. package/dist/exit-codes.js +1 -1
  59. package/dist/gateway/rpc/__tests__/freeze.test.d.ts +20 -0
  60. package/dist/gateway/rpc/__tests__/freeze.test.d.ts.map +1 -0
  61. package/dist/gateway/rpc/__tests__/freeze.test.js +122 -0
  62. package/dist/gateway/rpc/__tests__/freeze.test.js.map +1 -0
  63. package/dist/gateway/rpc/index.d.ts +23 -0
  64. package/dist/gateway/rpc/index.d.ts.map +1 -0
  65. package/dist/gateway/rpc/index.js +22 -0
  66. package/dist/gateway/rpc/index.js.map +1 -0
  67. package/dist/gateway/rpc/messages.d.ts +263 -0
  68. package/dist/gateway/rpc/messages.d.ts.map +1 -0
  69. package/dist/gateway/rpc/messages.js +152 -0
  70. package/dist/gateway/rpc/messages.js.map +1 -0
  71. package/dist/gateway/rpc/version.d.ts +39 -0
  72. package/dist/gateway/rpc/version.d.ts.map +1 -0
  73. package/dist/gateway/rpc/version.js +39 -0
  74. package/dist/gateway/rpc/version.js.map +1 -0
  75. package/dist/gateway.d.ts +239 -0
  76. package/dist/gateway.d.ts.map +1 -0
  77. package/dist/gateway.js +171 -0
  78. package/dist/gateway.js.map +1 -0
  79. package/dist/goal.d.ts +314 -0
  80. package/dist/goal.d.ts.map +1 -0
  81. package/dist/goal.js +118 -0
  82. package/dist/goal.js.map +1 -0
  83. package/dist/index.d.ts +9 -0
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +10 -0
  86. package/dist/index.js.map +1 -1
  87. package/dist/mvi.d.ts +209 -0
  88. package/dist/mvi.d.ts.map +1 -0
  89. package/dist/mvi.js +99 -0
  90. package/dist/mvi.js.map +1 -0
  91. package/dist/operations/attention.d.ts +114 -0
  92. package/dist/operations/attention.d.ts.map +1 -0
  93. package/dist/operations/attention.js +22 -0
  94. package/dist/operations/attention.js.map +1 -0
  95. package/dist/operations/focus.d.ts +25 -0
  96. package/dist/operations/focus.d.ts.map +1 -1
  97. package/dist/operations/index.d.ts +1 -0
  98. package/dist/operations/index.d.ts.map +1 -1
  99. package/dist/operations/index.js +1 -0
  100. package/dist/operations/index.js.map +1 -1
  101. package/dist/operations/session.d.ts +11 -0
  102. package/dist/operations/session.d.ts.map +1 -1
  103. package/dist/sdk-tool.d.ts +1 -1
  104. package/dist/sdk-tool.js +1 -1
  105. package/dist/supervisor-ipc/__tests__/freeze.test.d.ts +15 -0
  106. package/dist/supervisor-ipc/__tests__/freeze.test.d.ts.map +1 -0
  107. package/dist/supervisor-ipc/__tests__/freeze.test.js +159 -0
  108. package/dist/supervisor-ipc/__tests__/freeze.test.js.map +1 -0
  109. package/dist/supervisor-ipc/index.d.ts +15 -0
  110. package/dist/supervisor-ipc/index.d.ts.map +1 -0
  111. package/dist/supervisor-ipc/index.js +14 -0
  112. package/dist/supervisor-ipc/index.js.map +1 -0
  113. package/dist/supervisor-ipc/messages.d.ts +457 -0
  114. package/dist/supervisor-ipc/messages.d.ts.map +1 -0
  115. package/dist/supervisor-ipc/messages.js +312 -0
  116. package/dist/supervisor-ipc/messages.js.map +1 -0
  117. package/dist/supervisor-ipc/version.d.ts +29 -0
  118. package/dist/supervisor-ipc/version.d.ts.map +1 -0
  119. package/dist/supervisor-ipc/version.js +29 -0
  120. package/dist/supervisor-ipc/version.js.map +1 -0
  121. package/dist/tools/__tests__/composite.test.d.ts +14 -0
  122. package/dist/tools/__tests__/composite.test.d.ts.map +1 -0
  123. package/dist/tools/__tests__/composite.test.js +75 -0
  124. package/dist/tools/__tests__/composite.test.js.map +1 -0
  125. package/dist/tools/atomic.d.ts +195 -0
  126. package/dist/tools/atomic.d.ts.map +1 -0
  127. package/dist/tools/atomic.js +82 -0
  128. package/dist/tools/atomic.js.map +1 -0
  129. package/dist/tools/composite.d.ts +128 -0
  130. package/dist/tools/composite.d.ts.map +1 -0
  131. package/dist/tools/composite.js +88 -0
  132. package/dist/tools/composite.js.map +1 -0
  133. package/package.json +34 -2
  134. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  135. package/src/__tests__/atomic-tools-contract.test.ts +67 -0
  136. package/src/__tests__/gateway-contract.test.ts +219 -0
  137. package/src/__tests__/mvi.test.ts +175 -0
  138. package/src/__tests__/operation-def.test.ts +2 -1
  139. package/src/boundary.ts +74 -4
  140. package/src/branch-lock.ts +49 -2
  141. package/src/daemon/__tests__/health-roundtrip.test.ts +84 -0
  142. package/src/daemon/health.ts +121 -0
  143. package/src/daemon/index.ts +35 -0
  144. package/src/daemon/subsystem.ts +126 -0
  145. package/src/daemon-ipc/__tests__/daemon-ipc-freeze.test.ts +53 -0
  146. package/src/daemon-ipc/index.ts +90 -0
  147. package/src/db-inventory.json +8 -8
  148. package/src/dispatch/identity.ts +3 -0
  149. package/src/dispatch/operation-def.ts +16 -0
  150. package/src/dispatch/operations-registry.ts +140 -0
  151. package/src/exit-codes.ts +1 -1
  152. package/src/gateway/rpc/__tests__/freeze.test.ts +139 -0
  153. package/src/gateway/rpc/index.ts +43 -0
  154. package/src/gateway/rpc/messages.ts +187 -0
  155. package/src/gateway/rpc/version.ts +40 -0
  156. package/src/gateway.ts +252 -0
  157. package/src/goal.ts +348 -0
  158. package/src/index.ts +109 -0
  159. package/src/mvi.ts +236 -0
  160. package/src/operations/attention.ts +117 -0
  161. package/src/operations/focus.ts +26 -0
  162. package/src/operations/index.ts +1 -0
  163. package/src/operations/session.ts +11 -0
  164. package/src/sdk-tool.ts +1 -1
  165. package/src/supervisor-ipc/__tests__/freeze.test.ts +180 -0
  166. package/src/supervisor-ipc/index.ts +62 -0
  167. package/src/supervisor-ipc/messages.ts +396 -0
  168. package/src/supervisor-ipc/version.ts +30 -0
  169. package/src/tools/__tests__/composite.test.ts +88 -0
  170. package/src/tools/atomic.ts +274 -0
  171. package/src/tools/composite.ts +173 -0
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Contract-shape tests for the composite meta-tool contracts (T11484).
3
+ *
4
+ * The RUNTIME for `module-relocation` / `add-workspace-package` is deferred to
5
+ * epic T11456 (the TOOLS canonical structure that supplies their correct home,
6
+ * category enum, descriptor schema, and registry — see composite.ts header).
7
+ * These tests lock the CONTRACT surface that ships now: the breakage-class and
8
+ * wiring-point SoT arrays, and that the input/result shapes are constructible.
9
+ *
10
+ * @epic T11480
11
+ * @task T11484
12
+ */
13
+
14
+ import { describe, expect, expectTypeOf, it } from 'vitest';
15
+ import {
16
+ ADD_WORKSPACE_PACKAGE_WIRING_POINTS,
17
+ type AddWorkspacePackageInput,
18
+ type AddWorkspacePackageResult,
19
+ MODULE_RELOCATION_BREAKAGE_CLASSES,
20
+ type ModuleRelocationInput,
21
+ type ModuleRelocationResult,
22
+ } from '../composite.js';
23
+
24
+ describe('module-relocation contract (DHQ-032)', () => {
25
+ it('enumerates exactly the seven breakage classes', () => {
26
+ expect([...MODULE_RELOCATION_BREAKAGE_CLASSES]).toEqual([
27
+ 'relative-import-depth',
28
+ 'barrels',
29
+ 'js-form-and-dynamic-import',
30
+ 'mirror-test-dirs',
31
+ 'deprecations-yml',
32
+ 'doc-comments',
33
+ 'cross-package-importers',
34
+ ]);
35
+ expect(new Set(MODULE_RELOCATION_BREAKAGE_CLASSES).size).toBe(7);
36
+ });
37
+
38
+ it('has a constructible input + result shape', () => {
39
+ const input: ModuleRelocationInput = {
40
+ fromPath: 'packages/core/src/a.ts',
41
+ toPath: 'packages/core/src/sub/a.ts',
42
+ repoRoot: '/repo',
43
+ };
44
+ expect(input.fromPath).toBe('packages/core/src/a.ts');
45
+
46
+ const result: ModuleRelocationResult = {
47
+ toPath: input.toPath,
48
+ shimLeft: false,
49
+ dryRun: true,
50
+ classes: [{ class: 'barrels', files: ['packages/core/src/index.ts'], edits: 1 }],
51
+ filesTouched: ['packages/core/src/index.ts'],
52
+ };
53
+ expect(result.classes[0]?.class).toBe('barrels');
54
+ expectTypeOf(result.classes).items.toHaveProperty('class');
55
+ });
56
+ });
57
+
58
+ describe('add-workspace-package contract (DHQ-027)', () => {
59
+ it('enumerates exactly the six wiring points', () => {
60
+ expect([...ADD_WORKSPACE_PACKAGE_WIRING_POINTS]).toEqual([
61
+ 'deps',
62
+ 'tsconfig-refs',
63
+ 'build-mjs-buildpkg',
64
+ 'inline-maps',
65
+ 'boundary-entry',
66
+ 'private-no-readme',
67
+ ]);
68
+ expect(new Set(ADD_WORKSPACE_PACKAGE_WIRING_POINTS).size).toBe(6);
69
+ });
70
+
71
+ it('has a constructible input + result shape', () => {
72
+ const input: AddWorkspacePackageInput = {
73
+ packageName: '@cleocode/foo',
74
+ repoRoot: '/repo',
75
+ consumers: ['@cleocode/core'],
76
+ };
77
+ expect(input.packageName).toBe('@cleocode/foo');
78
+
79
+ const result: AddWorkspacePackageResult = {
80
+ packageName: input.packageName,
81
+ packageDir: 'packages/foo',
82
+ dryRun: true,
83
+ points: [{ point: 'deps', files: ['packages/core/package.json'] }],
84
+ };
85
+ expect(result.points[0]?.point).toBe('deps');
86
+ expectTypeOf(result.points).items.toHaveProperty('point');
87
+ });
88
+ });
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Atomic-tool primitive contracts — the canonical I/O shapes for the CORE SDK
3
+ * tool layer (E3 · T11403 · SG-PACKAGE-ARCH).
4
+ *
5
+ * A **tool primitive** is the smallest stateless unit of side-effecting work an
6
+ * agent can perform — read a file, write a file, run a shell command, search,
7
+ * fetch a URL, edit a notebook cell. Each primitive is a PURE function of its
8
+ * typed input (no session/loop/global coupling); composing primitives into
9
+ * multi-step capabilities is the job of the SKILLS layer (`packages/skills`),
10
+ * not the tool layer. The implementations live in `packages/core/src/tools/*`
11
+ * (T11405-T11407) behind a single deny-first guardrail chokepoint; this module
12
+ * is their shared contract so `core`, `mcp-adapter`, `caamp`, and `cleo-os`
13
+ * agree on one shape and never redefine it.
14
+ *
15
+ * This is a NEW submodule (`@cleocode/contracts/tools/atomic`) kept separate
16
+ * from the squatted `contracts/src/tools/index.ts` (query/derivation helpers —
17
+ * see the E3 inventory note `e3-tools-inventory`). Types-only — no runtime
18
+ * logic (Gate 10 `contracts-purity`).
19
+ *
20
+ * @epic T11390
21
+ * @task T11403
22
+ * @saga T11387
23
+ */
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Taxonomy
27
+ // ---------------------------------------------------------------------------
28
+
29
+ /**
30
+ * The atomic tool classes. Each class groups primitives that share a side-effect
31
+ * surface and therefore a guardrail policy (fs → path allowlist, shell → command
32
+ * denylist, net → egress policy, …).
33
+ */
34
+ export const TOOL_CLASSES = ['fs', 'shell', 'search', 'net', 'notebook'] as const;
35
+
36
+ /** One of the canonical {@link TOOL_CLASSES}. */
37
+ export type ToolClass = (typeof TOOL_CLASSES)[number];
38
+
39
+ /**
40
+ * Descriptor for a single registered tool primitive. The registry of these is
41
+ * the SoT for "what atomic tools exist", consumed by the guardrail chokepoint
42
+ * and (optionally) by the MCP adapter's tool catalog (T11411).
43
+ */
44
+ export interface ToolPrimitiveDescriptor {
45
+ /** Stable primitive name (e.g. `readFileText`, `executeShell`). */
46
+ readonly name: string;
47
+ /** The side-effect class this primitive belongs to. */
48
+ readonly class: ToolClass;
49
+ /** One-line statement of the single responsibility this primitive owns. */
50
+ readonly responsibility: string;
51
+ /**
52
+ * Whether the primitive is a pure function of its input with no
53
+ * session/loop/global state coupling. Atomic primitives MUST be `true`;
54
+ * the flag exists so the guardrail + lints can reject stateful drift.
55
+ */
56
+ readonly stateless: boolean;
57
+ }
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // fs primitives
61
+ // ---------------------------------------------------------------------------
62
+
63
+ /** Input for reading a file's text content. */
64
+ export interface ReadFileInput {
65
+ /** Absolute path to read. */
66
+ readonly path: string;
67
+ /** Text encoding. Defaults to `utf8` in the implementation. */
68
+ readonly encoding?: 'utf8' | 'utf-8' | 'ascii' | 'latin1';
69
+ }
70
+
71
+ /** Result of a text read. */
72
+ export interface ReadFileResult {
73
+ /** Absolute path that was read. */
74
+ readonly path: string;
75
+ /** File contents as text. */
76
+ readonly content: string;
77
+ }
78
+
79
+ /** Input for an atomic (tmp-then-rename) file write. */
80
+ export interface WriteFileInput {
81
+ /** Absolute path to write. */
82
+ readonly path: string;
83
+ /** Content to write. */
84
+ readonly content: string;
85
+ /** Create parent directories if missing. Defaults to `true`. */
86
+ readonly createDirs?: boolean;
87
+ }
88
+
89
+ /** Result of a file write. */
90
+ export interface WriteFileResult {
91
+ /** Absolute path that was written. */
92
+ readonly path: string;
93
+ /** Number of bytes written. */
94
+ readonly bytesWritten: number;
95
+ }
96
+
97
+ /** Input for an existence check. */
98
+ export interface PathExistsInput {
99
+ /** Absolute path to test. */
100
+ readonly path: string;
101
+ }
102
+
103
+ /** Result of an existence check. */
104
+ export interface PathExistsResult {
105
+ /** Whether the path exists. */
106
+ readonly exists: boolean;
107
+ /** What kind of entry it is, when it exists. */
108
+ readonly kind?: 'file' | 'directory' | 'other';
109
+ }
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // shell primitives
113
+ // ---------------------------------------------------------------------------
114
+
115
+ /** Input for a shell-command execution. */
116
+ export interface ExecuteShellInput {
117
+ /** Executable to run (NOT a shell string — args are passed separately). */
118
+ readonly command: string;
119
+ /** Arguments passed to {@link ExecuteShellInput.command}. */
120
+ readonly args?: readonly string[];
121
+ /** Working directory. Defaults to `process.cwd()` in the implementation. */
122
+ readonly cwd?: string;
123
+ /** Extra environment variables merged over the inherited environment. */
124
+ readonly env?: Readonly<Record<string, string>>;
125
+ /** Hard timeout in milliseconds; the process is killed when exceeded. */
126
+ readonly timeoutMs?: number;
127
+ }
128
+
129
+ /** Result of a shell-command execution. */
130
+ export interface ExecuteShellResult {
131
+ /** Captured standard output. */
132
+ readonly stdout: string;
133
+ /** Captured standard error. */
134
+ readonly stderr: string;
135
+ /** Process exit code (0 = success). `null` when killed by signal/timeout. */
136
+ readonly code: number | null;
137
+ }
138
+
139
+ /** Input for a `git` invocation (a constrained {@link ExecuteShellInput}). */
140
+ export interface RunGitInput {
141
+ /** Git arguments (e.g. `['status', '--porcelain']`). */
142
+ readonly args: readonly string[];
143
+ /** Repository working directory. */
144
+ readonly cwd?: string;
145
+ /** Hard timeout in milliseconds. */
146
+ readonly timeoutMs?: number;
147
+ }
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // search / net / notebook primitives
151
+ // ---------------------------------------------------------------------------
152
+
153
+ /** Input for a content/path search primitive. */
154
+ export interface SearchInput {
155
+ /** Pattern to search for (literal or regex per {@link SearchInput.isRegex}). */
156
+ readonly pattern: string;
157
+ /** Root directory to search under. */
158
+ readonly root: string;
159
+ /** Treat {@link SearchInput.pattern} as a regular expression. */
160
+ readonly isRegex?: boolean;
161
+ /** Cap on returned matches. */
162
+ readonly maxResults?: number;
163
+ }
164
+
165
+ /** A single search match. */
166
+ export interface SearchMatch {
167
+ /** Absolute path of the matching file. */
168
+ readonly path: string;
169
+ /** 1-based line number of the match. */
170
+ readonly line: number;
171
+ /** The matching line's text. */
172
+ readonly text: string;
173
+ }
174
+
175
+ /** Result of a search. */
176
+ export interface SearchResult {
177
+ /** Matches in deterministic (path, line) order. */
178
+ readonly matches: readonly SearchMatch[];
179
+ /** Whether {@link SearchInput.maxResults} truncated the results. */
180
+ readonly truncated: boolean;
181
+ }
182
+
183
+ /** Input for an outbound HTTP fetch primitive. */
184
+ export interface FetchInput {
185
+ /** Absolute URL to fetch. */
186
+ readonly url: string;
187
+ /** HTTP method. Defaults to `GET`. */
188
+ readonly method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
189
+ /** Request headers. */
190
+ readonly headers?: Readonly<Record<string, string>>;
191
+ /** Request body (already serialized). */
192
+ readonly body?: string;
193
+ /** Hard timeout in milliseconds. */
194
+ readonly timeoutMs?: number;
195
+ }
196
+
197
+ /** Result of an HTTP fetch. */
198
+ export interface FetchResult {
199
+ /** HTTP status code. */
200
+ readonly status: number;
201
+ /** Response headers. */
202
+ readonly headers: Readonly<Record<string, string>>;
203
+ /** Response body as text. */
204
+ readonly body: string;
205
+ }
206
+
207
+ /** Input for a notebook-cell edit primitive. */
208
+ export interface NotebookEditInput {
209
+ /** Absolute path to the `.ipynb` file. */
210
+ readonly path: string;
211
+ /** 0-based index of the cell to edit. */
212
+ readonly cellIndex: number;
213
+ /** New source for the cell. */
214
+ readonly source: string;
215
+ }
216
+
217
+ /** Result of a notebook-cell edit. */
218
+ export interface NotebookEditResult {
219
+ /** Absolute path that was edited. */
220
+ readonly path: string;
221
+ /** The cell index that changed. */
222
+ readonly cellIndex: number;
223
+ }
224
+
225
+ // ---------------------------------------------------------------------------
226
+ // Canonical primitive registry (the SoT for "what atomic tools exist")
227
+ // ---------------------------------------------------------------------------
228
+
229
+ /**
230
+ * The canonical atomic-tool primitives. Implementations in
231
+ * `packages/core/src/tools/*` register against these names; the MCP adapter
232
+ * (T11411) may derive its catalog from this list rather than hand-listing.
233
+ */
234
+ export const ATOMIC_TOOL_PRIMITIVES: readonly ToolPrimitiveDescriptor[] = [
235
+ { name: 'readFileText', class: 'fs', responsibility: 'Read a file as text', stateless: true },
236
+ { name: 'readJson', class: 'fs', responsibility: 'Read + parse a JSON file', stateless: true },
237
+ {
238
+ name: 'writeFileAtomic',
239
+ class: 'fs',
240
+ responsibility: 'Atomically write a file (tmp-then-rename)',
241
+ stateless: true,
242
+ },
243
+ {
244
+ name: 'pathExists',
245
+ class: 'fs',
246
+ responsibility: 'Test path existence + kind',
247
+ stateless: true,
248
+ },
249
+ {
250
+ name: 'executeShell',
251
+ class: 'shell',
252
+ responsibility: 'Run a command with explicit cwd/env/timeout',
253
+ stateless: true,
254
+ },
255
+ { name: 'runGit', class: 'shell', responsibility: 'Run a git subcommand', stateless: true },
256
+ {
257
+ name: 'search',
258
+ class: 'search',
259
+ responsibility: 'Search content/paths under a root',
260
+ stateless: true,
261
+ },
262
+ {
263
+ name: 'fetch',
264
+ class: 'net',
265
+ responsibility: 'Perform an outbound HTTP request',
266
+ stateless: true,
267
+ },
268
+ {
269
+ name: 'notebookEdit',
270
+ class: 'notebook',
271
+ responsibility: 'Replace a notebook cell source',
272
+ stateless: true,
273
+ },
274
+ ] as const;
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Composite meta-tool contracts — typed I/O shapes for repo-structure
3
+ * velocity-multiplier tools (T11484 · DHQ-027/032 · E-CORE-SELF-TOOLING).
4
+ *
5
+ * A **composite tool** COMPOSES atomic primitives (`@cleocode/contracts/tools/
6
+ * atomic` — fs/shell/search) plus repo-structure knowledge into one guarded,
7
+ * deterministic, multi-step operation that runs WITHOUT an LLM and returns a
8
+ * typed result. Per the TOOL-vs-SKILL discriminator (T11456 epic), that makes
9
+ * these TOOLs (typed return, no agent reasoning), NOT skills (`packages/skills`
10
+ * markdown capabilities).
11
+ *
12
+ * ─────────────────────────────────────────────────────────────────────────────
13
+ * PLACEMENT STATUS (read before implementing the runtime)
14
+ * ─────────────────────────────────────────────────────────────────────────────
15
+ * These are the CONTRACTS ONLY. The runtime implementations are intentionally
16
+ * DEFERRED to epic **T11456** (TOOLS canonical structure), because a composite
17
+ * tool cannot be CORRECTLY placed/named/registered until T11456 lands:
18
+ *
19
+ * - The atomic-tool boundary (`ATOMIC_TOOL_BOUNDARY` in `boundary.ts`, CI
20
+ * Gate 11) restricts `packages/core/src/tools/` to ATOMIC, STATELESS
21
+ * primitives (the 5 `TOOL_CLASSES`: fs/shell/search/net/notebook). These
22
+ * composites are multi-step and repo-structure-aware — NOT atomic primitives
23
+ * — so that home is wrong for them.
24
+ * - `packages/skills/` (the boundary's `skillsHome`) holds agent-facing
25
+ * SKILL.md markdown, not executable TS — also wrong.
26
+ * - T11456 defines the missing structure these tools NEED: a closed
27
+ * `ToolCategory` enum (it reserves `vcs`/`workflow`/`agent` — the categories
28
+ * these compose), strict `<scope>_<category>_<operation>` naming, a
29
+ * `ToolDescriptor` zod schema, and a `ToolRegistry` with project>global>core
30
+ * precedence. Authoring the runtime + a category BEFORE that epic would
31
+ * force a placement T11456 must then rework.
32
+ *
33
+ * Shipping the contracts now is the sound, additive, non-blocked unit: it pins
34
+ * the I/O shape (and enumerates every breakage/wiring class each tool owns) so
35
+ * the T11456 registry has a stable surface to register against. Types-only — no
36
+ * runtime logic (CI Gate 10 `contracts-purity`).
37
+ *
38
+ * @epic T11480
39
+ * @task T11484
40
+ * @see T11456 — TOOLS canonical structure (runtime home + registry + naming)
41
+ */
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // module-relocation (DHQ-032)
45
+ // ---------------------------------------------------------------------------
46
+
47
+ /**
48
+ * The seven breakage classes a module relocation MUST repair atomically. A
49
+ * naive `git mv` fixes none of these; leaving any one unrepaired is a red build.
50
+ * The runtime asserts each class is handled and reports per-class edit counts.
51
+ */
52
+ export const MODULE_RELOCATION_BREAKAGE_CLASSES = [
53
+ /** Relative-import depth (`../` vs `../../`) of the moved file's own imports. */
54
+ 'relative-import-depth',
55
+ /** Barrel (`index.ts`) re-exports that referenced the old path. */
56
+ 'barrels',
57
+ /** `.js`-suffixed specifiers AND dynamic `import()` specifiers. */
58
+ 'js-form-and-dynamic-import',
59
+ /** The mirrored `__tests__/` directory for the moved module. */
60
+ 'mirror-test-dirs',
61
+ /** `deprecations.yml` entries that pointed at the old path. */
62
+ 'deprecations-yml',
63
+ /** Doc comments / `@see` / `@module` references to the old path. */
64
+ 'doc-comments',
65
+ /** Cross-package importers that consumed the module via its package path. */
66
+ 'cross-package-importers',
67
+ ] as const;
68
+
69
+ /** One of the {@link MODULE_RELOCATION_BREAKAGE_CLASSES}. */
70
+ export type ModuleRelocationBreakageClass = (typeof MODULE_RELOCATION_BREAKAGE_CLASSES)[number];
71
+
72
+ /** Input for the `module-relocation` composite tool. */
73
+ export interface ModuleRelocationInput {
74
+ /** Absolute (or repo-root-relative) path of the module to move. */
75
+ readonly fromPath: string;
76
+ /** Destination path for the module. */
77
+ readonly toPath: string;
78
+ /** Repository root the operation is scoped to. */
79
+ readonly repoRoot: string;
80
+ /**
81
+ * Leave a re-export shim at {@link ModuleRelocationInput.fromPath} that
82
+ * re-exports from the new location (for a deprecation cycle) instead of
83
+ * rewriting every importer. Defaults to `false` (rewrite importers).
84
+ */
85
+ readonly leaveShim?: boolean;
86
+ /** Preview the edit plan without writing. Defaults to `false`. */
87
+ readonly dryRun?: boolean;
88
+ }
89
+
90
+ /** Per-class summary of the edits a relocation made (or would make). */
91
+ export interface ModuleRelocationClassReport {
92
+ /** Which breakage class this entry summarises. */
93
+ readonly class: ModuleRelocationBreakageClass;
94
+ /** Repo-relative files edited for this class. */
95
+ readonly files: readonly string[];
96
+ /** Total number of edits applied across {@link ModuleRelocationClassReport.files}. */
97
+ readonly edits: number;
98
+ }
99
+
100
+ /** Result of a `module-relocation` run. */
101
+ export interface ModuleRelocationResult {
102
+ /** The module's new path. */
103
+ readonly toPath: string;
104
+ /** Whether a re-export shim was left at the old path. */
105
+ readonly shimLeft: boolean;
106
+ /** Whether this was a dry-run (no writes performed). */
107
+ readonly dryRun: boolean;
108
+ /** Per-breakage-class edit report (one entry per class, even when empty). */
109
+ readonly classes: readonly ModuleRelocationClassReport[];
110
+ /** Total repo-relative files touched across every class. */
111
+ readonly filesTouched: readonly string[];
112
+ }
113
+
114
+ // ---------------------------------------------------------------------------
115
+ // add-workspace-package (DHQ-027)
116
+ // ---------------------------------------------------------------------------
117
+
118
+ /**
119
+ * The six wiring points a new workspace leaf MUST be threaded through. Missing
120
+ * any one yields a package that does not build, is not type-resolved, or is
121
+ * accidentally published. The runtime wires all six and reports each.
122
+ */
123
+ export const ADD_WORKSPACE_PACKAGE_WIRING_POINTS = [
124
+ /** `dependencies` / `devDependencies` of consuming packages. */
125
+ 'deps',
126
+ /** The three `tsconfig` references: root refs, build tsconfig, path mapping. */
127
+ 'tsconfig-refs',
128
+ /** The `buildPkg(...)` entry in the root `build.mjs` topological wave plan. */
129
+ 'build-mjs-buildpkg',
130
+ /** esbuild/tsc inline source-map wiring for the new leaf. */
131
+ 'inline-maps',
132
+ /** The `BOUNDARY_REGISTRY` entry in `packages/contracts/src/boundary.ts`. */
133
+ 'boundary-entry',
134
+ /** `private: true` + no published README (internal-leaf publish hygiene). */
135
+ 'private-no-readme',
136
+ ] as const;
137
+
138
+ /** One of the {@link ADD_WORKSPACE_PACKAGE_WIRING_POINTS}. */
139
+ export type AddWorkspacePackageWiringPoint = (typeof ADD_WORKSPACE_PACKAGE_WIRING_POINTS)[number];
140
+
141
+ /** Input for the `add-workspace-package` composite tool. */
142
+ export interface AddWorkspacePackageInput {
143
+ /** Scoped package name, e.g. `@cleocode/foo`. */
144
+ readonly packageName: string;
145
+ /** Directory under `packages/` to create (defaults to the unscoped name). */
146
+ readonly dirName?: string;
147
+ /** Repository root the operation is scoped to. */
148
+ readonly repoRoot: string;
149
+ /** Packages that should depend on the new leaf (wires the `deps` point). */
150
+ readonly consumers?: readonly string[];
151
+ /** Preview the wiring plan without writing. Defaults to `false`. */
152
+ readonly dryRun?: boolean;
153
+ }
154
+
155
+ /** Per-point summary of how a wiring point was satisfied. */
156
+ export interface AddWorkspacePackageWiringReport {
157
+ /** Which wiring point this entry summarises. */
158
+ readonly point: AddWorkspacePackageWiringPoint;
159
+ /** Repo-relative files created or edited for this point. */
160
+ readonly files: readonly string[];
161
+ }
162
+
163
+ /** Result of an `add-workspace-package` run. */
164
+ export interface AddWorkspacePackageResult {
165
+ /** The created package's name. */
166
+ readonly packageName: string;
167
+ /** Repo-relative directory of the new leaf. */
168
+ readonly packageDir: string;
169
+ /** Whether this was a dry-run (no writes performed). */
170
+ readonly dryRun: boolean;
171
+ /** Per-wiring-point report (one entry per point, even when empty). */
172
+ readonly points: readonly AddWorkspacePackageWiringReport[];
173
+ }