siteplane 0.2.0-next.1 → 0.2.0-rc.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
@@ -8,18 +8,21 @@ content as fallback in your code, a Draft Mode preview for the editor, Booking a
8
8
  Run this in the website's folder, or tell your coding agent "Install Siteplane":
9
9
 
10
10
  ```bash
11
- npx siteplane@next init
11
+ npx siteplane@latest init
12
12
  ```
13
13
 
14
- Version 0.2 is on the `next` tag until it becomes `latest`.
15
-
16
14
  `init` opens the approval page in the browser. There you sign up or sign in, create the website in Siteplane or pick
17
15
  an existing one, and click Connect. If you created the website in Siteplane first, its prompt names the site:
18
16
  `npx siteplane@<version> init --site <site ID>`.
19
17
 
20
18
  `init` then writes `siteplane.config.json`, installs this package, wraps `next.config` in `withSiteplane`, adds the
21
19
  route `app/api/siteplane/[...siteplane]/route.ts` and `<SiteplaneBridge />`, and prints the task for the agent.
22
- Siteplane needs no environment variables or secrets in your project.
20
+ Siteplane needs no environment variables or secrets in your project, and it never deploys: deploy the way the project
21
+ always does.
22
+
23
+ The approval creates an agent connection for this one website. It may read the task, sync fields and check the
24
+ status; it cannot publish. It is listed under Settings → Connected agents in Siteplane, where it can be revoked, and
25
+ expires after 90 days without use.
23
26
 
24
27
  ## Field API
25
28
 
@@ -77,3 +80,9 @@ npx siteplane booking test
77
80
  approval. The agent connection is stored in the user config directory (`$SITEPLANE_CONFIG_DIR`,
78
81
  `$XDG_CONFIG_HOME/siteplane`, `%APPDATA%\siteplane` or `~/.config/siteplane`), never in the project; `--no-browser`
79
82
  skips opening the browser.
83
+
84
+ ## More
85
+
86
+ - Instructions for coding agents: https://siteplane.io/agents and https://siteplane.io/llms.txt
87
+ - Removing Siteplane: Settings → Export in Siteplane downloads every field with its published value and gives your
88
+ coding agent a task that writes the values into the code and removes this package.
@@ -1349,6 +1349,9 @@ async function checkProject(root) {
1349
1349
  }
1350
1350
  }
1351
1351
  const scan = scanFields(await collectSourceFiles2(root));
1352
+ if (scan.fields.length === 0 && scan.issues.length === 0) {
1353
+ warning("no_fields", "There are no fields yet. Wire the content the task names (npx siteplane task shows it again), then run check again.");
1354
+ }
1352
1355
  return { project, fields: scan.fields, issues: [...issues, ...scan.issues] };
1353
1356
  }
