@usegraft/cli 0.2.0 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,16 +9,20 @@ Part of [Graft](https://github.com/AndersonDesign1/graft), a CMS built so an AI
9
9
  ```bash
10
10
  npm i -D @usegraft/cli
11
11
  # or run it without installing
12
- npx graft init
12
+ npx @usegraft/cli@latest init
13
13
  ```
14
14
 
15
+ Once installed, the command is `graft`. Run it without installing under the
16
+ full package name: the bare name `graft` on npm belongs to an unrelated
17
+ package, so `npx graft` fetches the wrong thing.
18
+
15
19
  ## Quickstart, with no database
16
20
 
17
21
  A content project needs no services at all. `graft compile` writes the index to a SQLite artifact your app reads embedded.
18
22
 
19
23
  ```bash
20
- npx graft init # scaffolds graft.config.ts, content/, llms.txt
21
- npx graft compile # → .graft/index.db
24
+ npx @usegraft/cli@latest init # scaffolds graft.config.ts, content/, llms.txt
25
+ npx @usegraft/cli@latest compile # → .graft/index.db
22
26
  ```
23
27
 
24
28
  Move to Postgres by setting `DATABASE_URL` and changing one line in `graft.config.ts`:
@@ -34,7 +38,7 @@ export const index = "postgres";
34
38
  | `graft init` | Scaffold a project |
35
39
  | `graft compile` | Validate authored MDX and project it into the index |
36
40
  | `graft dev` | Recompile on save |
37
- | `graft serve` | Run the headless runtime: typed functions, MCP over HTTP, `/healthz` |
41
+ | `graft serve` | Run the headless runtime: typed functions, MCP, authored-content reads, `/healthz` |
38
42
  | `graft studio` | Open the optional editing UI |
39
43
  | `graft mcp` | Serve MCP on stdio, for `.mcp.json` |
40
44
  | `graft add <item>` | Copy a registry primitive into your repo, shadcn-style |
@@ -57,4 +61,4 @@ graft harden graft_runtime
57
61
 
58
62
  ---
59
63
 
60
- MIT. [Repository](https://github.com/AndersonDesign1/graft) · [Changelog](https://github.com/AndersonDesign1/graft/blob/feat/core/packages/cli/CHANGELOG.md) · [Security policy](https://github.com/AndersonDesign1/graft/blob/feat/core/SECURITY.md)
64
+ MIT. [Repository](https://github.com/AndersonDesign1/graft) · [Changelog](https://github.com/AndersonDesign1/graft/blob/main/packages/cli/CHANGELOG.md) · [Security policy](https://github.com/AndersonDesign1/graft/blob/main/SECURITY.md)
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  findConfig
4
- } from "./chunk-2NH6KYTL.js";
4
+ } from "./chunk-2LG6TWYY.js";
5
5
 
6
6
  // src/commands/add.ts
7
7
  import { dirname } from "path";
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ approvalsListCommand,
4
+ decideCommand,
5
+ formatApproval,
6
+ operatorName
7
+ } from "./chunk-NBJ4N4O6.js";
8
+ import "./chunk-2LG6TWYY.js";
9
+ export {
10
+ approvalsListCommand,
11
+ decideCommand,
12
+ formatApproval,
13
+ operatorName
14
+ };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  loadProjectEnv
4
- } from "./chunk-2NH6KYTL.js";
4
+ } from "./chunk-2LG6TWYY.js";
5
5
 
6
6
  // src/commands/asset.ts
7
7
  import { readFileSync, statSync } from "fs";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  loadProjectEnv,
4
4
  requireDatabaseUrl
5
- } from "./chunk-2NH6KYTL.js";
5
+ } from "./chunk-2LG6TWYY.js";
6
6
 
7
7
  // src/commands/branch.ts
8
8
  import { GraftError } from "@usegraft/contracts";
