@elevasis/sdk 1.5.2 → 1.5.4
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 +899 -57
- package/dist/index.d.ts +94 -110
- package/package.json +3 -3
- package/reference/_navigation.md +11 -1
- package/reference/_reference-manifest.json +70 -0
- package/reference/claude-config/commands/submit-issue.md +12 -0
- package/reference/claude-config/hooks/post-edit-validate.mjs +109 -0
- package/reference/claude-config/hooks/tool-failure-recovery.mjs +73 -0
- package/reference/claude-config/rules/deployment.md +57 -0
- package/reference/claude-config/rules/docs.md +26 -0
- package/reference/claude-config/rules/error-handling.md +56 -0
- package/reference/claude-config/rules/execution.md +40 -0
- package/reference/claude-config/rules/frontend.md +43 -0
- package/reference/claude-config/rules/observability.md +31 -0
- package/reference/claude-config/rules/organization-os.md +62 -0
- package/reference/claude-config/rules/platform.md +41 -0
- package/reference/claude-config/rules/shared-types.md +46 -0
- package/reference/claude-config/rules/task-tracking.md +47 -0
- package/reference/claude-config/scripts/statusline-command.js +18 -0
- package/reference/claude-config/settings.json +30 -0
- package/reference/claude-config/skills/deploy/SKILL.md +166 -0
- package/reference/claude-config/skills/dsp/SKILL.md +66 -0
- package/reference/claude-config/skills/elevasis/SKILL.md +239 -0
- package/reference/claude-config/skills/explore/SKILL.md +78 -0
- package/reference/claude-config/skills/project/SKILL.md +918 -0
- package/reference/claude-config/skills/save/SKILL.md +197 -0
- package/reference/claude-config/skills/setup/SKILL.md +210 -0
- package/reference/claude-config/skills/status/SKILL.md +60 -0
- package/reference/claude-config/skills/submit-issue/SKILL.md +179 -0
- package/reference/claude-config/skills/sync/SKILL.md +81 -0
- package/reference/cli.mdx +35 -20
- package/reference/deployment/index.mdx +6 -5
- package/reference/deployment/provided-features.mdx +62 -40
- package/reference/deployment/ui-execution.mdx +1 -2
- package/reference/framework/agent.mdx +50 -50
- package/reference/framework/index.mdx +13 -10
- package/reference/framework/project-structure.mdx +76 -70
- package/reference/packages/core/src/README.md +24 -17
- package/reference/packages/core/src/business/README.md +52 -0
- package/reference/packages/core/src/organization-model/README.md +25 -26
- package/reference/packages/ui/src/app/README.md +24 -0
- package/reference/packages/ui/src/provider/README.md +8 -7
- package/reference/platform-tools/type-safety.mdx +0 -10
- package/reference/roadmap.mdx +6 -4
- package/reference/scaffold/core/organization-graph.mdx +37 -28
- package/reference/scaffold/core/organization-model.mdx +34 -36
- package/reference/scaffold/index.mdx +14 -9
- package/reference/scaffold/operations/propagation-pipeline.md +133 -0
- package/reference/scaffold/operations/scaffold-maintenance.md +125 -0
- package/reference/scaffold/operations/workflow-recipes.md +18 -1
- package/reference/scaffold/recipes/add-a-feature.md +37 -21
- package/reference/scaffold/recipes/add-a-resource.md +16 -12
- package/reference/scaffold/recipes/customize-organization-model.md +400 -0
- package/reference/scaffold/recipes/extend-a-base-entity.md +140 -0
- package/reference/scaffold/recipes/gate-by-feature-or-admin.md +18 -12
- package/reference/scaffold/recipes/index.md +3 -3
- package/reference/scaffold/reference/contracts.md +11 -32
- package/reference/scaffold/reference/feature-registry.md +10 -9
- package/reference/scaffold/reference/glossary.md +14 -18
- package/reference/scaffold/ui/customization.md +2 -2
- package/reference/scaffold/ui/feature-flags-and-gating.md +40 -54
- package/reference/scaffold/ui/feature-shell.mdx +23 -24
- package/reference/scaffold/ui/recipes.md +118 -3
package/dist/cli.cjs
CHANGED
|
@@ -288,8 +288,8 @@ var require_main = __commonJS({
|
|
|
288
288
|
const shortPaths = [];
|
|
289
289
|
for (const filePath of optionPaths) {
|
|
290
290
|
try {
|
|
291
|
-
const
|
|
292
|
-
shortPaths.push(
|
|
291
|
+
const relative2 = path.relative(process.cwd(), filePath);
|
|
292
|
+
shortPaths.push(relative2);
|
|
293
293
|
} catch (e) {
|
|
294
294
|
if (debug) {
|
|
295
295
|
_debug(`Failed to load ${filePath} ${e.message}`);
|
|
@@ -5245,7 +5245,7 @@ var require_buffer_list = __commonJS({
|
|
|
5245
5245
|
}
|
|
5246
5246
|
}, {
|
|
5247
5247
|
key: "join",
|
|
5248
|
-
value: function
|
|
5248
|
+
value: function join4(s) {
|
|
5249
5249
|
if (this.length === 0) return "";
|
|
5250
5250
|
var p = this.head;
|
|
5251
5251
|
var ret = "" + p.data;
|
|
@@ -6622,14 +6622,14 @@ var require_async_iterator = __commonJS({
|
|
|
6622
6622
|
};
|
|
6623
6623
|
}
|
|
6624
6624
|
function readAndResolve(iter) {
|
|
6625
|
-
var
|
|
6626
|
-
if (
|
|
6625
|
+
var resolve8 = iter[kLastResolve];
|
|
6626
|
+
if (resolve8 !== null) {
|
|
6627
6627
|
var data = iter[kStream].read();
|
|
6628
6628
|
if (data !== null) {
|
|
6629
6629
|
iter[kLastPromise] = null;
|
|
6630
6630
|
iter[kLastResolve] = null;
|
|
6631
6631
|
iter[kLastReject] = null;
|
|
6632
|
-
|
|
6632
|
+
resolve8(createIterResult(data, false));
|
|
6633
6633
|
}
|
|
6634
6634
|
}
|
|
6635
6635
|
}
|
|
@@ -6637,13 +6637,13 @@ var require_async_iterator = __commonJS({
|
|
|
6637
6637
|
process.nextTick(readAndResolve, iter);
|
|
6638
6638
|
}
|
|
6639
6639
|
function wrapForNext(lastPromise, iter) {
|
|
6640
|
-
return function(
|
|
6640
|
+
return function(resolve8, reject) {
|
|
6641
6641
|
lastPromise.then(function() {
|
|
6642
6642
|
if (iter[kEnded]) {
|
|
6643
|
-
|
|
6643
|
+
resolve8(createIterResult(void 0, true));
|
|
6644
6644
|
return;
|
|
6645
6645
|
}
|
|
6646
|
-
iter[kHandlePromise](
|
|
6646
|
+
iter[kHandlePromise](resolve8, reject);
|
|
6647
6647
|
}, reject);
|
|
6648
6648
|
};
|
|
6649
6649
|
}
|
|
@@ -6663,12 +6663,12 @@ var require_async_iterator = __commonJS({
|
|
|
6663
6663
|
return Promise.resolve(createIterResult(void 0, true));
|
|
6664
6664
|
}
|
|
6665
6665
|
if (this[kStream].destroyed) {
|
|
6666
|
-
return new Promise(function(
|
|
6666
|
+
return new Promise(function(resolve8, reject) {
|
|
6667
6667
|
process.nextTick(function() {
|
|
6668
6668
|
if (_this[kError]) {
|
|
6669
6669
|
reject(_this[kError]);
|
|
6670
6670
|
} else {
|
|
6671
|
-
|
|
6671
|
+
resolve8(createIterResult(void 0, true));
|
|
6672
6672
|
}
|
|
6673
6673
|
});
|
|
6674
6674
|
});
|
|
@@ -6691,13 +6691,13 @@ var require_async_iterator = __commonJS({
|
|
|
6691
6691
|
return this;
|
|
6692
6692
|
}), _defineProperty(_Object$setPrototypeO, "return", function _return() {
|
|
6693
6693
|
var _this2 = this;
|
|
6694
|
-
return new Promise(function(
|
|
6694
|
+
return new Promise(function(resolve8, reject) {
|
|
6695
6695
|
_this2[kStream].destroy(null, function(err) {
|
|
6696
6696
|
if (err) {
|
|
6697
6697
|
reject(err);
|
|
6698
6698
|
return;
|
|
6699
6699
|
}
|
|
6700
|
-
|
|
6700
|
+
resolve8(createIterResult(void 0, true));
|
|
6701
6701
|
});
|
|
6702
6702
|
});
|
|
6703
6703
|
}), _Object$setPrototypeO), AsyncIteratorPrototype);
|
|
@@ -6719,15 +6719,15 @@ var require_async_iterator = __commonJS({
|
|
|
6719
6719
|
value: stream._readableState.endEmitted,
|
|
6720
6720
|
writable: true
|
|
6721
6721
|
}), _defineProperty(_Object$create, kHandlePromise, {
|
|
6722
|
-
value: function value(
|
|
6722
|
+
value: function value(resolve8, reject) {
|
|
6723
6723
|
var data = iterator[kStream].read();
|
|
6724
6724
|
if (data) {
|
|
6725
6725
|
iterator[kLastPromise] = null;
|
|
6726
6726
|
iterator[kLastResolve] = null;
|
|
6727
6727
|
iterator[kLastReject] = null;
|
|
6728
|
-
|
|
6728
|
+
resolve8(createIterResult(data, false));
|
|
6729
6729
|
} else {
|
|
6730
|
-
iterator[kLastResolve] =
|
|
6730
|
+
iterator[kLastResolve] = resolve8;
|
|
6731
6731
|
iterator[kLastReject] = reject;
|
|
6732
6732
|
}
|
|
6733
6733
|
},
|
|
@@ -6746,12 +6746,12 @@ var require_async_iterator = __commonJS({
|
|
|
6746
6746
|
iterator[kError] = err;
|
|
6747
6747
|
return;
|
|
6748
6748
|
}
|
|
6749
|
-
var
|
|
6750
|
-
if (
|
|
6749
|
+
var resolve8 = iterator[kLastResolve];
|
|
6750
|
+
if (resolve8 !== null) {
|
|
6751
6751
|
iterator[kLastPromise] = null;
|
|
6752
6752
|
iterator[kLastResolve] = null;
|
|
6753
6753
|
iterator[kLastReject] = null;
|
|
6754
|
-
|
|
6754
|
+
resolve8(createIterResult(void 0, true));
|
|
6755
6755
|
}
|
|
6756
6756
|
iterator[kEnded] = true;
|
|
6757
6757
|
});
|
|
@@ -6766,7 +6766,7 @@ var require_async_iterator = __commonJS({
|
|
|
6766
6766
|
var require_from = __commonJS({
|
|
6767
6767
|
"../../node_modules/.pnpm/readable-stream@3.6.2/node_modules/readable-stream/lib/internal/streams/from.js"(exports2, module2) {
|
|
6768
6768
|
"use strict";
|
|
6769
|
-
function asyncGeneratorStep(gen,
|
|
6769
|
+
function asyncGeneratorStep(gen, resolve8, reject, _next, _throw, key, arg) {
|
|
6770
6770
|
try {
|
|
6771
6771
|
var info = gen[key](arg);
|
|
6772
6772
|
var value = info.value;
|
|
@@ -6775,7 +6775,7 @@ var require_from = __commonJS({
|
|
|
6775
6775
|
return;
|
|
6776
6776
|
}
|
|
6777
6777
|
if (info.done) {
|
|
6778
|
-
|
|
6778
|
+
resolve8(value);
|
|
6779
6779
|
} else {
|
|
6780
6780
|
Promise.resolve(value).then(_next, _throw);
|
|
6781
6781
|
}
|
|
@@ -6783,13 +6783,13 @@ var require_from = __commonJS({
|
|
|
6783
6783
|
function _asyncToGenerator(fn) {
|
|
6784
6784
|
return function() {
|
|
6785
6785
|
var self2 = this, args = arguments;
|
|
6786
|
-
return new Promise(function(
|
|
6786
|
+
return new Promise(function(resolve8, reject) {
|
|
6787
6787
|
var gen = fn.apply(self2, args);
|
|
6788
6788
|
function _next(value) {
|
|
6789
|
-
asyncGeneratorStep(gen,
|
|
6789
|
+
asyncGeneratorStep(gen, resolve8, reject, _next, _throw, "next", value);
|
|
6790
6790
|
}
|
|
6791
6791
|
function _throw(err) {
|
|
6792
|
-
asyncGeneratorStep(gen,
|
|
6792
|
+
asyncGeneratorStep(gen, resolve8, reject, _next, _throw, "throw", err);
|
|
6793
6793
|
}
|
|
6794
6794
|
_next(void 0);
|
|
6795
6795
|
});
|
|
@@ -43990,7 +43990,7 @@ function wrapAction(commandName, fn) {
|
|
|
43990
43990
|
// package.json
|
|
43991
43991
|
var package_default = {
|
|
43992
43992
|
name: "@elevasis/sdk",
|
|
43993
|
-
version: "1.5.
|
|
43993
|
+
version: "1.5.4",
|
|
43994
43994
|
description: "SDK for building Elevasis organization resources",
|
|
43995
43995
|
type: "module",
|
|
43996
43996
|
bin: {
|
|
@@ -44508,7 +44508,7 @@ async function pollForCompletion(resourceId, executionId, apiUrl) {
|
|
|
44508
44508
|
};
|
|
44509
44509
|
process.on("SIGINT", cleanup);
|
|
44510
44510
|
while (true) {
|
|
44511
|
-
await new Promise((
|
|
44511
|
+
await new Promise((resolve8) => setTimeout(resolve8, POLL_INTERVAL_MS));
|
|
44512
44512
|
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
44513
44513
|
pollSpinner.text = `Waiting for completion... (${elapsed}s)`;
|
|
44514
44514
|
try {
|
|
@@ -45096,10 +45096,10 @@ Credential renamed successfully!`));
|
|
|
45096
45096
|
var import_readline = require("readline");
|
|
45097
45097
|
function confirm(message) {
|
|
45098
45098
|
const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
45099
|
-
return new Promise((
|
|
45099
|
+
return new Promise((resolve8) => {
|
|
45100
45100
|
rl.question(message, (answer) => {
|
|
45101
45101
|
rl.close();
|
|
45102
|
-
|
|
45102
|
+
resolve8(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
45103
45103
|
});
|
|
45104
45104
|
});
|
|
45105
45105
|
}
|
|
@@ -45230,38 +45230,320 @@ function registerRenameCommand(program3) {
|
|
|
45230
45230
|
);
|
|
45231
45231
|
}
|
|
45232
45232
|
|
|
45233
|
+
// src/cli/commands/project/resolve-project.ts
|
|
45234
|
+
function isUuid(value) {
|
|
45235
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
45236
|
+
}
|
|
45237
|
+
function normalize(value) {
|
|
45238
|
+
return value.trim().toLowerCase();
|
|
45239
|
+
}
|
|
45240
|
+
function formatCandidates(projects) {
|
|
45241
|
+
return projects.slice(0, 5).map((project) => `${project.name} (${project.id})`).join(", ");
|
|
45242
|
+
}
|
|
45243
|
+
async function resolveProject(query, apiUrlOverride) {
|
|
45244
|
+
const trimmedQuery = query.trim();
|
|
45245
|
+
if (!trimmedQuery) {
|
|
45246
|
+
throw new Error("Project query is required");
|
|
45247
|
+
}
|
|
45248
|
+
const apiUrl = resolveApiUrl(apiUrlOverride);
|
|
45249
|
+
if (isUuid(trimmedQuery)) {
|
|
45250
|
+
try {
|
|
45251
|
+
const result2 = await apiGet(`/api/external/projects/${trimmedQuery}`, apiUrl);
|
|
45252
|
+
return result2.project;
|
|
45253
|
+
} catch (error46) {
|
|
45254
|
+
if (!(error46 instanceof Error) || !error46.message.includes("(404)")) {
|
|
45255
|
+
throw error46;
|
|
45256
|
+
}
|
|
45257
|
+
}
|
|
45258
|
+
}
|
|
45259
|
+
const params = new URLSearchParams({ search: trimmedQuery });
|
|
45260
|
+
const result = await apiGet(`/api/external/projects?${params.toString()}`, apiUrl);
|
|
45261
|
+
const projects = result.projects;
|
|
45262
|
+
if (projects.length === 0) {
|
|
45263
|
+
throw new Error(`No project matched "${trimmedQuery}"`);
|
|
45264
|
+
}
|
|
45265
|
+
const normalizedQuery = normalize(trimmedQuery);
|
|
45266
|
+
const exactNameMatches = projects.filter((project) => normalize(project.name) === normalizedQuery);
|
|
45267
|
+
if (exactNameMatches.length === 1) {
|
|
45268
|
+
return exactNameMatches[0];
|
|
45269
|
+
}
|
|
45270
|
+
if (exactNameMatches.length > 1) {
|
|
45271
|
+
throw new Error(`Multiple projects matched "${trimmedQuery}": ${formatCandidates(exactNameMatches)}`);
|
|
45272
|
+
}
|
|
45273
|
+
if (projects.length === 1) {
|
|
45274
|
+
return projects[0];
|
|
45275
|
+
}
|
|
45276
|
+
const prefixMatches = projects.filter((project) => normalize(project.name).startsWith(normalizedQuery));
|
|
45277
|
+
if (prefixMatches.length === 1) {
|
|
45278
|
+
return prefixMatches[0];
|
|
45279
|
+
}
|
|
45280
|
+
throw new Error(`Multiple projects matched "${trimmedQuery}": ${formatCandidates(projects)}`);
|
|
45281
|
+
}
|
|
45282
|
+
|
|
45283
|
+
// src/cli/commands/project/work-project.ts
|
|
45284
|
+
var TASK_DONE_STATUSES = /* @__PURE__ */ new Set(["completed", "approved"]);
|
|
45285
|
+
var TASK_ACTIVE_STATUSES = /* @__PURE__ */ new Set(["in_progress", "submitted", "revision_requested"]);
|
|
45286
|
+
var TASK_BLOCKED_STATUSES = /* @__PURE__ */ new Set(["blocked", "rejected"]);
|
|
45287
|
+
var MILESTONE_BLOCKED_STATUSES = /* @__PURE__ */ new Set(["blocked", "overdue"]);
|
|
45288
|
+
function toText(value) {
|
|
45289
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
45290
|
+
}
|
|
45291
|
+
function summarizeText(value, maxLength = 120) {
|
|
45292
|
+
const text = value?.trim() ?? "";
|
|
45293
|
+
if (!text) return "No recent summary recorded.";
|
|
45294
|
+
return text.length > maxLength ? `${text.slice(0, maxLength - 3)}...` : text;
|
|
45295
|
+
}
|
|
45296
|
+
function getTaskSavedState(task) {
|
|
45297
|
+
const resumeContext = task.resume_context;
|
|
45298
|
+
return toText(resumeContext?.current_state);
|
|
45299
|
+
}
|
|
45300
|
+
function compareUpdatedDesc(a, b) {
|
|
45301
|
+
return (b.updated_at ?? "").localeCompare(a.updated_at ?? "");
|
|
45302
|
+
}
|
|
45303
|
+
function deriveLifecycle(project, taskProgress, milestoneProgress) {
|
|
45304
|
+
if (project.status === "completed") return "completed";
|
|
45305
|
+
if (project.status === "blocked" || taskProgress.blocked > 0 || milestoneProgress.blocked > 0) {
|
|
45306
|
+
return "blocked";
|
|
45307
|
+
}
|
|
45308
|
+
const hasStartedWork = taskProgress.completed > 0 || taskProgress.active > 0 || milestoneProgress.completed > 0 || milestoneProgress.inProgress > 0;
|
|
45309
|
+
return hasStartedWork ? "active" : "planning";
|
|
45310
|
+
}
|
|
45311
|
+
function selectResumeTarget(project) {
|
|
45312
|
+
const activeTasks = project.tasks.filter((task) => TASK_ACTIVE_STATUSES.has(task.status ?? "")).sort(compareUpdatedDesc);
|
|
45313
|
+
const savedActiveTask = activeTasks.find((task) => getTaskSavedState(task));
|
|
45314
|
+
if (savedActiveTask) {
|
|
45315
|
+
return {
|
|
45316
|
+
kind: "task",
|
|
45317
|
+
id: savedActiveTask.id,
|
|
45318
|
+
name: savedActiveTask.name,
|
|
45319
|
+
status: savedActiveTask.status,
|
|
45320
|
+
savedState: getTaskSavedState(savedActiveTask)
|
|
45321
|
+
};
|
|
45322
|
+
}
|
|
45323
|
+
if (activeTasks[0]) {
|
|
45324
|
+
return {
|
|
45325
|
+
kind: "task",
|
|
45326
|
+
id: activeTasks[0].id,
|
|
45327
|
+
name: activeTasks[0].name,
|
|
45328
|
+
status: activeTasks[0].status,
|
|
45329
|
+
savedState: null
|
|
45330
|
+
};
|
|
45331
|
+
}
|
|
45332
|
+
const plannedTask = [...project.tasks].filter((task) => task.status === "planned").sort(compareUpdatedDesc)[0];
|
|
45333
|
+
if (plannedTask) {
|
|
45334
|
+
return {
|
|
45335
|
+
kind: "task",
|
|
45336
|
+
id: plannedTask.id,
|
|
45337
|
+
name: plannedTask.name,
|
|
45338
|
+
status: plannedTask.status,
|
|
45339
|
+
savedState: getTaskSavedState(plannedTask)
|
|
45340
|
+
};
|
|
45341
|
+
}
|
|
45342
|
+
const nextMilestone = project.milestones.find((milestone) => milestone.status !== "completed");
|
|
45343
|
+
if (nextMilestone) {
|
|
45344
|
+
return {
|
|
45345
|
+
kind: "milestone",
|
|
45346
|
+
id: nextMilestone.id,
|
|
45347
|
+
name: nextMilestone.name,
|
|
45348
|
+
status: nextMilestone.status,
|
|
45349
|
+
savedState: null
|
|
45350
|
+
};
|
|
45351
|
+
}
|
|
45352
|
+
return null;
|
|
45353
|
+
}
|
|
45354
|
+
function recommendNextStep(lifecycle, project, notes, resumeTarget) {
|
|
45355
|
+
const latestBlockerNote = notes.find((note) => note.type === "blocker" || note.type === "issue");
|
|
45356
|
+
const blockedTask = [...project.tasks].filter((task) => TASK_BLOCKED_STATUSES.has(task.status ?? "")).sort(compareUpdatedDesc)[0];
|
|
45357
|
+
if (lifecycle === "completed") {
|
|
45358
|
+
return "Review the closeout state and decide whether to archive this project or open the next phase.";
|
|
45359
|
+
}
|
|
45360
|
+
if (lifecycle === "blocked") {
|
|
45361
|
+
if (blockedTask) {
|
|
45362
|
+
return `Resolve the blocker on "${blockedTask.name}" before starting new work.`;
|
|
45363
|
+
}
|
|
45364
|
+
if (latestBlockerNote) {
|
|
45365
|
+
return `Address the latest ${latestBlockerNote.type ?? "blocker"} note, then update the project once it is cleared.`;
|
|
45366
|
+
}
|
|
45367
|
+
return "Clear the blocking issue first, then resume the next concrete task.";
|
|
45368
|
+
}
|
|
45369
|
+
if (resumeTarget?.kind === "task") {
|
|
45370
|
+
if (resumeTarget.savedState) {
|
|
45371
|
+
return `Resume "${resumeTarget.name}" from the saved task context.`;
|
|
45372
|
+
}
|
|
45373
|
+
return `Resume "${resumeTarget.name}" and record fresh resume context once the next checkpoint is stable.`;
|
|
45374
|
+
}
|
|
45375
|
+
if (resumeTarget?.kind === "milestone") {
|
|
45376
|
+
return `Start milestone "${resumeTarget.name}" and create the first concrete task under it.`;
|
|
45377
|
+
}
|
|
45378
|
+
const latestNote = notes[0];
|
|
45379
|
+
if (latestNote) {
|
|
45380
|
+
return "Review the latest project note and turn it into the next concrete task or milestone.";
|
|
45381
|
+
}
|
|
45382
|
+
return "Add the next concrete task or milestone before resuming work.";
|
|
45383
|
+
}
|
|
45384
|
+
function buildProjectWorkBrief(project, notes) {
|
|
45385
|
+
const taskProgress = {
|
|
45386
|
+
total: project.tasks.length,
|
|
45387
|
+
completed: project.tasks.filter((task) => TASK_DONE_STATUSES.has(task.status ?? "")).length,
|
|
45388
|
+
active: project.tasks.filter((task) => TASK_ACTIVE_STATUSES.has(task.status ?? "")).length,
|
|
45389
|
+
blocked: project.tasks.filter((task) => TASK_BLOCKED_STATUSES.has(task.status ?? "")).length
|
|
45390
|
+
};
|
|
45391
|
+
const milestoneProgress = {
|
|
45392
|
+
total: project.milestones.length,
|
|
45393
|
+
completed: project.milestones.filter((milestone) => milestone.status === "completed").length,
|
|
45394
|
+
inProgress: project.milestones.filter((milestone) => milestone.status === "in_progress").length,
|
|
45395
|
+
blocked: project.milestones.filter((milestone) => MILESTONE_BLOCKED_STATUSES.has(milestone.status ?? "")).length
|
|
45396
|
+
};
|
|
45397
|
+
const lifecycle = deriveLifecycle(project, taskProgress, milestoneProgress);
|
|
45398
|
+
const resumeTarget = selectResumeTarget(project);
|
|
45399
|
+
return {
|
|
45400
|
+
project: {
|
|
45401
|
+
id: project.id,
|
|
45402
|
+
name: project.name,
|
|
45403
|
+
kind: project.kind,
|
|
45404
|
+
status: project.status,
|
|
45405
|
+
description: project.description
|
|
45406
|
+
},
|
|
45407
|
+
lifecycle,
|
|
45408
|
+
progress: {
|
|
45409
|
+
milestones: milestoneProgress,
|
|
45410
|
+
tasks: taskProgress
|
|
45411
|
+
},
|
|
45412
|
+
recentNotes: notes.slice(0, 3).map((note) => ({
|
|
45413
|
+
id: note.id,
|
|
45414
|
+
type: note.type,
|
|
45415
|
+
occurredAt: note.occurred_at,
|
|
45416
|
+
summary: summarizeText(note.summary ?? note.content)
|
|
45417
|
+
})),
|
|
45418
|
+
resumeTarget,
|
|
45419
|
+
recommendedNextStep: recommendNextStep(lifecycle, project, notes, resumeTarget)
|
|
45420
|
+
};
|
|
45421
|
+
}
|
|
45422
|
+
function renderProjectWorkBrief(brief) {
|
|
45423
|
+
const lines = [
|
|
45424
|
+
`Project work brief: ${brief.project.name}`,
|
|
45425
|
+
` ID: ${brief.project.id}`,
|
|
45426
|
+
` Lifecycle: ${brief.lifecycle}`,
|
|
45427
|
+
` Status: ${brief.project.status ?? "unknown"}`
|
|
45428
|
+
];
|
|
45429
|
+
if (brief.project.kind) {
|
|
45430
|
+
lines.push(` Kind: ${brief.project.kind}`);
|
|
45431
|
+
}
|
|
45432
|
+
if (brief.project.description) {
|
|
45433
|
+
lines.push(` ${brief.project.description}`);
|
|
45434
|
+
}
|
|
45435
|
+
lines.push("");
|
|
45436
|
+
lines.push("Progress");
|
|
45437
|
+
lines.push(
|
|
45438
|
+
` Milestones: ${brief.progress.milestones.completed}/${brief.progress.milestones.total} completed, ${brief.progress.milestones.inProgress} in progress, ${brief.progress.milestones.blocked} blocked`
|
|
45439
|
+
);
|
|
45440
|
+
lines.push(
|
|
45441
|
+
` Tasks: ${brief.progress.tasks.completed}/${brief.progress.tasks.total} completed, ${brief.progress.tasks.active} active, ${brief.progress.tasks.blocked} blocked`
|
|
45442
|
+
);
|
|
45443
|
+
lines.push("");
|
|
45444
|
+
lines.push("Recent notes");
|
|
45445
|
+
if (brief.recentNotes.length === 0) {
|
|
45446
|
+
lines.push(" No recent notes recorded.");
|
|
45447
|
+
} else {
|
|
45448
|
+
for (const note of brief.recentNotes) {
|
|
45449
|
+
const dateLabel = note.occurredAt ? note.occurredAt.slice(0, 10) : "unknown-date";
|
|
45450
|
+
lines.push(` - ${dateLabel} ${note.type ?? "status_update"}: ${note.summary}`);
|
|
45451
|
+
}
|
|
45452
|
+
}
|
|
45453
|
+
lines.push("");
|
|
45454
|
+
lines.push("Resume target");
|
|
45455
|
+
if (!brief.resumeTarget) {
|
|
45456
|
+
lines.push(" No clear resume target yet.");
|
|
45457
|
+
} else {
|
|
45458
|
+
lines.push(
|
|
45459
|
+
` ${brief.resumeTarget.kind}: ${brief.resumeTarget.name} (${brief.resumeTarget.status ?? "unknown"})`
|
|
45460
|
+
);
|
|
45461
|
+
if (brief.resumeTarget.savedState) {
|
|
45462
|
+
lines.push(` Saved state: ${brief.resumeTarget.savedState}`);
|
|
45463
|
+
}
|
|
45464
|
+
}
|
|
45465
|
+
lines.push("");
|
|
45466
|
+
lines.push("Recommended next step");
|
|
45467
|
+
lines.push(` ${brief.recommendedNextStep}`);
|
|
45468
|
+
return lines.join("\n");
|
|
45469
|
+
}
|
|
45470
|
+
|
|
45233
45471
|
// src/cli/commands/project/projects.ts
|
|
45234
45472
|
function registerProjectList(program3) {
|
|
45235
|
-
program3.command("project:list").description("List
|
|
45236
|
-
wrapAction(
|
|
45237
|
-
|
|
45238
|
-
|
|
45239
|
-
|
|
45240
|
-
|
|
45241
|
-
|
|
45242
|
-
|
|
45243
|
-
|
|
45244
|
-
|
|
45245
|
-
const
|
|
45246
|
-
|
|
45247
|
-
|
|
45248
|
-
|
|
45249
|
-
|
|
45250
|
-
|
|
45473
|
+
program3.command("project:list").description("List projects\n Example: elevasis-sdk project:list --search alpha").option("--kind <kind>", "Filter by kind: client_engagement | internal | research | other").option("--status <status>", "Filter by status: active | on_track | at_risk | blocked | completed | paused").option("--search <query>", "Search by project name or description").option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
45474
|
+
wrapAction(
|
|
45475
|
+
"project:list",
|
|
45476
|
+
async (options2) => {
|
|
45477
|
+
const apiUrl = resolveApiUrl(options2.apiUrl);
|
|
45478
|
+
const params = new URLSearchParams();
|
|
45479
|
+
if (options2.kind) params.set("kind", options2.kind);
|
|
45480
|
+
if (options2.status) params.set("status", options2.status);
|
|
45481
|
+
if (options2.search) params.set("search", options2.search);
|
|
45482
|
+
const qs = params.toString();
|
|
45483
|
+
const endpoint = `/api/external/projects${qs ? `?${qs}` : ""}`;
|
|
45484
|
+
const result = await apiGet(endpoint, apiUrl);
|
|
45485
|
+
if (options2.pretty) {
|
|
45486
|
+
const projects = result.projects;
|
|
45487
|
+
if (projects.length === 0) {
|
|
45488
|
+
console.log(source_default.yellow("No projects found."));
|
|
45489
|
+
return;
|
|
45490
|
+
}
|
|
45491
|
+
console.log(source_default.cyan(`
|
|
45251
45492
|
Projects (${projects.length}):
|
|
45252
45493
|
`));
|
|
45253
|
-
|
|
45254
|
-
|
|
45255
|
-
|
|
45256
|
-
|
|
45494
|
+
for (const p of projects) {
|
|
45495
|
+
console.log(` ${source_default.bold(p.name)} ${source_default.dim(p.kind)} ${source_default.gray(p.status)}`);
|
|
45496
|
+
console.log(source_default.gray(` ID: ${p.id}`));
|
|
45497
|
+
if (p.description) console.log(source_default.gray(` ${p.description}`));
|
|
45498
|
+
}
|
|
45499
|
+
console.log();
|
|
45500
|
+
} else {
|
|
45501
|
+
console.log(JSON.stringify(result, null, 2));
|
|
45257
45502
|
}
|
|
45503
|
+
}
|
|
45504
|
+
)
|
|
45505
|
+
);
|
|
45506
|
+
}
|
|
45507
|
+
function registerProjectResolve(program3) {
|
|
45508
|
+
program3.command("project:resolve <query>").description('Resolve a project ID from a name, UUID, or search query\n Example: elevasis-sdk project:resolve "Alpha"').option("--api-url <url>", "API base URL").option("--pretty", "Render project details instead of only the resolved ID").action(
|
|
45509
|
+
wrapAction("project:resolve", async (query, options2) => {
|
|
45510
|
+
const project = await resolveProject(query, options2.apiUrl);
|
|
45511
|
+
if (options2.pretty) {
|
|
45512
|
+
console.log(source_default.cyan(`
|
|
45513
|
+
Resolved project: ${project.name}`));
|
|
45514
|
+
console.log(source_default.gray(` ID: ${project.id}`));
|
|
45515
|
+
if (project.kind) console.log(source_default.gray(` Kind: ${project.kind}`));
|
|
45516
|
+
if (project.status) console.log(source_default.gray(` Status: ${project.status}`));
|
|
45517
|
+
if (project.description) console.log(source_default.gray(` ${project.description}`));
|
|
45258
45518
|
console.log();
|
|
45259
45519
|
} else {
|
|
45260
|
-
console.log(
|
|
45520
|
+
console.log(project.id);
|
|
45261
45521
|
}
|
|
45262
45522
|
})
|
|
45263
45523
|
);
|
|
45264
45524
|
}
|
|
45525
|
+
function registerProjectWork(program3) {
|
|
45526
|
+
program3.command("project:work <query>").alias("project:open").description(
|
|
45527
|
+
'Resolve a project and print a lifecycle-aware work brief\n Example: elevasis-sdk project:work "Alpha"'
|
|
45528
|
+
).option("--api-url <url>", "API base URL").option("--json", "Render the structured work brief as JSON").action(
|
|
45529
|
+
wrapAction("project:work", async (query, options2) => {
|
|
45530
|
+
const apiUrl = resolveApiUrl(options2.apiUrl);
|
|
45531
|
+
const resolved = await resolveProject(query, options2.apiUrl);
|
|
45532
|
+
const [projectResult, notesResult] = await Promise.all([
|
|
45533
|
+
apiGet(`/api/external/projects/${resolved.id}`, apiUrl),
|
|
45534
|
+
apiGet(`/api/external/projects/${resolved.id}/notes`, apiUrl)
|
|
45535
|
+
]);
|
|
45536
|
+
const brief = buildProjectWorkBrief(projectResult.project, notesResult.notes);
|
|
45537
|
+
if (options2.json) {
|
|
45538
|
+
console.log(JSON.stringify(brief, null, 2));
|
|
45539
|
+
return;
|
|
45540
|
+
}
|
|
45541
|
+
console.log(source_default.cyan(`
|
|
45542
|
+
${renderProjectWorkBrief(brief)}
|
|
45543
|
+
`));
|
|
45544
|
+
})
|
|
45545
|
+
);
|
|
45546
|
+
}
|
|
45265
45547
|
function registerProjectGet(program3) {
|
|
45266
45548
|
program3.command("project:get <id>").description("Get a project by ID\n Example: elevasis-sdk project:get <uuid>").option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
45267
45549
|
wrapAction("project:get", async (id, options2) => {
|
|
@@ -45283,7 +45565,7 @@ Project: ${p.name}`));
|
|
|
45283
45565
|
);
|
|
45284
45566
|
}
|
|
45285
45567
|
function registerProjectCreate(program3) {
|
|
45286
|
-
program3.command("project:create").description('Create a new project\n Example: elevasis-sdk project:create --name "My Project" --kind internal').requiredOption("--name <name>", "Project name").requiredOption("--kind <kind>", "Project kind: client_engagement | internal | research | other").option("--status <status>", "Project status: active | on_track | at_risk | blocked | completed | paused").option("--description <description>", "Project description").option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
45568
|
+
program3.command("project:create").description('Create a new project\n Example: elevasis-sdk project:create --name "My Project" --kind internal').requiredOption("--name <name>", "Project name").requiredOption("--kind <kind>", "Project kind: client_engagement | internal | research | other").option("--status <status>", "Project status: active | on_track | at_risk | blocked | completed | paused").option("--description <description>", "Project description").option("--deal-id <uuid>", "Link to a deal (UUID)").option("--client-company-id <uuid>", "Link to a client company (UUID)").option("--start-date <date>", "Start date (ISO 8601, e.g. 2026-06-01)").option("--target-end-date <date>", "Target end date (ISO 8601, e.g. 2026-12-31)").option("--contract-value <amount>", "Contract value (number)", parseFloat).option("--metadata <json>", "Arbitrary metadata (JSON string)").option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
45287
45569
|
wrapAction(
|
|
45288
45570
|
"project:create",
|
|
45289
45571
|
async (options2) => {
|
|
@@ -45294,6 +45576,12 @@ function registerProjectCreate(program3) {
|
|
|
45294
45576
|
};
|
|
45295
45577
|
if (options2.status) body.status = options2.status;
|
|
45296
45578
|
if (options2.description) body.description = options2.description;
|
|
45579
|
+
if (options2.dealId) body.deal_id = options2.dealId;
|
|
45580
|
+
if (options2.clientCompanyId) body.client_company_id = options2.clientCompanyId;
|
|
45581
|
+
if (options2.startDate) body.start_date = options2.startDate;
|
|
45582
|
+
if (options2.targetEndDate) body.target_end_date = options2.targetEndDate;
|
|
45583
|
+
if (options2.contractValue !== void 0) body.contract_value = options2.contractValue;
|
|
45584
|
+
if (options2.metadata) body.metadata = JSON.parse(options2.metadata);
|
|
45297
45585
|
const result = await apiPost("/api/external/projects", body, apiUrl);
|
|
45298
45586
|
if (options2.pretty) {
|
|
45299
45587
|
const p = result.project;
|
|
@@ -45311,7 +45599,7 @@ Project created: ${p.name}`));
|
|
|
45311
45599
|
);
|
|
45312
45600
|
}
|
|
45313
45601
|
function registerProjectUpdate(program3) {
|
|
45314
|
-
program3.command("project:update <id>").description("Update a project\n Example: elevasis-sdk project:update <uuid> --status completed").option("--name <name>", "New project name").option("--status <status>", "New status: active | on_track | at_risk | blocked | completed | paused").option("--description <description>", "New description").option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
45602
|
+
program3.command("project:update <id>").description("Update a project\n Example: elevasis-sdk project:update <uuid> --status completed").option("--name <name>", "New project name").option("--status <status>", "New status: active | on_track | at_risk | blocked | completed | paused").option("--description <description>", "New description").option("--deal-id <uuid>", "Link to a deal (UUID)").option("--client-company-id <uuid>", "Link to a client company (UUID)").option("--start-date <date>", "Start date (ISO 8601, e.g. 2026-06-01)").option("--target-end-date <date>", "Target end date (ISO 8601, e.g. 2026-12-31)").option("--actual-end-date <date>", "Actual end date (ISO 8601)").option("--contract-value <amount>", "Contract value (number)", parseFloat).option("--metadata <json>", "Arbitrary metadata (JSON string)").option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
45315
45603
|
wrapAction(
|
|
45316
45604
|
"project:update",
|
|
45317
45605
|
async (id, options2) => {
|
|
@@ -45319,10 +45607,17 @@ function registerProjectUpdate(program3) {
|
|
|
45319
45607
|
if (options2.name !== void 0) body.name = options2.name;
|
|
45320
45608
|
if (options2.status !== void 0) body.status = options2.status;
|
|
45321
45609
|
if (options2.description !== void 0) body.description = options2.description;
|
|
45610
|
+
if (options2.dealId !== void 0) body.deal_id = options2.dealId;
|
|
45611
|
+
if (options2.clientCompanyId !== void 0) body.client_company_id = options2.clientCompanyId;
|
|
45612
|
+
if (options2.startDate !== void 0) body.start_date = options2.startDate;
|
|
45613
|
+
if (options2.targetEndDate !== void 0) body.target_end_date = options2.targetEndDate;
|
|
45614
|
+
if (options2.actualEndDate !== void 0) body.actual_end_date = options2.actualEndDate;
|
|
45615
|
+
if (options2.contractValue !== void 0) body.contract_value = options2.contractValue;
|
|
45616
|
+
if (options2.metadata !== void 0) body.metadata = JSON.parse(options2.metadata);
|
|
45322
45617
|
if (Object.keys(body).length === 0) {
|
|
45323
45618
|
process.stderr.write(
|
|
45324
45619
|
JSON.stringify({
|
|
45325
|
-
error: "At least one field must be provided (--name, --status, --description)",
|
|
45620
|
+
error: "At least one field must be provided (--name, --status, --description, --deal-id, --client-company-id, --start-date, --target-end-date, --actual-end-date, --contract-value, --metadata)",
|
|
45326
45621
|
code: "MISSING_FIELDS"
|
|
45327
45622
|
}) + "\n"
|
|
45328
45623
|
);
|
|
@@ -45419,7 +45714,7 @@ Milestone created: ${m.name}`));
|
|
|
45419
45714
|
);
|
|
45420
45715
|
}
|
|
45421
45716
|
function registerMilestoneUpdate(program3) {
|
|
45422
|
-
program3.command("project:milestone:update <id>").description("Update a milestone\n Example: elevasis-sdk project:milestone:update <uuid> --status completed").option("--name <name>", "New milestone name").option("--status <status>", "New status: upcoming | in_progress | completed | overdue | blocked").option("--due-date <date>", "New due date (ISO 8601)").option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
45717
|
+
program3.command("project:milestone:update <id>").description("Update a milestone\n Example: elevasis-sdk project:milestone:update <uuid> --status completed").option("--name <name>", "New milestone name").option("--status <status>", "New status: upcoming | in_progress | completed | overdue | blocked").option("--due-date <date>", "New due date (ISO 8601)").option("--checklist <json>", `Replace checklist (full array): '[{"id":"1","label":"Step","completed":false}]'`).option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
45423
45718
|
wrapAction(
|
|
45424
45719
|
"project:milestone:update",
|
|
45425
45720
|
async (id, options2) => {
|
|
@@ -45427,10 +45722,20 @@ function registerMilestoneUpdate(program3) {
|
|
|
45427
45722
|
if (options2.name !== void 0) body.name = options2.name;
|
|
45428
45723
|
if (options2.status !== void 0) body.status = options2.status;
|
|
45429
45724
|
if (options2.dueDate !== void 0) body.due_date = options2.dueDate;
|
|
45725
|
+
if (options2.checklist !== void 0) {
|
|
45726
|
+
try {
|
|
45727
|
+
body.checklist = JSON.parse(options2.checklist);
|
|
45728
|
+
} catch {
|
|
45729
|
+
process.stderr.write(
|
|
45730
|
+
JSON.stringify({ error: "--checklist must be valid JSON", code: "INVALID_JSON" }) + "\n"
|
|
45731
|
+
);
|
|
45732
|
+
process.exit(1);
|
|
45733
|
+
}
|
|
45734
|
+
}
|
|
45430
45735
|
if (Object.keys(body).length === 0) {
|
|
45431
45736
|
process.stderr.write(
|
|
45432
45737
|
JSON.stringify({
|
|
45433
|
-
error: "At least one field must be provided (--name, --status, --due-date)",
|
|
45738
|
+
error: "At least one field must be provided (--name, --status, --due-date, --checklist)",
|
|
45434
45739
|
code: "MISSING_FIELDS"
|
|
45435
45740
|
}) + "\n"
|
|
45436
45741
|
);
|
|
@@ -45578,7 +45883,7 @@ function registerTaskUpdate(program3) {
|
|
|
45578
45883
|
program3.command("project:task:update <id>").description("Update a task\n Example: elevasis-sdk project:task:update <uuid> --status completed").option("--title <title>", "New task title").option(
|
|
45579
45884
|
"--status <status>",
|
|
45580
45885
|
"New status: planned | in_progress | blocked | completed | cancelled | submitted | approved | rejected | revision_requested"
|
|
45581
|
-
).option("--milestone <milestone-id>", "New milestone ID (UUID)").option("--description <description>", "New description").option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
45886
|
+
).option("--milestone <milestone-id>", "New milestone ID (UUID)").option("--description <description>", "New description").option("--checklist <json>", `Replace checklist (full array): '[{"id":"1","label":"Step","completed":false}]'`).option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
45582
45887
|
wrapAction(
|
|
45583
45888
|
"project:task:update",
|
|
45584
45889
|
async (id, options2) => {
|
|
@@ -45587,10 +45892,20 @@ function registerTaskUpdate(program3) {
|
|
|
45587
45892
|
if (options2.status !== void 0) body.status = options2.status;
|
|
45588
45893
|
if (options2.milestone !== void 0) body.milestone_id = options2.milestone;
|
|
45589
45894
|
if (options2.description !== void 0) body.description = options2.description;
|
|
45895
|
+
if (options2.checklist !== void 0) {
|
|
45896
|
+
try {
|
|
45897
|
+
body.checklist = JSON.parse(options2.checklist);
|
|
45898
|
+
} catch {
|
|
45899
|
+
process.stderr.write(
|
|
45900
|
+
JSON.stringify({ error: "--checklist must be valid JSON", code: "INVALID_JSON" }) + "\n"
|
|
45901
|
+
);
|
|
45902
|
+
process.exit(1);
|
|
45903
|
+
}
|
|
45904
|
+
}
|
|
45590
45905
|
if (Object.keys(body).length === 0) {
|
|
45591
45906
|
process.stderr.write(
|
|
45592
45907
|
JSON.stringify({
|
|
45593
|
-
error: "At least one field must be provided (--title, --status, --milestone, --description)",
|
|
45908
|
+
error: "At least one field must be provided (--title, --status, --milestone, --description, --checklist)",
|
|
45594
45909
|
code: "MISSING_FIELDS"
|
|
45595
45910
|
}) + "\n"
|
|
45596
45911
|
);
|
|
@@ -45766,7 +46081,7 @@ Notes (${notes.length}):
|
|
|
45766
46081
|
function registerNoteCreate(program3) {
|
|
45767
46082
|
program3.command("project:note:create").description(
|
|
45768
46083
|
'Create a note\n Example: elevasis-sdk project:note:create --project <uuid> --content "Status update"'
|
|
45769
|
-
).requiredOption("--project <project-id>", "Project ID (UUID)").requiredOption("--content <content>", "Note content").option("--task <task-id>", "Attach to a task (UUID)").option("--milestone <milestone-id>", "Attach to a milestone (UUID)").option("--type <type>", "Note type: call_note | status_update | issue | blocker").option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
46084
|
+
).requiredOption("--project <project-id>", "Project ID (UUID)").requiredOption("--content <content>", "Note content").option("--task <task-id>", "Attach to a task (UUID)").option("--milestone <milestone-id>", "Attach to a milestone (UUID)").option("--type <type>", "Note type: call_note | status_update | issue | blocker | agent_learning").option("--api-url <url>", "API base URL").option("--pretty", "Render human-readable output instead of raw JSON").action(
|
|
45770
46085
|
wrapAction(
|
|
45771
46086
|
"project:note:create",
|
|
45772
46087
|
async (options2) => {
|
|
@@ -45827,6 +46142,8 @@ Note ${id} deleted.`));
|
|
|
45827
46142
|
// src/cli/commands/project/project.ts
|
|
45828
46143
|
function registerProjectCommands(program3) {
|
|
45829
46144
|
registerProjectList(program3);
|
|
46145
|
+
registerProjectResolve(program3);
|
|
46146
|
+
registerProjectWork(program3);
|
|
45830
46147
|
registerProjectGet(program3);
|
|
45831
46148
|
registerProjectCreate(program3);
|
|
45832
46149
|
registerProjectUpdate(program3);
|
|
@@ -45848,6 +46165,520 @@ function registerProjectCommands(program3) {
|
|
|
45848
46165
|
registerNoteDelete(program3);
|
|
45849
46166
|
}
|
|
45850
46167
|
|
|
46168
|
+
// src/cli/commands/init-claude.ts
|
|
46169
|
+
var import_path4 = require("path");
|
|
46170
|
+
var import_fs2 = require("fs");
|
|
46171
|
+
var import_url = require("url");
|
|
46172
|
+
var import_meta = {};
|
|
46173
|
+
var _filename = import_meta.url ? (0, import_url.fileURLToPath)(import_meta.url) : process.argv[1];
|
|
46174
|
+
var _dirname = (0, import_path4.dirname)(_filename);
|
|
46175
|
+
function copyDirectoryRecursive(sourceDir, targetDir) {
|
|
46176
|
+
if (!(0, import_fs2.existsSync)(sourceDir)) return 0;
|
|
46177
|
+
let count = 0;
|
|
46178
|
+
for (const entry of (0, import_fs2.readdirSync)(sourceDir, { withFileTypes: true })) {
|
|
46179
|
+
const srcPath = (0, import_path4.join)(sourceDir, entry.name);
|
|
46180
|
+
const destPath = (0, import_path4.join)(targetDir, entry.name);
|
|
46181
|
+
if (entry.isDirectory()) {
|
|
46182
|
+
count += copyDirectoryRecursive(srcPath, destPath);
|
|
46183
|
+
} else {
|
|
46184
|
+
(0, import_fs2.mkdirSync)((0, import_path4.dirname)(destPath), { recursive: true });
|
|
46185
|
+
(0, import_fs2.writeFileSync)(destPath, (0, import_fs2.readFileSync)(srcPath, "utf-8"), "utf-8");
|
|
46186
|
+
count++;
|
|
46187
|
+
}
|
|
46188
|
+
}
|
|
46189
|
+
return count;
|
|
46190
|
+
}
|
|
46191
|
+
function listFilesRecursive(dir, prefix = "") {
|
|
46192
|
+
for (const entry of (0, import_fs2.readdirSync)(dir, { withFileTypes: true })) {
|
|
46193
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
46194
|
+
if (entry.isDirectory()) {
|
|
46195
|
+
listFilesRecursive((0, import_path4.join)(dir, entry.name), rel);
|
|
46196
|
+
} else {
|
|
46197
|
+
console.log(source_default.gray(` .claude/${rel}`));
|
|
46198
|
+
}
|
|
46199
|
+
}
|
|
46200
|
+
}
|
|
46201
|
+
function registerInitClaudeCommand(program3) {
|
|
46202
|
+
program3.command("init-claude").description("Initialize or update .claude/ configuration from SDK defaults").option("--dry-run", "Show what would be copied without writing files").action(
|
|
46203
|
+
wrapAction("init-claude", async (options2) => {
|
|
46204
|
+
const spinner = ora("Initializing .claude/ configuration...").start();
|
|
46205
|
+
const claudeConfigSource = (0, import_path4.resolve)(_dirname, "..", "reference", "claude-config");
|
|
46206
|
+
if (!(0, import_fs2.existsSync)(claudeConfigSource)) {
|
|
46207
|
+
spinner.fail("Claude config source not found in SDK package");
|
|
46208
|
+
console.error(source_default.gray(` Expected: ${claudeConfigSource}`));
|
|
46209
|
+
console.error(source_default.gray(" This may indicate an incomplete SDK build."));
|
|
46210
|
+
throw new Error("Claude config source not found");
|
|
46211
|
+
}
|
|
46212
|
+
const targetDir = (0, import_path4.resolve)(process.cwd(), ".claude");
|
|
46213
|
+
if (options2.dryRun) {
|
|
46214
|
+
spinner.info("Dry run -- listing files that would be copied:");
|
|
46215
|
+
listFilesRecursive(claudeConfigSource);
|
|
46216
|
+
return;
|
|
46217
|
+
}
|
|
46218
|
+
const count = copyDirectoryRecursive(claudeConfigSource, targetDir);
|
|
46219
|
+
spinner.succeed(
|
|
46220
|
+
source_default.green(".claude/ configuration initialized") + source_default.gray(` (${count} file${count !== 1 ? "s" : ""} written)`)
|
|
46221
|
+
);
|
|
46222
|
+
})
|
|
46223
|
+
);
|
|
46224
|
+
}
|
|
46225
|
+
|
|
46226
|
+
// src/cli/commands/generate-docs.ts
|
|
46227
|
+
var import_fs3 = require("fs");
|
|
46228
|
+
var import_path5 = require("path");
|
|
46229
|
+
var EXCLUDED_ROOT_FILES = /* @__PURE__ */ new Set(["platform-navigation-map.md"]);
|
|
46230
|
+
var SECTION_ORDER = ["ui", "operations", "knowledge"];
|
|
46231
|
+
var LAST_SECTIONS = ["in-progress"];
|
|
46232
|
+
function parseFrontmatter(content) {
|
|
46233
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
46234
|
+
if (!match) return null;
|
|
46235
|
+
const fm = {};
|
|
46236
|
+
for (const line of match[1].split("\n")) {
|
|
46237
|
+
const idx = line.indexOf(":");
|
|
46238
|
+
if (idx === -1) continue;
|
|
46239
|
+
const key = line.slice(0, idx).trim();
|
|
46240
|
+
const val = line.slice(idx + 1).trim();
|
|
46241
|
+
fm[key] = val;
|
|
46242
|
+
}
|
|
46243
|
+
return fm;
|
|
46244
|
+
}
|
|
46245
|
+
function findMdFiles(dir, docsDir, indexPath, files = []) {
|
|
46246
|
+
for (const entry of (0, import_fs3.readdirSync)(dir)) {
|
|
46247
|
+
if (entry.startsWith(".")) continue;
|
|
46248
|
+
const full = (0, import_path5.join)(dir, entry);
|
|
46249
|
+
const stat = (0, import_fs3.statSync)(full);
|
|
46250
|
+
if (stat.isDirectory()) {
|
|
46251
|
+
findMdFiles(full, docsDir, indexPath, files);
|
|
46252
|
+
} else if (entry.endsWith(".md") && full !== indexPath) {
|
|
46253
|
+
if (dir === docsDir && EXCLUDED_ROOT_FILES.has(entry)) continue;
|
|
46254
|
+
files.push(full);
|
|
46255
|
+
}
|
|
46256
|
+
}
|
|
46257
|
+
return files;
|
|
46258
|
+
}
|
|
46259
|
+
function sectionTitle(slug) {
|
|
46260
|
+
if (slug === "ui") return "UI";
|
|
46261
|
+
return slug.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
|
|
46262
|
+
}
|
|
46263
|
+
function generateDocsIndex(docsDir) {
|
|
46264
|
+
const indexPath = (0, import_path5.join)(docsDir, "index.md");
|
|
46265
|
+
const files = findMdFiles(docsDir, docsDir, indexPath);
|
|
46266
|
+
const sections = /* @__PURE__ */ new Map();
|
|
46267
|
+
let warnings = 0;
|
|
46268
|
+
for (const file2 of files) {
|
|
46269
|
+
const rel = (0, import_path5.relative)(docsDir, file2).replace(/\\/g, "/");
|
|
46270
|
+
const content2 = (0, import_fs3.readFileSync)(file2, "utf-8");
|
|
46271
|
+
const fm = parseFrontmatter(content2);
|
|
46272
|
+
if (!fm || !fm["title"]) {
|
|
46273
|
+
console.warn(`WARNING: Missing frontmatter in ${rel} -- skipping`);
|
|
46274
|
+
warnings++;
|
|
46275
|
+
continue;
|
|
46276
|
+
}
|
|
46277
|
+
const topDir = rel.includes("/") ? rel.split("/")[0] : "_root";
|
|
46278
|
+
if (!sections.has(topDir)) sections.set(topDir, []);
|
|
46279
|
+
sections.get(topDir).push({ rel, title: fm["title"], description: fm["description"] || "", status: fm["status"] });
|
|
46280
|
+
}
|
|
46281
|
+
for (const entries of sections.values()) {
|
|
46282
|
+
entries.sort((a, b) => a.title.localeCompare(b.title));
|
|
46283
|
+
}
|
|
46284
|
+
const orderedSections = [];
|
|
46285
|
+
for (const s of SECTION_ORDER) {
|
|
46286
|
+
if (sections.has(s)) orderedSections.push(s);
|
|
46287
|
+
}
|
|
46288
|
+
for (const s of [...sections.keys()].sort()) {
|
|
46289
|
+
if (!SECTION_ORDER.includes(s) && !LAST_SECTIONS.includes(s) && s !== "_root") {
|
|
46290
|
+
orderedSections.push(s);
|
|
46291
|
+
}
|
|
46292
|
+
}
|
|
46293
|
+
for (const s of LAST_SECTIONS) {
|
|
46294
|
+
if (sections.has(s)) orderedSections.push(s);
|
|
46295
|
+
}
|
|
46296
|
+
if (sections.has("_root")) orderedSections.push("_root");
|
|
46297
|
+
const lines = [
|
|
46298
|
+
"---",
|
|
46299
|
+
"title: Documentation Index",
|
|
46300
|
+
"description: Auto-generated navigation hub of all project documentation",
|
|
46301
|
+
"---",
|
|
46302
|
+
"",
|
|
46303
|
+
"<!-- AUTO-GENERATED by elevasis-sdk generate-docs -- do not edit manually -->",
|
|
46304
|
+
"",
|
|
46305
|
+
"# Documentation Index",
|
|
46306
|
+
"",
|
|
46307
|
+
`> Last generated: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`,
|
|
46308
|
+
""
|
|
46309
|
+
];
|
|
46310
|
+
if (orderedSections.length === 0) {
|
|
46311
|
+
lines.push("No documented files found. Add frontmatter (`title`, `description`) to `.md` files in `docs/`.");
|
|
46312
|
+
lines.push("");
|
|
46313
|
+
} else {
|
|
46314
|
+
for (const section of orderedSections) {
|
|
46315
|
+
const entries = sections.get(section);
|
|
46316
|
+
if (!entries || entries.length === 0) continue;
|
|
46317
|
+
const title = section === "_root" ? "General" : sectionTitle(section);
|
|
46318
|
+
lines.push(`## ${title}`);
|
|
46319
|
+
lines.push("");
|
|
46320
|
+
for (const entry of entries) {
|
|
46321
|
+
const status = entry.status ? ` -- *${entry.status}*` : "";
|
|
46322
|
+
const desc = entry.description ? ` -- ${entry.description}` : "";
|
|
46323
|
+
lines.push(`- [${entry.title}](${entry.rel})${desc}${status}`);
|
|
46324
|
+
}
|
|
46325
|
+
lines.push("");
|
|
46326
|
+
}
|
|
46327
|
+
}
|
|
46328
|
+
const content = lines.join("\n");
|
|
46329
|
+
const docCount = files.length - warnings;
|
|
46330
|
+
console.log(
|
|
46331
|
+
`Generated docs/index.md: ${docCount} doc(s) indexed across ${orderedSections.length} section(s)` + (warnings > 0 ? ` (${warnings} skipped -- missing frontmatter)` : "")
|
|
46332
|
+
);
|
|
46333
|
+
return content;
|
|
46334
|
+
}
|
|
46335
|
+
function registerGenerateDocsCommand(program3) {
|
|
46336
|
+
program3.command("generate-docs").description("Generate docs/index.md from documentation frontmatter").option("--docs-dir <path>", "Path to docs directory (default: ./docs)").action(
|
|
46337
|
+
wrapAction("generate-docs", async (options2) => {
|
|
46338
|
+
const docsDir = (0, import_path5.resolve)(options2.docsDir ?? "./docs");
|
|
46339
|
+
const indexPath = (0, import_path5.join)(docsDir, "index.md");
|
|
46340
|
+
const spinner = ora("Generating docs/index.md...").start();
|
|
46341
|
+
const content = generateDocsIndex(docsDir);
|
|
46342
|
+
(0, import_fs3.writeFileSync)(indexPath, content);
|
|
46343
|
+
spinner.succeed(source_default.green("Generated docs/index.md"));
|
|
46344
|
+
})
|
|
46345
|
+
);
|
|
46346
|
+
}
|
|
46347
|
+
|
|
46348
|
+
// src/cli/commands/generate-resources.ts
|
|
46349
|
+
var import_promises2 = require("fs/promises");
|
|
46350
|
+
var import_path6 = require("path");
|
|
46351
|
+
function escapePipes(text) {
|
|
46352
|
+
return text.replace(/\|/g, "\\|");
|
|
46353
|
+
}
|
|
46354
|
+
function noneIfEmpty(lines, body) {
|
|
46355
|
+
const result = body();
|
|
46356
|
+
if (result.length === 0) {
|
|
46357
|
+
return [...lines, "_(none)_", ""];
|
|
46358
|
+
}
|
|
46359
|
+
return [...lines, ...result, ""];
|
|
46360
|
+
}
|
|
46361
|
+
function extractSchemaKeys(schema) {
|
|
46362
|
+
if (!schema || typeof schema !== "object") return null;
|
|
46363
|
+
const s = schema;
|
|
46364
|
+
if (s.shape && typeof s.shape === "object") {
|
|
46365
|
+
return Object.keys(s.shape).join(", ");
|
|
46366
|
+
}
|
|
46367
|
+
if (s._def && typeof s._def === "object") {
|
|
46368
|
+
const def = s._def;
|
|
46369
|
+
if (typeof def.shape === "function") {
|
|
46370
|
+
try {
|
|
46371
|
+
const shape = def.shape();
|
|
46372
|
+
return Object.keys(shape).join(", ");
|
|
46373
|
+
} catch {
|
|
46374
|
+
return null;
|
|
46375
|
+
}
|
|
46376
|
+
}
|
|
46377
|
+
if (Array.isArray(def.options) && def.options.every((o) => typeof o === "string")) {
|
|
46378
|
+
return def.options.join(" | ");
|
|
46379
|
+
}
|
|
46380
|
+
}
|
|
46381
|
+
if (s.properties && typeof s.properties === "object") {
|
|
46382
|
+
return Object.keys(s.properties).join(", ");
|
|
46383
|
+
}
|
|
46384
|
+
return null;
|
|
46385
|
+
}
|
|
46386
|
+
async function generateResourcesContent(spec) {
|
|
46387
|
+
const workflows = spec.workflows ?? [];
|
|
46388
|
+
const agents = spec.agents ?? [];
|
|
46389
|
+
const triggers = spec.triggers ?? [];
|
|
46390
|
+
const integrations = spec.integrations ?? [];
|
|
46391
|
+
const relationships = spec.relationships ?? {};
|
|
46392
|
+
const externalResources = spec.externalResources ?? [];
|
|
46393
|
+
const humanCheckpoints = spec.humanCheckpoints ?? [];
|
|
46394
|
+
const lines = [
|
|
46395
|
+
"---",
|
|
46396
|
+
"title: Platform Topology Map",
|
|
46397
|
+
"description: Auto-generated topology map for workflows, agents, relationships, triggers, integrations, and checkpoints declared in the DeploymentSpec",
|
|
46398
|
+
"---",
|
|
46399
|
+
"",
|
|
46400
|
+
"<!-- AUTO-GENERATED by elevasis-sdk generate-resources -- do not edit manually -->",
|
|
46401
|
+
"",
|
|
46402
|
+
"# Platform Topology Map",
|
|
46403
|
+
"",
|
|
46404
|
+
"> Auto-generated from `operations/src/index.ts`. Regenerate with `pnpm exec elevasis-sdk generate-resources`.",
|
|
46405
|
+
"",
|
|
46406
|
+
"Use this as the scaffold topology map for operational impact analysis.",
|
|
46407
|
+
"",
|
|
46408
|
+
"- Start here to understand what `operations/src/index.ts` deploys and how those resources connect.",
|
|
46409
|
+
"- Read it with `operations/src/README.md` when changing resource registration or deployment structure.",
|
|
46410
|
+
"- Treat `elevasis-sdk generate-resources` and the current `DeploymentSpec` as the source of truth for what this map can and cannot show.",
|
|
46411
|
+
""
|
|
46412
|
+
];
|
|
46413
|
+
const total = workflows.length + agents.length;
|
|
46414
|
+
lines.push("## Topology Summary", "");
|
|
46415
|
+
lines.push(`- Deployable resources: ${total}`);
|
|
46416
|
+
lines.push(`- Workflows: ${workflows.length}`);
|
|
46417
|
+
lines.push(`- Agents: ${agents.length}`);
|
|
46418
|
+
lines.push(`- Triggers: ${triggers.length}`);
|
|
46419
|
+
lines.push(`- Integrations: ${integrations.length}`);
|
|
46420
|
+
lines.push(`- Human checkpoints: ${humanCheckpoints.length}`);
|
|
46421
|
+
lines.push(`- External resources: ${externalResources.length}`);
|
|
46422
|
+
lines.push(`- Relationship declarations: ${Object.keys(relationships).length}`);
|
|
46423
|
+
lines.push("");
|
|
46424
|
+
lines.push("## Deployed Resource Inventory", "");
|
|
46425
|
+
lines.push(
|
|
46426
|
+
...noneIfEmpty([], () => {
|
|
46427
|
+
if (workflows.length === 0 && agents.length === 0) return [];
|
|
46428
|
+
const rows = [];
|
|
46429
|
+
if (workflows.length > 0) {
|
|
46430
|
+
rows.push("### Workflows", "");
|
|
46431
|
+
rows.push("| Resource ID | Name | Version | Status | Description | Domains |");
|
|
46432
|
+
rows.push("| --- | --- | --- | --- | --- | --- |");
|
|
46433
|
+
for (const wf of workflows) {
|
|
46434
|
+
const { resourceId, name, version: version2, status, description, domains } = wf.config;
|
|
46435
|
+
const domainStr = domains?.join(", ") ?? "";
|
|
46436
|
+
rows.push(
|
|
46437
|
+
`| \`${escapePipes(resourceId)}\` | ${escapePipes(name)} | ${escapePipes(version2)} | ${escapePipes(status)} | ${escapePipes(description ?? "")} | ${escapePipes(domainStr)} |`
|
|
46438
|
+
);
|
|
46439
|
+
}
|
|
46440
|
+
rows.push("");
|
|
46441
|
+
}
|
|
46442
|
+
if (agents.length > 0) {
|
|
46443
|
+
rows.push("### Agents", "");
|
|
46444
|
+
rows.push("| Resource ID | Name | Version | Status | Description | Domains | Session-Capable |");
|
|
46445
|
+
rows.push("| --- | --- | --- | --- | --- | --- | --- |");
|
|
46446
|
+
for (const ag of agents) {
|
|
46447
|
+
const { resourceId, name, version: version2, status, description, domains } = ag.config;
|
|
46448
|
+
const sessionCapable = ag.config.sessionCapable ? "Yes" : "No";
|
|
46449
|
+
const domainStr = domains?.join(", ") ?? "";
|
|
46450
|
+
rows.push(
|
|
46451
|
+
`| \`${escapePipes(resourceId)}\` | ${escapePipes(name)} | ${escapePipes(version2)} | ${escapePipes(status)} | ${escapePipes(description ?? "")} | ${escapePipes(domainStr)} | ${sessionCapable} |`
|
|
46452
|
+
);
|
|
46453
|
+
}
|
|
46454
|
+
rows.push("");
|
|
46455
|
+
}
|
|
46456
|
+
return rows;
|
|
46457
|
+
})
|
|
46458
|
+
);
|
|
46459
|
+
lines.push("## Topology Relationships", "");
|
|
46460
|
+
lines.push(
|
|
46461
|
+
...noneIfEmpty([], () => {
|
|
46462
|
+
const entries = Object.entries(relationships).filter(([, decl]) => {
|
|
46463
|
+
const hasTriggers = (decl.triggers?.agents?.length ?? 0) > 0 || (decl.triggers?.workflows?.length ?? 0) > 0;
|
|
46464
|
+
const hasUses = (decl.uses?.integrations?.length ?? 0) > 0;
|
|
46465
|
+
return hasTriggers || hasUses;
|
|
46466
|
+
});
|
|
46467
|
+
if (entries.length === 0) return [];
|
|
46468
|
+
const rows = [];
|
|
46469
|
+
for (const [resourceId, decl] of entries) {
|
|
46470
|
+
rows.push(`- **\`${resourceId}\`**`);
|
|
46471
|
+
const triggerAgents = decl.triggers?.agents ?? [];
|
|
46472
|
+
const triggerWorkflows = decl.triggers?.workflows ?? [];
|
|
46473
|
+
const usesIntegrations = decl.uses?.integrations ?? [];
|
|
46474
|
+
if (triggerAgents.length > 0 || triggerWorkflows.length > 0) {
|
|
46475
|
+
rows.push(" - Triggers:");
|
|
46476
|
+
for (const id of triggerWorkflows) rows.push(` - workflow \`${id}\``);
|
|
46477
|
+
for (const id of triggerAgents) rows.push(` - agent \`${id}\``);
|
|
46478
|
+
}
|
|
46479
|
+
if (usesIntegrations.length > 0) {
|
|
46480
|
+
rows.push(" - Uses:");
|
|
46481
|
+
for (const id of usesIntegrations) rows.push(` - integration \`${id}\``);
|
|
46482
|
+
}
|
|
46483
|
+
}
|
|
46484
|
+
return rows;
|
|
46485
|
+
})
|
|
46486
|
+
);
|
|
46487
|
+
lines.push("## Triggers", "");
|
|
46488
|
+
lines.push(
|
|
46489
|
+
...noneIfEmpty([], () => {
|
|
46490
|
+
if (triggers.length === 0) return [];
|
|
46491
|
+
const rows = [
|
|
46492
|
+
"| Resource ID | Trigger Type | Webhook Path | Schedule | Event Type |",
|
|
46493
|
+
"| --- | --- | --- | --- | --- |"
|
|
46494
|
+
];
|
|
46495
|
+
for (const tr of triggers) {
|
|
46496
|
+
const webhookPath = escapePipes(tr.webhookPath ?? "");
|
|
46497
|
+
const schedule = escapePipes(tr.schedule ?? "");
|
|
46498
|
+
const eventType = escapePipes(tr.eventType ?? "");
|
|
46499
|
+
rows.push(
|
|
46500
|
+
`| \`${escapePipes(tr.resourceId)}\` | ${escapePipes(tr.triggerType)} | ${webhookPath} | ${schedule} | ${eventType} |`
|
|
46501
|
+
);
|
|
46502
|
+
}
|
|
46503
|
+
return rows;
|
|
46504
|
+
})
|
|
46505
|
+
);
|
|
46506
|
+
lines.push("## Integrations", "");
|
|
46507
|
+
lines.push(
|
|
46508
|
+
...noneIfEmpty([], () => {
|
|
46509
|
+
if (integrations.length === 0) return [];
|
|
46510
|
+
const rows = ["| Resource ID | Provider | Credential Name |", "| --- | --- | --- |"];
|
|
46511
|
+
for (const ig of integrations) {
|
|
46512
|
+
rows.push(
|
|
46513
|
+
`| \`${escapePipes(ig.resourceId)}\` | ${escapePipes(ig.provider)} | \`${escapePipes(ig.credentialName)}\` |`
|
|
46514
|
+
);
|
|
46515
|
+
}
|
|
46516
|
+
return rows;
|
|
46517
|
+
})
|
|
46518
|
+
);
|
|
46519
|
+
lines.push("## Human Checkpoints", "");
|
|
46520
|
+
lines.push(
|
|
46521
|
+
...noneIfEmpty([], () => {
|
|
46522
|
+
if (humanCheckpoints.length === 0) return [];
|
|
46523
|
+
const rows = [];
|
|
46524
|
+
rows.push("| Resource ID | Requested By | Routes To |");
|
|
46525
|
+
rows.push("| --- | --- | --- |");
|
|
46526
|
+
for (const hc of humanCheckpoints) {
|
|
46527
|
+
const requestedBy = [
|
|
46528
|
+
...(hc.requestedBy?.agents ?? []).map((id) => `agent \`${id}\``),
|
|
46529
|
+
...(hc.requestedBy?.workflows ?? []).map((id) => `workflow \`${id}\``)
|
|
46530
|
+
].join(", ");
|
|
46531
|
+
const routesTo = [
|
|
46532
|
+
...(hc.routesTo?.agents ?? []).map((id) => `agent \`${id}\``),
|
|
46533
|
+
...(hc.routesTo?.workflows ?? []).map((id) => `workflow \`${id}\``)
|
|
46534
|
+
].join(", ");
|
|
46535
|
+
rows.push(`| \`${escapePipes(hc.resourceId)}\` | ${escapePipes(requestedBy)} | ${escapePipes(routesTo)} |`);
|
|
46536
|
+
}
|
|
46537
|
+
return rows;
|
|
46538
|
+
})
|
|
46539
|
+
);
|
|
46540
|
+
lines.push("## External Resources", "");
|
|
46541
|
+
lines.push(
|
|
46542
|
+
...noneIfEmpty([], () => {
|
|
46543
|
+
if (externalResources.length === 0) return [];
|
|
46544
|
+
const rows = [
|
|
46545
|
+
"| Resource ID | Platform | Platform URL | External ID | Triggers | Uses |",
|
|
46546
|
+
"| --- | --- | --- | --- | --- | --- |"
|
|
46547
|
+
];
|
|
46548
|
+
for (const er of externalResources) {
|
|
46549
|
+
const triggersList = [
|
|
46550
|
+
...(er.triggers?.workflows ?? []).map((id) => `workflow \`${id}\``),
|
|
46551
|
+
...(er.triggers?.agents ?? []).map((id) => `agent \`${id}\``)
|
|
46552
|
+
].join(", ");
|
|
46553
|
+
const usesList = (er.uses?.integrations ?? []).map((id) => `integration \`${id}\``).join(", ");
|
|
46554
|
+
rows.push(
|
|
46555
|
+
`| \`${escapePipes(er.resourceId)}\` | ${escapePipes(er.platform)} | ${escapePipes(er.platformUrl ?? "")} | ${escapePipes(er.externalId ?? "")} | ${escapePipes(triggersList)} | ${escapePipes(usesList)} |`
|
|
46556
|
+
);
|
|
46557
|
+
}
|
|
46558
|
+
return rows;
|
|
46559
|
+
})
|
|
46560
|
+
);
|
|
46561
|
+
lines.push("## Schema Signatures", "");
|
|
46562
|
+
const allResources = [
|
|
46563
|
+
...workflows.map((wf) => ({ resourceId: wf.config.resourceId, contract: wf.contract })),
|
|
46564
|
+
...agents.map((ag) => ({ resourceId: ag.config.resourceId, contract: ag.contract }))
|
|
46565
|
+
];
|
|
46566
|
+
lines.push(
|
|
46567
|
+
...noneIfEmpty([], () => {
|
|
46568
|
+
if (allResources.length === 0) return [];
|
|
46569
|
+
const rows = [];
|
|
46570
|
+
for (const { resourceId, contract } of allResources) {
|
|
46571
|
+
const inputKeys = contract?.inputSchema ? extractSchemaKeys(contract.inputSchema) : null;
|
|
46572
|
+
const outputKeys = contract?.outputSchema ? extractSchemaKeys(contract.outputSchema) : null;
|
|
46573
|
+
const inputSig = inputKeys !== null ? `\`{ ${inputKeys} }\`` : "_schema introspection unavailable \u2014 see source_";
|
|
46574
|
+
const outputSig = outputKeys !== null ? `\`{ ${outputKeys} }\`` : "_schema introspection unavailable \u2014 see source_";
|
|
46575
|
+
rows.push(`- **\`${resourceId}\`**`);
|
|
46576
|
+
rows.push(` - input: ${inputSig}`);
|
|
46577
|
+
rows.push(` - output: ${outputSig}`);
|
|
46578
|
+
}
|
|
46579
|
+
return rows;
|
|
46580
|
+
})
|
|
46581
|
+
);
|
|
46582
|
+
lines.push(
|
|
46583
|
+
`**Total:** ${total} resource${total !== 1 ? "s" : ""} (${workflows.length} workflow${workflows.length !== 1 ? "s" : ""}, ${agents.length} agent${agents.length !== 1 ? "s" : ""})`
|
|
46584
|
+
);
|
|
46585
|
+
lines.push("");
|
|
46586
|
+
return lines.join("\n");
|
|
46587
|
+
}
|
|
46588
|
+
function registerGenerateResourcesCommand(program3) {
|
|
46589
|
+
program3.command("generate-resources").description("Generate docs/resources.md from the DeploymentSpec").option("--entry <path>", "Path to operations entry file (default: ./src/index.ts)").option("--docs-dir <path>", "Path to docs directory (default: ../docs from entry)").action(
|
|
46590
|
+
wrapAction("generate-resources", async (options2) => {
|
|
46591
|
+
const entryPath = options2.entry ?? "./src/index.ts";
|
|
46592
|
+
const spinner = ora("Generating resources documentation...").start();
|
|
46593
|
+
const entryModule = await loadTsModule(entryPath);
|
|
46594
|
+
const spec = entryModule.default;
|
|
46595
|
+
if (!spec) {
|
|
46596
|
+
spinner.fail("Invalid entry: no default export found");
|
|
46597
|
+
throw new Error("Invalid entry");
|
|
46598
|
+
}
|
|
46599
|
+
const docsDir = (0, import_path6.resolve)(options2.docsDir ?? (0, import_path6.resolve)(entryPath, "../../docs"));
|
|
46600
|
+
const docsPath = (0, import_path6.resolve)(docsDir, "resources.md");
|
|
46601
|
+
const content = await generateResourcesContent(spec);
|
|
46602
|
+
await (0, import_promises2.mkdir)(docsDir, { recursive: true });
|
|
46603
|
+
await (0, import_promises2.writeFile)(docsPath, content, "utf-8");
|
|
46604
|
+
const workflows = spec.workflows ?? [];
|
|
46605
|
+
const agents = spec.agents ?? [];
|
|
46606
|
+
const total = workflows.length + agents.length;
|
|
46607
|
+
spinner.succeed(source_default.green("Generated docs/resources.md"));
|
|
46608
|
+
console.log(source_default.gray(` ${total} resource(s) (${workflows.length} workflow(s), ${agents.length} agent(s))`));
|
|
46609
|
+
})
|
|
46610
|
+
);
|
|
46611
|
+
}
|
|
46612
|
+
|
|
46613
|
+
// src/cli/commands/validate-docs.ts
|
|
46614
|
+
var import_fs4 = require("fs");
|
|
46615
|
+
var import_path7 = require("path");
|
|
46616
|
+
var import_child_process = require("child_process");
|
|
46617
|
+
function normalizeEol(value) {
|
|
46618
|
+
return value.replace(/\r\n/g, "\n");
|
|
46619
|
+
}
|
|
46620
|
+
function readRequiredFile(filePath) {
|
|
46621
|
+
if (!(0, import_fs4.existsSync)(filePath)) {
|
|
46622
|
+
throw new Error(`Missing generated file: ${filePath}`);
|
|
46623
|
+
}
|
|
46624
|
+
return normalizeEol((0, import_fs4.readFileSync)(filePath, "utf8"));
|
|
46625
|
+
}
|
|
46626
|
+
function runPnpmExec(args, cwd) {
|
|
46627
|
+
if (process.platform === "win32") {
|
|
46628
|
+
const command = `pnpm exec ${args.join(" ")}`;
|
|
46629
|
+
(0, import_child_process.execFileSync)(process.env["ComSpec"] ?? "cmd.exe", ["/d", "/s", "/c", command], {
|
|
46630
|
+
cwd,
|
|
46631
|
+
stdio: "pipe",
|
|
46632
|
+
encoding: "utf8"
|
|
46633
|
+
});
|
|
46634
|
+
return;
|
|
46635
|
+
}
|
|
46636
|
+
(0, import_child_process.execFileSync)("pnpm", ["exec", ...args], {
|
|
46637
|
+
cwd,
|
|
46638
|
+
stdio: "pipe",
|
|
46639
|
+
encoding: "utf8"
|
|
46640
|
+
});
|
|
46641
|
+
}
|
|
46642
|
+
function validateCommandOutput(filePath, commandDescription, sdkArgs, cwd) {
|
|
46643
|
+
const before = readRequiredFile(filePath);
|
|
46644
|
+
runPnpmExec(sdkArgs, cwd);
|
|
46645
|
+
const after = readRequiredFile(filePath);
|
|
46646
|
+
if (before !== after) {
|
|
46647
|
+
throw new Error(`Drift detected in ${filePath}. Regenerate with \`${commandDescription}\`.`);
|
|
46648
|
+
}
|
|
46649
|
+
}
|
|
46650
|
+
function registerValidateDocsCommand(program3) {
|
|
46651
|
+
program3.command("validate-docs").description("Validate that auto-generated docs are up to date").option("--docs-dir <path>", "Path to docs directory (default: ./docs)").option("--entry <path>", "Path to operations entry file for resources validation (default: ./src/index.ts)").action(
|
|
46652
|
+
wrapAction("validate-docs", async (options2) => {
|
|
46653
|
+
const spinner = ora("Validating generated docs...").start();
|
|
46654
|
+
const projectRoot = process.cwd();
|
|
46655
|
+
const docsDir = (0, import_path7.resolve)(options2.docsDir ?? "./docs");
|
|
46656
|
+
const entryPath = options2.entry ?? "./src/index.ts";
|
|
46657
|
+
spinner.text = "Validating docs/index.md...";
|
|
46658
|
+
const indexPath = (0, import_path7.join)(docsDir, "index.md");
|
|
46659
|
+
const generateDocsArgs = ["elevasis-sdk", "generate-docs", "--docs-dir", docsDir];
|
|
46660
|
+
validateCommandOutput(indexPath, "pnpm exec elevasis-sdk generate-docs", generateDocsArgs, projectRoot);
|
|
46661
|
+
spinner.text = "Validating docs/resources.md...";
|
|
46662
|
+
const resourcesPath = (0, import_path7.join)(docsDir, "resources.md");
|
|
46663
|
+
const generateResourcesArgs = [
|
|
46664
|
+
"elevasis-sdk",
|
|
46665
|
+
"generate-resources",
|
|
46666
|
+
"--entry",
|
|
46667
|
+
entryPath,
|
|
46668
|
+
"--docs-dir",
|
|
46669
|
+
docsDir
|
|
46670
|
+
];
|
|
46671
|
+
validateCommandOutput(
|
|
46672
|
+
resourcesPath,
|
|
46673
|
+
"pnpm exec elevasis-sdk generate-resources",
|
|
46674
|
+
generateResourcesArgs,
|
|
46675
|
+
projectRoot
|
|
46676
|
+
);
|
|
46677
|
+
spinner.succeed(source_default.green("Generated docs are fresh."));
|
|
46678
|
+
})
|
|
46679
|
+
);
|
|
46680
|
+
}
|
|
46681
|
+
|
|
45851
46682
|
// src/cli/index.ts
|
|
45852
46683
|
var envPath = findEnvFile();
|
|
45853
46684
|
if (envPath) {
|
|
@@ -45873,7 +46704,14 @@ Commands:
|
|
|
45873
46704
|
elevasis-sdk executions <resourceId> List execution history
|
|
45874
46705
|
elevasis-sdk execution <resourceId> <id> Get execution details
|
|
45875
46706
|
elevasis-sdk deployments List deployments
|
|
46707
|
+
elevasis-sdk project:list [--search <query>] List projects with optional search
|
|
46708
|
+
elevasis-sdk project:resolve <query> Resolve a project ID from a name or UUID
|
|
46709
|
+
elevasis-sdk project:work <query> Open a lifecycle-aware project work brief
|
|
45876
46710
|
elevasis-sdk rename <id> --to <newId> [--prod] Rename resource across platform tables
|
|
46711
|
+
elevasis-sdk init-claude Initialize .claude/ config from SDK
|
|
46712
|
+
elevasis-sdk generate-docs Generate docs/index.md from frontmatter
|
|
46713
|
+
elevasis-sdk generate-resources Generate docs/resources.md from DeploymentSpec
|
|
46714
|
+
elevasis-sdk validate-docs Validate generated docs are fresh
|
|
45877
46715
|
|
|
45878
46716
|
Use "elevasis-sdk <command> --help" for more information about a command.`
|
|
45879
46717
|
).version(SDK_VERSION);
|
|
@@ -45889,6 +46727,10 @@ registerCredsCommand(program2);
|
|
|
45889
46727
|
registerErrorCommand(program2);
|
|
45890
46728
|
registerRenameCommand(program2);
|
|
45891
46729
|
registerProjectCommands(program2);
|
|
46730
|
+
registerInitClaudeCommand(program2);
|
|
46731
|
+
registerGenerateDocsCommand(program2);
|
|
46732
|
+
registerGenerateResourcesCommand(program2);
|
|
46733
|
+
registerValidateDocsCommand(program2);
|
|
45892
46734
|
program2.parse();
|
|
45893
46735
|
/*! Bundled license information:
|
|
45894
46736
|
|