@elevasis/sdk 0.5.14 → 0.5.16
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 +841 -541
- package/dist/index.d.ts +24 -0
- package/dist/index.js +53 -0
- package/dist/templates.js +305 -108
- package/dist/worker/index.js +7 -8
- package/package.json +1 -1
- package/reference/framework/agent.mdx +7 -7
- package/reference/framework/index.mdx +10 -10
- package/reference/framework/project-structure.mdx +8 -8
- package/reference/getting-started.mdx +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -40164,6 +40164,12 @@ var DOMAIN_MAP = {
|
|
|
40164
40164
|
[DOMAINS.DIAGNOSTIC]: DIAGNOSTIC_DOMAIN
|
|
40165
40165
|
};
|
|
40166
40166
|
|
|
40167
|
+
// ../core/src/platform/registry/reserved.ts
|
|
40168
|
+
var RESERVED_RESOURCE_IDS = /* @__PURE__ */ new Set(["command-center-assistant"]);
|
|
40169
|
+
function isReservedResourceId(resourceId) {
|
|
40170
|
+
return RESERVED_RESOURCE_IDS.has(resourceId);
|
|
40171
|
+
}
|
|
40172
|
+
|
|
40167
40173
|
// ../core/src/execution/engine/base/errors.ts
|
|
40168
40174
|
var ExecutionError = class extends Error {
|
|
40169
40175
|
/**
|
|
@@ -43363,6 +43369,13 @@ var ResourceRegistry = class {
|
|
|
43363
43369
|
}
|
|
43364
43370
|
seen.add(id);
|
|
43365
43371
|
}
|
|
43372
|
+
for (const id of incomingIds) {
|
|
43373
|
+
if (isReservedResourceId(id)) {
|
|
43374
|
+
throw new Error(
|
|
43375
|
+
`Resource ID '${id}' is reserved for platform use. External deployments cannot use reserved resource IDs.`
|
|
43376
|
+
);
|
|
43377
|
+
}
|
|
43378
|
+
}
|
|
43366
43379
|
if (this.isRemote(orgName)) {
|
|
43367
43380
|
this.unregisterOrganization(orgName);
|
|
43368
43381
|
}
|
|
@@ -43395,6 +43408,46 @@ var ResourceRegistry = class {
|
|
|
43395
43408
|
}
|
|
43396
43409
|
this.serializedCache.set(orgName, serializeOrganization(this.registry[orgName]));
|
|
43397
43410
|
}
|
|
43411
|
+
/**
|
|
43412
|
+
* Register built-in platform resources (static, local execution)
|
|
43413
|
+
*
|
|
43414
|
+
* Unlike registerOrganization(), these resources:
|
|
43415
|
+
* - Do NOT have remote config (execute in-process, not in worker threads)
|
|
43416
|
+
* - Are NOT removed by unregisterOrganization() (persist across redeployments)
|
|
43417
|
+
* - Use reserved resource IDs that external deployments cannot claim
|
|
43418
|
+
*
|
|
43419
|
+
* @param orgName - Organization name
|
|
43420
|
+
* @param org - Resource definitions with real handlers (not stubs)
|
|
43421
|
+
*/
|
|
43422
|
+
registerStaticResources(orgName, org) {
|
|
43423
|
+
const incomingWorkflowIds = (org.workflows ?? []).map((w) => w.config.resourceId);
|
|
43424
|
+
const incomingAgentIds = (org.agents ?? []).map((a) => a.config.resourceId);
|
|
43425
|
+
const incomingIds = [...incomingWorkflowIds, ...incomingAgentIds];
|
|
43426
|
+
const seen = /* @__PURE__ */ new Set();
|
|
43427
|
+
for (const id of incomingIds) {
|
|
43428
|
+
if (seen.has(id)) {
|
|
43429
|
+
throw new Error(`Duplicate resource ID '${id}' in static resources.`);
|
|
43430
|
+
}
|
|
43431
|
+
seen.add(id);
|
|
43432
|
+
}
|
|
43433
|
+
const existingOrg = this.registry[orgName];
|
|
43434
|
+
if (existingOrg) {
|
|
43435
|
+
const existingWorkflowIds = new Set((existingOrg.workflows ?? []).map((w) => w.config.resourceId));
|
|
43436
|
+
const existingAgentIds = new Set((existingOrg.agents ?? []).map((a) => a.config.resourceId));
|
|
43437
|
+
for (const id of incomingIds) {
|
|
43438
|
+
if (existingWorkflowIds.has(id) || existingAgentIds.has(id)) {
|
|
43439
|
+
throw new Error(`Static resource '${id}' conflicts with existing resource in '${orgName}'.`);
|
|
43440
|
+
}
|
|
43441
|
+
}
|
|
43442
|
+
}
|
|
43443
|
+
if (existingOrg) {
|
|
43444
|
+
existingOrg.workflows = [...existingOrg.workflows ?? [], ...org.workflows ?? []];
|
|
43445
|
+
existingOrg.agents = [...existingOrg.agents ?? [], ...org.agents ?? []];
|
|
43446
|
+
} else {
|
|
43447
|
+
this.registry[orgName] = org;
|
|
43448
|
+
}
|
|
43449
|
+
this.serializedCache.set(orgName, serializeOrganization(this.registry[orgName]));
|
|
43450
|
+
}
|
|
43398
43451
|
/**
|
|
43399
43452
|
* Unregister runtime-registered resources for an organization
|
|
43400
43453
|
*
|
|
@@ -43798,7 +43851,7 @@ async function apiDelete(endpoint, apiUrl = resolveApiUrl()) {
|
|
|
43798
43851
|
// package.json
|
|
43799
43852
|
var package_default = {
|
|
43800
43853
|
name: "@elevasis/sdk",
|
|
43801
|
-
version: "0.5.
|
|
43854
|
+
version: "0.5.16",
|
|
43802
43855
|
description: "SDK for building Elevasis organization resources",
|
|
43803
43856
|
type: "module",
|
|
43804
43857
|
bin: {
|
|
@@ -43884,9 +43937,7 @@ async function scanDocumentation() {
|
|
|
43884
43937
|
const raw = await (0, import_promises.readFile)(fullPath, "utf-8");
|
|
43885
43938
|
const fileSize = Buffer.byteLength(raw, "utf-8");
|
|
43886
43939
|
if (fileSize > 100 * 1024) {
|
|
43887
|
-
throw new Error(
|
|
43888
|
-
`Documentation file exceeds 100KB: docs/${relPath} (${Math.round(fileSize / 1024)}KB)`
|
|
43889
|
-
);
|
|
43940
|
+
throw new Error(`Documentation file exceeds 100KB: docs/${relPath} (${Math.round(fileSize / 1024)}KB)`);
|
|
43890
43941
|
}
|
|
43891
43942
|
totalSize += fileSize;
|
|
43892
43943
|
if (totalSize > 1024 * 1024) {
|
|
@@ -43939,7 +43990,9 @@ async function generateResourceMap(org) {
|
|
|
43939
43990
|
);
|
|
43940
43991
|
for (const w of workflows) {
|
|
43941
43992
|
const desc = escapeMdx(w.config.description);
|
|
43942
|
-
lines.push(
|
|
43993
|
+
lines.push(
|
|
43994
|
+
`| \`${w.config.resourceId}\` | ${escapeMdx(w.config.name)} | ${w.config.version} | ${w.config.status} | ${desc} |`
|
|
43995
|
+
);
|
|
43943
43996
|
}
|
|
43944
43997
|
lines.push("");
|
|
43945
43998
|
}
|
|
@@ -43952,11 +44005,16 @@ async function generateResourceMap(org) {
|
|
|
43952
44005
|
);
|
|
43953
44006
|
for (const a of agents) {
|
|
43954
44007
|
const desc = escapeMdx(a.config.description);
|
|
43955
|
-
lines.push(
|
|
44008
|
+
lines.push(
|
|
44009
|
+
`| \`${a.config.resourceId}\` | ${escapeMdx(a.config.name)} | ${a.config.version} | ${a.config.status} | ${desc} |`
|
|
44010
|
+
);
|
|
43956
44011
|
}
|
|
43957
44012
|
lines.push("");
|
|
43958
44013
|
}
|
|
43959
|
-
lines.push(
|
|
44014
|
+
lines.push(
|
|
44015
|
+
`**Total:** ${workflows.length + agents.length} resources (${workflows.length} workflows, ${agents.length} agents)`,
|
|
44016
|
+
""
|
|
44017
|
+
);
|
|
43960
44018
|
await (0, import_promises.mkdir)((0, import_path.resolve)("docs"), { recursive: true });
|
|
43961
44019
|
await (0, import_promises.writeFile)((0, import_path.resolve)("docs/resource-map.mdx"), lines.join("\n"), "utf-8");
|
|
43962
44020
|
}
|
|
@@ -44020,8 +44078,12 @@ async function generateProjectMap(org) {
|
|
|
44020
44078
|
if (resourceCount === 0) {
|
|
44021
44079
|
types = "(utilities)";
|
|
44022
44080
|
} else {
|
|
44023
|
-
const wCount = workflows.filter(
|
|
44024
|
-
|
|
44081
|
+
const wCount = workflows.filter(
|
|
44082
|
+
(w) => Array.isArray(w.config.domains) && w.config.domains.includes(domainName)
|
|
44083
|
+
).length;
|
|
44084
|
+
const aCount = agents.filter(
|
|
44085
|
+
(a) => Array.isArray(a.config.domains) && a.config.domains.includes(domainName)
|
|
44086
|
+
).length;
|
|
44025
44087
|
const parts = [];
|
|
44026
44088
|
if (wCount > 0) parts.push(`${wCount} workflow${wCount !== 1 ? "s" : ""}`);
|
|
44027
44089
|
if (aCount > 0) parts.push(`${aCount} agent${aCount !== 1 ? "s" : ""}`);
|
|
@@ -44133,10 +44195,7 @@ async function generateProjectMap(org) {
|
|
|
44133
44195
|
if (categories.length === 0) {
|
|
44134
44196
|
lines.push("SDK reference found but no categories parsed.", "");
|
|
44135
44197
|
} else {
|
|
44136
|
-
lines.push(
|
|
44137
|
-
"| Category | Files | Covers |",
|
|
44138
|
-
"| --- | --- | --- |"
|
|
44139
|
-
);
|
|
44198
|
+
lines.push("| Category | Files | Covers |", "| --- | --- | --- |");
|
|
44140
44199
|
for (const cat of categories) {
|
|
44141
44200
|
const covers = cat.titles.slice(0, 5).join(", ") + (cat.titles.length > 5 ? ", ..." : "");
|
|
44142
44201
|
lines.push(`| ${escapeMdx(cat.name)} | ${cat.count} | ${escapeMdx(covers)} |`);
|
|
@@ -44154,10 +44213,7 @@ async function generateProjectMap(org) {
|
|
|
44154
44213
|
if (cmdFiles.length === 0) {
|
|
44155
44214
|
lines.push("No commands found.", "");
|
|
44156
44215
|
} else {
|
|
44157
|
-
lines.push(
|
|
44158
|
-
"| Command | File | Purpose |",
|
|
44159
|
-
"| --- | --- | --- |"
|
|
44160
|
-
);
|
|
44216
|
+
lines.push("| Command | File | Purpose |", "| --- | --- | --- |");
|
|
44161
44217
|
for (const f of cmdFiles) {
|
|
44162
44218
|
const cmdName = f.name.replace(/\.md$/, "");
|
|
44163
44219
|
let purpose = "";
|
|
@@ -44186,10 +44242,7 @@ async function generateProjectMap(org) {
|
|
|
44186
44242
|
if (ruleFiles.length === 0) {
|
|
44187
44243
|
lines.push("No rules found.", "");
|
|
44188
44244
|
} else {
|
|
44189
|
-
lines.push(
|
|
44190
|
-
"| Rule | File | Scope |",
|
|
44191
|
-
"| --- | --- | --- |"
|
|
44192
|
-
);
|
|
44245
|
+
lines.push("| Rule | File | Scope |", "| --- | --- | --- |");
|
|
44193
44246
|
for (const f of ruleFiles) {
|
|
44194
44247
|
const ruleName = f.name.replace(/\.md$/, "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
44195
44248
|
let scope = "";
|
|
@@ -44220,10 +44273,7 @@ async function generateProjectMap(org) {
|
|
|
44220
44273
|
if (skillDirs.length === 0) {
|
|
44221
44274
|
lines.push("No skills found.", "");
|
|
44222
44275
|
} else {
|
|
44223
|
-
lines.push(
|
|
44224
|
-
"| Skill | File | Trigger |",
|
|
44225
|
-
"| --- | --- | --- |"
|
|
44226
|
-
);
|
|
44276
|
+
lines.push("| Skill | File | Trigger |", "| --- | --- | --- |");
|
|
44227
44277
|
for (const d of skillDirs) {
|
|
44228
44278
|
const skillFile = (0, import_path.resolve)(".claude/skills", d.name, "SKILL.md");
|
|
44229
44279
|
let trigger = "";
|
|
@@ -44279,10 +44329,7 @@ async function generateProjectMap(org) {
|
|
|
44279
44329
|
if (memoryFiles.length === 0) {
|
|
44280
44330
|
lines.push("No memory files found.", "");
|
|
44281
44331
|
} else {
|
|
44282
|
-
lines.push(
|
|
44283
|
-
"| File | Purpose | Last Modified |",
|
|
44284
|
-
"| --- | --- | --- |"
|
|
44285
|
-
);
|
|
44332
|
+
lines.push("| File | Purpose | Last Modified |", "| --- | --- | --- |");
|
|
44286
44333
|
for (const m of memoryFiles) {
|
|
44287
44334
|
lines.push(`| .claude/memory/${m.rel} | ${escapeMdx(m.purpose)} | ${m.mtime} |`);
|
|
44288
44335
|
}
|
|
@@ -44314,250 +44361,293 @@ async function generateProjectMap(org) {
|
|
|
44314
44361
|
await (0, import_promises.mkdir)((0, import_path.resolve)("docs"), { recursive: true });
|
|
44315
44362
|
await (0, import_promises.writeFile)((0, import_path.resolve)("docs/project-map.mdx"), lines.join("\n"), "utf-8");
|
|
44316
44363
|
}
|
|
44364
|
+
async function generateNavigationMap(docs) {
|
|
44365
|
+
const excludedFiles = /* @__PURE__ */ new Set(["docs/navigation-map.mdx", "docs/project-map.mdx", "docs/resource-map.mdx"]);
|
|
44366
|
+
const filtered = docs.filter((doc) => !excludedFiles.has(doc.path)).sort((a, b) => {
|
|
44367
|
+
const orderA = a.frontmatter.order ?? 9999;
|
|
44368
|
+
const orderB = b.frontmatter.order ?? 9999;
|
|
44369
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
44370
|
+
return a.frontmatter.title.localeCompare(b.frontmatter.title);
|
|
44371
|
+
});
|
|
44372
|
+
const lines = [
|
|
44373
|
+
"---",
|
|
44374
|
+
"title: Navigation Map",
|
|
44375
|
+
"description: Auto-generated table of contents for all documentation (updated on each deploy)",
|
|
44376
|
+
"order: 997",
|
|
44377
|
+
"---",
|
|
44378
|
+
"",
|
|
44379
|
+
"# Navigation Map",
|
|
44380
|
+
"",
|
|
44381
|
+
"> Auto-generated by `elevasis-sdk deploy`. Do not edit manually.",
|
|
44382
|
+
"",
|
|
44383
|
+
"| Title | Description | Path |",
|
|
44384
|
+
"| --- | --- | --- |"
|
|
44385
|
+
];
|
|
44386
|
+
for (const doc of filtered) {
|
|
44387
|
+
lines.push(`| ${escapeMdx(doc.frontmatter.title)} | ${escapeMdx(doc.frontmatter.description)} | ${doc.path} |`);
|
|
44388
|
+
}
|
|
44389
|
+
lines.push("");
|
|
44390
|
+
await (0, import_promises.mkdir)((0, import_path.resolve)("docs"), { recursive: true });
|
|
44391
|
+
await (0, import_promises.writeFile)((0, import_path.resolve)("docs/navigation-map.mdx"), lines.join("\n"), "utf-8");
|
|
44392
|
+
}
|
|
44317
44393
|
function registerDeployCommand(program3) {
|
|
44318
|
-
program3.command("deploy").description(
|
|
44319
|
-
|
|
44320
|
-
|
|
44321
|
-
|
|
44322
|
-
|
|
44323
|
-
|
|
44324
|
-
|
|
44325
|
-
|
|
44326
|
-
const
|
|
44327
|
-
orgName
|
|
44328
|
-
|
|
44329
|
-
|
|
44330
|
-
|
|
44331
|
-
|
|
44332
|
-
|
|
44333
|
-
|
|
44334
|
-
|
|
44335
|
-
|
|
44336
|
-
|
|
44337
|
-
|
|
44338
|
-
|
|
44339
|
-
|
|
44340
|
-
|
|
44394
|
+
program3.command("deploy").description(
|
|
44395
|
+
"Validate, bundle, upload, and deploy project resources\n Example: elevasis-sdk deploy --api-url http://localhost:5170"
|
|
44396
|
+
).option("--api-url <url>", "API URL").option("--entry <path>", "Path to entry file (default: ./src/index.ts)").option("--prod", "Deploy to production (overrides NODE_ENV=development)").action(
|
|
44397
|
+
wrapAction("deploy", async (options2) => {
|
|
44398
|
+
const startTime = Date.now();
|
|
44399
|
+
const apiUrl = resolveApiUrl(options2.apiUrl, options2.prod);
|
|
44400
|
+
const env2 = resolveEnvironment(options2.prod);
|
|
44401
|
+
const entryPath = options2.entry ?? "./src/index.ts";
|
|
44402
|
+
const authSpinner = ora("Authenticating...").start();
|
|
44403
|
+
let orgName;
|
|
44404
|
+
try {
|
|
44405
|
+
const me = await apiGet("/api/external/me", apiUrl);
|
|
44406
|
+
orgName = me.organizationName;
|
|
44407
|
+
authSpinner.succeed(
|
|
44408
|
+
source_default.green("Authenticating...") + source_default.white(" done") + source_default.gray(` (${orgName})`)
|
|
44409
|
+
);
|
|
44410
|
+
} catch (error46) {
|
|
44411
|
+
authSpinner.fail(source_default.red("Authentication failed"));
|
|
44412
|
+
const errMsg = error46 instanceof Error ? error46.message : String(error46);
|
|
44413
|
+
if (errMsg.includes("401") || errMsg.toLowerCase().includes("unauthorized")) {
|
|
44414
|
+
const keyVar = !options2.prod && process.env.NODE_ENV === "development" && process.env.ELEVASIS_PLATFORM_KEY_DEV ? "ELEVASIS_PLATFORM_KEY_DEV" : "ELEVASIS_PLATFORM_KEY";
|
|
44415
|
+
console.error(source_default.red(" Invalid API key."));
|
|
44416
|
+
console.error(source_default.gray(` Your ${keyVar} was rejected by the server.`));
|
|
44417
|
+
console.error(source_default.gray(" Check your .env file and verify the key in the Elevasis dashboard."));
|
|
44418
|
+
} else {
|
|
44419
|
+
console.error(source_default.gray(" Check your API key and API URL."));
|
|
44420
|
+
}
|
|
44421
|
+
throw error46;
|
|
44341
44422
|
}
|
|
44342
|
-
|
|
44343
|
-
|
|
44344
|
-
|
|
44345
|
-
|
|
44346
|
-
|
|
44347
|
-
|
|
44348
|
-
|
|
44349
|
-
|
|
44350
|
-
|
|
44351
|
-
|
|
44352
|
-
|
|
44353
|
-
|
|
44423
|
+
const validateSpinner = ora("Validating...").start();
|
|
44424
|
+
let org;
|
|
44425
|
+
try {
|
|
44426
|
+
const jiti = (0, import_jiti.createJiti)(import_meta.url);
|
|
44427
|
+
const entryModule = await jiti.import((0, import_path.resolve)(entryPath));
|
|
44428
|
+
org = entryModule.default;
|
|
44429
|
+
if (!org) {
|
|
44430
|
+
validateSpinner.fail("Invalid entry: no default export found");
|
|
44431
|
+
console.error(source_default.gray(` Entry file: ${(0, import_path.resolve)(entryPath)}`));
|
|
44432
|
+
throw new Error("Invalid entry: no default export found");
|
|
44433
|
+
}
|
|
44434
|
+
new ResourceRegistry({ [orgName]: org });
|
|
44435
|
+
const workflowCount = org.workflows?.length ?? 0;
|
|
44436
|
+
const agentCount = org.agents?.length ?? 0;
|
|
44437
|
+
const totalCount = workflowCount + agentCount;
|
|
44438
|
+
validateSpinner.succeed(
|
|
44439
|
+
source_default.green("Validating...") + source_default.white(" done") + source_default.gray(` (${totalCount} resource${totalCount !== 1 ? "s" : ""}, 0 errors)`)
|
|
44440
|
+
);
|
|
44441
|
+
console.log("");
|
|
44442
|
+
console.log(source_default.gray(` Org: ${orgName}`));
|
|
44443
|
+
console.log(source_default.gray(` Target: ${apiUrl} (${env2})`));
|
|
44444
|
+
console.log("");
|
|
44445
|
+
for (const w of org.workflows ?? []) {
|
|
44446
|
+
console.log(source_default.gray(` workflow ${source_default.white(w.config.resourceId)} v${w.config.version}`));
|
|
44447
|
+
}
|
|
44448
|
+
for (const a of org.agents ?? []) {
|
|
44449
|
+
console.log(source_default.gray(` agent ${source_default.white(a.config.resourceId)} v${a.config.version}`));
|
|
44450
|
+
}
|
|
44451
|
+
console.log("");
|
|
44452
|
+
} catch (error46) {
|
|
44453
|
+
if (error46 instanceof RegistryValidationError) {
|
|
44454
|
+
validateSpinner.fail(source_default.red("Validation failed"));
|
|
44455
|
+
console.error("");
|
|
44456
|
+
console.error(source_default.red(` ERROR ${error46.message}`));
|
|
44457
|
+
if (error46.resourceId) {
|
|
44458
|
+
console.error(source_default.gray(` Resource: ${error46.resourceId}`));
|
|
44459
|
+
}
|
|
44460
|
+
console.error("");
|
|
44461
|
+
console.error(source_default.gray(" Deploy aborted."));
|
|
44462
|
+
}
|
|
44463
|
+
throw error46;
|
|
44354
44464
|
}
|
|
44355
|
-
|
|
44356
|
-
|
|
44357
|
-
|
|
44358
|
-
|
|
44359
|
-
|
|
44360
|
-
|
|
44361
|
-
|
|
44362
|
-
|
|
44363
|
-
|
|
44364
|
-
|
|
44365
|
-
|
|
44366
|
-
for (const w of org.workflows ?? []) {
|
|
44367
|
-
console.log(source_default.gray(` workflow ${source_default.white(w.config.resourceId)} v${w.config.version}`));
|
|
44465
|
+
await generateResourceMap(org);
|
|
44466
|
+
await generateProjectMap(org);
|
|
44467
|
+
let documentation = await scanDocumentation();
|
|
44468
|
+
if (documentation) {
|
|
44469
|
+
await generateNavigationMap(documentation);
|
|
44470
|
+
documentation = await scanDocumentation();
|
|
44471
|
+
console.log(
|
|
44472
|
+
source_default.gray(
|
|
44473
|
+
` docs ${source_default.white(String(documentation.length))} file${documentation.length !== 1 ? "s" : ""}`
|
|
44474
|
+
)
|
|
44475
|
+
);
|
|
44368
44476
|
}
|
|
44369
|
-
|
|
44370
|
-
|
|
44477
|
+
const triggerCount = org.triggers?.length ?? 0;
|
|
44478
|
+
const integrationCount = org.integrations?.length ?? 0;
|
|
44479
|
+
const checkpointCount = org.humanCheckpoints?.length ?? 0;
|
|
44480
|
+
if (triggerCount > 0) console.log(source_default.gray(` triggers ${source_default.white(String(triggerCount))}`));
|
|
44481
|
+
if (integrationCount > 0) console.log(source_default.gray(` integrations ${source_default.white(String(integrationCount))}`));
|
|
44482
|
+
if (checkpointCount > 0) console.log(source_default.gray(` checkpoints ${source_default.white(String(checkpointCount))}`));
|
|
44483
|
+
const relationshipCount = org.relationships ? Object.keys(org.relationships).length : 0;
|
|
44484
|
+
if (relationshipCount > 0) {
|
|
44485
|
+
console.log(
|
|
44486
|
+
source_default.gray(
|
|
44487
|
+
` rels ${source_default.white(String(relationshipCount))} resource${relationshipCount !== 1 ? "s" : ""}`
|
|
44488
|
+
)
|
|
44489
|
+
);
|
|
44371
44490
|
}
|
|
44372
|
-
|
|
44373
|
-
|
|
44374
|
-
|
|
44375
|
-
|
|
44376
|
-
|
|
44377
|
-
|
|
44378
|
-
|
|
44379
|
-
|
|
44491
|
+
const schemaWarnings = [];
|
|
44492
|
+
const workflows = (org.workflows ?? []).map((w) => {
|
|
44493
|
+
const meta = {
|
|
44494
|
+
resourceId: w.config.resourceId,
|
|
44495
|
+
name: w.config.name,
|
|
44496
|
+
version: w.config.version,
|
|
44497
|
+
status: w.config.status,
|
|
44498
|
+
description: w.config.description,
|
|
44499
|
+
domains: w.config.domains
|
|
44500
|
+
};
|
|
44501
|
+
if (w.contract.inputSchema) {
|
|
44502
|
+
try {
|
|
44503
|
+
meta.inputSchema = external_exports.toJSONSchema(w.contract.inputSchema);
|
|
44504
|
+
} catch {
|
|
44505
|
+
schemaWarnings.push(`${w.config.resourceId}: inputSchema could not be serialized`);
|
|
44506
|
+
}
|
|
44380
44507
|
}
|
|
44381
|
-
|
|
44382
|
-
|
|
44383
|
-
|
|
44384
|
-
|
|
44385
|
-
|
|
44386
|
-
|
|
44387
|
-
await generateProjectMap(org);
|
|
44388
|
-
const documentation = await scanDocumentation();
|
|
44389
|
-
if (documentation) {
|
|
44390
|
-
console.log(source_default.gray(` docs ${source_default.white(String(documentation.length))} file${documentation.length !== 1 ? "s" : ""}`));
|
|
44391
|
-
}
|
|
44392
|
-
const triggerCount = org.triggers?.length ?? 0;
|
|
44393
|
-
const integrationCount = org.integrations?.length ?? 0;
|
|
44394
|
-
const checkpointCount = org.humanCheckpoints?.length ?? 0;
|
|
44395
|
-
if (triggerCount > 0) console.log(source_default.gray(` triggers ${source_default.white(String(triggerCount))}`));
|
|
44396
|
-
if (integrationCount > 0) console.log(source_default.gray(` integrations ${source_default.white(String(integrationCount))}`));
|
|
44397
|
-
if (checkpointCount > 0) console.log(source_default.gray(` checkpoints ${source_default.white(String(checkpointCount))}`));
|
|
44398
|
-
const relationshipCount = org.relationships ? Object.keys(org.relationships).length : 0;
|
|
44399
|
-
if (relationshipCount > 0) {
|
|
44400
|
-
console.log(source_default.gray(` rels ${source_default.white(String(relationshipCount))} resource${relationshipCount !== 1 ? "s" : ""}`));
|
|
44401
|
-
}
|
|
44402
|
-
const schemaWarnings = [];
|
|
44403
|
-
const workflows = (org.workflows ?? []).map((w) => {
|
|
44404
|
-
const meta = {
|
|
44405
|
-
resourceId: w.config.resourceId,
|
|
44406
|
-
name: w.config.name,
|
|
44407
|
-
version: w.config.version,
|
|
44408
|
-
status: w.config.status,
|
|
44409
|
-
description: w.config.description,
|
|
44410
|
-
domains: w.config.domains
|
|
44411
|
-
};
|
|
44412
|
-
if (w.contract.inputSchema) {
|
|
44413
|
-
try {
|
|
44414
|
-
meta.inputSchema = external_exports.toJSONSchema(w.contract.inputSchema);
|
|
44415
|
-
} catch {
|
|
44416
|
-
schemaWarnings.push(`${w.config.resourceId}: inputSchema could not be serialized`);
|
|
44508
|
+
if (w.contract.outputSchema) {
|
|
44509
|
+
try {
|
|
44510
|
+
meta.outputSchema = external_exports.toJSONSchema(w.contract.outputSchema);
|
|
44511
|
+
} catch {
|
|
44512
|
+
schemaWarnings.push(`${w.config.resourceId}: outputSchema could not be serialized`);
|
|
44513
|
+
}
|
|
44417
44514
|
}
|
|
44418
|
-
|
|
44419
|
-
|
|
44420
|
-
|
|
44421
|
-
|
|
44422
|
-
|
|
44423
|
-
|
|
44515
|
+
return meta;
|
|
44516
|
+
});
|
|
44517
|
+
const agents = (org.agents ?? []).map((a) => {
|
|
44518
|
+
const meta = {
|
|
44519
|
+
resourceId: a.config.resourceId,
|
|
44520
|
+
name: a.config.name,
|
|
44521
|
+
version: a.config.version,
|
|
44522
|
+
status: a.config.status,
|
|
44523
|
+
description: a.config.description,
|
|
44524
|
+
domains: a.config.domains
|
|
44525
|
+
};
|
|
44526
|
+
if (a.contract.inputSchema) {
|
|
44527
|
+
try {
|
|
44528
|
+
meta.inputSchema = external_exports.toJSONSchema(a.contract.inputSchema);
|
|
44529
|
+
} catch {
|
|
44530
|
+
schemaWarnings.push(`${a.config.resourceId}: inputSchema could not be serialized`);
|
|
44531
|
+
}
|
|
44424
44532
|
}
|
|
44425
|
-
|
|
44426
|
-
|
|
44427
|
-
|
|
44428
|
-
|
|
44429
|
-
|
|
44430
|
-
|
|
44431
|
-
name: a.config.name,
|
|
44432
|
-
version: a.config.version,
|
|
44433
|
-
status: a.config.status,
|
|
44434
|
-
description: a.config.description,
|
|
44435
|
-
domains: a.config.domains
|
|
44436
|
-
};
|
|
44437
|
-
if (a.contract.inputSchema) {
|
|
44438
|
-
try {
|
|
44439
|
-
meta.inputSchema = external_exports.toJSONSchema(a.contract.inputSchema);
|
|
44440
|
-
} catch {
|
|
44441
|
-
schemaWarnings.push(`${a.config.resourceId}: inputSchema could not be serialized`);
|
|
44533
|
+
if (a.contract.outputSchema) {
|
|
44534
|
+
try {
|
|
44535
|
+
meta.outputSchema = external_exports.toJSONSchema(a.contract.outputSchema);
|
|
44536
|
+
} catch {
|
|
44537
|
+
schemaWarnings.push(`${a.config.resourceId}: outputSchema could not be serialized`);
|
|
44538
|
+
}
|
|
44442
44539
|
}
|
|
44443
|
-
|
|
44444
|
-
|
|
44445
|
-
|
|
44446
|
-
|
|
44447
|
-
|
|
44448
|
-
schemaWarnings.push(`${a.config.resourceId}: outputSchema could not be serialized`);
|
|
44540
|
+
return meta;
|
|
44541
|
+
});
|
|
44542
|
+
if (schemaWarnings.length > 0) {
|
|
44543
|
+
for (const warning of schemaWarnings) {
|
|
44544
|
+
console.log(source_default.yellow(` warn ${warning}`));
|
|
44449
44545
|
}
|
|
44546
|
+
console.log(source_default.gray(" Schemas will be unavailable on the platform for these resources."));
|
|
44547
|
+
console.log("");
|
|
44450
44548
|
}
|
|
44451
|
-
|
|
44452
|
-
|
|
44453
|
-
|
|
44454
|
-
|
|
44455
|
-
|
|
44456
|
-
|
|
44457
|
-
console.log(source_default.gray(" Schemas will be unavailable on the platform for these resources."));
|
|
44458
|
-
console.log("");
|
|
44459
|
-
}
|
|
44460
|
-
const bundleSpinner = ora("Bundling...").start();
|
|
44461
|
-
const wrapperPath = (0, import_path.resolve)("__elevasis_worker.ts");
|
|
44462
|
-
const bundleOutfile = (0, import_path.resolve)("dist/bundle.js");
|
|
44463
|
-
try {
|
|
44464
|
-
const entryImport = entryPath.replace(/\.ts$/, ".js");
|
|
44465
|
-
const wrapperContent = `import org from ${JSON.stringify(entryImport)}
|
|
44549
|
+
const bundleSpinner = ora("Bundling...").start();
|
|
44550
|
+
const wrapperPath = (0, import_path.resolve)("__elevasis_worker.ts");
|
|
44551
|
+
const bundleOutfile = (0, import_path.resolve)("dist/bundle.js");
|
|
44552
|
+
try {
|
|
44553
|
+
const entryImport = entryPath.replace(/\.ts$/, ".js");
|
|
44554
|
+
const wrapperContent = `import org from ${JSON.stringify(entryImport)}
|
|
44466
44555
|
import { startWorker } from '@elevasis/sdk/worker'
|
|
44467
44556
|
startWorker(org)
|
|
44468
44557
|
`;
|
|
44469
|
-
|
|
44470
|
-
|
|
44471
|
-
|
|
44472
|
-
|
|
44473
|
-
|
|
44474
|
-
|
|
44475
|
-
|
|
44476
|
-
|
|
44477
|
-
|
|
44478
|
-
await (0, import_promises.unlink)(wrapperPath);
|
|
44479
|
-
const bundleBuffer = await (0, import_promises.readFile)(bundleOutfile);
|
|
44480
|
-
const bundleSizeKB = Math.round(bundleBuffer.length / 1024);
|
|
44481
|
-
bundleSpinner.succeed(
|
|
44482
|
-
source_default.green("Bundling...") + source_default.white(" done") + source_default.gray(` (${bundleSizeKB} KB)`)
|
|
44483
|
-
);
|
|
44484
|
-
} catch (error46) {
|
|
44485
|
-
try {
|
|
44558
|
+
await (0, import_promises.writeFile)(wrapperPath, wrapperContent, "utf-8");
|
|
44559
|
+
await (0, import_promises.mkdir)((0, import_path.resolve)("dist"), { recursive: true });
|
|
44560
|
+
await esbuild.build({
|
|
44561
|
+
entryPoints: [wrapperPath],
|
|
44562
|
+
bundle: true,
|
|
44563
|
+
platform: "node",
|
|
44564
|
+
format: "cjs",
|
|
44565
|
+
outfile: bundleOutfile
|
|
44566
|
+
});
|
|
44486
44567
|
await (0, import_promises.unlink)(wrapperPath);
|
|
44487
|
-
|
|
44568
|
+
const bundleBuffer = await (0, import_promises.readFile)(bundleOutfile);
|
|
44569
|
+
const bundleSizeKB = Math.round(bundleBuffer.length / 1024);
|
|
44570
|
+
bundleSpinner.succeed(
|
|
44571
|
+
source_default.green("Bundling...") + source_default.white(" done") + source_default.gray(` (${bundleSizeKB} KB)`)
|
|
44572
|
+
);
|
|
44573
|
+
} catch (error46) {
|
|
44574
|
+
try {
|
|
44575
|
+
await (0, import_promises.unlink)(wrapperPath);
|
|
44576
|
+
} catch {
|
|
44577
|
+
}
|
|
44578
|
+
bundleSpinner.fail(source_default.red("Bundling failed"));
|
|
44579
|
+
throw error46;
|
|
44488
44580
|
}
|
|
44489
|
-
|
|
44490
|
-
|
|
44491
|
-
|
|
44492
|
-
|
|
44493
|
-
|
|
44494
|
-
|
|
44495
|
-
|
|
44496
|
-
|
|
44497
|
-
|
|
44498
|
-
|
|
44499
|
-
|
|
44500
|
-
|
|
44501
|
-
|
|
44502
|
-
console.error(source_default.gray(" ELEVASIS_PLATFORM_KEY=sk_..."));
|
|
44503
|
-
}
|
|
44504
|
-
throw new Error("Missing API key environment variable");
|
|
44505
|
-
}
|
|
44506
|
-
const metadata = {
|
|
44507
|
-
sdkVersion: SDK_VERSION,
|
|
44508
|
-
mode: env2,
|
|
44509
|
-
resources: { workflows, agents },
|
|
44510
|
-
...documentation ? { documentation } : {},
|
|
44511
|
-
...org.relationships ? { relationships: org.relationships } : {}
|
|
44512
|
-
};
|
|
44513
|
-
const form = new FormData();
|
|
44514
|
-
form.append("bundle", new Blob([bundleBuffer]), "bundle.js");
|
|
44515
|
-
form.append("metadata", JSON.stringify(metadata));
|
|
44516
|
-
const response = await fetch(`${apiUrl}/api/external/deploy`, {
|
|
44517
|
-
method: "POST",
|
|
44518
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
44519
|
-
body: form
|
|
44520
|
-
});
|
|
44521
|
-
if (!response.ok) {
|
|
44522
|
-
const errorText = await response.text();
|
|
44523
|
-
uploadSpinner.fail(source_default.red("Upload failed"));
|
|
44524
|
-
console.error(source_default.red(` ${response.status}: ${errorText}`));
|
|
44525
|
-
throw new Error(`Deploy upload failed (${response.status}): ${errorText}`);
|
|
44526
|
-
}
|
|
44527
|
-
const result = await response.json();
|
|
44528
|
-
uploadSpinner.succeed(
|
|
44529
|
-
source_default.green("Uploading...") + source_default.white(" done")
|
|
44530
|
-
);
|
|
44531
|
-
const totalResources = (org.workflows?.length ?? 0) + (org.agents?.length ?? 0);
|
|
44532
|
-
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
44533
|
-
if (result.status === "active") {
|
|
44534
|
-
console.log("");
|
|
44535
|
-
console.log(source_default.green.bold(` Deployed! ${totalResources} resource${totalResources !== 1 ? "s" : ""} live.`));
|
|
44536
|
-
if (result.deployId) {
|
|
44537
|
-
console.log(source_default.gray(` Version: ${result.deployId}`));
|
|
44581
|
+
const uploadSpinner = ora("Uploading...").start();
|
|
44582
|
+
try {
|
|
44583
|
+
const bundleBuffer = await (0, import_promises.readFile)(bundleOutfile);
|
|
44584
|
+
const apiKey = resolveApiKey(options2.prod);
|
|
44585
|
+
if (!apiKey) {
|
|
44586
|
+
uploadSpinner.fail(source_default.red("Missing API key environment variable"));
|
|
44587
|
+
console.error(source_default.gray(" Set it in your .env file or shell environment:"));
|
|
44588
|
+
if (!options2.prod && process.env.NODE_ENV === "development") {
|
|
44589
|
+
console.error(source_default.gray(" ELEVASIS_PLATFORM_KEY_DEV=sk_... (or ELEVASIS_PLATFORM_KEY as fallback)"));
|
|
44590
|
+
} else {
|
|
44591
|
+
console.error(source_default.gray(" ELEVASIS_PLATFORM_KEY=sk_..."));
|
|
44592
|
+
}
|
|
44593
|
+
throw new Error("Missing API key environment variable");
|
|
44538
44594
|
}
|
|
44539
|
-
|
|
44540
|
-
|
|
44541
|
-
|
|
44542
|
-
|
|
44543
|
-
|
|
44544
|
-
|
|
44595
|
+
const metadata = {
|
|
44596
|
+
sdkVersion: SDK_VERSION,
|
|
44597
|
+
mode: env2,
|
|
44598
|
+
resources: { workflows, agents },
|
|
44599
|
+
...documentation ? { documentation } : {},
|
|
44600
|
+
...org.relationships ? { relationships: org.relationships } : {}
|
|
44601
|
+
};
|
|
44602
|
+
const form = new FormData();
|
|
44603
|
+
form.append("bundle", new Blob([bundleBuffer]), "bundle.js");
|
|
44604
|
+
form.append("metadata", JSON.stringify(metadata));
|
|
44605
|
+
const response = await fetch(`${apiUrl}/api/external/deploy`, {
|
|
44606
|
+
method: "POST",
|
|
44607
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
44608
|
+
body: form
|
|
44609
|
+
});
|
|
44610
|
+
if (!response.ok) {
|
|
44611
|
+
const errorText = await response.text();
|
|
44612
|
+
uploadSpinner.fail(source_default.red("Upload failed"));
|
|
44613
|
+
console.error(source_default.red(` ${response.status}: ${errorText}`));
|
|
44614
|
+
throw new Error(`Deploy upload failed (${response.status}): ${errorText}`);
|
|
44615
|
+
}
|
|
44616
|
+
const result = await response.json();
|
|
44617
|
+
uploadSpinner.succeed(source_default.green("Uploading...") + source_default.white(" done"));
|
|
44618
|
+
const totalResources = (org.workflows?.length ?? 0) + (org.agents?.length ?? 0);
|
|
44619
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
44620
|
+
if (result.status === "active") {
|
|
44621
|
+
console.log("");
|
|
44622
|
+
console.log(
|
|
44623
|
+
source_default.green.bold(` Deployed! ${totalResources} resource${totalResources !== 1 ? "s" : ""} live.`)
|
|
44624
|
+
);
|
|
44625
|
+
if (result.deployId) {
|
|
44626
|
+
console.log(source_default.gray(` Version: ${result.deployId}`));
|
|
44627
|
+
}
|
|
44628
|
+
console.log(source_default.gray(` Duration: ${elapsed}s`));
|
|
44629
|
+
} else if (result.status === "failed") {
|
|
44630
|
+
console.log("");
|
|
44631
|
+
console.log(source_default.red.bold(" Deploy failed."));
|
|
44632
|
+
if (result.error) {
|
|
44633
|
+
console.error(source_default.red(` ${result.error}`));
|
|
44634
|
+
}
|
|
44635
|
+
throw new Error(`Deploy failed: ${result.error ?? "unknown error"}`);
|
|
44636
|
+
} else {
|
|
44637
|
+
console.log("");
|
|
44638
|
+
console.log(source_default.yellow(` Deploy status: ${result.status ?? "unknown"}`));
|
|
44639
|
+
if (result.deployId) {
|
|
44640
|
+
console.log(source_default.gray(` Version: ${result.deployId}`));
|
|
44641
|
+
}
|
|
44545
44642
|
}
|
|
44546
|
-
|
|
44547
|
-
|
|
44548
|
-
|
|
44549
|
-
console.log(source_default.yellow(` Deploy status: ${result.status ?? "unknown"}`));
|
|
44550
|
-
if (result.deployId) {
|
|
44551
|
-
console.log(source_default.gray(` Version: ${result.deployId}`));
|
|
44643
|
+
} catch (error46) {
|
|
44644
|
+
if (uploadSpinner.isSpinning) {
|
|
44645
|
+
uploadSpinner.fail(source_default.red("Deploy failed"));
|
|
44552
44646
|
}
|
|
44647
|
+
throw error46;
|
|
44553
44648
|
}
|
|
44554
|
-
}
|
|
44555
|
-
|
|
44556
|
-
uploadSpinner.fail(source_default.red("Deploy failed"));
|
|
44557
|
-
}
|
|
44558
|
-
throw error46;
|
|
44559
|
-
}
|
|
44560
|
-
}));
|
|
44649
|
+
})
|
|
44650
|
+
);
|
|
44561
44651
|
}
|
|
44562
44652
|
|
|
44563
44653
|
// src/cli/commands/check.ts
|
|
@@ -44663,6 +44753,7 @@ function registerCheckCommand(program3) {
|
|
|
44663
44753
|
}
|
|
44664
44754
|
|
|
44665
44755
|
// src/cli/commands/exec.ts
|
|
44756
|
+
var import_node_fs = require("node:fs");
|
|
44666
44757
|
var POLL_INTERVAL_MS = 3e3;
|
|
44667
44758
|
async function pollForCompletion(resourceId, executionId, apiUrl) {
|
|
44668
44759
|
const pollSpinner = ora("Waiting for completion...").start();
|
|
@@ -44681,10 +44772,7 @@ async function pollForCompletion(resourceId, executionId, apiUrl) {
|
|
|
44681
44772
|
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
44682
44773
|
pollSpinner.text = `Waiting for completion... (${elapsed}s)`;
|
|
44683
44774
|
try {
|
|
44684
|
-
const detail = await apiGet(
|
|
44685
|
-
`/api/external/executions/${resourceId}/${executionId}`,
|
|
44686
|
-
apiUrl
|
|
44687
|
-
);
|
|
44775
|
+
const detail = await apiGet(`/api/external/executions/${resourceId}/${executionId}`, apiUrl);
|
|
44688
44776
|
if (detail.status === "completed" || detail.status === "failed") {
|
|
44689
44777
|
process.removeListener("SIGINT", cleanup);
|
|
44690
44778
|
if (detail.status === "completed") {
|
|
@@ -44717,70 +44805,71 @@ async function pollForCompletion(resourceId, executionId, apiUrl) {
|
|
|
44717
44805
|
}
|
|
44718
44806
|
function registerExecCommand(program3) {
|
|
44719
44807
|
program3.command("exec <resourceId>").description(`Execute a deployed resource
|
|
44720
|
-
Example: elevasis-sdk 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(
|
|
44721
|
-
|
|
44722
|
-
|
|
44723
|
-
|
|
44724
|
-
|
|
44725
|
-
|
|
44726
|
-
|
|
44727
|
-
|
|
44728
|
-
|
|
44729
|
-
|
|
44730
|
-
|
|
44731
|
-
console.log(source_default.gray(" Execution ID:"), source_default.cyan(asyncResult.executionId));
|
|
44732
|
-
console.log("");
|
|
44733
|
-
await pollForCompletion(resourceId, asyncResult.executionId, apiUrl);
|
|
44734
|
-
return;
|
|
44735
|
-
}
|
|
44736
|
-
const spinner = ora(`Executing ${resourceId}...`).start();
|
|
44737
|
-
try {
|
|
44738
|
-
const result = await apiPost(
|
|
44739
|
-
"/api/external/execute",
|
|
44740
|
-
{ resourceId, input },
|
|
44741
|
-
apiUrl
|
|
44742
|
-
);
|
|
44743
|
-
if (result.success) {
|
|
44744
|
-
spinner.succeed(source_default.green("Execution complete"));
|
|
44745
|
-
console.log(source_default.gray(" Execution ID:"), source_default.cyan(result.executionId));
|
|
44746
|
-
console.log("");
|
|
44747
|
-
console.log(source_default.green(" Output:"));
|
|
44748
|
-
console.log(JSON.stringify(result.data, null, 2));
|
|
44749
|
-
} else {
|
|
44750
|
-
spinner.fail(source_default.red("Execution failed"));
|
|
44751
|
-
console.log(source_default.gray(" Execution ID:"), source_default.cyan(result.executionId));
|
|
44752
|
-
}
|
|
44753
|
-
} catch (error46) {
|
|
44754
|
-
const message = error46 instanceof Error ? error46.message : String(error46);
|
|
44755
|
-
const isConnectionFailure = message.includes("fetch failed") || message.includes("ECONNRESET") || message.includes("socket hang up") || message.includes("network");
|
|
44756
|
-
if (isConnectionFailure) {
|
|
44757
|
-
spinner.warn("Connection lost -- execution may still be running");
|
|
44758
|
-
console.log();
|
|
44759
|
-
try {
|
|
44760
|
-
const recoverSpinner = ora("Recovering execution...").start();
|
|
44761
|
-
const listResult = await apiGet(
|
|
44762
|
-
`/api/external/executions/${resourceId}?limit=1`,
|
|
44808
|
+
Example: elevasis-sdk exec my-workflow -i '{"key":"value"}'`).option("-i, --input <json>", "Input data as JSON string").option("-f, --input-file <path>", "Read input from a JSON file (avoids shell escaping issues)").option("--async", "Execute asynchronously with polling").option("--api-url <url>", "API URL").action(
|
|
44809
|
+
wrapAction(
|
|
44810
|
+
"exec",
|
|
44811
|
+
async (resourceId, options2) => {
|
|
44812
|
+
const input = options2.inputFile ? JSON.parse((0, import_node_fs.readFileSync)(options2.inputFile, "utf8")) : options2.input ? JSON.parse(options2.input) : {};
|
|
44813
|
+
const apiUrl = resolveApiUrl(options2.apiUrl);
|
|
44814
|
+
if (options2.async) {
|
|
44815
|
+
const asyncSpinner = ora(`Starting async execution of ${resourceId}...`).start();
|
|
44816
|
+
const asyncResult = await apiPost(
|
|
44817
|
+
"/api/external/execute-async",
|
|
44818
|
+
{ resourceId, input },
|
|
44763
44819
|
apiUrl
|
|
44764
44820
|
);
|
|
44765
|
-
|
|
44766
|
-
|
|
44767
|
-
|
|
44768
|
-
|
|
44769
|
-
|
|
44821
|
+
asyncSpinner.succeed(source_default.green("Execution started"));
|
|
44822
|
+
console.log(source_default.gray(" Execution ID:"), source_default.cyan(asyncResult.executionId));
|
|
44823
|
+
console.log("");
|
|
44824
|
+
await pollForCompletion(resourceId, asyncResult.executionId, apiUrl);
|
|
44825
|
+
return;
|
|
44826
|
+
}
|
|
44827
|
+
const spinner = ora(`Executing ${resourceId}...`).start();
|
|
44828
|
+
try {
|
|
44829
|
+
const result = await apiPost("/api/external/execute", { resourceId, input }, apiUrl);
|
|
44830
|
+
if (result.success) {
|
|
44831
|
+
spinner.succeed(source_default.green("Execution complete"));
|
|
44832
|
+
console.log(source_default.gray(" Execution ID:"), source_default.cyan(result.executionId));
|
|
44833
|
+
console.log("");
|
|
44834
|
+
console.log(source_default.green(" Output:"));
|
|
44835
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
44770
44836
|
} else {
|
|
44771
|
-
|
|
44772
|
-
console.log(source_default.
|
|
44837
|
+
spinner.fail(source_default.red("Execution failed"));
|
|
44838
|
+
console.log(source_default.gray(" Execution ID:"), source_default.cyan(result.executionId));
|
|
44773
44839
|
}
|
|
44774
|
-
} catch {
|
|
44775
|
-
|
|
44776
|
-
|
|
44840
|
+
} catch (error46) {
|
|
44841
|
+
const message = error46 instanceof Error ? error46.message : String(error46);
|
|
44842
|
+
const isConnectionFailure = message.includes("fetch failed") || message.includes("ECONNRESET") || message.includes("socket hang up") || message.includes("network");
|
|
44843
|
+
if (isConnectionFailure) {
|
|
44844
|
+
spinner.warn("Connection lost -- execution may still be running");
|
|
44845
|
+
console.log();
|
|
44846
|
+
try {
|
|
44847
|
+
const recoverSpinner = ora("Recovering execution...").start();
|
|
44848
|
+
const listResult = await apiGet(
|
|
44849
|
+
`/api/external/executions/${resourceId}?limit=1`,
|
|
44850
|
+
apiUrl
|
|
44851
|
+
);
|
|
44852
|
+
const running = listResult.executions.find((e) => e.status === "running");
|
|
44853
|
+
if (running) {
|
|
44854
|
+
recoverSpinner.succeed(`Found running execution: ${running.id}`);
|
|
44855
|
+
console.log();
|
|
44856
|
+
await pollForCompletion(resourceId, running.id, apiUrl);
|
|
44857
|
+
} else {
|
|
44858
|
+
recoverSpinner.info("No running execution found -- it may have already completed");
|
|
44859
|
+
console.log(source_default.dim(" Check status manually or re-run with --async"));
|
|
44860
|
+
}
|
|
44861
|
+
} catch {
|
|
44862
|
+
console.log(source_default.yellow("Could not recover. The execution may still be running on the server."));
|
|
44863
|
+
console.log(source_default.dim(" Check status manually or re-run with --async"));
|
|
44864
|
+
}
|
|
44865
|
+
process.exit(0);
|
|
44866
|
+
}
|
|
44867
|
+
spinner.fail(source_default.red("Execution failed"));
|
|
44868
|
+
throw error46;
|
|
44777
44869
|
}
|
|
44778
|
-
process.exit(0);
|
|
44779
44870
|
}
|
|
44780
|
-
|
|
44781
|
-
|
|
44782
|
-
}
|
|
44783
|
-
}));
|
|
44871
|
+
)
|
|
44872
|
+
);
|
|
44784
44873
|
}
|
|
44785
44874
|
|
|
44786
44875
|
// src/cli/commands/resources.ts
|
|
@@ -45090,7 +45179,7 @@ var import_path3 = require("path");
|
|
|
45090
45179
|
var import_promises2 = require("fs/promises");
|
|
45091
45180
|
|
|
45092
45181
|
// src/cli/commands/templates/core/workspace.ts
|
|
45093
|
-
var TEMPLATE_VERSION =
|
|
45182
|
+
var TEMPLATE_VERSION = 29;
|
|
45094
45183
|
function configTemplate() {
|
|
45095
45184
|
return `import type { ElevasConfig } from '@elevasis/sdk'
|
|
45096
45185
|
|
|
@@ -45102,44 +45191,52 @@ export default {
|
|
|
45102
45191
|
`;
|
|
45103
45192
|
}
|
|
45104
45193
|
function packageJsonTemplate(organization) {
|
|
45105
|
-
return JSON.stringify(
|
|
45106
|
-
|
|
45107
|
-
|
|
45108
|
-
|
|
45109
|
-
|
|
45110
|
-
|
|
45111
|
-
|
|
45112
|
-
|
|
45113
|
-
|
|
45114
|
-
|
|
45115
|
-
|
|
45194
|
+
return JSON.stringify(
|
|
45195
|
+
{
|
|
45196
|
+
name: organization,
|
|
45197
|
+
private: true,
|
|
45198
|
+
type: "module",
|
|
45199
|
+
scripts: {
|
|
45200
|
+
"check-types": "tsc --noEmit",
|
|
45201
|
+
check: "elevasis-sdk check",
|
|
45202
|
+
deploy: "elevasis-sdk deploy"
|
|
45203
|
+
},
|
|
45204
|
+
dependencies: {
|
|
45205
|
+
"@elevasis/sdk": `^${SDK_VERSION}`
|
|
45206
|
+
},
|
|
45207
|
+
devDependencies: {
|
|
45208
|
+
typescript: "5.9.2",
|
|
45209
|
+
zod: "4.1.12"
|
|
45210
|
+
}
|
|
45116
45211
|
},
|
|
45117
|
-
|
|
45118
|
-
|
|
45119
|
-
|
|
45120
|
-
}
|
|
45121
|
-
}, null, 2) + "\n";
|
|
45212
|
+
null,
|
|
45213
|
+
2
|
|
45214
|
+
) + "\n";
|
|
45122
45215
|
}
|
|
45123
45216
|
function pnpmWorkspaceTemplate() {
|
|
45124
45217
|
return `packages: []
|
|
45125
45218
|
`;
|
|
45126
45219
|
}
|
|
45127
45220
|
function tsconfigTemplate() {
|
|
45128
|
-
return JSON.stringify(
|
|
45129
|
-
|
|
45130
|
-
|
|
45131
|
-
|
|
45132
|
-
|
|
45133
|
-
|
|
45134
|
-
|
|
45135
|
-
|
|
45136
|
-
|
|
45137
|
-
|
|
45138
|
-
|
|
45221
|
+
return JSON.stringify(
|
|
45222
|
+
{
|
|
45223
|
+
compilerOptions: {
|
|
45224
|
+
target: "ES2022",
|
|
45225
|
+
module: "ESNext",
|
|
45226
|
+
moduleResolution: "Bundler",
|
|
45227
|
+
strict: true,
|
|
45228
|
+
esModuleInterop: true,
|
|
45229
|
+
skipLibCheck: true,
|
|
45230
|
+
forceConsistentCasingInFileNames: true,
|
|
45231
|
+
isolatedModules: true,
|
|
45232
|
+
outDir: "./dist"
|
|
45233
|
+
},
|
|
45234
|
+
include: ["src"],
|
|
45235
|
+
exclude: ["node_modules", "dist"]
|
|
45139
45236
|
},
|
|
45140
|
-
|
|
45141
|
-
|
|
45142
|
-
|
|
45237
|
+
null,
|
|
45238
|
+
2
|
|
45239
|
+
) + "\n";
|
|
45143
45240
|
}
|
|
45144
45241
|
function envTemplate() {
|
|
45145
45242
|
return `ELEVASIS_PLATFORM_KEY=
|
|
@@ -45257,6 +45354,28 @@ function claudeSettingsTemplate() {
|
|
|
45257
45354
|
}
|
|
45258
45355
|
]
|
|
45259
45356
|
}
|
|
45357
|
+
],
|
|
45358
|
+
PostToolUse: [
|
|
45359
|
+
{
|
|
45360
|
+
matcher: "Write|Edit|MultiEdit",
|
|
45361
|
+
hooks: [
|
|
45362
|
+
{
|
|
45363
|
+
type: "command",
|
|
45364
|
+
command: "node .claude/hooks/post-edit-validate.mjs"
|
|
45365
|
+
}
|
|
45366
|
+
]
|
|
45367
|
+
}
|
|
45368
|
+
],
|
|
45369
|
+
PostToolUseFailure: [
|
|
45370
|
+
{
|
|
45371
|
+
matcher: "Bash",
|
|
45372
|
+
hooks: [
|
|
45373
|
+
{
|
|
45374
|
+
type: "command",
|
|
45375
|
+
command: "node .claude/hooks/tool-failure-recovery.mjs"
|
|
45376
|
+
}
|
|
45377
|
+
]
|
|
45378
|
+
}
|
|
45260
45379
|
]
|
|
45261
45380
|
}
|
|
45262
45381
|
},
|
|
@@ -45290,8 +45409,8 @@ process.stdin.on('end', () => {
|
|
|
45290
45409
|
function claudeSdkBoundaryHookTemplate() {
|
|
45291
45410
|
return String.raw`#!/usr/bin/env node
|
|
45292
45411
|
// enforce-sdk-boundary.mjs
|
|
45293
|
-
// Blocks
|
|
45294
|
-
//
|
|
45412
|
+
// Blocks file modifications (Write, Edit, MultiEdit, destructive Bash) outside the project root.
|
|
45413
|
+
// Git, gh, and other CLI tools are NOT blocked -- the agent can use them freely.
|
|
45295
45414
|
|
|
45296
45415
|
import { resolve, normalize } from "node:path";
|
|
45297
45416
|
import { appendFileSync, mkdirSync } from "node:fs";
|
|
@@ -45336,44 +45455,6 @@ try {
|
|
|
45336
45455
|
if (input.tool_name === "Bash") {
|
|
45337
45456
|
const cmd = input.tool_input?.command ?? "";
|
|
45338
45457
|
|
|
45339
|
-
// GitHub CLI -- blocked (affects shared remote state, user-initiated only)
|
|
45340
|
-
if (/\bgh\b/.test(cmd)) {
|
|
45341
|
-
deny(
|
|
45342
|
-
"BLOCKED: GitHub CLI (gh) command detected.\n" +
|
|
45343
|
-
"WHY: GitHub CLI operations affect shared remote state (PRs, issues, releases). These must be user-initiated.\n" +
|
|
45344
|
-
"INSTEAD: Ask the user to run this gh command manually."
|
|
45345
|
-
);
|
|
45346
|
-
process.exit(0);
|
|
45347
|
-
}
|
|
45348
|
-
|
|
45349
|
-
// Destructive git -- blocked
|
|
45350
|
-
if (/(?<!-)\bgit\s+reset\b/.test(cmd) && /--hard/.test(cmd)) {
|
|
45351
|
-
deny(
|
|
45352
|
-
"BLOCKED: git reset --hard detected.\n" +
|
|
45353
|
-
"WHY: Hard resets destroy uncommitted work and cannot be undone. This must be user-initiated.\n" +
|
|
45354
|
-
"INSTEAD: Ask the user to run this git command manually."
|
|
45355
|
-
);
|
|
45356
|
-
process.exit(0);
|
|
45357
|
-
}
|
|
45358
|
-
|
|
45359
|
-
if (/(?<!-)\bgit\s+clean\b/.test(cmd) && /-[a-zA-Z]*f/.test(cmd)) {
|
|
45360
|
-
deny(
|
|
45361
|
-
"BLOCKED: git clean -f detected.\n" +
|
|
45362
|
-
"WHY: Force-cleaning the working tree permanently removes untracked files. This must be user-initiated.\n" +
|
|
45363
|
-
"INSTEAD: Ask the user to run this git command manually."
|
|
45364
|
-
);
|
|
45365
|
-
process.exit(0);
|
|
45366
|
-
}
|
|
45367
|
-
|
|
45368
|
-
if (/(?<!-)\bgit\s+(rebase|merge)\b/.test(cmd)) {
|
|
45369
|
-
deny(
|
|
45370
|
-
"BLOCKED: git rebase/merge detected.\n" +
|
|
45371
|
-
"WHY: Rebase and merge rewrite history or combine branches in ways that require user judgment.\n" +
|
|
45372
|
-
"INSTEAD: Ask the user to run this git command manually."
|
|
45373
|
-
);
|
|
45374
|
-
process.exit(0);
|
|
45375
|
-
}
|
|
45376
|
-
|
|
45377
45458
|
// Path-scoped blocks -- destructive commands or redirects outside project root
|
|
45378
45459
|
const winPaths = cmd.match(/(?<![A-Za-z])[A-Za-z]:[/\\][^\s"'|;&)]+/g) || [];
|
|
45379
45460
|
const unixPaths = cmd.match(/(?<=\s|^|"|')\/[^\s"'|;&)]+/g) || [];
|
|
@@ -45556,7 +45637,7 @@ For detailed per-dimension adaptation rules, read
|
|
|
45556
45637
|
| --- | --- |
|
|
45557
45638
|
| \`/meta\` | Project lifecycle: init, status, fix, deploy, health |
|
|
45558
45639
|
| \`/docs\` | Browse, create, and verify permanent documentation |
|
|
45559
|
-
| \`/work\` | Task tracking: create, save, resume
|
|
45640
|
+
| \`/work\` | Task tracking: auto-detects intent (create, save, resume); suggests complete |
|
|
45560
45641
|
| \`/tutorial\` | Progressive learning path (21 items across 4 sections) |
|
|
45561
45642
|
|
|
45562
45643
|
## Skills
|
|
@@ -45788,43 +45869,46 @@ Observation focus: full lifecycle coverage, pipeline internals.
|
|
|
45788
45869
|
**Item 4: /work and /docs**
|
|
45789
45870
|
|
|
45790
45871
|
When automation is none:
|
|
45791
|
-
"You can ask the assistant to track work across conversations.
|
|
45792
|
-
|
|
45793
|
-
you
|
|
45794
|
-
|
|
45795
|
-
|
|
45796
|
-
|
|
45797
|
-
|
|
45872
|
+
"You can ask the assistant to track work across conversations. Just say /work and tell it
|
|
45873
|
+
what you're working on -- it figures out the rest. It'll save your progress automatically,
|
|
45874
|
+
and next session you just say /work to pick up where you left off." Walk through the
|
|
45875
|
+
concept without deep command details. Then introduce /docs:
|
|
45876
|
+
"When a task is done, the assistant will ask if you want to finalize it -- that moves it
|
|
45877
|
+
to docs/ permanently. /docs helps you find and read what's there -- like a notebook for
|
|
45878
|
+
your project." Run /docs (no args) to show the docs/ overview together.
|
|
45879
|
+
Verify: Run \`/work\` and describe "practice task", see it created automatically.
|
|
45798
45880
|
Then run /docs to see the docs/ structure.
|
|
45799
45881
|
Observation focus: persistence concept, cross-session continuity, docs as permanent notes.
|
|
45800
45882
|
|
|
45801
45883
|
When automation is low-code:
|
|
45802
|
-
Show /work
|
|
45803
|
-
|
|
45804
|
-
|
|
45884
|
+
Show /work as intent-driven: the agent detects whether to create, resume, or save based
|
|
45885
|
+
on context. Create happens when you describe new work, resume when you pick an existing
|
|
45886
|
+
task, save happens automatically when progress is made. Complete is the only action that
|
|
45887
|
+
asks permission first.
|
|
45805
45888
|
Then introduce /docs: "/docs is for permanent knowledge -- things that don't expire. Use
|
|
45806
45889
|
/docs create to document a workflow or integration. Use /docs verify to check if your
|
|
45807
45890
|
docs match the current code." Explain the boundary: /work manages in-progress/, /docs
|
|
45808
45891
|
manages permanent docs/.
|
|
45809
|
-
Verify:
|
|
45810
|
-
|
|
45811
|
-
Observation focus: task tracking
|
|
45892
|
+
Verify: Run \`/work\` and describe "practice task", see auto-create. Make some changes,
|
|
45893
|
+
then notice auto-save. Then run /docs to browse docs/.
|
|
45894
|
+
Observation focus: intent-driven task tracking, /work vs /docs separation.
|
|
45812
45895
|
|
|
45813
45896
|
When automation is custom:
|
|
45814
45897
|
Read \`.claude/commands/work.md\`. Full /work coverage:
|
|
45815
|
-
|
|
45816
|
-
|
|
45817
|
-
|
|
45898
|
+
Intent detection table (list, create, resume, save auto-invoked; complete always suggests).
|
|
45899
|
+
Task doc anatomy: kebab-case filename, frontmatter with status, Objective/Plan/Progress/
|
|
45900
|
+
Resume Context sections. Auto-save behavior: triggers on heavy context, wrap-up signals,
|
|
45901
|
+
2+ steps completed. Resolution order for resume: number, keyword match, single in-progress.
|
|
45818
45902
|
Then read \`.claude/commands/docs.md\`. Cover /docs operations:
|
|
45819
45903
|
/docs (default): browse permanent docs/, categorized with read-only auto-generated files
|
|
45820
45904
|
separate; /docs create: interview-driven, resource-aware doc creation from src/ code
|
|
45821
45905
|
analysis; /docs verify: cross-references resource IDs, schema fields, platform tools in
|
|
45822
45906
|
docs against src/ -- standalone analog of /meta fix step 5.
|
|
45823
|
-
Explain the relationship: /work owns docs/in-progress/ (task lifecycle),
|
|
45907
|
+
Explain the relationship: /work owns docs/in-progress/ (task lifecycle), completing a task
|
|
45824
45908
|
moves docs to permanent location, /docs browses and verifies what's there.
|
|
45825
|
-
Verify: Create a task
|
|
45826
|
-
to see it in the permanent docs/ listing.
|
|
45827
|
-
Observation focus:
|
|
45909
|
+
Verify: Create a task via /work, make progress, observe auto-save, then run /work complete
|
|
45910
|
+
to move it. Run /docs to see it in the permanent docs/ listing.
|
|
45911
|
+
Observation focus: intent detection, auto-save behavior, /work-to-/docs handoff, docs verification.
|
|
45828
45912
|
|
|
45829
45913
|
**Item 5: Your First Custom Workflow**
|
|
45830
45914
|
|
|
@@ -46358,6 +46442,8 @@ function claudeWorkCommandTemplate() {
|
|
|
46358
46442
|
|
|
46359
46443
|
You are a task tracking assistant for this Elevasis workspace. \`/work\` is the primary command for managing all work and projects.
|
|
46360
46444
|
|
|
46445
|
+
Your job is to **intelligently manage tasks without requiring the user to know subcommands**. Detect what the user needs from context and act accordingly.
|
|
46446
|
+
|
|
46361
46447
|
## Context
|
|
46362
46448
|
|
|
46363
46449
|
Read \`docs/priorities.mdx\` if it exists for current priorities.
|
|
@@ -46377,9 +46463,25 @@ When scanning, treat \`index.mdx\` as the primary task doc for a directory.
|
|
|
46377
46463
|
|
|
46378
46464
|
Enforce exactly three values in frontmatter: \`planned\`, \`in-progress\`, \`complete\`.
|
|
46379
46465
|
|
|
46380
|
-
##
|
|
46466
|
+
## Intent Detection
|
|
46467
|
+
|
|
46468
|
+
When \`/work\` is invoked (with or without arguments), detect intent from context:
|
|
46469
|
+
|
|
46470
|
+
| Signal | Action |
|
|
46471
|
+
|--------|--------|
|
|
46472
|
+
| No arguments, no active conversation context | **List** tasks, then let user pick |
|
|
46473
|
+
| User picks a number or names a task | **Resume** that task automatically |
|
|
46474
|
+
| User describes new work (not matching existing tasks) | **Create** a task doc automatically |
|
|
46475
|
+
| \`/work\` with a description that matches no existing task | **Create** automatically |
|
|
46476
|
+
| \`/work\` with a keyword/name matching an existing task | **Resume** automatically |
|
|
46477
|
+
| Conversation is getting heavy, user wrapping up, or 2+ steps completed | **Save** automatically |
|
|
46478
|
+
| All plan steps are COMPLETE | **Suggest** \`/work complete\` (never auto-invoke) |
|
|
46479
|
+
|
|
46480
|
+
**Key principle:** Create, save, and resume are auto-invoked. Complete always asks permission first.
|
|
46481
|
+
|
|
46482
|
+
## Behaviors
|
|
46381
46483
|
|
|
46382
|
-
###
|
|
46484
|
+
### List and Pick (default when no context)
|
|
46383
46485
|
|
|
46384
46486
|
1. Scan \`docs/in-progress/\` recursively for \`.mdx\` files with \`status\` frontmatter
|
|
46385
46487
|
2. For directories, read \`index.mdx\` as the primary task doc
|
|
@@ -46398,30 +46500,25 @@ Active Tasks
|
|
|
46398
46500
|
3. [complete] CRM Integration
|
|
46399
46501
|
Completed: 2026-03-03
|
|
46400
46502
|
|
|
46401
|
-
Pick a task by number or name
|
|
46402
|
-
- "create" to start new work
|
|
46403
|
-
- "complete <name>" to finish a task
|
|
46503
|
+
Pick a task by number or name, or describe new work to start.
|
|
46404
46504
|
\`\`\`
|
|
46405
46505
|
|
|
46406
46506
|
4. Cross-reference with \`docs/priorities.mdx\` if it exists
|
|
46407
|
-
5. When the user picks a number or describes a task
|
|
46408
|
-
6. If no tasks found,
|
|
46507
|
+
5. When the user picks a number or describes a task, auto-invoke the appropriate flow (resume or create)
|
|
46508
|
+
6. If no tasks found, ask: "What are you trying to accomplish?"
|
|
46409
46509
|
|
|
46410
|
-
###
|
|
46510
|
+
### Create (auto-invoked when new work detected)
|
|
46411
46511
|
|
|
46412
|
-
|
|
46413
|
-
|
|
46414
|
-
1. If no description given, ask: "What are you trying to accomplish?"
|
|
46415
|
-
2. Ask 2-3 focused questions:
|
|
46512
|
+
1. If the user's intent is clear, skip the interview and create directly
|
|
46513
|
+
2. If ambiguous, ask 1-2 focused questions:
|
|
46416
46514
|
- "What does success look like?" (acceptance criteria)
|
|
46417
|
-
- "
|
|
46418
|
-
|
|
46419
|
-
|
|
46420
|
-
- Scan \`docs/in-progress/\` for existing directories related to the topic
|
|
46515
|
+
- "Do we need to investigate anything first, or is the path clear?"
|
|
46516
|
+
3. Scan \`docs/in-progress/\` for existing directories related to the topic
|
|
46517
|
+
4. Determine directory placement:
|
|
46421
46518
|
- If related to existing directory, create as a file within it
|
|
46422
46519
|
- If new concept that may grow, create \`docs/in-progress/<slug>/index.mdx\`
|
|
46423
46520
|
- If small/standalone, create \`docs/in-progress/<slug>.mdx\`
|
|
46424
|
-
|
|
46521
|
+
5. Create the doc with \`status: planned\` and structured sections:
|
|
46425
46522
|
|
|
46426
46523
|
\`\`\`yaml
|
|
46427
46524
|
---
|
|
@@ -46433,15 +46530,20 @@ status: planned
|
|
|
46433
46530
|
|
|
46434
46531
|
Sections: Objective (what and why), Plan (numbered steps), Progress (per-step tracking with PENDING markers), Resume Context (current state, key docs, "To continue" prompt).
|
|
46435
46532
|
|
|
46436
|
-
|
|
46437
|
-
|
|
46533
|
+
6. Update \`docs/priorities.mdx\` if it exists
|
|
46534
|
+
7. Report what was created with location and step count
|
|
46438
46535
|
|
|
46439
|
-
###
|
|
46536
|
+
### Save (auto-invoked when progress detected)
|
|
46440
46537
|
|
|
46441
|
-
|
|
46538
|
+
Auto-save triggers (do NOT ask, just save):
|
|
46539
|
+
- The conversation context is getting heavy (many tool calls, large file reads)
|
|
46540
|
+
- The user appears to be wrapping up (thanks, goodbye, switching topics)
|
|
46541
|
+
- Significant progress has been made (2+ steps completed without saving)
|
|
46542
|
+
- Before a context reset
|
|
46442
46543
|
|
|
46443
|
-
|
|
46444
|
-
|
|
46544
|
+
Save flow:
|
|
46545
|
+
1. Identify the current task from conversation context
|
|
46546
|
+
2. If not working on a tracked task, offer to create a new one
|
|
46445
46547
|
3. Update Progress section:
|
|
46446
46548
|
- Mark completed steps as \`COMPLETE\` with: what was done, key decisions, files changed
|
|
46447
46549
|
- Mark current step as \`IN PROGRESS\` with current state
|
|
@@ -46451,15 +46553,13 @@ Update the current task doc's Progress and Resume Context:
|
|
|
46451
46553
|
- Files Modified: table of file paths and descriptions of changes
|
|
46452
46554
|
- Key docs to read on resume: file paths with why they matter
|
|
46453
46555
|
- To continue: copy-pasteable prompt for the next session
|
|
46454
|
-
5. Set \`status\` appropriately (\`in-progress\` if ongoing
|
|
46455
|
-
6.
|
|
46456
|
-
|
|
46457
|
-
### \`resume [name-or-number]\`
|
|
46556
|
+
5. Set \`status\` appropriately (\`in-progress\` if ongoing)
|
|
46557
|
+
6. Briefly confirm: "Progress saved to {path}."
|
|
46458
46558
|
|
|
46459
|
-
Resume
|
|
46559
|
+
### Resume (auto-invoked when existing task detected)
|
|
46460
46560
|
|
|
46461
46561
|
**Resolution order:**
|
|
46462
|
-
1. If a number is given
|
|
46562
|
+
1. If a number is given, map to the numbered list from the task list
|
|
46463
46563
|
2. If a name/keyword is given, substring match against task titles and filenames in \`docs/in-progress/\`
|
|
46464
46564
|
3. If no argument, scan for \`status: in-progress\` docs -- if one found, use it; if multiple, list and ask
|
|
46465
46565
|
4. If multiple matches, list them and ask which to resume
|
|
@@ -46485,35 +46585,32 @@ Key context loaded:
|
|
|
46485
46585
|
Ready to continue. {Copy of "To continue" prompt}
|
|
46486
46586
|
\`\`\`
|
|
46487
46587
|
|
|
46488
|
-
###
|
|
46588
|
+
### Complete (NEVER auto-invoked -- always suggest)
|
|
46589
|
+
|
|
46590
|
+
When all plan steps are COMPLETE, suggest: "All steps for '{task}' look done. Want me to finalize it?"
|
|
46489
46591
|
|
|
46490
|
-
|
|
46592
|
+
Only proceed after explicit user confirmation. Then:
|
|
46491
46593
|
|
|
46492
|
-
1.
|
|
46493
|
-
2. **
|
|
46494
|
-
3. **Clean up the doc:**
|
|
46594
|
+
1. **Validate readiness:** Check that all plan steps are marked COMPLETE. If not, warn and ask for confirmation.
|
|
46595
|
+
2. **Clean up the doc:**
|
|
46495
46596
|
- Strip \`## Resume Context\` section entirely (header through next \`##\` or EOF)
|
|
46496
46597
|
- Strip progress markers: \`COMPLETE\`, \`IN PROGRESS\`, \`PENDING\` from step headings
|
|
46497
46598
|
- Remove steps still marked PENDING entirely (header + body) -- they were never done
|
|
46498
46599
|
- Remove \`status\` from frontmatter (keep \`title\` and \`description\`)
|
|
46499
46600
|
- Target 200-400 lines; flag if result exceeds 500 lines
|
|
46500
|
-
|
|
46601
|
+
3. **Determine destination:**
|
|
46501
46602
|
- Scan \`docs/\` (excluding \`docs/in-progress/\`) for existing directories related to this work
|
|
46502
46603
|
- If a related directory exists, propose merging into it
|
|
46503
46604
|
- If no related directory, propose \`docs/<slug>/\` or \`docs/<slug>.mdx\`
|
|
46504
46605
|
- Present the proposed destination and ask user to confirm
|
|
46505
|
-
|
|
46606
|
+
4. **Move the doc(s):**
|
|
46506
46607
|
- Single file: move from \`docs/in-progress/\` to destination
|
|
46507
46608
|
- Directory: move entire directory
|
|
46508
|
-
|
|
46609
|
+
5. **Verify and report:**
|
|
46509
46610
|
- Confirm destination exists, source removed
|
|
46510
46611
|
- Check no leftover \`status:\` or \`## Resume Context\` in moved files
|
|
46511
46612
|
- Update \`docs/priorities.mdx\` if it exists
|
|
46512
46613
|
- Report: task title, cleanup stats (lines before/after), destination path
|
|
46513
|
-
|
|
46514
|
-
## Completion Suggestions
|
|
46515
|
-
|
|
46516
|
-
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."
|
|
46517
46614
|
`;
|
|
46518
46615
|
}
|
|
46519
46616
|
function claudeDocsCommandTemplate() {
|
|
@@ -46579,7 +46676,7 @@ Steps:
|
|
|
46579
46676
|
|
|
46580
46677
|
### \`create [description]\` -- Reference Doc Creation
|
|
46581
46678
|
|
|
46582
|
-
Interview-driven creation for permanent documentation. Task docs go through \`/work
|
|
46679
|
+
Interview-driven creation for permanent documentation. Task docs go through \`/work\`.
|
|
46583
46680
|
|
|
46584
46681
|
1. If no description given, ask: "What do you want to document?"
|
|
46585
46682
|
2. Determine doc type (infer or ask):
|
|
@@ -46949,17 +47046,19 @@ Exactly three values for frontmatter \`status\`: \`planned\`, \`in-progress\`, \
|
|
|
46949
47046
|
- IN PROGRESS -> COMPLETE (finishing a step)
|
|
46950
47047
|
- Do NOT update on every action -- only on step transitions
|
|
46951
47048
|
|
|
46952
|
-
## Save
|
|
47049
|
+
## Auto-Save Behavior
|
|
46953
47050
|
|
|
46954
|
-
|
|
47051
|
+
The agent auto-saves progress (no user action needed) when:
|
|
46955
47052
|
- The conversation context is getting heavy (many tool calls, large file reads)
|
|
46956
47053
|
- The user appears to be wrapping up (thanks, goodbye, switching topics)
|
|
46957
47054
|
- Significant progress has been made (2+ steps completed without saving)
|
|
46958
|
-
- Before a
|
|
47055
|
+
- Before a context reset
|
|
47056
|
+
|
|
47057
|
+
Auto-save updates the task doc's Progress and Resume Context sections silently, then briefly confirms.
|
|
46959
47058
|
|
|
46960
47059
|
## Completion Suggestions
|
|
46961
47060
|
|
|
46962
|
-
When all plan steps are marked COMPLETE, suggest
|
|
47061
|
+
When all plan steps are marked COMPLETE, **suggest** completing the task -- never auto-invoke. Ask: "All steps for '{task}' look done. Want me to finalize it?"
|
|
46963
47062
|
|
|
46964
47063
|
## Directory Conventions
|
|
46965
47064
|
|
|
@@ -46969,6 +47068,199 @@ When all plan steps are marked COMPLETE, suggest \`/work complete\` to finalize
|
|
|
46969
47068
|
- Completed tasks move OUT of \`docs/in-progress/\` to \`docs/<relevant-dir>/\`
|
|
46970
47069
|
`;
|
|
46971
47070
|
}
|
|
47071
|
+
function claudePostEditValidateHookTemplate() {
|
|
47072
|
+
return `#!/usr/bin/env node
|
|
47073
|
+
// post-edit-validate.mjs
|
|
47074
|
+
// PostToolUse hook \u2014 auto-formats with prettier, type-checks .ts/.tsx files.
|
|
47075
|
+
// Fires after Edit|Write|MultiEdit succeeds.
|
|
47076
|
+
|
|
47077
|
+
import { existsSync } from 'node:fs'
|
|
47078
|
+
import { resolve, normalize, extname, join, dirname } from 'node:path'
|
|
47079
|
+
import { execSync } from 'node:child_process'
|
|
47080
|
+
|
|
47081
|
+
const ROOT = process.env.CLAUDE_PROJECT_DIR ?? process.cwd()
|
|
47082
|
+
|
|
47083
|
+
// Extensions prettier should format
|
|
47084
|
+
const PRETTIER_EXTENSIONS = new Set([
|
|
47085
|
+
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', '.css', '.md', '.mdx'
|
|
47086
|
+
])
|
|
47087
|
+
|
|
47088
|
+
// Extensions that trigger type-checking
|
|
47089
|
+
const TS_EXTENSIONS = new Set(['.ts', '.tsx'])
|
|
47090
|
+
|
|
47091
|
+
function findNearestTsconfig(startDir) {
|
|
47092
|
+
let dir = startDir
|
|
47093
|
+
const root = normalize(ROOT)
|
|
47094
|
+
while (dir.length >= root.length) {
|
|
47095
|
+
const candidate = join(dir, 'tsconfig.json')
|
|
47096
|
+
if (existsSync(candidate)) return candidate
|
|
47097
|
+
const parent = dirname(dir)
|
|
47098
|
+
if (parent === dir) break
|
|
47099
|
+
dir = parent
|
|
47100
|
+
}
|
|
47101
|
+
return null
|
|
47102
|
+
}
|
|
47103
|
+
|
|
47104
|
+
try {
|
|
47105
|
+
const chunks = []
|
|
47106
|
+
for await (const chunk of process.stdin) chunks.push(chunk)
|
|
47107
|
+
const input = JSON.parse(Buffer.concat(chunks).toString())
|
|
47108
|
+
|
|
47109
|
+
const filePath = input.tool_input?.file_path
|
|
47110
|
+
if (!filePath) process.exit(0)
|
|
47111
|
+
|
|
47112
|
+
const ext = extname(filePath).toLowerCase()
|
|
47113
|
+
const absPath = normalize(resolve(filePath))
|
|
47114
|
+
if (!existsSync(absPath)) process.exit(0)
|
|
47115
|
+
|
|
47116
|
+
const results = []
|
|
47117
|
+
|
|
47118
|
+
// 1. Prettier
|
|
47119
|
+
if (PRETTIER_EXTENSIONS.has(ext)) {
|
|
47120
|
+
try {
|
|
47121
|
+
execSync('pnpm exec prettier --write "' + absPath + '"', {
|
|
47122
|
+
cwd: ROOT,
|
|
47123
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
47124
|
+
timeout: 10_000
|
|
47125
|
+
})
|
|
47126
|
+
} catch (err) {
|
|
47127
|
+
const stderr = err.stderr?.toString().trim() || ''
|
|
47128
|
+
if (stderr && !/ignored/i.test(stderr)) {
|
|
47129
|
+
results.push('Prettier error: ' + stderr.slice(0, 300))
|
|
47130
|
+
}
|
|
47131
|
+
}
|
|
47132
|
+
}
|
|
47133
|
+
|
|
47134
|
+
// 2. Type-check for .ts/.tsx
|
|
47135
|
+
if (TS_EXTENSIONS.has(ext)) {
|
|
47136
|
+
const tsconfig = findNearestTsconfig(dirname(absPath))
|
|
47137
|
+
if (tsconfig) {
|
|
47138
|
+
try {
|
|
47139
|
+
execSync('pnpm exec tsc --noEmit -p "' + tsconfig + '"', {
|
|
47140
|
+
cwd: ROOT,
|
|
47141
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
47142
|
+
timeout: 30_000,
|
|
47143
|
+
env: { ...process.env, NODE_OPTIONS: '--max-old-space-size=4096' }
|
|
47144
|
+
})
|
|
47145
|
+
} catch (err) {
|
|
47146
|
+
if (err.killed) process.exit(0) // Don't block on timeout
|
|
47147
|
+
const stdout = err.stdout?.toString() || ''
|
|
47148
|
+
if (stdout.includes('error TS')) {
|
|
47149
|
+
const errorLines = stdout
|
|
47150
|
+
.split('\\n')
|
|
47151
|
+
.filter(l => l.includes('error TS'))
|
|
47152
|
+
.slice(0, 10)
|
|
47153
|
+
results.push('Type errors after editing ' + filePath + ':\\n' + errorLines.join('\\n'))
|
|
47154
|
+
}
|
|
47155
|
+
}
|
|
47156
|
+
}
|
|
47157
|
+
}
|
|
47158
|
+
|
|
47159
|
+
// Output errors to Claude's context (silence = success)
|
|
47160
|
+
if (results.length > 0) {
|
|
47161
|
+
process.stderr.write(results.join('\\n\\n'))
|
|
47162
|
+
process.exit(2) // Exit 2 = send stderr as feedback to Claude
|
|
47163
|
+
}
|
|
47164
|
+
} catch {}
|
|
47165
|
+
|
|
47166
|
+
process.exit(0)
|
|
47167
|
+
`;
|
|
47168
|
+
}
|
|
47169
|
+
function claudeToolFailureRecoveryHookTemplate() {
|
|
47170
|
+
return `#!/usr/bin/env node
|
|
47171
|
+
// tool-failure-recovery.mjs
|
|
47172
|
+
// PostToolUseFailure hook \u2014 pattern-matches known Bash errors and returns
|
|
47173
|
+
// recovery advice via stderr + exit 2 (feedback to Claude).
|
|
47174
|
+
|
|
47175
|
+
const RECOVERY_TABLE = [
|
|
47176
|
+
{
|
|
47177
|
+
test: r => /JavaScript heap out of memory/i.test(r),
|
|
47178
|
+
advice: 'Out of memory.',
|
|
47179
|
+
fix: 'Run the command with NODE_OPTIONS="--max-old-space-size=4096".',
|
|
47180
|
+
why: 'Large TypeScript projects can exceed Node default heap limit.',
|
|
47181
|
+
},
|
|
47182
|
+
{
|
|
47183
|
+
test: r => /boundary hook/i.test(r) && /block|denied/i.test(r),
|
|
47184
|
+
advice: 'Command blocked by SDK boundary hook.',
|
|
47185
|
+
fix: 'Ask the user to run this command manually.',
|
|
47186
|
+
why: 'The boundary hook blocks file modifications (Write, Edit, destructive Bash) outside the project boundary.',
|
|
47187
|
+
see: 'CLAUDE.md',
|
|
47188
|
+
},
|
|
47189
|
+
{
|
|
47190
|
+
test: r => /ENOENT/.test(r) && /node_modules/.test(r),
|
|
47191
|
+
advice: 'Missing node_modules dependency.',
|
|
47192
|
+
fix: 'Run: pnpm install',
|
|
47193
|
+
why: 'Dependencies are not installed or were cleared.',
|
|
47194
|
+
},
|
|
47195
|
+
{
|
|
47196
|
+
test: r => /ERR_MODULE_NOT_FOUND/.test(r) && /@elevasis\\/sdk/.test(r),
|
|
47197
|
+
advice: '@elevasis/sdk module not found.',
|
|
47198
|
+
fix: 'Run: pnpm install \u2014 then verify @elevasis/sdk is in package.json dependencies.',
|
|
47199
|
+
why: 'The SDK package is not installed or the version is mismatched.',
|
|
47200
|
+
},
|
|
47201
|
+
{
|
|
47202
|
+
test: r => /ERR_MODULE_NOT_FOUND/.test(r),
|
|
47203
|
+
advice: 'Module not found at import path.',
|
|
47204
|
+
fix: 'Check the import path and verify the package is installed (pnpm install).',
|
|
47205
|
+
why: 'The import path does not match any installed package or local file.',
|
|
47206
|
+
},
|
|
47207
|
+
{
|
|
47208
|
+
test: r => /TS2307/.test(r) || (/cannot find/i.test(r) && /declaration/i.test(r)),
|
|
47209
|
+
advice: 'TypeScript cannot resolve module or declaration file.',
|
|
47210
|
+
fix: 'Check that the package is installed and tsconfig paths are correct.',
|
|
47211
|
+
why: 'Missing dependency or incorrect TypeScript configuration.',
|
|
47212
|
+
},
|
|
47213
|
+
{
|
|
47214
|
+
test: r => /EPERM/.test(r) || /permission denied/i.test(r),
|
|
47215
|
+
advice: 'Permission denied (EPERM).',
|
|
47216
|
+
fix: 'Close the file in any other process (editor, terminal, or dev server) and retry.',
|
|
47217
|
+
why: 'On Windows, files locked by another process cannot be written to.',
|
|
47218
|
+
},
|
|
47219
|
+
{
|
|
47220
|
+
test: r => /lockfile/i.test(r) && /conflict|outdated|ERR_PNPM/i.test(r),
|
|
47221
|
+
advice: 'pnpm lockfile conflict or outdated.',
|
|
47222
|
+
fix: 'Run: pnpm install to regenerate the lockfile.',
|
|
47223
|
+
why: 'The lockfile is out of sync with package.json changes.',
|
|
47224
|
+
},
|
|
47225
|
+
{
|
|
47226
|
+
test: r => /elevasis-sdk check/.test(r) || /elevasis-sdk deploy/.test(r),
|
|
47227
|
+
advice: 'elevasis-sdk CLI command failed.',
|
|
47228
|
+
fix: 'Check the error output above. Common causes: missing .env ELEVASIS_API_KEY, invalid resource schemas, or network issues.',
|
|
47229
|
+
why: 'The SDK CLI validates resources and communicates with the platform API.',
|
|
47230
|
+
},
|
|
47231
|
+
{
|
|
47232
|
+
test: r => /EADDRINUSE/.test(r),
|
|
47233
|
+
advice: 'Port already in use.',
|
|
47234
|
+
fix: 'Find and kill the process using the port, or use a different port.',
|
|
47235
|
+
why: 'A previous dev server or process is still holding the port.',
|
|
47236
|
+
},
|
|
47237
|
+
]
|
|
47238
|
+
|
|
47239
|
+
function formatRecovery(entry) {
|
|
47240
|
+
let msg = 'FAILED: ' + entry.advice + '\\nFIX: ' + entry.fix
|
|
47241
|
+
if (entry.why) msg += '\\nWHY: ' + entry.why
|
|
47242
|
+
if (entry.see) msg += '\\nSEE: ' + entry.see
|
|
47243
|
+
return msg
|
|
47244
|
+
}
|
|
47245
|
+
|
|
47246
|
+
try {
|
|
47247
|
+
const chunks = []
|
|
47248
|
+
for await (const chunk of process.stdin) chunks.push(chunk)
|
|
47249
|
+
const input = JSON.parse(Buffer.concat(chunks).toString())
|
|
47250
|
+
|
|
47251
|
+
const response = input.tool_response ?? ''
|
|
47252
|
+
|
|
47253
|
+
for (const entry of RECOVERY_TABLE) {
|
|
47254
|
+
if (entry.test(response)) {
|
|
47255
|
+
process.stderr.write(formatRecovery(entry))
|
|
47256
|
+
process.exit(2)
|
|
47257
|
+
}
|
|
47258
|
+
}
|
|
47259
|
+
} catch {}
|
|
47260
|
+
|
|
47261
|
+
process.exit(0)
|
|
47262
|
+
`;
|
|
47263
|
+
}
|
|
46972
47264
|
|
|
46973
47265
|
// src/cli/commands/templates/core/resources.ts
|
|
46974
47266
|
function starterTemplate() {
|
|
@@ -47144,6 +47436,8 @@ function getManagedTemplates(ctx = {}) {
|
|
|
47144
47436
|
".claude/settings.json": claudeSettingsTemplate,
|
|
47145
47437
|
".claude/scripts/statusline-command.js": claudeStatuslineScriptTemplate,
|
|
47146
47438
|
".claude/hooks/enforce-sdk-boundary.mjs": claudeSdkBoundaryHookTemplate,
|
|
47439
|
+
".claude/hooks/post-edit-validate.mjs": claudePostEditValidateHookTemplate,
|
|
47440
|
+
".claude/hooks/tool-failure-recovery.mjs": claudeToolFailureRecoveryHookTemplate,
|
|
47147
47441
|
".claude/commands/tutorial.md": claudeTutorialCommandTemplate,
|
|
47148
47442
|
".claude/commands/meta.md": claudeMetaCommandTemplate,
|
|
47149
47443
|
".claude/commands/work.md": claudeWorkCommandTemplate,
|
|
@@ -47356,6 +47650,8 @@ var MANAGED_FILES = [
|
|
|
47356
47650
|
"CLAUDE.md",
|
|
47357
47651
|
".claude/settings.json",
|
|
47358
47652
|
".claude/hooks/enforce-sdk-boundary.mjs",
|
|
47653
|
+
".claude/hooks/post-edit-validate.mjs",
|
|
47654
|
+
".claude/hooks/tool-failure-recovery.mjs",
|
|
47359
47655
|
".claude/commands/tutorial.md",
|
|
47360
47656
|
".claude/commands/meta.md",
|
|
47361
47657
|
".claude/commands/work.md",
|
|
@@ -47370,96 +47666,100 @@ var MANAGED_FILES = [
|
|
|
47370
47666
|
];
|
|
47371
47667
|
var SCAFFOLD_FILES = [...INIT_ONLY_FILES, ...MANAGED_FILES];
|
|
47372
47668
|
function registerInitCommand(program3) {
|
|
47373
|
-
program3.command("init [directory]").description("Scaffold a new Elevasis workspace\n Example: elevasis-sdk init my-workspace").option("--force", "Overwrite existing files").option("--ui", "Include a Vite + React UI app in ui/").action(
|
|
47374
|
-
|
|
47375
|
-
|
|
47376
|
-
|
|
47377
|
-
|
|
47378
|
-
|
|
47379
|
-
|
|
47380
|
-
|
|
47381
|
-
|
|
47382
|
-
|
|
47383
|
-
|
|
47384
|
-
|
|
47385
|
-
|
|
47386
|
-
if (conflicts.length > 0) {
|
|
47387
|
-
console.error(source_default.red("Files already exist:"));
|
|
47388
|
-
for (const f of conflicts) {
|
|
47389
|
-
console.error(source_default.gray(` ${f}`));
|
|
47669
|
+
program3.command("init [directory]").description("Scaffold a new Elevasis workspace\n Example: elevasis-sdk init my-workspace").option("--force", "Overwrite existing files").option("--ui", "Include a Vite + React UI app in ui/").action(
|
|
47670
|
+
wrapAction("init", async (directory, options2) => {
|
|
47671
|
+
const targetDir = directory ? (0, import_path3.resolve)(directory) : process.cwd();
|
|
47672
|
+
const orgSlug = toSlug((0, import_path3.basename)(targetDir));
|
|
47673
|
+
if (!options2.force) {
|
|
47674
|
+
const filesToCheck = options2.ui ? [...SCAFFOLD_FILES, ...UI_INIT_FILES] : SCAFFOLD_FILES;
|
|
47675
|
+
const conflicts = [];
|
|
47676
|
+
for (const file2 of filesToCheck) {
|
|
47677
|
+
try {
|
|
47678
|
+
await (0, import_promises2.access)((0, import_path3.resolve)(targetDir, file2));
|
|
47679
|
+
conflicts.push(file2);
|
|
47680
|
+
} catch {
|
|
47681
|
+
}
|
|
47390
47682
|
}
|
|
47391
|
-
|
|
47392
|
-
|
|
47393
|
-
|
|
47394
|
-
|
|
47395
|
-
|
|
47396
|
-
|
|
47397
|
-
|
|
47398
|
-
|
|
47399
|
-
|
|
47400
|
-
|
|
47401
|
-
|
|
47402
|
-
|
|
47403
|
-
|
|
47404
|
-
|
|
47405
|
-
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "
|
|
47406
|
-
|
|
47407
|
-
|
|
47408
|
-
|
|
47409
|
-
|
|
47410
|
-
|
|
47411
|
-
|
|
47412
|
-
|
|
47413
|
-
|
|
47414
|
-
|
|
47415
|
-
|
|
47416
|
-
|
|
47417
|
-
|
|
47418
|
-
|
|
47419
|
-
|
|
47420
|
-
|
|
47421
|
-
|
|
47422
|
-
|
|
47423
|
-
|
|
47424
|
-
|
|
47425
|
-
|
|
47426
|
-
|
|
47427
|
-
|
|
47428
|
-
|
|
47429
|
-
|
|
47430
|
-
|
|
47431
|
-
|
|
47432
|
-
|
|
47433
|
-
|
|
47434
|
-
|
|
47435
|
-
|
|
47436
|
-
|
|
47437
|
-
|
|
47438
|
-
|
|
47439
|
-
|
|
47440
|
-
|
|
47441
|
-
|
|
47442
|
-
|
|
47443
|
-
|
|
47444
|
-
|
|
47445
|
-
|
|
47446
|
-
|
|
47447
|
-
|
|
47448
|
-
|
|
47449
|
-
|
|
47450
|
-
|
|
47451
|
-
|
|
47452
|
-
|
|
47453
|
-
console.log(source_default.gray(" elevasis-sdk check"));
|
|
47454
|
-
console.log(source_default.gray(" elevasis-sdk deploy"));
|
|
47455
|
-
if (options2.ui) {
|
|
47683
|
+
if (conflicts.length > 0) {
|
|
47684
|
+
console.error(source_default.red("Files already exist:"));
|
|
47685
|
+
for (const f of conflicts) {
|
|
47686
|
+
console.error(source_default.gray(` ${f}`));
|
|
47687
|
+
}
|
|
47688
|
+
console.error(source_default.gray("\n Use --force to overwrite."));
|
|
47689
|
+
throw new Error("Scaffold conflict");
|
|
47690
|
+
}
|
|
47691
|
+
}
|
|
47692
|
+
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src/operations"), { recursive: true });
|
|
47693
|
+
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src/example"), { recursive: true });
|
|
47694
|
+
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src/shared"), { recursive: true });
|
|
47695
|
+
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "docs/in-progress"), { recursive: true });
|
|
47696
|
+
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/hooks"), { recursive: true });
|
|
47697
|
+
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/scripts"), { recursive: true });
|
|
47698
|
+
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/commands"), { recursive: true });
|
|
47699
|
+
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/skills/creds"), { recursive: true });
|
|
47700
|
+
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, ".claude/rules"), { recursive: true });
|
|
47701
|
+
if (options2.ui) {
|
|
47702
|
+
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "ui/src"), { recursive: true });
|
|
47703
|
+
}
|
|
47704
|
+
const files = {
|
|
47705
|
+
"elevasis.config.ts": configTemplate(),
|
|
47706
|
+
"package.json": packageJsonTemplate(orgSlug),
|
|
47707
|
+
"pnpm-workspace.yaml": pnpmWorkspaceTemplate(),
|
|
47708
|
+
"tsconfig.json": tsconfigTemplate(),
|
|
47709
|
+
".env": envTemplate(),
|
|
47710
|
+
".npmrc": npmrcTemplate(),
|
|
47711
|
+
".gitignore": gitignoreTemplate({ hasUI: options2.ui }),
|
|
47712
|
+
"src/index.ts": starterTemplate(),
|
|
47713
|
+
"src/operations/platform-status.ts": platformStatusTemplate(),
|
|
47714
|
+
"src/operations/index.ts": operationsBarrelTemplate(),
|
|
47715
|
+
"src/example/echo.ts": starterWorkflowTemplate(),
|
|
47716
|
+
"src/example/index.ts": exampleBarrelTemplate(),
|
|
47717
|
+
"src/shared/.gitkeep": "",
|
|
47718
|
+
"docs/index.mdx": docsIndexTemplate(orgSlug),
|
|
47719
|
+
"docs/in-progress/.gitkeep": "",
|
|
47720
|
+
"CLAUDE.md": claudeMdTemplate({ hasUI: options2.ui }),
|
|
47721
|
+
".claude/settings.json": claudeSettingsTemplate(),
|
|
47722
|
+
".claude/hooks/enforce-sdk-boundary.mjs": claudeSdkBoundaryHookTemplate(),
|
|
47723
|
+
".claude/hooks/post-edit-validate.mjs": claudePostEditValidateHookTemplate(),
|
|
47724
|
+
".claude/hooks/tool-failure-recovery.mjs": claudeToolFailureRecoveryHookTemplate(),
|
|
47725
|
+
".claude/commands/tutorial.md": claudeTutorialCommandTemplate(),
|
|
47726
|
+
".claude/commands/meta.md": claudeMetaCommandTemplate(),
|
|
47727
|
+
".claude/commands/work.md": claudeWorkCommandTemplate(),
|
|
47728
|
+
".claude/commands/docs.md": claudeDocsCommandTemplate(),
|
|
47729
|
+
".claude/skills/creds/SKILL.md": claudeCredsSkillTemplate(),
|
|
47730
|
+
".claude/rules/sdk-patterns.md": claudeSdkPatternsRuleTemplate(),
|
|
47731
|
+
".claude/rules/workspace-patterns.md": claudeWorkspaceRulesTemplate(),
|
|
47732
|
+
".claude/rules/docs-authoring.md": claudeDocsAuthoringRuleTemplate(),
|
|
47733
|
+
".claude/rules/memory-conventions.md": claudeMemoryConventionsRuleTemplate(),
|
|
47734
|
+
".claude/rules/project-map.md": claudeProjectMapRuleTemplate(),
|
|
47735
|
+
".claude/rules/task-tracking.md": claudeTaskTrackingRuleTemplate(),
|
|
47736
|
+
".claude/scripts/statusline-command.js": claudeStatuslineScriptTemplate()
|
|
47737
|
+
};
|
|
47738
|
+
if (options2.ui) {
|
|
47739
|
+
Object.assign(files, getUIFiles(orgSlug));
|
|
47740
|
+
}
|
|
47741
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
47742
|
+
await (0, import_promises2.writeFile)((0, import_path3.resolve)(targetDir, filePath), content, "utf-8");
|
|
47743
|
+
}
|
|
47744
|
+
console.log(source_default.green.bold(" Workspace created!"));
|
|
47456
47745
|
console.log("");
|
|
47457
|
-
console.log(source_default.gray("
|
|
47458
|
-
|
|
47459
|
-
|
|
47460
|
-
|
|
47461
|
-
|
|
47462
|
-
|
|
47746
|
+
console.log(source_default.gray(" Next steps:"));
|
|
47747
|
+
if (directory) {
|
|
47748
|
+
console.log(source_default.gray(` cd ${directory}`));
|
|
47749
|
+
}
|
|
47750
|
+
console.log(source_default.gray(" pnpm install"));
|
|
47751
|
+
console.log(source_default.gray(" # Add your API key to .env"));
|
|
47752
|
+
console.log(source_default.gray(" elevasis-sdk check"));
|
|
47753
|
+
console.log(source_default.gray(" elevasis-sdk deploy"));
|
|
47754
|
+
if (options2.ui) {
|
|
47755
|
+
console.log("");
|
|
47756
|
+
console.log(source_default.gray(" UI app:"));
|
|
47757
|
+
console.log(source_default.gray(" cd ui && pnpm install"));
|
|
47758
|
+
console.log(source_default.gray(" # Set VITE_WORKOS_CLIENT_ID in ui/.env"));
|
|
47759
|
+
console.log(source_default.gray(" pnpm dev"));
|
|
47760
|
+
}
|
|
47761
|
+
})
|
|
47762
|
+
);
|
|
47463
47763
|
}
|
|
47464
47764
|
function toSlug(name) {
|
|
47465
47765
|
const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^[^a-z]+/, "").replace(/-+/g, "-").replace(/-$/, "");
|