@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 +1275 -250
- package/dist/index.d.ts +7 -4
- package/dist/templates.js +1109 -79
- package/dist/types/templates.d.ts +2 -1
- package/package.json +10 -10
- package/reference/_navigation.md +3 -2
- package/reference/deployment/command-center-ui.mdx +151 -0
- package/reference/developer/interaction-guidance.mdx +21 -52
- package/reference/framework/agent.mdx +11 -21
- package/reference/framework/index.mdx +4 -3
- package/reference/framework/project-structure.mdx +5 -13
- package/reference/runtime/limits.mdx +1 -3
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
43855
|
+
var import_meta = {};
|
|
43901
43856
|
async function scanDocumentation() {
|
|
43902
|
-
const docsDir = (0,
|
|
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,
|
|
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,
|
|
43958
|
-
const cfg = await jiti.import((0,
|
|
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
|
|
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,
|
|
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(
|
|
44027
|
-
|
|
44028
|
-
|
|
44029
|
-
|
|
44030
|
-
|
|
44031
|
-
|
|
44032
|
-
|
|
44033
|
-
|
|
44034
|
-
|
|
44035
|
-
|
|
44036
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
44321
|
-
await (0, import_promises.writeFile)((0,
|
|
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
|
-
|
|
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,
|
|
44346
|
-
const entryModule = await jiti.import((0,
|
|
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,
|
|
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,
|
|
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,
|
|
44463
|
-
const bundleOutfile = (0,
|
|
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,
|
|
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
|
-
|
|
44567
|
-
|
|
44568
|
-
|
|
44569
|
-
|
|
44570
|
-
|
|
44571
|
-
|
|
44572
|
-
|
|
44573
|
-
|
|
44574
|
-
console.log(source_default.gray(" Execution ID:"), source_default.cyan(
|
|
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
|
-
|
|
44577
|
-
|
|
44578
|
-
}
|
|
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
|
-
|
|
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 =
|
|
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({
|
|
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
|
|
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
|
-
-
|
|
45140
|
-
|
|
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
|
|
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
|
|
45204
|
-
return `# /
|
|
45546
|
+
function claudeTutorialCommandTemplate() {
|
|
45547
|
+
return `# /tutorial command
|
|
45205
45548
|
|
|
45206
|
-
You are a
|
|
45549
|
+
You are a tutorial guide for this Elevasis workspace.
|
|
45207
45550
|
|
|
45208
45551
|
## Context
|
|
45209
45552
|
|
|
45210
|
-
Read
|
|
45211
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
45216
|
-
|
|
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
|
-
|
|
45219
|
-
|
|
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
|
-
|
|
45223
|
-
definitions. Flag mismatches between documented schemas and code.
|
|
45571
|
+
Read \`.claude/memory/tutorial-progress.md\` to check current lesson progress.
|
|
45224
45572
|
|
|
45225
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
45577
|
+
## Menu
|
|
45237
45578
|
|
|
45238
|
-
|
|
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
|
-
|
|
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
|
-
|
|
45245
|
-
|
|
45246
|
-
|
|
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
|
|
45253
|
-
3. Guide user to build or modify something (
|
|
45254
|
-
4. Verify it works (
|
|
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\`.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
45285
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
45299
|
-
|
|
45300
|
-
|
|
45301
|
-
|
|
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
|
-
|
|
45306
|
-
-
|
|
45307
|
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
-
|
|
45344
|
-
If
|
|
45345
|
-
|
|
45346
|
-
|
|
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
|
|
45357
|
-
- "Have you
|
|
45358
|
-
|
|
45359
|
-
|
|
45360
|
-
|
|
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
|
|
45394
|
-
suggest /tutorial
|
|
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:**
|
|
45425
|
-
|
|
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
|
|
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
|
-
|
|
45490
|
-
|
|
45491
|
-
|
|
45492
|
-
|
|
45493
|
-
|
|
45494
|
-
|
|
45495
|
-
|
|
45496
|
-
|
|
45497
|
-
|
|
45498
|
-
|
|
45499
|
-
|
|
45500
|
-
|
|
45501
|
-
|
|
45502
|
-
2.
|
|
45503
|
-
|
|
45504
|
-
|
|
45505
|
-
|
|
45506
|
-
-
|
|
45507
|
-
|
|
45508
|
-
|
|
45509
|
-
|
|
45510
|
-
|
|
45511
|
-
|
|
45512
|
-
|
|
45513
|
-
|
|
45514
|
-
|
|
45515
|
-
|
|
45516
|
-
|
|
45517
|
-
|
|
45518
|
-
|
|
45519
|
-
|
|
45520
|
-
|
|
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\`
|
|
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
|
-
|
|
45775
|
-
|
|
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
|
-
-
|
|
45778
|
-
|
|
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/
|
|
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/
|
|
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/
|
|
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:");
|