agentful 0.4.14 → 0.4.15

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 (3) hide show
  1. package/README.md +6 -2
  2. package/dist/index.cjs +263 -77
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -26,7 +26,7 @@ agentful open # open the preview
26
26
  | `login` / `logout` / `whoami` | Browser-based sign-in; the token is kept in the macOS Keychain (on Linux: a `0600` file under `~/.config/agentful/`) |
27
27
  | `init [--link <id>] [--title <t>]` | Create a cloud project or link an existing one (`.agentful/project.json`) |
28
28
  | `push [--prebuilt [--dir <path>]]` | Upload the source and build in the cloud; `--prebuilt` uploads a local build |
29
- | `push --dry-run [--json]` | Analyse what a push would upload and what would block it — uploads nothing |
29
+ | `push --dry-run [--json [--summary]]` | Analyse what a push would upload and what would block it — uploads nothing |
30
30
  | `open` | Open the live preview |
31
31
  | `share [--private]` | Make the preview public, or private again |
32
32
  | `publish <subdomain>` | Publish at `https://<subdomain>.agentful.page` |
@@ -39,6 +39,9 @@ agentful open # open the preview
39
39
 
40
40
  Inside the interface, `/` lists the Agentful commands (`/setup`, `/push`,
41
41
  `/preview`, `/publish`, `/share`, `/status`) alongside the built-in ones.
42
+ `/push` checks before it uploads: the agent reads the same analysis as
43
+ `push --dry-run`, explains what would block the upload or go missing on the
44
+ site, and asks you before it changes a file or pushes.
42
45
 
43
46
  `push` respects your `.gitignore` and always excludes `.git`, `node_modules`,
44
47
  build output and every `.env` file. A push carries at most about 4.4 MiB
@@ -51,7 +54,8 @@ and what is left out, the largest files, identical copies, media embedded in
51
54
  HTML, and every reason the upload would be rejected. It uploads nothing,
52
55
  creates no project and changes no file; it exits with 1 when the push would be
53
56
  blocked. `--dry-run --json` prints the same analysis as one JSON document
54
- (`"schema": "agentful.push-analysis"`) for scripts and agents.
57
+ (`"schema": "agentful.push-analysis"`) for scripts and agents; with `--summary`
58
+ it leaves out the list of every file and keeps totals, limits and findings.
55
59
 
56
60
  ## What you can deploy
57
61
 
