@overaihq/cli 0.4.0 → 0.6.0

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 +1 -1
  2. package/dist/index.js +282 -67
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -24,7 +24,7 @@ overai clone <workspaceId> # materialize resources and WORKSPACE.md
24
24
  # ...edit files...
25
25
  overai status # local edits + remote drift
26
26
  overai diff # what changed
27
- overai review # validation + best-practice review packet
27
+ overai review # local checks + authenticated TypeScript lint + guides
28
28
  overai report bug "<details>" # report broken or unexplained behavior
29
29
  overai report feature "<details>" # request a missing platform capability
30
30
  overai push -m "why" # create new versions (not live yet)
package/dist/index.js CHANGED
@@ -26690,26 +26690,6 @@ var readValue = async (value, label) => {
26690
26690
  }
26691
26691
  };
26692
26692
 
26693
- // src/gql.ts
26694
- var gqlRequest = async (query, variables, options) => {
26695
- const res = await v2Fetch(
26696
- options?.v1 === true ? "/v1/graphql" : "/v2/graphql",
26697
- {
26698
- method: "POST",
26699
- headers: { "Content-Type": "application/json" },
26700
- body: JSON.stringify({ query, variables })
26701
- }
26702
- );
26703
- const body = await res.json().catch(() => null);
26704
- if (body?.errors != null && body.errors.length > 0) {
26705
- throw new Error(body.errors.map((e) => e.message).join("; "));
26706
- }
26707
- if (!res.ok || body?.data == null) {
26708
- throw new Error(`graphql failed (${String(res.status)})`);
26709
- }
26710
- return body.data;
26711
- };
26712
-
26713
26693
  // src/output.ts
