@isomorph.ai/cli 0.2.0 → 0.2.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
@@ -46,7 +46,7 @@ isomorph status | retry | promote | setup | profile | audience | secrets … --o
46
46
 
47
47
  ## Apps set up by the old `harbour` CLI
48
48
 
49
- Before 0.2.0 the CLI was published as `@fourier-labs/harbour` with the command `harbour`, and it kept the kit's files under `.harbour/`. That layout is not read any more — not by this CLI and not by the deployment pipeline. Run `isomorph init --upgrade --app-root .` once in each such app: it renames `.harbour/` to `.isomorph/` (git records renames), moves the two committed schema ids with it, rewrites the managed block in CLAUDE.md / AGENTS.md and the project skill, deletes the checks the old kit generated (the next `isomorph check` regenerates them) and rewrites the SDK package name and its exported type names in `package.json`, `src/`, `jobs/` and the checks you wrote yourself. Every other command refuses an un-upgraded app and names that command. The old CLI can stay installed; it no longer deploys.
49
+ Before 0.2.0 the CLI was published as `@fourier-labs/harbour` with the command `harbour`, and it kept the kit's files under `.harbour/`. That layout is not read any more — not by this CLI and not by the deployment pipeline. Run `isomorph init --upgrade --app-root .` once in each such app: it renames `.harbour/` to `.isomorph/` (git records renames), moves the two committed schema ids with it, rewrites the managed block in CLAUDE.md / AGENTS.md, replaces the project skill (the old `.claude/skills/harbour-kit/SKILL.md` is deleted when it is the kit's — the frontmatter name and the opening line every release wrote — and kept, and reported as kept, when you rewrote its body), deletes the checks the old kit generated (the next `isomorph check` regenerates them) and rewrites the SDK package name, its exported type names and the retired `HARBOUR_*` environment names (`HARBOUR_APP_URL` → `ISOMORPH_APP_URL` and the rest) in `package.json`, `vite.config.*`, `src/`, `jobs/` and the checks you wrote yourself. Run it again after updating the CLI: an app already moved by 0.2.0 loses its old skill and old environment names the same way. Every other command refuses an un-upgraded app and names that command. The old CLI can stay installed; it no longer deploys.
50
50
 
51
51
  ## Development
52
52
 