@@ -98,6 +98,7 @@ async function loadConfig(configPath) {
98
98
  const index = parseIndexConfig(mod.index, projectDir, configPath);
99
99
  assertStaticSupports(index, collections, functions, configPath);
100
100
  const mdxTrust = parseMdxTrust(mod.mdxTrust, configPath);
101
+ const approvalPolicy = parseApprovalPolicy(mod.approvalPolicy, configPath);
101
102
  const contentDirSetting = typeof mod.contentDir === "string" ? mod.contentDir : "content";
102
103
  const migrationsDirSetting = typeof mod.migrationsDir === "string" ? mod.migrationsDir : "migrations";
103
104
  return {
@@ -108,7 +109,8 @@ async function loadConfig(configPath) {
108
109
  index,
109
110
  collections,
110
111
  functions,
111
- mdxTrust
112
+ mdxTrust,
113
+ approvalPolicy
112
114
  };
113
115
  }
114
116
  function parseMdxTrust(value, configPath) {
@@ -121,6 +123,16 @@ function parseMdxTrust(value, configPath) {
121
123
  details: { mdxTrust: value }
122
124
  });
123
125
  }
126
+ function parseApprovalPolicy(value, configPath) {
127
+ if (value === void 0) return "none";
128
+ if (value === "none" || value === "human" || value === "unattended") return value;
129
+ throw new GraftError({
130
+ code: "CONFIG_INVALID",
131
+ message: `${configPath} exports \`approvalPolicy\` as ${JSON.stringify(value)}, which is not "none", "human" or "unattended".`,
132
+ fix: 'Export `approvalPolicy = "none"` (the default: only `destructive` functions are gated), `"human"` (every mutation is gated), or `"unattended"` (nothing is gated, for a caller with no human behind it). Omit the export to keep the default.',
133
+ details: { approvalPolicy: value }
134
+ });
135
+ }
124
136
  function assertStaticSupports(index, collections, functions, configPath) {
125
137
  if (index.driver !== "static") return;
126
138
  const dbCollections = Object.values(collections).filter((collection) => collection.authority === "db-authoritative").map((collection) => collection.name);
@@ -7,7 +7,7 @@ import {
7
7
  loadConfig,
8
8
  loadProjectEnv,
9
9
  requireDatabaseUrl
10
- } from "./chunk-2NH6KYTL.js";
10
+ } from "./chunk-2LG6TWYY.js";
11
11
 
12
12
  // src/commands/compile.ts
13
13
  import { GraftError } from "@usegraft/contracts";
@@ -4,7 +4,7 @@ import {
4
4
  loadConfig,
5
5
  loadProjectEnv,
6
6
  requireDatabaseUrl
7
- } from "./chunk-2NH6KYTL.js";
7
+ } from "./chunk-2LG6TWYY.js";
8
8
 
9
9
  // src/commands/serve.ts
10
10
  import { execFileSync } from "child_process";
