@moda-ai/cli 1.25.0 → 1.27.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 +62 -24
- package/dist/{cli-vc7zddj1.js → cli-59yacef3.js} +14 -1
- package/dist/{cli-8s69sg7f.js → cli-9xw5bq1j.js} +1 -1
- package/dist/{cli-kqvjt12y.js → cli-cjqzrp9k.js} +127 -46
- package/dist/{cli-jntzpem2.js → cli-g931ntph.js} +2 -2
- package/dist/{cli-tdttb4cv.js → cli-pq6rte0w.js} +2 -2
- package/dist/cli.js +1473 -73
- package/dist/{harness-github-actions-rjyc9a7z.js → harness-github-actions-ct8fwzdr.js} +2 -2
- package/dist/{index-fm9ey3ks.js → index-fwxpt51s.js} +5 -5
- package/dist/{provision-dphktm75.js → provision-r0cq3g2v.js} +3 -3
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +80 -12
package/README.md
CHANGED
|
@@ -34,49 +34,87 @@ Get your API key from [moda.dev/settings](https://moda.dev/settings).
|
|
|
34
34
|
|
|
35
35
|
## Usage
|
|
36
36
|
|
|
37
|
+
Find things by meaning, then drill in:
|
|
38
|
+
|
|
37
39
|
```bash
|
|
38
|
-
moda
|
|
39
|
-
moda
|
|
40
|
+
moda search "user wants a refund" --mode=semantic # Semantic/keyword/hybrid message search
|
|
41
|
+
moda search "stripe.charges.create" --mode=keyword # Exact identifiers, incl. tool calls
|
|
42
|
+
moda search "checkout" --include-tool-io # Search tool inputs/outputs too
|
|
43
|
+
moda context <conversation_id> --msg-index=5 # Read the exact turn
|
|
44
|
+
moda audit <conversation_id|trace_id> # Raw span/trace audit
|
|
45
|
+
```
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
moda clusters --parent-id=node-abc # Drill into a category
|
|
47
|
+
Production intelligence:
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
```bash
|
|
50
|
+
moda overview --days-back=30 # Health briefing (KPIs, clusters, activity)
|
|
51
|
+
moda ask "what should I fix first?" # Grounded answer with evidence
|
|
52
|
+
moda investigate # Ranked issues worth fixing
|
|
53
|
+
moda problems # Cross-signal Problems by root cause
|
|
54
|
+
moda problem <problem_id> # One Problem: full dossier
|
|
55
|
+
moda problem <problem_id> --evidence # ...attribution evidence (keyset paged)
|
|
56
|
+
moda problem <problem_id> --reports # ...investigation reports
|
|
57
|
+
moda problem <problem_id> --conversations # ...affected conversations
|
|
58
|
+
moda problem-feedback <problem_id> --action=mark_fixed # Close the loop from the terminal
|
|
59
|
+
```
|
|
45
60
|
|
|
46
|
-
|
|
47
|
-
moda conversations --time-range=24h # Filter by time
|
|
48
|
-
moda conversations --environment=production # Filter by environment
|
|
61
|
+
Signals:
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
moda
|
|
63
|
+
```bash
|
|
64
|
+
moda emotions --family=confusion # Multi-family emotion detections
|
|
65
|
+
moda hallucinations --kind=contradicted # Grounding: contradicted/verified outputs
|
|
66
|
+
moda frustrations --include-window --window=1 # Legacy single-family frustration view
|
|
67
|
+
moda tool-failures # Tool failure overview
|
|
68
|
+
moda tool-failure-detail <tool_name> --include-window
|
|
69
|
+
moda step-scores <conversation_id> # Graph-PRM per-step reward curves
|
|
70
|
+
```
|
|
52
71
|
|
|
53
|
-
|
|
54
|
-
moda frustrations --days-back=14 --limit=20
|
|
55
|
-
moda frustrations --include-window --window=1 # Attach 3-message slice per row
|
|
72
|
+
Conversations, clusters, memory:
|
|
56
73
|
|
|
57
|
-
|
|
58
|
-
moda
|
|
59
|
-
moda
|
|
74
|
+
```bash
|
|
75
|
+
moda conversations --search="error" --environment=production
|
|
76
|
+
moda clusters # Walk the topic hierarchy
|
|
77
|
+
moda clusters --search="billing disputes" # Find a cluster by meaning
|
|
78
|
+
moda world-state <conversation_id> # Agent memory (slots/threads/events)
|
|
79
|
+
moda world-state <id> --snapshot --msg-index=42 # What the agent believed at turn 42
|
|
80
|
+
moda world-state <id> --replay --message-count=50 # State evolution frame by frame
|
|
81
|
+
```
|
|
60
82
|
|
|
61
|
-
|
|
62
|
-
moda feedback "..." --category=missing_data --conversation-id=<id>
|
|
83
|
+
Live tail (one JSON line per new item — `tail -f` for your agent):
|
|
63
84
|
|
|
64
|
-
|
|
85
|
+
```bash
|
|
86
|
+
moda tail # New conversations, every 15s
|
|
87
|
+
moda tail --signal=all --interval=30 # Conversations + emotion detections
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Prompt management:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
65
93
|
moda prompts init # Create .moda/prompts.yml
|
|
66
94
|
moda prompts status # Read-only local prompt status
|
|
67
|
-
moda prompts diff # Read-only local prompt diff/status
|
|
68
95
|
moda prompts sync # Upload changed prompt versions
|
|
69
|
-
moda prompts sync --watch # Sync when local prompt hashes change
|
|
70
96
|
moda prompts promote support.triage --label=prod --version=pver_abc123
|
|
71
97
|
```
|
|
72
98
|
|
|
73
|
-
|
|
99
|
+
Feedback:
|
|
74
100
|
|
|
75
101
|
```bash
|
|
76
|
-
moda
|
|
77
|
-
moda frustrations | jq '.frustrations[].primary_cause'
|
|
102
|
+
moda feedback "cluster label looks wrong" --category=bad_cluster_label
|
|
78
103
|
```
|
|
79
104
|
|
|
105
|
+
Output adapts to where it runs: piped/CI/agent environments get schema-versioned
|
|
106
|
+
JSON (`moda.agent.v1`), an interactive terminal gets human prose. Force a mode
|
|
107
|
+
with `--json`, `--agent`, or `--human`. In JSON modes, pipe to `jq`:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
moda overview | jq '.data.metrics'
|
|
111
|
+
moda frustrations | jq '.data.frustrations[].primary_cause'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Every command supports `moda <command> --help` for scoped usage, and
|
|
115
|
+
`moda manifest --json` describes the full machine protocol (commands, exit
|
|
116
|
+
codes, event schema) for agents.
|
|
117
|
+
|
|
80
118
|
## Prompt Management
|
|
81
119
|
|
|
82
120
|
Prompt management is code-first. Keep prompts in `prompts/**/*.prompt.md` or
|
|
@@ -1301,6 +1301,19 @@ function createAgentEvent(command, event, state) {
|
|
|
1301
1301
|
};
|
|
1302
1302
|
return redactDeep(stamped);
|
|
1303
1303
|
}
|
|
1304
|
+
function formatValidationMessage(error) {
|
|
1305
|
+
const issues = error.issues;
|
|
1306
|
+
if (error.name !== "ZodError" || !Array.isArray(issues) || issues.length === 0) {
|
|
1307
|
+
return error.message;
|
|
1308
|
+
}
|
|
1309
|
+
const parts = issues.map((issue) => {
|
|
1310
|
+
const record = issue;
|
|
1311
|
+
const path = Array.isArray(record.path) && record.path.length > 0 ? record.path.join(".") : "input";
|
|
1312
|
+
const message = typeof record.message === "string" ? record.message : "is invalid";
|
|
1313
|
+
return `${path}: ${message}`;
|
|
1314
|
+
});
|
|
1315
|
+
return `Validation error — ${parts.join("; ")}`;
|
|
1316
|
+
}
|
|
1304
1317
|
function errorToAgentError(error) {
|
|
1305
1318
|
if (error instanceof CliInputRequiredError) {
|
|
1306
1319
|
return {
|
|
@@ -1344,7 +1357,7 @@ function errorToAgentError(error) {
|
|
|
1344
1357
|
if (error instanceof CliInputError || error.name === "ZodError") {
|
|
1345
1358
|
return {
|
|
1346
1359
|
code: "input_error",
|
|
1347
|
-
message: error
|
|
1360
|
+
message: formatValidationMessage(error),
|
|
1348
1361
|
cause: "usage" in error && typeof error.usage === "string" ? error.usage : undefined,
|
|
1349
1362
|
suggested_commands: "usage" in error && typeof error.usage === "string" ? extractSuggestedCommands(error.usage) : undefined,
|
|
1350
1363
|
blocking: true
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
renderStatusHuman,
|
|
12
12
|
scanHarness,
|
|
13
13
|
validateHarnessReport
|
|
14
|
-
} from "./cli-
|
|
14
|
+
} from "./cli-g931ntph.js";
|
|
15
15
|
import {
|
|
16
16
|
isAuthSessionValid,
|
|
17
17
|
loadAuthSession
|
|
18
|
-
} from "./cli-
|
|
18
|
+
} from "./cli-9xw5bq1j.js";
|
|
19
19
|
import {
|
|
20
20
|
CliInputError,
|
|
21
21
|
createCommandContext,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
resolveTenantId,
|
|
32
32
|
secretFilePathFromRef,
|
|
33
33
|
validateConfig
|
|
34
|
-
} from "./cli-
|
|
34
|
+
} from "./cli-59yacef3.js";
|
|
35
35
|
|
|
36
36
|
// src/prompts.ts
|
|
37
37
|
import { execFileSync } from "node:child_process";
|
|
@@ -1071,14 +1071,14 @@ function detectAgents() {
|
|
|
1071
1071
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
1072
1072
|
import { createHash as createHash2 } from "node:crypto";
|
|
1073
1073
|
import {
|
|
1074
|
-
existsSync as
|
|
1074
|
+
existsSync as existsSync7,
|
|
1075
1075
|
lstatSync as lstatSync2,
|
|
1076
|
-
mkdirSync as
|
|
1076
|
+
mkdirSync as mkdirSync5,
|
|
1077
1077
|
readdirSync as readdirSync3,
|
|
1078
|
-
readFileSync as
|
|
1079
|
-
writeFileSync as
|
|
1078
|
+
readFileSync as readFileSync5,
|
|
1079
|
+
writeFileSync as writeFileSync6
|
|
1080
1080
|
} from "node:fs";
|
|
1081
|
-
import { basename, dirname as
|
|
1081
|
+
import { basename, dirname as dirname5, join as join6, relative as relative3 } from "node:path";
|
|
1082
1082
|
|
|
1083
1083
|
// src/skills-manifest.ts
|
|
1084
1084
|
import {
|
|
@@ -1299,10 +1299,86 @@ function yamlQuote(value) {
|
|
|
1299
1299
|
return JSON.stringify(value);
|
|
1300
1300
|
}
|
|
1301
1301
|
|
|
1302
|
+
// src/agents-md.ts
|
|
1303
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
|
|
1304
|
+
import { dirname as dirname4, join as join5 } from "node:path";
|
|
1305
|
+
var AGENTS_MD_FILENAME = "AGENTS.md";
|
|
1306
|
+
var FIXES_BLOCK_BEGIN = "<!-- moda:fixes:begin -->";
|
|
1307
|
+
var FIXES_BLOCK_END = "<!-- moda:fixes:end -->";
|
|
1308
|
+
function renderFixesBlock() {
|
|
1309
|
+
return [
|
|
1310
|
+
FIXES_BLOCK_BEGIN,
|
|
1311
|
+
"## Moda Fixes (managed block — edits inside are overwritten by moda-cli)",
|
|
1312
|
+
"",
|
|
1313
|
+
"- Before editing prompt or skill files, run `moda fixes` to see the ranked",
|
|
1314
|
+
' queue of Moda Fixes ("a fix with proof") already drafted for this repo,',
|
|
1315
|
+
" and `moda fix <fix_id> --packet` for the evidence packet.",
|
|
1316
|
+
"- Gate any prompt/skill change against production evidence with",
|
|
1317
|
+
" `moda fix verify <fix_id> [--prompt-file=<path>]`. Exit codes: 0 = holdout",
|
|
1318
|
+
" win vs baseline (pass), 1 = failing (per-case findings in the envelope),",
|
|
1319
|
+
" 3 = degraded/inconclusive — never treat exit 3 as a pass.",
|
|
1320
|
+
"- Fix PRs must carry the magic word `Fixes MODA-FIX-<SHORTREF>` in the PR",
|
|
1321
|
+
" body so the merge confirms the fix and starts production monitoring.",
|
|
1322
|
+
FIXES_BLOCK_END
|
|
1323
|
+
].join(`
|
|
1324
|
+
`);
|
|
1325
|
+
}
|
|
1326
|
+
function upsertManagedBlock(existing, block) {
|
|
1327
|
+
if (existing === null) {
|
|
1328
|
+
return `${block}
|
|
1329
|
+
`;
|
|
1330
|
+
}
|
|
1331
|
+
const beginIdx = existing.indexOf(FIXES_BLOCK_BEGIN);
|
|
1332
|
+
const endIdx = existing.indexOf(FIXES_BLOCK_END);
|
|
1333
|
+
if (beginIdx !== -1 && endIdx !== -1 && endIdx >= beginIdx) {
|
|
1334
|
+
const before = existing.slice(0, beginIdx);
|
|
1335
|
+
const after = existing.slice(endIdx + FIXES_BLOCK_END.length);
|
|
1336
|
+
return `${before}${block}${after}`;
|
|
1337
|
+
}
|
|
1338
|
+
const separator = existing === "" || existing.endsWith(`
|
|
1339
|
+
|
|
1340
|
+
`) ? "" : existing.endsWith(`
|
|
1341
|
+
`) ? `
|
|
1342
|
+
` : `
|
|
1343
|
+
|
|
1344
|
+
`;
|
|
1345
|
+
return `${existing}${separator}${block}
|
|
1346
|
+
`;
|
|
1347
|
+
}
|
|
1348
|
+
function writeFileIfChanged(filePath, content) {
|
|
1349
|
+
try {
|
|
1350
|
+
mkdirSync4(dirname4(filePath), { recursive: true });
|
|
1351
|
+
if (existsSync6(filePath)) {
|
|
1352
|
+
const current = readFileSync4(filePath, "utf8");
|
|
1353
|
+
if (current === content)
|
|
1354
|
+
return "unchanged";
|
|
1355
|
+
writeFileSync5(filePath, content);
|
|
1356
|
+
return "updated";
|
|
1357
|
+
}
|
|
1358
|
+
writeFileSync5(filePath, content);
|
|
1359
|
+
return "wrote";
|
|
1360
|
+
} catch {
|
|
1361
|
+
return "failed";
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
function upsertFixesAgentsBlock(cwd) {
|
|
1365
|
+
const filePath = join5(cwd, AGENTS_MD_FILENAME);
|
|
1366
|
+
let existing = null;
|
|
1367
|
+
if (existsSync6(filePath)) {
|
|
1368
|
+
try {
|
|
1369
|
+
existing = readFileSync4(filePath, "utf8");
|
|
1370
|
+
} catch {
|
|
1371
|
+
return { path: AGENTS_MD_FILENAME, status: "failed" };
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
const next = upsertManagedBlock(existing, renderFixesBlock());
|
|
1375
|
+
return { path: AGENTS_MD_FILENAME, status: writeFileIfChanged(filePath, next) };
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1302
1378
|
// src/skills.ts
|
|
1303
1379
|
var SKILLS_REQUEST_TIMEOUT_MS = 15000;
|
|
1304
1380
|
var VALID_STATUSES = new Set(["approved", "proposed", "all"]);
|
|
1305
|
-
var SKILL_PROPOSALS_MANIFEST_PATH =
|
|
1381
|
+
var SKILL_PROPOSALS_MANIFEST_PATH = join6(".moda", "skill-proposals.yml");
|
|
1306
1382
|
async function fetchSkills(status) {
|
|
1307
1383
|
const apiKey = resolveApiKey();
|
|
1308
1384
|
if (!apiKey) {
|
|
@@ -1427,15 +1503,15 @@ function formatRunStatusHuman(run, events) {
|
|
|
1427
1503
|
}
|
|
1428
1504
|
function writeIfChanged(filePath, content) {
|
|
1429
1505
|
try {
|
|
1430
|
-
|
|
1431
|
-
if (
|
|
1432
|
-
const current =
|
|
1506
|
+
mkdirSync5(dirname5(filePath), { recursive: true });
|
|
1507
|
+
if (existsSync7(filePath)) {
|
|
1508
|
+
const current = readFileSync5(filePath, "utf8");
|
|
1433
1509
|
if (current === content)
|
|
1434
1510
|
return "unchanged";
|
|
1435
|
-
|
|
1511
|
+
writeFileSync6(filePath, content);
|
|
1436
1512
|
return "updated";
|
|
1437
1513
|
}
|
|
1438
|
-
|
|
1514
|
+
writeFileSync6(filePath, content);
|
|
1439
1515
|
return "wrote";
|
|
1440
1516
|
} catch {
|
|
1441
1517
|
return "failed";
|
|
@@ -1472,7 +1548,7 @@ async function pullSkills(options = {}) {
|
|
|
1472
1548
|
}
|
|
1473
1549
|
}
|
|
1474
1550
|
const agents = detectAgents();
|
|
1475
|
-
const writeCursor = agents.includes("Cursor") ||
|
|
1551
|
+
const writeCursor = agents.includes("Cursor") || existsSync7(join6(cwd, ".cursor"));
|
|
1476
1552
|
const records = [];
|
|
1477
1553
|
for (const skill of skills) {
|
|
1478
1554
|
if (!skill.skill_id || !skill.skill_md) {
|
|
@@ -1485,7 +1561,7 @@ async function pullSkills(options = {}) {
|
|
|
1485
1561
|
log(` skipped invalid skill_id: ${JSON.stringify(skill.skill_id).slice(0, 80)}`);
|
|
1486
1562
|
continue;
|
|
1487
1563
|
}
|
|
1488
|
-
const claudePath =
|
|
1564
|
+
const claudePath = join6(cwd, ".claude", "skills", safeId, "SKILL.md");
|
|
1489
1565
|
const claudeStatus = writeIfChanged(claudePath, skill.skill_md);
|
|
1490
1566
|
const claudeRelativePath = `.claude/skills/${safeId}/SKILL.md`;
|
|
1491
1567
|
recordOutcome(summary, claudeStatus, log, claudeRelativePath);
|
|
@@ -1493,7 +1569,7 @@ async function pullSkills(options = {}) {
|
|
|
1493
1569
|
if (claudeStatus !== "failed")
|
|
1494
1570
|
paths.push(claudeRelativePath);
|
|
1495
1571
|
if (writeCursor) {
|
|
1496
|
-
const cursorPath =
|
|
1572
|
+
const cursorPath = join6(cwd, ".cursor", "rules", `${safeId}.mdc`);
|
|
1497
1573
|
const cursorStatus = writeIfChanged(cursorPath, claudeSkillToCursorRule(skill.skill_md));
|
|
1498
1574
|
const cursorRelativePath = `.cursor/rules/${safeId}.mdc`;
|
|
1499
1575
|
recordOutcome(summary, cursorStatus, log, cursorRelativePath);
|
|
@@ -1531,15 +1607,16 @@ function recordOutcome(summary, status, log, relativePath) {
|
|
|
1531
1607
|
break;
|
|
1532
1608
|
}
|
|
1533
1609
|
}
|
|
1534
|
-
var SKILLS_DIR =
|
|
1610
|
+
var SKILLS_DIR = join6(".claude", "skills");
|
|
1535
1611
|
var SKILL_FILE = "SKILL.md";
|
|
1536
1612
|
var MAX_SKILL_WALK_DEPTH = 4;
|
|
1537
1613
|
async function runSkillSync(flags = {}, profileOptions = {}, options = {}) {
|
|
1538
1614
|
const cwd = options.cwd ?? process.cwd();
|
|
1539
1615
|
const dryRun = flags["dry-run"] === "true";
|
|
1616
|
+
const agentsMd = dryRun ? undefined : upsertFixesAgentsBlock(cwd);
|
|
1540
1617
|
const skills = discoverLocalSkills(cwd);
|
|
1541
1618
|
if (skills.length === 0) {
|
|
1542
|
-
return { synced: [], dryRun };
|
|
1619
|
+
return { synced: [], dryRun, ...agentsMd ? { agentsMd } : {} };
|
|
1543
1620
|
}
|
|
1544
1621
|
const sourceCommit = getGitCommit2(cwd);
|
|
1545
1622
|
const response = await callControlAPI("/skills/sync", {
|
|
@@ -1558,11 +1635,11 @@ async function runSkillSync(flags = {}, profileOptions = {}, options = {}) {
|
|
|
1558
1635
|
dryRun
|
|
1559
1636
|
})
|
|
1560
1637
|
}, profileOptions);
|
|
1561
|
-
return { synced: response.synced ?? [], dryRun };
|
|
1638
|
+
return { synced: response.synced ?? [], dryRun, ...agentsMd ? { agentsMd } : {} };
|
|
1562
1639
|
}
|
|
1563
1640
|
function discoverLocalSkills(cwd) {
|
|
1564
|
-
const skillsRoot =
|
|
1565
|
-
if (!
|
|
1641
|
+
const skillsRoot = join6(cwd, SKILLS_DIR);
|
|
1642
|
+
if (!existsSync7(skillsRoot))
|
|
1566
1643
|
return [];
|
|
1567
1644
|
const managed = new Set(managedSkillPaths(cwd));
|
|
1568
1645
|
const skills = [];
|
|
@@ -1570,12 +1647,12 @@ function discoverLocalSkills(cwd) {
|
|
|
1570
1647
|
const sourcePath = relative3(cwd, absPath).replace(/\\/g, "/");
|
|
1571
1648
|
if (managed.has(sourcePath))
|
|
1572
1649
|
continue;
|
|
1573
|
-
const key = basename(
|
|
1650
|
+
const key = basename(dirname5(absPath));
|
|
1574
1651
|
if (key === "moda-cli" || !/^[A-Za-z0-9_-]+$/.test(key))
|
|
1575
1652
|
continue;
|
|
1576
1653
|
let content;
|
|
1577
1654
|
try {
|
|
1578
|
-
content =
|
|
1655
|
+
content = readFileSync5(absPath, "utf8");
|
|
1579
1656
|
} catch {
|
|
1580
1657
|
continue;
|
|
1581
1658
|
}
|
|
@@ -1610,7 +1687,7 @@ function walkSkillFiles(dir, depth) {
|
|
|
1610
1687
|
}
|
|
1611
1688
|
const files = [];
|
|
1612
1689
|
for (const entry of entries) {
|
|
1613
|
-
const fullPath =
|
|
1690
|
+
const fullPath = join6(dir, entry);
|
|
1614
1691
|
let stat;
|
|
1615
1692
|
try {
|
|
1616
1693
|
stat = lstatSync2(fullPath);
|
|
@@ -1684,7 +1761,7 @@ async function applySkillProposal(proposalId, flags, profileOptions = {}, option
|
|
|
1684
1761
|
}
|
|
1685
1762
|
const safeId = safeSkillId(proposal.skillId);
|
|
1686
1763
|
const relativePath = `.claude/skills/${safeId}/SKILL.md`;
|
|
1687
|
-
const writeStatus = writeIfChanged(
|
|
1764
|
+
const writeStatus = writeIfChanged(join6(cwd, relativePath), proposal.skillMd);
|
|
1688
1765
|
if (writeStatus === "failed") {
|
|
1689
1766
|
throw new Error(`Failed to write ${relativePath}`);
|
|
1690
1767
|
}
|
|
@@ -1717,9 +1794,9 @@ function safeSkillId(skillId) {
|
|
|
1717
1794
|
return safeId;
|
|
1718
1795
|
}
|
|
1719
1796
|
function writeSkillProposalManifest(cwd, proposal, relativePath) {
|
|
1720
|
-
const manifestPath =
|
|
1721
|
-
|
|
1722
|
-
const existing =
|
|
1797
|
+
const manifestPath = join6(cwd, SKILL_PROPOSALS_MANIFEST_PATH);
|
|
1798
|
+
mkdirSync5(dirname5(manifestPath), { recursive: true });
|
|
1799
|
+
const existing = existsSync7(manifestPath) ? readFileSync5(manifestPath, "utf8") : `applied_proposals:
|
|
1723
1800
|
`;
|
|
1724
1801
|
const entry = [
|
|
1725
1802
|
` - proposal_id: "${escapeYamlString(proposal.proposalId)}"`,
|
|
@@ -1738,7 +1815,7 @@ function writeSkillProposalManifest(cwd, proposal, relativePath) {
|
|
|
1738
1815
|
const prefix = existing.endsWith(`
|
|
1739
1816
|
`) ? existing : `${existing}
|
|
1740
1817
|
`;
|
|
1741
|
-
|
|
1818
|
+
writeFileSync6(manifestPath, `${prefix}${entry}
|
|
1742
1819
|
`);
|
|
1743
1820
|
}
|
|
1744
1821
|
function escapeYamlString(value) {
|
|
@@ -1843,7 +1920,11 @@ async function runSkillsCommand(input) {
|
|
|
1843
1920
|
}
|
|
1844
1921
|
if (sub === "sync") {
|
|
1845
1922
|
const result = await runSkillSync(flags, { profile: context.profile, env: context.env });
|
|
1846
|
-
context.output.writeData({
|
|
1923
|
+
context.output.writeData({
|
|
1924
|
+
synced: result.synced,
|
|
1925
|
+
dryRun: result.dryRun,
|
|
1926
|
+
...result.agentsMd ? { agentsMd: result.agentsMd } : {}
|
|
1927
|
+
});
|
|
1847
1928
|
return 0;
|
|
1848
1929
|
}
|
|
1849
1930
|
if (sub !== "pull") {
|
|
@@ -1973,8 +2054,8 @@ function sha2562(input) {
|
|
|
1973
2054
|
|
|
1974
2055
|
// src/doctor.ts
|
|
1975
2056
|
import { createHash as createHash3 } from "node:crypto";
|
|
1976
|
-
import { existsSync as
|
|
1977
|
-
import { join as
|
|
2057
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, statSync } from "node:fs";
|
|
2058
|
+
import { join as join7 } from "node:path";
|
|
1978
2059
|
async function runDoctorCommand(context) {
|
|
1979
2060
|
const report = await buildDoctorReport(context, {
|
|
1980
2061
|
offline: context.flags.offline === "true"
|
|
@@ -2097,8 +2178,8 @@ async function buildDoctorReport(context, options) {
|
|
|
2097
2178
|
checks.push(urlCheck("base_url_valid", "config", "Base URL is valid", baseUrl));
|
|
2098
2179
|
checks.push(urlCheck("ingest_url_valid", "config", "Ingest URL is valid", ingestUrl));
|
|
2099
2180
|
checks.push(localBaseUrlCheck(activeProfile.environment, baseUrl));
|
|
2100
|
-
const manifestPath =
|
|
2101
|
-
const manifestExists =
|
|
2181
|
+
const manifestPath = join7(context.cwd, ".moda", "harness.json");
|
|
2182
|
+
const manifestExists = existsSync8(manifestPath);
|
|
2102
2183
|
checks.push({
|
|
2103
2184
|
id: "harness_manifest_exists",
|
|
2104
2185
|
group: "harness",
|
|
@@ -2184,7 +2265,7 @@ function authTokenCheck(context, hasApiKey) {
|
|
|
2184
2265
|
};
|
|
2185
2266
|
}
|
|
2186
2267
|
function configPermissionCheck(path) {
|
|
2187
|
-
if (!
|
|
2268
|
+
if (!existsSync8(path)) {
|
|
2188
2269
|
return {
|
|
2189
2270
|
id: "config_file_permissions",
|
|
2190
2271
|
group: "config",
|
|
@@ -2327,7 +2408,7 @@ function harnessCurrentCheck(cwd, manifestPath, manifestExists, tenantId) {
|
|
|
2327
2408
|
tenantId,
|
|
2328
2409
|
tenantSource: tenantId ? "env_or_config" : undefined
|
|
2329
2410
|
});
|
|
2330
|
-
const saved = JSON.parse(
|
|
2411
|
+
const saved = JSON.parse(readFileSync6(manifestPath, "utf8"));
|
|
2331
2412
|
const savedHash = stableGraphHash(saved);
|
|
2332
2413
|
const currentHash = stableGraphHash(current);
|
|
2333
2414
|
const matches = savedHash === currentHash;
|
|
@@ -2387,8 +2468,8 @@ function harnessSyncStateCheck(cwd) {
|
|
|
2387
2468
|
};
|
|
2388
2469
|
}
|
|
2389
2470
|
function harnessReportStateCheck(cwd) {
|
|
2390
|
-
const reportPath =
|
|
2391
|
-
if (!
|
|
2471
|
+
const reportPath = join7(cwd, HARNESS_REPORT_PATH);
|
|
2472
|
+
if (!existsSync8(reportPath)) {
|
|
2392
2473
|
return {
|
|
2393
2474
|
id: "harness_report_approved_synced",
|
|
2394
2475
|
group: "harness",
|
|
@@ -2481,11 +2562,11 @@ function harnessReportStateCheck(cwd) {
|
|
|
2481
2562
|
}
|
|
2482
2563
|
}
|
|
2483
2564
|
function readHarnessReportApproval(cwd) {
|
|
2484
|
-
const approvalPath =
|
|
2485
|
-
if (!
|
|
2565
|
+
const approvalPath = join7(cwd, HARNESS_REPORT_APPROVAL_PATH);
|
|
2566
|
+
if (!existsSync8(approvalPath))
|
|
2486
2567
|
return null;
|
|
2487
2568
|
try {
|
|
2488
|
-
return JSON.parse(
|
|
2569
|
+
return JSON.parse(readFileSync6(approvalPath, "utf8"));
|
|
2489
2570
|
} catch {
|
|
2490
2571
|
return {
|
|
2491
2572
|
schemaVersion: HARNESS_REPORT_APPROVAL_SCHEMA_VERSION,
|
|
@@ -2499,7 +2580,7 @@ function readHarnessReportApproval(cwd) {
|
|
|
2499
2580
|
}
|
|
2500
2581
|
}
|
|
2501
2582
|
function discoveryCheck(id, group, title, cwd, relativePaths, nextCommand) {
|
|
2502
|
-
const found = relativePaths.filter((path) =>
|
|
2583
|
+
const found = relativePaths.filter((path) => existsSync8(join7(cwd, path)));
|
|
2503
2584
|
return {
|
|
2504
2585
|
id,
|
|
2505
2586
|
group,
|
|
@@ -2515,8 +2596,8 @@ function sdkInitializationCheck(cwd) {
|
|
|
2515
2596
|
const candidates = ["src", "app", "agent_harness"];
|
|
2516
2597
|
let detected = false;
|
|
2517
2598
|
for (const candidate of candidates) {
|
|
2518
|
-
const path =
|
|
2519
|
-
if (!
|
|
2599
|
+
const path = join7(cwd, candidate);
|
|
2600
|
+
if (!existsSync8(path))
|
|
2520
2601
|
continue;
|
|
2521
2602
|
try {
|
|
2522
2603
|
const stat = statSync(path);
|
|
@@ -2839,4 +2920,4 @@ function isRecord(value) {
|
|
|
2839
2920
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2840
2921
|
}
|
|
2841
2922
|
|
|
2842
|
-
export { runPromptsCommand, initPrompts, runPromptSync, claudeSkillToCursorRule, installRules, detectAgents, SKILL_SOURCE_INTEGRATION_BUNDLED, readSkillsManifest, upsertSkillManifestRecord, runSkillSync, runSkillsCommand, runDoctorCommand, runStatusCommand, buildDoctorReport };
|
|
2923
|
+
export { DEFAULT_POLL_INTERVAL_MS, DEFAULT_WAIT_TIMEOUT_MS, fetchLatestRun, runPromptsCommand, initPrompts, runPromptSync, claudeSkillToCursorRule, installRules, detectAgents, SKILL_SOURCE_INTEGRATION_BUNDLED, readSkillsManifest, upsertSkillManifestRecord, runSkillSync, runSkillsCommand, runDoctorCommand, runStatusCommand, buildDoctorReport };
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
resolveTenantId,
|
|
24
24
|
shouldInventoryFile,
|
|
25
25
|
stringOption
|
|
26
|
-
} from "./cli-
|
|
26
|
+
} from "./cli-59yacef3.js";
|
|
27
27
|
|
|
28
28
|
// src/api-client.ts
|
|
29
29
|
var REQUEST_TIMEOUT_MS = 30000;
|
|
@@ -3338,7 +3338,7 @@ async function runHarnessCommand(context) {
|
|
|
3338
3338
|
elapsed_ms: Date.now() - context.startedAt
|
|
3339
3339
|
});
|
|
3340
3340
|
if (context.flags["github-actions"] === "true") {
|
|
3341
|
-
const { runGithubActionsAnalyze } = await import("./harness-github-actions-
|
|
3341
|
+
const { runGithubActionsAnalyze } = await import("./harness-github-actions-ct8fwzdr.js");
|
|
3342
3342
|
const result = await runGithubActionsAnalyze(rootDir, {
|
|
3343
3343
|
writeReport: (report2) => {
|
|
3344
3344
|
const normalized = normalizeHarnessReport(report2);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
authFetch,
|
|
3
3
|
getCachedLoginContext
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-9xw5bq1j.js";
|
|
5
5
|
import {
|
|
6
6
|
CliAccessError,
|
|
7
7
|
CliInputError,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
resolveTenantIdFromActiveProfile,
|
|
16
16
|
saveCliConfig,
|
|
17
17
|
writeSecretJsonFile
|
|
18
|
-
} from "./cli-
|
|
18
|
+
} from "./cli-59yacef3.js";
|
|
19
19
|
|
|
20
20
|
// src/init/tenant.ts
|
|
21
21
|
import { randomUUID } from "node:crypto";
|