@elevasis/sdk 0.5.1 → 0.5.3

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
@@ -27004,7 +27004,7 @@ var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
27004
27004
  var source_default = chalk;
27005
27005
 
27006
27006
  // src/cli/commands/check.ts
27007
- var import_path = require("path");
27007
+ var import_path2 = require("path");
27008
27008
 
27009
27009
  // ../../node_modules/.pnpm/ora@7.0.1/node_modules/ora/index.js
27010
27010
  var import_node_process6 = __toESM(require("node:process"), 1);
@@ -27516,7 +27516,7 @@ function ora(options2) {
27516
27516
  }
27517
27517
 
27518
27518
  // src/cli/commands/check.ts
27519
- var import_jiti = require("jiti");
27519
+ var import_jiti2 = require("jiti");
27520
27520
 
27521
27521
  // ../../node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/external.js
27522
27522
  var external_exports = {};
@@ -43685,59 +43685,11 @@ function wrapAction(commandName, fn) {
43685
43685
  };
43686
43686
  }
43687
43687
 
43688
- // src/cli/commands/check.ts
43689
- var import_meta = {};
43690
- function registerCheckCommand(program3) {
43691
- program3.command("check").description("Validate project resources against the ResourceRegistry\n Example: elevasis check --entry ./src/index.ts").option("--entry <path>", "Path to entry file (default: ./src/index.ts)").action(wrapAction("check", async (options2) => {
43692
- const entryPath = options2.entry ?? "./src/index.ts";
43693
- const spinner = ora("Validating resources...").start();
43694
- try {
43695
- const jiti = (0, import_jiti.createJiti)(import_meta.url);
43696
- const entryModule = await jiti.import((0, import_path.resolve)(entryPath));
43697
- const org = entryModule.default;
43698
- if (!org) {
43699
- spinner.fail("Invalid entry: no default export found");
43700
- console.error(source_default.gray(` Entry file: ${(0, import_path.resolve)(entryPath)}`));
43701
- console.error(source_default.gray(" Expected: export default { workflows: [...], agents: [...] }"));
43702
- throw new Error("Invalid entry");
43703
- }
43704
- new ResourceRegistry({ _check: org });
43705
- const workflowCount = org.workflows?.length ?? 0;
43706
- const agentCount = org.agents?.length ?? 0;
43707
- const totalCount = workflowCount + agentCount;
43708
- spinner.succeed(
43709
- source_default.green("Validation passed") + source_default.gray(` (${totalCount} resource${totalCount !== 1 ? "s" : ""}, 0 errors)`)
43710
- );
43711
- if (workflowCount > 0) {
43712
- console.log(source_default.gray(` Workflows: ${workflowCount}`));
43713
- }
43714
- if (agentCount > 0) {
43715
- console.log(source_default.gray(` Agents: ${agentCount}`));
43716
- }
43717
- } catch (error46) {
43718
- if (error46 instanceof RegistryValidationError) {
43719
- spinner.fail(source_default.red("Validation failed"));
43720
- console.error("");
43721
- console.error(source_default.red(` ERROR ${error46.message}`));
43722
- if (error46.resourceId) {
43723
- console.error(source_default.gray(` Resource: ${error46.resourceId}`));
43724
- }
43725
- if (error46.field) {
43726
- console.error(source_default.gray(` Field: ${error46.field}`));
43727
- }
43728
- console.error("");
43729
- console.error(source_default.gray(" 1 error. Fix the issue and run again."));
43730
- }
43731
- throw error46;
43732
- }
43733
- }));
43734
- }
43735
-
43736
43688
  // src/cli/commands/deploy.ts
43737
- var import_path2 = require("path");
43689
+ var import_path = require("path");
43738
43690
  var import_promises = require("fs/promises");
43739
43691
  var import_gray_matter = __toESM(require_gray_matter(), 1);
43740
- var import_jiti2 = require("jiti");
43692
+ var import_jiti = require("jiti");
43741
43693
  var esbuild = __toESM(require("esbuild"), 1);
43742
43694
 
43743
43695
  // src/cli/config.ts
@@ -43769,6 +43721,9 @@ async function apiGet(endpoint, apiUrl = resolveApiUrl()) {
43769
43721
  });
43770
43722
  if (!response.ok) {
43771
43723
  const errorText = await response.text();
43724
+ if (response.status === 401) {
43725
+ throw new Error(`401 Unauthorized: Invalid or missing ELEVASIS_API_KEY. Check your .env file.`);
43726
+ }
43772
43727
  throw new Error(`API request failed (${response.status}): ${errorText}`);
43773
43728
  }