@@ -18,6 +18,9 @@ function createServeRouter(routes) {
18
18
  if (pathname === "/healthz") return routes.health(request);
19
19
  if (pathname === "/api/mcp") return routes.mcp(request);
20
20
  if (pathname === "/api/fn" || pathname.startsWith("/api/fn/")) return routes.fn(request);
21
+ if (pathname === "/api/content/v1" || pathname.startsWith("/api/content/v1/")) {
22
+ return routes.content(request);
23
+ }
21
24
  if (routes.studio && (pathname === "/studio" || pathname.startsWith("/studio/") || pathname.startsWith("/api/studio/"))) {
22
25
  return routes.studio(request);
23
26
  }
@@ -25,7 +28,7 @@ function createServeRouter(routes) {
25
28
  const error = new GraftError({
26
29
  code: "ROUTE_NOT_FOUND",
27
30
  message: `Nothing is mounted at ${pathname}.`,
28
- fix: `Use POST /api/fn/<name> (typed functions), POST /api/mcp (MCP Streamable HTTP), or GET /healthz${studioHint}.`,
31
+ fix: `Use POST /api/fn/<name> (typed functions), POST /api/mcp (MCP Streamable HTTP), GET /api/content/v1/documents (authored content), GET /api/content/v1/search, or GET /healthz${studioHint}.`,
29
32
  details: { pathname }
30
33
  });
31
34
  return new Response(JSON.stringify(error.toJSON()), {
@@ -128,11 +131,13 @@ async function startServe(options) {
128
131
  const [
129
132
  { createFunctionsHandler },
130
133
  { createGraftMcpHandler },
131
- { createDb, resolveBranchHandle, scopeWriteBranch, sql },
134
+ { createContentApiHandler },
135
+ { createDb, createDbIndexReader, resolveBranchHandle, scopeWriteBranch, sql },
132
136
  studioMod
133
137
  ] = await Promise.all([
134
138
  import("@usegraft/core"),
135
139
  import("@usegraft/mcp"),
140
+ import("@usegraft/content-api"),
136
141
  import("@usegraft/db"),
137
142
  enableStudio ? import("@usegraft/studio") : Promise.resolve(null)
138
143
  ]);
@@ -147,7 +152,12 @@ async function startServe(options) {
147
152
  issuers,
148
153
  devTokens: devToken ? { [devToken]: { kind: "agent", id: "graft-serve", scopes } } : void 0
149
154
  });
150
- const approvalPolicy = process.env.GRAFT_APPROVAL_POLICY === "human" ? "human" : "none";
155
+ const approvalPolicy = config.approvalPolicy;
156
+ if (process.env.GRAFT_APPROVAL_POLICY) {
157
+ console.warn(
158
+ `[graft serve] GRAFT_APPROVAL_POLICY is set and is now ignored. Approval policy moved to graft.config.ts so that turning off the gate on destructive functions appears in a diff. Export \`approvalPolicy = "${process.env.GRAFT_APPROVAL_POLICY}"\` there instead. Serving with "${approvalPolicy}".`
159
+ );
160
+ }
151
161
  const host = options.host ?? process.env.HOST ?? "127.0.0.1";
152
162
  const loopback = host === "127.0.0.1" || host === "localhost" || host === "::1";
153
163
  const anonymousOptIn = process.env.GRAFT_MCP_ALLOW_ANONYMOUS === "1";
@@ -174,7 +184,26 @@ async function startServe(options) {
174
184
  branchId: writeBranch,
175
185
  scope: branch.scope,
176
186
  actor: resolveActor,
177
- allowAnonymous: allowAnonymousMcp
187
+ allowAnonymous: allowAnonymousMcp,
188
+ // The same backstop the functions handler gets. Without it a function with
189
+ // no per-function rateLimit was capped over POST /api/fn and uncapped over
190
+ // run_function, so the transport decided the limit — and tools/functions.ts
191
+ // claims the two surfaces apply rate limits identically.
192
+ rateLimit: { limit: 60, windowSeconds: 60 }
193
+ });
194
+ const allowedOriginsRaw = process.env.GRAFT_CONTENT_ALLOWED_ORIGINS?.trim();
195
+ const allowedOrigins = allowedOriginsRaw === void 0 || allowedOriginsRaw === "" ? void 0 : allowedOriginsRaw === "*" ? "*" : allowedOriginsRaw.split(",").map((entry) => entry.trim()).filter(Boolean);
196
+ const contentHandler = createContentApiHandler({
197
+ collections: Object.keys(config.collections),
198
+ branch: writeBranch,
199
+ index: createDbIndexReader(branch.db),
200
+ ...allowedOrigins === void 0 ? {} : { allowedOrigins },
201
+ // The same backstop the functions handler gets, for the same reason. These
202
+ // routes authenticate nobody and run database listings and full-text
203
+ // searches, so without a limit here one anonymous caller can keep the index
204
+ // busy indefinitely — and `graft serve` is the mount most likely to face
205
+ // the open internet.
206
+ rateLimit: { limit: 60, windowSeconds: 60 }
178
207
  });
179
208
  const health = async () => {
180
209
  try {
@@ -218,6 +247,11 @@ async function startServe(options) {
218
247
  "[graft serve] WARNING: binding beyond loopback with no identity configured \u2014 MCP will refuse every caller, because there is nothing to authenticate them against. Set GRAFT_DEV_TOKEN or GRAFT_TRUSTED_ISSUERS."
219
248
  );
220
249
  }
250
+ if (approvalPolicy === "unattended") {
251
+ console.warn(
252
+ '[graft serve] WARNING: approvalPolicy = "unattended" in graft.config.ts \u2014 destructive functions run without human approval. Deletes are not recoverable from git: deleteRecord removes rows outright and the asset store keeps no history. Audit rows are still written. This applies to POST /api/fn only; run_function over /api/mcp stays gated, because the policy is not part of the MCP surface.'
253
+ );
254
+ }
221
255
  let studioHandler;
222
256
  if (studioMod) {
223
257
  const authenticate = !loopback && (devToken || issuers.length > 0) ? async (request) => {
@@ -240,7 +274,13 @@ async function startServe(options) {
240
274
  }
241
275
  const server = createServer(
242
276
  createNodeListener(
243
- createServeRouter({ fn: fnHandler, mcp: mcpHandler, health, studio: studioHandler }),
277
+ createServeRouter({
278
+ fn: fnHandler,
279
+ mcp: mcpHandler,
280
+ content: contentHandler,
281
+ health,
282
+ studio: studioHandler
283
+ }),
244
284
  { allowedHosts: allowedHostsFor(host) }
245
285
  )
246
286
  );
@@ -272,6 +312,8 @@ async function serveCommand(options) {
272
312
  `graft serve \u2014 branch "${running.branch}"`,
273
313
  ` functions POST ${base}/api/fn/<name>`,
274
314
  ` mcp POST ${base}/api/mcp`,
315
+ ` documents GET ${base}/api/content/v1/documents`,
316
+ ` search GET ${base}/api/content/v1/search`,
275
317
  ` health GET ${base}/healthz`,
276
318
  ...studioOn ? [
277
319
  ` studio GET ${base}/studio`,
@@ -4,7 +4,7 @@ import {
4
4
  loadConfig,
5
5
  loadProjectEnv,
6
6
  requireDatabaseUrl
7
- } from "./chunk-2NH6KYTL.js";
7
+ } from "./chunk-2LG6TWYY.js";
8
8
 
9
9
  // src/commands/migrate.ts
10
10
  import { existsSync, readdirSync, statSync } from "fs";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  loadProjectEnv,
4
4
  requireDatabaseUrl
5
- } from "./chunk-2NH6KYTL.js";
5
+ } from "./chunk-2LG6TWYY.js";
6
6
 
7
7
  // src/commands/approvals.ts
8
8
  import { userInfo } from "os";
@@ -59,7 +59,9 @@ function formatApproval(row) {
59
59
  ` input: ${JSON.stringify(row.input)}`
60
60
  ].join("\n");
61
61
  }
62
+
62
63
  export {
64
+ operatorName,
63
65
  approvalsListCommand,
64
66
  decideCommand,
65
67
  formatApproval
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  loadProjectEnv,
4
4
  requireDatabaseUrl
5
- } from "./chunk-2NH6KYTL.js";
5
+ } from "./chunk-2LG6TWYY.js";
6
6
 
7
7
  // src/commands/compilations.ts
8
8
  async function compilationsListCommand(options) {
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  assertNoStaticBranch,
4
4
  compileCommand
5
- } from "./chunk-GDTOV3DU.js";
5
+ } from "./chunk-2N3HXYNN.js";
6
6
  import "./chunk-GPA66AP5.js";
7
- import "./chunk-2NH6KYTL.js";
7
+ import "./chunk-2LG6TWYY.js";
8
8
  export {
9
9
  assertNoStaticBranch,
10
10
  compileCommand
@@ -4,7 +4,7 @@ import {
4
4
  loadConfig,
5
5
  loadProjectEnv,
6
6
  requireDatabaseUrl
7
- } from "./chunk-2NH6KYTL.js";
7
+ } from "./chunk-2LG6TWYY.js";
8
8
 
9
9
  // src/commands/content.ts
10
10
  async function contentListCommand(options) {
@@ -4,7 +4,7 @@ import {
4
4
  loadConfig,
5
5
  loadProjectEnv,
6
6
  requireDatabaseUrl
7
- } from "./chunk-2NH6KYTL.js";
7
+ } from "./chunk-2LG6TWYY.js";
8
8
 
9
9
  // src/commands/db.ts
10
10
  import { GraftError } from "@usegraft/contracts";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  assertNoStaticBranch
4
- } from "./chunk-GDTOV3DU.js";
4
+ } from "./chunk-2N3HXYNN.js";
5
5
  import {
6
6
  formatCompileResult,
7
7
  printGraftError
@@ -12,7 +12,7 @@ import {
12
12
  loadConfig,
13
13
  loadProjectEnv,
14
14
  requireDatabaseUrl
15
- } from "./chunk-2NH6KYTL.js";
15
+ } from "./chunk-2LG6TWYY.js";
16
16
 
