@elevasis/sdk 0.5.14 → 0.5.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.
package/dist/cli.cjs CHANGED
@@ -40164,6 +40164,12 @@ var DOMAIN_MAP = {
40164
40164
  [DOMAINS.DIAGNOSTIC]: DIAGNOSTIC_DOMAIN
40165
40165
  };
40166
40166
 
40167
+ // ../core/src/platform/registry/reserved.ts
40168
+ var RESERVED_RESOURCE_IDS = /* @__PURE__ */ new Set(["command-center-assistant"]);
40169
+ function isReservedResourceId(resourceId) {
40170
+ return RESERVED_RESOURCE_IDS.has(resourceId);
40171
+ }
40172
+
40167
40173
  // ../core/src/execution/engine/base/errors.ts
40168
40174
  var ExecutionError = class extends Error {
40169
40175
  /**
@@ -43363,6 +43369,13 @@ var ResourceRegistry = class {
43363
43369
  }
43364
43370
  seen.add(id);
43365
43371
  }
43372
+ for (const id of incomingIds) {
43373
+ if (isReservedResourceId(id)) {
43374
+ throw new Error(
43375
+ `Resource ID '${id}' is reserved for platform use. External deployments cannot use reserved resource IDs.`
43376
+ );
43377
+ }
43378
+ }
43366
43379
  if (this.isRemote(orgName)) {
43367
43380
  this.unregisterOrganization(orgName);
43368
43381
  }
@@ -43395,6 +43408,46 @@ var ResourceRegistry = class {
43395
43408
  }
43396
43409
  this.serializedCache.set(orgName, serializeOrganization(this.registry[orgName]));
43397
43410
  }
43411
+ /**
43412
+ * Register built-in platform resources (static, local execution)
43413
+ *
43414
+ * Unlike registerOrganization(), these resources:
43415
+ * - Do NOT have remote config (execute in-process, not in worker threads)
43416
+ * - Are NOT removed by unregisterOrganization() (persist across redeployments)
43417
+ * - Use reserved resource IDs that external deployments cannot claim
43418
+ *
43419
+ * @param orgName - Organization name
43420
+ * @param org - Resource definitions with real handlers (not stubs)
43421
+ */
43422
+ registerStaticResources(orgName, org) {
43423
+ const incomingWorkflowIds = (org.workflows ?? []).map((w) => w.config.resourceId);
43424
+ const incomingAgentIds = (org.agents ?? []).map((a) => a.config.resourceId);
43425
+ const incomingIds = [...incomingWorkflowIds, ...incomingAgentIds];
43426
+ const seen = /* @__PURE__ */ new Set();
43427
+ for (const id of incomingIds) {
43428
+ if (seen.has(id)) {
43429
+ throw new Error(`Duplicate resource ID '${id}' in static resources.`);
43430
+ }
43431
+ seen.add(id);
43432
+ }
43433
+ const existingOrg = this.registry[orgName];
43434
+ if (existingOrg) {
43435
+ const existingWorkflowIds = new Set((existingOrg.workflows ?? []).map((w) => w.config.resourceId));
43436
+ const existingAgentIds = new Set((existingOrg.agents ?? []).map((a) => a.config.resourceId));
43437
+ for (const id of incomingIds) {
43438
+ if (existingWorkflowIds.has(id) || existingAgentIds.has(id)) {
43439
+ throw new Error(`Static resource '${id}' conflicts with existing resource in '${orgName}'.`);
43440
+ }
43441
+ }
43442
+ }
43443
+ if (existingOrg) {
43444
+ existingOrg.workflows = [...existingOrg.workflows ?? [], ...org.workflows ?? []];
43445
+ existingOrg.agents = [...existingOrg.agents ?? [], ...org.agents ?? []];
43446
+ } else {
43447
+ this.registry[orgName] = org;
43448
+ }
43449
+ this.serializedCache.set(orgName, serializeOrganization(this.registry[orgName]));
43450
+ }
43398
43451
  /**
43399
43452
  * Unregister runtime-registered resources for an organization
43400
43453
  *
@@ -43798,7 +43851,7 @@ async function apiDelete(endpoint, apiUrl = resolveApiUrl()) {
43798
43851
  // package.json
43799
43852
  var package_default = {
43800
43853
  name: "@elevasis/sdk",
43801
- version: "0.5.14",
43854
+ version: "0.5.15",
43802
43855
  description: "SDK for building Elevasis organization resources",
43803
43856
  type: "module",
43804
43857
  bin: {
@@ -43884,9 +43937,7 @@ async function scanDocumentation() {
43884
43937
  const raw = await (0, import_promises.readFile)(fullPath, "utf-8");
43885
43938
  const fileSize = Buffer.byteLength(raw, "utf-8");
43886
43939
  if (fileSize > 100 * 1024) {
43887
- throw new Error(
43888
- `Documentation file exceeds 100KB: docs/${relPath} (${Math.round(fileSize / 1024)}KB)`
43889
- );
43940
+ throw new Error(`Documentation file exceeds 100KB: docs/${relPath} (${Math.round(fileSize / 1024)}KB)`);
43890
43941
  }
43891
43942
  totalSize += fileSize;
43892
43943
  if (totalSize > 1024 * 1024) {
@@ -43939,7 +43990,9 @@ async function generateResourceMap(org) {
43939
43990
  );
43940
43991
  for (const w of workflows) {
43941
43992
  const desc = escapeMdx(w.config.description);
43942
- lines.push(`| \`${w.config.resourceId}\` | ${escapeMdx(w.config.name)} | ${w.config.version} | ${w.config.status} | ${desc} |`);
43993
+ lines.push(
43994
+ `| \`${w.config.resourceId}\` | ${escapeMdx(w.config.name)} | ${w.config.version} | ${w.config.status} | ${desc} |`
43995
+ );
43943
43996
  }
43944
43997
  lines.push("");
43945
43998
  }
@@ -43952,11 +44005,16 @@ async function generateResourceMap(org) {
43952
44005
  );
43953
44006
  for (const a of agents) {
43954
44007
  const desc = escapeMdx(a.config.description);
43955
- lines.push(`| \`${a.config.resourceId}\` | ${escapeMdx(a.config.name)} | ${a.config.version} | ${a.config.status} | ${desc} |`);
44008
+ lines.push(
44009
+ `| \`${a.config.resourceId}\` | ${escapeMdx(a.config.name)} | ${a.config.version} | ${a.config.status} | ${desc} |`
44010
+ );
43956
44011
  }
43957
44012
  lines.push("");
43958
44013
  }
43959
- lines.push(`**Total:** ${workflows.length + agents.length} resources (${workflows.length} workflows, ${agents.length} agents)`, "");
44014
+ lines.push(
44015
+ `**Total:** ${workflows.length + agents.length} resources (${workflows.length} workflows, ${agents.length} agents)`,
44016
+ ""
44017
+ );
43960
44018
  await (0, import_promises.mkdir)((0, import_path.resolve)("docs"), { recursive: true });
43961
44019
  await (0, import_promises.writeFile)((0, import_path.resolve)("docs/resource-map.mdx"), lines.join("\n"), "utf-8");
43962
44020
  }
@@ -44020,8 +44078,12 @@ async function generateProjectMap(org) {
44020
44078
  if (resourceCount === 0) {
44021
44079
  types = "(utilities)";
44022
44080
  } else {
44023
- const wCount = workflows.filter((w) => Array.isArray(w.config.domains) && w.config.domains.includes(domainName)).length;
44024
- const aCount = agents.filter((a) => Array.isArray(a.config.domains) && a.config.domains.includes(domainName)).length;
44081
+ const wCount = workflows.filter(
44082
+ (w) => Array.isArray(w.config.domains) && w.config.domains.includes(domainName)
44083
+ ).length;
44084
+ const aCount = agents.filter(
44085
+ (a) => Array.isArray(a.config.domains) && a.config.domains.includes(domainName)
44086
+ ).length;
44025
44087
  const parts = [];
44026
44088
  if (wCount > 0) parts.push(`${wCount} workflow${wCount !== 1 ? "s" : ""}`);
44027
44089
  if (aCount > 0) parts.push(`${aCount} agent${aCount !== 1 ? "s" : ""}`);
@@ -44133,10 +44195,7 @@ async function generateProjectMap(org) {
44133
44195
  if (categories.length === 0) {
44134
44196
  lines.push("SDK reference found but no categories parsed.", "");
44135
44197
  } else {
44136
- lines.push(
44137
- "| Category | Files | Covers |",
44138
- "| --- | --- | --- |"
44139
- );
44198
+ lines.push("| Category | Files | Covers |", "| --- | --- | --- |");
44140
44199
  for (const cat of categories) {
44141
44200
  const covers = cat.titles.slice(0, 5).join(", ") + (cat.titles.length > 5 ? ", ..." : "");
44142
44201
  lines.push(`| ${escapeMdx(cat.name)} | ${cat.count} | ${escapeMdx(covers)} |`);
@@ -44154,10 +44213,7 @@ async function generateProjectMap(org) {
44154
44213
  if (cmdFiles.length === 0) {
44155
44214
  lines.push("No commands found.", "");
44156
44215
  } else {
44157
- lines.push(
44158
- "| Command | File | Purpose |",
44159
- "| --- | --- | --- |"
44160
- );
44216
+ lines.push("| Command | File | Purpose |", "| --- | --- | --- |");
44161
44217
  for (const f of cmdFiles) {
44162
44218
  const cmdName = f.name.replace(/\.md$/, "");
44163
44219
  let purpose = "";
@@ -44186,10 +44242,7 @@ async function generateProjectMap(org) {
44186
44242
  if (ruleFiles.length === 0) {
44187
44243
  lines.push("No rules found.", "");
44188
44244
  } else {
44189
- lines.push(
44190
- "| Rule | File | Scope |",
44191
- "| --- | --- | --- |"
44192
- );
44245
+ lines.push("| Rule | File | Scope |", "| --- | --- | --- |");
44193
44246
  for (const f of ruleFiles) {
44194
44247
  const ruleName = f.name.replace(/\.md$/, "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
44195
44248
  let scope = "";
@@ -44220,10 +44273,7 @@ async function generateProjectMap(org) {
44220
44273
  if (skillDirs.length === 0) {
44221
44274
  lines.push("No skills found.", "");
44222
44275
  } else {
44223
- lines.push(
44224
- "| Skill | File | Trigger |",
44225
- "| --- | --- | --- |"
44226
- );
44276
+ lines.push("| Skill | File | Trigger |", "| --- | --- | --- |");
44227
44277
  for (const d of skillDirs) {
44228
44278
  const skillFile = (0, import_path.resolve)(".claude/skills", d.name, "SKILL.md");
44229
44279
  let trigger = "";
@@ -44279,10 +44329,7 @@ async function generateProjectMap(org) {
44279
44329
  if (memoryFiles.length === 0) {
44280
44330
  lines.push("No memory files found.", "");
44281
44331
  } else {
44282
- lines.push(
44283
- "| File | Purpose | Last Modified |",
44284
- "| --- | --- | --- |"
44285
- );
44332
+ lines.push("| File | Purpose | Last Modified |", "| --- | --- | --- |");
44286
44333
  for (const m of memoryFiles) {
44287
44334
  lines.push(`| .claude/memory/${m.rel} | ${escapeMdx(m.purpose)} | ${m.mtime} |`);
44288
44335
  }
@@ -44314,250 +44361,293 @@ async function generateProjectMap(org) {
44314
44361
  await (0, import_promises.mkdir)((0, import_path.resolve)("docs"), { recursive: true });
44315
44362
  await (0, import_promises.writeFile)((0, import_path.resolve)("docs/project-map.mdx"), lines.join("\n"), "utf-8");
44316
44363
  }
44364
+ async function generateNavigationMap(docs) {
44365
+ const excludedFiles = /* @__PURE__ */ new Set(["docs/navigation-map.mdx", "docs/project-map.mdx", "docs/resource-map.mdx"]);
44366
+ const filtered = docs.filter((doc) => !excludedFiles.has(doc.path)).sort((a, b) => {
44367
+ const orderA = a.frontmatter.order ?? 9999;
44368
+ const orderB = b.frontmatter.order ?? 9999;
44369
+ if (orderA !== orderB) return orderA - orderB;
44370
+ return a.frontmatter.title.localeCompare(b.frontmatter.title);
44371
+ });
44372
+ const lines = [
44373
+ "---",
44374
+ "title: Navigation Map",
44375
+ "description: Auto-generated table of contents for all documentation (updated on each deploy)",
44376
+ "order: 997",
44377
+ "---",
44378
+ "",
44379
+ "# Navigation Map",
44380
+ "",
44381
+ "> Auto-generated by `elevasis-sdk deploy`. Do not edit manually.",
44382
+ "",
44383
+ "| Title | Description | Path |",
44384
+ "| --- | --- | --- |"
44385
+ ];
44386
+ for (const doc of filtered) {
44387
+ lines.push(`| ${escapeMdx(doc.frontmatter.title)} | ${escapeMdx(doc.frontmatter.description)} | ${doc.path} |`);
44388
+ }
44389
+ lines.push("");
44390
+ await (0, import_promises.mkdir)((0, import_path.resolve)("docs"), { recursive: true });
44391
+ await (0, import_promises.writeFile)((0, import_path.resolve)("docs/navigation-map.mdx"), lines.join("\n"), "utf-8");
44392
+ }
44317
44393
  function registerDeployCommand(program3) {
44318
- program3.command("deploy").description("Validate, bundle, upload, and deploy project resources\n Example: elevasis-sdk deploy --api-url http://localhost:5170").option("--api-url <url>", "API URL").option("--entry <path>", "Path to entry file (default: ./src/index.ts)").option("--prod", "Deploy to production (overrides NODE_ENV=development)").action(wrapAction("deploy", async (options2) => {
44319
- const startTime = Date.now();
44320
- const apiUrl = resolveApiUrl(options2.apiUrl, options2.prod);
44321
- const env2 = resolveEnvironment(options2.prod);
44322
- const entryPath = options2.entry ?? "./src/index.ts";
44323
- const authSpinner = ora("Authenticating...").start();
44324
- let orgName;
44325
- try {
44326
- const me = await apiGet("/api/external/me", apiUrl);
44327
- orgName = me.organizationName;
44328
- authSpinner.succeed(
44329
- source_default.green("Authenticating...") + source_default.white(" done") + source_default.gray(` (${orgName})`)
44330
- );
44331
- } catch (error46) {
44332
- authSpinner.fail(source_default.red("Authentication failed"));
44333
- const errMsg = error46 instanceof Error ? error46.message : String(error46);
44334
- if (errMsg.includes("401") || errMsg.toLowerCase().includes("unauthorized")) {
44335
- const keyVar = !options2.prod && process.env.NODE_ENV === "development" && process.env.ELEVASIS_PLATFORM_KEY_DEV ? "ELEVASIS_PLATFORM_KEY_DEV" : "ELEVASIS_PLATFORM_KEY";
44336
- console.error(source_default.red(" Invalid API key."));
44337
- console.error(source_default.gray(` Your ${keyVar} was rejected by the server.`));
44338
- console.error(source_default.gray(" Check your .env file and verify the key in the Elevasis dashboard."));
44339
- } else {
44340
- console.error(source_default.gray(" Check your API key and API URL."));
44394
+ program3.command("deploy").description(
44395
+ "Validate, bundle, upload, and deploy project resources\n Example: elevasis-sdk deploy --api-url http://localhost:5170"
44396
+ ).option("--api-url <url>", "API URL").option("--entry <path>", "Path to entry file (default: ./src/index.ts)").option("--prod", "Deploy to production (overrides NODE_ENV=development)").action(
44397
+ wrapAction("deploy", async (options2) => {
44398
+ const startTime = Date.now();
44399
+ const apiUrl = resolveApiUrl(options2.apiUrl, options2.prod);
44400
+ const env2 = resolveEnvironment(options2.prod);
44401
+ const entryPath = options2.entry ?? "./src/index.ts";
44402
+ const authSpinner = ora("Authenticating...").start();
44403
+ let orgName;
44404
+ try {
44405
+ const me = await apiGet("/api/external/me", apiUrl);
44406
+ orgName = me.organizationName;
44407
+ authSpinner.succeed(
44408
+ source_default.green("Authenticating...") + source_default.white(" done") + source_default.gray(` (${orgName})`)
44409
+ );
44410
+ } catch (error46) {
44411
+ authSpinner.fail(source_default.red("Authentication failed"));
44412
+ const errMsg = error46 instanceof Error ? error46.message : String(error46);
44413
+ if (errMsg.includes("401") || errMsg.toLowerCase().includes("unauthorized")) {
44414
+ const keyVar = !options2.prod && process.env.NODE_ENV === "development" && process.env.ELEVASIS_PLATFORM_KEY_DEV ? "ELEVASIS_PLATFORM_KEY_DEV" : "ELEVASIS_PLATFORM_KEY";
44415
+ console.error(source_default.red(" Invalid API key."));
44416
+ console.error(source_default.gray(` Your ${keyVar} was rejected by the server.`));
44417
+ console.error(source_default.gray(" Check your .env file and verify the key in the Elevasis dashboard."));
44418
+ } else {
44419
+ console.error(source_default.gray(" Check your API key and API URL."));
44420
+ }
44421
+ throw error46;
44341
44422
  }
44342
- throw error46;
44343
- }
44344
- const validateSpinner = ora("Validating...").start();
44345
- let org;
44346
- try {
44347
- const jiti = (0, import_jiti.createJiti)(import_meta.url);
44348
- const entryModule = await jiti.import((0, import_path.resolve)(entryPath));
44349
- org = entryModule.default;
44350
- if (!org) {
44351
- validateSpinner.fail("Invalid entry: no default export found");
44352
- console.error(source_default.gray(` Entry file: ${(0, import_path.resolve)(entryPath)}`));
44353
- throw new Error("Invalid entry: no default export found");
44423
+ const validateSpinner = ora("Validating...").start();
44424
+ let org;
44425
+ try {
44426
+ const jiti = (0, import_jiti.createJiti)(import_meta.url);
44427
+ const entryModule = await jiti.import((0, import_path.resolve)(entryPath));
44428
+ org = entryModule.default;
44429
+ if (!org) {
44430
+ validateSpinner.fail("Invalid entry: no default export found");
44431
+ console.error(source_default.gray(` Entry file: ${(0, import_path.resolve)(entryPath)}`));
44432
+ throw new Error("Invalid entry: no default export found");
44433
+ }
44434
+ new ResourceRegistry({ [orgName]: org });
44435
+ const workflowCount = org.workflows?.length ?? 0;
44436
+ const agentCount = org.agents?.length ?? 0;
44437
+ const totalCount = workflowCount + agentCount;
44438
+ validateSpinner.succeed(
44439
+ source_default.green("Validating...") + source_default.white(" done") + source_default.gray(` (${totalCount} resource${totalCount !== 1 ? "s" : ""}, 0 errors)`)
44440
+ );
44441
+ console.log("");
44442
+ console.log(source_default.gray(` Org: ${orgName}`));
44443
+ console.log(source_default.gray(` Target: ${apiUrl} (${env2})`));
44444
+ console.log("");
44445
+ for (const w of org.workflows ?? []) {
44446
+ console.log(source_default.gray(` workflow ${source_default.white(w.config.resourceId)} v${w.config.version}`));
44447
+ }
44448
+ for (const a of org.agents ?? []) {
44449
+ console.log(source_default.gray(` agent ${source_default.white(a.config.resourceId)} v${a.config.version}`));
44450
+ }
44451
+ console.log("");
44452
+ } catch (error46) {
44453
+ if (error46 instanceof RegistryValidationError) {
44454
+ validateSpinner.fail(source_default.red("Validation failed"));
44455
+ console.error("");
44456
+ console.error(source_default.red(` ERROR ${error46.message}`));
44457
+ if (error46.resourceId) {
44458
+ console.error(source_default.gray(` Resource: ${error46.resourceId}`));
44459
+ }
44460
+ console.error("");
44461
+ console.error(source_default.gray(" Deploy aborted."));
44462
+ }
44463
+ throw error46;
44354
44464
  }
44355
- new ResourceRegistry({ [orgName]: org });
44356
- const workflowCount = org.workflows?.length ?? 0;
44357
- const agentCount = org.agents?.length ?? 0;
44358
- const totalCount = workflowCount + agentCount;
44359
- validateSpinner.succeed(
44360
- source_default.green("Validating...") + source_default.white(" done") + source_default.gray(` (${totalCount} resource${totalCount !== 1 ? "s" : ""}, 0 errors)`)
44361
- );
44362
- console.log("");
44363
- console.log(source_default.gray(` Org: ${orgName}`));
44364
- console.log(source_default.gray(` Target: ${apiUrl} (${env2})`));
44365
- console.log("");
44366
- for (const w of org.workflows ?? []) {
44367
- console.log(source_default.gray(` workflow ${source_default.white(w.config.resourceId)} v${w.config.version}`));
44465
+ await generateResourceMap(org);
44466
+ await generateProjectMap(org);
44467
+ let documentation = await scanDocumentation();
44468
+ if (documentation) {
44469
+ await generateNavigationMap(documentation);
44470
+ documentation = await scanDocumentation();
44471
+ console.log(
44472
+ source_default.gray(
44473
+ ` docs ${source_default.white(String(documentation.length))} file${documentation.length !== 1 ? "s" : ""}`
44474
+ )
44475
+ );
44368
44476
  }
44369
- for (const a of org.agents ?? []) {
44370
- console.log(source_default.gray(` agent ${source_default.white(a.config.resourceId)} v${a.config.version}`));
44477
+ const triggerCount = org.triggers?.length ?? 0;
44478
+ const integrationCount = org.integrations?.length ?? 0;
44479
+ const checkpointCount = org.humanCheckpoints?.length ?? 0;
44480
+ if (triggerCount > 0) console.log(source_default.gray(` triggers ${source_default.white(String(triggerCount))}`));
44481
+ if (integrationCount > 0) console.log(source_default.gray(` integrations ${source_default.white(String(integrationCount))}`));
44482
+ if (checkpointCount > 0) console.log(source_default.gray(` checkpoints ${source_default.white(String(checkpointCount))}`));
44483
+ const relationshipCount = org.relationships ? Object.keys(org.relationships).length : 0;
44484
+ if (relationshipCount > 0) {
44485
+ console.log(
44486
+ source_default.gray(
44487
+ ` rels ${source_default.white(String(relationshipCount))} resource${relationshipCount !== 1 ? "s" : ""}`
44488
+ )
44489
+ );
44371
44490
  }
44372
- console.log("");
44373
- } catch (error46) {
44374
- if (error46 instanceof RegistryValidationError) {
44375
- validateSpinner.fail(source_default.red("Validation failed"));
44376
- console.error("");
44377
- console.error(source_default.red(` ERROR ${error46.message}`));
44378
- if (error46.resourceId) {
44379
- console.error(source_default.gray(` Resource: ${error46.resourceId}`));
44491
+ const schemaWarnings = [];
44492
+ const workflows = (org.workflows ?? []).map((w) => {
44493
+ const meta = {
44494
+ resourceId: w.config.resourceId,
44495
+ name: w.config.name,
44496
+ version: w.config.version,
44497
+ status: w.config.status,
44498
+ description: w.config.description,
44499
+ domains: w.config.domains
44500
+ };
44501
+ if (w.contract.inputSchema) {
44502
+ try {
44503
+ meta.inputSchema = external_exports.toJSONSchema(w.contract.inputSchema);
44504
+ } catch {
44505
+ schemaWarnings.push(`${w.config.resourceId}: inputSchema could not be serialized`);
44506
+ }
44380
44507
  }
44381
- console.error("");
44382
- console.error(source_default.gray(" Deploy aborted."));
44383
- }
44384
- throw error46;
44385
- }
44386
- await generateResourceMap(org);
44387
- await generateProjectMap(org);
44388
- const documentation = await scanDocumentation();
44389
- if (documentation) {
44390
- console.log(source_default.gray(` docs ${source_default.white(String(documentation.length))} file${documentation.length !== 1 ? "s" : ""}`));
44391
- }
44392
- const triggerCount = org.triggers?.length ?? 0;
44393
- const integrationCount = org.integrations?.length ?? 0;
44394
- const checkpointCount = org.humanCheckpoints?.length ?? 0;
44395
- if (triggerCount > 0) console.log(source_default.gray(` triggers ${source_default.white(String(triggerCount))}`));
44396
- if (integrationCount > 0) console.log(source_default.gray(` integrations ${source_default.white(String(integrationCount))}`));
44397
- if (checkpointCount > 0) console.log(source_default.gray(` checkpoints ${source_default.white(String(checkpointCount))}`));
44398
- const relationshipCount = org.relationships ? Object.keys(org.relationships).length : 0;
44399
- if (relationshipCount > 0) {
44400
- console.log(source_default.gray(` rels ${source_default.white(String(relationshipCount))} resource${relationshipCount !== 1 ? "s" : ""}`));
44401
- }
44402
- const schemaWarnings = [];
44403
- const workflows = (org.workflows ?? []).map((w) => {
44404
- const meta = {
44405
- resourceId: w.config.resourceId,
44406
- name: w.config.name,
44407
- version: w.config.version,
44408
- status: w.config.status,
44409
- description: w.config.description,
44410
- domains: w.config.domains
44411
- };
44412
- if (w.contract.inputSchema) {
44413
- try {
44414
- meta.inputSchema = external_exports.toJSONSchema(w.contract.inputSchema);
44415
- } catch {
44416
- schemaWarnings.push(`${w.config.resourceId}: inputSchema could not be serialized`);
44508
+ if (w.contract.outputSchema) {
44509
+ try {
44510
+ meta.outputSchema = external_exports.toJSONSchema(w.contract.outputSchema);
44511
+ } catch {
44512
+ schemaWarnings.push(`${w.config.resourceId}: outputSchema could not be serialized`);
44513
+ }
44417
44514
  }
44418
- }
44419
- if (w.contract.outputSchema) {
44420
- try {
44421
- meta.outputSchema = external_exports.toJSONSchema(w.contract.outputSchema);
44422
- } catch {
44423
- schemaWarnings.push(`${w.config.resourceId}: outputSchema could not be serialized`);
44515
+ return meta;
44516
+ });
44517
+ const agents = (org.agents ?? []).map((a) => {
44518
+ const meta = {
44519
+ resourceId: a.config.resourceId,
44520
+ name: a.config.name,
44521
+ version: a.config.version,
44522
+ status: a.config.status,
44523
+ description: a.config.description,
44524
+ domains: a.config.domains
44525
+ };
44526
+ if (a.contract.inputSchema) {
44527
+ try {
44528
+ meta.inputSchema = external_exports.toJSONSchema(a.contract.inputSchema);
44529
+ } catch {
44530
+ schemaWarnings.push(`${a.config.resourceId}: inputSchema could not be serialized`);
44531
+ }
44424
44532
  }
44425
- }
44426
- return meta;
44427
- });
44428
- const agents = (org.agents ?? []).map((a) => {
44429
- const meta = {
44430
- resourceId: a.config.resourceId,
44431
- name: a.config.name,
44432
- version: a.config.version,
44433
- status: a.config.status,
44434
- description: a.config.description,
44435
- domains: a.config.domains
44436
- };
44437
- if (a.contract.inputSchema) {
44438
- try {
44439
- meta.inputSchema = external_exports.toJSONSchema(a.contract.inputSchema);
44440
- } catch {
44441
- schemaWarnings.push(`${a.config.resourceId}: inputSchema could not be serialized`);
44533
+ if (a.contract.outputSchema) {
44534
+ try {
44535
+ meta.outputSchema = external_exports.toJSONSchema(a.contract.outputSchema);
44536
+ } catch {
44537
+ schemaWarnings.push(`${a.config.resourceId}: outputSchema could not be serialized`);
44538
+ }
44442
44539
  }
44443
- }
44444
- if (a.contract.outputSchema) {
44445
- try {
44446
- meta.outputSchema = external_exports.toJSONSchema(a.contract.outputSchema);
44447
- } catch {
44448
- schemaWarnings.push(`${a.config.resourceId}: outputSchema could not be serialized`);
44540
+ return meta;
44541
+ });
44542
+ if (schemaWarnings.length > 0) {
44543
+ for (const warning of schemaWarnings) {
44544
+ console.log(source_default.yellow(` warn ${warning}`));
44449
44545
  }
44546
+ console.log(source_default.gray(" Schemas will be unavailable on the platform for these resources."));
44547
+ console.log("");
44450
44548
  }
44451
- return meta;
44452
- });
44453
- if (schemaWarnings.length > 0) {
44454
- for (const warning of schemaWarnings) {
44455
- console.log(source_default.yellow(` warn ${warning}`));
44456
- }
44457
- console.log(source_default.gray(" Schemas will be unavailable on the platform for these resources."));
44458
- console.log("");
44459
- }
44460
- const bundleSpinner = ora("Bundling...").start();
44461
- const wrapperPath = (0, import_path.resolve)("__elevasis_worker.ts");
44462
- const bundleOutfile = (0, import_path.resolve)("dist/bundle.js");
44463
- try {
44464
- const entryImport = entryPath.replace(/\.ts$/, ".js");
44465
- const wrapperContent = `import org from ${JSON.stringify(entryImport)}
44549
+ const bundleSpinner = ora("Bundling...").start();
44550
+ const wrapperPath = (0, import_path.resolve)("__elevasis_worker.ts");
44551
+ const bundleOutfile = (0, import_path.resolve)("dist/bundle.js");
44552
+ try {
44553
+ const entryImport = entryPath.replace(/\.ts$/, ".js");
44554
+ const wrapperContent = `import org from ${JSON.stringify(entryImport)}
44466
44555
  import { startWorker } from '@elevasis/sdk/worker'
44467
44556
  startWorker(org)
44468
44557
  `;
44469
- await (0, import_promises.writeFile)(wrapperPath, wrapperContent, "utf-8");
44470
- await (0, import_promises.mkdir)((0, import_path.resolve)("dist"), { recursive: true });
44471
- await esbuild.build({
44472
- entryPoints: [wrapperPath],
44473
- bundle: true,
44474
- platform: "node",
44475
- format: "cjs",
44476
- outfile: bundleOutfile
44477
- });
44478
- await (0, import_promises.unlink)(wrapperPath);
44479
- const bundleBuffer = await (0, import_promises.readFile)(bundleOutfile);
44480
- const bundleSizeKB = Math.round(bundleBuffer.length / 1024);
44481
- bundleSpinner.succeed(
44482
- source_default.green("Bundling...") + source_default.white(" done") + source_default.gray(` (${bundleSizeKB} KB)`)
44483
- );
44484
- } catch (error46) {
44485
- try {
44558
+ await (0, import_promises.writeFile)(wrapperPath, wrapperContent, "utf-8");
44559
+ await (0, import_promises.mkdir)((0, import_path.resolve)("dist"), { recursive: true });
44560
+ await esbuild.build({
44561
+ entryPoints: [wrapperPath],
44562
+ bundle: true,
44563
+ platform: "node",
44564
+ format: "cjs",
44565
+ outfile: bundleOutfile
44566
+ });
44486
44567
  await (0, import_promises.unlink)(wrapperPath);
44487
- } catch {
44568
+ const bundleBuffer = await (0, import_promises.readFile)(bundleOutfile);
44569
+ const bundleSizeKB = Math.round(bundleBuffer.length / 1024);
44570
+ bundleSpinner.succeed(
44571
+ source_default.green("Bundling...") + source_default.white(" done") + source_default.gray(` (${bundleSizeKB} KB)`)
44572
+ );
44573
+ } catch (error46) {
44574
+ try {
44575
+ await (0, import_promises.unlink)(wrapperPath);
44576
+ } catch {
44577
+ }
44578
+ bundleSpinner.fail(source_default.red("Bundling failed"));
44579
+ throw error46;
44488
44580
  }
44489
- bundleSpinner.fail(source_default.red("Bundling failed"));
44490
- throw error46;
44491
- }
44492
- const uploadSpinner = ora("Uploading...").start();
44493
- try {
44494
- const bundleBuffer = await (0, import_promises.readFile)(bundleOutfile);
44495
- const apiKey = resolveApiKey(options2.prod);
44496
- if (!apiKey) {
44497
- uploadSpinner.fail(source_default.red("Missing API key environment variable"));
44498
- console.error(source_default.gray(" Set it in your .env file or shell environment:"));
44499
- if (!options2.prod && process.env.NODE_ENV === "development") {
44500
- console.error(source_default.gray(" ELEVASIS_PLATFORM_KEY_DEV=sk_... (or ELEVASIS_PLATFORM_KEY as fallback)"));
44501
- } else {
44502
- console.error(source_default.gray(" ELEVASIS_PLATFORM_KEY=sk_..."));
44503
- }
44504
- throw new Error("Missing API key environment variable");
44505
- }
44506
- const metadata = {
44507
- sdkVersion: SDK_VERSION,
44508
- mode: env2,
44509
- resources: { workflows, agents },
44510
- ...documentation ? { documentation } : {},
44511
- ...org.relationships ? { relationships: org.relationships } : {}
44512
- };
44513
- const form = new FormData();
44514
- form.append("bundle", new Blob([bundleBuffer]), "bundle.js");
44515
- form.append("metadata", JSON.stringify(metadata));
44516
- const response = await fetch(`${apiUrl}/api/external/deploy`, {
44517
- method: "POST",
44518
- headers: { Authorization: `Bearer ${apiKey}` },
44519
- body: form
44520
- });
44521
- if (!response.ok) {
44522
- const errorText = await response.text();
44523
- uploadSpinner.fail(source_default.red("Upload failed"));
44524
- console.error(source_default.red(` ${response.status}: ${errorText}`));
44525
- throw new Error(`Deploy upload failed (${response.status}): ${errorText}`);
44526
- }
44527
- const result = await response.json();
44528
- uploadSpinner.succeed(
44529
- source_default.green("Uploading...") + source_default.white(" done")
44530
- );
44531
- const totalResources = (org.workflows?.length ?? 0) + (org.agents?.length ?? 0);
44532
- const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
44533
- if (result.status === "active") {
44534
- console.log("");
44535
- console.log(source_default.green.bold(` Deployed! ${totalResources} resource${totalResources !== 1 ? "s" : ""} live.`));
44536
- if (result.deployId) {
44537
- console.log(source_default.gray(` Version: ${result.deployId}`));
44581
+ const uploadSpinner = ora("Uploading...").start();
44582
+ try {
44583
+ const bundleBuffer = await (0, import_promises.readFile)(bundleOutfile);
44584
+ const apiKey = resolveApiKey(options2.prod);
44585
+ if (!apiKey) {
44586
+ uploadSpinner.fail(source_default.red("Missing API key environment variable"));
44587
+ console.error(source_default.gray(" Set it in your .env file or shell environment:"));
44588
+ if (!options2.prod && process.env.NODE_ENV === "development") {
44589
+ console.error(source_default.gray(" ELEVASIS_PLATFORM_KEY_DEV=sk_... (or ELEVASIS_PLATFORM_KEY as fallback)"));
44590
+ } else {
44591
+ console.error(source_default.gray(" ELEVASIS_PLATFORM_KEY=sk_..."));
44592
+ }
44593
+ throw new Error("Missing API key environment variable");
44538
44594
  }
44539
- console.log(source_default.gray(` Duration: ${elapsed}s`));
44540
- } else if (result.status === "failed") {
44541
- console.log("");
44542
- console.log(source_default.red.bold(" Deploy failed."));
44543
- if (result.error) {
44544
- console.error(source_default.red(` ${result.error}`));
44595
+ const metadata = {
44596
+ sdkVersion: SDK_VERSION,
44597
+ mode: env2,
44598
+ resources: { workflows, agents },
44599
+ ...documentation ? { documentation } : {},
44600
+ ...org.relationships ? { relationships: org.relationships } : {}
44601
+ };
44602
+ const form = new FormData();
44603
+ form.append("bundle", new Blob([bundleBuffer]), "bundle.js");
44604
+ form.append("metadata", JSON.stringify(metadata));
44605
+ const response = await fetch(`${apiUrl}/api/external/deploy`, {
44606
+ method: "POST",
44607
+ headers: { Authorization: `Bearer ${apiKey}` },
44608
+ body: form
44609
+ });
44610
+ if (!response.ok) {
44611
+ const errorText = await response.text();
44612
+ uploadSpinner.fail(source_default.red("Upload failed"));
44613
+ console.error(source_default.red(` ${response.status}: ${errorText}`));
44614
+ throw new Error(`Deploy upload failed (${response.status}): ${errorText}`);
44615
+ }
44616
+ const result = await response.json();
44617
+ uploadSpinner.succeed(source_default.green("Uploading...") + source_default.white(" done"));
44618
+ const totalResources = (org.workflows?.length ?? 0) + (org.agents?.length ?? 0);
44619
+ const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
44620
+ if (result.status === "active") {
44621
+ console.log("");
44622
+ console.log(
44623
+ source_default.green.bold(` Deployed! ${totalResources} resource${totalResources !== 1 ? "s" : ""} live.`)
44624
+ );
44625
+ if (result.deployId) {
44626
+ console.log(source_default.gray(` Version: ${result.deployId}`));
44627
+ }
44628
+ console.log(source_default.gray(` Duration: ${elapsed}s`));
44629
+ } else if (result.status === "failed") {
44630
+ console.log("");
44631
+ console.log(source_default.red.bold(" Deploy failed."));
44632
+ if (result.error) {
44633
+ console.error(source_default.red(` ${result.error}`));
44634
+ }
44635
+ throw new Error(`Deploy failed: ${result.error ?? "unknown error"}`);
44636
+ } else {
44637
+ console.log("");
44638
+ console.log(source_default.yellow(` Deploy status: ${result.status ?? "unknown"}`));
44639
+ if (result.deployId) {
44640
+ console.log(source_default.gray(` Version: ${result.deployId}`));
44641
+ }
44545
44642
  }
44546
- throw new Error(`Deploy failed: ${result.error ?? "unknown error"}`);
44547
- } else {
44548
- console.log("");
44549
- console.log(source_default.yellow(` Deploy status: ${result.status ?? "unknown"}`));
44550
- if (result.deployId) {
44551
- console.log(source_default.gray(` Version: ${result.deployId}`));
44643
+ } catch (error46) {
44644
+ if (uploadSpinner.isSpinning) {
44645
+ uploadSpinner.fail(source_default.red("Deploy failed"));
44552
44646
  }
44647
+ throw error46;
44553
44648
  }
44554
- } catch (error46) {
44555
- if (uploadSpinner.isSpinning) {
44556
- uploadSpinner.fail(source_default.red("Deploy failed"));
44557
- }
44558
- throw error46;
44559
- }
44560
- }));
44649
+ })
44650
+ );
44561
44651
  }
44562
44652
 
44563
44653
  // src/cli/commands/check.ts
@@ -45090,7 +45180,7 @@ var import_path3 = require("path");
45090
45180
  var import_promises2 = require("fs/promises");
45091
45181
 
45092
45182
  // src/cli/commands/templates/core/workspace.ts
45093
- var TEMPLATE_VERSION = 27;
45183
+ var TEMPLATE_VERSION = 28;
45094
45184
  function configTemplate() {
45095
45185
  return `import type { ElevasConfig } from '@elevasis/sdk'
45096
45186
 
@@ -45102,44 +45192,52 @@ export default {
45102
45192
  `;
45103
45193
  }
45104
45194
  function packageJsonTemplate(organization) {
45105
- return JSON.stringify({
45106
- name: organization,
45107
- private: true,
45108
- type: "module",
45109
- scripts: {
45110
- "check-types": "tsc --noEmit",
45111
- check: "elevasis-sdk check",
45112
- deploy: "elevasis-sdk deploy"
45113
- },
45114
- dependencies: {
45115
- "@elevasis/sdk": `^${SDK_VERSION}`
45195
+ return JSON.stringify(
45196
+ {
45197
+ name: organization,
45198
+ private: true,
45199
+ type: "module",
45200
+ scripts: {
45201
+ "check-types": "tsc --noEmit",
45202
+ check: "elevasis-sdk check",
45203
+ deploy: "elevasis-sdk deploy"
45204
+ },
45205
+ dependencies: {
45206
+ "@elevasis/sdk": `^${SDK_VERSION}`
45207
+ },
45208
+ devDependencies: {
45209
+ typescript: "5.9.2",
45210
+ zod: "4.1.12"
45211
+ }
45116
45212
  },
45117
- devDependencies: {
45118
- typescript: "5.9.2",
45119
- zod: "4.1.12"
45120
- }
45121
- }, null, 2) + "\n";
45213
+ null,
45214
+ 2
45215
+ ) + "\n";
45122
45216
  }
45123
45217
  function pnpmWorkspaceTemplate() {
45124
45218
  return `packages: []
45125
45219
  `;
45126
45220
  }
45127
45221
  function tsconfigTemplate() {
45128
- return JSON.stringify({
45129
- compilerOptions: {
45130
- target: "ES2022",
45131
- module: "ESNext",
45132
- moduleResolution: "Bundler",
45133
- strict: true,
45134
- esModuleInterop: true,
45135
- skipLibCheck: true,
45136
- forceConsistentCasingInFileNames: true,
45137
- isolatedModules: true,
45138
- outDir: "./dist"
45222
+ return JSON.stringify(
45223
+ {
45224
+ compilerOptions: {
45225
+ target: "ES2022",
45226
+ module: "ESNext",
45227
+ moduleResolution: "Bundler",
45228
+ strict: true,
45229
+ esModuleInterop: true,
45230
+ skipLibCheck: true,
45231
+ forceConsistentCasingInFileNames: true,
45232
+ isolatedModules: true,
45233
+ outDir: "./dist"
45234
+ },
45235
+ include: ["src"],
45236
+ exclude: ["node_modules", "dist"]
45139
45237
  },
45140
- include: ["src"],
45141
- exclude: ["node_modules", "dist"]
45142
- }, null, 2) + "\n";
45238
+ null,
45239
+ 2
45240
+ ) + "\n";
45143
45241
  }
45144
45242
  function envTemplate() {
45145
45243
  return `ELEVASIS_PLATFORM_KEY=
@@ -45257,6 +45355,28 @@ function claudeSettingsTemplate() {
45257
45355
  }
45258
45356
  ]
45259
45357
  }
45358
+ ],
45359
+ PostToolUse: [
45360
+ {
45361
+ matcher: "Write|Edit|MultiEdit",
45362
+ hooks: [
45363
+ {
45364
+ type: "command",
45365
+ command: "node .claude/hooks/post-edit-validate.mjs"
45366
+ }
45367
+ ]
45368
+ }
45369
+ ],
45370
+ PostToolUseFailure: [
45371
+ {
45372
+ matcher: "Bash",
45373
+ hooks: [
45374
+ {
45375
+ type: "command",
45376
+ command: "node .claude/hooks/tool-failure-recovery.mjs"
45377
+ }
45378
+ ]
45379
+ }
45260
45380
  ]
45261
45381
  }
45262
45382
  },
@@ -46969,6 +47089,199 @@ When all plan steps are marked COMPLETE, suggest \`/work complete\` to finalize
46969
47089
  - Completed tasks move OUT of \`docs/in-progress/\` to \`docs/<relevant-dir>/\`
46970
47090
  `;
46971
47091
  }
47092
+ function claudePostEditValidateHookTemplate() {
47093
+ return `#!/usr/bin/env node
47094
+ // post-edit-validate.mjs
47095
+ // PostToolUse hook \u2014 auto-formats with prettier, type-checks .ts/.tsx files.
47096
+ // Fires after Edit|Write|MultiEdit succeeds.
47097
+
47098
+ import { existsSync } from 'node:fs'
47099
+ import { resolve, normalize, extname, join, dirname } from 'node:path'
47100
+ import { execSync } from 'node:child_process'
47101
+
47102
+ const ROOT = process.env.CLAUDE_PROJECT_DIR ?? process.cwd()
47103
+
47104
+ // Extensions prettier should format
47105
+ const PRETTIER_EXTENSIONS = new Set([
47106
+ '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', '.css', '.md', '.mdx'
47107
+ ])
47108
+
47109
+ // Extensions that trigger type-checking
47110
+ const TS_EXTENSIONS = new Set(['.ts', '.tsx'])
47111
+
47112
+ function findNearestTsconfig(startDir) {
47113
+ let dir = startDir
47114
+ const root = normalize(ROOT)
47115
+ while (dir.length >= root.length) {
47116
+ const candidate = join(dir, 'tsconfig.json')
47117
+ if (existsSync(candidate)) return candidate
47118
+ const parent = dirname(dir)
47119
+ if (parent === dir) break
47120
+ dir = parent
47121
+ }
47122
+ return null
47123
+ }
47124
+
47125
+ try {
47126
+ const chunks = []
47127
+ for await (const chunk of process.stdin) chunks.push(chunk)
47128
+ const input = JSON.parse(Buffer.concat(chunks).toString())
47129
+
47130
+ const filePath = input.tool_input?.file_path
47131
+ if (!filePath) process.exit(0)
47132
+
47133
+ const ext = extname(filePath).toLowerCase()
47134
+ const absPath = normalize(resolve(filePath))
47135
+ if (!existsSync(absPath)) process.exit(0)
47136
+
47137
+ const results = []
47138
+
47139
+ // 1. Prettier
47140
+ if (PRETTIER_EXTENSIONS.has(ext)) {
47141
+ try {
47142
+ execSync('pnpm exec prettier --write "' + absPath + '"', {
47143
+ cwd: ROOT,
47144
+ stdio: ['pipe', 'pipe', 'pipe'],
47145
+ timeout: 10_000
47146
+ })
47147
+ } catch (err) {
47148
+ const stderr = err.stderr?.toString().trim() || ''
47149
+ if (stderr && !/ignored/i.test(stderr)) {
47150
+ results.push('Prettier error: ' + stderr.slice(0, 300))
47151
+ }
47152
+ }
47153
+ }
47154
+
47155
+ // 2. Type-check for .ts/.tsx
47156
+ if (TS_EXTENSIONS.has(ext)) {
47157
+ const tsconfig = findNearestTsconfig(dirname(absPath))
47158
+ if (tsconfig) {
47159
+ try {
47160
+ execSync('pnpm exec tsc --noEmit -p "' + tsconfig + '"', {
47161
+ cwd: ROOT,
47162
+ stdio: ['pipe', 'pipe', 'pipe'],
47163
+ timeout: 30_000,
47164
+ env: { ...process.env, NODE_OPTIONS: '--max-old-space-size=4096' }
47165
+ })
47166
+ } catch (err) {
47167
+ if (err.killed) process.exit(0) // Don't block on timeout
47168
+ const stdout = err.stdout?.toString() || ''
47169
+ if (stdout.includes('error TS')) {
47170
+ const errorLines = stdout
47171
+ .split('\\n')
47172
+ .filter(l => l.includes('error TS'))
47173
+ .slice(0, 10)
47174
+ results.push('Type errors after editing ' + filePath + ':\\n' + errorLines.join('\\n'))
47175
+ }
47176
+ }
47177
+ }
47178
+ }
47179
+
47180
+ // Output errors to Claude's context (silence = success)
47181
+ if (results.length > 0) {
47182
+ process.stderr.write(results.join('\\n\\n'))
47183
+ process.exit(2) // Exit 2 = send stderr as feedback to Claude
47184
+ }
47185
+ } catch {}
47186
+
47187
+ process.exit(0)
47188
+ `;
47189
+ }
47190
+ function claudeToolFailureRecoveryHookTemplate() {
47191
+ return `#!/usr/bin/env node
47192
+ // tool-failure-recovery.mjs
47193
+ // PostToolUseFailure hook \u2014 pattern-matches known Bash errors and returns
47194
+ // recovery advice via stderr + exit 2 (feedback to Claude).
47195
+
47196
+ const RECOVERY_TABLE = [
47197
+ {
47198
+ test: r => /JavaScript heap out of memory/i.test(r),
47199
+ advice: 'Out of memory.',
47200
+ fix: 'Run the command with NODE_OPTIONS="--max-old-space-size=4096".',
47201
+ why: 'Large TypeScript projects can exceed Node default heap limit.',
47202
+ },
47203
+ {
47204
+ test: r => /boundary hook/i.test(r) && /block|denied/i.test(r),
47205
+ advice: 'Command blocked by SDK boundary hook.',
47206
+ fix: 'Ask the user to run this command manually.',
47207
+ why: 'The boundary hook blocks gh CLI, destructive git operations, and file writes outside the project.',
47208
+ see: 'CLAUDE.md',
47209
+ },
47210
+ {
47211
+ test: r => /ENOENT/.test(r) && /node_modules/.test(r),
47212
+ advice: 'Missing node_modules dependency.',
47213
+ fix: 'Run: pnpm install',
47214
+ why: 'Dependencies are not installed or were cleared.',
47215
+ },
47216
+ {
47217
+ test: r => /ERR_MODULE_NOT_FOUND/.test(r) && /@elevasis\\/sdk/.test(r),
47218
+ advice: '@elevasis/sdk module not found.',
47219
+ fix: 'Run: pnpm install \u2014 then verify @elevasis/sdk is in package.json dependencies.',
47220
+ why: 'The SDK package is not installed or the version is mismatched.',
47221
+ },
47222
+ {
47223
+ test: r => /ERR_MODULE_NOT_FOUND/.test(r),
47224
+ advice: 'Module not found at import path.',
47225
+ fix: 'Check the import path and verify the package is installed (pnpm install).',
47226
+ why: 'The import path does not match any installed package or local file.',
47227
+ },
47228
+ {
47229
+ test: r => /TS2307/.test(r) || (/cannot find/i.test(r) && /declaration/i.test(r)),
47230
+ advice: 'TypeScript cannot resolve module or declaration file.',
47231
+ fix: 'Check that the package is installed and tsconfig paths are correct.',
47232
+ why: 'Missing dependency or incorrect TypeScript configuration.',
47233
+ },
47234
+ {
47235
+ test: r => /EPERM/.test(r) || /permission denied/i.test(r),
47236
+ advice: 'Permission denied (EPERM).',
47237
+ fix: 'Close the file in any other process (editor, terminal, or dev server) and retry.',
47238
+ why: 'On Windows, files locked by another process cannot be written to.',
47239
+ },
47240
+ {
47241
+ test: r => /lockfile/i.test(r) && /conflict|outdated|ERR_PNPM/i.test(r),
47242
+ advice: 'pnpm lockfile conflict or outdated.',
47243
+ fix: 'Run: pnpm install to regenerate the lockfile.',
47244
+ why: 'The lockfile is out of sync with package.json changes.',
47245
+ },
47246
+ {
47247
+ test: r => /elevasis-sdk check/.test(r) || /elevasis-sdk deploy/.test(r),
47248
+ advice: 'elevasis-sdk CLI command failed.',
47249
+ fix: 'Check the error output above. Common causes: missing .env ELEVASIS_API_KEY, invalid resource schemas, or network issues.',
47250
+ why: 'The SDK CLI validates resources and communicates with the platform API.',
47251
+ },
47252
+ {
47253
+ test: r => /EADDRINUSE/.test(r),
47254
+ advice: 'Port already in use.',
47255
+ fix: 'Find and kill the process using the port, or use a different port.',
47256
+ why: 'A previous dev server or process is still holding the port.',
47257
+ },
47258
+ ]
47259
+
47260
+ function formatRecovery(entry) {
47261
+ let msg = 'FAILED: ' + entry.advice + '\\nFIX: ' + entry.fix
47262
+ if (entry.why) msg += '\\nWHY: ' + entry.why
47263
+ if (entry.see) msg += '\\nSEE: ' + entry.see
47264
+ return msg
47265
+ }
47266
+
47267
+ try {
47268
+ const chunks = []
47269
+ for await (const chunk of process.stdin) chunks.push(chunk)
47270
+ const input = JSON.parse(Buffer.concat(chunks).toString())
47271
+
47272
+ const response = input.tool_response ?? ''
47273
+
47274
+ for (const entry of RECOVERY_TABLE) {
47275
+ if (entry.test(response)) {
47276
+ process.stderr.write(formatRecovery(entry))
47277
+ process.exit(2)
47278
+ }
47279
+ }
47280
+ } catch {}
47281
+
47282
+ process.exit(0)
47283
+ `;
47284
+ }
46972
47285
 
46973
47286
  // src/cli/commands/templates/core/resources.ts
46974
47287
  function starterTemplate() {
@@ -47144,6 +47457,8 @@ function getManagedTemplates(ctx = {}) {
47144
47457
  ".claude/settings.json": claudeSettingsTemplate,
47145
47458
  ".claude/scripts/statusline-command.js": claudeStatuslineScriptTemplate,
47146
47459
  ".claude/hooks/enforce-sdk-boundary.mjs": claudeSdkBoundaryHookTemplate,
47460
+ ".claude/hooks/post-edit-validate.mjs": claudePostEditValidateHookTemplate,
47461
+ ".claude/hooks/tool-failure-recovery.mjs": claudeToolFailureRecoveryHookTemplate,
47147
47462
  ".claude/commands/tutorial.md": claudeTutorialCommandTemplate,
47148
47463
  ".claude/commands/meta.md": claudeMetaCommandTemplate,
47149
47464
  ".claude/commands/work.md": claudeWorkCommandTemplate,
@@ -47356,6 +47671,8 @@ var MANAGED_FILES = [
47356
47671
  "CLAUDE.md",
47357
47672
  ".claude/settings.json",
47358
47673
  ".claude/hooks/enforce-sdk-boundary.mjs",
47674
+ ".claude/hooks/post-edit-validate.mjs",
47675
+ ".claude/hooks/tool-failure-recovery.mjs",
47359
47676
  ".claude/commands/tutorial.md",
47360
47677
  ".claude/commands/meta.md",
47361
47678
  ".claude/commands/work.md",
@@ -47370,96 +47687,100 @@ var MANAGED_FILES = [
47370
47687
  ];
47371
47688
  var SCAFFOLD_FILES = [...INIT_ONLY_FILES, ...MANAGED_FILES];
47372
47689
  function registerInitCommand(program3) {
47373
- program3.command("init [directory]").description("Scaffold a new Elevasis workspace\n Example: elevasis-sdk init my-workspace").option("--force", "Overwrite existing files").option("--ui", "Include a Vite + React UI app in ui/").action(wrapAction("init", async (directory, options2) => {
47374
- const targetDir = directory ? (0, import_path3.resolve)(directory) : process.cwd();
47375
- const orgSlug = toSlug((0, import_path3.basename)(targetDir));
47376
- if (!options2.force) {
47377
- const filesToCheck = options2.ui ? [...SCAFFOLD_FILES, ...UI_INIT_FILES] : SCAFFOLD_FILES;
47378
- const conflicts = [];
47379
- for (const file2 of filesToCheck) {
47380
- try {
47381
- await (0, import_promises2.access)((0, import_path3.resolve)(targetDir, file2));
47382
- conflicts.push(file2);
47383
- } catch {
47384
- }
47385
- }
47386
- if (conflicts.length > 0) {
47387
- console.error(source_default.red("Files already exist:"));
47388
- for (const f of conflicts) {
47389
- console.error(source_default.gray(` ${f}`));
47690
+ program3.command("init [directory]").description("Scaffold a new Elevasis workspace\n Example: elevasis-sdk init my-workspace").option("--force", "Overwrite existing files").option("--ui", "Include a Vite + React UI app in ui/").action(
47691
+ wrapAction("init", async (directory, options2) => {
47692
+ const targetDir = directory ? (0, import_path3.resolve)(directory) : process.cwd();
47693
+ const orgSlug = toSlug((0, import_path3.basename)(targetDir));
47694
+ if (!options2.force) {
47695
+ const filesToCheck = options2.ui ? [...SCAFFOLD_FILES, ...UI_INIT_FILES] : SCAFFOLD_FILES;
47696
+ const conflicts = [];
47697
+ for (const file2 of filesToCheck) {
47698
+ try {
47699
+ await (0, import_promises2.access)((0, import_path3.resolve)(targetDir, file2));
47700
+ conflicts.push(file2);
47701
+ } catch {
47702
+ }
47390
47703
  }
47391
- console.error(source_default.gray("\n Use --force to overwrite."));
47392
- throw new Error("Scaffold conflict");
47393
- }
47394
- }
47395
- await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src/operations"), { recursive: true });
47396
- await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src/example"), { recursive: true });
47397
- await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src/shared"), { recursive: true });
47398
- await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "docs/in-progress"), { recursive: true });
47399
- await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/hooks"), { recursive: true });
47400
- await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/scripts"), { recursive: true });
47401
- await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/commands"), { recursive: true });
47402
- await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/skills/creds"), { recursive: true });
47403
- await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/rules"), { recursive: true });
47404
- if (options2.ui) {
47405
- await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "ui/src"), { recursive: true });
47406
- }
47407
- const files = {
47408
- "elevasis.config.ts": configTemplate(),
47409
- "package.json": packageJsonTemplate(orgSlug),
47410
- "pnpm-workspace.yaml": pnpmWorkspaceTemplate(),
47411
- "tsconfig.json": tsconfigTemplate(),
47412
- ".env": envTemplate(),
47413
- ".npmrc": npmrcTemplate(),
47414
- ".gitignore": gitignoreTemplate({ hasUI: options2.ui }),
47415
- "src/index.ts": starterTemplate(),
47416
- "src/operations/platform-status.ts": platformStatusTemplate(),
47417
- "src/operations/index.ts": operationsBarrelTemplate(),
47418
- "src/example/echo.ts": starterWorkflowTemplate(),
47419
- "src/example/index.ts": exampleBarrelTemplate(),
47420
- "src/shared/.gitkeep": "",
47421
- "docs/index.mdx": docsIndexTemplate(orgSlug),
47422
- "docs/in-progress/.gitkeep": "",
47423
- "CLAUDE.md": claudeMdTemplate({ hasUI: options2.ui }),
47424
- ".claude/settings.json": claudeSettingsTemplate(),
47425
- ".claude/hooks/enforce-sdk-boundary.mjs": claudeSdkBoundaryHookTemplate(),
47426
- ".claude/commands/tutorial.md": claudeTutorialCommandTemplate(),
47427
- ".claude/commands/meta.md": claudeMetaCommandTemplate(),
47428
- ".claude/commands/work.md": claudeWorkCommandTemplate(),
47429
- ".claude/commands/docs.md": claudeDocsCommandTemplate(),
47430
- ".claude/skills/creds/SKILL.md": claudeCredsSkillTemplate(),
47431
- ".claude/rules/sdk-patterns.md": claudeSdkPatternsRuleTemplate(),
47432
- ".claude/rules/workspace-patterns.md": claudeWorkspaceRulesTemplate(),
47433
- ".claude/rules/docs-authoring.md": claudeDocsAuthoringRuleTemplate(),
47434
- ".claude/rules/memory-conventions.md": claudeMemoryConventionsRuleTemplate(),
47435
- ".claude/rules/project-map.md": claudeProjectMapRuleTemplate(),
47436
- ".claude/rules/task-tracking.md": claudeTaskTrackingRuleTemplate(),
47437
- ".claude/scripts/statusline-command.js": claudeStatuslineScriptTemplate()
47438
- };
47439
- if (options2.ui) {
47440
- Object.assign(files, getUIFiles(orgSlug));
47441
- }
47442
- for (const [filePath, content] of Object.entries(files)) {
47443
- await (0, import_promises2.writeFile)((0, import_path3.resolve)(targetDir, filePath), content, "utf-8");
47444
- }
47445
- console.log(source_default.green.bold(" Workspace created!"));
47446
- console.log("");
47447
- console.log(source_default.gray(" Next steps:"));
47448
- if (directory) {
47449
- console.log(source_default.gray(` cd ${directory}`));
47450
- }
47451
- console.log(source_default.gray(" pnpm install"));
47452
- console.log(source_default.gray(" # Add your API key to .env"));
47453
- console.log(source_default.gray(" elevasis-sdk check"));
47454
- console.log(source_default.gray(" elevasis-sdk deploy"));
47455
- if (options2.ui) {
47704
+ if (conflicts.length > 0) {
47705
+ console.error(source_default.red("Files already exist:"));
47706
+ for (const f of conflicts) {
47707
+ console.error(source_default.gray(` ${f}`));
47708
+ }
47709
+ console.error(source_default.gray("\n Use --force to overwrite."));
47710
+ throw new Error("Scaffold conflict");
47711
+ }
47712
+ }
47713
+ await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src/operations"), { recursive: true });
47714
+ await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src/example"), { recursive: true });
47715
+ await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src/shared"), { recursive: true });
47716
+ await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "docs/in-progress"), { recursive: true });
47717
+ await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/hooks"), { recursive: true });
47718
+ await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/scripts"), { recursive: true });
47719
+ await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/commands"), { recursive: true });
47720
+ await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/skills/creds"), { recursive: true });
47721
+ await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/rules"), { recursive: true });
47722
+ if (options2.ui) {
47723
+ await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "ui/src"), { recursive: true });
47724
+ }
47725
+ const files = {
47726
+ "elevasis.config.ts": configTemplate(),
47727
+ "package.json": packageJsonTemplate(orgSlug),
47728
+ "pnpm-workspace.yaml": pnpmWorkspaceTemplate(),
47729
+ "tsconfig.json": tsconfigTemplate(),
47730
+ ".env": envTemplate(),
47731
+ ".npmrc": npmrcTemplate(),
47732
+ ".gitignore": gitignoreTemplate({ hasUI: options2.ui }),
47733
+ "src/index.ts": starterTemplate(),
47734
+ "src/operations/platform-status.ts": platformStatusTemplate(),
47735
+ "src/operations/index.ts": operationsBarrelTemplate(),
47736
+ "src/example/echo.ts": starterWorkflowTemplate(),
47737
+ "src/example/index.ts": exampleBarrelTemplate(),
47738
+ "src/shared/.gitkeep": "",
47739
+ "docs/index.mdx": docsIndexTemplate(orgSlug),
47740
+ "docs/in-progress/.gitkeep": "",
47741
+ "CLAUDE.md": claudeMdTemplate({ hasUI: options2.ui }),
47742
+ ".claude/settings.json": claudeSettingsTemplate(),
47743
+ ".claude/hooks/enforce-sdk-boundary.mjs": claudeSdkBoundaryHookTemplate(),
47744
+ ".claude/hooks/post-edit-validate.mjs": claudePostEditValidateHookTemplate(),
47745
+ ".claude/hooks/tool-failure-recovery.mjs": claudeToolFailureRecoveryHookTemplate(),
47746
+ ".claude/commands/tutorial.md": claudeTutorialCommandTemplate(),
47747
+ ".claude/commands/meta.md": claudeMetaCommandTemplate(),
47748
+ ".claude/commands/work.md": claudeWorkCommandTemplate(),
47749
+ ".claude/commands/docs.md": claudeDocsCommandTemplate(),
47750
+ ".claude/skills/creds/SKILL.md": claudeCredsSkillTemplate(),
47751
+ ".claude/rules/sdk-patterns.md": claudeSdkPatternsRuleTemplate(),
47752
+ ".claude/rules/workspace-patterns.md": claudeWorkspaceRulesTemplate(),
47753
+ ".claude/rules/docs-authoring.md": claudeDocsAuthoringRuleTemplate(),
47754
+ ".claude/rules/memory-conventions.md": claudeMemoryConventionsRuleTemplate(),
47755
+ ".claude/rules/project-map.md": claudeProjectMapRuleTemplate(),
47756
+ ".claude/rules/task-tracking.md": claudeTaskTrackingRuleTemplate(),
47757
+ ".claude/scripts/statusline-command.js": claudeStatuslineScriptTemplate()
47758
+ };
47759
+ if (options2.ui) {
47760
+ Object.assign(files, getUIFiles(orgSlug));
47761
+ }
47762
+ for (const [filePath, content] of Object.entries(files)) {
47763
+ await (0, import_promises2.writeFile)((0, import_path3.resolve)(targetDir, filePath), content, "utf-8");
47764
+ }
47765
+ console.log(source_default.green.bold(" Workspace created!"));
47456
47766
  console.log("");
47457
- console.log(source_default.gray(" UI app:"));
47458
- console.log(source_default.gray(" cd ui && pnpm install"));
47459
- console.log(source_default.gray(" # Set VITE_WORKOS_CLIENT_ID in ui/.env"));
47460
- console.log(source_default.gray(" pnpm dev"));
47461
- }
47462
- }));
47767
+ console.log(source_default.gray(" Next steps:"));
47768
+ if (directory) {
47769
+ console.log(source_default.gray(` cd ${directory}`));
47770
+ }
47771
+ console.log(source_default.gray(" pnpm install"));
47772
+ console.log(source_default.gray(" # Add your API key to .env"));
47773
+ console.log(source_default.gray(" elevasis-sdk check"));
47774
+ console.log(source_default.gray(" elevasis-sdk deploy"));
47775
+ if (options2.ui) {
47776
+ console.log("");
47777
+ console.log(source_default.gray(" UI app:"));
47778
+ console.log(source_default.gray(" cd ui && pnpm install"));
47779
+ console.log(source_default.gray(" # Set VITE_WORKOS_CLIENT_ID in ui/.env"));
47780
+ console.log(source_default.gray(" pnpm dev"));
47781
+ }
47782
+ })
47783
+ );
47463
47784
  }
47464
47785
  function toSlug(name) {
47465
47786
  const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^[^a-z]+/, "").replace(/-+/g, "-").replace(/-$/, "");