@impetik/xeer-mcp 0.2.5 → 0.2.6

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 (54) hide show
  1. package/README.md +4 -1
  2. package/dist/dev-session.d.ts +1 -1
  3. package/dist/network-policy.js +1 -1
  4. package/dist/server.d.ts +1 -1
  5. package/dist/server.js +4 -4
  6. package/dist/test-run.d.ts +1 -1
  7. package/dist/xeer-cli.d.ts +1 -1
  8. package/package.json +8 -5
  9. package/vendor/spec/actions.d.ts +1250 -0
  10. package/vendor/spec/actions.js +805 -0
  11. package/vendor/spec/admin-sql.d.ts +59 -0
  12. package/vendor/spec/admin-sql.js +147 -0
  13. package/vendor/spec/admin.d.ts +110 -0
  14. package/vendor/spec/admin.js +58 -0
  15. package/vendor/spec/canonical.d.ts +3 -0
  16. package/vendor/spec/canonical.js +36 -0
  17. package/vendor/spec/diagnostics.d.ts +49 -0
  18. package/vendor/spec/diagnostics.js +500 -0
  19. package/vendor/spec/docs.d.ts +21 -0
  20. package/vendor/spec/docs.js +57 -0
  21. package/vendor/spec/events.d.ts +8 -0
  22. package/vendor/spec/events.js +21 -0
  23. package/vendor/spec/identity-keys.d.ts +36 -0
  24. package/vendor/spec/identity-keys.js +72 -0
  25. package/vendor/spec/index.d.ts +20 -0
  26. package/vendor/spec/index.js +20 -0
  27. package/vendor/spec/local-identity.d.ts +69 -0
  28. package/vendor/spec/local-identity.js +132 -0
  29. package/vendor/spec/network-policy.d.ts +16 -0
  30. package/vendor/spec/network-policy.js +50 -0
  31. package/vendor/spec/public-assets.d.ts +153 -0
  32. package/vendor/spec/public-assets.js +166 -0
  33. package/vendor/spec/review.d.ts +82 -0
  34. package/vendor/spec/review.js +175 -0
  35. package/vendor/spec/route.d.ts +43 -0
  36. package/vendor/spec/route.js +87 -0
  37. package/vendor/spec/schema-lifecycle.d.ts +6 -0
  38. package/vendor/spec/schema-lifecycle.js +59 -0
  39. package/vendor/spec/schema-plan.d.ts +98 -0
  40. package/vendor/spec/schema-plan.js +194 -0
  41. package/vendor/spec/schema.d.ts +166 -0
  42. package/vendor/spec/schema.js +409 -0
  43. package/vendor/spec/sql-expression.d.ts +91 -0
  44. package/vendor/spec/sql-expression.js +650 -0
  45. package/vendor/spec/state-export.d.ts +143 -0
  46. package/vendor/spec/state-export.js +341 -0
  47. package/vendor/spec/storage.d.ts +61 -0
  48. package/vendor/spec/storage.js +120 -0
  49. package/vendor/spec/table-ddl.d.ts +162 -0
  50. package/vendor/spec/table-ddl.js +508 -0
  51. package/vendor/spec/types.d.ts +275 -0
  52. package/vendor/spec/types.js +11 -0
  53. package/vendor/spec/value.d.ts +22 -0
  54. package/vendor/spec/value.js +72 -0