43774
43729
  if (response.status === 204) {
@@ -43830,7 +43785,7 @@ async function apiDelete(endpoint, apiUrl = resolveApiUrl()) {
43830
43785
  // package.json
43831
43786
  var package_default = {
43832
43787
  name: "@elevasis/sdk",
43833
- version: "0.5.1",
43788
+ version: "0.5.3",
43834
43789
  description: "SDK for building Elevasis organization resources",
43835
43790
  "comment:bin": "IMPORTANT: This package shares the 'elevasis' binary name with @repo/cli. They never conflict because @elevasis/sdk must NEVER be added as a dependency of any workspace package (apps/*, packages/*, organizations/*). Workspace projects use @repo/cli for the 'elevasis' binary. External developers (outside the workspace) get this SDK's binary via npm install.",
43836
43791
  type: "module",
@@ -43897,9 +43852,9 @@ var package_default = {
43897
43852
  var SDK_VERSION = package_default.version;
43898
43853
 
43899
43854
  // src/cli/commands/deploy.ts
43900
- var import_meta2 = {};
43855
+ var import_meta = {};
43901
43856
  async function scanDocumentation() {
43902
- const docsDir = (0, import_path2.resolve)("docs");
43857
+ const docsDir = (0, import_path.resolve)("docs");
43903
43858
  const files = [];
43904
43859
  let totalSize = 0;
43905
43860
  async function scan(dir, relPrefix) {
@@ -43910,7 +43865,7 @@ async function scanDocumentation() {
43910
43865
  return;
43911
43866
  }
43912
43867
  for (const entry of entries) {
43913
- const fullPath = (0, import_path2.resolve)(dir, entry.name);
43868
+ const fullPath = (0, import_path.resolve)(dir, entry.name);
43914
43869
  const relPath = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
43915
43870
  if (entry.isDirectory()) {
43916
43871
  await scan(fullPath, relPath);
@@ -43949,15 +43904,60 @@ function escapeMdx(text) {
43949
43904
  if (!text) return "";
43950
43905
  return text.replace(/\|/g, "\\|").replace(/\{/g, "\\{").replace(/\}/g, "\\}");
43951
43906
  }
43907
+ async function generateResourceMap(org) {
43908
+ const workflows = org.workflows ?? [];
43909
+ const agents = org.agents ?? [];
43910
+ const lines = [
43911
+ "---",
43912
+ "title: Resource Map",
43913
+ "description: Auto-generated resource inventory (updated on each deploy)",
43914
+ "order: 998",
43915
+ "---",
43916
+ "",
43917
+ "# Resource Map",
43918
+ "",
43919
+ "> Auto-generated by `elevasis deploy`. Do not edit manually.",
43920
+ ""
43921
+ ];
43922
+ if (workflows.length > 0) {
43923
+ lines.push(
43924
+ "## Workflows",
43925
+ "",
43926
+ "| Resource ID | Name | Version | Status | Description |",
43927
+ "| --- | --- | --- | --- | --- |"
43928
+ );
43929
+ for (const w of workflows) {
43930
+ const desc = escapeMdx(w.config.description);
43931
+ lines.push(`| \`${w.config.resourceId}\` | ${escapeMdx(w.config.name)} | ${w.config.version} | ${w.config.status} | ${desc} |`);
43932
+ }
43933
+ lines.push("");
43934
+ }
43935
+ if (agents.length > 0) {
43936
+ lines.push(
43937
+ "## Agents",
43938
+ "",
43939
+ "| Resource ID | Name | Version | Status | Description |",
43940
+ "| --- | --- | --- | --- | --- |"
43941
+ );
43942
+ for (const a of agents) {
43943
+ const desc = escapeMdx(a.config.description);
43944
+ lines.push(`| \`${a.config.resourceId}\` | ${escapeMdx(a.config.name)} | ${a.config.version} | ${a.config.status} | ${desc} |`);
43945
+ }
43946
+ lines.push("");
43947
+ }
43948
+ lines.push(`**Total:** ${workflows.length + agents.length} resources (${workflows.length} workflows, ${agents.length} agents)`, "");
43949
+ await (0, import_promises.mkdir)((0, import_path.resolve)("docs"), { recursive: true });
43950
+ await (0, import_promises.writeFile)((0, import_path.resolve)("docs/resource-map.mdx"), lines.join("\n"), "utf-8");
43951
+ }
43952
43952
  async function generateProjectMap(org) {
43953
43953
  const workflows = org.workflows ?? [];
43954
43954
  const agents = org.agents ?? [];
43955
43955
  let templateVersion = "unknown";
43956
43956
  try {
43957
- const jiti = (0, import_jiti2.createJiti)(import_meta2.url);
43958
- const cfg = await jiti.import((0, import_path2.resolve)("elevasis.config.ts"));
43957
+ const jiti = (0, import_jiti.createJiti)(import_meta.url);
43958
+ const cfg = await jiti.import((0, import_path.resolve)("elevasis.config.ts"));
43959
43959
  const cfgObj = cfg.default ?? cfg;
43960
- if (cfgObj && (typeof cfgObj.templateVersion === "string" || typeof cfgObj.templateVersion === "number")) {
43960
+ if (cfgObj?.templateVersion != null) {
43961
43961
  templateVersion = String(cfgObj.templateVersion);
43962
43962
  }
43963
43963
  } catch {
@@ -43993,7 +43993,7 @@ async function generateProjectMap(org) {
43993
43993
  "| --- | --- | --- | --- |"
43994
43994
  );
43995
43995
  try {
43996
- const srcEntries = await (0, import_promises.readdir)((0, import_path2.resolve)("src"), { withFileTypes: true });
43996
+ const srcEntries = await (0, import_promises.readdir)((0, import_path.resolve)("src"), { withFileTypes: true });
43997
43997
  const domainDirs = srcEntries.filter((e) => e.isDirectory());
43998
43998
  if (domainDirs.length === 0) {
43999
43999
  lines.push("| (none) | src/ | 0 | \u2014 |");
@@ -44023,34 +44023,17 @@ async function generateProjectMap(org) {
44023
44023
  lines.push("| (src/ not found) | \u2014 | 0 | \u2014 |");
44024
44024
  }
44025
44025
  lines.push("");
44026
- lines.push("## Resources", "");
44027
- if (workflows.length > 0) {
44028
- lines.push(
44029
- "### Workflows",
44030
- "",
44031
- "| Resource ID | Name | Version | Status | Description |",
44032
- "| --- | --- | --- | --- | --- |"
44033
- );
44034
- for (const w of workflows) {
44035
- const desc = escapeMdx(w.config.description);
44036
- lines.push(`| \`${w.config.resourceId}\` | ${escapeMdx(w.config.name)} | ${w.config.version} | ${w.config.status} | ${desc} |`);
44037
- }
44038
- lines.push("");
44039
- }
44040
- if (agents.length > 0) {
44041
- lines.push(
44042
- "### Agents",
44043
- "",
44044
- "| Resource ID | Name | Version | Status | Description |",
44045
- "| --- | --- | --- | --- | --- |"
44046
- );
44047
- for (const a of agents) {
44048
- const desc = escapeMdx(a.config.description);
44049
- lines.push(`| \`${a.config.resourceId}\` | ${escapeMdx(a.config.name)} | ${a.config.version} | ${a.config.status} | ${desc} |`);
44050
- }
44051
- lines.push("");
44052
- }
44053
- lines.push(`**Total:** ${workflows.length + agents.length} resources (${workflows.length} workflows, ${agents.length} agents)`, "");
44026
+ lines.push(
44027
+ "## Resources",
44028
+ "",
44029
+ "| Type | Count |",
44030
+ "| --- | --- |",
44031
+ `| Workflows | ${workflows.length} |`,
44032
+ `| Agents | ${agents.length} |`,
44033
+ "",
44034
+ `Full inventory (names, versions, descriptions): \`docs/resource-map.mdx\``,
44035
+ ""
44036
+ );
44054
44037
  lines.push(
44055
44038
  "## Documentation Index",
44056
44039
  "",
@@ -44071,10 +44054,10 @@ async function generateProjectMap(org) {
44071
44054
  for (const entry of entries) {
44072
44055
  const relPath = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
44073
44056
  if (entry.isDirectory()) {
44074
- await scanDocsDir((0, import_path2.resolve)(dir, entry.name), relPath);
44075
- } else if (entry.isFile() && entry.name.endsWith(".mdx") && relPath !== "project-map.mdx") {
44057
+ await scanDocsDir((0, import_path.resolve)(dir, entry.name), relPath);
44058
+ } else if (entry.isFile() && entry.name.endsWith(".mdx") && relPath !== "project-map.mdx" && relPath !== "resource-map.mdx") {
44076
44059
  try {
44077
- const raw = await (0, import_promises.readFile)((0, import_path2.resolve)(dir, entry.name), "utf-8");
44060
+ const raw = await (0, import_promises.readFile)((0, import_path.resolve)(dir, entry.name), "utf-8");
44078
44061
  const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
44079
44062
  let title = relPath;
44080
44063
  let description = "";
@@ -44094,7 +44077,7 @@ async function generateProjectMap(org) {
44094
44077
  }
44095
44078
  }
44096
44079
  }
44097
- await scanDocsDir((0, import_path2.resolve)("docs"), "");
44080
+ await scanDocsDir((0, import_path.resolve)("docs"), "");
44098
44081
  docEntries.sort((a, b) => {
44099
44082
  if (a.order !== b.order) return a.order - b.order;
44100
44083
  return a.title.localeCompare(b.title);
@@ -44112,7 +44095,7 @@ async function generateProjectMap(org) {
44112
44095
  lines.push("");
44113
44096
  lines.push("## SDK Reference", "");
44114
44097
  try {
44115
- const navPath = (0, import_path2.resolve)("node_modules/@elevasis/sdk/reference/_navigation.md");
44098
+ const navPath = (0, import_path.resolve)("node_modules/@elevasis/sdk/reference/_navigation.md");
44116
44099
  const navContent = await (0, import_promises.readFile)(navPath, "utf-8");
44117
44100
  const navLines = navContent.split(/\r?\n/);
44118
44101
  const categories = [];
@@ -44155,7 +44138,7 @@ async function generateProjectMap(org) {
44155
44138
  }
44156
44139
  lines.push("## Command and Rules System", "", "### Commands", "");
44157
44140
  try {
44158
- const cmdEntries = await (0, import_promises.readdir)((0, import_path2.resolve)(".claude/commands"), { withFileTypes: true });
44141
+ const cmdEntries = await (0, import_promises.readdir)((0, import_path.resolve)(".claude/commands"), { withFileTypes: true });
44159
44142
  const cmdFiles = cmdEntries.filter((e) => e.isFile() && e.name.endsWith(".md"));
44160
44143
  if (cmdFiles.length === 0) {
44161
44144
  lines.push("No commands found.", "");
@@ -44168,7 +44151,7 @@ async function generateProjectMap(org) {
44168
44151
  const cmdName = f.name.replace(/\.md$/, "");
44169
44152
  let purpose = "";
44170
44153
  try {
44171
- const raw = await (0, import_promises.readFile)((0, import_path2.resolve)(".claude/commands", f.name), "utf-8");
44154
+ const raw = await (0, import_promises.readFile)((0, import_path.resolve)(".claude/commands", f.name), "utf-8");
44172
44155
  const goalMatch = raw.match(/\*\*Goal:\*\*\s*(.+)/);
44173
44156
  if (goalMatch) {
44174
44157
  purpose = goalMatch[1].trim();
@@ -44187,7 +44170,7 @@ async function generateProjectMap(org) {
44187
44170
  }
44188
44171
  lines.push("### Rules (auto-injected)", "");
44189
44172
  try {
44190
- const ruleEntries = await (0, import_promises.readdir)((0, import_path2.resolve)(".claude/rules"), { withFileTypes: true });
44173
+ const ruleEntries = await (0, import_promises.readdir)((0, import_path.resolve)(".claude/rules"), { withFileTypes: true });
44191
44174
  const ruleFiles = ruleEntries.filter((e) => e.isFile() && e.name.endsWith(".md"));
44192
44175
  if (ruleFiles.length === 0) {
44193
44176
  lines.push("No rules found.", "");
@@ -44200,7 +44183,7 @@ async function generateProjectMap(org) {
44200
44183
  const ruleName = f.name.replace(/\.md$/, "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
44201
44184
  let scope = "";
44202
44185
  try {
44203
- const raw = await (0, import_promises.readFile)((0, import_path2.resolve)(".claude/rules", f.name), "utf-8");
44186
+ const raw = await (0, import_promises.readFile)((0, import_path.resolve)(".claude/rules", f.name), "utf-8");
44204
44187
  const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
44205
44188
  if (fmMatch) {
44206
44189
  const descMatch = fmMatch[1].match(/^description:\s*(.+)$/m);
@@ -44221,7 +44204,7 @@ async function generateProjectMap(org) {
44221
44204
  }
44222
44205
  lines.push("### Skills", "");
44223
44206
  try {
44224
- const skillEntries = await (0, import_promises.readdir)((0, import_path2.resolve)(".claude/skills"), { withFileTypes: true });
44207
+ const skillEntries = await (0, import_promises.readdir)((0, import_path.resolve)(".claude/skills"), { withFileTypes: true });
44225
44208
  const skillDirs = skillEntries.filter((e) => e.isDirectory());
44226
44209
  if (skillDirs.length === 0) {
44227
44210
  lines.push("No skills found.", "");
@@ -44231,7 +44214,7 @@ async function generateProjectMap(org) {
44231
44214
  "| --- | --- | --- |"
44232
44215
  );
44233
44216
  for (const d of skillDirs) {
44234
- const skillFile = (0, import_path2.resolve)(".claude/skills", d.name, "SKILL.md");
44217
+ const skillFile = (0, import_path.resolve)(".claude/skills", d.name, "SKILL.md");
44235
44218
  let trigger = "";
44236
44219
  try {
44237
44220
  const raw = await (0, import_promises.readFile)(skillFile, "utf-8");
@@ -44264,7 +44247,7 @@ async function generateProjectMap(org) {
44264
44247
  return;
44265
44248
  }
44266
44249
  for (const entry of entries) {
44267
- const fullPath = (0, import_path2.resolve)(dir, entry.name);
44250
+ const fullPath = (0, import_path.resolve)(dir, entry.name);
44268
44251
  const relPath = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
44269
44252
  if (entry.isDirectory()) {
44270
44253
  await scanMemory(fullPath, relPath);
@@ -44281,7 +44264,7 @@ async function generateProjectMap(org) {
44281
44264
  }
44282
44265
  }
44283
44266
  }
44284
- await scanMemory((0, import_path2.resolve)(".claude/memory"), "");
44267
+ await scanMemory((0, import_path.resolve)(".claude/memory"), "");
44285
44268
  if (memoryFiles.length === 0) {
44286
44269
  lines.push("No memory files found.", "");
44287
44270
  } else {
@@ -44301,7 +44284,7 @@ async function generateProjectMap(org) {
44301
44284
  let apiKeySet = "no";
44302
44285
  let nodeEnv = "not set";
44303
44286
  try {
44304
- const envContent = await (0, import_promises.readFile)((0, import_path2.resolve)(".env"), "utf-8");
44287
+ const envContent = await (0, import_promises.readFile)((0, import_path.resolve)(".env"), "utf-8");
44305
44288
  const apiKeyMatch = envContent.match(/^ELEVASIS_API_KEY=(.+)$/m);
44306
44289
  if (apiKeyMatch && apiKeyMatch[1].trim()) apiKeySet = "yes";
44307
44290
  const nodeEnvMatch = envContent.match(/^NODE_ENV=(.+)$/m);
@@ -44317,8 +44300,8 @@ async function generateProjectMap(org) {
44317
44300
  `| Node Env | .env | ${nodeEnv} |`,
44318
44301
  ""
44319
44302
  );
44320
- await (0, import_promises.mkdir)((0, import_path2.resolve)("docs"), { recursive: true });
44321
- await (0, import_promises.writeFile)((0, import_path2.resolve)("docs/project-map.mdx"), lines.join("\n"), "utf-8");
44303
+ await (0, import_promises.mkdir)((0, import_path.resolve)("docs"), { recursive: true });
44304
+ await (0, import_promises.writeFile)((0, import_path.resolve)("docs/project-map.mdx"), lines.join("\n"), "utf-8");
44322
44305
  }
44323
44306
  function registerDeployCommand(program3) {
44324
44307
  program3.command("deploy").description("Validate, bundle, upload, and deploy project resources\n Example: elevasis deploy --api-url http://localhost:5170").option("--api-url <url>", "API URL").option("--entry <path>", "Path to entry file (default: ./src/index.ts)").action(wrapAction("deploy", async (options2) => {
@@ -44336,18 +44319,25 @@ function registerDeployCommand(program3) {
44336
44319
  );
44337
44320
  } catch (error46) {
44338
44321
  authSpinner.fail(source_default.red("Authentication failed"));
44339
- console.error(source_default.gray(" Check your ELEVASIS_API_KEY and API URL."));
44322
+ const errMsg = error46 instanceof Error ? error46.message : String(error46);
44323
+ if (errMsg.includes("401") || errMsg.toLowerCase().includes("unauthorized")) {
44324
+ console.error(source_default.red(" Invalid API key."));
44325
+ console.error(source_default.gray(" Your ELEVASIS_API_KEY was rejected by the server."));
44326
+ console.error(source_default.gray(" Check your .env file and verify the key in the Elevasis dashboard."));
44327
+ } else {
44328
+ console.error(source_default.gray(" Check your ELEVASIS_API_KEY and API URL."));
44329
+ }
44340
44330
  throw error46;
44341
44331
  }
44342
44332
  const validateSpinner = ora("Validating...").start();
44343
44333
  let org;
44344
44334
  try {
44345
- const jiti = (0, import_jiti2.createJiti)(import_meta2.url);
44346
- const entryModule = await jiti.import((0, import_path2.resolve)(entryPath));
44335
+ const jiti = (0, import_jiti.createJiti)(import_meta.url);
44336
+ const entryModule = await jiti.import((0, import_path.resolve)(entryPath));
44347
44337
  org = entryModule.default;
44348
44338
  if (!org) {
44349
44339
  validateSpinner.fail("Invalid entry: no default export found");
44350
- console.error(source_default.gray(` Entry file: ${(0, import_path2.resolve)(entryPath)}`));
44340
+ console.error(source_default.gray(` Entry file: ${(0, import_path.resolve)(entryPath)}`));
44351
44341
  throw new Error("Invalid entry: no default export found");
44352
44342
  }
44353
44343
  new ResourceRegistry({ [orgName]: org });
@@ -44381,11 +44371,8 @@ function registerDeployCommand(program3) {
44381
44371
  }
44382
44372
  throw error46;
44383
44373
  }
44374
+ await generateResourceMap(org);
44384
44375
  await generateProjectMap(org);
44385
- try {
44386
- await (0, import_promises.unlink)((0, import_path2.resolve)("docs/resource-map.mdx"));
44387
- } catch {
44388
- }
44389
44376
  const documentation = await scanDocumentation();
44390
44377
  if (documentation) {
44391
44378
  console.log(source_default.gray(` docs ${source_default.white(String(documentation.length))} file${documentation.length !== 1 ? "s" : ""}`));
@@ -44459,8 +44446,8 @@ function registerDeployCommand(program3) {
44459
44446
  console.log("");
44460
44447
  }
44461
44448
  const bundleSpinner = ora("Bundling...").start();
44462
- const wrapperPath = (0, import_path2.resolve)("__elevasis_worker.ts");
44463
- const bundleOutfile = (0, import_path2.resolve)("dist/bundle.js");
44449
+ const wrapperPath = (0, import_path.resolve)("__elevasis_worker.ts");
44450
+ const bundleOutfile = (0, import_path.resolve)("dist/bundle.js");
44464
44451
  try {
44465
44452
  const entryImport = entryPath.replace(/\.ts$/, ".js");
44466
44453
  const wrapperContent = `import org from ${JSON.stringify(entryImport)}
@@ -44468,7 +44455,7 @@ import { startWorker } from '@elevasis/sdk/worker'
44468
44455
  startWorker(org)
44469
44456
  `;
44470
44457
  await (0, import_promises.writeFile)(wrapperPath, wrapperContent, "utf-8");
44471
- await (0, import_promises.mkdir)((0, import_path2.resolve)("dist"), { recursive: true });
44458
+ await (0, import_promises.mkdir)((0, import_path.resolve)("dist"), { recursive: true });
44472
44459
  await esbuild.build({
44473
44460
  entryPoints: [wrapperPath],
44474
44461
  bundle: true,
@@ -44557,27 +44544,225 @@ startWorker(org)
44557
44544
  }));
44558
44545
  }
44559
44546
 
44547
+ // src/cli/commands/check.ts
44548
+ var import_meta2 = {};
44549
+ function registerCheckCommand(program3) {
44550
+ program3.command("check").description("Validate project resources against the ResourceRegistry\n Example: elevasis check --entry ./src/index.ts").option("--entry <path>", "Path to entry file (default: ./src/index.ts)").action(wrapAction("check", async (options2) => {
44551
+ const entryPath = options2.entry ?? "./src/index.ts";
44552
+ const spinner = ora("Validating resources...").start();
44553
+ try {
44554
+ const jiti = (0, import_jiti2.createJiti)(import_meta2.url);
44555
+ const entryModule = await jiti.import((0, import_path2.resolve)(entryPath));
44556
+ const org = entryModule.default;
44557
+ if (!org) {
44558
+ spinner.fail("Invalid entry: no default export found");
44559
+ console.error(source_default.gray(` Entry file: ${(0, import_path2.resolve)(entryPath)}`));
44560
+ console.error(source_default.gray(" Expected: export default { workflows: [...], agents: [...] }"));
44561
+ throw new Error("Invalid entry");
44562
+ }
44563
+ new ResourceRegistry({ _check: org });
44564
+ const workflowCount = org.workflows?.length ?? 0;
44565
+ const agentCount = org.agents?.length ?? 0;
44566
+ const totalCount = workflowCount + agentCount;
44567
+ spinner.succeed(
44568
+ source_default.green("Validation passed") + source_default.gray(` (${totalCount} resource${totalCount !== 1 ? "s" : ""}, 0 errors)`)
44569
+ );
44570
+ if (workflowCount > 0) {
44571
+ console.log(source_default.gray(` Workflows: ${workflowCount}`));
44572
+ }
44573
+ if (agentCount > 0) {
44574
+ console.log(source_default.gray(` Agents: ${agentCount}`));
44575
+ }
44576
+ const triggerCount = org.triggers?.length ?? 0;
44577
+ const integrationCount = org.integrations?.length ?? 0;
44578
+ const checkpointCount = org.humanCheckpoints?.length ?? 0;
44579
+ if (triggerCount > 0) console.log(source_default.gray(` Triggers: ${triggerCount}`));
44580
+ if (integrationCount > 0) console.log(source_default.gray(` Integrations: ${integrationCount}`));
44581
+ if (checkpointCount > 0) console.log(source_default.gray(` Checkpoints: ${checkpointCount}`));
44582
+ const relationshipCount = org.relationships ? Object.keys(org.relationships).length : 0;
44583
+ if (relationshipCount > 0) {
44584
+ console.log(source_default.gray(` Relationships: ${relationshipCount}`));
44585
+ }
44586
+ const documentation = await scanDocumentation();
44587
+ if (documentation) {
44588
+ console.log(source_default.gray(` Docs: ${documentation.length} file${documentation.length !== 1 ? "s" : ""}`));
44589
+ }
44590
+ const schemaWarnings = [];
44591
+ for (const w of org.workflows ?? []) {
44592
+ if (w.contract.inputSchema) {
44593
+ try {
44594
+ external_exports.toJSONSchema(w.contract.inputSchema);
44595
+ } catch {
44596
+ schemaWarnings.push(`${w.config.resourceId}: inputSchema could not be serialized`);
44597
+ }
44598
+ }
44599
+ if (w.contract.outputSchema) {
44600
+ try {
44601
+ external_exports.toJSONSchema(w.contract.outputSchema);
44602
+ } catch {
44603
+ schemaWarnings.push(`${w.config.resourceId}: outputSchema could not be serialized`);
44604
+ }
44605
+ }
44606
+ }
44607
+ for (const a of org.agents ?? []) {
44608
+ if (a.contract.inputSchema) {
44609
+ try {
44610
+ external_exports.toJSONSchema(a.contract.inputSchema);
44611
+ } catch {
44612
+ schemaWarnings.push(`${a.config.resourceId}: inputSchema could not be serialized`);
44613
+ }
44614
+ }
44615
+ if (a.contract.outputSchema) {
44616
+ try {
44617
+ external_exports.toJSONSchema(a.contract.outputSchema);
44618
+ } catch {
44619
+ schemaWarnings.push(`${a.config.resourceId}: outputSchema could not be serialized`);
44620
+ }
44621
+ }
44622
+ }
44623
+ if (schemaWarnings.length > 0) {
44624
+ console.log("");
44625
+ for (const warning of schemaWarnings) {
44626
+ console.log(source_default.yellow(` warn ${warning}`));
44627
+ }
44628
+ console.log(source_default.gray(" Schemas will be unavailable on the platform for these resources."));
44629
+ }
44630
+ } catch (error46) {
44631
+ if (error46 instanceof RegistryValidationError) {
44632
+ spinner.fail(source_default.red("Validation failed"));
44633
+ console.error("");
44634
+ console.error(source_default.red(` ERROR ${error46.message}`));
44635
+ if (error46.resourceId) {
44636
+ console.error(source_default.gray(` Resource: ${error46.resourceId}`));
44637
+ }
44638
+ if (error46.field) {
44639
+ console.error(source_default.gray(` Field: ${error46.field}`));
44640
+ }
44641
+ console.error("");
44642
+ console.error(source_default.gray(" 1 error. Fix the issue and run again."));
44643
+ }
44644
+ throw error46;
44645
+ }
44646
+ }));
44647
+ }
44648
+
44560
44649
  // src/cli/commands/exec.ts
44650
+ var POLL_INTERVAL_MS = 3e3;
44651
+ async function pollForCompletion(resourceId, executionId, apiUrl) {
44652
+ const pollSpinner = ora("Waiting for completion...").start();
44653
+ const startTime = Date.now();
44654
+ const cleanup = () => {
44655
+ pollSpinner.stop();
44656
+ console.log();
44657
+ console.log(source_default.yellow("Polling stopped. Execution continues on server."));
44658
+ console.log(source_default.gray(" Execution ID:"), source_default.cyan(executionId));
44659
+ console.log(source_default.dim(" Check status manually or re-run with --async"));
44660
+ process.exit(0);
44661
+ };
44662
+ process.on("SIGINT", cleanup);
44663
+ while (true) {
44664
+ await new Promise((resolve6) => setTimeout(resolve6, POLL_INTERVAL_MS));
44665
+ const elapsed = Math.round((Date.now() - startTime) / 1e3);
44666
+ pollSpinner.text = `Waiting for completion... (${elapsed}s)`;
44667
+ try {
44668
+ const detail = await apiGet(
44669
+ `/api/external/executions/${resourceId}/${executionId}`,
44670
+ apiUrl
44671
+ );
44672
+ if (detail.status === "completed" || detail.status === "failed") {
44673
+ process.removeListener("SIGINT", cleanup);
44674
+ if (detail.status === "completed") {
44675
+ pollSpinner.succeed(source_default.green(`Execution completed (${elapsed}s)`));
44676
+ console.log(source_default.gray(" Execution ID:"), source_default.cyan(executionId));
44677
+ console.log("");
44678
+ console.log(source_default.green(" Output:"));
44679
+ console.log(JSON.stringify(detail.result, null, 2));
44680
+ } else {
44681
+ pollSpinner.fail(source_default.red(`Execution failed (${elapsed}s)`));
44682
+ console.log(source_default.gray(" Execution ID:"), source_default.cyan(executionId));
44683
+ console.log(source_default.red(" Error:"), detail.error || "Unknown error");
44684
+ }
44685
+ if (detail.startTime && detail.endTime) {
44686
+ console.log("");
44687
+ console.log(source_default.gray(" Duration:"), `${detail.endTime - detail.startTime}ms`);
44688
+ }
44689
+ return;
44690
+ }
44691
+ } catch {
44692
+ process.removeListener("SIGINT", cleanup);
44693
+ pollSpinner.warn("Lost connection while polling");
44694
+ console.log();
44695
+ console.log(source_default.yellow("Could not reach server. Execution may still be running."));
44696
+ console.log(source_default.gray(" Execution ID:"), source_default.cyan(executionId));
44697
+ console.log(source_default.dim(" Check status manually or re-run with --async"));
44698
+ return;
44699
+ }
44700
+ }
44701
+ }
44561
44702
  function registerExecCommand(program3) {
44562
44703
  program3.command("exec <resourceId>").description(`Execute a deployed resource
44563
- Example: elevasis exec my-workflow -i '{"key":"value"}'`).option("-i, --input <json>", "Input data as JSON string").option("--api-url <url>", "API URL").action(wrapAction("exec", async (resourceId, options2) => {
44704
+ Example: elevasis exec my-workflow -i '{"key":"value"}'`).option("-i, --input <json>", "Input data as JSON string").option("--async", "Execute asynchronously with polling").option("--api-url <url>", "API URL").action(wrapAction("exec", async (resourceId, options2) => {
44564
44705
  const input = options2.input ? JSON.parse(options2.input) : {};
44565
44706
  const apiUrl = resolveApiUrl(options2.apiUrl);
44566
- const spinner = ora(`Executing ${resourceId}...`).start();
44567
- const result = await apiPost(
44568
- "/api/external/execute",
44569
- { resourceId, input },
44570
- apiUrl
44571
- );
44572
- if (result.success) {
44573
- spinner.succeed(source_default.green("Execution complete"));
44574
- console.log(source_default.gray(" Execution ID:"), source_default.cyan(result.executionId));
44707
+ if (options2.async) {
44708
+ const asyncSpinner = ora(`Starting async execution of ${resourceId}...`).start();
44709
+ const asyncResult = await apiPost(
44710
+ "/api/external/execute-async",
44711
+ { resourceId, input },
44712
+ apiUrl
44713
+ );
44714
+ asyncSpinner.succeed(source_default.green("Execution started"));
44715
+ console.log(source_default.gray(" Execution ID:"), source_default.cyan(asyncResult.executionId));
44575
44716
  console.log("");
44576
- console.log(source_default.green(" Output:"));
44577
- console.log(JSON.stringify(result.data, null, 2));
44578
- } else {
44717
+ await pollForCompletion(resourceId, asyncResult.executionId, apiUrl);
44718
+ return;
44719
+ }
44720
+ const spinner = ora(`Executing ${resourceId}...`).start();
44721
+ try {
44722
+ const result = await apiPost(
44723
+ "/api/external/execute",
44724
+ { resourceId, input },
44725
+ apiUrl
44726
+ );
44727
+ if (result.success) {
44728
+ spinner.succeed(source_default.green("Execution complete"));
44729
+ console.log(source_default.gray(" Execution ID:"), source_default.cyan(result.executionId));
44730
+ console.log("");
44731
+ console.log(source_default.green(" Output:"));
44732
+ console.log(JSON.stringify(result.data, null, 2));
44733
+ } else {
44734
+ spinner.fail(source_default.red("Execution failed"));
44735
+ console.log(source_default.gray(" Execution ID:"), source_default.cyan(result.executionId));
44736
+ }
44737
+ } catch (error46) {
44738
+ const message = error46 instanceof Error ? error46.message : String(error46);
44739
+ const isConnectionFailure = message.includes("fetch failed") || message.includes("ECONNRESET") || message.includes("socket hang up") || message.includes("network");
44740
+ if (isConnectionFailure) {
44741
+ spinner.warn("Connection lost -- execution may still be running");
44742
+ console.log();
44743
+ try {
44744
+ const recoverSpinner = ora("Recovering execution...").start();
44745
+ const listResult = await apiGet(
44746
+ `/api/external/executions/${resourceId}?limit=1`,
44747
+ apiUrl
44748
+ );
44749
+ const running = listResult.executions.find((e) => e.status === "running");
44750
+ if (running) {
44751
+ recoverSpinner.succeed(`Found running execution: ${running.id}`);
44752
+ console.log();
44753
+ await pollForCompletion(resourceId, running.id, apiUrl);
44754
+ } else {
44755
+ recoverSpinner.info("No running execution found -- it may have already completed");
44756
+ console.log(source_default.dim(" Check status manually or re-run with --async"));
44757
+ }
44758
+ } catch {
44759
+ console.log(source_default.yellow("Could not recover. The execution may still be running on the server."));
44760
+ console.log(source_default.dim(" Check status manually or re-run with --async"));
44761
+ }
44762
+ process.exit(0);
44763
+ }
44579
44764
  spinner.fail(source_default.red("Execution failed"));
44580
- console.log(source_default.gray(" Execution ID:"), source_default.cyan(result.executionId));
44765
+ throw error46;
44581
44766
  }
44582
44767
  }));
44583
44768
  }
@@ -44889,7 +45074,7 @@ var import_path3 = require("path");
44889
45074
  var import_promises2 = require("fs/promises");
44890
45075
 
44891
45076
  // src/cli/commands/templates/core/workspace.ts
44892
- var TEMPLATE_VERSION = 14;
45077
+ var TEMPLATE_VERSION = 21;
44893
45078
  function configTemplate() {
44894
45079
  return `import type { ElevasConfig } from '@elevasis/sdk'
44895
45080
 
@@ -45041,7 +45226,157 @@ elevasis exec echo --input '{"message": "hello"}'
45041
45226
 
45042
45227
  // src/cli/commands/templates/core/claude.ts
45043
45228
  function claudeSettingsTemplate() {
45044
- return JSON.stringify({ autoCompact: false }, null, 2) + "\n";
45229
+ return JSON.stringify({
45230
+ autoCompact: false,
45231
+ hooks: {
45232
+ PreToolUse: [
45233
+ {
45234
+ matcher: "Write|Edit|MultiEdit|Bash",
45235
+ hooks: [
45236
+ {
45237
+ type: "command",
45238
+ command: "node .claude/hooks/enforce-sdk-boundary.mjs"
45239
+ }
45240
+ ]
45241
+ }
45242
+ ]
45243
+ }
45244
+ }, null, 2) + "\n";
45245
+ }
45246
+ function claudeSdkBoundaryHookTemplate() {
45247
+ return String.raw`#!/usr/bin/env node
45248
+ // enforce-sdk-boundary.mjs
45249
+ // Blocks gh CLI, destructive git operations, and file writes outside the project root.
45250
+ // Allows git add, commit, push, pull, fetch -- used by /meta deploy.
45251
+
45252
+ import { resolve, normalize } from "node:path";
45253
+ import { appendFileSync, mkdirSync } from "node:fs";
45254
+
45255
+ const LOG_DIR = (process.env.CLAUDE_PROJECT_DIR ?? process.cwd()) + "/.claude/logs";
45256
+ const LOG_FILE = LOG_DIR + "/boundary-hook.log";
45257
+
45258
+ function log(msg) {
45259
+ try {
45260
+ mkdirSync(LOG_DIR, { recursive: true });
45261
+ appendFileSync(LOG_FILE, "[" + new Date().toISOString() + "] " + msg + "\n");
45262
+ } catch {}
45263
+ }
45264
+
45265
+ function deny(reason) {
45266
+ log("DENY -- " + reason);
45267
+ process.stdout.write(
45268
+ JSON.stringify({
45269
+ hookSpecificOutput: {
45270
+ hookEventName: "PreToolUse",
45271
+ permissionDecision: "deny",
45272
+ permissionDecisionReason: reason,
45273
+ },
45274
+ })
45275
+ );
45276
+ }
45277
+
45278
+ try {
45279
+ const chunks = [];
45280
+ for await (const chunk of process.stdin) chunks.push(chunk);
45281
+ const input = JSON.parse(Buffer.concat(chunks).toString());
45282
+
45283
+ const projectDir = normalize(process.env.CLAUDE_PROJECT_DIR ?? process.cwd());
45284
+ const sep = process.platform === "win32" ? "\\" : "/";
45285
+ const prefix = projectDir.endsWith("\\") || projectDir.endsWith("/") ? projectDir : projectDir + sep;
45286
+
45287
+ function isOutside(filePath) {
45288
+ const target = normalize(resolve(filePath));
45289
+ return !target.startsWith(prefix) && target !== projectDir;
45290
+ }
45291
+
45292
+ if (input.tool_name === "Bash") {
45293
+ const cmd = input.tool_input?.command ?? "";
45294
+
45295
+ // GitHub CLI -- blocked (affects shared remote state, user-initiated only)
45296
+ if (/\bgh\b/.test(cmd)) {
45297
+ deny(
45298
+ "BLOCKED: GitHub CLI (gh) command detected.\n" +
45299
+ "WHY: GitHub CLI operations affect shared remote state (PRs, issues, releases). These must be user-initiated.\n" +
45300
+ "INSTEAD: Ask the user to run this gh command manually."
45301
+ );
45302
+ process.exit(0);
45303
+ }
45304
+
45305
+ // Destructive git -- blocked
45306
+ if (/(?<!-)\bgit\s+reset\b/.test(cmd) && /--hard/.test(cmd)) {
45307
+ deny(
45308
+ "BLOCKED: git reset --hard detected.\n" +
45309
+ "WHY: Hard resets destroy uncommitted work and cannot be undone. This must be user-initiated.\n" +
45310
+ "INSTEAD: Ask the user to run this git command manually."
45311
+ );
45312
+ process.exit(0);
45313
+ }
45314
+
45315
+ if (/(?<!-)\bgit\s+clean\b/.test(cmd) && /-[a-zA-Z]*f/.test(cmd)) {
45316
+ deny(
45317
+ "BLOCKED: git clean -f detected.\n" +
45318
+ "WHY: Force-cleaning the working tree permanently removes untracked files. This must be user-initiated.\n" +
45319
+ "INSTEAD: Ask the user to run this git command manually."
45320
+ );
45321
+ process.exit(0);
45322
+ }
45323
+
45324
+ if (/(?<!-)\bgit\s+(rebase|merge)\b/.test(cmd)) {
45325
+ deny(
45326
+ "BLOCKED: git rebase/merge detected.\n" +
45327
+ "WHY: Rebase and merge rewrite history or combine branches in ways that require user judgment.\n" +
45328
+ "INSTEAD: Ask the user to run this git command manually."
45329
+ );
45330
+ process.exit(0);
45331
+ }
45332
+
45333
+ // Path-scoped blocks -- destructive commands or redirects outside project root
45334
+ const winPaths = cmd.match(/(?<![A-Za-z])[A-Za-z]:[/\\][^\s"'|;&)]+/g) || [];
45335
+ const unixPaths = cmd.match(/(?<=\s|^|"|')\/[^\s"'|;&)]+/g) || [];
45336
+ const allPaths = [...winPaths, ...unixPaths]
45337
+ .map((p) => p.trim())
45338
+ .filter((p) => !p.startsWith("/dev/"));
45339
+
45340
+ const outsidePaths = allPaths.filter((p) => isOutside(p));
45341
+
45342
+ if (outsidePaths.length > 0) {
45343
+ const hasDestructiveCmd = /(?<![-])\b(rm|rmdir|del|unlink|mv|cp|touch|mkdir|chmod|chown|truncate|tee|dd|install)\b/.test(cmd);
45344
+ const hasInPlaceEdit = /\bsed\b.*\s-i/.test(cmd);
45345
+ const hasRedirect = />{1,2}\s*[^\s&>]/.test(cmd);
45346
+
45347
+ const hasTempPath = outsidePaths.some(
45348
+ (p) => /[/\\]tmp[/\\]/i.test(p) || /[/\\]Temp[/\\]/i.test(p)
45349
+ );
45350
+
45351
+ if (hasDestructiveCmd || hasInPlaceEdit || hasRedirect) {
45352
+ const instead = hasTempPath
45353
+ ? "INSTEAD: Use pipes instead of temp files: cmd 2>&1 | grep pattern not cmd > /tmp/out.txt. All file writes must target paths inside the project."
45354
+ : "INSTEAD: Only files within the project directory may be modified. Ask the user to run this command manually if external paths are needed.";
45355
+
45356
+ deny(
45357
+ "BLOCKED: destructive command references path(s) outside the project: " + outsidePaths.join(", ") + ".\n" +
45358
+ "WHY: File modifications outside the project boundary are not allowed.\n" +
45359
+ instead
45360
+ );
45361
+ }
45362
+ }
45363
+ } else {
45364
+ // Write, Edit, MultiEdit: check file_path
45365
+ const filePath = input.tool_input?.file_path;
45366
+ if (filePath && isOutside(filePath)) {
45367
+ deny(
45368
+ "BLOCKED: " + filePath + " is outside the project.\n" +
45369
+ "WHY: [" + (input.tool_name ?? "Unknown") + "] file operations outside the project boundary are not allowed.\n" +
45370
+ "INSTEAD: Only files within the project directory may be modified."
45371
+ );
45372
+ }
45373
+ }
45374
+ } catch (err) {
45375
+ log("ERROR: " + err.message + "\n" + err.stack);
45376
+ }
45377
+
45378
+ process.exit(0);
45379
+ `;
45045
45380
  }
45046
45381
  function claudeMdTemplate(ctx = {}) {
45047
45382
  return `<!-- initialized: false -->
@@ -45063,8 +45398,10 @@ At the start of every session:
45063
45398
  3. Check installed \`@elevasis/sdk\` template version against \`templateVersion\`
45064
45399
  in \`elevasis.config.ts\`. If newer, notify and suggest \`/meta update\`.
45065
45400
  4. If \`.claude/memory/\` does not exist, suggest \`/meta init\`.
45066
- 5. If user TypeScript level is beginner (from skills.md) and
45401
+ 5. If user Platform Navigation level is none (from skills.md) and
45067
45402
  \`.claude/memory/tutorial-progress.md\` does not exist, suggest \`/tutorial\`.
45403
+ If tutorial-progress.md exists and Phase is not complete, mention that the
45404
+ tutorial has more to explore.
45068
45405
 
45069
45406
  Do this silently. Do not narrate the steps to the user.
45070
45407
 
@@ -45092,8 +45429,10 @@ proactivity -- to their assessed levels.
45092
45429
  | Interaction guidance | \`reference/developer/interaction-guidance.mdx\` | Unsure how to adapt for a skill combination |
45093
45430
  | Error history | \`.claude/memory/errors/index.md\` | Debugging errors, checking past fixes |
45094
45431
  | Command View model | \`reference/deployment/command-view.mdx\` | Deploying or building resources that invoke other resources |
45432
+ | Command Center UI reference | \`reference/deployment/command-center-ui.mdx\` | User asks about post-deployment UI or Command Center navigation |
45095
45433
  | SDK error reference | \`reference/troubleshooting/common-errors.mdx\` | Unknown error not in workspace memory |
45096
45434
  | Project map | \`docs/project-map.mdx\` | Session start, project orientation |
45435
+ | Resource inventory | \`docs/resource-map.mdx\` | Finding a specific resource by name or ID |
45097
45436
  | Project priorities | \`docs/priorities.mdx\` | Deciding what to work on next |
45098
45437
  | User profile and skills | \`.claude/memory/profile/skills.md\` | Session start (mandatory) |
45099
45438
  | Cross-session memory | \`.claude/memory/index.md\` | Session start, recalling past context |${ctx.hasUI ? `
@@ -45108,6 +45447,7 @@ SDK patterns (imports, source structure, platform tools) are auto-loaded from
45108
45447
  \`.claude/rules/workspace-patterns.md\`.
45109
45448
 
45110
45449
  - Documentation goes in \`docs/\` as \`.mdx\` files
45450
+ - Resources are not visible in the Command Center until deployed. Always deploy before directing users to verify in the UI.
45111
45451
 
45112
45452
  ### Error Handling
45113
45453
 
@@ -45136,10 +45476,10 @@ based on what you find.
45136
45476
 
45137
45477
  - Match vocabulary to the user's level. Avoid jargon for beginners;
45138
45478
  be precise for experts. Define technical terms in parentheses the first time.
45139
- - Show complete, working files for users below intermediate programming.
45140
- Never show code fragments they can't place.
45479
+ - Provide step-by-step UI navigation for users with Platform Navigation below comfortable.
45480
+ Reference exact page names and paths until they are oriented.
45141
45481
  - Explain "why" before "how" for users new to automation.
45142
- - For users comfortable with code, focus on SDK-specific patterns.
45482
+ - For users comfortable with the platform, focus on SDK-specific patterns and advanced operations.
45143
45483
  - Leverage domain expertise -- if the user knows sales but not code,
45144
45484
  ask for business process descriptions and translate.
45145
45485
  - When growth is observed, note it in the skills.md Growth Log.
@@ -45152,9 +45492,8 @@ For detailed per-dimension adaptation rules, read
45152
45492
  | Command | Purpose |
45153
45493
  | --- | --- |
45154
45494
  | \`/meta\` | Project lifecycle: init, status, fix, deploy, health |
45155
- | \`/docs\` | Documentation lifecycle: create, review, verify |
45156
45495
  | \`/work\` | Task tracking: create, save, resume, complete |
45157
- | \`/tutorial\` | Progressive learning path |
45496
+ | \`/tutorial\` | Progressive learning path (7 core lessons + 9 modules) |
45158
45497
 
45159
45498
  ## Skills
45160
45499
 
@@ -45200,122 +45539,539 @@ Do not store in \`.claude/memory/\`:
45200
45539
  - If an error pattern recurs 3+ times, promote to Rules above
45201
45540
  `;
45202
45541
  }
45203
- function claudeDocsCommandTemplate() {
45204
- return `# /docs command
45542
+ function claudeTutorialCommandTemplate() {
45543
+ return `# /tutorial command
45205
45544
 
45206
- You are a documentation assistant for this Elevasis workspace.
45545
+ You are a tutorial guide for this Elevasis workspace.
45207
45546
 
45208
45547
  ## Context
45209
45548
 
45210
- Read the project's CLAUDE.md and all files in docs/ to understand the project.
45211
- Read src/index.ts and the domain directories (src/operations/, src/example/, etc.) to understand the resource definitions.
45549
+ Read \`.claude/memory/profile/skills.md\` first. The \`automation\` skill level
45550
+ controls which docs you load and which lesson variant you deliver.
45212
45551
 
45213
- ## Operations
45552
+ **automation: none**
45553
+ - Load from \`reference/concepts/index.mdx\`: Glossary, What is a Workflow,
45554
+ Platform Tools Overview only. Skip Zod, Execution Model, Design Decisions.
45555
+ - Load \`reference/deployment/command-center-ui.mdx\` for UI-first teaching.
45556
+ - Do NOT load \`reference/resources/patterns.mdx\` or
45557
+ \`reference/platform-tools/adapters.mdx\` during core lessons.
45214
45558
 
45215
- **No arguments (default):** Review existing docs/ files and suggest improvements.
45216
- Identify undocumented resources, missing descriptions, and structural gaps.
45559
+ **automation: low-code**
45560
+ - Load all sections of \`reference/concepts/index.mdx\`.
45561
+ - Load \`reference/resources/patterns.mdx\` with Zapier/Make mapping in mind.
45562
+ - Load \`reference/platform-tools/adapters.mdx\` on-demand (when tools are used).
45217
45563
 
45218
- **\`create <page-name>\`:** Create a new documentation page in docs/.
45219
- Use the frontmatter schema (title, description, order).
45220
- Populate with content based on the resource definitions in src/.
45564
+ **automation: custom**
45565
+ - Load all docs listed per-lesson and per-module as specified below.
45221
45566
 
45222
- **\`review\`:** Review all docs/ files for accuracy against the actual resource
45223
- definitions. Flag mismatches between documented schemas and code.
45567
+ Read \`.claude/memory/tutorial-progress.md\` to check current lesson progress.
45224
45568
 
45225
- **\`verify [path]\`:** Cross-reference documentation with the codebase.
45226
- Read the specified doc (or all docs if no path), compare claims against actual
45227
- code (resource IDs, schema fields, platform tools used), and report
45228
- discrepancies. Useful before \`/meta deploy\` to ensure docs are accurate.
45229
- `;
45230
- }
45231
- function claudeTutorialCommandTemplate() {
45232
- return `# /tutorial command
45569
+ ## Invocation
45233
45570
 
45234
- You are a tutorial guide for this Elevasis workspace.
45571
+ \`/tutorial\` -- ALWAYS shows the main menu first. Never silently auto-resumes without giving the user a choice. See ## Menu section for the exact menu format.
45235
45572
 
45236
- ## Context
45573
+ ## Menu
45237
45574
 
45238
- Read \`.claude/memory/profile/skills.md\` to adapt lesson pacing and vocabulary.
45239
- Read \`.claude/memory/tutorial-progress.md\` to check current lesson progress.
45240
- Read \`reference/concepts/index.mdx\` for teaching vocabulary and concept definitions.
45575
+ When \`/tutorial\` is invoked:
45241
45576
 
45242
- ## Invocation
45577
+ 1. Read \`.claude/memory/tutorial-progress.md\` (if it exists)
45578
+ 2. Display the appropriate menu:
45579
+
45580
+ **No progress (first time):**
45581
+
45582
+ \`\`\`
45583
+ Welcome to the Elevasis Tutorial!
45584
+ ==================================
45585
+
45586
+ What would you like to learn?
45587
+
45588
+ 1. Orchestration Concepts (Core Path)
45589
+ Build workflows step-by-step: data schemas, platform tools,
45590
+ multi-step flows, decision points, and production deployment.
45591
+ 7 lessons -- estimated 2-3 hours total.
45592
+
45593
+ 2. Examples & Advanced Modules
45594
+ Hands-on deep-dives: human-in-the-loop, scheduling,
45595
+ notifications, integrations, LLM, agents, and more.
45596
+ 9 standalone modules -- pick any order.
45597
+
45598
+ 3. Meta-Framework
45599
+ Learn the Claude Code context system: /meta, /work,
45600
+ rules, memory, and how to customize your workspace.
45601
+ 5 lessons -- estimated 1-2 hours total.
45602
+
45603
+ Pick a number to start, or say "status" to see your progress.
45604
+ \`\`\`
45605
+
45606
+ **With existing progress:**
45607
+
45608
+ Show active tracks at the top, then offer resume options alongside new choices. Example:
45609
+
45610
+ \`\`\`
45611
+ Progress: Core Path (Lesson 4/7) | Meta-Framework (MF2/5)
45612
+
45613
+ 1. Resume Orchestration Concepts (Lesson 4)
45614
+ 2. Orchestration Concepts (Core Path -- start over)
45615
+ 3. Examples & Advanced Modules
45616
+ 4. Resume Meta-Framework (MF2)
45617
+ 5. Meta-Framework (start over)
45618
+ 6. Show full status
45619
+
45620
+ Pick a number, or describe what you'd like to learn.
45621
+ \`\`\`
45622
+
45623
+ After user picks:
45624
+ - Orchestration Concepts: Resume or start core lesson flow
45625
+ - Examples & Advanced Modules: Show module menu (see ## Module Menu)
45626
+ - Meta-Framework: Resume or start MF1 (see ## Meta-Framework Track)
45627
+ - "status": Display Phase, current position, completion counts
45243
45628
 
45244
- - \`/tutorial\` -- Resume from current lesson. If no progress exists, start Lesson 1.
45245
- - \`/tutorial start\` -- Reset progress and begin from Lesson 1.
45246
- - \`/tutorial <number>\` -- Jump to a specific lesson (1-7).
45629
+ ## Progress Logic
45630
+
45631
+ 1. Read \`.claude/memory/tutorial-progress.md\`
45632
+ 2. Show the menu (## Menu section) with progress summary if progress exists
45633
+ 3. After user picks a track:
45634
+ - **Orchestration Concepts**: If Completed Lessons < 7 -> start/resume at Current Lesson; if 7 complete -> note completion, offer module menu or repeat
45635
+ - **Examples & Advanced Modules**: Show module menu (## Module Menu section)
45636
+ - **Meta-Framework**: If Current MF Lesson is set -> resume at that lesson; otherwise start MF1
45637
+ 4. Track completion: mark the track done in progress file when all lessons/modules complete
45638
+ 5. If all three tracks complete -> set Phase to \`complete\`, congratulate, suggest exploring docs/ or /work
45247
45639
 
45248
45640
  ## Lesson Flow
45249
45641
 
45250
45642
  Each lesson follows this flow:
45251
45643
  1. Announce lesson title and what they'll learn (1-2 sentences)
45252
- 2. Explain the concept (read concepts page, adapt to skill level)
45253
- 3. Guide user to build or modify something (show complete code for beginners)
45254
- 4. Verify it works (run CLI command, check output)
45644
+ 2. Explain the concept (read docs per skill level, adapt to user)
45645
+ 3. Guide user to build or modify something (agent writes all code for automation: none)
45646
+ 4. Verify it works (Execution Runner is primary for none; CLI + UI are equal for others)
45255
45647
  5. Celebrate success, record observations in \`.claude/memory/tutorial-progress.md\`
45256
45648
  6. Ask: "Ready for the next lesson, or want to practice more?"
45257
45649
 
45258
45650
  ## Lessons
45259
45651
 
45260
45652
  **Lesson 1: Welcome & Orientation**
45653
+
45654
+ When automation is none:
45655
+ Skip the file tour. Start with what Elevasis does for their business -- use analogies
45656
+ from \`reference/developer/interaction-guidance.mdx\` (recipe, assembly line, kitchen
45657
+ appliance). Explain deployment plainly: "You write the recipe here, then deploy it so
45658
+ it's live." Deploy the starter echo workflow (\`elevasis check\` + \`elevasis deploy\`),
45659
+ THEN tour the Command Center so the user sees populated pages, not empty ones. Tour:
45660
+ Command View (echo node), Execution Runner (run echo from form), Execution Logs (result).
45661
+ Observation focus: automation value understanding, Command Center comfort.
45662
+
45663
+ When automation is low-code or custom:
45261
45664
  Tour project files: src/index.ts (registry), src/example/echo.ts (starter
45262
45665
  workflow), src/operations/platform-status.ts (platform API example),
45263
45666
  elevasis.config.ts, .env, docs/. Explain the execution model.
45264
- Verify: run \`elevasis resources\`. Observation focus: cloud deployment model.
45667
+ Verify: run \`elevasis resources\`. Then open the Command Center and tour the
45668
+ main pages: Command View (resource graph), Execution Runner, Execution Logs.
45669
+ Point out the echo workflow node in Command View.
45670
+ Observation focus: cloud deployment model, UI navigation comfort.
45265
45671
 
45266
45672
  **Lesson 2: Your First Custom Workflow**
45673
+
45674
+ When automation is none:
45675
+ Frame the workflow as "a recipe." Use plain language: "settings" not "config",
45676
+ "what data it needs" not "contract", "instructions" not "steps", "where it starts"
45677
+ not "entryPoint." Agent writes all code changes. Execution Runner form is PRIMARY
45678
+ verification -- show user how to fill the form and run it. CLI is secondary:
45679
+ "here's the power user way." Observation focus: recipe-to-result connection, form comfort.
45680
+
45681
+ When automation is low-code or custom:
45267
45682
  Modify the echo workflow. Walk through each part: config, contract, steps,
45268
45683
  entryPoint. Deploy: \`elevasis check\` then \`elevasis deploy\`. Test with
45269
- \`elevasis exec echo --input '{"message":"hello"}'\`.
45270
- Observation focus: TypeScript syntax comfort.
45684
+ \`elevasis exec echo --input '{"message":"hello"}'\`. Then open the Execution
45685
+ Runner in the Command Center, find the echo workflow, fill out the form, and
45686
+ run it from the UI. Compare the result to the CLI output.
45687
+ Observation focus: deployment-to-execution loop, TypeScript syntax comfort.
45271
45688
 
45272
45689
  **Lesson 3: Understanding Data (Schemas)**
45690
+
45691
+ When automation is none:
45692
+ Frame as "What information does your automation need?" Each piece becomes a form
45693
+ field. Describe types in plain English: text, number, yes/no, a choice from a list.
45694
+ NO Zod, no z.string(), no z.infer(), no code shown. Agent reads identity.md goals
45695
+ and writes the workflow schema. User fills the form in Execution Runner to verify.
45696
+ Observation focus: data-to-form connection, required vs optional understanding.
45697
+
45698
+ When automation is low-code:
45699
+ "Field mapping like Zapier, but with validation." Show Zod types briefly. Demonstrate
45700
+ how \`.describe()\` sets the form field label. Build schema based on identity.md goals.
45701
+ After deploy, open Execution Runner and point out each field.
45702
+ Observation focus: schema-to-form connection, optional fields, types.
45703
+
45704
+ When automation is custom:
45273
45705
  Explain schemas in plain English (concepts page). Show common Zod types.
45274
45706
  Explain \`z.infer\`. Build a new workflow with real-world input schema based
45275
- on the user's goals (read .claude/memory/profile/identity.md).
45276
- Observation focus: optional fields, types, suggesting own fields.
45707
+ on the user's goals (read .claude/memory/profile/identity.md). After deploy,
45708
+ open the Execution Runner and show how each Zod field becomes a form control.
45709
+ Point out how \`.describe()\` sets the field label.
45710
+ Observation focus: schema-to-form connection, optional fields, types.
45277
45711
 
45278
45712
  **Lesson 4: Using Platform Tools**
45713
+
45714
+ When automation is none:
45715
+ Frame as "Connecting your automation to a service you already use." Pick ONE
45716
+ service from identity.md tools. Agent makes the code edit -- no adapter or
45717
+ singleton explanation, no imports shown. Guide credential creation step-by-step
45718
+ via Command Center UI (Settings > Credentials). Show where the connection appears
45719
+ in Command View after deploy.
45720
+ Observation focus: service-connection concept, credential comfort.
45721
+
45722
+ When automation is low-code or custom:
45279
45723
  Explain platform tools (concepts page). Browse available tools via
45280
45724
  reference/platform-tools/index.mdx. Pick a tool based on user's goals.
45281
45725
  Build: add a tool step using typed adapters (preferred) or platform.call().
45282
45726
  Show adapter pattern: \`const attio = createAttioAdapter('cred')\`.
45283
45727
  Show singleton pattern: \`import { scheduler, llm } from '@elevasis/sdk/worker'\`.
45284
- Explain credential setup. See reference/platform-tools/adapters.mdx for full API.
45285
- Observation focus: credential model, async/await.
45728
+ Guide credential creation: for API keys, use the \`creds\` skill or Settings >
45729
+ Credentials in the Command Center. For OAuth credentials, the UI is required.
45730
+ If using the approval tool, note that pending requests surface in Command Queue.
45731
+ See reference/platform-tools/adapters.mdx for full API.
45732
+ Observation focus: credential setup (CLI + UI), async/await.
45286
45733
 
45287
45734
  **Lesson 5: Multi-Step Workflows**
45735
+
45736
+ When automation is none:
45737
+ Frame as "Step 1 passes its result to Step 2, like a relay race." Command View is
45738
+ PRIMARY teaching tool -- show the visual graph before explaining code. Agent builds
45739
+ the 2-step workflow. User verifies in Command View (see the two nodes + arrow) and
45740
+ Execution Runner (see output flow). Code is secondary.
45741
+ Observation focus: relay-race concept, visual graph comprehension.
45742
+
45743
+ When automation is low-code or custom:
45288
45744
  Chain steps with StepType.LINEAR. Build a 2-step workflow. Explain data
45289
- flow between steps. Deploy and test.
45290
- Observation focus: data flow reasoning.
45745
+ flow between steps. Deploy and test. Then open the Command View and show the
45746
+ relationship edges between resources. Explain how declared relationships map
45747
+ to the visual graph.
45748
+ Observation focus: data flow reasoning, relationship visualization.
45291
45749
 
45292
45750
  **Lesson 6: Decision Points**
45751
+
45752
+ When automation is none:
45753
+ Frame as "If the customer is VIP, do this -- otherwise, do that." No StepType.CONDITIONAL
45754
+ jargon -- focus on the concept, not the implementation. Agent adds the condition.
45755
+ User tests both paths via Execution Runner, then opens Execution Logs to see which
45756
+ path ran. Guide log navigation step-by-step.
45757
+ Observation focus: branching concept understanding, log navigation.
45758
+
45759
+ When automation is low-code or custom:
45293
45760
  Conditional routing with StepType.CONDITIONAL. Add a condition to the
45294
- multi-step workflow from Lesson 5. Test both paths.
45295
- Observation focus: branching logic reasoning.
45761
+ multi-step workflow from Lesson 5. Test both paths. After testing, open
45762
+ Execution Logs in the Command Center, filter by the resource, and show how
45763
+ each path appears in the log detail (step trace, input/output).
45764
+ Observation focus: branching logic reasoning, execution log interpretation.
45296
45765
 
45297
45766
  **Lesson 7: Going to Production**
45298
- Change status from dev to production. Show monitoring: elevasis executions,
45299
- elevasis execution. Cover error handling: try/catch, ExecutionError,
45300
- PlatformToolError. Suggest next steps based on goals.
45301
- Observation focus: readiness for independent development.
45767
+
45768
+ When automation is none:
45769
+ Frame as "draft vs live" not "dev vs production." Error handling: "when something
45770
+ goes wrong with your data" / "when a connected service fails" -- no type names.
45771
+ Task Scheduler and Knowledge Base stay as-is (UI-friendly already). Open Deployments
45772
+ page to show version is active.
45773
+ Observation focus: draft/live concept, readiness for independent Command Center use.
45774
+
45775
+ When automation is low-code or custom:
45776
+ Change status from dev to production. Cover error handling: try/catch,
45777
+ ExecutionError, PlatformToolError. Create a schedule in Task Scheduler (use
45778
+ Recurring type for a cron schedule). If docs/ has pages, show Knowledge Base.
45779
+ Open Deployments page to confirm the latest version is active. Show CLI
45780
+ monitoring: elevasis executions, elevasis execution. Suggest next steps.
45781
+ Observation focus: readiness for independent operation (CLI + UI).
45782
+
45783
+ ## Module Menu
45784
+
45785
+ Fixed module order: hitl, schedules, notifications, integrations, workflows,
45786
+ composition, llm, agents, error-handling.
45787
+
45788
+ Show the next 2-3 uncompleted modules from the list. Format:
45789
+
45790
+ "Core path complete! Here are your next modules:"
45791
+ 1. <Title> -- <one-line description>
45792
+ 2. <Title> -- <one-line description>
45793
+ 3. <Title> -- <one-line description>
45794
+ "Pick a number to start, or say 'show more' to see additional modules."
45795
+
45796
+ If all 9 modules are complete -> set Phase to \`complete\`, congratulate.
45797
+
45798
+ ## Module Flow
45799
+
45800
+ Each module follows this flow:
45801
+ 1. Announce module title and what they'll build (1-2 sentences)
45802
+ 2. Read the listed SDK reference docs for teaching context
45803
+ 3. Guide user to build a resource exercising the module's concepts
45804
+ 4. Verify it works (CLI + Command Center where applicable)
45805
+ 5. Record observations in \`.claude/memory/tutorial-progress.md\`
45806
+ 6. Return to module menu (show next uncompleted modules)
45807
+
45808
+ ## Modules
45809
+
45810
+ **Module: hitl -- Human-in-the-Loop**
45811
+ Read: \`reference/deployment/command-center-ui.mdx\` (Command Queue section).
45812
+ Build: Add an approval gate using \`approval.requestApproval()\`. Test full lifecycle:
45813
+ trigger, see pending in Command Queue, approve/reject, observe resume.
45814
+ Key concepts: approval adapter, pending state, Command Queue UI, resume on decision.
45815
+ Verify: Trigger workflow, open Command Queue, approve, confirm completion.
45816
+
45817
+ **Module: schedules -- Task Scheduling**
45818
+ Read: \`reference/deployment/command-center-ui.mdx\` (Task Scheduler section).
45819
+ Build: Create all three schedule types (Recurring cron, Relative delay, Absolute
45820
+ datetime). Use \`scheduler\` adapter for in-workflow scheduling.
45821
+ Key concepts: schedule types, cron syntax, scheduler adapter, Task Scheduler UI.
45822
+ Verify: Create each type in Task Scheduler, confirm scheduled execution in Execution Logs.
45823
+
45824
+ **Module: notifications -- Notification System**
45825
+ Read: \`reference/platform-tools/adapters.mdx\` (notifications, email singletons).
45826
+ Build: Add notification and email steps to a workflow. Send alerts on completion.
45827
+ Key concepts: notifications singleton, email singleton, alert patterns.
45828
+ Verify: Run workflow, check notification in Command Center, confirm email received.
45829
+
45830
+ **Module: integrations -- Real-World Integrations**
45831
+ Read: \`reference/platform-tools/index.mdx\`, \`reference/platform-tools/adapters.mdx\`,
45832
+ \`reference/security/credentials.mdx\`.
45833
+ Build: Pick a real integration adapter based on user's goals (read \`identity.md\`).
45834
+ Set up credential (OAuth via UI, API key via CLI). Build end-to-end integration workflow.
45835
+ Key concepts: adapter pattern, credential scoping, error handling for external calls.
45836
+ Verify: Run workflow with real credential, confirm external service call, test error
45837
+ handling with invalid credential.
45838
+
45839
+ **Module: workflows -- Advanced Workflows**
45840
+ Read: \`reference/resources/patterns.mdx\` (execution store, logging, organizing).
45841
+ Build: Refactor an existing workflow to use \`context.store\` for cross-step data,
45842
+ \`context.logger\` for structured logging, and organize into a domain directory.
45843
+ Add advanced schema patterns (nested objects, arrays, optional fields).
45844
+ Key concepts: context.store, context.logger, domain organization, schema depth.
45845
+ Verify: Run workflow, confirm store values in step output, check logs in Execution Logs.
45846
+
45847
+ **Module: composition -- Resource Composition**
45848
+ Read: \`reference/deployment/command-view.mdx\`, \`reference/resources/patterns.mdx\`.
45849
+ Build: Create two workflows where the first triggers the second using
45850
+ \`execution.trigger()\`. Declare the relationship.
45851
+ Key concepts: execution.trigger, relationship declarations, Command View graph edges.
45852
+ Verify: Deploy, see relationship edge in Command View, trigger parent and confirm
45853
+ child executes.
45854
+
45855
+ **Module: llm -- LLM Integration**
45856
+ Read: \`reference/platform-tools/adapters.mdx\` (llm singleton).
45857
+ Build: Create a workflow step using \`llm.generate()\` with structured output.
45858
+ Experiment with model selection and temperature.
45859
+ Key concepts: llm singleton, structured output, model selection, temperature.
45860
+ Verify: Run workflow, compare outputs with different settings, confirm structured output.
45861
+
45862
+ **Module: agents -- AI Agents**
45863
+ Read: \`reference/framework/agent.mdx\`.
45864
+ Build: Create an agent definition with tools. Configure LLM tool calling.
45865
+ Compare agent vs workflow for a task.
45866
+ Key concepts: agent definition, tool registration, LLM tool calling, execution trace.
45867
+ Verify: Run agent with \`elevasis exec\`, review tool call trace in Execution Logs.
45868
+
45869
+ **Module: error-handling -- Error Handling Mastery**
45870
+ Read: \`reference/resources/patterns.mdx\` (error handling),
45871
+ \`reference/troubleshooting/common-errors.mdx\`.
45872
+ Build: Create a workflow demonstrating all three error types. Add try/catch,
45873
+ \`context.logger\`, and error recovery.
45874
+ Key concepts: ExecutionError, PlatformToolError, ToolingError, recovery patterns.
45875
+ Verify: Trigger each error type, confirm messages in Execution Logs with correct
45876
+ categorization.
45877
+
45878
+ ## Meta-Framework Track
45879
+
45880
+ The Meta-Framework track teaches you how the Claude Code workspace works -- the commands,
45881
+ rules, memory system, and customization model. It is independent of the core path and
45882
+ can be taken in any order.
45883
+
45884
+ **MF1: The Agent Framework -- How This Workspace Works**
45885
+
45886
+ When automation is none:
45887
+ "This workspace comes with a built-in assistant that knows your project, your tools,
45888
+ and your goals. Let me show you how it's set up." Open CLAUDE.md and explain in
45889
+ plain terms: it's the agent's instruction sheet. Point out the commands in the
45890
+ Commands table. Show /meta, /tutorial, /work. Explain the creds skill as
45891
+ "the assistant automatically helps when you mention API keys." Tour the memory folder
45892
+ at a high level -- "this is where the agent stores what it learns about your project."
45893
+ Verify: Ask the user a question about their business goal and show how the agent
45894
+ references their profile in the answer.
45895
+ Observation focus: agent-as-assistant concept, CLAUDE.md as instruction sheet.
45896
+
45897
+ When automation is low-code:
45898
+ Read CLAUDE.md and walk through each section. Explain: what the agent reads on
45899
+ session start, how the navigation table works, what the Skills section means.
45900
+ Explain the four commands briefly. Show that the agent has memory: open
45901
+ \`.claude/memory/profile/skills.md\` and show their own profile -- "every session,
45902
+ the agent reads this and adapts." Explain the initialized flag.
45903
+ Verify: Run /meta to see project status.
45904
+ Observation focus: memory system concept, session initialization flow.
45905
+
45906
+ When automation is custom:
45907
+ Read CLAUDE.md in full. Explain the session initialization sequence: CLAUDE.md ->
45908
+ navigation table -> memory files -> context loading. Walk through: Commands section
45909
+ (4 commands + creds skill), Rules section (auto-loaded based on file paths), Skills
45910
+ section (auto-triggered by content patterns). Point out the initialized flag and
45911
+ explain how /meta init set it.
45912
+ Verify: Run /meta to see project status; observe which fields it reports.
45913
+ Observation focus: initialization model, command-vs-rule-vs-skill distinction.
45914
+
45915
+ **MF2: The /meta Command -- Project Lifecycle**
45916
+
45917
+ When automation is none:
45918
+ "Think of /meta as your project dashboard -- it shows what's healthy and what needs
45919
+ attention." Run /meta (no arguments) and narrate the output in plain language: what
45920
+ each field means. Explain /meta fix as "the agent tidies up and applies updates."
45921
+ Explain /meta deploy as "the agent publishes your changes in one step." Briefly note
45922
+ /meta health shows what happened when something goes wrong.
45923
+ Verify: Run /meta (no arguments). Narrate the output together.
45924
+ Observation focus: project lifecycle concept, dashboard reading.
45925
+
45926
+ When automation is low-code:
45927
+ Show all /meta operations with their purpose. Map to familiar concepts: /meta fix is
45928
+ like "repair this Zap" in Zapier; /meta deploy is a one-command publish pipeline.
45929
+ Walk through the /meta (no-args) output: template version (SDK template your workspace
45930
+ uses), SDK version (installed package), profile summary, drift check.
45931
+ Verify: Run /meta and interpret each field together.
45932
+ Observation focus: deploy pipeline understanding, version tracking.
45933
+
45934
+ When automation is custom:
45935
+ Read \`.claude/commands/meta.md\`. Walk through each operation: init (first-run setup
45936
+ with assessment), (no-args) (status dashboard), fix (drift repair + SDK upgrade +
45937
+ rules health), deploy (7-step pipeline: check, typecheck, docs, git, deploy,
45938
+ project-map, verify), health (runtime diagnostics). Explain the merge strategy for
45939
+ CLAUDE.md and commands. Note the template access model: templates read from
45940
+ @elevasis/sdk/templates subpath.
45941
+ Verify: Run /meta to see project status. Identify what a version mismatch looks like.
45942
+ Observation focus: full lifecycle coverage, pipeline internals.
45943
+
45944
+ **MF3: /work -- Task Lifecycle**
45945
+
45946
+ When automation is none:
45947
+ "You can ask the assistant to track work across conversations. When you start something
45948
+ complex, use /work create to save your place. Next session, /work resume picks up where
45949
+ you left off." Walk through the concept without deep command details. Show docs/ -- explain
45950
+ it's where project notes live.
45951
+ Verify: Create a task with \`/work create "practice task"\`, then run /work to see it listed.
45952
+ Observation focus: persistence concept, cross-session continuity.
45953
+
45954
+ When automation is low-code:
45955
+ Show /work operations: create (task doc with frontmatter + sections), save (updates
45956
+ Progress + Resume Context), resume (loads context for next session).
45957
+ Explain how task docs persist: they're workspace files, not memory -- they survive
45958
+ session compaction.
45959
+ Verify: Create a task with \`/work create "practice task"\`, run /work save, inspect the file.
45960
+ Observation focus: task tracking workflow, doc creation pattern.
45961
+
45962
+ When automation is custom:
45963
+ Read \`.claude/commands/work.md\`. Full coverage:
45964
+ /work create (kebab-case filename, frontmatter with status, Objective/Plan/Progress/
45965
+ Resume Context sections), /work save (Progress + Resume Context update), /work resume
45966
+ (multiple-task disambiguation), /work complete (moves to final location).
45967
+ Explain how Resume Context serves as the session handoff artifact.
45968
+ Verify: Create a task doc, save progress, inspect the generated file structure.
45969
+ Observation focus: task doc anatomy, resume context as handoff pattern.
45970
+
45971
+ **MF4: Rules, Memory, and Customization**
45972
+
45973
+ When automation is none:
45974
+ "The assistant has a set of reminders specific to your project. Over time, when it
45975
+ makes the same mistake 3 times, you can tell it to always remember that rule -- it
45976
+ lives in a file so the rule sticks across conversations."
45977
+ Show \`.claude/rules/workspace-patterns.md\` without technical detail. Explain: "This
45978
+ is where YOUR project's rules go. The other rule files are updated automatically by
45979
+ SDK releases."
45980
+ Verify: Open \`.claude/rules/workspace-patterns.md\` and read the current content together.
45981
+ Observation focus: customization concept, owned vs managed files.
45982
+
45983
+ When automation is low-code:
45984
+ Show the \`.claude/rules/\` directory. Explain the two types: sdk-patterns.md (MANAGED --
45985
+ updated by elevasis update) and workspace-patterns.md (INIT_ONLY -- yours to edit).
45986
+ Explain path-scoping briefly: "These rules only activate when the agent is working on
45987
+ certain file types." Show an example rule with WRONG/RIGHT pattern. Explain error
45988
+ promotion: if something goes wrong 3+ times, add it here.
45989
+ Verify: Read \`.claude/rules/workspace-patterns.md\`. Note the "When to Add a Rule" section.
45990
+ Observation focus: two-tier ownership model, rule format.
45991
+
45992
+ When automation is custom:
45993
+ Read \`.claude/rules/sdk-patterns.md\` and \`.claude/rules/workspace-patterns.md\`.
45994
+ Explain MANAGED vs INIT_ONLY lifecycle. Show rule frontmatter structure (paths: for
45995
+ auto-loading by the agent). Explain the memory system layout: \`.claude/memory/\`
45996
+ (index.md root, profile/ subdirectory, errors/ for promoted issues,
45997
+ tutorial-progress.md). Explain error promotion: 3+ recurrences -> rule in
45998
+ workspace-patterns.md. Walk through how to add a new rule.
45999
+ Verify: Read \`.claude/rules/workspace-patterns.md\`. Add a sample rule entry together.
46000
+ Observation focus: MANAGED vs INIT_ONLY lifecycle, rule authoring, memory layout.
46001
+
46002
+ **MF5: Advanced -- Template Lifecycle and Extending**
46003
+
46004
+ When automation is none:
46005
+ "When Elevasis SDK releases updates, the assistant can apply them to your workspace
46006
+ automatically. You don't have to redo your customizations." Explain /meta fix as
46007
+ the command that applies updates. Skip technical file classification details.
46008
+ Show \`docs/project-map.mdx\` briefly: "This is an auto-generated map of everything
46009
+ in your project -- the agent uses it for navigation."
46010
+ Verify: Run /meta fix (or explain what it would do). Check \`docs/project-map.mdx\`.
46011
+ Next steps: point to reference docs in docs/, encourage using /work for new tasks.
46012
+ Observation focus: update model concept, project map as navigation aid.
46013
+
46014
+ When automation is low-code:
46015
+ Explain MANAGED (auto-updated by elevasis update) vs INIT_ONLY (set once, yours).
46016
+ Show how /meta fix applies SDK updates with conflict handling: "If a file changed,
46017
+ it shows you both versions and lets you decide."
46018
+ Show \`docs/project-map.mdx\` and explain what it contains: resources, commands,
46019
+ rules, memory index. Note that deploy auto-regenerates it.
46020
+ Verify: Read \`elevasis.config.ts\` templateVersion. Compare to the running SDK version from /meta.
46021
+ Observation focus: update workflow, project map freshness.
46022
+
46023
+ When automation is custom:
46024
+ Read \`elevasis.config.ts\` for the current templateVersion. Read \`docs/project-map.mdx\`.
46025
+ Explain the full template lifecycle: init (writes all files with classification),
46026
+ update (applies MANAGED changes, flags conflicts, preserves INIT_ONLY), fix (detects
46027
+ drift, offers SDK upgrade, runs full repair). Explain conflict handling: read current
46028
+ file, read template from @elevasis/sdk/templates, merge preserving customizations.
46029
+ Explain the project map: auto-generated by deploy, contains resource inventory,
46030
+ command list, rule list, memory index.
46031
+ Next steps: explore reference docs, use /work for complex tasks, build custom rules.
46032
+ Verify: Compare elevasis.config.ts templateVersion with output of /meta. Identify any drift.
46033
+ Observation focus: full template lifecycle, conflict resolution pattern.
45302
46034
 
45303
46035
  ## Adaptation Rules
45304
46036
 
45305
- - If user is intermediate/advanced (from skills.md), condense explanations
45306
- - If user struggles, slow down with more plain-English explanation
45307
- - If user is fast, acknowledge and offer to skip ahead
46037
+ **automation: none**
46038
+ Use the non-technical variant for each lesson. Agent writes all code -- user
46039
+ never types code. User verifies visually (Execution Runner, Command View, Logs).
46040
+ Avoid all jargon; use analogies. Execution Runner is the primary verification tool.
46041
+ Always deploy before directing the user to the Command Center.
46042
+
46043
+ **automation: low-code**
46044
+ Map concepts to Zapier/Make equivalents. Show code with plain-English explanations.
46045
+ Execution Runner and CLI are equal verification tools. Show Zod types with labels.
46046
+
46047
+ **automation: custom**
46048
+ Full technical content. Code-first. CLI and UI are equal. Condense explanations
46049
+ for intermediate/advanced users.
46050
+
46051
+ **General rules (all levels)**
46052
+ - If user is fast, acknowledge and offer to skip ahead within a lesson
45308
46053
  - After each lesson, update \`.claude/memory/tutorial-progress.md\`
45309
46054
  - If user demonstrates a level change, promote to skills.md Growth Log
46055
+ - After completing a module, return to the module menu
46056
+ - Adapt module depth to skill level as with lessons
45310
46057
 
45311
46058
  ## Progress Format
45312
46059
 
45313
46060
  Store in \`.claude/memory/tutorial-progress.md\`:
45314
- - Current Lesson number
46061
+ - Phase (core | modules | meta-framework | complete)
46062
+ - Current Lesson number (during core phase)
46063
+ - Current Module (module id during modules phase)
46064
+ - Current MF Lesson (MF1-MF5, during meta-framework phase)
45315
46065
  - Started and Last Session dates
45316
46066
  - Completed Lessons table (lesson, title, completed, duration)
45317
- - Capability Observations table (lesson, observation)
46067
+ - Completed Modules table (module, title, completed, duration)
46068
+ - Completed MF Lessons table (lesson, title, completed, notes)
46069
+ - Capability Observations table (lesson/module, observation) -- prefix L# for lessons, M:<id> for modules, MF# for meta-framework lessons
45318
46070
  - Assessment Notes (bullet points)
46071
+
46072
+ Backward-compatible: missing Phase is inferred from lesson count;
46073
+ missing Completed Modules is treated as empty;
46074
+ missing Completed MF Lessons is treated as empty.
45319
46075
  `;
45320
46076
  }
45321
46077
  function claudeMetaCommandTemplate() {
@@ -45340,10 +46096,17 @@ by the \`<!-- initialized: false -->\` flag in CLAUDE.md, or run manually.
45340
46096
  Run \`pnpm install\`. Wait for completion. Report any errors.
45341
46097
 
45342
46098
  2. **Setup environment**
45343
- Check if \`.env\` has \`ELEVASIS_API_KEY\` set.
45344
- If not, ask the user for their API key and write it to \`.env\`.
45345
- Validate the key works: run \`elevasis resources\` (should return empty list,
45346
- not an auth error).
46099
+ Read \`.env\`. It should contain only a single line: \`ELEVASIS_API_KEY=\`.
46100
+ If \`ELEVASIS_API_KEY\` is empty or missing, ask the user: "What is your
46101
+ Elevasis API key?" When the user provides it, write \`.env\` with exactly:
46102
+ \`\`\`
46103
+ ELEVASIS_API_KEY=<value they provided>
46104
+ \`\`\`
46105
+ No other keys (no \`NODE_ENV\`, no extras). The \`.env\` file must contain
46106
+ only \`ELEVASIS_API_KEY\`.
46107
+ Validate the key works: run \`elevasis resources\` (should return an empty
46108
+ list, not an auth error). If auth fails, tell the user their key appears
46109
+ invalid and ask them to check it in the Elevasis dashboard.
45347
46110
 
45348
46111
  3. **Competency Assessment**
45349
46112
  Ask these questions to build the user's profile:
@@ -45353,16 +46116,11 @@ by the \`<!-- initialized: false -->\` flag in CLAUDE.md, or run manually.
45353
46116
  - "What do you want to automate with Elevasis?"
45354
46117
  - "Which tools does your team already use?" (email, CRM, spreadsheets, etc.)
45355
46118
 
45356
- Competency (2-3 questions with conditional skipping):
45357
- - "Have you written code before? If so, what kind?"
45358
- No code -> programming: none, skip next question
45359
- HTML/CSS/Excel -> programming: minimal, skip next question
45360
- Scripts/websites -> programming: intermediate, ask next question
45361
- Production apps -> programming: advanced, ask next question
45362
- - (Only if scripts+) "Have you used TypeScript or a typed language?"
45363
- No -> typescript: none
45364
- Read it / used typed language -> typescript: exposure
45365
- Written TypeScript projects -> typescript: proficient
46119
+ Competency (2 questions):
46120
+ - "Have you used the Elevasis Command Center before?"
46121
+ Never used it -> platformNavigation: none
46122
+ Explored it / know the main sections -> platformNavigation: oriented
46123
+ Use it regularly -> platformNavigation: comfortable
45366
46124
  - "Have you used automation tools? (Zapier, Make, cron jobs, scripts)"
45367
46125
  No -> automation: none
45368
46126
  Zapier/Make flows -> automation: low-code
@@ -45390,8 +46148,8 @@ by the \`<!-- initialized: false -->\` flag in CLAUDE.md, or run manually.
45390
46148
  6. **Report**
45391
46149
  Summary of what was set up. Suggest next steps based on goals.
45392
46150
 
45393
- If the user's TypeScript level is beginner or automation level is new,
45394
- suggest /tutorial start: "I recommend starting with /tutorial -- it walks
46151
+ If the user's Platform Navigation level is none or automation level is none,
46152
+ suggest /tutorial: "I recommend starting with /tutorial -- it walks
45395
46153
  you through building workflows step by step, at your own pace."
45396
46154
 
45397
46155
  7. **Update flag**
@@ -45421,8 +46179,19 @@ Detect and repair all drift. Optionally upgrades the SDK first.
45421
46179
  1. **Missing managed files:** Regenerate from templates
45422
46180
  2. **Gitignore drift:** Append missing entries
45423
46181
  3. **CLAUDE.md sections:** Add missing sections in correct position
45424
- 4. **Memory structure:** Verify index consistency
45425
- 5. **Doc cross-references:** Scan for broken links
46182
+ 4. **Memory structure:** Create base structure if missing, then verify index consistency.
46183
+ If \`.claude/memory/\` does not exist or is empty:
46184
+ - Create \`memory/index.md\` (root index with placeholder entries)
46185
+ - Create \`memory/errors/index.md\` (error category summary, empty tables)
46186
+ - Create \`memory/errors/deploy.md\`, \`memory/errors/runtime.md\`, \`memory/errors/typescript.md\`
46187
+ If memory exists, verify: every file referenced in an index exists; every file
46188
+ without an index entry gets one added; broken references are removed.
46189
+ 5. **Documentation verification:** For each file in docs/:
46190
+ a. Cross-reference claims against src/ code (resource IDs, schema fields, platform tools used)
46191
+ b. Verify file path references point to real files
46192
+ c. Flag mismatches between documented schemas and actual resource definitions
46193
+ d. Check for undocumented resources (resources in src/ without docs coverage)
46194
+ e. Report discrepancies with suggested fixes
45426
46195
  6. **Settings consistency:** Verify expected fields
45427
46196
  7. **Rules health:** Scan \`memory/errors/\` -- flag any entry that has recurred
45428
46197
  3+ times and is not yet in \`.claude/rules/workspace-patterns.md\`.
@@ -45477,47 +46246,164 @@ The agent reads current templates from the installed SDK:
45477
46246
  function claudeWorkCommandTemplate() {
45478
46247
  return `# /work command
45479
46248
 
45480
- You are a task tracking assistant for this Elevasis workspace.
46249
+ You are a task tracking assistant for this Elevasis workspace. \`/work\` is the primary command for managing all work and projects.
45481
46250
 
45482
46251
  ## Context
45483
46252
 
45484
46253
  Read \`docs/priorities.mdx\` if it exists for current priorities.
45485
- Scan \`docs/in-progress/\` for task documents with \`status\` frontmatter.
46254
+ Scan \`docs/in-progress/\` recursively for \`.mdx\` files with \`status\` frontmatter.
46255
+
46256
+ ## Directory Structure
46257
+
46258
+ Task docs live in \`docs/in-progress/\`. Organize intelligently:
46259
+
46260
+ - **Small standalone tasks**: \`docs/in-progress/<slug>.mdx\`
46261
+ - **Multi-file tasks**: \`docs/in-progress/<slug>/index.mdx\` (+ supporting docs)
46262
+ - **Related tasks**: group under the same directory
46263
+
46264
+ When scanning, treat \`index.mdx\` as the primary task doc for a directory.
46265
+
46266
+ ## Status Values
46267
+
46268
+ Enforce exactly three values in frontmatter: \`planned\`, \`in-progress\`, \`complete\`.
45486
46269
 
45487
46270
  ## Operations
45488
46271
 
45489
- **No arguments (default):** Show all tasks from \`docs/in-progress/\` with status
45490
- from frontmatter, cross-referenced with \`docs/priorities.mdx\`.
45491
- Display as a status table sorted by status (in-progress first).
45492
-
45493
- **\`create <description>\`:** Create a task doc in \`docs/in-progress/<name>.mdx\`:
45494
- 1. Derive a kebab-case filename from the description
45495
- 2. Create the file with frontmatter (\`status: in-progress\`)
45496
- 3. Add sections: Objective, Plan, Progress, Resume Context
45497
- 4. Update \`docs/priorities.mdx\` with the new task
45498
- 5. Report what was created
45499
-
45500
- **\`save\`:** Update the current task doc's Progress and Resume Context sections:
45501
- 1. Identify the current task from conversation context
45502
- 2. Update Progress section (completed steps, files changed, decisions)
45503
- 3. Update Resume Context section with:
45504
- - Current State (date, what was done, what's next)
45505
- - Files Modified (table of changed files)
45506
- - Key docs to read on resume (file paths)
45507
- - To continue (copy-pasteable prompt)
45508
- 4. Set \`status\` appropriately
45509
-
45510
- **\`resume [name]\`:** Resume in-progress work:
45511
- 1. If name given, find matching doc in \`docs/in-progress/\`
45512
- 2. If no name, scan for docs with \`status: in-progress\`, ask if multiple
45513
- 3. Parse Resume Context section
45514
- 4. Present: "Resuming [task]. Last completed: [step]. Next: [step]."
45515
-
45516
- **\`complete [name]\`:** Mark task complete:
45517
- 1. Find the task doc
45518
- 2. Set \`status: complete\` in frontmatter
45519
- 3. Move from \`docs/in-progress/\` to final \`docs/\` location
45520
- 4. Update \`docs/priorities.mdx\`
46272
+ ### No arguments (default) -- List and Pick
46273
+
46274
+ 1. Scan \`docs/in-progress/\` recursively for \`.mdx\` files with \`status\` frontmatter
46275
+ 2. For directories, read \`index.mdx\` as the primary task doc
46276
+ 3. Display a numbered list sorted by status (\`in-progress\` first, then \`planned\`, then \`complete\`):
46277
+
46278
+ \`\`\`
46279
+ Active Tasks
46280
+ ============
46281
+
46282
+ 1. [in-progress] SDK Adapter Migration
46283
+ Last saved: 2026-03-02 | Step 3/5: Building factory adapters
46284
+
46285
+ 2. [planned] Email Template System
46286
+ Created: 2026-03-01 | Not started
46287
+
46288
+ 3. [complete] CRM Integration
46289
+ Completed: 2026-03-03
46290
+
46291
+ Pick a task by number or name to resume, or say:
46292
+ - "create" to start new work
46293
+ - "complete <name>" to finish a task
46294
+ \`\`\`
46295
+
46296
+ 4. Cross-reference with \`docs/priorities.mdx\` if it exists
46297
+ 5. When the user picks a number or describes a task in natural language, auto-invoke the **resume** flow on that task
46298
+ 6. If no tasks found, suggest \`/work create\`
46299
+
46300
+ ### \`create [description]\`
46301
+
46302
+ Guided task creation with interview:
46303
+
46304
+ 1. If no description given, ask: "What are you trying to accomplish?"
46305
+ 2. Ask 2-3 focused questions:
46306
+ - "What does success look like?" (acceptance criteria)
46307
+ - "Is this related to any existing work?" (scan \`docs/in-progress/\` and \`docs/\` for related topics)
46308
+ - "Do we need to investigate anything first, or is the path clear?" (suggest investigation if needed)
46309
+ 3. Determine directory placement:
46310
+ - Scan \`docs/in-progress/\` for existing directories related to the topic
46311
+ - If related to existing directory, create as a file within it
46312
+ - If new concept that may grow, create \`docs/in-progress/<slug>/index.mdx\`
46313
+ - If small/standalone, create \`docs/in-progress/<slug>.mdx\`
46314
+ 4. Create the doc with \`status: planned\` and structured sections:
46315
+
46316
+ \`\`\`yaml
46317
+ ---
46318
+ title: {Task Title}
46319
+ description: {Concise description}
46320
+ status: planned
46321
+ ---
46322
+ \`\`\`
46323
+
46324
+ Sections: Objective (what and why), Plan (numbered steps), Progress (per-step tracking with PENDING markers), Resume Context (current state, key docs, "To continue" prompt).
46325
+
46326
+ 5. Update \`docs/priorities.mdx\` if it exists
46327
+ 6. Report what was created with location and step count
46328
+
46329
+ ### \`save\`
46330
+
46331
+ Update the current task doc's Progress and Resume Context:
46332
+
46333
+ 1. Identify the current task from conversation context (which in-progress doc was loaded/discussed)
46334
+ 2. If not working on a tracked task, list available docs and ask which to save to, or offer to create a new one
46335
+ 3. Update Progress section:
46336
+ - Mark completed steps as \`COMPLETE\` with: what was done, key decisions, files changed
46337
+ - Mark current step as \`IN PROGRESS\` with current state
46338
+ - Leave future steps as \`PENDING\`
46339
+ 4. Update Resume Context section with:
46340
+ - Current State ({today's date}): summary of accomplishments and next steps
46341
+ - Files Modified: table of file paths and descriptions of changes
46342
+ - Key docs to read on resume: file paths with why they matter
46343
+ - To continue: copy-pasteable prompt for the next session
46344
+ 5. Set \`status\` appropriately (\`in-progress\` if ongoing, \`complete\` if all steps done)
46345
+ 6. Report: task title, status, completed steps count, last step, and the resume command
46346
+
46347
+ ### \`resume [name-or-number]\`
46348
+
46349
+ Resume in-progress work. Designed for non-technical users who may not know file paths.
46350
+
46351
+ **Resolution order:**
46352
+ 1. If a number is given (e.g., \`/work resume 2\`), map to the numbered list from the default list operation
46353
+ 2. If a name/keyword is given, substring match against task titles and filenames in \`docs/in-progress/\`
46354
+ 3. If no argument, scan for \`status: in-progress\` docs -- if one found, use it; if multiple, list and ask
46355
+ 4. If multiple matches, list them and ask which to resume
46356
+
46357
+ **Resume flow:**
46358
+ 1. Read the target task doc
46359
+ 2. Parse the Resume Context section
46360
+ 3. Load key docs listed in "Key docs to read on resume" in parallel
46361
+ 4. Quick verify: check that referenced files exist (Glob), report warnings for missing files
46362
+ 5. Present resume summary:
46363
+
46364
+ \`\`\`
46365
+ Resuming: {task title}
46366
+ ========================
46367
+
46368
+ Last completed: Step {N}: {title}
46369
+ Next: Step {M}: {title}
46370
+
46371
+ Key context loaded:
46372
+ - {doc 1}
46373
+ - {doc 2}
46374
+
46375
+ Ready to continue. {Copy of "To continue" prompt}
46376
+ \`\`\`
46377
+
46378
+ ### \`complete [name]\`
46379
+
46380
+ Mark task complete, clean up, and move to permanent docs:
46381
+
46382
+ 1. Resolve target (same as resume: number, name, or scan for \`status: complete\`)
46383
+ 2. **Validate readiness:** Check that all plan steps are marked COMPLETE. If not, warn and ask for confirmation.
46384
+ 3. **Clean up the doc:**
46385
+ - Strip \`## Resume Context\` section entirely (header through next \`##\` or EOF)
46386
+ - Strip progress markers: \`COMPLETE\`, \`IN PROGRESS\`, \`PENDING\` from step headings
46387
+ - Remove steps still marked PENDING entirely (header + body) -- they were never done
46388
+ - Remove \`status\` from frontmatter (keep \`title\` and \`description\`)
46389
+ - Target 200-400 lines; flag if result exceeds 500 lines
46390
+ 4. **Determine destination:**
46391
+ - Scan \`docs/\` (excluding \`docs/in-progress/\`) for existing directories related to this work
46392
+ - If a related directory exists, propose merging into it
46393
+ - If no related directory, propose \`docs/<slug>/\` or \`docs/<slug>.mdx\`
46394
+ - Present the proposed destination and ask user to confirm
46395
+ 5. **Move the doc(s):**
46396
+ - Single file: move from \`docs/in-progress/\` to destination
46397
+ - Directory: move entire directory
46398
+ 6. **Verify and report:**
46399
+ - Confirm destination exists, source removed
46400
+ - Check no leftover \`status:\` or \`## Resume Context\` in moved files
46401
+ - Update \`docs/priorities.mdx\` if it exists
46402
+ - Report: task title, cleanup stats (lines before/after), destination path
46403
+
46404
+ ## Completion Suggestions
46405
+
46406
+ When the agent observes that all plan steps for a task are marked COMPLETE and the user seems to be moving on, proactively suggest: "All steps for '{task}' are complete. Run \`/work complete\` to finalize it."
45521
46407
  `;
45522
46408
  }
45523
46409
  function claudeCredsSkillTemplate() {
@@ -45758,24 +46644,54 @@ description: Project map conventions -- auto-generated, do not edit, maintained
45758
46644
 
45759
46645
  # Project Map
45760
46646
 
45761
- - \`docs/project-map.mdx\` is fully auto-generated by \`elevasis deploy\`
45762
- - Do not edit manually -- changes are overwritten on next deploy
46647
+ - \`docs/project-map.mdx\` and \`docs/resource-map.mdx\` are fully auto-generated by \`elevasis deploy\`
46648
+ - Do not edit either file manually -- changes are overwritten on next deploy
45763
46649
  - \`/meta fix\` step 8 checks for drift and patches the map
45764
46650
  - If a new command, rule, skill, or memory file is added, run \`/meta fix\` or \`elevasis deploy\` to update
45765
46651
  `;
45766
46652
  }
45767
46653
  function claudeTaskTrackingRuleTemplate() {
45768
46654
  return `---
45769
- description: In-progress task conventions -- /work command, doc format, status values
46655
+ description: In-progress task conventions -- /work command, doc format, status values, auto-save behavior
45770
46656
  ---
45771
46657
 
45772
46658
  # Task Tracking
45773
46659
 
45774
- - Task docs managed via \`/work\` command (create, save, resume, complete)
45775
- - Frontmatter \`status\`: \`planned\`, \`in-progress\`, \`blocked\`, \`complete\`
46660
+ ## Status Values
46661
+
46662
+ Exactly three values for frontmatter \`status\`: \`planned\`, \`in-progress\`, \`complete\`.
46663
+
46664
+ ## Doc Format
46665
+
46666
+ - Frontmatter: \`title\`, \`description\`, \`status\`
45776
46667
  - Sections: Objective, Plan, Progress, Resume Context
45777
- - Update Progress section only on step transitions, not every action
45778
- - When context is heavy, suggest \`/work save\`
46668
+ - Progress subsections use markers: \`### Step N: Title -- PENDING\`, \`-- IN PROGRESS\`, \`-- COMPLETE\`
46669
+
46670
+ ## Auto-Update Behavior
46671
+
46672
+ - When working on a tracked task, update the Progress section when a plan step transitions:
46673
+ - PENDING -> IN PROGRESS (starting work on a step)
46674
+ - IN PROGRESS -> COMPLETE (finishing a step)
46675
+ - Do NOT update on every action -- only on step transitions
46676
+
46677
+ ## Save Suggestions
46678
+
46679
+ Proactively suggest \`/work save\` when:
46680
+ - The conversation context is getting heavy (many tool calls, large file reads)
46681
+ - The user appears to be wrapping up (thanks, goodbye, switching topics)
46682
+ - Significant progress has been made (2+ steps completed without saving)
46683
+ - Before a \`/clear\` or context reset
46684
+
46685
+ ## Completion Suggestions
46686
+
46687
+ When all plan steps are marked COMPLETE, suggest \`/work complete\` to finalize the task.
46688
+
46689
+ ## Directory Conventions
46690
+
46691
+ - \`docs/in-progress/\` for all active task docs
46692
+ - Small tasks: \`<slug>.mdx\` directly in \`docs/in-progress/\`
46693
+ - Multi-file tasks: \`<slug>/index.mdx\` with supporting docs alongside
46694
+ - Completed tasks move OUT of \`docs/in-progress/\` to \`docs/<relevant-dir>/\`
45779
46695
  `;
45780
46696
  }
45781
46697
 
@@ -45951,7 +46867,7 @@ function getManagedTemplates(ctx = {}) {
45951
46867
  ".gitignore": () => gitignoreTemplate(ctx),
45952
46868
  "CLAUDE.md": () => claudeMdTemplate(ctx),
45953
46869
  ".claude/settings.json": claudeSettingsTemplate,
45954
- ".claude/commands/docs.md": claudeDocsCommandTemplate,
46870
+ ".claude/hooks/enforce-sdk-boundary.mjs": claudeSdkBoundaryHookTemplate,
45955
46871
  ".claude/commands/tutorial.md": claudeTutorialCommandTemplate,
45956
46872
  ".claude/commands/meta.md": claudeMetaCommandTemplate,
45957
46873
  ".claude/commands/work.md": claudeWorkCommandTemplate,
@@ -46157,7 +47073,7 @@ var MANAGED_FILES = [
46157
47073
  ".gitignore",
46158
47074
  "CLAUDE.md",
46159
47075
  ".claude/settings.json",
46160
- ".claude/commands/docs.md",
47076
+ ".claude/hooks/enforce-sdk-boundary.mjs",
46161
47077
  ".claude/commands/tutorial.md",
46162
47078
  ".claude/commands/meta.md",
46163
47079
  ".claude/commands/work.md",
@@ -46196,6 +47112,7 @@ function registerInitCommand(program3) {
46196
47112
  await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src/example"), { recursive: true });
46197
47113
  await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src/shared"), { recursive: true });
46198
47114
  await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "docs/in-progress"), { recursive: true });
47115
+ await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/hooks"), { recursive: true });
46199
47116
  await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/commands"), { recursive: true });
46200
47117
  await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/skills/creds"), { recursive: true });
46201
47118
  await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/rules"), { recursive: true });
@@ -46221,7 +47138,7 @@ function registerInitCommand(program3) {
46221
47138
  "docs/in-progress/.gitkeep": "",
46222
47139
  "CLAUDE.md": claudeMdTemplate({ hasUI: options2.ui }),
46223
47140
  ".claude/settings.json": claudeSettingsTemplate(),
46224
- ".claude/commands/docs.md": claudeDocsCommandTemplate(),
47141
+ ".claude/hooks/enforce-sdk-boundary.mjs": claudeSdkBoundaryHookTemplate(),
46225
47142
  ".claude/commands/tutorial.md": claudeTutorialCommandTemplate(),
46226
47143
  ".claude/commands/meta.md": claudeMetaCommandTemplate(),
46227
47144
  ".claude/commands/work.md": claudeWorkCommandTemplate(),
@@ -46266,6 +47183,79 @@ function toSlug(name) {
46266
47183
  // src/cli/commands/update.ts
46267
47184
  var import_path4 = require("path");
46268
47185
  var import_promises3 = require("fs/promises");
47186
+ var SDK_OWNED_SECTIONS = [
47187
+ "Session Initialization",
47188
+ "Identity",
47189
+ "Navigation",
47190
+ "Interaction Guidance",
47191
+ "Commands",
47192
+ "Skills",
47193
+ "Maintaining Memory"
47194
+ ];
47195
+ function parseSections(md) {
47196
+ const lines = md.split("\n");
47197
+ const sections = [];
47198
+ let currentHeading = "";
47199
+ let currentLines = [];
47200
+ for (const line of lines) {
47201
+ if (line.startsWith("## ")) {
47202
+ if (currentLines.length > 0 || currentHeading === "") {
47203
+ sections.push({ heading: currentHeading, content: currentLines.join("\n") });
47204
+ }
47205
+ currentHeading = line;
47206
+ currentLines = [line];
47207
+ } else {
47208
+ currentLines.push(line);
47209
+ }
47210
+ }
47211
+ if (currentLines.length > 0 || currentHeading !== "") {
47212
+ sections.push({ heading: currentHeading, content: currentLines.join("\n") });
47213
+ }
47214
+ return sections;
47215
+ }
47216
+ function mergeSections(existing, template, sdkOwned) {
47217
+ const existingSections = parseSections(existing);
47218
+ const templateSections = parseSections(template);
47219
+ const updated = [];
47220
+ const preserved = [];
47221
+ const templateMap = /* @__PURE__ */ new Map();
47222
+ for (const section of templateSections) {
47223
+ templateMap.set(section.heading, section);
47224
+ }
47225
+ const sdkOwnedSet = new Set(sdkOwned.map((s) => `## ${s}`));
47226
+ const resultSections = [];
47227
+ const processedHeadings = /* @__PURE__ */ new Set();
47228
+ for (const section of existingSections) {
47229
+ processedHeadings.add(section.heading);
47230
+ if (section.heading === "") {
47231
+ resultSections.push(section);
47232
+ continue;
47233
+ }
47234
+ if (sdkOwnedSet.has(section.heading)) {
47235
+ const templateSection = templateMap.get(section.heading);
47236
+ if (templateSection) {
47237
+ resultSections.push(templateSection);
47238
+ updated.push(section.heading.replace("## ", ""));
47239
+ } else {
47240
+ updated.push(section.heading.replace("## ", "") + " (removed)");
47241
+ }
47242
+ } else {
47243
+ resultSections.push(section);
47244
+ preserved.push(section.heading.replace("## ", ""));
47245
+ }
47246
+ }
47247
+ for (const section of templateSections) {
47248
+ if (section.heading !== "" && !processedHeadings.has(section.heading)) {
47249
+ resultSections.push(section);
47250
+ updated.push(section.heading.replace("## ", "") + " (new)");
47251
+ }
47252
+ }
47253
+ return {
47254
+ merged: resultSections.map((s) => s.content).join("\n"),
47255
+ updated,
47256
+ preserved
47257
+ };
47258
+ }
46269
47259
  function registerUpdateCommand(program3) {
46270
47260
  program3.command("update").description("Update project scaffold to latest template version").option("--ui", "Add a Vite + React UI app in ui/").action(wrapAction("update", async (options2 = {}) => {
46271
47261
  const cwd = process.cwd();
@@ -46297,6 +47287,8 @@ function registerUpdateCommand(program3) {
46297
47287
  const added = [];
46298
47288
  const flagged = [];
46299
47289
  const appendedGitignore = [];
47290
+ const mergedUpdated = [];
47291
+ const mergedPreserved = [];
46300
47292
  const uiAffectedFiles = /* @__PURE__ */ new Set([".gitignore", "CLAUDE.md"]);
46301
47293
  const filesToProcess = upToDate ? MANAGED_FILES.filter((f) => uiAffectedFiles.has(f)) : MANAGED_FILES;
46302
47294
  for (const file2 of filesToProcess) {
@@ -46352,6 +47344,25 @@ function registerUpdateCommand(program3) {
46352
47344
  }
46353
47345
  continue;
46354
47346
  }
47347
+ if (file2 === "CLAUDE.md") {
47348
+ let existingContent = null;
47349
+ try {
47350
+ await (0, import_promises3.access)(filePath);
47351
+ existingContent = await (0, import_promises3.readFile)(filePath, "utf-8");
47352
+ } catch {
47353
+ }
47354
+ if (existingContent !== null) {
47355
+ const { merged, updated: sectionUpdated, preserved: sectionPreserved } = mergeSections(existingContent, templateContent, SDK_OWNED_SECTIONS);
47356
+ await (0, import_promises3.writeFile)(filePath, merged, "utf-8");
47357
+ mergedUpdated.push(...sectionUpdated);
47358
+ mergedPreserved.push(...sectionPreserved);
47359
+ } else {
47360
+ await (0, import_promises3.mkdir)((0, import_path4.dirname)(filePath), { recursive: true });
47361
+ await (0, import_promises3.writeFile)(filePath, templateContent, "utf-8");
47362
+ added.push(file2);
47363
+ }
47364
+ continue;
47365
+ }
46355
47366
  let exists = false;
46356
47367
  try {
46357
47368
  await (0, import_promises3.access)(filePath);
@@ -46425,6 +47436,16 @@ function registerUpdateCommand(program3) {
46425
47436
  console.log(source_default.gray(" Run /meta update in Claude Code to merge flagged files."));
46426
47437
  console.log(source_default.gray(" Or run /meta fix to verify and repair the full framework."));
46427
47438
  }
47439
+ if (mergedUpdated.length > 0 || mergedPreserved.length > 0) {
47440
+ console.log("");
47441
+ console.log(" CLAUDE.md sections:");
47442
+ for (const section of mergedUpdated) {
47443
+ console.log(source_default.green(` updated: ${section}`));
47444
+ }
47445
+ for (const section of mergedPreserved) {
47446
+ console.log(source_default.gray(` preserved: ${section}`));
47447
+ }
47448
+ }
46428
47449
  if (uiAdded.length > 0) {
46429
47450
  console.log("");
46430
47451
  console.log(" UI scaffold added:");