@elevasis/sdk 0.5.0 → 0.5.2

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