agentful 0.2.0 → 0.2.2

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 (2) hide show
  1. package/dist/index.cjs +197 -108
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3044,7 +3044,7 @@ var VERSION, brand, useColor, wrap, paint, sym, ui;
3044
3044
  var init_branding = __esm({
3045
3045
  "src/branding.ts"() {
3046
3046
  "use strict";
3047
- VERSION = "0.2.0" ? "0.2.0" : null.version;
3047
+ VERSION = "0.2.2" ? "0.2.2" : null.version;
3048
3048
  brand = {
3049
3049
  name: "agentful",
3050
3050
  // the command users type
@@ -4395,7 +4395,8 @@ function materializeStarter(name, targetDir, projectName) {
4395
4395
  throw new ApiError(
4396
4396
  0,
4397
4397
  "directory_not_empty",
4398
- `This directory is not empty (found ${blockers.slice(0, 3).join(", ")}${blockers.length > 3 ? ", \u2026" : ""}). \`init --template\` scaffolds into an empty directory only \u2014 it never overwrites your files.`
4398
+ `This directory is not empty (found ${blockers.slice(0, 3).join(", ")}${blockers.length > 3 ? ", \u2026" : ""}). \`init --template\` scaffolds into an empty directory only \u2014 it never overwrites your files.
4399
+ Pass a new folder name instead: \`agentful init --template ${name} my-app\` (creates it for you).`
4399
4400
  );
4400
4401
  }
4401
4402
  let written = 0;
@@ -4419,16 +4420,15 @@ function materializeStarter(name, targetDir, projectName) {
4419
4420
  function slugify(value) {
4420
4421
  return value.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
4421
4422
  }
4423
+ function prepareScaffoldDirectory(baseDir, directory) {
4424
+ const target = (0, import_node_path4.resolve)(baseDir, directory);
4425
+ (0, import_node_fs3.mkdirSync)(target, { recursive: true });
4426
+ return target;
4427
+ }
4422
4428
 
4423
4429
  // src/commands/init.ts
4424
- async function initCommand(opts) {
4430
+ async function initCommand(opts, directory) {
4425
4431
  console.log(banner());
4426
- const existing = readProjectConfig();
4427
- if (existing) {
4428
- ui.info(`Already initialized (project ${existing.projectId} \u2014 "${existing.title}").`);
4429
- ui.info("Delete .agentful/project.json to re-initialize.");
4430
- return;
4431
- }
4432
4432
  if (opts.template && opts.link) {
4433
4433
  throw new ApiError(
4434
4434
  0,
@@ -4436,6 +4436,22 @@ async function initCommand(opts) {
4436
4436
  "--template scaffolds a new project and cannot be combined with --link."
4437
4437
  );
4438
4438
  }
4439
+ if (directory && !opts.template) {
4440
+ throw new ApiError(
4441
+ 0,
4442
+ "invalid_options",
4443
+ "A target directory only makes sense with --template \u2014 plain `init` links the directory you are in."
4444
+ );
4445
+ }
4446
+ if (opts.template && directory) {
4447
+ process.chdir(prepareScaffoldDirectory(process.cwd(), directory));
4448
+ }
4449
+ const existing = readProjectConfig();
4450
+ if (existing) {
4451
+ ui.info(`Already initialized (project ${existing.projectId} \u2014 "${existing.title}").`);
4452
+ ui.info("Delete .agentful/project.json to re-initialize.");
4453
+ return;
4454
+ }
4439
4455
  if (opts.template) {
4440
4456
  const title2 = opts.title?.trim() || (0, import_node_path5.basename)(process.cwd());
4441
4457
  ui.step(`Scaffolding from the ${opts.template} starter\u2026`);
@@ -4465,6 +4481,7 @@ async function initCommand(opts) {
4465
4481
  const config = await createProject(auth, title);
4466
4482
  ui.ok(`Project created (${config.projectId}). Next: \`agentful push\``);
4467
4483
  if (opts.template) {
4484
+ if (directory) ui.info(`Next: \`cd ${directory}\` \u2014 the project lives there now.`);
4468
4485
  ui.info("Run `npm install` before local dev; `agentful push` builds in the cloud either way.");
4469
4486
  ui.info(`Or start the coding agent right away: \`agentful\` (templates: ${STARTER_NAMES.join(", ")}).`);
4470
4487
  }
@@ -5404,9 +5421,9 @@ var contract_default = {
5404
5421
  details: "raw build output excerpt"
5405
5422
  },
5406
5423
  adopted_by: {
5407
- build_engine: ["status", "timestamp", "error", "details", "framework"],
5408
- executor: ["status", "timestamp", "phase", "error"],
5409
- $comment: "Current state, not the target: the engine writes no phase codes and neither builder writes build_id/started_at/status:running yet. Full adoption is AP4 (engine) + AP5 (executor); until then the CLI must not poll against build identity \u2014 it uses the timestamp freshness guard instead."
5424
+ build_engine: ["schema_version", "build_id", "started_at", "status", "timestamp", "phase", "error", "details", "framework"],
5425
+ executor: ["schema_version", "build_id", "started_at", "status", "timestamp", "phase", "error"],
5426
+ $comment: "Both builders write the attempt identity since AP4/AP5 (2026-08-18); failures carry phase codes and \u2014 for contract violations \u2014 code/reason/hints. The engine additionally writes a running record at start; the executor has no separate build-start moment (started_at is the turn start) and writes no running record. The CLI may graduate from timestamp freshness to identity-based polling for the ENGINE path."
5410
5427
  }
5411
5428
  }
5412
5429
  };
@@ -5590,6 +5607,38 @@ function runPreflight(rootDir) {
5590
5607
  return { framework, issues, warnings };
5591
5608
  }
5592
5609
 
5610
+ // src/lib/backendDecl.ts
5611
+ var import_node_fs6 = require("fs");
5612
+ var import_node_path8 = require("path");
5613
+ function readBackendDeclaration(dir = process.cwd()) {
5614
+ const path = (0, import_node_path8.join)(dir, ".agentful", "backend.json");
5615
+ if (!(0, import_node_fs6.existsSync)(path)) return null;
5616
+ try {
5617
+ const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path, "utf8"));
5618
+ if (!parsed || typeof parsed !== "object") return null;
5619
+ const decl = {
5620
+ schema_version: Number(parsed.schema_version) || 1,
5621
+ managed_db: Boolean(parsed.managed_db),
5622
+ actions: Array.isArray(parsed.actions) ? parsed.actions.filter((a) => typeof a === "string") : [],
5623
+ contract_doc: typeof parsed.contract_doc === "string" ? parsed.contract_doc : void 0
5624
+ };
5625
+ if (!decl.managed_db && decl.actions.length === 0) return null;
5626
+ return decl;
5627
+ } catch {
5628
+ return null;
5629
+ }
5630
+ }
5631
+ function describeBackendDeclaration(decl) {
5632
+ const parts = [];
5633
+ if (decl.managed_db) parts.push("managed DB");
5634
+ if (decl.actions && decl.actions.length > 0) parts.push(`${decl.actions.length} server action(s)`);
5635
+ const base = parts.join(" + ") || "managed backend";
5636
+ return decl.contract_doc ? `${base} (contract: ${decl.contract_doc})` : base;
5637
+ }
5638
+ function backendTabUrl(userId, projectId) {
5639
+ return `${APP_URL}/workspace/${userId}/${projectId}?view=backend`;
5640
+ }
5641
+
5593
5642
  // src/lib/buildStatus.ts
5594
5643
  init_branding();
5595
5644
  var DIAGNOSIS_MAX_AGE_MS = 15 * 60 * 1e3;
@@ -5715,6 +5764,10 @@ async function pushCommand(opts) {
5715
5764
  console.log("");
5716
5765
  ui.ok(`Live preview: ${ui.url(url)}`);
5717
5766
  ui.info(`Manage in the browser: ${ui.url(`https://app.agentful.dev/workspace/${userId}/${projectId}`)}`);
5767
+ const backend = readBackendDeclaration();
5768
+ if (backend) {
5769
+ ui.info(`Declared managed backend: ${describeBackendDeclaration(backend)} \u2014 enable/manage it in the Backend tab: \`agentful backend\` takes you there.`);
5770
+ }
5718
5771
  }
5719
5772
  async function pollBuild(token, userId, projectId, startedAtMs) {
5720
5773
  const deadline = Date.now() + 10 * 60 * 1e3;
@@ -5800,6 +5853,24 @@ async function buildStatusCommand() {
5800
5853
  }
5801
5854
  }
5802
5855
 
5856
+ // src/commands/backend.ts
5857
+ init_branding();
5858
+ async function backendCommand() {
5859
+ console.log(banner());
5860
+ const project = requireProject();
5861
+ const decl = readBackendDeclaration();
5862
+ if (decl) {
5863
+ ui.info(`This project declares: ${describeBackendDeclaration(decl)}.`);
5864
+ ui.info("Declared in code is not enabled on the platform \u2014 that happens in the Backend tab.");
5865
+ }
5866
+ const url = backendTabUrl(project.userId, project.projectId);
5867
+ ui.step(`Opening the Backend tab: ${ui.url(url)}`);
5868
+ if (!openInBrowser(url)) {
5869
+ ui.info("Could not launch a browser \u2014 open this URL manually:");
5870
+ ui.info(ui.url(url));
5871
+ }
5872
+ }
5873
+
5803
5874
  // src/commands/open.ts
5804
5875
  init_branding();
5805
5876
  async function openCommand() {
@@ -5816,15 +5887,15 @@ async function openCommand() {
5816
5887
 
5817
5888
  // src/commands/tui.ts
5818
5889
  var import_node_child_process5 = require("child_process");
5819
- var import_node_path16 = require("path");
5890
+ var import_node_path17 = require("path");
5820
5891
 
5821
5892
  // src/lib/binWrapper.ts
5822
- var import_node_fs6 = require("fs");
5823
- var import_node_path8 = require("path");
5893
+ var import_node_fs7 = require("fs");
5894
+ var import_node_path9 = require("path");
5824
5895
  var import_node_os3 = require("os");
5825
5896
  function ensureCliOnPath() {
5826
- const binDir = (0, import_node_path8.join)((0, import_node_os3.homedir)(), ".local", "share", "agentful", "bin");
5827
- (0, import_node_fs6.mkdirSync)(binDir, { recursive: true });
5897
+ const binDir = (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".local", "share", "agentful", "bin");
5898
+ (0, import_node_fs7.mkdirSync)(binDir, { recursive: true });
5828
5899
  const entry = process.argv[1];
5829
5900
  if (!entry) return binDir;
5830
5901
  const restore = (name) => {
@@ -5833,8 +5904,8 @@ function ensureCliOnPath() {
5833
5904
  ` : `export ${name}=${JSON.stringify(original)}
5834
5905
  `;
5835
5906
  };
5836
- const wrapper = (0, import_node_path8.join)(binDir, "agentful");
5837
- (0, import_node_fs6.writeFileSync)(
5907
+ const wrapper = (0, import_node_path9.join)(binDir, "agentful");
5908
+ (0, import_node_fs7.writeFileSync)(
5838
5909
  wrapper,
5839
5910
  `#!/bin/sh
5840
5911
  # Generated by the Agentful CLI so the coding agent can run \`agentful \u2026\`.
@@ -5844,7 +5915,7 @@ function ensureCliOnPath() {
5844
5915
  "utf8"
5845
5916
  );
5846
5917
  try {
5847
- (0, import_node_fs6.chmodSync)(wrapper, 493);
5918
+ (0, import_node_fs7.chmodSync)(wrapper, 493);
5848
5919
  } catch {
5849
5920
  }
5850
5921
  return binDir;
@@ -5854,9 +5925,9 @@ function ensureCliOnPath() {
5854
5925
  init_branding();
5855
5926
 
5856
5927
  // src/lib/engine.ts
5857
- var import_node_fs7 = require("fs");
5928
+ var import_node_fs8 = require("fs");
5858
5929
  var import_node_crypto = require("crypto");
5859
- var import_node_path9 = require("path");
5930
+ var import_node_path10 = require("path");
5860
5931
  var import_node_os4 = require("os");
5861
5932
  var import_node_child_process3 = require("child_process");
5862
5933
  var import_node_stream = require("stream");
@@ -5897,8 +5968,8 @@ function platformKey() {
5897
5968
  );
5898
5969
  }
5899
5970
  function cacheDir(version) {
5900
- const base = process.env.XDG_CACHE_HOME || (0, import_node_path9.join)((0, import_node_os4.homedir)(), ".cache");
5901
- return (0, import_node_path9.join)(base, "agentful", "engine", version);
5971
+ const base = process.env.XDG_CACHE_HOME || (0, import_node_path10.join)((0, import_node_os4.homedir)(), ".cache");
5972
+ return (0, import_node_path10.join)(base, "agentful", "engine", version);
5902
5973
  }
5903
5974
  async function fetchManifest() {
5904
5975
  try {
@@ -5909,7 +5980,7 @@ async function fetchManifest() {
5909
5980
  }
5910
5981
  async function sha256File(path) {
5911
5982
  const hash = (0, import_node_crypto.createHash)("sha256");
5912
- await (0, import_promises2.pipeline)((0, import_node_fs7.createReadStream)(path), hash);
5983
+ await (0, import_promises2.pipeline)((0, import_node_fs8.createReadStream)(path), hash);
5913
5984
  return hash.digest("hex");
5914
5985
  }
5915
5986
  async function ensureEngine() {
@@ -5918,12 +5989,12 @@ async function ensureEngine() {
5918
5989
  noticeIfOutdated(manifest);
5919
5990
  const version = manifest?.engine_version || FALLBACK_ENGINE_VERSION;
5920
5991
  const dir = cacheDir(version);
5921
- const binPath = (0, import_node_path9.join)(dir, ENGINE_BINARY);
5922
- if ((0, import_node_fs7.existsSync)(binPath)) return binPath;
5992
+ const binPath = (0, import_node_path10.join)(dir, ENGINE_BINARY);
5993
+ if ((0, import_node_fs8.existsSync)(binPath)) return binPath;
5923
5994
  const artifact = manifest?.artifacts?.[key];
5924
5995
  const url = artifact?.url || `${APP_URL}/cli/engine/${version}/${key}${key.startsWith("darwin") ? ".zip" : ".tar.gz"}`;
5925
- (0, import_node_fs7.mkdirSync)(dir, { recursive: true });
5926
- const archivePath = (0, import_node_path9.join)(dir, key.startsWith("darwin") ? "engine.zip" : "engine.tar.gz");
5996
+ (0, import_node_fs8.mkdirSync)(dir, { recursive: true });
5997
+ const archivePath = (0, import_node_path10.join)(dir, key.startsWith("darwin") ? "engine.zip" : "engine.tar.gz");
5927
5998
  ui.step(`Preparing the ${brand.displayName} engine (first run only)\u2026`);
5928
5999
  const resp = await fetch(url, {
5929
6000
  redirect: "follow",
@@ -5937,11 +6008,11 @@ async function ensureEngine() {
5937
6008
  `Could not download the engine (HTTP ${resp.status}). Check your connection and try again.`
5938
6009
  );
5939
6010
  }
5940
- await (0, import_promises2.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0, import_node_fs7.createWriteStream)(archivePath));
6011
+ await (0, import_promises2.pipeline)(import_node_stream.Readable.fromWeb(resp.body), (0, import_node_fs8.createWriteStream)(archivePath));
5941
6012
  if (artifact?.sha256) {
5942
6013
  const actual = await sha256File(archivePath);
5943
6014
  if (actual !== artifact.sha256) {
5944
- (0, import_node_fs7.rmSync)(archivePath, { force: true });
6015
+ (0, import_node_fs8.rmSync)(archivePath, { force: true });
5945
6016
  throw new ApiError(
5946
6017
  0,
5947
6018
  "engine_checksum_mismatch",
@@ -5950,7 +6021,7 @@ async function ensureEngine() {
5950
6021
  }
5951
6022
  }
5952
6023
  const extract = archivePath.endsWith(".zip") ? (0, import_node_child_process3.spawnSync)("unzip", ["-oq", archivePath], { cwd: dir, stdio: "ignore" }) : (0, import_node_child_process3.spawnSync)("tar", ["-xzf", archivePath], { cwd: dir, stdio: "ignore" });
5953
- (0, import_node_fs7.rmSync)(archivePath, { force: true });
6024
+ (0, import_node_fs8.rmSync)(archivePath, { force: true });
5954
6025
  if (extract.status !== 0) {
5955
6026
  throw new ApiError(
5956
6027
  0,
@@ -5958,24 +6029,24 @@ async function ensureEngine() {
5958
6029
  `Could not unpack the engine (needs ${archivePath.endsWith(".zip") ? "unzip" : "tar"} on PATH).`
5959
6030
  );
5960
6031
  }
5961
- const extracted = (0, import_node_path9.join)(dir, "opencode");
5962
- if ((0, import_node_fs7.existsSync)(extracted) && !(0, import_node_fs7.existsSync)(binPath)) (0, import_node_fs7.renameSync)(extracted, binPath);
5963
- if (!(0, import_node_fs7.existsSync)(binPath)) {
6032
+ const extracted = (0, import_node_path10.join)(dir, "opencode");
6033
+ if ((0, import_node_fs8.existsSync)(extracted) && !(0, import_node_fs8.existsSync)(binPath)) (0, import_node_fs8.renameSync)(extracted, binPath);
6034
+ if (!(0, import_node_fs8.existsSync)(binPath)) {
5964
6035
  throw new ApiError(0, "engine_extract_failed", "Engine archive did not contain the expected binary.");
5965
6036
  }
5966
- (0, import_node_fs7.chmodSync)(binPath, 493);
6037
+ (0, import_node_fs8.chmodSync)(binPath, 493);
5967
6038
  ui.ok(`Engine ready.`);
5968
6039
  console.log(paint.dim(" powered by opencode (MIT) \xB7 run `agentful licenses` for details"));
5969
6040
  return binPath;
5970
6041
  }
5971
6042
 
5972
6043
  // src/lib/gatewayToken.ts
5973
- var import_node_fs8 = require("fs");
5974
- var import_node_path10 = require("path");
5975
- var cachePath = () => (0, import_node_path10.join)(configDir(), "gateway.json");
6044
+ var import_node_fs9 = require("fs");
6045
+ var import_node_path11 = require("path");
6046
+ var cachePath = () => (0, import_node_path11.join)(configDir(), "gateway.json");
5976
6047
  async function ensureGatewaySession(auth, projectId) {
5977
6048
  try {
5978
- const cached = JSON.parse((0, import_node_fs8.readFileSync)(cachePath(), "utf8"));
6049
+ const cached = JSON.parse((0, import_node_fs9.readFileSync)(cachePath(), "utf8"));
5979
6050
  if (cached?.token && cached.expires_at - Date.now() / 1e3 > 3600) {
5980
6051
  return cached;
5981
6052
  }
@@ -5986,18 +6057,18 @@ async function ensureGatewaySession(auth, projectId) {
5986
6057
  body: projectId ? { project_id: projectId } : {},
5987
6058
  timeoutMs: 3e4
5988
6059
  });
5989
- (0, import_node_fs8.mkdirSync)(configDir(), { recursive: true });
5990
- (0, import_node_fs8.writeFileSync)(cachePath(), JSON.stringify(res, null, 2) + "\n", "utf8");
6060
+ (0, import_node_fs9.mkdirSync)(configDir(), { recursive: true });
6061
+ (0, import_node_fs9.writeFileSync)(cachePath(), JSON.stringify(res, null, 2) + "\n", "utf8");
5991
6062
  try {
5992
- (0, import_node_fs8.chmodSync)(cachePath(), 384);
6063
+ (0, import_node_fs9.chmodSync)(cachePath(), 384);
5993
6064
  } catch {
5994
6065
  }
5995
6066
  return res;
5996
6067
  }
5997
6068
 
5998
6069
  // src/lib/engineConfig.ts
5999
- var import_node_fs10 = require("fs");
6000
- var import_node_path12 = require("path");
6070
+ var import_node_fs11 = require("fs");
6071
+ var import_node_path13 = require("path");
6001
6072
  var import_node_os5 = require("os");
6002
6073
 
6003
6074
  // src/branding/agentful-theme.json
@@ -6106,6 +6177,10 @@ var SLASH_COMMANDS = {
6106
6177
  description: `${brand.displayName}: show the last cloud build result`,
6107
6178
  template: "Run `agentful build-status` from the project root using the bash tool and report what it prints: status, phase, framework, when it was recorded, and the error/details verbatim if it failed. That record is the only source of truth about the cloud build \u2014 do not speculate beyond it. " + NEEDS_A_PUSH
6108
6179
  },
6180
+ backend: {
6181
+ description: `${brand.displayName}: open the Backend tab (managed DB + server actions)`,
6182
+ template: "Run `agentful backend` from the project root using the bash tool and report the URL it opened. If it prints a declared backend (managed DB / server actions), summarize that and remind me that declared-in-code serves nothing until enabled in that tab. " + NEEDS_A_PUSH
6183
+ },
6109
6184
  preview: {
6110
6185
  description: `${brand.displayName}: open the live preview in the browser`,
6111
6186
  template: "Run `agentful open` from the project root using the bash tool and report the URL it opened. Note that `open` neither builds nor uploads. " + NEEDS_A_PUSH
@@ -6136,22 +6211,22 @@ ${cmd.template}
6136
6211
  }
6137
6212
 
6138
6213
  // src/lib/tools.ts
6139
- var import_node_fs9 = require("fs");
6140
- var import_node_path11 = require("path");
6214
+ var import_node_fs10 = require("fs");
6215
+ var import_node_path12 = require("path");
6141
6216
  function assetsDir() {
6142
- const here = (0, import_node_path11.dirname)(process.argv[1] || "");
6143
- for (const candidate of [(0, import_node_path11.join)(here, "..", "assets"), (0, import_node_path11.join)(here, "assets")]) {
6144
- if ((0, import_node_fs9.existsSync)((0, import_node_path11.join)(candidate, "tools", "imagegen.ts"))) return candidate;
6217
+ const here = (0, import_node_path12.dirname)(process.argv[1] || "");
6218
+ for (const candidate of [(0, import_node_path12.join)(here, "..", "assets"), (0, import_node_path12.join)(here, "assets")]) {
6219
+ if ((0, import_node_fs10.existsSync)((0, import_node_path12.join)(candidate, "tools", "imagegen.ts"))) return candidate;
6145
6220
  }
6146
6221
  return null;
6147
6222
  }
6148
6223
  function installEngineTools(configDir2) {
6149
6224
  const assets = assetsDir();
6150
6225
  if (!assets) return false;
6151
- const target = (0, import_node_path11.join)(configDir2, "tools");
6152
- (0, import_node_fs9.mkdirSync)(target, { recursive: true });
6226
+ const target = (0, import_node_path12.join)(configDir2, "tools");
6227
+ (0, import_node_fs10.mkdirSync)(target, { recursive: true });
6153
6228
  try {
6154
- (0, import_node_fs9.cpSync)((0, import_node_path11.join)(assets, "tools"), target, { recursive: true });
6229
+ (0, import_node_fs10.cpSync)((0, import_node_path12.join)(assets, "tools"), target, { recursive: true });
6155
6230
  return true;
6156
6231
  } catch {
6157
6232
  return false;
@@ -6229,6 +6304,19 @@ statically evaluable; prefer client-side fetch or Managed Server Actions.
6229
6304
  (b) an external backend called from the client. Never a bundled server
6230
6305
  process, and never a script the user must run locally to produce the result.
6231
6306
 
6307
+ When you design a managed backend (DB schema, server actions), also write the
6308
+ machine-readable declaration \`.agentful/backend.json\`:
6309
+ \`{"schema_version": 1, "managed_db": true, "actions": ["<action-name>", \u2026], "contract_doc": "docs/backend-contract.md"}\`
6310
+ \u2014 \`agentful push\` and \`agentful backend\` use it to tell the user honestly
6311
+ what is declared in code but not yet enabled on the platform.
6312
+
6313
+ When you point the user at the Backend tab, NEVER say "the Backend tab"
6314
+ without an address. Read \`.agentful/project.json\` and give the full URL
6315
+ \`https://app.agentful.dev/workspace/<userId>/<projectId>?view=backend\` \u2014 or
6316
+ simply the command \`agentful backend\`, which opens exactly that page.
6317
+ Declaring a backend never enables it; enabling is the user's conscious step
6318
+ in that tab.
6319
+
6232
6320
  ## Diagnosing failed pushes and cloud builds (hard rule)
6233
6321
 
6234
6322
  Diagnose **only from evidence**: the preflight output of \`agentful push\`, and
@@ -6296,11 +6384,11 @@ function buildEngineConfig(opts) {
6296
6384
  };
6297
6385
  }
6298
6386
  function engineXdg() {
6299
- const root = (0, import_node_path12.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful");
6387
+ const root = (0, import_node_path13.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful");
6300
6388
  return {
6301
- configHome: (0, import_node_path12.join)(root, "engine-config"),
6302
- dataHome: (0, import_node_path12.join)(root, "engine-data"),
6303
- stateHome: (0, import_node_path12.join)(root, "engine-state")
6389
+ configHome: (0, import_node_path13.join)(root, "engine-config"),
6390
+ dataHome: (0, import_node_path13.join)(root, "engine-data"),
6391
+ stateHome: (0, import_node_path13.join)(root, "engine-state")
6304
6392
  };
6305
6393
  }
6306
6394
  async function resolveTheme() {
@@ -6319,24 +6407,24 @@ async function resolveTheme() {
6319
6407
  }
6320
6408
  async function writeEngineSession(session, catalog, localProviders = {}, framework = "unknown", skillsInstruction = null) {
6321
6409
  const xdg = engineXdg();
6322
- const configDir2 = (0, import_node_path12.join)(xdg.configHome, "opencode");
6323
- const dataDir = (0, import_node_path12.join)(xdg.dataHome, "opencode");
6324
- const themesDir = (0, import_node_path12.join)(configDir2, "themes");
6325
- const pluginsDir = (0, import_node_path12.join)(configDir2, "plugins");
6326
- const commandsDir = (0, import_node_path12.join)(configDir2, "commands");
6327
- for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0, import_node_path12.join)(xdg.stateHome, "opencode")]) {
6328
- (0, import_node_fs10.mkdirSync)(dir, { recursive: true });
6410
+ const configDir2 = (0, import_node_path13.join)(xdg.configHome, "opencode");
6411
+ const dataDir = (0, import_node_path13.join)(xdg.dataHome, "opencode");
6412
+ const themesDir = (0, import_node_path13.join)(configDir2, "themes");
6413
+ const pluginsDir = (0, import_node_path13.join)(configDir2, "plugins");
6414
+ const commandsDir = (0, import_node_path13.join)(configDir2, "commands");
6415
+ for (const dir of [themesDir, pluginsDir, commandsDir, dataDir, (0, import_node_path13.join)(xdg.stateHome, "opencode")]) {
6416
+ (0, import_node_fs11.mkdirSync)(dir, { recursive: true });
6329
6417
  }
6330
6418
  const imagegenAvailable = installEngineTools(configDir2) && session.img_on !== false;
6331
- const cloudInstructionsPath = (0, import_node_path12.join)(configDir2, CLOUD_INSTRUCTIONS_FILENAME);
6332
- (0, import_node_fs10.writeFileSync)(cloudInstructionsPath, renderCloudInstructions(framework));
6333
- const skillsPath = (0, import_node_path12.join)(configDir2, "AGENTFUL_SKILLS.md");
6419
+ const cloudInstructionsPath = (0, import_node_path13.join)(configDir2, CLOUD_INSTRUCTIONS_FILENAME);
6420
+ (0, import_node_fs11.writeFileSync)(cloudInstructionsPath, renderCloudInstructions(framework));
6421
+ const skillsPath = (0, import_node_path13.join)(configDir2, "AGENTFUL_SKILLS.md");
6334
6422
  const instructionPaths = [cloudInstructionsPath];
6335
6423
  if (skillsInstruction) {
6336
- (0, import_node_fs10.writeFileSync)(skillsPath, skillsInstruction);
6424
+ (0, import_node_fs11.writeFileSync)(skillsPath, skillsInstruction);
6337
6425
  instructionPaths.push(skillsPath);
6338
6426
  } else {
6339
- (0, import_node_fs10.rmSync)(skillsPath, { force: true });
6427
+ (0, import_node_fs11.rmSync)(skillsPath, { force: true });
6340
6428
  }
6341
6429
  const config = buildEngineConfig({
6342
6430
  session,
@@ -6345,24 +6433,24 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
6345
6433
  imagegenAvailable,
6346
6434
  instructionPaths
6347
6435
  });
6348
- (0, import_node_fs10.writeFileSync)((0, import_node_path12.join)(configDir2, "config.json"), JSON.stringify(config, null, 2));
6349
- (0, import_node_fs10.writeFileSync)((0, import_node_path12.join)(dataDir, "auth.json"), JSON.stringify({
6436
+ (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(configDir2, "config.json"), JSON.stringify(config, null, 2));
6437
+ (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(dataDir, "auth.json"), JSON.stringify({
6350
6438
  [PROVIDER_ID]: { apiKey: session.token }
6351
6439
  }, null, 2));
6352
6440
  const { brandingPluginSource: brandingPluginSource2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
6353
- const pluginPath = (0, import_node_path12.join)(pluginsDir, "agentful-branding.tsx");
6354
- (0, import_node_fs10.writeFileSync)(pluginPath, brandingPluginSource2());
6355
- (0, import_node_fs10.writeFileSync)((0, import_node_path12.join)(configDir2, "tui.json"), JSON.stringify({
6441
+ const pluginPath = (0, import_node_path13.join)(pluginsDir, "agentful-branding.tsx");
6442
+ (0, import_node_fs11.writeFileSync)(pluginPath, brandingPluginSource2());
6443
+ (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(configDir2, "tui.json"), JSON.stringify({
6356
6444
  theme: "agentful",
6357
6445
  plugin: [`file://${pluginPath}`]
6358
6446
  }, null, 2));
6359
- (0, import_node_fs10.writeFileSync)((0, import_node_path12.join)(themesDir, "agentful.json"), JSON.stringify(await resolveTheme(), null, 2));
6447
+ (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(themesDir, "agentful.json"), JSON.stringify(await resolveTheme(), null, 2));
6360
6448
  for (const name of Object.keys(SLASH_COMMANDS)) {
6361
- (0, import_node_fs10.writeFileSync)((0, import_node_path12.join)(commandsDir, `${name}.md`), commandMarkdown(name));
6449
+ (0, import_node_fs11.writeFileSync)((0, import_node_path13.join)(commandsDir, `${name}.md`), commandMarkdown(name));
6362
6450
  }
6363
6451
  for (const legacy of ["xdg-config", "xdg-data", "xdg-state"]) {
6364
6452
  try {
6365
- (0, import_node_fs10.rmSync)((0, import_node_path12.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful", legacy), { recursive: true, force: true });
6453
+ (0, import_node_fs11.rmSync)((0, import_node_path13.join)((0, import_node_os5.homedir)(), ".local", "share", "agentful", legacy), { recursive: true, force: true });
6366
6454
  } catch {
6367
6455
  }
6368
6456
  }
@@ -6370,8 +6458,8 @@ async function writeEngineSession(session, catalog, localProviders = {}, framewo
6370
6458
  }
6371
6459
 
6372
6460
  // src/lib/skills.ts
6373
- var import_node_fs11 = require("fs");
6374
- var import_node_path13 = require("path");
6461
+ var import_node_fs12 = require("fs");
6462
+ var import_node_path14 = require("path");
6375
6463
 
6376
6464
  // src/generated/skills.json
6377
6465
  var skills_default = {
@@ -6398,7 +6486,7 @@ var SCAFFOLD_BY_FRAMEWORK = {
6398
6486
  };
6399
6487
  function hasVueDependency(rootDir) {
6400
6488
  try {
6401
- const pkg = JSON.parse((0, import_node_fs11.readFileSync)((0, import_node_path13.join)(rootDir, "package.json"), "utf8"));
6489
+ const pkg = JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path14.join)(rootDir, "package.json"), "utf8"));
6402
6490
  return Boolean({ ...pkg.dependencies, ...pkg.devDependencies }["vue"]);
6403
6491
  } catch {
6404
6492
  return false;
@@ -6426,7 +6514,7 @@ function renderSkillsInstruction(names) {
6426
6514
  return "# Platform deployment skills (mandatory)\n\nThese are the same skills the cloud build agent loads for this stack.\nFollow them when scaffolding or editing \u2014 they are not optional hints.\n\n" + sections.join("\n\n---\n\n") + "\n";
6427
6515
  }
6428
6516
  function skillsInstructionFor(framework, rootDir) {
6429
- if (!(0, import_node_fs11.existsSync)(rootDir)) return null;
6517
+ if (!(0, import_node_fs12.existsSync)(rootDir)) return null;
6430
6518
  return renderSkillsInstruction(selectSkills(framework, rootDir));
6431
6519
  }
6432
6520
 
@@ -6541,15 +6629,15 @@ function decideLocalProviders(policy) {
6541
6629
  }
6542
6630
 
6543
6631
  // src/lib/localProviders.ts
6544
- var import_node_fs12 = require("fs");
6545
- var import_node_path14 = require("path");
6632
+ var import_node_fs13 = require("fs");
6633
+ var import_node_path15 = require("path");
6546
6634
  var LOCAL_PROVIDERS_FILE = "local-providers.json";
6547
6635
  function localProvidersPath() {
6548
- return (0, import_node_path14.join)(configDir(), LOCAL_PROVIDERS_FILE);
6636
+ return (0, import_node_path15.join)(configDir(), LOCAL_PROVIDERS_FILE);
6549
6637
  }
6550
6638
  function readLocalProviders() {
6551
6639
  try {
6552
- const raw = JSON.parse((0, import_node_fs12.readFileSync)(localProvidersPath(), "utf8"));
6640
+ const raw = JSON.parse((0, import_node_fs13.readFileSync)(localProvidersPath(), "utf8"));
6553
6641
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
6554
6642
  const map = raw.providers && typeof raw.providers === "object" ? raw.providers : raw;
6555
6643
  const usable = {};
@@ -6584,8 +6672,8 @@ function resolveLocalProviders(policy) {
6584
6672
  }
6585
6673
  function writeLocalProvidersExample() {
6586
6674
  const path = localProvidersPath();
6587
- if ((0, import_node_fs12.existsSync)(path)) return path;
6588
- (0, import_node_fs12.mkdirSync)(configDir(), { recursive: true });
6675
+ if ((0, import_node_fs13.existsSync)(path)) return path;
6676
+ (0, import_node_fs13.mkdirSync)(configDir(), { recursive: true });
6589
6677
  const example = {
6590
6678
  _comment: 'Providers with your own API key, running locally. Requests go directly to the provider \u2014 no Agentful credits, no platform region guarantee. Remove the leading underscore from "_anthropic" and fill in your key to enable it.',
6591
6679
  _anthropic: {
@@ -6595,21 +6683,21 @@ function writeLocalProvidersExample() {
6595
6683
  models: { "claude-sonnet-4-5": { name: "Claude Sonnet 4.5 (my key)" } }
6596
6684
  }
6597
6685
  };
6598
- (0, import_node_fs12.writeFileSync)(path, JSON.stringify(example, null, 2) + "\n", "utf8");
6686
+ (0, import_node_fs13.writeFileSync)(path, JSON.stringify(example, null, 2) + "\n", "utf8");
6599
6687
  try {
6600
- (0, import_node_fs12.chmodSync)(path, 384);
6688
+ (0, import_node_fs13.chmodSync)(path, 384);
6601
6689
  } catch {
6602
6690
  }
6603
6691
  return path;
6604
6692
  }
6605
6693
 
6606
6694
  // src/lib/sessions.ts
6607
- var import_node_fs13 = require("fs");
6608
- var import_node_path15 = require("path");
6695
+ var import_node_fs14 = require("fs");
6696
+ var import_node_path16 = require("path");
6609
6697
  var import_node_child_process4 = require("child_process");
6610
6698
  function lastSessionForDirectory(cwd = process.cwd()) {
6611
- const dbPath = (0, import_node_path15.join)(engineXdg().dataHome, "opencode", "opencode.db");
6612
- if (!(0, import_node_fs13.existsSync)(dbPath)) return null;
6699
+ const dbPath = (0, import_node_path16.join)(engineXdg().dataHome, "opencode", "opencode.db");
6700
+ if (!(0, import_node_fs14.existsSync)(dbPath)) return null;
6613
6701
  const escaped = cwd.replace(/'/g, "''");
6614
6702
  const res = (0, import_node_child_process4.spawnSync)(
6615
6703
  "sqlite3",
@@ -6688,7 +6776,7 @@ async function tuiCommand(opts = {}) {
6688
6776
  }
6689
6777
  console.log("");
6690
6778
  if (process.stdout.isTTY) process.stdout.write(`\x1B]0;${brand.displayName}\x07`);
6691
- const code = await new Promise((resolve) => {
6779
+ const code = await new Promise((resolve2) => {
6692
6780
  const engineArgs = opts.session ? ["--session", opts.session] : [];
6693
6781
  const child = (0, import_node_child_process5.spawn)(binPath, engineArgs, {
6694
6782
  stdio: "inherit",
@@ -6697,7 +6785,7 @@ async function tuiCommand(opts = {}) {
6697
6785
  ...process.env,
6698
6786
  // Slash commands run `agentful …` through the agent's bash tool, which
6699
6787
  // has no access to the user's shell aliases.
6700
- PATH: `${binDir}${import_node_path16.delimiter}${process.env.PATH || ""}`,
6788
+ PATH: `${binDir}${import_node_path17.delimiter}${process.env.PATH || ""}`,
6701
6789
  XDG_CONFIG_HOME: xdg.configHome,
6702
6790
  XDG_DATA_HOME: xdg.dataHome,
6703
6791
  XDG_STATE_HOME: xdg.stateHome,
@@ -6715,10 +6803,10 @@ async function tuiCommand(opts = {}) {
6715
6803
  ...session.image_model ? { IMAGE_MODEL: session.image_model } : {}
6716
6804
  }
6717
6805
  });
6718
- child.on("exit", (c) => resolve(c ?? 0));
6806
+ child.on("exit", (c) => resolve2(c ?? 0));
6719
6807
  child.on("error", (err2) => {
6720
6808
  ui.fail(`Could not start the engine: ${err2.message}`);
6721
- resolve(1);
6809
+ resolve2(1);
6722
6810
  });
6723
6811
  });
6724
6812
  try {
@@ -6802,28 +6890,28 @@ async function shareCommand(opts) {
6802
6890
  }
6803
6891
 
6804
6892
  // src/commands/pull.ts
6805
- var import_node_fs14 = require("fs");
6806
- var import_node_path17 = require("path");
6893
+ var import_node_fs15 = require("fs");
6894
+ var import_node_path18 = require("path");
6807
6895
  init_branding();
6808
6896
  init_branding();
6809
6897
  function isProbablyBase64Binary(path) {
6810
6898
  return /\.(png|jpe?g|gif|webp|ico|woff2?|ttf|otf|eot|pdf|zip|mp[34]|webm|avif)$/i.test(path);
6811
6899
  }
6812
6900
  function writeEntry(root, rel, value) {
6813
- const target = (0, import_node_path17.join)(root, rel);
6814
- (0, import_node_fs14.mkdirSync)((0, import_node_path17.dirname)(target), { recursive: true });
6901
+ const target = (0, import_node_path18.join)(root, rel);
6902
+ (0, import_node_fs15.mkdirSync)((0, import_node_path18.dirname)(target), { recursive: true });
6815
6903
  const content = typeof value === "object" && value !== null && "content" in value ? String(value.content) : String(value ?? "");
6816
6904
  if (isProbablyBase64Binary(rel)) {
6817
- (0, import_node_fs14.writeFileSync)(target, Buffer.from(content, "base64"));
6905
+ (0, import_node_fs15.writeFileSync)(target, Buffer.from(content, "base64"));
6818
6906
  } else {
6819
- (0, import_node_fs14.writeFileSync)(target, content, "utf8");
6907
+ (0, import_node_fs15.writeFileSync)(target, content, "utf8");
6820
6908
  }
6821
6909
  }
6822
6910
  async function pullCommand(opts) {
6823
6911
  console.log(banner());
6824
6912
  const auth = await ensureAuth();
6825
6913
  const project = requireProject();
6826
- const nonHidden = (0, import_node_fs14.readdirSync)(process.cwd()).filter((n) => n !== ".agentful" && !n.startsWith("."));
6914
+ const nonHidden = (0, import_node_fs15.readdirSync)(process.cwd()).filter((n) => n !== ".agentful" && !n.startsWith("."));
6827
6915
  if (nonHidden.length > 0 && !opts.force) {
6828
6916
  throw new ApiError(
6829
6917
  0,
@@ -6847,9 +6935,9 @@ async function pullCommand(opts) {
6847
6935
  ui.warn(`Skipped ${rel} (HTTP ${resp.status})`);
6848
6936
  continue;
6849
6937
  }
6850
- const target = (0, import_node_path17.join)(process.cwd(), rel);
6851
- (0, import_node_fs14.mkdirSync)((0, import_node_path17.dirname)(target), { recursive: true });
6852
- (0, import_node_fs14.writeFileSync)(target, Buffer.from(await resp.arrayBuffer()));
6938
+ const target = (0, import_node_path18.join)(process.cwd(), rel);
6939
+ (0, import_node_fs15.mkdirSync)((0, import_node_path18.dirname)(target), { recursive: true });
6940
+ (0, import_node_fs15.writeFileSync)(target, Buffer.from(await resp.arrayBuffer()));
6853
6941
  written++;
6854
6942
  }
6855
6943
  } else if (data.files) {
@@ -6992,9 +7080,10 @@ program2.name("agentful").description("Agentful in your terminal \u2014 local de
6992
7080
  program2.command("login").description("Sign in to your Agentful account (device flow)").option("--no-browser", "do not open the browser automatically").action(run((opts) => loginCommand({ browser: opts.browser !== false })));
6993
7081
  program2.command("logout").description("Remove the stored credentials").action(run(logoutCommand));
6994
7082
  program2.command("whoami").description("Show the signed-in account").action(run(whoamiCommand));
6995
- program2.command("init").description("Create (or link) the Agentful cloud project for this directory").option("--link <projectId>", "link an existing project instead of creating one").option("--title <title>", "project title (skips the prompt)").option("--template <framework>", "scaffold this empty directory from a platform starter (nextjs, react, vue, vanilla)").action(run((opts) => initCommand(opts)));
7083
+ program2.command("init").description("Create (or link) the Agentful cloud project for this directory").argument("[directory]", "with --template: new folder to scaffold into (created for you)").option("--link <projectId>", "link an existing project instead of creating one").option("--title <title>", "project title (skips the prompt)").option("--template <framework>", "scaffold from a platform starter (nextjs, react, vue, vanilla)").action(run((directory, opts) => initCommand(opts, directory)));
6996
7084
  program2.command("push").description("Upload this project and get a live preview URL").option("--prebuilt", "upload a locally built output instead of building in the cloud").option("--dir <path>", "built-output directory for --prebuilt (default: auto-detect)").option("--force", "push even when the pre-upload compatibility check finds blocking issues").action(run((opts) => pushCommand(opts)));
6997
7085
  program2.command("build-status").description("Show the cloud's record of the last build (the real error on failures)").action(run(buildStatusCommand));
7086
+ program2.command("backend").description("Open this project's Backend tab (managed DB + server actions) in the browser").action(run(backendCommand));
6998
7087
  program2.command("open").description("Open the live preview in the browser").action(run(openCommand));
6999
7088
  program2.command("tui").description("Start the Agentful coding TUI with your Agentful account").option("-s, --session <id>", "resume a previous session").action(run((opts) => tuiCommand(opts)));
7000
7089
  program2.command("publish <subdomain>").description("Publish the built project at https://<subdomain>.agentful.dev").action(run(publishCommand));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentful",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Agentful in your terminal — local development with push-to-cloud previews",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://agentful.dev",