package/dist/index.cjs CHANGED
@@ -3044,7 +3044,7 @@ var VERSION, brand, useColor, wrap, paint, tuiDesign, sym, ui;
3044
3044
  var init_branding = __esm({
3045
3045
  "src/branding.ts"() {
3046
3046
  "use strict";
3047
- VERSION = "0.4.14" ? "0.4.14" : null.version;
3047
+ VERSION = "0.4.15" ? "0.4.15" : null.version;
3048
3048
  brand = {
3049
3049
  name: "agentful",
3050
3050
  // the command users type
@@ -6606,6 +6606,7 @@ function failedAnalysis(mode, projectDir, blocker) {
6606
6606
  schema: ANALYSIS_SCHEMA,
6607
6607
  schema_version: ANALYSIS_SCHEMA_VERSION,
6608
6608
  cli_version: VERSION,
6609
+ view: "full",
6609
6610
  mode,
6610
6611
  root: null,
6611
6612
  ok: false,
@@ -6911,6 +6912,7 @@ function analyzePush(options) {
6911
6912
  schema: ANALYSIS_SCHEMA,
6912
6913
  schema_version: ANALYSIS_SCHEMA_VERSION,
6913
6914
  cli_version: VERSION,
6915
+ view: "full",
6914
6916
  mode,
6915
6917
  root: rootDir,
6916
6918
  ok: blockers.length === 0,
@@ -6947,6 +6949,81 @@ function analyzePush(options) {
6947
6949
  fingerprint: `sha256:${fingerprint}`
6948
6950
  };
6949
6951
  }
6952
+ var SUMMARY_LISTS = {
6953
+ largest_files: 8,
6954
+ by_top_level: 8,
6955
+ duplicates: 8,
6956
+ duplicate_paths: 8,
6957
+ embedded_media: 8,
6958
+ entry_candidates: 20,
6959
+ excluded_entries: 30,
6960
+ dropped_by_platform: 20,
6961
+ finding_paths: 8
6962
+ };
6963
+ var SUMMARY_MAX_BYTES = 24 * 1024;
6964
+ var printAnalysis = (analysis) => `${JSON.stringify(analysis, null, 2)}
6965
+ `;
6966
+ function summarize(analysis, divisor) {
6967
+ const shortened = {};
6968
+ const cut = (name, list, cap, alreadyOmitted = 0) => {
6969
+ const keep = Math.max(1, Math.floor(cap / divisor));
6970
+ const omitted = Math.max(0, list.length - keep) + alreadyOmitted;
6971
+ if (omitted > 0) shortened[name] = (shortened[name] ?? 0) + omitted;
6972
+ return list.slice(0, keep);
6973
+ };
6974
+ const finding = (kind) => (item) => item.paths ? { ...item, paths: cut(`${kind}.paths`, item.paths, SUMMARY_LISTS.finding_paths) } : item;
6975
+ if (analysis.totals.files > 0) shortened.files = analysis.totals.files;
6976
+ const excludedEntries = cut(
6977
+ "excluded.entries",
6978
+ analysis.excluded.entries,
6979
+ SUMMARY_LISTS.excluded_entries,
6980
+ analysis.excluded.omitted
6981
+ );
6982
+ return {
6983
+ schema: analysis.schema,
6984
+ schema_version: analysis.schema_version,
6985
+ cli_version: analysis.cli_version,
6986
+ view: "summary",
6987
+ shortened,
6988
+ mode: analysis.mode,
6989
+ root: analysis.root,
6990
+ ok: analysis.ok,
6991
+ framework: analysis.framework,
6992
+ project: analysis.project,
6993
+ limits: analysis.limits,
6994
+ totals: analysis.totals,
6995
+ blockers: analysis.blockers.map(finding("blockers")),
6996
+ // The note about the cap of the full file list describes a list that is not here.
6997
+ warnings: analysis.warnings.filter((warning) => warning.code !== "file_list_truncated").map(finding("warnings")),
6998
+ suggestions: analysis.suggestions.map(finding("suggestions")),
6999
+ entry: {
7000
+ ...analysis.entry,
7001
+ candidates: cut("entry.candidates", analysis.entry.candidates, SUMMARY_LISTS.entry_candidates)
7002
+ },
7003
+ flattened_prefix: analysis.flattened_prefix,
7004
+ largest_files: cut("largest_files", analysis.largest_files, SUMMARY_LISTS.largest_files),
7005
+ by_class: analysis.by_class,
7006
+ by_top_level: cut("by_top_level", analysis.by_top_level, SUMMARY_LISTS.by_top_level),
7007
+ duplicates: cut("duplicates", analysis.duplicates, SUMMARY_LISTS.duplicates).map((group) => ({ ...group, paths: cut("duplicates.paths", group.paths, SUMMARY_LISTS.duplicate_paths) })),
7008
+ embedded_media: cut("embedded_media", analysis.embedded_media, SUMMARY_LISTS.embedded_media),
7009
+ excluded: {
7010
+ ...analysis.excluded,
7011
+ entries: excludedEntries,
7012
+ omitted: analysis.excluded.omitted + analysis.excluded.entries.length - excludedEntries.length
7013
+ },
7014
+ dropped_by_platform: cut("dropped_by_platform", analysis.dropped_by_platform, SUMMARY_LISTS.dropped_by_platform),
7015
+ files: [],
7016
+ files_omitted: analysis.totals.files,
7017
+ fingerprint: analysis.fingerprint
7018
+ };
7019
+ }
7020
+ function summarizeAnalysis(analysis) {
7021
+ let summary = summarize(analysis, 1);
7022
+ for (let divisor = 2; divisor <= 32 && Buffer.byteLength(printAnalysis(summary), "utf8") > SUMMARY_MAX_BYTES; divisor *= 2) {
7023
+ summary = summarize(analysis, divisor);
7024
+ }
7025
+ return summary;
7026
+ }
6950
7027
  function wouldUpload(analysis, force = false) {
6951
7028
  return analysis.blockers.every((blocker) => blocker.overridable && force);
6952
7029
  }
@@ -7425,6 +7502,13 @@ async function pushCommand(opts) {
7425
7502
  "--json belongs to --dry-run: `agentful push --dry-run --json` prints the upload analysis and uploads nothing."
7426
7503
  );
7427
7504
  }
7505
+ if (opts.summary && !opts.json) {
7506
+ throw new ApiError(
7507
+ 0,
7508
+ "invalid_options",
7509
+ "--summary belongs to --dry-run --json: it prints the analysis without the list of every file."
7510
+ );
7511
+ }
7428
7512
  if (!opts.json) console.log(banner());
7429
7513
  const mode = opts.prebuilt ? "dist" : "source";
7430
7514
  const cwd = process.cwd();
@@ -7438,8 +7522,7 @@ async function pushCommand(opts) {
7438
7522
  analysis = failedAnalysis(mode, cwd, { code: err2.code, message: err2.message });
7439
7523
  }
7440
7524
  if (opts.dryRun) {
7441
- if (opts.json) process.stdout.write(`${JSON.stringify(analysis, null, 2)}
7442
- `);
7525
+ if (opts.json) process.stdout.write(printAnalysis(opts.summary ? summarizeAnalysis(analysis) : analysis));
7443
7526
  else for (const line of renderAnalysis(analysis, { force: opts.force, dryRun: true })) console.log(line);
7444
7527
  if (!wouldUpload(analysis, opts.force)) process.exitCode = 1;
7445
7528
  return;
@@ -7968,75 +8051,6 @@ var agentful_theme_default = {
7968
8051
 
7969
8052
  // src/branding/commands.ts
7970
8053
  init_branding();
7971
- var NEEDS_A_PUSH = 'If the command reports "No Agentful project here", nothing has been deployed from this directory yet \u2014 run `agentful push` first (it creates the project and a preview), then retry.';
7972
- var SLASH_COMMANDS = {
7973
- setup: {
7974
- description: `${brand.displayName}: create or link the cloud project for this directory`,
7975
- template: 'Check whether .agentful/project.json exists. If it does, report the linked project title and id and stop. Otherwise run `agentful init --title "<name>"` using the bash tool, where <name> is $ARGUMENTS if I gave one, otherwise the current directory name. Report what it created and mention the title I can change it to. Do not modify any other project files.'
7976
- },
7977
- push: {
7978
- description: `${brand.displayName}: deploy this project and get a preview URL`,
7979
- template: "Run `agentful push` from the project root using the bash tool. It creates the project automatically if this directory is not linked yet. Stream what it prints, and when it finishes report the preview URL on its own line. If it fails: quote the printed error verbatim, and if the failure happened in the cloud (not in the pre-upload preflight), also run `agentful build-status` and quote its phase/error/details as the cause. Diagnose ONLY from those records \u2014 never invent causes, env vars, or settings they do not show. Do not modify project files unless I ask."
7980
- },
7981
- "build-status": {
7982
- description: `${brand.displayName}: show the last cloud build result`,
7983
- 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
7984
- },
7985
- backend: {
7986
- description: `${brand.displayName}: open the Backend tab (managed DB + server actions)`,
7987
- 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
7988
- },
7989
- preview: {
7990
- description: `${brand.displayName}: open the live preview in the browser`,
7991
- 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
7992
- },
7993
- publish: {
7994
- description: `${brand.displayName}: publish on a public subdomain`,
7995
- template: "Ask me for the subdomain if I did not provide one in $ARGUMENTS, then run `agentful publish <subdomain>` from the project root using the bash tool and report the public URL. " + NEEDS_A_PUSH
7996
- },
7997
- share: {
7998
- description: `${brand.displayName}: make the live preview public (or private again)`,
7999
- template: "Run `agentful share` from the project root using the bash tool (add `--private` if $ARGUMENTS says private) and report the resulting visibility and URL. " + NEEDS_A_PUSH
8000
- },
8001
- status: {
8002
- description: `${brand.displayName}: show the linked project and account`,
8003
- template: "Run `agentful whoami` using the bash tool. Then read .agentful/project.json and report the linked project title and id, plus the preview URL https://preview-<projectId>.agentful.page/. If the file does not exist, say that this directory is not linked yet and that `/setup` creates the link."
8004
- }
8005
- };
8006
- function commandMarkdown(name) {
8007
- const cmd = SLASH_COMMANDS[name];
8008
- const description = JSON.stringify(cmd.description);
8009
- return `---
8010
- description: ${description}
8011
- agent: ${cmd.agent ?? "agentful-build"}
8012
- ---
8013
-
8014
- ${cmd.template}
8015
- `;
8016
- }
8017
-
8018
- // src/lib/tools.ts
8019
- var import_node_fs13 = require("fs");
8020
- var import_node_path15 = require("path");
8021
- function assetsDir() {
8022
- const here = (0, import_node_path15.dirname)(process.argv[1] || "");
8023
- for (const candidate of [(0, import_node_path15.join)(here, "..", "assets"), (0, import_node_path15.join)(here, "assets")]) {
8024
- if ((0, import_node_fs13.existsSync)((0, import_node_path15.join)(candidate, "tools", "imagegen.ts"))) return candidate;
8025
- }
8026
- return null;
8027
- }
8028
- function installEngineTools(configDir2) {
8029
- const assets = assetsDir();
8030
- if (!assets) return false;
8031
- const target = (0, import_node_path15.join)(configDir2, "tools");
8032
- (0, import_node_fs13.mkdirSync)(target, { recursive: true });
8033
- try {
8034
- (0, import_node_fs13.cpSync)((0, import_node_path15.join)(assets, "tools"), target, { recursive: true });
8035
- return true;
8036
- } catch {
8037
- return false;
8038
- }
8039
- }
8040
8054
 
8041
8055
  // src/lib/cloudTarget.ts
8042
8056
  var CLOUD_INSTRUCTIONS_FILENAME = "AGENTFUL_CLOUD.md";
@@ -8146,6 +8160,87 @@ function serverSideSection() {
8146
8160
  )
8147
8161
  ].join("\n\n");
8148
8162
  }
8163
+ var PUSH_ANALYSIS_COMMAND = "agentful push --dry-run --json --summary";
8164
+ function pushSection() {
8165
+ const excludedDirs = [...ALWAYS_EXCLUDE_DIRS].filter((name) => /^[a-z]/.test(name)).map((name) => `\`${name}\``).join(", ");
8166
+ return `## Pushing to the platform (\`agentful push\`)
8167
+
8168
+ Two ways to upload. Every push replaces the previous upload completely, so a
8169
+ site can never be uploaded in several parts.
8170
+
8171
+ - **Source push**, \`agentful push\`: uploads this directory, then builds it in
8172
+ the cloud. Never uploaded: paths listed in \`.gitignore\`, \`.env*\` files,
8173
+ symlinks, tool folders such as \`.git\` or \`.next\`, and any folder named
8174
+ ${excludedDirs}, at any depth.
8175
+ The cloud build runs \`npm ci\` (with a \`package-lock.json\`, otherwise
8176
+ \`npm install\`) and then the build command from the table above. Nothing
8177
+ else runs: no other package manager, no separate data or prepare script, no
8178
+ CI workflow step.
8179
+ - **Prebuilt push**, \`agentful push --prebuilt --dir <folder>\`: uploads a
8180
+ finished output folder and serves it as it is. No cloud build, \`.gitignore\`
8181
+ is not applied, and \`index.html\` must sit at the top of that folder. It
8182
+ carries the website only: the project source in the cloud stays as it was (a
8183
+ later build in the cloud workspace replaces the site with a build of that
8184
+ source), and Managed Server Actions and \`.agentful/backend.json\` are NOT
8185
+ deployed by it.
8186
+
8187
+ Limits of one push, either way: about ${formatBytes(maxZipBytesForUpload(), "down")} compressed, ${formatBytes(MAX_UNPACKED_BYTES)} of files, ${MAX_UPLOAD_FILES.toLocaleString("en-US")} files.
8188
+ \`--force\` lifts none of them: it only lets the cloud build try despite a
8189
+ compatibility finding.
8190
+
8191
+ **The analysis decides, not an estimate.** \`${PUSH_ANALYSIS_COMMAND}\`
8192
+ (with \`--prebuilt --dir <folder>\` for a prebuilt push) reads the files and
8193
+ reports what a push would upload: no sign-in, no project, no upload, nothing
8194
+ written. Sizes, limits, what is left out and what blocks the upload come from
8195
+ that report and from nowhere else: quote its figures, never estimate or
8196
+ recompute them (\`limits.max_zip_bytes\` is the exact limit for this push).
8197
+ Run it before the first push of a session, after a push was stopped, and after
8198
+ you changed what gets uploaded. In between, \`agentful push\` applies the same
8199
+ check itself and stops before sign-in when something blocks. A blocker with
8200
+ \`"overridable": false\` is final, the platform rejects that upload: do not
8201
+ retry, do not add \`--force\`, do not look for another route.
8202
+
8203
+ Every change to the user's files for a push (including \`.gitignore\`) and every
8204
+ project script you run for it needs the user's OK first: name the concrete
8205
+ change, ask, wait for the answer.
8206
+
8207
+ - **No start page** (\`entry_missing\`): the platform serves \`index.html\` from
8208
+ the top level. With \`entry.candidates\`: show them (path and title;
8209
+ \`same_content_as\` marks copies) and ask which page opens first. A candidate
8210
+ at the top level can be renamed to \`index.html\`, or copied, which keeps the
8211
+ old address and adds the file's size to the upload: ask which. A candidate
8212
+ in a subfolder must not be copied up, its relative links would break. If
8213
+ that folder is the whole site, push the folder (\`suggestions\` carries the
8214
+ command); otherwise adjust the links and show the diff. Without candidates,
8215
+ ask which folder holds the site.
8216
+ - **Too large or too many files** (\`zip_too_large\`, \`unpacked_too_large\`,
8217
+ \`too_many_files\`): explain where the size is, from \`largest_files\`,
8218
+ \`by_top_level\`, \`duplicates\` and \`embedded_media\` (media stored inside text
8219
+ files as \`data:\` URLs). The ways out that exist: leave out files the site
8220
+ does not load (\`.gitignore\`, source push), or push only the finished output
8221
+ folder (prebuilt). Before proposing to leave a file out, search the sources
8222
+ for references to it and say what you checked: identical content does not
8223
+ make a copy unused. Never shrink, re-encode, move or delete the user's files
8224
+ on your own. If what the site needs stays above the limits, say plainly that
8225
+ it cannot be pushed yet. Larger uploads are not available.
8226
+ - **Compatibility findings** (\`"overridable": true\`): fix the cause in the
8227
+ code. \`--force\` only when the user asks to try the cloud build anyway.
8228
+ - **Files that would be missing on the site:** the warnings
8229
+ \`dropped_by_platform\` and \`excluded_static_directory\`, and entries of
8230
+ \`excluded.entries\` with reason \`gitignore\` that the site loads at runtime
8231
+ (generated data under \`public/\`, for example). The build can succeed and the
8232
+ site still be broken: tell the user before pushing and ask.
8233
+ - **Steps the cloud build does not run:** before the first push of a project
8234
+ you did not create in this session, read the \`package.json\` scripts, the
8235
+ README and the CI workflows for data generation, code generation or another
8236
+ package manager. If the site needs such a step, a source push is the wrong
8237
+ way: build locally with the project's own commands and its real data, keep
8238
+ its checks, and push the output folder prebuilt. Never put sample data in
8239
+ place of real data to get a push through.
8240
+
8241
+ A finished build or upload does not prove that the site works: say what you
8242
+ verified and what you did not.`;
8243
+ }
8149
8244
  function renderCloudInstructions(framework, backend) {
8150
8245
  return `# Agentful Cloud target (platform contract)
8151
8246
 
@@ -8300,6 +8395,8 @@ user's conscious step \u2014 in that tab, or by answering the question
8300
8395
  \`agentful push\` asks once (it names both consequences; a non-interactive
8301
8396
  push never enables anything).
8302
8397
 
8398
+ ${pushSection()}
8399
+
8303
8400
  ## Diagnosing failed pushes and cloud builds (hard rule)
8304
8401
 
8305
8402
  Diagnose **only from evidence**: the preflight output of \`agentful push\`, and
@@ -8318,6 +8415,93 @@ ${backendSessionLines(backend)}
8318
8415
  `;
8319
8416
  }
8320
8417
 
8418
+ // src/branding/commands.ts
8419
+ var PUSH_TOOL_TIMEOUT_MS = 9e5;
8420
+ var NEEDS_A_PUSH = 'If the command reports "No Agentful project here", nothing has been deployed from this directory yet \u2014 run `agentful push` first (it creates the project and a preview), then retry.';
8421
+ var SLASH_COMMANDS = {
8422
+ setup: {
8423
+ description: `${brand.displayName}: create or link the cloud project for this directory`,
8424
+ template: 'Check whether .agentful/project.json exists. If it does, report the linked project title and id and stop. Otherwise run `agentful init --title "<name>"` using the bash tool, where <name> is $ARGUMENTS if I gave one, otherwise the current directory name. Report what it created and mention the title I can change it to. Do not modify any other project files.'
8425
+ },
8426
+ // The analysis comes first (upload plan AP7): the report, not the model,
8427
+ // says what would be uploaded, how large it is and what blocks it, and the
8428
+ // rules for acting on it are in AGENTFUL_CLOUD.md. Two things the agent's
8429
+ // shell tool forces on this template: `--summary`, because the tool cuts
8430
+ // output after 2,000 lines or 50 KB and the full report lists every file;
8431
+ // and an explicit timeout, because the tool ends a command after two
8432
+ // minutes by default and a cloud build takes longer (engine 1.18.32).
8433
+ push: {
8434
+ description: `${brand.displayName}: check, deploy and get a preview URL`,
8435
+ template: `Deploy this project, following "Pushing to the platform" in AGENTFUL_CLOUD.md. Run every command from the project root using the bash tool. What I typed after the command (may be empty): "$ARGUMENTS". If it names \`--prebuilt\`, a folder for \`--dir\`, or \`--force\`, pass that on to both commands below.
8436
+
8437
+ 1. Run \`${PUSH_ANALYSIS_COMMAND}\`. It only reads files: no sign-in, no upload. Its JSON report is the only source for sizes, limits and what blocks the upload: quote its figures, never estimate them.
8438
+ 2. If the report or the project calls for a decision (a blocker, a file the site needs that would not be uploaded, a build step the cloud does not run), explain the finding in plain words with the figures of the report and ask me how to proceed. One question at a time, then wait for my answer. Until then do not change any file, do not run project scripts and do not push.
8439
+ 3. After a change I approved, run the analysis again.
8440
+ 4. When nothing blocks, run \`agentful push\` with the bash tool's \`timeout\` set to ${PUSH_TOOL_TIMEOUT_MS} (milliseconds): the cloud build takes minutes and the default timeout would cut the push off halfway. The command never waits for input. It creates the project automatically if this directory is not linked yet.
8441
+ 5. Report the preview URL on its own line and what the push printed about the backend. If it fails: quote the printed error verbatim, and if the failure happened in the cloud (not before the upload), also run \`agentful build-status\` and quote its phase/error/details as the cause. Diagnose ONLY from those records \u2014 never invent causes, env vars, or settings they do not show.
8442
+
8443
+ Apart from a change I approved in step 2, do not modify project files.`
8444
+ },
8445
+ "build-status": {
8446
+ description: `${brand.displayName}: show the last cloud build result`,
8447
+ 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
8448
+ },
8449
+ backend: {
8450
+ description: `${brand.displayName}: open the Backend tab (managed DB + server actions)`,
8451
+ 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
8452
+ },
8453
+ preview: {
8454
+ description: `${brand.displayName}: open the live preview in the browser`,
8455
+ 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
8456
+ },
8457
+ publish: {
8458
+ description: `${brand.displayName}: publish on a public subdomain`,
8459
+ template: "Ask me for the subdomain if I did not provide one in $ARGUMENTS, then run `agentful publish <subdomain>` from the project root using the bash tool and report the public URL. " + NEEDS_A_PUSH
8460
+ },
8461
+ share: {
8462
+ description: `${brand.displayName}: make the live preview public (or private again)`,
8463
+ template: "Run `agentful share` from the project root using the bash tool (add `--private` if $ARGUMENTS says private) and report the resulting visibility and URL. " + NEEDS_A_PUSH
8464
+ },
8465
+ status: {
8466
+ description: `${brand.displayName}: show the linked project and account`,
8467
+ template: "Run `agentful whoami` using the bash tool. Then read .agentful/project.json and report the linked project title and id, plus the preview URL https://preview-<projectId>.agentful.page/. If the file does not exist, say that this directory is not linked yet and that `/setup` creates the link."
8468
+ }
8469
+ };
8470
+ function commandMarkdown(name) {
8471
+ const cmd = SLASH_COMMANDS[name];
8472
+ const description = JSON.stringify(cmd.description);
8473
+ return `---
8474
+ description: ${description}
8475
+ agent: ${cmd.agent ?? "agentful-build"}
8476
+ ---
8477
+
8478
+ ${cmd.template}
8479
+ `;
8480
+ }
8481
+
8482
+ // src/lib/tools.ts
8483
+ var import_node_fs13 = require("fs");
8484
+ var import_node_path15 = require("path");
8485
+ function assetsDir() {
8486
+ const here = (0, import_node_path15.dirname)(process.argv[1] || "");
8487
+ for (const candidate of [(0, import_node_path15.join)(here, "..", "assets"), (0, import_node_path15.join)(here, "assets")]) {
8488
+ if ((0, import_node_fs13.existsSync)((0, import_node_path15.join)(candidate, "tools", "imagegen.ts"))) return candidate;
8489
+ }
8490
+ return null;
8491
+ }
8492
+ function installEngineTools(configDir2) {
8493
+ const assets = assetsDir();
8494
+ if (!assets) return false;
8495
+ const target = (0, import_node_path15.join)(configDir2, "tools");
8496
+ (0, import_node_fs13.mkdirSync)(target, { recursive: true });
8497
+ try {
8498
+ (0, import_node_fs13.cpSync)((0, import_node_path15.join)(assets, "tools"), target, { recursive: true });
8499
+ return true;
8500
+ } catch {
8501
+ return false;
8502
+ }
8503
+ }
8504
+
8321
8505
  // src/lib/engineConfig.ts
8322
8506
  var PROVIDER_ID = "agentful";
8323
8507
  var DISABLED_ENGINE_PLUGINS = {
@@ -8555,18 +8739,20 @@ var SCAFFOLD_BY_FRAMEWORK = {
8555
8739
  // cra/nuxt get no scaffold skill: the react/vue skills are Vite-specific
8556
8740
  // and would teach the wrong shape — the template contract still applies.
8557
8741
  };
8558
- function hasVueDependency(rootDir) {
8742
+ function dependencies(rootDir) {
8559
8743
  try {
8560
8744
  const pkg = JSON.parse((0, import_node_fs15.readFileSync)((0, import_node_path17.join)(rootDir, "package.json"), "utf8"));
8561
- return Boolean({ ...pkg.dependencies, ...pkg.devDependencies }["vue"]);
8745
+ return { ...pkg.dependencies, ...pkg.devDependencies };
8562
8746
  } catch {
8563
- return false;
8747
+ return {};
8564
8748
  }
8565
8749
  }
8566
8750
  function selectSkills(framework, rootDir) {
8567
8751
  const names = [CONTRACT_SKILL];
8568
8752
  if (framework === "vite") {
8569
- names.push(hasVueDependency(rootDir) ? "vue-scaffold" : "react-scaffold");
8753
+ const deps = dependencies(rootDir);
8754
+ if (deps.vue) names.push("vue-scaffold");
8755
+ else if (deps.react) names.push("react-scaffold");
8570
8756
  } else {
8571
8757
  const scaffold = SCAFFOLD_BY_FRAMEWORK[framework];
8572
8758
  if (scaffold) names.push(scaffold);
@@ -9895,7 +10081,7 @@ program2.command("login").description("Sign in to your Agentful account (device
9895
10081
  program2.command("logout").description("Remove the stored credentials").action(run(logoutCommand));
9896
10082
  program2.command("whoami").description("Show the signed-in account").action(run(whoamiCommand));
9897
10083
  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)));
9898
- 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").option("-y, --yes", "enable the declared managed database without asking (CI); a non-interactive push never enables it").option("--dry-run", "analyse only: what would be uploaded, how large, and what would block it \u2014 no sign-in, no project, no upload").option("--json", "with --dry-run: print the analysis as JSON (for scripts and agents)").action(run((opts) => pushCommand(opts)));
10084
+ 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").option("-y, --yes", "enable the declared managed database without asking (CI); a non-interactive push never enables it").option("--dry-run", "analyse only: what would be uploaded, how large, and what would block it \u2014 no sign-in, no project, no upload").option("--json", "with --dry-run: print the analysis as JSON (for scripts and agents)").option("--summary", "with --dry-run --json: totals and findings without the list of every file").action(run((opts) => pushCommand(opts)));
9899
10085
  program2.command("build-status").description("Show the cloud's record of the last build (the real error on failures)").action(run(buildStatusCommand));
9900
10086
  program2.command("backend").description("Open this project's Backend tab (managed DB + server actions) in the browser").action(run(backendCommand));
9901
10087
  program2.command("open").description("Open the live preview in the browser").action(run(openCommand));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentful",
3
- "version": "0.4.14",
3
+ "version": "0.4.15",
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",