26714
26694
  var resolveFormat = (explicit) => {
26715
26695
  if (explicit === "json" || explicit === "csv" || explicit === "table") {
@@ -26764,6 +26744,43 @@ var printResult = (data, format2) => {
26764
26744
  `);
26765
26745
  };
26766
26746
 
26747
+ // src/commands/agent.ts
26748
+ var runAgent = async (args) => {
26749
+ const [operation, id] = args;
26750
+ if (operation !== "generate" || id == null) throw new Error("Usage: overai agent generate <id> --message <text|@file|@->");
26751
+ const message = await readValue(flagValue(args, "--message", "-m"), "--message");
26752
+ if (message == null) throw new Error("Agent generate requires --message.");
26753
+ const { organizationId } = await getAccessToken();
26754
+ const response = await v2Fetch(`/v2/agents/${organizationId}/${id}/generate/text`, {
26755
+ method: "POST",
26756
+ headers: { "Content-Type": "application/json" },
26757
+ body: JSON.stringify({ message })
26758
+ });
26759
+ const body = await response.json().catch(() => null);
26760
+ if (!response.ok) throw new Error(body == null ? `Agent generate failed (${String(response.status)}).` : JSON.stringify(body));
26761
+ printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
26762
+ };
26763
+
26764
+ // src/gql.ts
26765
+ var gqlRequest = async (query, variables, options) => {
26766
+ const res = await v2Fetch(
26767
+ options?.v1 === true ? "/v1/graphql" : "/v2/graphql",
26768
+ {
26769
+ method: "POST",
26770
+ headers: { "Content-Type": "application/json" },
26771
+ body: JSON.stringify({ query, variables })
26772
+ }
26773
+ );
26774
+ const body = await res.json().catch(() => null);
26775
+ if (body?.errors != null && body.errors.length > 0) {
26776
+ throw new Error(body.errors.map((e) => e.message).join("; "));
26777
+ }
26778
+ if (!res.ok || body?.data == null) {
26779
+ throw new Error(`graphql failed (${String(res.status)})`);
26780
+ }
26781
+ return body.data;
26782
+ };
26783
+
26767
26784
  // src/commands/automation.ts
26768
26785
  var runRuns = async (automationId, args) => {
26769
26786
  const limit2 = Number(flagValue(args, "--limit") ?? 20);
@@ -26854,23 +26871,6 @@ var runAutomation = async (args) => {
26854
26871
  process.exitCode = 1;
26855
26872
  };
26856
26873
 
26857
- // src/commands/agent.ts
26858
- var runAgent = async (args) => {
26859
- const [operation, id] = args;
26860
- if (operation !== "generate" || id == null) throw new Error("Usage: overai agent generate <id> --message <text|@file|@->");
26861
- const message = await readValue(flagValue(args, "--message", "-m"), "--message");
26862
- if (message == null) throw new Error("Agent generate requires --message.");
26863
- const { organizationId } = await getAccessToken();
26864
- const response = await v2Fetch(`/v2/agents/${organizationId}/${id}/generate/text`, {
26865
- method: "POST",
26866
- headers: { "Content-Type": "application/json" },
26867
- body: JSON.stringify({ message })
26868
- });
26869
- const body = await response.json().catch(() => null);
26870
- if (!response.ok) throw new Error(body == null ? `Agent generate failed (${String(response.status)}).` : JSON.stringify(body));
26871
- printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
26872
- };
26873
-
26874
26874
  // src/workspace/manifest.ts
26875
26875
  import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
26876
26876
  import { dirname, join as join2, resolve } from "path";
@@ -33428,6 +33428,92 @@ import {
33428
33428
  writeFileSync as writeFileSync4
33429
33429
  } from "fs";
33430
33430
  import { join as join4, resolve as resolve3 } from "path";
33431
+
33432
+ // ../common/src/lint/schema.ts
33433
+ var lintSurfaceSchema = external_exports.enum([
33434
+ "webPage",
33435
+ // React/TSX page — gets rules-of-hooks
33436
+ "automationCode",
33437
+ // automation TS-code node (globals STATE/env/OUTPUT)
33438
+ "function"
33439
+ // Web-standard-runtime TS module (export default handler)
33440
+ ]);
33441
+ var lintDiagnosticSchema = external_exports.object({
33442
+ line: external_exports.number(),
33443
+ column: external_exports.number(),
33444
+ /** ESLint rule id, or null for a fatal parse error. */
33445
+ ruleId: external_exports.string().nullable(),
33446
+ message: external_exports.string(),
33447
+ severity: external_exports.enum(["error", "warning"])
33448
+ });
33449
+ var lintRequestSchema = external_exports.object({
33450
+ code: external_exports.string(),
33451
+ surface: lintSurfaceSchema,
33452
+ // The workspace the code is being authored in. Used to (a) authorize the
33453
+ // caller as a builder in that workspace and (b) resolve its real resources so
33454
+ // env id references are type-narrowed. A user token carries no workspace, so
33455
+ // this rides the request body.
33456
+ workspaceId: external_exports.string(),
33457
+ // The linted function's own declared output schema (function surface only),
33458
+ // enabling the handler return-vs-schema compliance check. Client-supplied
33459
+ // because the draft being linted lives in the editor, unsaved; advisory, so
33460
+ // nothing is trusted from it. '' or omitted skips the check.
33461
+ functionOutputSchema: external_exports.string().optional()
33462
+ });
33463
+ var lintResponseSchema = external_exports.object({
33464
+ diagnostics: external_exports.array(lintDiagnosticSchema)
33465
+ });
33466
+ var functionDeployImpactRequestSchema = external_exports.object({
33467
+ // Authorizes the builder-in-workspace gate, like the lint request.
33468
+ workspaceId: external_exports.string(),
33469
+ functionId: external_exports.string(),
33470
+ /** The saved version being deployed; its schema pair is read server-side. */
33471
+ versionNumber: external_exports.number().int()
33472
+ });
33473
+ var functionDeployDependentSchema = external_exports.object({
33474
+ kind: external_exports.enum(["webPage", "printablePage", "function", "automation"]),
33475
+ id: external_exports.string(),
33476
+ name: external_exports.string()
33477
+ });
33478
+ var functionDeployImpactResponseSchema = external_exports.object({
33479
+ /** True only when the contract is incompatible AND deployed callers exist. */
33480
+ breaking: external_exports.boolean(),
33481
+ /** Human-readable type mismatches, one per broken direction. */
33482
+ reasons: external_exports.array(external_exports.string()),
33483
+ /** Deployed resources whose code references this function's handle. */
33484
+ dependents: external_exports.array(functionDeployDependentSchema)
33485
+ });
33486
+
33487
+ // src/deployImpact.ts
33488
+ var NOT_BREAKING = {
33489
+ breaking: false,
33490
+ reasons: [],
33491
+ dependents: []
33492
+ };
33493
+ var fetchFunctionDeployImpact = async (workspaceId, functionId, versionNumber) => {
33494
+ try {
33495
+ const res = await v2Fetch("/v2/architect/functionDeployImpact", {
33496
+ method: "POST",
33497
+ headers: { "Content-Type": "application/json" },
33498
+ body: JSON.stringify({ workspaceId, functionId, versionNumber })
33499
+ });
33500
+ if (!res.ok) return NOT_BREAKING;
33501
+ const parsed = functionDeployImpactResponseSchema.safeParse(
33502
+ await res.json()
33503
+ );
33504
+ return parsed.success ? parsed.data : NOT_BREAKING;
33505
+ } catch {
33506
+ return NOT_BREAKING;
33507
+ }
33508
+ };
33509
+ var breakingDeployMessage = (name2, versionNumber, impact) => [
33510
+ `SKIP ${name2}: v${String(versionNumber)}'s declared input/output schemas are incompatible with deployed callers:`,
33511
+ ...impact.dependents.map((d) => ` ${d.name} (${d.kind}, ${d.id})`),
33512
+ ...impact.reasons.map((r) => ` ${r}`),
33513
+ `Fix the callers (or the schemas) first, or pass --allow-breaking to deploy anyway.`
33514
+ ].join("\n");
33515
+
33516
+ // src/commands/sync.ts
33431
33517
  var toManifestResource = (type, remote) => ({
33432
33518
  type,
33433
33519
  name: remote.name,
@@ -33665,6 +33751,7 @@ var runPush = async (args) => {
33665
33751
  const message = flagValue(args, "-m", "--message") ?? "Pushed via overai CLI";
33666
33752
  const deploy = args.includes("--deploy");
33667
33753
  const force = args.includes("--force");
33754
+ const allowBreaking = args.includes("--allow-breaking");
33668
33755
  const pathFilter = positionals(args)[0];
33669
33756
  let pushed = 0;
33670
33757
  for (const [id, resource] of resourcesMatching(manifest, pathFilter)) {
@@ -33709,6 +33796,20 @@ var runPush = async (args) => {
33709
33796
  `
33710
33797
  );
33711
33798
  if (deploy) {
33799
+ if (resource.type === "function" && !allowBreaking) {
33800
+ const impact = await fetchFunctionDeployImpact(
33801
+ manifest.workspaceId,
33802
+ id,
33803
+ versionNumber
33804
+ );
33805
+ if (impact.breaking) {
33806
+ process.stdout.write(
33807
+ `${breakingDeployMessage(resource.name, versionNumber, impact)}
33808
+ `
33809
+ );
33810
+ continue;
33811
+ }
33812
+ }
33712
33813
  await adapter.deploy(id, versionNumber);
33713
33814
  resource.deployedVersion = versionNumber;
33714
33815
  writeManifest(root, manifest);
@@ -33726,6 +33827,7 @@ var runPush = async (args) => {
33726
33827
  var runDeploy = async (args) => {
33727
33828
  const { root, manifest } = openClone();
33728
33829
  const pathFilter = positionals(args)[0];
33830
+ const allowBreaking = args.includes("--allow-breaking");
33729
33831
  const pending = resourcesMatching(manifest, pathFilter).filter(
33730
33832
  ([, r]) => r.deployedVersion !== r.baseVersion
33731
33833
  );
@@ -33749,6 +33851,20 @@ Pass a filter to deploy one, or --all to deploy everything.`
33749
33851
  );
33750
33852
  continue;
33751
33853
  }
33854
+ if (resource.type === "function" && !allowBreaking) {
33855
+ const impact = await fetchFunctionDeployImpact(
33856
+ manifest.workspaceId,
33857
+ id,
33858
+ resource.baseVersion
33859
+ );
33860
+ if (impact.breaking) {
33861
+ process.stdout.write(
33862
+ `${breakingDeployMessage(resource.name, resource.baseVersion, impact)}
33863
+ `
33864
+ );
33865
+ continue;
33866
+ }
33867
+ }
33752
33868
  await adapterFor(resource.type).deploy(id, resource.baseVersion);
33753
33869
  resource.deployedVersion = resource.baseVersion;
33754
33870
  writeManifest(root, manifest);
@@ -34146,30 +34262,6 @@ var runOpen = async (args) => {
34146
34262
  openBrowser(url);
34147
34263
  };
34148
34264
 
34149
- // src/commands/printablePage.ts
34150
- var runPrintablePage = async (args) => {
34151
- const [operation, id] = args;
34152
- if (operation !== "render" || id == null) throw new Error("Usage: overai printable-page render <id> [--search-params <json|@file>] [--timeout <ms>]");
34153
- const data = await gqlRequest(
34154
- `query CliPrintablePageWorkspace($id: String!) { printablePage(id: $id) { workspaceId } }`,
34155
- { id }
34156
- );
34157
- if (data.printablePage == null) throw new Error(`Printable page ${id} not found.`);
34158
- const searchParams = await readValue(flagValue(args, "--search-params"), "--search-params") ?? "{}";
34159
- JSON.parse(searchParams);
34160
- const { organizationId } = await getAccessToken();
34161
- const timeoutMs = Number(flagValue(args, "--timeout") ?? 6e4);
34162
- const response = await v2Fetch(`/v2/actions/${organizationId}/${data.printablePage.workspaceId}/run`, {
34163
- method: "POST",
34164
- headers: { "Content-Type": "application/json" },
34165
- body: JSON.stringify({ actionId: "PRINTABLE_PAGE_TO_PDF", inputs: { printablePageId: id, searchParams, includeDiagnostics: true } }),
34166
- signal: AbortSignal.timeout(timeoutMs)
34167
- });
34168
- const body = await response.json().catch(() => null);
34169
- if (!response.ok) throw new Error(JSON.stringify(body));
34170
- printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
34171
- };
34172
-
34173
34265
  // src/commands/org.ts
34174
34266
  var USER_ORGS_QUERY = `
34175
34267
  query UserOrganizations($identityId: String!) {
@@ -34235,6 +34327,30 @@ ${result.redirectUrl}
34235
34327
  `);
34236
34328
  };
34237
34329
 
34330
+ // src/commands/printablePage.ts
34331
+ var runPrintablePage = async (args) => {
34332
+ const [operation, id] = args;
34333
+ if (operation !== "render" || id == null) throw new Error("Usage: overai printable-page render <id> [--search-params <json|@file>] [--timeout <ms>]");
34334
+ const data = await gqlRequest(
34335
+ `query CliPrintablePageWorkspace($id: String!) { printablePage(id: $id) { workspaceId } }`,
34336
+ { id }
34337
+ );
34338
+ if (data.printablePage == null) throw new Error(`Printable page ${id} not found.`);
34339
+ const searchParams = await readValue(flagValue(args, "--search-params"), "--search-params") ?? "{}";
34340
+ JSON.parse(searchParams);
34341
+ const { organizationId } = await getAccessToken();
34342
+ const timeoutMs = Number(flagValue(args, "--timeout") ?? 6e4);
34343
+ const response = await v2Fetch(`/v2/actions/${organizationId}/${data.printablePage.workspaceId}/run`, {
34344
+ method: "POST",
34345
+ headers: { "Content-Type": "application/json" },
34346
+ body: JSON.stringify({ actionId: "PRINTABLE_PAGE_TO_PDF", inputs: { printablePageId: id, searchParams, includeDiagnostics: true } }),
34347
+ signal: AbortSignal.timeout(timeoutMs)
34348
+ });
34349
+ const body = await response.json().catch(() => null);
34350
+ if (!response.ok) throw new Error(JSON.stringify(body));
34351
+ printResult(body, resolveFormat(flagValue(args, "-o", "--output")));
34352
+ };
34353
+
34238
34354
  // src/commands/update.ts
34239
34355
  import { spawnSync } from "child_process";
34240
34356
  import { existsSync as existsSync4, readFileSync as readFileSync8 } from "fs";
@@ -34629,6 +34745,10 @@ executionMemory MB).
34629
34745
  responds with \u2014 no request envelope) so env.FNxxx.call() is typed for
34630
34746
  callers: the deployed version's schemas become FunctionHandle<In, Out> in
34631
34747
  the editor and lint. Leave it "" when the response shape is unspecified.
34748
+ - That contract is checked at deploy time: \`overai deploy\` and
34749
+ \`overai push --deploy\` skip a function whose declared schema change is
34750
+ incompatible with deployed callers, printing the affected resources. Fix the
34751
+ callers (or the schemas) first, or pass \`--allow-breaking\` to deploy anyway.
34632
34752
  - Verify with \`overai fn <id> --input k=v\` (invokes the DEPLOYED version;
34633
34753
  add \`--method\`/\`--path\` to exercise one route of a router) and
34634
34754
  \`overai fn logs <id>\` / \`--execution <FEX id>\` for status, timing, logs,
@@ -34644,7 +34764,8 @@ var workflow = `# CLI editing workflow
34644
34764
  each nested workspace you need separately.
34645
34765
  2. Edit files. The id in each filename is authoritative; slugs are cosmetic.
34646
34766
  3. \`overai status\` / \`overai diff\` \u2014 review what changed.
34647
- 4. \`overai review\` \u2014 validation + these guides; fix findings first.
34767
+ 4. \`overai review\` \u2014 local checks, authenticated TypeScript lint against the
34768
+ live workspace, and these guides; fix findings first.
34648
34769
  5. \`overai push -m "why"\` \u2014 creates NEW versions (not live). Fast-forward
34649
34770
  only: if the resource moved remotely, pull, re-apply, retry. Write commit
34650
34771
  messages like PR titles \u2014 they're the version history.
@@ -34682,11 +34803,11 @@ var runBestPractices = (args) => {
34682
34803
  );
34683
34804
  };
34684
34805
  var runReview = async () => {
34685
- const root = findWorkspaceRoot();
34686
- const manifest = readManifest(root);
34806
+ const { root, manifest } = openClone();
34687
34807
  const findings2 = [];
34688
34808
  const touchedTypes = /* @__PURE__ */ new Set();
34689
34809
  const changed = [];
34810
+ const lintTargets = [];
34690
34811
  for (const [id, resource] of Object.entries(manifest.resources)) {
34691
34812
  const { state, changedFiles } = localState(root, resource);
34692
34813
  if (state !== "modified") continue;
@@ -34733,6 +34854,37 @@ var runReview = async () => {
34733
34854
  }
34734
34855
  }
34735
34856
  }
34857
+ if (resource.type === "webPage") {
34858
+ changedFiles.filter((relPath) => relPath.endsWith(".tsx")).forEach((relPath) => {
34859
+ const code = readWorkingFile(root, relPath);
34860
+ if (code != null) {
34861
+ lintTargets.push({ label: relPath, code, surface: "webPage" });
34862
+ }
34863
+ });
34864
+ }
34865
+ if (resource.type === "function") {
34866
+ const codeFile = resource.files.find((path) => path.endsWith(".ts"));
34867
+ const metaFile = resource.files.find(
34868
+ (path) => path.endsWith(".meta.json")
34869
+ );
34870
+ const code = codeFile == null ? null : readWorkingFile(root, codeFile);
34871
+ const meta = metaFile == null ? null : readWorkingFile(root, metaFile);
34872
+ if (codeFile != null && code != null) {
34873
+ const parsedMeta = (() => {
34874
+ try {
34875
+ return JSON.parse(meta ?? "null");
34876
+ } catch {
34877
+ return null;
34878
+ }
34879
+ })();
34880
+ lintTargets.push({
34881
+ label: codeFile,
34882
+ code,
34883
+ surface: "function",
34884
+ functionOutputSchema: parsedMeta != null && typeof parsedMeta === "object" && "outputSchema" in parsedMeta && typeof parsedMeta.outputSchema === "string" ? parsedMeta.outputSchema : void 0
34885
+ });
34886
+ }
34887
+ }
34736
34888
  if (resource.type === "automation") {
34737
34889
  const docFile = resource.files.find((f) => f.endsWith(".json"));
34738
34890
  const raw2 = docFile == null ? null : readWorkingFile(root, docFile);
@@ -34744,6 +34896,25 @@ var runReview = async () => {
34744
34896
  (f) => `${docFile} elements.${f}`
34745
34897
  )
34746
34898
  );
34899
+ const nodes = Array.isArray(doc.elements) ? [...doc.elements] : [];
34900
+ for (const node of nodes) {
34901
+ if (node == null || typeof node !== "object") continue;
34902
+ const nodeRecord = node;
34903
+ if (Array.isArray(nodeRecord.elements)) {
34904
+ nodes.push(...nodeRecord.elements);
34905
+ }
34906
+ const inputs = nodeRecord.inputs;
34907
+ if (nodeRecord.nodeTypeId !== "TYPESCRIPT_CODE" || inputs == null || typeof inputs !== "object") {
34908
+ continue;
34909
+ }
34910
+ const code = inputs.code;
34911
+ if (typeof code !== "string") continue;
34912
+ lintTargets.push({
34913
+ label: `${docFile} ${typeof nodeRecord.variableId === "string" ? nodeRecord.variableId : "unknown node"}`,
34914
+ code,
34915
+ surface: "automationCode"
34916
+ });
34917
+ }
34747
34918
  } catch {
34748
34919
  }
34749
34920
  }
@@ -34753,6 +34924,47 @@ var runReview = async () => {
34753
34924
  process.stdout.write("Nothing modified locally \u2014 nothing to review.\n");
34754
34925
  return;
34755
34926
  }
34927
+ for (const target of lintTargets) {
34928
+ try {
34929
+ const response = await v2Fetch("/v2/architect/lint", {
34930
+ method: "POST",
34931
+ headers: { "Content-Type": "application/json" },
34932
+ body: JSON.stringify({
34933
+ code: target.code,
34934
+ surface: target.surface,
34935
+ workspaceId: manifest.workspaceId,
34936
+ functionOutputSchema: target.functionOutputSchema
34937
+ })
34938
+ });
34939
+ const responseText = await response.text();
34940
+ if (!response.ok) {
34941
+ throw new Error(
34942
+ `${target.label}: HTTP ${String(response.status)}${responseText ? ` \u2014 ${responseText}` : ""}`
34943
+ );
34944
+ }
34945
+ const parsed = lintResponseSchema.safeParse(
34946
+ (() => {
34947
+ try {
34948
+ return JSON.parse(responseText);
34949
+ } catch {
34950
+ return null;
34951
+ }
34952
+ })()
34953
+ );
34954
+ if (!parsed.success) {
34955
+ throw new Error(`${target.label}: invalid response from lint service`);
34956
+ }
34957
+ findings2.push(
34958
+ ...parsed.data.diagnostics.map(
34959
+ (diagnostic) => `${target.label}:${String(diagnostic.line)}:${String(diagnostic.column)}: ${diagnostic.severity}: ${diagnostic.message}${diagnostic.ruleId == null ? "" : ` (${diagnostic.ruleId})`}`
34960
+ )
34961
+ );
34962
+ } catch (error) {
34963
+ findings2.push(
34964
+ `${target.label}: remote lint could not run \u2014 ${error instanceof Error ? error.message : String(error)}`
34965
+ );
34966
+ }
34967
+ }
34756
34968
  const guides = ["workflow", ...touchedTypes].map((t) => t === "webPage" || t === "printablePage" ? "pages" : t).map((t) => t === "automation" ? "automations" : t).map((t) => t === "function" ? "functions" : t);
34757
34969
  const guideDocs = [...new Set(guides)].map((t) => BEST_PRACTICES[t]).filter((d) => d != null);
34758
34970
  process.stdout.write(
@@ -35410,14 +35622,17 @@ Workspace sync (edit resources as local files):
35410
35622
  overai outline <filter>[@ref] Top-level declarations with their line
35411
35623
  ranges and doc comments
35412
35624
  overai pull Fast-forward clean resources to remote
35413
- overai push [filter] -m <msg> [--deploy] [--force]
35625
+ overai push [filter] -m <msg> [--deploy] [--force] [--allow-breaking]
35414
35626
  Create new versions from local edits
35415
- overai deploy [filter | --all] Point deployed versions at pushed ones
35627
+ overai deploy [filter | --all] Point deployed versions at pushed ones;
35628
+ a function whose schema change breaks
35629
+ deployed callers is skipped unless
35630
+ --allow-breaking
35416
35631
  overai restore <filter | --all> Discard local edits (back to base)
35417
35632
  overai open [filter | id] Open a resource (or the workspace) in the webapp
35418
35633
  overai dev <filter> [--port N] [--open]
35419
35634
  Live-preview a web page from local files
35420
- overai review Validation + best-practice review packet
35635
+ overai review Local checks + authenticated TypeScript lint + guides
35421
35636
  overai best-practices [topic] Print bundled guides
35422
35637
  overai report bug|feature "<details>"
35423
35638
  Send product feedback (use - for stdin)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overaihq/cli",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Create, edit, deploy, and run OverAI workspace resources from the command line",
5
5
  "type": "module",
6
6
  "homepage": "https://overai.com",
@@ -30,7 +30,7 @@
30
30
  "lint": "echo 'Run lint from the root of the repo'",
31
31
  "ts-check": "tsc --noEmit",
32
32
  "build": "tsup",
33
- "build:watch": "tsc -w",
33
+ "build:watch": "tsup --watch",
34
34
  "start": "node dist/index.js",
35
35
  "test": "vitest",
36
36
  "test:localhost": "vitest --run --config vitest.localhost.config.js"