@moda-ai/cli 1.35.0 → 1.37.0
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/README.md +2 -0
- package/dist/{cli-0p6n391h.js → cli-07tv75te.js} +21 -6
- package/dist/cli-0v6na3yp.js +35 -0
- package/dist/cli-c5c6f0yw.js +128 -0
- package/dist/{cli-7qkhy41j.js → cli-ert1sbbt.js} +112 -444
- package/dist/{cli-drabjmaj.js → cli-q79sq80a.js} +1 -35
- package/dist/{cli-scjxgaba.js → cli-qgf2cz2v.js} +2 -2
- package/dist/cli-ssfq84k5.js +393 -0
- package/dist/{cli-yz99t7n8.js → cli-strngwn3.js} +4 -2
- package/dist/cli.js +427 -12
- package/dist/{harness-github-actions-tk17mnge.js → harness-github-actions-qz5d22rz.js} +3 -2
- package/dist/{harness-heasma1f.js → harness-n2xgzz31.js} +3 -2
- package/dist/{index-pf2hvadv.js → index-vmmfya6b.js} +167 -207
- package/dist/prompt-source-mcp.js +3 -2
- package/dist/{provision-d242y9qd.js → provision-spwn5t41.js} +4 -3
- package/dist/skills-bundled-mdmhzfsr.js +14 -0
- package/package.json +1 -1
- package/skills/integration/index.json +3 -3
- package/skills/integration/integration-python-langchain/EXAMPLE.md +1 -1
- package/skills/integration/integration-python-langchain/SKILL.md +44 -11
- package/skills/moda-cli/SKILL.md +47 -3
|
@@ -11,16 +11,22 @@ import {
|
|
|
11
11
|
resolvePromptSource,
|
|
12
12
|
safePromptEvidencePath,
|
|
13
13
|
scanHarness
|
|
14
|
-
} from "./cli-
|
|
14
|
+
} from "./cli-07tv75te.js";
|
|
15
|
+
import {
|
|
16
|
+
SKILL_SOURCE_HARNESS,
|
|
17
|
+
SKILL_SOURCE_INTEGRATION_BUNDLED,
|
|
18
|
+
claudeSkillToCursorRule,
|
|
19
|
+
detectAgents,
|
|
20
|
+
pruneManagedSkillPaths,
|
|
21
|
+
readSkillsManifest,
|
|
22
|
+
writeSkillsManifest
|
|
23
|
+
} from "./cli-ssfq84k5.js";
|
|
15
24
|
import {
|
|
16
25
|
isAuthSessionValid,
|
|
17
26
|
loadAuthSession
|
|
18
|
-
} from "./cli-
|
|
27
|
+
} from "./cli-strngwn3.js";
|
|
19
28
|
import {
|
|
20
29
|
CliInputError,
|
|
21
|
-
__commonJS,
|
|
22
|
-
__require,
|
|
23
|
-
__toESM,
|
|
24
30
|
createCommandContext,
|
|
25
31
|
ensureSecretFilePermissions,
|
|
26
32
|
getActiveProfile,
|
|
@@ -34,7 +40,12 @@ import {
|
|
|
34
40
|
resolveTenantId,
|
|
35
41
|
secretFilePathFromRef,
|
|
36
42
|
validateConfig
|
|
37
|
-
} from "./cli-
|
|
43
|
+
} from "./cli-q79sq80a.js";
|
|
44
|
+
import {
|
|
45
|
+
__commonJS,
|
|
46
|
+
__require,
|
|
47
|
+
__toESM
|
|
48
|
+
} from "./cli-0v6na3yp.js";
|
|
38
49
|
|
|
39
50
|
// ../../node_modules/.bun/fast-glob@3.3.3/node_modules/fast-glob/out/utils/array.js
|
|
40
51
|
var require_array = __commonJS((exports) => {
|
|
@@ -6309,7 +6320,7 @@ async function runPromptSyncExclusive(flags, profileOptions, options) {
|
|
|
6309
6320
|
const analyzedDefinitions = new Map;
|
|
6310
6321
|
if (flags["no-analyze"] !== "true") {
|
|
6311
6322
|
options.onProgress?.("Analyzing prompt sources before sync.");
|
|
6312
|
-
const { analyzeHarnessPrompts } = await import("./harness-
|
|
6323
|
+
const { analyzeHarnessPrompts } = await import("./harness-n2xgzz31.js");
|
|
6313
6324
|
const result = await analyzeHarnessPrompts({
|
|
6314
6325
|
rootDir: process.cwd(),
|
|
6315
6326
|
existing: established.flatMap((prompt) => prompt.sourceDefinition ? [prompt.sourceDefinition.source] : []),
|
|
@@ -6790,416 +6801,22 @@ function printJson(value) {
|
|
|
6790
6801
|
console.log(JSON.stringify(value, null, 2));
|
|
6791
6802
|
}
|
|
6792
6803
|
|
|
6793
|
-
// src/init/rules.ts
|
|
6794
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6795
|
-
import { join as join3 } from "node:path";
|
|
6796
|
-
import { confirm } from "@inquirer/prompts";
|
|
6797
|
-
|
|
6798
|
-
// src/package-assets.ts
|
|
6799
|
-
import { dirname as dirname3, join as join2 } from "node:path";
|
|
6800
|
-
import { fileURLToPath } from "node:url";
|
|
6801
|
-
function packageAssetPath(...parts) {
|
|
6802
|
-
return join2(dirname3(fileURLToPath(import.meta.url)), "..", ...parts);
|
|
6803
|
-
}
|
|
6804
|
-
|
|
6805
|
-
// src/init/rules.ts
|
|
6806
|
-
var SKILLS_SOURCE_DIR = packageAssetPath("skills");
|
|
6807
|
-
var SKILLS = [
|
|
6808
|
-
{
|
|
6809
|
-
id: "moda-cli",
|
|
6810
|
-
label: "moda-cli — let coding agents query Moda analytics via the CLI"
|
|
6811
|
-
}
|
|
6812
|
-
];
|
|
6813
|
-
var ADAPTERS = {
|
|
6814
|
-
"Claude Code": {
|
|
6815
|
-
name: "Claude Code",
|
|
6816
|
-
targetFor: (skillId) => ({
|
|
6817
|
-
dir: join3(".claude", "skills", skillId),
|
|
6818
|
-
filename: "SKILL.md"
|
|
6819
|
-
}),
|
|
6820
|
-
transform: (content) => content
|
|
6821
|
-
},
|
|
6822
|
-
Cursor: {
|
|
6823
|
-
name: "Cursor",
|
|
6824
|
-
targetFor: (skillId) => ({
|
|
6825
|
-
dir: join3(".cursor", "rules"),
|
|
6826
|
-
filename: `${skillId}.mdc`
|
|
6827
|
-
}),
|
|
6828
|
-
transform: claudeSkillToCursorRule
|
|
6829
|
-
}
|
|
6830
|
-
};
|
|
6831
|
-
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
|
|
6832
|
-
var DESCRIPTION_RE = /^description:\s*(.+?)\s*$/m;
|
|
6833
|
-
function claudeSkillToCursorRule(skillContent) {
|
|
6834
|
-
const match = FRONTMATTER_RE.exec(skillContent);
|
|
6835
|
-
if (!match) {
|
|
6836
|
-
return skillContent;
|
|
6837
|
-
}
|
|
6838
|
-
const [, frontmatter, body] = match;
|
|
6839
|
-
const descMatch = DESCRIPTION_RE.exec(frontmatter);
|
|
6840
|
-
const description = descMatch?.[1].trim() ?? "";
|
|
6841
|
-
const cursorFrontmatter = `---
|
|
6842
|
-
` + `description: ${JSON.stringify(description)}
|
|
6843
|
-
` + `alwaysApply: false
|
|
6844
|
-
` + `---
|
|
6845
|
-
`;
|
|
6846
|
-
return `${cursorFrontmatter}
|
|
6847
|
-
${body.replace(/^\s+/, "")}`;
|
|
6848
|
-
}
|
|
6849
|
-
function loadSkillSource(skillId) {
|
|
6850
|
-
const path = join3(SKILLS_SOURCE_DIR, skillId, "SKILL.md");
|
|
6851
|
-
if (!existsSync3(path)) {
|
|
6852
|
-
throw new Error(`Skill source missing: ${path}`);
|
|
6853
|
-
}
|
|
6854
|
-
return readFileSync2(path, "utf8");
|
|
6855
|
-
}
|
|
6856
|
-
function installSkillForAgent(skill, adapter, content) {
|
|
6857
|
-
const { dir, filename } = adapter.targetFor(skill.id);
|
|
6858
|
-
const dirPath = join3(process.cwd(), dir);
|
|
6859
|
-
const filePath = join3(dirPath, filename);
|
|
6860
|
-
const relativePath = join3(dir, filename);
|
|
6861
|
-
try {
|
|
6862
|
-
mkdirSync2(dirPath, { recursive: true });
|
|
6863
|
-
if (existsSync3(filePath)) {
|
|
6864
|
-
return { agent: adapter.name, relativePath, status: "exists" };
|
|
6865
|
-
}
|
|
6866
|
-
writeFileSync3(filePath, adapter.transform(content));
|
|
6867
|
-
return { agent: adapter.name, relativePath, status: "wrote" };
|
|
6868
|
-
} catch (err) {
|
|
6869
|
-
return {
|
|
6870
|
-
agent: adapter.name,
|
|
6871
|
-
relativePath,
|
|
6872
|
-
status: "failed",
|
|
6873
|
-
error: err instanceof Error ? err.message : String(err)
|
|
6874
|
-
};
|
|
6875
|
-
}
|
|
6876
|
-
}
|
|
6877
|
-
async function installRules(detectedAgents, options = {}) {
|
|
6878
|
-
const adapters = detectedAgents.map((a) => ADAPTERS[a]).filter((a) => a !== undefined);
|
|
6879
|
-
if (adapters.length === 0) {
|
|
6880
|
-
if (!options.silent)
|
|
6881
|
-
console.log(" No supported coding agents detected");
|
|
6882
|
-
return;
|
|
6883
|
-
}
|
|
6884
|
-
const agentList = adapters.map((a) => a.name).join(", ");
|
|
6885
|
-
const skillList = SKILLS.map((s) => `
|
|
6886
|
-
• ${s.label}`).join("");
|
|
6887
|
-
const shouldInstall = options.yes ? true : await confirm({
|
|
6888
|
-
message: `Install Moda skills for ${agentList}?${skillList}
|
|
6889
|
-
Continue?`,
|
|
6890
|
-
default: true
|
|
6891
|
-
});
|
|
6892
|
-
if (!shouldInstall) {
|
|
6893
|
-
if (!options.silent)
|
|
6894
|
-
console.log(" Skipped");
|
|
6895
|
-
return;
|
|
6896
|
-
}
|
|
6897
|
-
let wrote = 0;
|
|
6898
|
-
let skipped = 0;
|
|
6899
|
-
let failed = 0;
|
|
6900
|
-
for (const skill of SKILLS) {
|
|
6901
|
-
let content;
|
|
6902
|
-
try {
|
|
6903
|
-
content = loadSkillSource(skill.id);
|
|
6904
|
-
} catch (err) {
|
|
6905
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
6906
|
-
console.error(` Warning: ${message}`);
|
|
6907
|
-
failed += adapters.length;
|
|
6908
|
-
continue;
|
|
6909
|
-
}
|
|
6910
|
-
for (const adapter of adapters) {
|
|
6911
|
-
const outcome = installSkillForAgent(skill, adapter, content);
|
|
6912
|
-
switch (outcome.status) {
|
|
6913
|
-
case "wrote":
|
|
6914
|
-
if (!options.silent)
|
|
6915
|
-
console.log(` ${outcome.agent}: wrote ${outcome.relativePath}`);
|
|
6916
|
-
wrote++;
|
|
6917
|
-
break;
|
|
6918
|
-
case "exists":
|
|
6919
|
-
if (!options.silent) {
|
|
6920
|
-
console.log(` ${outcome.agent}: ${outcome.relativePath} already exists — skipping`);
|
|
6921
|
-
}
|
|
6922
|
-
skipped++;
|
|
6923
|
-
break;
|
|
6924
|
-
case "failed":
|
|
6925
|
-
if (!options.silent) {
|
|
6926
|
-
console.error(` ${outcome.agent}: failed to write ${outcome.relativePath} — ${outcome.error}`);
|
|
6927
|
-
}
|
|
6928
|
-
failed++;
|
|
6929
|
-
break;
|
|
6930
|
-
}
|
|
6931
|
-
}
|
|
6932
|
-
}
|
|
6933
|
-
const summary = [
|
|
6934
|
-
wrote && `${wrote} written`,
|
|
6935
|
-
skipped && `${skipped} already present`,
|
|
6936
|
-
failed && `${failed} failed`
|
|
6937
|
-
].filter(Boolean).join(", ");
|
|
6938
|
-
if (summary) {
|
|
6939
|
-
if (!options.silent)
|
|
6940
|
-
console.log(` Done — ${summary}`);
|
|
6941
|
-
}
|
|
6942
|
-
}
|
|
6943
|
-
|
|
6944
|
-
// src/init/mcp.ts
|
|
6945
|
-
import { existsSync as existsSync4 } from "node:fs";
|
|
6946
|
-
import { join as join4 } from "node:path";
|
|
6947
|
-
import { execSync } from "node:child_process";
|
|
6948
|
-
function isOnPath(cmd) {
|
|
6949
|
-
try {
|
|
6950
|
-
execSync(`which ${cmd}`, { stdio: "pipe" });
|
|
6951
|
-
return true;
|
|
6952
|
-
} catch {
|
|
6953
|
-
return false;
|
|
6954
|
-
}
|
|
6955
|
-
}
|
|
6956
|
-
function detectAgents() {
|
|
6957
|
-
const cwd = process.cwd();
|
|
6958
|
-
const agents = [];
|
|
6959
|
-
if (existsSync4(join4(cwd, ".claude")) || isOnPath("claude")) {
|
|
6960
|
-
agents.push("Claude Code");
|
|
6961
|
-
}
|
|
6962
|
-
if (existsSync4(join4(cwd, ".cursor"))) {
|
|
6963
|
-
agents.push("Cursor");
|
|
6964
|
-
}
|
|
6965
|
-
return agents;
|
|
6966
|
-
}
|
|
6967
|
-
|
|
6968
6804
|
// src/skills.ts
|
|
6969
6805
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
6970
6806
|
import { createHash as createHash2 } from "node:crypto";
|
|
6971
6807
|
import {
|
|
6972
|
-
existsSync as
|
|
6808
|
+
existsSync as existsSync4,
|
|
6973
6809
|
lstatSync as lstatSync2,
|
|
6974
|
-
mkdirSync as mkdirSync5,
|
|
6975
|
-
readdirSync as readdirSync2,
|
|
6976
|
-
readFileSync as readFileSync5,
|
|
6977
|
-
writeFileSync as writeFileSync6
|
|
6978
|
-
} from "node:fs";
|
|
6979
|
-
import { basename, dirname as dirname6, join as join7, relative as relative3 } from "node:path";
|
|
6980
|
-
|
|
6981
|
-
// src/skills-manifest.ts
|
|
6982
|
-
import {
|
|
6983
|
-
existsSync as existsSync5,
|
|
6984
6810
|
mkdirSync as mkdirSync3,
|
|
6985
6811
|
readdirSync,
|
|
6986
6812
|
readFileSync as readFileSync3,
|
|
6987
|
-
rmdirSync,
|
|
6988
|
-
unlinkSync,
|
|
6989
6813
|
writeFileSync as writeFileSync4
|
|
6990
6814
|
} from "node:fs";
|
|
6991
|
-
import { dirname as dirname4,
|
|
6992
|
-
var SKILLS_MANIFEST_PATH = join5(".moda", "skills.yml");
|
|
6993
|
-
var SKILL_SOURCE_HARNESS = "moda";
|
|
6994
|
-
var SKILL_SOURCE_INTEGRATION_BUNDLED = "integration-bundled";
|
|
6995
|
-
function readSkillsManifest(cwd) {
|
|
6996
|
-
const manifestPath = join5(cwd, SKILLS_MANIFEST_PATH);
|
|
6997
|
-
if (!existsSync5(manifestPath))
|
|
6998
|
-
return null;
|
|
6999
|
-
let text = "";
|
|
7000
|
-
try {
|
|
7001
|
-
text = readFileSync3(manifestPath, "utf8");
|
|
7002
|
-
} catch {
|
|
7003
|
-
return null;
|
|
7004
|
-
}
|
|
7005
|
-
return {
|
|
7006
|
-
tenantId: readYamlScalar(text, "tenant_id"),
|
|
7007
|
-
generationRunId: readYamlScalar(text, "generation_run_id"),
|
|
7008
|
-
generationVersion: Number(readYamlScalar(text, "generation_version") || 0),
|
|
7009
|
-
releaseKey: readYamlScalar(text, "release_key"),
|
|
7010
|
-
statusFilter: readYamlScalar(text, "status_filter"),
|
|
7011
|
-
releasedAt: readYamlScalar(text, "released_at"),
|
|
7012
|
-
managedPaths: readManagedPaths(text),
|
|
7013
|
-
records: readSkillRecords(text)
|
|
7014
|
-
};
|
|
7015
|
-
}
|
|
7016
|
-
function manifestHeaderFrom(manifest) {
|
|
7017
|
-
return {
|
|
7018
|
-
tenantId: manifest?.tenantId ?? "",
|
|
7019
|
-
releaseKey: manifest?.releaseKey ?? "",
|
|
7020
|
-
statusFilter: manifest?.statusFilter ?? "",
|
|
7021
|
-
generationRunId: manifest?.generationRunId ?? "",
|
|
7022
|
-
generationVersion: manifest?.generationVersion ?? 0,
|
|
7023
|
-
releasedAt: manifest?.releasedAt ?? ""
|
|
7024
|
-
};
|
|
7025
|
-
}
|
|
7026
|
-
function writeSkillsManifest(cwd, header, records) {
|
|
7027
|
-
const manifestPath = join5(cwd, SKILLS_MANIFEST_PATH);
|
|
7028
|
-
const managedPaths = [...new Set(records.flatMap((record) => record.paths))].sort();
|
|
7029
|
-
const lines = [
|
|
7030
|
-
"version: 1",
|
|
7031
|
-
"source: moda",
|
|
7032
|
-
`tenant_id: ${yamlQuote(header.tenantId)}`,
|
|
7033
|
-
`release_key: ${yamlQuote(header.releaseKey)}`,
|
|
7034
|
-
`status_filter: ${yamlQuote(header.statusFilter)}`,
|
|
7035
|
-
`generation_run_id: ${yamlQuote(header.generationRunId)}`,
|
|
7036
|
-
`generation_version: ${header.generationVersion}`,
|
|
7037
|
-
`released_at: ${yamlQuote(header.releasedAt)}`,
|
|
7038
|
-
`installed_at: ${yamlQuote(new Date().toISOString())}`,
|
|
7039
|
-
"managed_paths:",
|
|
7040
|
-
...managedPaths.map((path) => ` - ${yamlQuote(path)}`),
|
|
7041
|
-
"skills:",
|
|
7042
|
-
...records.flatMap((record) => [
|
|
7043
|
-
` - skill_id: ${yamlQuote(record.skillId)}`,
|
|
7044
|
-
` content_hash: ${yamlQuote(record.contentHash)}`,
|
|
7045
|
-
` source: ${yamlQuote(record.source)}`,
|
|
7046
|
-
" paths:",
|
|
7047
|
-
...record.paths.map((path) => ` - ${yamlQuote(path)}`)
|
|
7048
|
-
]),
|
|
7049
|
-
""
|
|
7050
|
-
];
|
|
7051
|
-
mkdirSync3(dirname4(manifestPath), { recursive: true });
|
|
7052
|
-
writeFileSync4(manifestPath, lines.join(`
|
|
7053
|
-
`));
|
|
7054
|
-
}
|
|
7055
|
-
function upsertSkillManifestRecord(cwd, record) {
|
|
7056
|
-
const manifest = readSkillsManifest(cwd);
|
|
7057
|
-
const records = (manifest?.records ?? []).filter((existing) => !(existing.skillId === record.skillId && existing.source === record.source));
|
|
7058
|
-
records.push(record);
|
|
7059
|
-
writeSkillsManifest(cwd, manifestHeaderFrom(manifest), records);
|
|
7060
|
-
}
|
|
7061
|
-
function pruneManagedSkillPaths(cwd, managedPaths, log) {
|
|
7062
|
-
let pruned = 0;
|
|
7063
|
-
for (const rawPath of [...new Set(managedPaths)]) {
|
|
7064
|
-
const safePath = safeManagedRelativePath(rawPath);
|
|
7065
|
-
if (!safePath)
|
|
7066
|
-
continue;
|
|
7067
|
-
const absolutePath = absoluteManagedPath(cwd, safePath);
|
|
7068
|
-
if (!absolutePath || !existsSync5(absolutePath))
|
|
7069
|
-
continue;
|
|
7070
|
-
try {
|
|
7071
|
-
unlinkSync(absolutePath);
|
|
7072
|
-
pruned++;
|
|
7073
|
-
log(` pruned ${safePath}`);
|
|
7074
|
-
pruneEmptyClaudeSkillDir(cwd, safePath);
|
|
7075
|
-
} catch {}
|
|
7076
|
-
}
|
|
7077
|
-
return pruned;
|
|
7078
|
-
}
|
|
7079
|
-
function pruneEmptyClaudeSkillDir(cwd, relativePath) {
|
|
7080
|
-
if (!relativePath.startsWith(".claude/skills/") || !relativePath.endsWith("/SKILL.md"))
|
|
7081
|
-
return;
|
|
7082
|
-
const dir = dirname4(join5(cwd, relativePath));
|
|
7083
|
-
try {
|
|
7084
|
-
if (existsSync5(dir) && readdirSync(dir).length === 0) {
|
|
7085
|
-
rmdirSync(dir);
|
|
7086
|
-
}
|
|
7087
|
-
} catch {}
|
|
7088
|
-
}
|
|
7089
|
-
function absoluteManagedPath(cwd, relativePath) {
|
|
7090
|
-
const abs = resolve5(cwd, relativePath);
|
|
7091
|
-
const root = resolve5(cwd);
|
|
7092
|
-
const rel = relative2(root, abs);
|
|
7093
|
-
if (rel.startsWith("..") || isAbsolute4(rel))
|
|
7094
|
-
return null;
|
|
7095
|
-
return abs;
|
|
7096
|
-
}
|
|
7097
|
-
function safeManagedRelativePath(rawPath) {
|
|
7098
|
-
if (!rawPath || rawPath.includes("\x00") || isAbsolute4(rawPath))
|
|
7099
|
-
return null;
|
|
7100
|
-
const normalized = normalize(rawPath).replace(/\\/g, "/");
|
|
7101
|
-
if (normalized.startsWith("../") || normalized === ".." || normalized.includes("/../"))
|
|
7102
|
-
return null;
|
|
7103
|
-
const isClaudeSkill = normalized.startsWith(".claude/skills/") && normalized.endsWith("/SKILL.md");
|
|
7104
|
-
const isCursorRule = normalized.startsWith(".cursor/rules/") && normalized.endsWith(".mdc");
|
|
7105
|
-
return isClaudeSkill || isCursorRule ? normalized : null;
|
|
7106
|
-
}
|
|
7107
|
-
function readYamlScalar(text, key) {
|
|
7108
|
-
const match = text.match(new RegExp(`^${key}:\\s*(.*?)\\s*$`, "m"));
|
|
7109
|
-
return match ? parseYamlScalar(match[1]) : "";
|
|
7110
|
-
}
|
|
7111
|
-
function readSkillRecords(text) {
|
|
7112
|
-
const records = [];
|
|
7113
|
-
let inSkills = false;
|
|
7114
|
-
let current = null;
|
|
7115
|
-
let inPaths = false;
|
|
7116
|
-
for (const line of text.split(/\r?\n/)) {
|
|
7117
|
-
if (line.trim() === "skills:") {
|
|
7118
|
-
inSkills = true;
|
|
7119
|
-
continue;
|
|
7120
|
-
}
|
|
7121
|
-
if (!inSkills)
|
|
7122
|
-
continue;
|
|
7123
|
-
if (line.length > 0 && !line.startsWith(" "))
|
|
7124
|
-
break;
|
|
7125
|
-
const skillIdMatch = line.match(/^ {2}- skill_id:\s*(.*?)\s*$/);
|
|
7126
|
-
if (skillIdMatch) {
|
|
7127
|
-
if (current)
|
|
7128
|
-
records.push(current);
|
|
7129
|
-
current = {
|
|
7130
|
-
skillId: parseYamlScalar(skillIdMatch[1]),
|
|
7131
|
-
contentHash: "",
|
|
7132
|
-
source: SKILL_SOURCE_HARNESS,
|
|
7133
|
-
paths: []
|
|
7134
|
-
};
|
|
7135
|
-
inPaths = false;
|
|
7136
|
-
continue;
|
|
7137
|
-
}
|
|
7138
|
-
if (!current)
|
|
7139
|
-
continue;
|
|
7140
|
-
const hashMatch = line.match(/^ {4}content_hash:\s*(.*?)\s*$/);
|
|
7141
|
-
if (hashMatch) {
|
|
7142
|
-
current.contentHash = parseYamlScalar(hashMatch[1]);
|
|
7143
|
-
continue;
|
|
7144
|
-
}
|
|
7145
|
-
const sourceMatch = line.match(/^ {4}source:\s*(.*?)\s*$/);
|
|
7146
|
-
if (sourceMatch) {
|
|
7147
|
-
current.source = parseYamlScalar(sourceMatch[1]) || SKILL_SOURCE_HARNESS;
|
|
7148
|
-
continue;
|
|
7149
|
-
}
|
|
7150
|
-
if (/^ {4}paths:\s*$/.test(line)) {
|
|
7151
|
-
inPaths = true;
|
|
7152
|
-
continue;
|
|
7153
|
-
}
|
|
7154
|
-
const pathMatch = inPaths ? line.match(/^ {6}-\s*(.*?)\s*$/) : null;
|
|
7155
|
-
if (pathMatch) {
|
|
7156
|
-
const value = parseYamlScalar(pathMatch[1]);
|
|
7157
|
-
if (value)
|
|
7158
|
-
current.paths.push(value);
|
|
7159
|
-
}
|
|
7160
|
-
}
|
|
7161
|
-
if (current)
|
|
7162
|
-
records.push(current);
|
|
7163
|
-
return records;
|
|
7164
|
-
}
|
|
7165
|
-
function readManagedPaths(text) {
|
|
7166
|
-
const paths = [];
|
|
7167
|
-
let inManagedPaths = false;
|
|
7168
|
-
for (const line of text.split(/\r?\n/)) {
|
|
7169
|
-
if (line.trim() === "managed_paths:") {
|
|
7170
|
-
inManagedPaths = true;
|
|
7171
|
-
continue;
|
|
7172
|
-
}
|
|
7173
|
-
if (inManagedPaths && line.length > 0 && !line.startsWith(" "))
|
|
7174
|
-
break;
|
|
7175
|
-
if (!inManagedPaths)
|
|
7176
|
-
continue;
|
|
7177
|
-
const match = line.match(/^\s*-\s*(.*?)\s*$/);
|
|
7178
|
-
if (match)
|
|
7179
|
-
paths.push(parseYamlScalar(match[1]));
|
|
7180
|
-
}
|
|
7181
|
-
return paths.filter(Boolean);
|
|
7182
|
-
}
|
|
7183
|
-
function parseYamlScalar(raw) {
|
|
7184
|
-
const value = raw.trim();
|
|
7185
|
-
if (!value)
|
|
7186
|
-
return "";
|
|
7187
|
-
if (value.startsWith('"')) {
|
|
7188
|
-
try {
|
|
7189
|
-
return String(JSON.parse(value));
|
|
7190
|
-
} catch {
|
|
7191
|
-
return value.replace(/^"|"$/g, "");
|
|
7192
|
-
}
|
|
7193
|
-
}
|
|
7194
|
-
return value.replace(/^'|'$/g, "");
|
|
7195
|
-
}
|
|
7196
|
-
function yamlQuote(value) {
|
|
7197
|
-
return JSON.stringify(value);
|
|
7198
|
-
}
|
|
6815
|
+
import { basename, dirname as dirname4, join as join3, relative as relative2 } from "node:path";
|
|
7199
6816
|
|
|
7200
6817
|
// src/agents-md.ts
|
|
7201
|
-
import { existsSync as
|
|
7202
|
-
import { dirname as
|
|
6818
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6819
|
+
import { dirname as dirname3, join as join2 } from "node:path";
|
|
7203
6820
|
var AGENTS_MD_FILENAME = "AGENTS.md";
|
|
7204
6821
|
var FIXES_BLOCK_BEGIN = "<!-- moda:fixes:begin -->";
|
|
7205
6822
|
var FIXES_BLOCK_END = "<!-- moda:fixes:end -->";
|
|
@@ -7245,26 +6862,26 @@ function upsertManagedBlock(existing, block) {
|
|
|
7245
6862
|
}
|
|
7246
6863
|
function writeFileIfChanged(filePath, content) {
|
|
7247
6864
|
try {
|
|
7248
|
-
|
|
7249
|
-
if (
|
|
7250
|
-
const current =
|
|
6865
|
+
mkdirSync2(dirname3(filePath), { recursive: true });
|
|
6866
|
+
if (existsSync3(filePath)) {
|
|
6867
|
+
const current = readFileSync2(filePath, "utf8");
|
|
7251
6868
|
if (current === content)
|
|
7252
6869
|
return "unchanged";
|
|
7253
|
-
|
|
6870
|
+
writeFileSync3(filePath, content);
|
|
7254
6871
|
return "updated";
|
|
7255
6872
|
}
|
|
7256
|
-
|
|
6873
|
+
writeFileSync3(filePath, content);
|
|
7257
6874
|
return "wrote";
|
|
7258
6875
|
} catch {
|
|
7259
6876
|
return "failed";
|
|
7260
6877
|
}
|
|
7261
6878
|
}
|
|
7262
6879
|
function upsertFixesAgentsBlock(cwd) {
|
|
7263
|
-
const filePath =
|
|
6880
|
+
const filePath = join2(cwd, AGENTS_MD_FILENAME);
|
|
7264
6881
|
let existing = null;
|
|
7265
|
-
if (
|
|
6882
|
+
if (existsSync3(filePath)) {
|
|
7266
6883
|
try {
|
|
7267
|
-
existing =
|
|
6884
|
+
existing = readFileSync2(filePath, "utf8");
|
|
7268
6885
|
} catch {
|
|
7269
6886
|
return { path: AGENTS_MD_FILENAME, status: "failed" };
|
|
7270
6887
|
}
|
|
@@ -7276,7 +6893,7 @@ function upsertFixesAgentsBlock(cwd) {
|
|
|
7276
6893
|
// src/skills.ts
|
|
7277
6894
|
var SKILLS_REQUEST_TIMEOUT_MS = 15000;
|
|
7278
6895
|
var VALID_STATUSES = new Set(["approved", "proposed", "all"]);
|
|
7279
|
-
var SKILL_PROPOSALS_MANIFEST_PATH =
|
|
6896
|
+
var SKILL_PROPOSALS_MANIFEST_PATH = join3(".moda", "skill-proposals.yml");
|
|
7280
6897
|
async function fetchSkills(status) {
|
|
7281
6898
|
const apiKey = resolveApiKey();
|
|
7282
6899
|
if (!apiKey) {
|
|
@@ -7401,15 +7018,15 @@ function formatRunStatusHuman(run, events) {
|
|
|
7401
7018
|
}
|
|
7402
7019
|
function writeIfChanged(filePath, content) {
|
|
7403
7020
|
try {
|
|
7404
|
-
|
|
7405
|
-
if (
|
|
7406
|
-
const current =
|
|
7021
|
+
mkdirSync3(dirname4(filePath), { recursive: true });
|
|
7022
|
+
if (existsSync4(filePath)) {
|
|
7023
|
+
const current = readFileSync3(filePath, "utf8");
|
|
7407
7024
|
if (current === content)
|
|
7408
7025
|
return "unchanged";
|
|
7409
|
-
|
|
7026
|
+
writeFileSync4(filePath, content);
|
|
7410
7027
|
return "updated";
|
|
7411
7028
|
}
|
|
7412
|
-
|
|
7029
|
+
writeFileSync4(filePath, content);
|
|
7413
7030
|
return "wrote";
|
|
7414
7031
|
} catch {
|
|
7415
7032
|
return "failed";
|
|
@@ -7446,7 +7063,7 @@ async function pullSkills(options = {}) {
|
|
|
7446
7063
|
}
|
|
7447
7064
|
}
|
|
7448
7065
|
const agents = detectAgents();
|
|
7449
|
-
const writeCursor = agents.includes("Cursor") ||
|
|
7066
|
+
const writeCursor = agents.includes("Cursor") || existsSync4(join3(cwd, ".cursor"));
|
|
7450
7067
|
const records = [];
|
|
7451
7068
|
for (const skill of skills) {
|
|
7452
7069
|
if (!skill.skill_id || !skill.skill_md) {
|
|
@@ -7459,7 +7076,7 @@ async function pullSkills(options = {}) {
|
|
|
7459
7076
|
log(` skipped invalid skill_id: ${JSON.stringify(skill.skill_id).slice(0, 80)}`);
|
|
7460
7077
|
continue;
|
|
7461
7078
|
}
|
|
7462
|
-
const claudePath =
|
|
7079
|
+
const claudePath = join3(cwd, ".claude", "skills", safeId, "SKILL.md");
|
|
7463
7080
|
const claudeStatus = writeIfChanged(claudePath, skill.skill_md);
|
|
7464
7081
|
const claudeRelativePath = `.claude/skills/${safeId}/SKILL.md`;
|
|
7465
7082
|
recordOutcome(summary, claudeStatus, log, claudeRelativePath);
|
|
@@ -7467,7 +7084,7 @@ async function pullSkills(options = {}) {
|
|
|
7467
7084
|
if (claudeStatus !== "failed")
|
|
7468
7085
|
paths.push(claudeRelativePath);
|
|
7469
7086
|
if (writeCursor) {
|
|
7470
|
-
const cursorPath =
|
|
7087
|
+
const cursorPath = join3(cwd, ".cursor", "rules", `${safeId}.mdc`);
|
|
7471
7088
|
const cursorStatus = writeIfChanged(cursorPath, claudeSkillToCursorRule(skill.skill_md));
|
|
7472
7089
|
const cursorRelativePath = `.cursor/rules/${safeId}.mdc`;
|
|
7473
7090
|
recordOutcome(summary, cursorStatus, log, cursorRelativePath);
|
|
@@ -7505,7 +7122,7 @@ function recordOutcome(summary, status, log, relativePath) {
|
|
|
7505
7122
|
break;
|
|
7506
7123
|
}
|
|
7507
7124
|
}
|
|
7508
|
-
var SKILLS_DIR =
|
|
7125
|
+
var SKILLS_DIR = join3(".claude", "skills");
|
|
7509
7126
|
var SKILL_FILE = "SKILL.md";
|
|
7510
7127
|
var MAX_SKILL_WALK_DEPTH = 4;
|
|
7511
7128
|
async function runSkillSync(flags = {}, profileOptions = {}, options = {}) {
|
|
@@ -7536,21 +7153,21 @@ async function runSkillSync(flags = {}, profileOptions = {}, options = {}) {
|
|
|
7536
7153
|
return { synced: response.synced ?? [], dryRun, ...agentsMd ? { agentsMd } : {} };
|
|
7537
7154
|
}
|
|
7538
7155
|
function discoverLocalSkills(cwd) {
|
|
7539
|
-
const skillsRoot =
|
|
7540
|
-
if (!
|
|
7156
|
+
const skillsRoot = join3(cwd, SKILLS_DIR);
|
|
7157
|
+
if (!existsSync4(skillsRoot))
|
|
7541
7158
|
return [];
|
|
7542
7159
|
const managed = new Set(managedSkillPaths(cwd));
|
|
7543
7160
|
const skills = [];
|
|
7544
7161
|
for (const absPath of walkSkillFiles(skillsRoot, 0)) {
|
|
7545
|
-
const sourcePath =
|
|
7162
|
+
const sourcePath = relative2(cwd, absPath).replace(/\\/g, "/");
|
|
7546
7163
|
if (managed.has(sourcePath))
|
|
7547
7164
|
continue;
|
|
7548
|
-
const key = basename(
|
|
7165
|
+
const key = basename(dirname4(absPath));
|
|
7549
7166
|
if (key === "moda-cli" || !/^[A-Za-z0-9_-]+$/.test(key))
|
|
7550
7167
|
continue;
|
|
7551
7168
|
let content;
|
|
7552
7169
|
try {
|
|
7553
|
-
content =
|
|
7170
|
+
content = readFileSync3(absPath, "utf8");
|
|
7554
7171
|
} catch {
|
|
7555
7172
|
continue;
|
|
7556
7173
|
}
|
|
@@ -7579,13 +7196,13 @@ function walkSkillFiles(dir, depth) {
|
|
|
7579
7196
|
return [];
|
|
7580
7197
|
let entries;
|
|
7581
7198
|
try {
|
|
7582
|
-
entries =
|
|
7199
|
+
entries = readdirSync(dir);
|
|
7583
7200
|
} catch {
|
|
7584
7201
|
return [];
|
|
7585
7202
|
}
|
|
7586
7203
|
const files = [];
|
|
7587
7204
|
for (const entry of entries) {
|
|
7588
|
-
const fullPath =
|
|
7205
|
+
const fullPath = join3(dir, entry);
|
|
7589
7206
|
let stat;
|
|
7590
7207
|
try {
|
|
7591
7208
|
stat = lstatSync2(fullPath);
|
|
@@ -7659,7 +7276,7 @@ async function applySkillProposal(proposalId, flags, profileOptions = {}, option
|
|
|
7659
7276
|
}
|
|
7660
7277
|
const safeId = safeSkillId(proposal.skillId);
|
|
7661
7278
|
const relativePath = `.claude/skills/${safeId}/SKILL.md`;
|
|
7662
|
-
const writeStatus = writeIfChanged(
|
|
7279
|
+
const writeStatus = writeIfChanged(join3(cwd, relativePath), proposal.skillMd);
|
|
7663
7280
|
if (writeStatus === "failed") {
|
|
7664
7281
|
throw new Error(`Failed to write ${relativePath}`);
|
|
7665
7282
|
}
|
|
@@ -7692,9 +7309,9 @@ function safeSkillId(skillId) {
|
|
|
7692
7309
|
return safeId;
|
|
7693
7310
|
}
|
|
7694
7311
|
function writeSkillProposalManifest(cwd, proposal, relativePath) {
|
|
7695
|
-
const manifestPath =
|
|
7696
|
-
|
|
7697
|
-
const existing =
|
|
7312
|
+
const manifestPath = join3(cwd, SKILL_PROPOSALS_MANIFEST_PATH);
|
|
7313
|
+
mkdirSync3(dirname4(manifestPath), { recursive: true });
|
|
7314
|
+
const existing = existsSync4(manifestPath) ? readFileSync3(manifestPath, "utf8") : `applied_proposals:
|
|
7698
7315
|
`;
|
|
7699
7316
|
const entry = [
|
|
7700
7317
|
` - proposal_id: "${escapeYamlString(proposal.proposalId)}"`,
|
|
@@ -7713,7 +7330,7 @@ function writeSkillProposalManifest(cwd, proposal, relativePath) {
|
|
|
7713
7330
|
const prefix = existing.endsWith(`
|
|
7714
7331
|
`) ? existing : `${existing}
|
|
7715
7332
|
`;
|
|
7716
|
-
|
|
7333
|
+
writeFileSync4(manifestPath, `${prefix}${entry}
|
|
7717
7334
|
`);
|
|
7718
7335
|
}
|
|
7719
7336
|
function escapeYamlString(value) {
|
|
@@ -7825,8 +7442,11 @@ async function runSkillsCommand(input) {
|
|
|
7825
7442
|
});
|
|
7826
7443
|
return 0;
|
|
7827
7444
|
}
|
|
7445
|
+
if (sub === "install") {
|
|
7446
|
+
return runSkillsInstall(context, positionals.slice(1));
|
|
7447
|
+
}
|
|
7828
7448
|
if (sub !== "pull") {
|
|
7829
|
-
throw new CliInputError(sub ? `Unknown skills subcommand '${sub}'` : "A skills subcommand is required", "Usage: moda skills gen [--source=all|sdk] [--max-sessions=N] [--start-at=ISO] [--end-at=ISO] [--replay] [--replay-set-id=ID] [--reprocess-segments] [--force-reprocess] [--wait] | moda skills status [run-id] | moda skills proposals list [--status=ready_for_pr] | moda skills proposal apply <proposal-id> | moda skills pull [--status=approved|proposed|all] | moda skills sync [--dry-run]");
|
|
7449
|
+
throw new CliInputError(sub ? `Unknown skills subcommand '${sub}'` : "A skills subcommand is required", "Usage: moda skills gen [--source=all|sdk] [--max-sessions=N] [--start-at=ISO] [--end-at=ISO] [--replay] [--replay-set-id=ID] [--reprocess-segments] [--force-reprocess] [--wait] | moda skills status [run-id] | moda skills proposals list [--status=ready_for_pr] | moda skills proposal apply <proposal-id> | moda skills pull [--status=approved|proposed|all] | moda skills sync [--dry-run] | moda skills install [--list] <id...>");
|
|
7830
7450
|
}
|
|
7831
7451
|
const status = (flags.status ?? "approved").toLowerCase();
|
|
7832
7452
|
if (!VALID_STATUSES.has(status)) {
|
|
@@ -7949,11 +7569,59 @@ function manifestHeaderFor(fetched) {
|
|
|
7949
7569
|
function sha2562(input) {
|
|
7950
7570
|
return createHash2("sha256").update(input).digest("hex");
|
|
7951
7571
|
}
|
|
7572
|
+
async function runSkillsInstall(context, ids) {
|
|
7573
|
+
const { installIntegrationSkill, readBundledIndex, readBundledSkillMd } = await import("./skills-bundled-mdmhzfsr.js");
|
|
7574
|
+
const index = readBundledIndex();
|
|
7575
|
+
if (context.flags.list === "true" || ids.length === 0) {
|
|
7576
|
+
if (!index) {
|
|
7577
|
+
throw new CliInputError("This CLI build has no bundled integration guides", "Install a published @moda-ai/cli, or run `bun run bundle:skills` in apps/moda-cli.");
|
|
7578
|
+
}
|
|
7579
|
+
const skills = index.skills.map((skill) => ({
|
|
7580
|
+
id: skill.id,
|
|
7581
|
+
version: skill.version,
|
|
7582
|
+
language: skill.targets.language,
|
|
7583
|
+
summary: skill.summary
|
|
7584
|
+
}));
|
|
7585
|
+
if (context.outputMode !== "human") {
|
|
7586
|
+
context.output.writeData({ skills });
|
|
7587
|
+
} else {
|
|
7588
|
+
for (const skill of skills)
|
|
7589
|
+
console.log(`${skill.id} ${skill.version} ${skill.summary}`);
|
|
7590
|
+
if (ids.length === 0 && context.flags.list !== "true") {
|
|
7591
|
+
console.error("Install one or more with: moda skills install <id...>");
|
|
7592
|
+
}
|
|
7593
|
+
}
|
|
7594
|
+
return 0;
|
|
7595
|
+
}
|
|
7596
|
+
const unknown = ids.filter((id) => !/^[a-z0-9-]+$/.test(id) || readBundledSkillMd(id) === null);
|
|
7597
|
+
if (unknown.length > 0) {
|
|
7598
|
+
throw new CliInputError(`Not bundled with this CLI: ${unknown.map((id) => JSON.stringify(id).slice(0, 80)).join(", ")}`, "List the bundled guides with: moda skills install --list");
|
|
7599
|
+
}
|
|
7600
|
+
const results = [];
|
|
7601
|
+
for (const id of ids) {
|
|
7602
|
+
try {
|
|
7603
|
+
results.push(installIntegrationSkill(id));
|
|
7604
|
+
} catch (error) {
|
|
7605
|
+
const done = results.map((result) => result.skillId);
|
|
7606
|
+
throw new CliInputError(error instanceof Error ? error.message : String(error), done.length > 0 ? `Already installed before the failure: ${done.join(", ")}. Fix the error and rerun; installed guides are left as they are.` : "List the bundled guides with: moda skills install --list");
|
|
7607
|
+
}
|
|
7608
|
+
}
|
|
7609
|
+
if (context.outputMode !== "human") {
|
|
7610
|
+
context.output.writeData({ installed: results });
|
|
7611
|
+
} else {
|
|
7612
|
+
for (const result of results) {
|
|
7613
|
+
console.log(`${result.status} ${result.skillId}`);
|
|
7614
|
+
for (const path of result.paths)
|
|
7615
|
+
console.log(` ${path}`);
|
|
7616
|
+
}
|
|
7617
|
+
}
|
|
7618
|
+
return 0;
|
|
7619
|
+
}
|
|
7952
7620
|
|
|
7953
7621
|
// src/doctor.ts
|
|
7954
7622
|
import { createHash as createHash3 } from "node:crypto";
|
|
7955
|
-
import { existsSync as
|
|
7956
|
-
import { join as
|
|
7623
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, statSync } from "node:fs";
|
|
7624
|
+
import { join as join4 } from "node:path";
|
|
7957
7625
|
async function runDoctorCommand(context) {
|
|
7958
7626
|
const report = await buildDoctorReport(context, {
|
|
7959
7627
|
offline: context.flags.offline === "true"
|
|
@@ -8076,8 +7744,8 @@ async function buildDoctorReport(context, options) {
|
|
|
8076
7744
|
checks.push(urlCheck("base_url_valid", "config", "Base URL is valid", baseUrl));
|
|
8077
7745
|
checks.push(urlCheck("ingest_url_valid", "config", "Ingest URL is valid", ingestUrl));
|
|
8078
7746
|
checks.push(localBaseUrlCheck(activeProfile.environment, baseUrl));
|
|
8079
|
-
const manifestPath =
|
|
8080
|
-
const manifestExists =
|
|
7747
|
+
const manifestPath = join4(context.cwd, ".moda", "harness.json");
|
|
7748
|
+
const manifestExists = existsSync5(manifestPath);
|
|
8081
7749
|
checks.push({
|
|
8082
7750
|
id: "harness_manifest_exists",
|
|
8083
7751
|
group: "harness",
|
|
@@ -8161,7 +7829,7 @@ function authTokenCheck(context, hasApiKey) {
|
|
|
8161
7829
|
};
|
|
8162
7830
|
}
|
|
8163
7831
|
function configPermissionCheck(path) {
|
|
8164
|
-
if (!
|
|
7832
|
+
if (!existsSync5(path)) {
|
|
8165
7833
|
return {
|
|
8166
7834
|
id: "config_file_permissions",
|
|
8167
7835
|
group: "config",
|
|
@@ -8304,7 +7972,7 @@ function harnessCurrentCheck(cwd, manifestPath, manifestExists, tenantId) {
|
|
|
8304
7972
|
tenantId,
|
|
8305
7973
|
tenantSource: tenantId ? "env_or_config" : undefined
|
|
8306
7974
|
});
|
|
8307
|
-
const saved = JSON.parse(
|
|
7975
|
+
const saved = JSON.parse(readFileSync4(manifestPath, "utf8"));
|
|
8308
7976
|
const savedHash = stableGraphHash(saved);
|
|
8309
7977
|
const currentHash = stableGraphHash(current);
|
|
8310
7978
|
const matches = savedHash === currentHash;
|
|
@@ -8364,7 +8032,7 @@ function harnessSyncStateCheck(cwd) {
|
|
|
8364
8032
|
};
|
|
8365
8033
|
}
|
|
8366
8034
|
function discoveryCheck(id, group, title, cwd, relativePaths, nextCommand) {
|
|
8367
|
-
const found = relativePaths.filter((path) =>
|
|
8035
|
+
const found = relativePaths.filter((path) => existsSync5(join4(cwd, path)));
|
|
8368
8036
|
return {
|
|
8369
8037
|
id,
|
|
8370
8038
|
group,
|
|
@@ -8681,4 +8349,4 @@ function isRecord(value) {
|
|
|
8681
8349
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8682
8350
|
}
|
|
8683
8351
|
|
|
8684
|
-
export { DEFAULT_POLL_INTERVAL_MS, DEFAULT_WAIT_TIMEOUT_MS, fetchLatestRun, runPromptsCommand, initPrompts, runPromptSync,
|
|
8352
|
+
export { DEFAULT_POLL_INTERVAL_MS, DEFAULT_WAIT_TIMEOUT_MS, fetchLatestRun, runPromptsCommand, initPrompts, runPromptSync, runSkillSync, runSkillsCommand, runDoctorCommand, runStatusCommand, buildDoctorReport };
|