17
17
  // src/commands/dev.ts
18
18
  import { existsSync, watch } from "fs";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  loadProjectEnv,
4
4
  requireDatabaseUrl
5
- } from "./chunk-2NH6KYTL.js";
5
+ } from "./chunk-2LG6TWYY.js";
6
6
 
7
7
  // src/commands/harden.ts
8
8
  async function hardenCommand(options) {
package/dist/index.js CHANGED
@@ -50,6 +50,11 @@ function printHelp() {
50
50
  " --port <n> Port for `graft serve` / `graft studio` (serve default 3903; studio 4983)",
51
51
  " --host <h> Host for `graft serve` / `graft studio` (default: 127.0.0.1, or HOST)",
52
52
  " --studio Mount opt-in Studio on `graft serve` at /studio (or GRAFT_STUDIO=1)",
53
+ " --elicit-approvals",
54
+ " `graft mcp` asks THIS terminal's operator to decide a destructive call,",
55
+ " instead of failing with an id for `graft approve`. Local only: the human",
56
+ " must be present and the client must support MCP elicitation. Decisions",
57
+ " are recorded as the OS user, exactly as `graft approve` records them.",
53
58
  " --from <name> Parent to fork from (branch create; default: main)",
54
59
  " --into <name> Merge target (merge; default: main)",
55
60
  " --backend <kind> Branch backend: overlay (default) or neon (branch create)",
@@ -80,6 +85,7 @@ function parseArgs(rest) {
80
85
  let overwrite = false;
81
86
  let pruneUnknown = false;
82
87
  let studio = false;
88
+ let elicitApprovals = false;
83
89
  let initDriver;
84
90
  const value = (flag, raw) => {
85
91
  if (!raw || raw.startsWith("-")) {
@@ -115,6 +121,8 @@ function parseArgs(rest) {
115
121
  pruneUnknown = true;
116
122
  } else if (arg === "--studio") {
117
123
  studio = true;
124
+ } else if (arg === "--elicit-approvals") {
125
+ elicitApprovals = true;
118
126
  } else if (arg === "--postgres") {
119
127
  initDriver = "postgres";
120
128
  } else if (arg === "--static") {
@@ -138,6 +146,7 @@ function parseArgs(rest) {
138
146
  overwrite,
139
147
  pruneUnknown,
140
148
  studio,
149
+ elicitApprovals,
141
150
  initDriver
142
151
  };
143
152
  }
@@ -156,7 +165,7 @@ async function run(argv, options = {}) {
156
165
  const args = parseArgs(rest);
157
166
  switch (command) {
158
167
  case "init": {
159
- const { initCommand } = await import("./init-TRPSGUYW.js");
168
+ const { initCommand } = await import("./init-FV7Q6VXM.js");
160
169
  const result = initCommand({
161
170
  targetDir: args.positionals[0] ?? cwd,
162
171
  driver: args.initDriver
@@ -187,12 +196,12 @@ async function run(argv, options = {}) {
187
196
  return 0;
188
197
  }
189
198
  case "compile": {
190
- const { compileCommand } = await import("./compile-TCBGZZAU.js");
199
+ const { compileCommand } = await import("./compile-AWPSEA7U.js");
191
200
  await compileCommand({ cwd, branchId: args.branchId, pruneUnknown: args.pruneUnknown });
192
201
  return 0;
193
202
  }
194
203
  case "dev": {
195
- const { devCommand } = await import("./dev-4YSMWIAS.js");
204
+ const { devCommand } = await import("./dev-WHTIRWMR.js");
196
205
  await devCommand({ cwd, branchId: args.branchId });
197
206
  return 0;
198
207
  }
@@ -203,7 +212,7 @@ async function run(argv, options = {}) {
203
212
  subcommand ? `unknown db subcommand "${subcommand}" \u2014 the only one is \`graft db migrate\`` : "usage: graft db migrate [--dry-run]"
204
213
  );
205
214
  }
206
- const { dbMigrateCommand, formatMigrateResult } = await import("./db-WACSH3HN.js");
215
+ const { dbMigrateCommand, formatMigrateResult } = await import("./db-5YMFKHGO.js");
207
216
  const result = await dbMigrateCommand({ cwd, dryRun: args.dryRun });
208
217
  console.log(formatMigrateResult(result));
209
218
  return 0;
@@ -213,7 +222,7 @@ async function run(argv, options = {}) {
213
222
  if (subcommand !== "put" || !file) {
214
223
  throw new UsageError("usage: graft asset put <file> [key]");
215
224
  }
216
- const { assetPutCommand } = await import("./asset-DMNX5JBZ.js");
225
+ const { assetPutCommand } = await import("./asset-JWER6AOG.js");
217
226
  const result = await assetPutCommand({ cwd, file, key });
218
227
  console.log(`Uploaded ${result.key} (${result.contentType}, ${result.bytes} bytes)`);
219
228
  console.log(
@@ -228,7 +237,7 @@ async function run(argv, options = {}) {
228
237
  return 0;
229
238
  }
230
239
  case "approvals": {
231
- const { approvalsListCommand, formatApproval } = await import("./approvals-MN3IS3FO.js");
240
+ const { approvalsListCommand, formatApproval } = await import("./approvals-4PAANLA4.js");
232
241
  const pending = await approvalsListCommand({ cwd });
233
242
  if (pending.length === 0) {
234
243
  console.log("No pending approvals.");
@@ -245,7 +254,7 @@ async function run(argv, options = {}) {
245
254
  case "deny": {
246
255
  const id = args.positionals[0];
247
256
  if (!id) throw new UsageError(`usage: graft ${command} <approval-id>`);
248
- const { decideCommand } = await import("./approvals-MN3IS3FO.js");
257
+ const { decideCommand } = await import("./approvals-4PAANLA4.js");
249
258
  const decision = command === "approve" ? "approved" : "denied";
250
259
  const row = await decideCommand({ cwd, id, decision });
251
260
  console.log(
@@ -259,7 +268,7 @@ async function run(argv, options = {}) {
259
268
  return 0;
260
269
  }
261
270
  case "migrate": {
262
- const { migrateCommand } = await import("./migrate-QB44VL6R.js");
271
+ const { migrateCommand } = await import("./migrate-6ZYG2H5C.js");
263
272
  await migrateCommand({ cwd, branchId: args.branchId, apply: args.apply });
264
273
  return 0;
265
274
  }
@@ -273,7 +282,7 @@ async function run(argv, options = {}) {
273
282
  if (subcommand && !name) {
274
283
  throw new UsageError(`usage: graft branch ${subcommand} <name>`);
275
284
  }
276
- const mod = await import("./branch-IDY42TAV.js");
285
+ const mod = await import("./branch-U7FFDTFV.js");
277
286
  if (!subcommand) {
278
287
  const rows = await mod.branchListCommand({ cwd });
279
288
  if (rows.length === 0) {
@@ -316,12 +325,12 @@ async function run(argv, options = {}) {
316
325
  const branch = args.positionals[0];
317
326
  if (!branch)
318
327
  throw new UsageError("usage: graft merge <branch> [--into <target>] [--apply]");
319
- const { mergeCommand } = await import("./merge-XMTFPLHO.js");
328
+ const { mergeCommand } = await import("./merge-ZWYZJVFE.js");
320
329
  await mergeCommand({ cwd, branch, into: args.into, apply: args.apply });
321
330
  return 0;
322
331
  }
323
332
  case "add": {
324
- const { addCommand } = await import("./add-ASXTMYCX.js");
333
+ const { addCommand } = await import("./add-TCQY3YAN.js");
325
334
  await addCommand({
326
335
  cwd,
327
336
  names: args.positionals,
@@ -331,12 +340,12 @@ async function run(argv, options = {}) {
331
340
  return 0;
332
341
  }
333
342
  case "mcp": {
334
- const { mcpCommand } = await import("./mcp-LUJMTAQO.js");
335
- await mcpCommand({ cwd, branchId: args.branchId });
343
+ const { mcpCommand } = await import("./mcp-77QFFJXK.js");
344
+ await mcpCommand({ cwd, branchId: args.branchId, elicitApprovals: args.elicitApprovals });
336
345
  return 0;
337
346
  }
338
347
  case "serve": {
339
- const { serveCommand } = await import("./serve-ZGXLEAEB.js");
348
+ const { serveCommand } = await import("./serve-5ADUQG66.js");
340
349
  await serveCommand({
341
350
  cwd,
342
351
  branchId: args.branchId,
@@ -347,7 +356,7 @@ async function run(argv, options = {}) {
347
356
  return 0;
348
357
  }
349
358
  case "studio": {
350
- const { studioCommand } = await import("./studio-HXVSO4WJ.js");
359
+ const { studioCommand } = await import("./studio-7VAYFEWS.js");
351
360
  await studioCommand({
352
361
  cwd,
353
362
  branchId: args.branchId,
@@ -357,7 +366,7 @@ async function run(argv, options = {}) {
357
366
  return 0;
358
367
  }
359
368
  case "content": {
360
- const { contentListCommand, formatContentLine } = await import("./content-VLKNYIHL.js");
369
+ const { contentListCommand, formatContentLine } = await import("./content-ZULMTJMD.js");
361
370
  const { branch, lines } = await contentListCommand({ cwd, branchId: args.branchId });
362
371
  if (lines.length === 0) {
363
372
  console.log(`No content on branch "${branch}" (run graft compile).`);
@@ -369,7 +378,7 @@ async function run(argv, options = {}) {
369
378
  return 0;
370
379
  }
371
380
  case "compilations": {
372
- const { compilationsListCommand, formatCompilation } = await import("./compilations-72DJ2VEP.js");
381
+ const { compilationsListCommand, formatCompilation } = await import("./compilations-CH6AIOFG.js");
373
382
  const rows = await compilationsListCommand({ cwd, branchId: args.branchId });
374
383
  if (rows.length === 0) {
375
384
  console.log("No compilations recorded yet (run graft compile).");
@@ -381,7 +390,7 @@ async function run(argv, options = {}) {
381
390
  case "harden": {
382
391
  const role = args.positionals[0];
383
392
  if (!role) throw new UsageError("usage: graft harden <role>");
384
- const { hardenCommand } = await import("./harden-QASY2KMI.js");
393
+ const { hardenCommand } = await import("./harden-QONL2MAO.js");
385
394
  const result = await hardenCommand({ cwd, role });
386
395
  console.log(`Hardened runtime role "${result.role}" (${result.statements.length} grants):`);
387
396
  for (const statement of result.statements) console.log(` ${statement}`);
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CONFIG_FILENAMES
4
- } from "./chunk-2NH6KYTL.js";
4
+ } from "./chunk-2LG6TWYY.js";
5
5
 
6
6
  // src/commands/init.ts
7
7
  import { existsSync, mkdirSync, writeFileSync } from "fs";
@@ -1,14 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  assertNoStaticBranch
4
- } from "./chunk-GDTOV3DU.js";
4
+ } from "./chunk-2N3HXYNN.js";
5
5
  import "./chunk-GPA66AP5.js";
6
+ import {
7
+ operatorName
8
+ } from "./chunk-NBJ4N4O6.js";
6
9
  import {
7
10
  findConfig,
8
11
  loadConfig,
9
12
  loadProjectEnv,
10
13
  requireDatabaseUrl
11
- } from "./chunk-2NH6KYTL.js";
14
+ } from "./chunk-2LG6TWYY.js";
12
15
 
13
16
  // src/commands/mcp.ts
14
17
  import { createActorResolver } from "@usegraft/auth";
@@ -38,6 +41,7 @@ async function mcpCommand(options) {
38
41
  const resolved = await resolveActor(request);
39
42
  return resolved.kind === "anonymous" ? cliActor : resolved;
40
43
  };
44
+ const approvalElicitation = options.elicitApprovals ? { decider: { kind: "human", id: operatorName() } } : void 0;
41
45
  const server = createGraftMcp({
42
46
  name: "graft",
43
47
  contentDir: config.contentDir,
@@ -56,7 +60,8 @@ async function mcpCommand(options) {
56
60
  // Local agents may upload a file from the project; remote mounts get no
57
61
  // `path` argument at all.
58
62
  localUploadRoot: config.projectDir,
59
- defaultAuthorization: devToken
63
+ defaultAuthorization: devToken,
64
+ approvalElicitation
60
65
  });
61
66
  await serveStdio(server);
62
67
  } finally {
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  discoverMigrations
4
- } from "./chunk-6Z27SGVO.js";
4
+ } from "./chunk-DW6WTDRX.js";
5
5
  import {
6
6
  findConfig,
7
7
  loadConfig,
8
8
  loadProjectEnv,
9
9
  requireDatabaseUrl
10
- } from "./chunk-2NH6KYTL.js";
10
+ } from "./chunk-2LG6TWYY.js";
11
11
 
12
12
  // src/commands/merge.ts
13
13
  import { basename } from "path";
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  discoverMigrations,
4
4
  migrateCommand
5
- } from "./chunk-6Z27SGVO.js";
6
- import "./chunk-2NH6KYTL.js";
5
+ } from "./chunk-DW6WTDRX.js";
6
+ import "./chunk-2LG6TWYY.js";
7
7
  export {
8
8
  discoverMigrations,
9
9
  migrateCommand
@@ -5,8 +5,8 @@ import {
5
5
  createServeRouter,
6
6
  serveCommand,
7
7
  startServe
8
- } from "./chunk-U25NVB6W.js";
9
- import "./chunk-2NH6KYTL.js";
8
+ } from "./chunk-CONTLKSZ.js";
9
+ import "./chunk-2LG6TWYY.js";
10
10
  export {
11
11
  allowedHostsFor,
12
12
  createNodeListener,
@@ -2,13 +2,13 @@
2
2
  import {
3
3
  allowedHostsFor,
4
4
  createNodeListener
5
- } from "./chunk-U25NVB6W.js";
5
+ } from "./chunk-CONTLKSZ.js";
6
6
  import {
7
7
  findConfig,
8
8
  loadConfig,
9
9
  loadProjectEnv,
10
10
  requireDatabaseUrl
11
- } from "./chunk-2NH6KYTL.js";
11
+ } from "./chunk-2LG6TWYY.js";
12
12
 
13
13
  // src/commands/studio.ts
14
14
  import { execFile } from "child_process";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usegraft/cli",
3
- "version": "0.2.0",
3
+ "version": "1.0.0-beta.1",
4
4
  "description": "The graft CLI: scaffold, compile authored content, serve the runtime, and split your database credentials.",
5
5
  "keywords": [
6
6
  "agent",
@@ -34,21 +34,22 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@usegraft/assets": "0.2.0",
38
- "@usegraft/auth": "0.2.0",
39
- "@usegraft/compiler": "0.2.0",
40
- "@usegraft/content-migrations": "0.2.0",
41
- "@usegraft/contracts": "0.2.0",
42
- "@usegraft/core": "0.2.0",
43
- "@usegraft/db": "0.2.0",
44
- "@usegraft/mcp": "0.2.0",
45
- "@usegraft/mdx-safety": "0.2.0",
46
- "@usegraft/registry": "0.2.0",
47
- "@usegraft/studio": "0.2.0",
48
- "jiti": "^2.4.0"
37
+ "@usegraft/assets": "1.0.0-beta.1",
38
+ "@usegraft/auth": "1.0.0-beta.1",
39
+ "@usegraft/compiler": "1.0.0-beta.1",
40
+ "@usegraft/content-api": "1.0.0-beta.1",
41
+ "@usegraft/content-migrations": "1.0.0-beta.1",
42
+ "@usegraft/contracts": "1.0.0-beta.1",
43
+ "@usegraft/core": "1.0.0-beta.1",
44
+ "@usegraft/db": "1.0.0-beta.1",
45
+ "@usegraft/mcp": "1.0.0-beta.1",
46
+ "@usegraft/mdx-safety": "1.0.0-beta.1",
47
+ "@usegraft/registry": "1.0.0-beta.1",
48
+ "@usegraft/studio": "1.0.0-beta.1",
49
+ "jiti": "^2.7.0"
49
50
  },
50
51
  "devDependencies": {
51
- "@usegraft/sdk-core": "0.2.0"
52
+ "@usegraft/sdk-core": "1.0.0-beta.1"
52
53
  },
53
54
  "engines": {
54
55
  "node": ">=22.16"