@@ -0,0 +1,805 @@
1
+ /** Stable protocol for the action catalogue consumed by CLI, MCP, and agent context surfaces. */
2
+ import { REVIEW_RECEIPT_ID_SOURCE } from './review.js';
3
+ export const ACTION_REGISTRY_PROTOCOL = 'xeer.actions.v0';
4
+ export const XEER_MCP_PROFILES = ['author', 'operator'];
5
+ export const XEER_ARTIFACT_ID_PATTERN = '^sha256:[a-f0-9]{64}$';
6
+ export const MCP_INPUT_SCHEMA_DRAFT = 'http://json-schema.org/draft-07/schema#';
7
+ const NOT_EXPOSED_V0 = 'Not exposed through MCP v0; use the CLI deliberately.';
8
+ const HUMAN_IDENTITY = 'Human identity and project ownership decisions are not delegated through MCP.';
9
+ const SECRET_VALUES = 'Secret values are intentionally unreachable through MCP; env pull returns plaintext.';
10
+ const SERVICE_CREDENTIALS = 'Service credential issuance and revocation are intentionally CLI-only.';
11
+ const DESTRUCTIVE_STATE = 'Destructive state replacement requires an explicit CLI invocation.';
12
+ const PREVIEW_ONLY = 'MCP exposes a separate preview-only deploy action; direct production deploy stays CLI-only.';
13
+ const OPERATOR_ONLY = 'MCP promotion is a separate action available only when the server starts in operator profile.';
14
+ const PROJECT_DIRECTORY_INPUT = {
15
+ description: 'Project directory. Relative paths resolve against the server root; defaults to it.',
16
+ type: 'string',
17
+ };
18
+ const CONTROL_URL_INPUT = { description: 'Allowed control-plane origin override.', type: 'string' };
19
+ const SESSION_ID_INPUT = { description: 'Optional when only one session exists.', type: 'string' };
20
+ const ARTIFACT_ID_INPUT = {
21
+ description: 'Exact artifact id to promote: sha256: followed by 64 lowercase hexadecimal characters.',
22
+ type: 'string',
23
+ minLength: 71,
24
+ maxLength: 71,
25
+ pattern: XEER_ARTIFACT_ID_PATTERN,
26
+ };
27
+ const REVIEW_RECEIPT_ID_INPUT = {
28
+ description: 'Versioned receipt returned by the exact reviewed preview deployment.',
29
+ type: 'string',
30
+ minLength: 19,
31
+ maxLength: 103,
32
+ pattern: REVIEW_RECEIPT_ID_SOURCE,
33
+ };
34
+ const MCP_PRESENTATION = {
35
+ check: {
36
+ mcpTitle: 'Check a Xeer project',
37
+ mcpDescription: [
38
+ 'Validate a Xeer project and return its diagnostics as JSON. This is the repair loop:',
39
+ 'read diagnostics, apply the smallest edit each one names, run it again, converge on ok: true.',
40
+ 'Runs the manifest stage and, if that passes, the module-graph/zone/operations/type analysis.',
41
+ 'Envelope: { protocol: "xeer.command.v0", command: "check", ok, diagnostics: Diagnostic[],',
42
+ 'result?: { manifest } }. Diagnostic codes are stable — look one up with xeer_diagnostics.',
43
+ ].join(' '),
44
+ },
45
+ build: {
46
+ mcpTitle: 'Build a Xeer project',
47
+ mcpDescription: [
48
+ 'Build the content-addressed artifact. Runs check first, so build diagnostics are a superset',
49
+ 'of check diagnostics plus bundling, budget, and asset codes (XE14xx, XE15xx).',
50
+ 'On success result is { artifactId, outputDirectory, modules, assets, operations }.',
51
+ 'Run xeer_test before this to prove the application behaves, not just that it compiles.',
52
+ ].join(' '),
53
+ },
54
+ test: {
55
+ mcpTitle: 'Run a Xeer project\'s tests',
56
+ mcpDescription: [
57
+ 'Run the application\'s own tests: build, type-check tests/**/*.test.ts against the generated',
58
+ 'contract, boot the verified artifact in workerd with fresh isolated state, run every test, tear',
59
+ 'down. This is the convergence check — a green check proves the project compiles, a green test',
60
+ 'proves it behaves. Returns { ok, total, passed, failed, cases, diagnostics, events }.',
61
+ 'Each failed case carries a Diagnostic-shaped failure with matcher, expected, actual, and the',
62
+ 'project-relative test location: XE1904 assertion, XE1905 threw or unexpectedly refused call,',
63
+ 'XE1906 timeout. Compiler diagnostics abort the run before any test boots.',
64
+ 'Tests call as(\'alice\'|\'bob\'|\'guest\'), so ownership and authorization are directly testable.',
65
+ ].join(' '),
66
+ },
67
+ new: {
68
+ mcpTitle: 'Scaffold a Xeer project',
69
+ mcpDescription: 'Create a new Xeer project in an empty or non-existent directory. '
70
+ + 'Every template checks, tests, and builds clean, so use one as the starting point rather '
71
+ + 'than writing a manifest by hand. "notes" (the default) and "todo" are per-user apps, "blog" '
72
+ + 'is public to read and private to write, and "personal-site" has no database at all. None of '
73
+ + 'them scaffolds sign-in UI. result is { directory, name, template, files }; an unknown '
74
+ + 'template is XE3002 and writes nothing.',
75
+ },
76
+ doctor: {
77
+ mcpTitle: 'Diagnose the Xeer toolchain',
78
+ mcpDescription: 'Check the local toolchain and generated-file state. Use it when a command fails for '
79
+ + 'a reason no project edit explains. result is { checks, summary }; failures also appear as XE4001 '
80
+ + 'diagnostics.',
81
+ },
82
+ deploy: {
83
+ mcpTitle: 'Deploy a Xeer preview',
84
+ mcpDescription: 'Build, verify, and upload the artifact to the preview environment. This tool never '
85
+ + 'changes production. Requires a builder '
86
+ + 'credential: XE5002 means a human must run `xeer auth login` first. result is '
87
+ + '{ application, url, ... } for the app\'s side-by-side preview URL; production keeps serving, '
88
+ + 'and preview has its own disposable state.',
89
+ },
90
+ promote: {
91
+ mcpTitle: 'Promote an exact preview artifact (operator)',
92
+ mcpDescription: 'Make the version currently on the app\'s preview URL live on production — the same '
93
+ + 'bundle, not a rebuild, with production\'s own environment values. The second half of the '
94
+ + '"deploy to preview, look at it, then ship it" loop. XE5175 means nothing has been deployed to '
95
+ + 'preview yet. The exact artifact id is required. This tool exists only when the server starts '
96
+ + 'with XEER_MCP_PROFILE=operator. result is { artifactId, replaced, url, ... }.',
97
+ },
98
+ 'auth.status': {
99
+ mcpTitle: 'Report builder sign-in status',
100
+ mcpDescription: 'Report whether a builder credential is available for deployment. Sign-in itself is '
101
+ + 'interactive and cannot be completed by an agent; XE5002 means ask the human to run '
102
+ + '`xeer auth login`.',
103
+ },
104
+ inspect: {
105
+ mcpTitle: 'Inspect a running preview',
106
+ mcpDescription: 'Read the inspector API of a running dev or preview server: normalized manifest '
107
+ + '(manifest), per-table record counts (state), the bounded structured log ring (logs), or '
108
+ + 'every stored record with the schema that describes it (export). Use the URLs from '
109
+ + 'xeer_dev_status.preview.',
110
+ },
111
+ 'dev.start': {
112
+ mcpTitle: 'Start a dev session',
113
+ mcpDescription: [
114
+ 'Start `xeer dev --json` and return the xeer.dev.v0 events it emitted up to the point it settled.',
115
+ 'Returns when preview.ready arrives (status "ready"), or when the first compile fails',
116
+ '(status "compile_failed", openDiagnostics populated). Port 0 by default, so the real URL is in',
117
+ 'preview.ready. Keep the returned cursor and pass it to xeer_dev_status after each edit.',
118
+ 'You do not need a dev session to repair diagnostics: xeer_check is the same compiler.',
119
+ ].join(' '),
120
+ },
121
+ 'dev.status': {
122
+ mcpTitle: 'Read new dev-session events',
123
+ mcpDescription: [
124
+ 'Read the xeer.dev.v0 events emitted since `cursor`, then return the session summary.',
125
+ 'After editing a file, call this with waitMilliseconds set: the CLI coalesces changes behind a',
126
+ '75 ms quiet window, rebuilds, and emits compile.start, any compile.diagnostic, and compile.ready',
127
+ 'when the rebuild is accepted. openDiagnostics is the outcome of the latest attempt, so an empty',
128
+ 'openDiagnostics with a higher generation means the edit was accepted.',
129
+ ].join(' '),
130
+ },
131
+ 'dev.stop': {
132
+ mcpTitle: 'Stop a dev session',
133
+ mcpDescription: 'Stop a dev session over the documented xeer.dev.control.v0 IPC shutdown, releasing '
134
+ + 'the local state lease. Always stop sessions you started: the lease is per project and mode, '
135
+ + 'so a leaked session blocks the next dev run with XE1812.',
136
+ },
137
+ diagnostics: {
138
+ mcpTitle: 'Look up Xeer diagnostic codes',
139
+ mcpDescription: 'Explain a diagnostic code: what the platform observed and which edit closes it. '
140
+ + 'Omit `code` for the whole generated reference. This is the same catalogue the compiler emits '
141
+ + 'from, so it cannot drift from the codes you receive.',
142
+ },
143
+ };
144
+ const actions = [
145
+ {
146
+ id: 'check', command: ['check'], summary: 'Validate a project and report structured diagnostics.',
147
+ usage: ['check [directory] [--json]'], helpOrder: 10,
148
+ outputProtocol: 'xeer.command.v0', effects: ['read-source', 'write-generated'],
149
+ idempotent: true, reversible: true, destructive: false, humanPrerequisites: [],
150
+ pathPolicy: 'project-relative', surfaces: {
151
+ cli: true, mcpTool: 'xeer_check', mcpProfiles: XEER_MCP_PROFILES,
152
+ ...MCP_PRESENTATION.check,
153
+ mcpInputSchema: { type: 'object', properties: { directory: PROJECT_DIRECTORY_INPUT } },
154
+ },
155
+ },
156
+ {
157
+ id: 'build', command: ['build'], summary: 'Build and verify a content-addressed application artifact.',
158
+ usage: ['build [directory] [--json]'], helpOrder: 40,
159
+ outputProtocol: 'xeer.command.v0', effects: ['read-source', 'write-generated', 'run-local'],
160
+ idempotent: true, reversible: true, destructive: false, humanPrerequisites: [],
161
+ pathPolicy: 'project-relative', surfaces: {
162
+ cli: true, mcpTool: 'xeer_build', mcpProfiles: XEER_MCP_PROFILES,
163
+ ...MCP_PRESENTATION.build,
164
+ mcpInputSchema: { type: 'object', properties: { directory: PROJECT_DIRECTORY_INPUT } },
165
+ },
166
+ },
167
+ {
168
+ id: 'test', command: ['test'], summary: 'Run application tests against fresh isolated local state.',
169
+ usage: ['test [directory] [--host <host>] [--json]'], helpOrder: 250, outputProtocol: 'xeer.dev.v0',
170
+ effects: ['read-source', 'write-generated', 'run-local', 'write-state'],
171
+ idempotent: true, reversible: true, destructive: false, humanPrerequisites: [],
172
+ pathPolicy: 'project-relative', surfaces: {
173
+ cli: true, mcpTool: 'xeer_test', mcpProfiles: XEER_MCP_PROFILES,
174
+ ...MCP_PRESENTATION.test,
175
+ mcpInputSchema: { type: 'object', properties: {
176
+ directory: PROJECT_DIRECTORY_INPUT,
177
+ timeoutMilliseconds: { type: 'integer', minimum: 1_000, maximum: 1_800_000 },
178
+ } },
179
+ },
180
+ },
181
+ {
182
+ id: 'new', command: ['new'], summary: 'Create a new project from a supported scaffold.',
183
+ usage: ['new <directory> [--template {{templates}}] [--json]'], helpOrder: 30,
184
+ outputProtocol: 'xeer.command.v0', effects: ['write-source'], idempotent: false,
185
+ reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'project-relative',
186
+ surfaces: {
187
+ cli: true, mcpTool: 'xeer_new', mcpProfiles: XEER_MCP_PROFILES,
188
+ ...MCP_PRESENTATION.new,
189
+ mcpInputSchema: {
190
+ type: 'object',
191
+ properties: {
192
+ directory: {
193
+ type: 'string', description: 'Target directory, relative to the server root. Must be empty or absent.',
194
+ },
195
+ template: {
196
+ description: 'Which scaffold to write. Defaults to notes.', type: 'string',
197
+ enum: ['notes', 'todo', 'blog', 'personal-site'],
198
+ },
199
+ },
200
+ required: ['directory'],
201
+ },
202
+ },
203
+ },
204
+ {
205
+ id: 'agent.setup', command: ['agent', 'setup'],
206
+ summary: 'Install or verify project-confined agent adapters.',
207
+ usage: ['agent setup [directory] [--target auto|agents|claude|codex|cursor|vscode|mcp] [--check] [--json]'],
208
+ helpOrder: 35, outputProtocol: 'xeer.command.v0', effects: ['read-source', 'write-source'],
209
+ idempotent: true, reversible: true, destructive: false, humanPrerequisites: [],
210
+ pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
211
+ },
212
+ {
213
+ id: 'agent.context', command: ['agent', 'context'],
214
+ summary: 'Read normalized project facts, operations, diagnostics, tests, and safe next actions.',
215
+ usage: ['agent context [directory] [--json]'], helpOrder: 34,
216
+ outputProtocol: 'xeer.agent-context.v0', effects: ['read-source'],
217
+ idempotent: true, reversible: true, destructive: false, humanPrerequisites: [],
218
+ pathPolicy: 'project-relative', surfaces: {
219
+ cli: true, mcpTool: 'xeer_agent_context', mcpProfiles: XEER_MCP_PROFILES,
220
+ mcpTitle: 'Read normalized Xeer project context',
221
+ mcpDescription: 'Read the bounded, deterministic, versioned project context without writing generated files. Returns normalized manifest facts, source zones, operations, diagnostics, tests, safe next actions, resources, and a stable content hash.',
222
+ mcpInputSchema: { type: 'object', properties: { directory: PROJECT_DIRECTORY_INPUT } },
223
+ },
224
+ },
225
+ {
226
+ id: 'docs.search', command: ['docs', 'search'],
227
+ summary: 'Search the installed-version Xeer documentation index.',
228
+ usage: ['docs search <query> [--limit <n>] [--json]'], helpOrder: 36,
229
+ outputProtocol: 'xeer.docs-search.v0', effects: [],
230
+ idempotent: true, reversible: true, destructive: false, humanPrerequisites: [],
231
+ pathPolicy: 'none', surfaces: {
232
+ cli: true, mcpTool: 'xeer_docs_search', mcpProfiles: XEER_MCP_PROFILES,
233
+ mcpTitle: 'Search installed Xeer documentation',
234
+ mcpDescription: 'Run deterministic lexical search over the compact index bundled with this exact framework version. Returns matching resource URIs and content hashes; read the selected xeer://docs/<path> resource on demand.',
235
+ mcpInputSchema: { type: 'object', properties: {
236
+ query: { type: 'string', minLength: 1, maxLength: 500, description: 'Terms or phrase to search for.' },
237
+ limit: { type: 'integer', minimum: 1, maximum: 20, default: 5, description: 'Maximum result count.' },
238
+ }, required: ['query'] },
239
+ },
240
+ },
241
+ {
242
+ id: 'doctor', command: ['doctor'], summary: 'Diagnose the toolchain and generated project state.',
243
+ usage: ['doctor [directory] [--json]'], helpOrder: 20,
244
+ outputProtocol: 'xeer.command.v0', effects: ['read-source', 'write-generated'],
245
+ idempotent: true, reversible: true, destructive: false, humanPrerequisites: [],
246
+ pathPolicy: 'project-relative', surfaces: {
247
+ cli: true, mcpTool: 'xeer_doctor', mcpProfiles: XEER_MCP_PROFILES,
248
+ ...MCP_PRESENTATION.doctor,
249
+ mcpInputSchema: { type: 'object', properties: { directory: PROJECT_DIRECTORY_INPUT } },
250
+ },
251
+ },
252
+ {
253
+ id: 'deploy', command: ['deploy'], summary: 'Build and deploy an application artifact.',
254
+ usage: ['deploy [directory] [--environment prod|preview] [--control-url <url>] [--json]'], helpOrder: 50,
255
+ outputProtocol: 'xeer.command.v0',
256
+ effects: ['read-source', 'write-generated', 'run-local', 'network-read', 'network-write', 'production-change'],
257
+ idempotent: false, reversible: true, destructive: false,
258
+ humanPrerequisites: ['A human must establish the builder credential with xeer auth login.'],
259
+ pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: PREVIEW_ONLY },
260
+ },
261
+ {
262
+ id: 'promote', command: ['promote'], summary: 'Promote a preview artifact to production.',
263
+ usage: ['promote [app|directory] [--receipt <receiptId> | --from-artifact <artifactId>] '
264
+ + '[--control-url <url>] [--json]'], helpOrder: 80,
265
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'network-write', 'production-change'],
266
+ idempotent: false, reversible: true, destructive: false,
267
+ humanPrerequisites: ['The preview artifact must be reviewed before production promotion.'],
268
+ pathPolicy: 'project-or-url', surfaces: { cli: true, mcpExclusion: OPERATOR_ONLY },
269
+ },
270
+ {
271
+ id: 'deploy.preview', command: ['deploy'], summary: 'Build and deploy an artifact to preview only.',
272
+ usage: [], outputProtocol: 'xeer.command.v0',
273
+ effects: ['read-source', 'write-generated', 'run-local', 'network-read', 'network-write'],
274
+ idempotent: false, reversible: true, destructive: false,
275
+ humanPrerequisites: ['A human must establish the builder credential with xeer auth login.'],
276
+ pathPolicy: 'project-relative', surfaces: {
277
+ cli: false, mcpTool: 'xeer_deploy_preview', mcpProfiles: ['author', 'operator'],
278
+ ...MCP_PRESENTATION.deploy,
279
+ mcpInputSchema: { type: 'object', properties: {
280
+ directory: PROJECT_DIRECTORY_INPUT,
281
+ controlUrl: {
282
+ description: 'Allowed control-plane origin override. The hosted Xeer origin is allowed by default; '
283
+ + 'additional exact HTTPS or loopback development origins must be preconfigured.',
284
+ type: 'string',
285
+ },
286
+ } },
287
+ },
288
+ },
289
+ {
290
+ id: 'promote.operator', command: ['promote'], summary: 'Promote an exact review receipt in operator mode.',
291
+ usage: [], outputProtocol: 'xeer.command.v0', effects: ['network-read', 'network-write', 'production-change'],
292
+ idempotent: false, reversible: true, destructive: false,
293
+ humanPrerequisites: [
294
+ 'A human must review the exact artifact and start xeer-mcp with XEER_MCP_PROFILE=operator.',
295
+ ],
296
+ pathPolicy: 'project-or-url', surfaces: {
297
+ cli: false, mcpTool: 'xeer_promote', mcpProfiles: ['operator'],
298
+ ...MCP_PRESENTATION.promote,
299
+ mcpInputSchema: { type: 'object', properties: {
300
+ directory: PROJECT_DIRECTORY_INPUT,
301
+ receiptId: REVIEW_RECEIPT_ID_INPUT,
302
+ controlUrl: CONTROL_URL_INPUT,
303
+ }, required: ['receiptId'] },
304
+ },
305
+ },
306
+ {
307
+ id: 'auth.status', command: ['auth', 'status'], summary: 'Report builder credential metadata.',
308
+ usage: ['auth status [--control-url <url>] [--json]'], helpOrder: 180,
309
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'secret-metadata'], idempotent: true,
310
+ reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'none',
311
+ surfaces: {
312
+ cli: true, mcpTool: 'xeer_auth_status', mcpProfiles: XEER_MCP_PROFILES,
313
+ ...MCP_PRESENTATION['auth.status'],
314
+ mcpInputSchema: { type: 'object', properties: { controlUrl: CONTROL_URL_INPUT } },
315
+ },
316
+ },
317
+ {
318
+ id: 'inspect', command: ['inspect'], summary: 'Read a running application manifest.',
319
+ usage: ['inspect <local-url|app> [--environment prod|preview] [--control-url <url>] [--json]'], helpOrder: 230,
320
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'read-state'], idempotent: true,
321
+ reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'app-or-url',
322
+ surfaces: {
323
+ cli: true, mcpTool: 'xeer_inspect', mcpProfiles: XEER_MCP_PROFILES,
324
+ ...MCP_PRESENTATION.inspect,
325
+ mcpInputSchema: {
326
+ type: 'object',
327
+ properties: {
328
+ previewUrl: {
329
+ type: 'string', description: 'Loopback URL from preview.ready, hosted https://<app>.xeer.run origin, '
330
+ + 'application name, or appId.',
331
+ },
332
+ view: { default: 'manifest', type: 'string', enum: ['manifest', 'state', 'logs', 'export'] },
333
+ after: { description: 'Log cursor, for view: "logs".', type: 'string' },
334
+ },
335
+ required: ['previewUrl'],
336
+ },
337
+ },
338
+ },
339
+ {
340
+ id: 'dev.start', command: ['dev'], summary: 'Start a local development session.',
341
+ usage: ['dev [directory] [--host <host>] [--port <port>] [--json]'], helpOrder: 220,
342
+ outputProtocol: 'xeer.dev.v0', effects: ['read-source', 'write-generated', 'run-local', 'write-state'],
343
+ idempotent: false, reversible: true, destructive: false, humanPrerequisites: [],
344
+ pathPolicy: 'project-relative', surfaces: {
345
+ cli: true, mcpTool: 'xeer_dev_start', mcpProfiles: XEER_MCP_PROFILES,
346
+ ...MCP_PRESENTATION['dev.start'],
347
+ mcpInputSchema: { type: 'object', properties: {
348
+ directory: PROJECT_DIRECTORY_INPUT,
349
+ host: { description: 'Defaults to 127.0.0.1.', type: 'string' },
350
+ port: { description: 'Defaults to 0 (OS-assigned).', type: 'integer', minimum: 0, maximum: 65_535 },
351
+ timeoutMilliseconds: { type: 'integer', minimum: 1_000, maximum: 600_000 },
352
+ } },
353
+ },
354
+ },
355
+ {
356
+ id: 'dev.status', command: ['dev'], summary: 'Read new events from a local development session.',
357
+ usage: [], outputProtocol: 'xeer.dev.v0', effects: [], idempotent: true, reversible: true,
358
+ destructive: false, humanPrerequisites: [], pathPolicy: 'none',
359
+ surfaces: {
360
+ cli: false, mcpTool: 'xeer_dev_status', mcpProfiles: XEER_MCP_PROFILES,
361
+ ...MCP_PRESENTATION['dev.status'],
362
+ mcpInputSchema: { type: 'object', properties: {
363
+ sessionId: SESSION_ID_INPUT,
364
+ cursor: {
365
+ default: 0, description: 'Last seq you have already read.', type: 'integer',
366
+ minimum: 0, maximum: Number.MAX_SAFE_INTEGER,
367
+ },
368
+ waitMilliseconds: {
369
+ default: 0, description: 'Wait up to this long for new events to arrive and settle.',
370
+ type: 'integer', minimum: 0, maximum: 600_000,
371
+ },
372
+ limit: { default: 200, type: 'integer', minimum: 1, maximum: 1_000 },
373
+ } },
374
+ },
375
+ },
376
+ {
377
+ id: 'dev.stop', command: ['dev'], summary: 'Stop a local development session and release its lease.',
378
+ usage: [], outputProtocol: 'xeer.dev.v0', effects: ['run-local'], idempotent: true,
379
+ reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'none',
380
+ surfaces: {
381
+ cli: false, mcpTool: 'xeer_dev_stop', mcpProfiles: XEER_MCP_PROFILES,
382
+ ...MCP_PRESENTATION['dev.stop'],
383
+ mcpInputSchema: { type: 'object', properties: {
384
+ sessionId: SESSION_ID_INPUT,
385
+ cursor: { default: 0, type: 'integer', minimum: 0, maximum: Number.MAX_SAFE_INTEGER },
386
+ } },
387
+ },
388
+ },
389
+ {
390
+ id: 'diagnostics', command: [], summary: 'Read the generated diagnostic catalogue.',
391
+ usage: [], outputProtocol: 'unversioned', effects: [], idempotent: true, reversible: true,
392
+ destructive: false, humanPrerequisites: [], pathPolicy: 'none',
393
+ surfaces: {
394
+ cli: false, mcpTool: 'xeer_diagnostics', mcpProfiles: XEER_MCP_PROFILES,
395
+ ...MCP_PRESENTATION.diagnostics,
396
+ mcpInputSchema: { type: 'object', properties: {
397
+ code: { description: 'An XE#### code, e.g. XE1202.', type: 'string' },
398
+ } },
399
+ },
400
+ },
401
+ {
402
+ id: 'link', command: ['link'], summary: 'Link a checkout to an application the builder owns.',
403
+ usage: ['link [directory] [--app <appId>] [--new] [--control-url <url>] [--json]'], helpOrder: 60,
404
+ outputProtocol: 'xeer.command.v0', effects: ['read-source', 'write-source', 'network-read', 'network-write'],
405
+ idempotent: false, reversible: true, destructive: false,
406
+ humanPrerequisites: ['A human must choose which hosted application this checkout controls.'],
407
+ pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: HUMAN_IDENTITY },
408
+ },
409
+ {
410
+ id: 'deployments', command: ['deployments'], summary: 'List deployment history for an application.',
411
+ usage: ['deployments [app|directory] [--limit <n>] [--control-url <url>] [--json]'], helpOrder: 70,
412
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'read-state', 'secret-metadata'],
413
+ idempotent: true, reversible: true, destructive: false,
414
+ humanPrerequisites: ['A human must establish the builder credential with xeer auth login.'],
415
+ pathPolicy: 'project-or-url', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
416
+ },
417
+ {
418
+ id: 'rollback', command: ['rollback'], summary: 'Redeploy a previously deployed artifact.',
419
+ usage: ['rollback [app|directory] <artifactId> [--environment prod|preview] [--control-url <url>] [--json]'],
420
+ helpOrder: 90, outputProtocol: 'xeer.command.v0',
421
+ effects: ['network-read', 'network-write', 'production-change'], idempotent: false,
422
+ reversible: true, destructive: false,
423
+ humanPrerequisites: ['A human must choose the artifact and deployment environment to restore.'],
424
+ pathPolicy: 'project-or-url', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
425
+ },
426
+ {
427
+ id: 'disable', command: ['disable'], summary: 'Take an application offline without deleting its data.',
428
+ usage: ['disable [app|directory] [--control-url <url>] [--json]'], helpOrder: 100,
429
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'network-write', 'production-change'],
430
+ idempotent: true, reversible: true, destructive: false,
431
+ humanPrerequisites: ['A human must authorize taking the production application offline.'],
432
+ pathPolicy: 'project-or-url', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
433
+ },
434
+ {
435
+ id: 'enable', command: ['enable'], summary: 'Restore a disabled application to service.',
436
+ usage: ['enable [app|directory] [--control-url <url>] [--json]'], helpOrder: 110,
437
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'network-write', 'production-change'],
438
+ idempotent: true, reversible: true, destructive: false,
439
+ humanPrerequisites: ['A human must authorize restoring the production application.'],
440
+ pathPolicy: 'project-or-url', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
441
+ },
442
+ {
443
+ id: 'delete', command: ['delete'], summary: 'Permanently delete an application and all of its data.',
444
+ usage: ['delete [app|directory] --confirm <application> [--control-url <url>] [--json]'], helpOrder: 120,
445
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'network-write', 'write-state', 'production-change'],
446
+ idempotent: false, reversible: false, destructive: true,
447
+ humanPrerequisites: ['A human must provide the exact application name as irreversible confirmation.'],
448
+ pathPolicy: 'project-or-url', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
449
+ },
450
+ {
451
+ id: 'domains.add', command: ['domains', 'add'], summary: 'Attach a customer-owned hostname to an application.',
452
+ usage: ['domains add <hostname> [app|directory] [--control-url <url>] [--json]'], helpOrder: 125,
453
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'network-write', 'production-change'],
454
+ idempotent: true, reversible: true, destructive: false,
455
+ humanPrerequisites: ['A human must control the hostname DNS and authorize production routing.'],
456
+ pathPolicy: 'project-or-url', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
457
+ },
458
+ {
459
+ id: 'domains.ls', command: ['domains', 'ls'], aliases: [['domains', 'list']],
460
+ summary: 'List an application\'s platform and custom domains.',
461
+ usage: ['domains ls [app|directory] [--control-url <url>] [--json]'], helpOrder: 126,
462
+ outputProtocol: 'xeer.command.v0', effects: ['network-read'], idempotent: true,
463
+ reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'project-or-url',
464
+ surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
465
+ },
466
+ {
467
+ id: 'domains.status', command: ['domains', 'status'], summary: 'Refresh hostname and certificate validation.',
468
+ usage: ['domains status <hostname> [app|directory] [--control-url <url>] [--json]'], helpOrder: 127,
469
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'network-write', 'production-change'], idempotent: true,
470
+ reversible: true, destructive: false,
471
+ humanPrerequisites: ['A human must control the hostname DNS and authorize activating production routing.'],
472
+ pathPolicy: 'project-or-url',
473
+ surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
474
+ },
475
+ {
476
+ id: 'domains.remove', command: ['domains', 'remove'], aliases: [['domains', 'rm']],
477
+ summary: 'Detach a custom hostname and delete its provider certificate.',
478
+ usage: ['domains remove <hostname> [app|directory] [--control-url <url>] [--json]'], helpOrder: 128,
479
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'network-write', 'production-change'],
480
+ idempotent: true, reversible: true, destructive: true,
481
+ humanPrerequisites: ['A human must authorize removing production routing for the hostname.'],
482
+ pathPolicy: 'project-or-url', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
483
+ },
484
+ {
485
+ id: 'preview', command: ['preview'], summary: 'Run a verified artifact in a local preview server.',
486
+ usage: ['preview [directory] [--host <host>] [--port <port>] [--json]'], helpOrder: 240,
487
+ outputProtocol: 'xeer.dev.v0', effects: ['read-source', 'write-generated', 'run-local', 'write-state'],
488
+ idempotent: false, reversible: true, destructive: false, humanPrerequisites: [],
489
+ pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
490
+ },
491
+ {
492
+ id: 'state.read', command: ['state'], summary: 'Read record counts from a running application.',
493
+ usage: ['state <local-url|app> [--environment prod|preview] [--control-url <url>] [--json]'], helpOrder: 260,
494
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'read-state'], idempotent: true,
495
+ reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'app-or-url',
496
+ surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
497
+ },
498
+ {
499
+ id: 'state.reset', command: ['state', 'reset'], summary: 'Permanently reset local environment state.',
500
+ usage: ['state reset [directory] --state <dev|preview|test> --confirm <application> [--json]'], helpOrder: 270,
501
+ outputProtocol: 'xeer.command.v0', effects: ['read-source', 'write-state'], idempotent: false,
502
+ reversible: false, destructive: true,
503
+ humanPrerequisites: ['A human must provide the exact application name as destructive confirmation.'],
504
+ pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: DESTRUCTIVE_STATE },
505
+ },
506
+ {
507
+ id: 'logs', command: ['logs'], summary: 'Read the bounded log ring of a running application.',
508
+ usage: ['logs <local-url|app> [--environment prod|preview] [--after <cursor>] [--control-url <url>] [--json]'],
509
+ helpOrder: 280, outputProtocol: 'xeer.command.v0', effects: ['network-read', 'read-state'],
510
+ idempotent: true, reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'app-or-url',
511
+ surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
512
+ },
513
+ {
514
+ id: 'export', command: ['export'], summary: 'Export application state to a document.',
515
+ usage: [
516
+ 'export <local-url|app> [--environment prod|preview] [--out <file>] [--control-url <url>] [--json]',
517
+ 'export --state <dev|preview> [directory] [--out <file>] [--json]',
518
+ ],
519
+ helpOrder: 290, outputProtocol: 'xeer.command.v0', effects: ['network-read', 'read-state', 'write-file'],
520
+ idempotent: true, reversible: true, destructive: false, humanPrerequisites: [],
521
+ pathPolicy: 'project-or-url', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
522
+ },
523
+ {
524
+ id: 'import', command: ['import'], summary: 'Replace local or preview state from an export document.',
525
+ usage: [
526
+ 'import <preview-url> <file> [--json]',
527
+ 'import --state <dev|preview> [directory] <file> [--json]',
528
+ ],
529
+ helpOrder: 300, outputProtocol: 'xeer.command.v0',
530
+ effects: ['read-source', 'network-write', 'write-state'], idempotent: false,
531
+ reversible: false, destructive: true,
532
+ humanPrerequisites: ['A human must choose the export document that replaces existing state.'],
533
+ pathPolicy: 'project-or-url', surfaces: { cli: true, mcpExclusion: DESTRUCTIVE_STATE },
534
+ },
535
+ {
536
+ id: 'auth.login', command: ['auth', 'login'], summary: 'Establish a human builder credential.',
537
+ usage: ['auth login [--control-url <url>] [--no-open] [--json]'], helpOrder: 170,
538
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'network-write', 'secret-write'],
539
+ idempotent: false, reversible: true, destructive: false,
540
+ humanPrerequisites: ['A human must approve the exact browser verification code.'],
541
+ pathPolicy: 'none', surfaces: { cli: true, mcpExclusion: HUMAN_IDENTITY },
542
+ },
543
+ {
544
+ id: 'auth.logout', command: ['auth', 'logout'], summary: 'Remove the local builder credential.',
545
+ usage: ['auth logout [--control-url <url>] [--json]'], helpOrder: 190,
546
+ outputProtocol: 'xeer.command.v0', effects: ['network-write', 'secret-write'], idempotent: true,
547
+ reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'none',
548
+ surfaces: { cli: true, mcpExclusion: HUMAN_IDENTITY },
549
+ },
550
+ {
551
+ id: 'auth.as', command: ['auth', 'as'], summary: 'Select a deterministic local development persona.',
552
+ usage: ['auth as <alice|bob> [directory] [--workspace <id>]... [--json]'], helpOrder: 200,
553
+ outputProtocol: 'xeer.command.v0', effects: ['write-state'], idempotent: true,
554
+ reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'project-relative',
555
+ surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
556
+ },
557
+ {
558
+ id: 'auth.clear', command: ['auth', 'clear'], summary: 'Reset the local development persona.',
559
+ usage: ['auth clear [directory] [--json]'], helpOrder: 210,
560
+ outputProtocol: 'xeer.command.v0', effects: ['write-state'], idempotent: true,
561
+ reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'project-relative',
562
+ surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
563
+ },
564
+ {
565
+ id: 'env.set', command: ['env', 'set'], summary: 'Set an environment value or secret.',
566
+ usage: ['env set <NAME> [value] [--environment prod|preview|dev] [--dir <directory>] [--control-url <url>] [--json]'],
567
+ helpOrder: 130,
568
+ outputProtocol: 'xeer.command.v0', effects: ['network-write', 'write-state', 'secret-write'],
569
+ idempotent: true, reversible: true, destructive: false,
570
+ humanPrerequisites: ['A human must deliberately provide the secret value.'],
571
+ pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: SECRET_VALUES },
572
+ },
573
+ {
574
+ id: 'env.ls', command: ['env', 'ls'], aliases: [['env', 'list']],
575
+ summary: 'List environment variable names and metadata.',
576
+ usage: ['env ls [--environment prod|preview|dev] [--dir <directory>] [--control-url <url>] [--json]'],
577
+ helpOrder: 140,
578
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'secret-metadata'], idempotent: true,
579
+ reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'project-relative',
580
+ surfaces: { cli: true, mcpExclusion: SECRET_VALUES },
581
+ },
582
+ {
583
+ id: 'env.rm', command: ['env', 'rm'], aliases: [['env', 'remove']],
584
+ summary: 'Remove an environment value or secret.',
585
+ usage: ['env rm <NAME> [--environment prod|preview|dev] [--dir <directory>] [--control-url <url>] [--json]'],
586
+ helpOrder: 150,
587
+ outputProtocol: 'xeer.command.v0', effects: ['network-write', 'write-state', 'secret-write'],
588
+ idempotent: true, reversible: false, destructive: true,
589
+ humanPrerequisites: ['A human must choose the secret name to remove.'],
590
+ pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: SECRET_VALUES },
591
+ },
592
+ {
593
+ id: 'env.pull', command: ['env', 'pull'], summary: 'Write development secrets to a local file.',
594
+ usage: ['env pull [--environment prod|preview|dev] [--dir <directory>] [--control-url <url>] [--json]'],
595
+ helpOrder: 160,
596
+ outputProtocol: 'xeer.command.v0', effects: ['network-read', 'write-file', 'secret-read'],
597
+ idempotent: true, reversible: true, destructive: false,
598
+ humanPrerequisites: ['A human must authorize writing plaintext secrets to the checkout.'],
599
+ pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: SECRET_VALUES },
600
+ },
601
+ {
602
+ id: 'token.create', command: ['token', 'create'], summary: 'Issue a service builder token.',
603
+ usage: ['token create --name <label> [--control-url <url>] [--json]'], hiddenFromHelp: true,
604
+ outputProtocol: 'xeer.command.v0', effects: ['network-write', 'secret-write'], idempotent: false,
605
+ reversible: true, destructive: false,
606
+ humanPrerequisites: ['A human service builder must store the one-time plaintext token securely.'],
607
+ pathPolicy: 'none', surfaces: { cli: true, mcpExclusion: SERVICE_CREDENTIALS },
608
+ },
609
+ {
610
+ id: 'token.ls', command: ['token', 'ls'], aliases: [['token', 'list']],
611
+ summary: 'List service token metadata.', usage: ['token ls [--control-url <url>] [--json]'],
612
+ hiddenFromHelp: true, outputProtocol: 'xeer.command.v0', effects: ['network-read', 'secret-metadata'],
613
+ idempotent: true, reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'none',
614
+ surfaces: { cli: true, mcpExclusion: SERVICE_CREDENTIALS },
615
+ },
616
+ {
617
+ id: 'token.revoke', command: ['token', 'revoke'], summary: 'Revoke a service builder token.',
618
+ usage: ['token revoke <tokenId> [--reason <why>] [--control-url <url>] [--json]'], hiddenFromHelp: true,
619
+ outputProtocol: 'xeer.command.v0', effects: ['network-write', 'secret-write'], idempotent: true,
620
+ reversible: false, destructive: true,
621
+ humanPrerequisites: ['A human service builder must choose the credential to revoke.'],
622
+ pathPolicy: 'none', surfaces: { cli: true, mcpExclusion: SERVICE_CREDENTIALS },
623
+ },
624
+ {
625
+ id: 'actions', command: ['actions'], summary: 'Print the machine-readable action and safety catalogue.',
626
+ usage: ['actions [--json]'], helpOrder: 310, outputProtocol: 'xeer.command.v0', effects: [],
627
+ idempotent: true, reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'none',
628
+ surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
629
+ },
630
+ {
631
+ id: 'db.tables', command: ['db', 'tables'], summary: 'List the tables of a running application database.',
632
+ usage: ['db tables [directory] [--state <dev|preview>] [--json]'], helpOrder: 320,
633
+ outputProtocol: 'xeer.command.v0', effects: ['read-state'], idempotent: true, reversible: true,
634
+ destructive: false, humanPrerequisites: [], pathPolicy: 'project-relative',
635
+ surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
636
+ },
637
+ {
638
+ id: 'db.schema', command: ['db', 'schema'], summary: 'Print the declared schema of a running application database.',
639
+ usage: ['db schema [directory] [--state <dev|preview>] [--json]'], helpOrder: 330,
640
+ outputProtocol: 'xeer.command.v0', effects: ['read-state'], idempotent: true, reversible: true,
641
+ destructive: false, humanPrerequisites: [], pathPolicy: 'project-relative',
642
+ surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
643
+ },
644
+ {
645
+ // Not idempotent and not reversible, because `--write` admits UPDATE and DELETE and the action
646
+ // catalogue describes what an invocation may do rather than what a particular statement does.
647
+ // `destructive: false` all the same: a destructive action here is one that discards state
648
+ // wholesale, and this refuses DDL outright — the worst a statement can do is edit rows the
649
+ // schema still constrains.
650
+ id: 'db.exec', command: ['db', 'exec'], summary: 'Run one SQL statement against a running application database.',
651
+ usage: ['db exec [directory] <sql> [--state <dev|preview>] [--write] [--json]'], helpOrder: 340,
652
+ outputProtocol: 'xeer.command.v0', effects: ['read-state', 'write-state'], idempotent: false,
653
+ reversible: false, destructive: false,
654
+ humanPrerequisites: ['A human must pass --write before any statement that modifies rows.'],
655
+ pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: DESTRUCTIVE_STATE },
656
+ },
657
+ ];
658
+ export const XEER_ACTIONS = Object.freeze(actions);
659
+ export function actionDefinition(id) {
660
+ return XEER_ACTIONS.find((action) => action.id === id);
661
+ }
662
+ export function mcpActions(profile) {
663
+ return XEER_ACTIONS.filter((action) => action.surfaces.mcpTool
664
+ && action.surfaces.mcpProfiles?.includes(profile));
665
+ }
666
+ export function cliHelpActions() {
667
+ return XEER_ACTIONS
668
+ .filter((action) => action.surfaces.cli && !action.hiddenFromHelp)
669
+ .sort((left, right) => (left.helpOrder ?? Number.MAX_SAFE_INTEGER) - (right.helpOrder ?? Number.MAX_SAFE_INTEGER));
670
+ }
671
+ /**
672
+ * Extract the CLI option contract from the same usage strings that generate `xeer --help`.
673
+ * An option followed by a documented value token takes one; a closing bracket or end-of-line
674
+ * denotes a boolean flag. Keeping this derived means help, dispatch validation, and aliases cannot
675
+ * acquire separate hand-maintained allowlists.
676
+ */
677
+ export function cliOptionDefinitions(action) {
678
+ const definitions = new Map();
679
+ for (const usage of action.usage) {
680
+ for (const match of usage.matchAll(/--[a-z][a-z-]*/gu)) {
681
+ const name = match[0];
682
+ const tail = usage.slice((match.index ?? 0) + name.length);
683
+ const takesValue = /^[ \t]+(?:<|[A-Za-z0-9{])/u.test(tail);
684
+ const existing = definitions.get(name);
685
+ if (existing !== undefined && existing !== takesValue) {
686
+ throw new Error(`CLI option ${name} has inconsistent usage in ${action.id}.`);
687
+ }
688
+ definitions.set(name, takesValue);
689
+ }
690
+ }
691
+ return [...definitions].map(([name, takesValue]) => ({ name, takesValue }));
692
+ }
693
+ /** Resolves canonical and alias command prefixes, preferring `state reset` over the `state` target action. */
694
+ export function cliActionForTokens(tokens) {
695
+ let result;
696
+ let matchedLength = -1;
697
+ for (const action of XEER_ACTIONS) {
698
+ if (!action.surfaces.cli)
699
+ continue;
700
+ for (const command of [action.command, ...(action.aliases ?? [])]) {
701
+ if (command.length <= matchedLength)
702
+ continue;
703
+ if (command.every((token, index) => tokens[index] === token)) {
704
+ result = action;
705
+ matchedLength = command.length;
706
+ }
707
+ }
708
+ }
709
+ return result;
710
+ }
711
+ const WRITE_EFFECTS = new Set([
712
+ 'write-source', 'write-file', 'write-generated', 'run-local', 'write-state', 'network-write',
713
+ 'production-change', 'secret-write',
714
+ ]);
715
+ export function mcpAnnotations(action) {
716
+ return Object.freeze({
717
+ readOnlyHint: !action.effects.some((effect) => WRITE_EFFECTS.has(effect)),
718
+ destructiveHint: action.destructive,
719
+ idempotentHint: action.idempotent,
720
+ openWorldHint: action.effects.includes('network-read') || action.effects.includes('network-write'),
721
+ });
722
+ }
723
+ /** Exact Draft-07 object the MCP SDK must advertise for a registered tool. */
724
+ export function mcpInputJsonSchema(action) {
725
+ const schema = action.surfaces.mcpInputSchema;
726
+ if (!schema)
727
+ return undefined;
728
+ return Object.freeze({ ...schema, $schema: MCP_INPUT_SCHEMA_DRAFT });
729
+ }
730
+ export const ACTION_REFERENCE_START = '<!-- xeer-action-reference:start -->';
731
+ export const ACTION_REFERENCE_END = '<!-- xeer-action-reference:end -->';
732
+ export const ACTION_REFERENCE_TARGETS = [
733
+ 'docs/AGENTS.md',
734
+ 'packages/website/content/guides/agents.md',
735
+ '.agents/skills/xeer/SKILL.md',
736
+ 'packages/mcp/README.md',
737
+ 'packages/website/content/reference/cli.md',
738
+ ];
739
+ function code(value) {
740
+ return `\`${String(value).replaceAll('|', '\\|')}\``;
741
+ }
742
+ function effects(action) {
743
+ return action.effects.length === 0 ? 'none' : action.effects.map(code).join('<br>');
744
+ }
745
+ function safety(action) {
746
+ const annotations = mcpAnnotations(action);
747
+ return [
748
+ annotations.readOnlyHint ? 'read-only' : 'writes',
749
+ action.idempotent ? 'idempotent' : 'non-idempotent',
750
+ action.reversible ? 'reversible' : 'irreversible',
751
+ action.destructive ? 'destructive' : 'non-destructive',
752
+ ].join('; ');
753
+ }
754
+ function inputs(action) {
755
+ const schema = action.surfaces.mcpInputSchema;
756
+ if (!schema || Object.keys(schema.properties).length === 0)
757
+ return 'none';
758
+ const required = new Set(schema.required ?? []);
759
+ return Object.entries(schema.properties).map(([name, property]) => {
760
+ const kind = property.enum
761
+ ? property.enum.map(code).join(' / ')
762
+ : code(property.type);
763
+ const bounds = property.minimum === undefined && property.maximum === undefined
764
+ ? ''
765
+ : ` [${property.minimum ?? '-inf'}..${property.maximum ?? 'inf'}]`;
766
+ const fallback = property.default === undefined ? '' : `; default ${code(property.default)}`;
767
+ return `${code(`${name}${required.has(name) ? '' : '?'}`)}: ${kind}${bounds}${fallback}`;
768
+ }).join('<br>');
769
+ }
770
+ function prerequisites(action) {
771
+ return action.humanPrerequisites.length === 0
772
+ ? 'none'
773
+ : action.humanPrerequisites.map((value) => value.replaceAll('|', '\\|')).join('<br>');
774
+ }
775
+ /** One generated block is embedded in every agent-facing action-policy surface. */
776
+ export function renderActionReference() {
777
+ const mcp = XEER_ACTIONS.filter((action) => action.surfaces.mcpTool);
778
+ const authorCount = mcpActions('author').length;
779
+ const operatorCount = mcpActions('operator').length;
780
+ const excluded = XEER_ACTIONS.filter((action) => action.surfaces.cli && !action.surfaces.mcpTool);
781
+ return [
782
+ ACTION_REFERENCE_START,
783
+ '<!-- Generated from packages/spec/src/actions.ts by `pnpm generate:agent-reference`. Do not edit. -->',
784
+ '',
785
+ `**MCP tools (${mcp.length} total; ${authorCount} author, ${operatorCount} operator).** `
786
+ + 'Inputs ending in `?` are optional.',
787
+ '',
788
+ '| Tool | Action | Profiles | Summary | Inputs | Effects | Safety | Path policy | Output | Human prerequisite |',
789
+ '| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |',
790
+ ...mcp.map((action) => `| ${code(action.surfaces.mcpTool)} | ${code(action.id)} | `
791
+ + `${action.surfaces.mcpProfiles.map(code).join(' / ')} | `
792
+ + `${action.summary.replaceAll('|', '\\|')} | ${inputs(action)} | `
793
+ + `${effects(action)} | ${safety(action)} | ${code(action.pathPolicy)} | ${code(action.outputProtocol)} | `
794
+ + `${prerequisites(action)} |`),
795
+ '',
796
+ `**CLI actions intentionally excluded from MCP (${excluded.length}).**`,
797
+ '',
798
+ '| CLI action | Action | Summary | Effects | Safety | Path policy | Output | Why no MCP tool |',
799
+ '| --- | --- | --- | --- | --- | --- | --- | --- |',
800
+ ...excluded.map((action) => `| ${code(`xeer ${action.command.join(' ')}`)} | ${code(action.id)} | ${action.summary.replaceAll('|', '\\|')} | `
801
+ + `${effects(action)} | ${safety(action)} | ${code(action.pathPolicy)} | ${code(action.outputProtocol)} | `
802
+ + `${action.surfaces.mcpExclusion.replaceAll('|', '\\|')} |`),
803
+ ACTION_REFERENCE_END,
804
+ ].join('\n');
805
+ }