1354
1357
  function reachesSiteplaneRoutes(source) {
@@ -1448,26 +1451,51 @@ import ts6 from "typescript";
1448
1451
  var IMPORT = 'import { withSiteplane } from "siteplane/next";';
1449
1452
  function wrapNextConfig(source) {
1450
1453
  const file = ts6.createSourceFile("next.config.ts", source, ts6.ScriptTarget.Latest, true, ts6.ScriptKind.TS);
1451
- const exported = file.statements.find((statement) => ts6.isExportAssignment(statement) && !statement.isExportEquals);
1452
- if (!exported) {
1454
+ const edits = wrapDefaultExport(file, source);
1455
+ if (edits === "already")
1456
+ return { status: "already" };
1457
+ if (!edits) {
1453
1458
  const commonJs = /\bmodule\.exports\s*=|\bexports\.default\s*=/u.test(source);
1454
1459
  return { status: "manual", reason: commonJs ? "commonjs" : "unrecognized" };
1455
1460
  }
1456
- const expression = exported.expression;
1457
- if (ts6.isCallExpression(expression) && expression.expression.getText(file) === "withSiteplane") {
1458
- return { status: "already" };
1459
- }
1460
- const start = expression.getStart(file);
1461
- let next = `${source.slice(0, start)}withSiteplane(${source.slice(start, expression.end)})${source.slice(expression.end)}`;
1462
1461
  if (!source.includes(IMPORT)) {
1463
- const imports = file.statements.filter(ts6.isImportDeclaration);
1464
- const last = imports.at(-1);
1465
- next = last ? `${next.slice(0, last.end)}
1466
- ${IMPORT}${next.slice(last.end)}` : `${IMPORT}
1467
- ${next}`;
1462
+ const last = file.statements.filter(ts6.isImportDeclaration).at(-1);
1463
+ edits.push(last ? { start: last.end, end: last.end, text: `
1464
+ ${IMPORT}` } : { start: 0, end: 0, text: `${IMPORT}
1465
+ ` });
1468
1466
  }
1467
+ const next = edits.sort((a, b) => b.start - a.start || b.end - a.end).reduce((text, edit) => `${text.slice(0, edit.start)}${edit.text}${text.slice(edit.end)}`, source);
1469
1468
  return { status: "wrapped", source: next };
1470
1469
  }
1470
+ function wrapDefaultExport(file, source) {
1471
+ const exported = file.statements.find((statement) => ts6.isExportAssignment(statement) && !statement.isExportEquals);
1472
+ if (exported) {
1473
+ const expression = exported.expression;
1474
+ if (ts6.isCallExpression(expression) && expression.expression.getText(file) === "withSiteplane")
1475
+ return "already";
1476
+ const start = expression.getStart(file);
1477
+ return [
1478
+ { start, end: start, text: "withSiteplane(" },
1479
+ { start: expression.end, end: expression.end, text: ")" }
1480
+ ];
1481
+ }
1482
+ const declaration = file.statements.find((statement) => ts6.isFunctionDeclaration(statement) && Boolean(ts6.getModifiers(statement)?.some((modifier) => modifier.kind === ts6.SyntaxKind.DefaultKeyword)));
1483
+ if (!declaration?.body)
1484
+ return null;
1485
+ const modifiers = ts6.getModifiers(declaration) ?? [];
1486
+ const exportStart = modifiers[0]?.getStart(file) ?? declaration.getStart(file);
1487
+ const defaultEnd = modifiers.find((modifier) => modifier.kind === ts6.SyntaxKind.DefaultKeyword)?.end ?? exportStart;
1488
+ const functionStart = defaultEnd + (/^\s*/u.exec(source.slice(defaultEnd))?.[0].length ?? 0);
1489
+ return declaration.name ? [
1490
+ { start: exportStart, end: functionStart, text: "" },
1491
+ { start: declaration.end, end: declaration.end, text: `
1492
+
1493
+ export default withSiteplane(${declaration.name.text});` }
1494
+ ] : [
1495
+ { start: exportStart, end: functionStart, text: "export default withSiteplane(" },
1496
+ { start: declaration.end, end: declaration.end, text: ");" }
1497
+ ];
1498
+ }
1471
1499
  function newNextConfig(typescript) {
1472
1500
  return typescript ? `import type { NextConfig } from "next";
1473
1501
  ${IMPORT}
@@ -1488,7 +1516,7 @@ export default withSiteplane(nextConfig);
1488
1516
  function formatTask(task) {
1489
1517
  const wish = clean(task.editableBrief ?? "").trim();
1490
1518
  const rules = [
1491
- "Before you change code, tell the user this task and the fields you picked, in a few lines.",
1519
+ "Tell the user this task and the fields you picked in a few lines, then go on without waiting for an answer; ask only when something blocks you.",
1492
1520
  "Wire content values only: texts, links and images. Keep components, layout, styles, animations and accessibility as they are.",
1493
1521
  'Give every field one value call and put {...siteplane.attrs("<id>")} on each element that shows it.',
1494
1522
  "Field IDs are lowercase with at least two dot-separated parts (page.section.item) and stay the same when the text changes. Repeated content gets its own IDs (services.haircut.price); one ID on several pages is one shared field.",
@@ -1501,6 +1529,7 @@ function formatTask(task) {
1501
1529
  ];
1502
1530
  return [
1503
1531
  `Siteplane task for ${JSON.stringify(normalizeSiteName(task.site.name))} (site ${task.site.id})`,
1532
+ "Carry out this task now: wire the fields, then check, sync, deploy as usual and confirm with npx siteplane status --url.",
1504
1533
  "",
1505
1534
  "What should be editable (the owner's wish in Siteplane):",
1506
1535
  ...wish ? wish.split("\n").map((line) => `> ${line.trim()}`) : ["Agent decides: pick the content the owner will want to change."],
@@ -2046,7 +2075,7 @@ async function writeStore(connections, options) {
2046
2075
  }
2047
2076
 
2048
2077
  // src/version.ts
2049
- var PACKAGE_VERSION = "0.2.0-next.1";
2078
+ var PACKAGE_VERSION = "0.2.0-rc.1";
2050
2079
 
2051
2080
  // src/bin/run-siteplane.ts
2052
2081
  var usage = [
package/dist/route.js CHANGED
@@ -114,7 +114,7 @@ async function readBoundedText(request) {
114
114
  }
115
115
 
116
116
  // src/version.ts
117
- var PACKAGE_VERSION = "0.2.0-next.1";
117
+ var PACKAGE_VERSION = "0.2.0-rc.1";
118
118
 
119
119
  // src/route.ts
120
120
  var embeddable = { httpOnly: true, secure: true, sameSite: "none", partitioned: true, path: "/" };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "siteplane",
3
3
  "private": false,
4
- "version": "0.2.0-next.1",
4
+ "version": "0.2.0-rc.1",
5
5
  "description": "Siteplane for Next.js websites: editable content with code fallbacks, Draft Mode preview, Booking and the siteplane CLI.",
6
6
  "type": "module",
7
7
  "license": "SEE LICENSE IN LICENSE",