@@ -26,7 +26,8 @@ export async function initKit(root, bundle, options = {}) {
26
26
  const result = { root, created: [], kept: [], updated: [], mode: options.upgrade ? "upgrade" : emptyDir ? "starter" : "existing", bundleChanges: [], agents: options.env ? await agentSetup(options.env) : { created: [], updated: [], kept: [], removed: [] } };
27
27
  if (options.upgrade) {
28
28
  await migrateLegacyKit(root, bundle, result);
29
- await rewriteSdkReferences(root, bundle.sdk.package, result);
29
+ await retireLegacySkill(root, result);
30
+ await rewriteLegacyReferences(root, bundle.sdk.package, result);
30
31
  }
31
32
  const write = async (path, content) => {
32
33
  const absolute = join(root, path);
@@ -76,18 +77,29 @@ const LEGACY = {
76
77
  generatedPrefix: "// harbour:generated sha256:",
77
78
  gitignoreLine: ".harbour/local/",
78
79
  skill: ".claude/skills/harbour-kit/SKILL.md",
80
+ /** How every release's project skill opened, under the frontmatter `name: harbour-kit`; the body below it changed from release to release. */
81
+ skillOpening: 'Follow the "Harbour development kit" block',
79
82
  sdkPackage: "@harbour/app-sdk",
80
83
  /** The SDK's exported type names, renamed with the package. */
81
- typeNames: /\bHarbour(User|ErrorCategory|Error|Client|Result|CompatSource)\b/g
84
+ typeNames: /\bHarbour(User|ErrorCategory|Error|Client|Result|CompatSource)\b/g,
85
+ /**
86
+ * The environment names the kit and the platform export to a check, a job and
87
+ * the Vite config, renamed with the CLI. Whole words: the session's
88
+ * `HARBOUR_LOCAL_USER_EMAIL` and the CLI↔gateway `HARBOUR_APP_GATEWAY_*` and
89
+ * `HARBOUR_GATE_*` names are not builder-visible and are not renamed.
90
+ */
91
+ envNames: /\bHARBOUR_(APP_URL|SDK_MODULE|IDENTITY_CONTEXT_SECOND_USER|IDENTITY_CONTEXT_HEADER|IDENTITY_CONTEXT|GATEWAY_URL|WORKLOAD_TOKEN|SCHEDULED_AT|LOCAL_ORIGIN|VITE_PORT)\b/g
82
92
  };
83
93
  /**
84
94
  * Moves an app set up by a CLI older than 0.2.0 to this kit, once, and only under
85
95
  * `init --upgrade`: the kit directory and its five entries are renamed (git records
86
- * renames), the two committed schema ids move with it, the `.gitignore` line, the
87
- * managed block in CLAUDE.md / AGENTS.md and the project skill are rewritten, and
88
- * the checks the old kit generated are deleted for the next `check` to regenerate.
89
- * A check the author wrote or edited is kept, as everywhere else. Nothing happens
90
- * when there is no old layout, or when the current one already exists beside it.
96
+ * renames), the two committed schema ids move with it, the `.gitignore` line and
97
+ * the managed block in CLAUDE.md / AGENTS.md are rewritten, and the checks the old
98
+ * kit generated are deleted for the next `check` to regenerate. A check the author
99
+ * wrote or edited is kept, as everywhere else. Nothing happens when there is no old
100
+ * layout, or when the current one already exists beside it. The old project skill
101
+ * and the old names in the app's own files are handled after this, on every
102
+ * `--upgrade`, so an app this pass already moved still loses them.
91
103
  */
92
104
  async function migrateLegacyKit(root, bundle, result) {
93
105
  const legacy = join(root, LEGACY_KIT_DIRECTORY);
@@ -124,12 +136,6 @@ async function migrateLegacyKit(root, bundle, result) {
124
136
  touched(file);
125
137
  }
126
138
  }
127
- const skill = join(root, LEGACY.skill);
128
- if ((await readFile(skill, "utf8").catch(() => undefined)) === LEGACY_SKILL_FILE) {
129
- await rm(skill);
130
- await rmdir(dirname(skill)).catch(() => undefined);
131
- touched(LEGACY.skill);
132
- }
133
139
  for (const name of await readdir(paths.checks).catch(() => [])) {
134
140
  if (!isPristine(await readFile(join(paths.checks, name), "utf8").catch(() => ""), LEGACY.generatedPrefix))
135
141
  continue;
@@ -138,15 +144,45 @@ async function migrateLegacyKit(root, bundle, result) {
138
144
  }
139
145
  }
140
146
  /**
141
- * Rewrites the SDK's previous package name (and, in `package.json`, the tarball
142
- * path the old kit staged it under) and its six exported type names in the app's
143
- * own files: `package.json`, `src/`, `jobs/` and the retained checks. Whole words
144
- * only, so an author's own `HarbourUserProfile` is theirs. Idempotent: an app that
145
- * names none of them is left untouched and unreported.
147
+ * Deletes the project skill an older CLI wrote, on every `--upgrade` — an app the
148
+ * layout migration moved under 0.2.0 still has it beside the new skill. The file
149
+ * is the kit's by shape, not by bytes: each release wrote a different body under
150
+ * the same frontmatter name and the same opening line, so one whose body an
151
+ * author rewrote is kept, and reported, as theirs.
152
+ */
153
+ async function retireLegacySkill(root, result) {
154
+ const path = join(root, LEGACY.skill);
155
+ const text = await readFile(path, "utf8").catch(() => undefined);
156
+ if (text === undefined)
157
+ return;
158
+ if (!isLegacyKitSkill(text)) {
159
+ result.kept.push(LEGACY.skill);
160
+ return;
161
+ }
162
+ await rm(path);
163
+ await rmdir(dirname(path)).catch(() => undefined);
164
+ result.updated.push(LEGACY.skill);
165
+ }
166
+ /** Frontmatter naming `harbour-kit`, and the opening every release wrote as the body's first line. */
167
+ function isLegacyKitSkill(text) {
168
+ const lines = text.split(/\r?\n/);
169
+ const close = lines.indexOf("---", 1);
170
+ if (lines[0] !== "---" || close < 0)
171
+ return false;
172
+ return lines.slice(1, close).includes("name: harbour-kit") && (lines.slice(close + 1).find(line => line.trim()) ?? "").startsWith(LEGACY.skillOpening);
173
+ }
174
+ /**
175
+ * Rewrites what the old kit named in the app's own files — `package.json`,
176
+ * `vite.config.*`, `src/`, `jobs/` and the retained checks — on every `--upgrade`:
177
+ * the SDK's previous package name (and, in `package.json`, the tarball path the
178
+ * old kit staged it under), its six exported type names, and the environment
179
+ * names the kit and the platform export to a check, a job and the Vite config.
180
+ * Whole words only, so an author's own `HarbourUserProfile` is theirs. Idempotent:
181
+ * an app that names none of them is left untouched and unreported.
146
182
  */
147
- async function rewriteSdkReferences(root, sdkPackage, result) {
148
- const rewrite = (text) => text.replaceAll(`"${LEGACY.sdkPackage}"`, `"${sdkPackage}"`).replaceAll(`'${LEGACY.sdkPackage}'`, `'${sdkPackage}'`).replace(LEGACY.typeNames, "Isomorph$1");
149
- for (const path of ["package.json", ...(await appSourceFiles(root))]) {
183
+ async function rewriteLegacyReferences(root, sdkPackage, result) {
184
+ const rewrite = (text) => text.replaceAll(`"${LEGACY.sdkPackage}"`, `"${sdkPackage}"`).replaceAll(`'${LEGACY.sdkPackage}'`, `'${sdkPackage}'`).replace(LEGACY.typeNames, "Isomorph$1").replace(LEGACY.envNames, "ISOMORPH_$1");
185
+ for (const path of ["package.json", "vite.config.ts", "vite.config.js", "vite.config.mjs", ...(await appSourceFiles(root))]) {
150
186
  const current = await readFile(join(root, path), "utf8").catch(() => undefined);
151
187
  if (current === undefined)
152
188
  continue;
@@ -242,21 +278,6 @@ Follow the "Isomorph development kit" block in CLAUDE.md / AGENTS.md. Workflow:
242
278
  5. \`isomorph integrations request <connection> --reason "<why>" --app-root .\` asks IT for access now — one request per connection, approved once for every environment; \`isomorph dev\` (signed in) and \`isomorph productionise\` file it for you. Pending is not ready.
243
279
  6. \`isomorph productionise --app-root .\` saves and deploys the preview; \`isomorph promote\` after the person has tested it.
244
280
  `;
245
- /** The project skill CLI releases before 0.2.0 wrote, byte for byte: `init --upgrade` deletes the old file only when it is still exactly this. */
246
- export const LEGACY_SKILL_FILE = `---
247
- name: harbour-kit
248
- description: Build, run and check this Harbour app with the Harbour CLI (dev, check, integrations, productionise).
249
- ---
250
-
251
- Follow the "Harbour development kit" block in CLAUDE.md / AGENTS.md. Workflow:
252
-
253
- 1. \`harbour dev --app-root .\` starts Postgres, storage and one Harbour gateway (session identities, fixtures, realtime) plus Vite behind one loopback origin printed in the banner.
254
- 2. Edit \`src/\` and \`migrations/\`. Use the SDK only. \`.harbour/integrations.json\` starts with no connections and gains one only when the app really calls a company system: declare it with only the operations the app calls, then request access (step 5). A declared connection the app does not call blocks every deploy until IT grants it; an undeclared one cannot be requested, so it never gets a grant. README.md has the worked Slack and warehouse examples — the file itself is strict JSON and cannot hold comments.
255
- 3. \`.harbour/checks/\` is generated, not written by hand: \`harbour check\` reads \`migrations/\` and \`src/\` and writes one retained journey per capability the app's own code uses (plus a cross-user denial per owner-scoped table), deleting the ones the app no longer needs — so run it in the same edit that changes the app instead of adding or deleting these files yourself. It reports anything it cannot generate (\`actions\`, \`realtime\`, a table with no migration) for you to write. Edit a generated check and it becomes yours: Harbour keeps it, stops managing it and never removes it, so deleting it when the feature goes is then your job. \`harbour check\` and the deployment pipeline replay these checks against a real App Gateway and refuse the app (\`flow.check-failed\`) in both directions — a capability no check exercises, and a check that exercises something the code no longer does.
256
- 4. \`harbour check --app-root .\` before every hand-off; read \`.harbour/local/check-report.json\`. Real integrations are reported as not tested unless \`--integrations\` is passed (read operations only); a retained check that calls \`harbour.integrations.execute\` is answered by the gate's fixture (the contract's shape for what the app declared, nothing sent or read) and the passed check says so. A governed AI call (\`harbour.ai.chat\`) is exercised for real through the development route while \`harbour dev\` is up; the pipeline answers it with a canned completion and reports it as not tested.
257
- 5. \`harbour integrations request <connection> --reason "<why>" --app-root .\` asks IT for access now — one request per connection, approved once for every environment; \`harbour dev\` (signed in) and \`harbour productionise\` file it for you. Pending is not ready.
258
- 6. \`harbour productionise --app-root .\` saves and deploys the preview; \`harbour promote\` after the person has tested it.
259
- `;
260
281
  /**
261
282
  * Kit infrastructure: structural files every kit app needs, on every path —
262
283
  * `init` in an empty directory, `init` over an existing Vite + React app, and
@@ -1 +1 @@
1
- export const CLI_VERSION = "0.2.0";
1
+ export const CLI_VERSION = "0.2.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isomorph.ai/cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Isomorph development kit CLI",
5
5
  "type": "module",
6
6
  "bin": {