@moda-ai/cli 1.55.31 → 1.55.33
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 +3 -1
- package/dist/{agent-e4k17rzy.js → agent-g07tg64w.js} +2 -2
- package/dist/{cli-a03st7e3.js → cli-5fstvkva.js} +1 -1
- package/dist/{cli-n1d52cww.js → cli-602yx84w.js} +6 -6
- package/dist/{cli-zgj5bakm.js → cli-6j8h2ave.js} +2 -2
- package/dist/{cli-8dtnwp9r.js → cli-hcwdz9sd.js} +14 -248
- package/dist/{cli-mg5zv4s6.js → cli-k007ztbs.js} +45 -10
- package/dist/{cli-frhm2wz7.js → cli-mc3z77bv.js} +8 -4
- package/dist/cli-qm5b1vck.js +220 -0
- package/dist/cli-rjav0vj1.js +25 -0
- package/dist/cli.js +67 -13
- package/dist/{doctor-hnzte896.js → doctor-tvre7zvk.js} +2 -2
- package/dist/{index-6atgddwt.js → index-75vcntpe.js} +9 -6
- package/dist/mcp-1qre9f0n.js +7 -0
- package/dist/{prompts-ab-qed3ra5e.js → prompts-ab-t7xptf0c.js} +2 -2
- package/dist/{prompts-kswdk3ft.js → prompts-b8qx3mrv.js} +1 -1
- package/dist/{prompts-propose-y5r68pcw.js → prompts-propose-8feadk2t.js} +2 -2
- package/dist/rules-qddx5t0j.js +13 -0
- package/dist/{skills-j8865009.js → skills-96z55tj4.js} +4 -2
- package/dist/{skills-bundled-dq20spzk.js → skills-bundled-7jes6hqx.js} +4 -2
- package/dist/{sync-5e8t28t7.js → sync-drz7req6.js} +5 -3
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/skills/moda-cli/SKILL.md +7 -3
- package/skills/moda-cli/references/output-protocol.md +6 -2
package/README.md
CHANGED
|
@@ -143,7 +143,9 @@ Flag aliases: `moda context --message-index=N` = `--msg-index=N`, and
|
|
|
143
143
|
`moda users --limit=N` = `--page-size=N`. Each command rejects a flag it does
|
|
144
144
|
not take, and time windows differ by command: `--days-back=N` (a whole number,
|
|
145
145
|
1-90) on the overview, Problem and detection commands, `--time-range` on
|
|
146
|
-
`traces`, `search` and `clusters`.
|
|
146
|
+
`traces`, `search` and `clusters`. The other window spelling is rewritten when
|
|
147
|
+
it names the same span (`--days-back=7` is `--time-range=7d`, `--time-range=30d`
|
|
148
|
+
is `--days-back=30`); one with no exact match fails. Boolean flags accept `true`/`false`, `1`/`0`,
|
|
147
149
|
`yes`/`no` and `on`/`off`.
|
|
148
150
|
|
|
149
151
|
Prompt management:
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DEFAULT_POLL_INTERVAL_MS,
|
|
3
|
-
DEFAULT_WAIT_TIMEOUT_MS,
|
|
4
|
-
fetchLatestRun
|
|
5
|
-
} from "./cli-yz8xcpyk.js";
|
|
6
1
|
import {
|
|
7
2
|
assertReplayablePrompt,
|
|
8
3
|
discoverPrompts,
|
|
9
4
|
parsePromptFileForAb,
|
|
10
5
|
readPromptLock,
|
|
11
6
|
runPromptSync
|
|
12
|
-
} from "./cli-
|
|
7
|
+
} from "./cli-6j8h2ave.js";
|
|
8
|
+
import {
|
|
9
|
+
DEFAULT_POLL_INTERVAL_MS,
|
|
10
|
+
DEFAULT_WAIT_TIMEOUT_MS,
|
|
11
|
+
fetchLatestRun
|
|
12
|
+
} from "./cli-yz8xcpyk.js";
|
|
13
13
|
import {
|
|
14
14
|
UUID_RE
|
|
15
15
|
} from "./cli-n2hng3gv.js";
|
|
@@ -5725,9 +5725,9 @@ async function runPromptsCommand(input, legacyFlags, legacyProfileOptions) {
|
|
|
5725
5725
|
await promotePrompt(positionals, flags, profileOptions, context);
|
|
5726
5726
|
break;
|
|
5727
5727
|
case "ab":
|
|
5728
|
-
return (await import("./prompts-ab-
|
|
5728
|
+
return (await import("./prompts-ab-t7xptf0c.js")).runPromptAb(flags, profileOptions, context);
|
|
5729
5729
|
case "propose":
|
|
5730
|
-
await (await import("./prompts-propose-
|
|
5730
|
+
await (await import("./prompts-propose-8feadk2t.js")).runPromptPropose(positionals, flags, profileOptions, context);
|
|
5731
5731
|
break;
|
|
5732
5732
|
case "list":
|
|
5733
5733
|
case "show":
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
// src/init/rules.ts
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname as dirname2, join as join2, sep } from "node:path";
|
|
5
|
+
import { confirm } from "@inquirer/prompts";
|
|
6
|
+
|
|
1
7
|
// src/package-assets.ts
|
|
2
8
|
import { dirname, join } from "node:path";
|
|
3
9
|
import { fileURLToPath } from "node:url";
|
|
@@ -6,10 +12,6 @@ function packageAssetPath(...parts) {
|
|
|
6
12
|
}
|
|
7
13
|
|
|
8
14
|
// src/init/rules.ts
|
|
9
|
-
import { createHash } from "node:crypto";
|
|
10
|
-
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
11
|
-
import { dirname as dirname2, join as join2, sep } from "node:path";
|
|
12
|
-
import { confirm } from "@inquirer/prompts";
|
|
13
15
|
var SKILLS_SOURCE_DIR = packageAssetPath("skills");
|
|
14
16
|
var SKILLS = [
|
|
15
17
|
{
|
|
@@ -89,6 +91,13 @@ function writeInstallRecord(record) {
|
|
|
89
91
|
writeFileSync(path, `${JSON.stringify(record, null, 2)}
|
|
90
92
|
`);
|
|
91
93
|
}
|
|
94
|
+
function bundledSkillVersion(skillId) {
|
|
95
|
+
try {
|
|
96
|
+
return skillVersion(loadSkillSource(skillId).content);
|
|
97
|
+
} catch {
|
|
98
|
+
return "unknown";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
92
101
|
function skillVersion(skillContent) {
|
|
93
102
|
const frontmatter = FRONTMATTER_RE.exec(skillContent)?.[1] ?? "";
|
|
94
103
|
return /^version:\s*(\S+)\s*$/m.exec(frontmatter)?.[1] ?? "unknown";
|
|
@@ -357,247 +366,4 @@ async function installRules(detectedAgents, options = {}) {
|
|
|
357
366
|
return result;
|
|
358
367
|
}
|
|
359
368
|
|
|
360
|
-
|
|
361
|
-
import { existsSync as existsSync2 } from "node:fs";
|
|
362
|
-
import { join as join3 } from "node:path";
|
|
363
|
-
import { execSync } from "node:child_process";
|
|
364
|
-
function isOnPath(cmd) {
|
|
365
|
-
try {
|
|
366
|
-
execSync(`which ${cmd}`, { stdio: "pipe" });
|
|
367
|
-
return true;
|
|
368
|
-
} catch {
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
function detectAgents() {
|
|
373
|
-
const cwd = process.cwd();
|
|
374
|
-
const agents = [];
|
|
375
|
-
if (existsSync2(join3(cwd, ".claude")) || isOnPath("claude")) {
|
|
376
|
-
agents.push("Claude Code");
|
|
377
|
-
}
|
|
378
|
-
if (existsSync2(join3(cwd, ".cursor"))) {
|
|
379
|
-
agents.push("Cursor");
|
|
380
|
-
}
|
|
381
|
-
return agents;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// src/skills-manifest.ts
|
|
385
|
-
import {
|
|
386
|
-
existsSync as existsSync3,
|
|
387
|
-
mkdirSync as mkdirSync2,
|
|
388
|
-
readdirSync as readdirSync2,
|
|
389
|
-
readFileSync as readFileSync2,
|
|
390
|
-
rmdirSync,
|
|
391
|
-
unlinkSync as unlinkSync2,
|
|
392
|
-
writeFileSync as writeFileSync2
|
|
393
|
-
} from "node:fs";
|
|
394
|
-
import { dirname as dirname3, isAbsolute, join as join4, normalize, relative, resolve } from "node:path";
|
|
395
|
-
var SKILLS_MANIFEST_PATH = join4(".moda", "skills.yml");
|
|
396
|
-
var SKILL_SOURCE_HARNESS = "moda";
|
|
397
|
-
var SKILL_SOURCE_INTEGRATION_BUNDLED = "integration-bundled";
|
|
398
|
-
function readSkillsManifest(cwd) {
|
|
399
|
-
const manifestPath = join4(cwd, SKILLS_MANIFEST_PATH);
|
|
400
|
-
if (!existsSync3(manifestPath))
|
|
401
|
-
return null;
|
|
402
|
-
let text = "";
|
|
403
|
-
try {
|
|
404
|
-
text = readFileSync2(manifestPath, "utf8");
|
|
405
|
-
} catch {
|
|
406
|
-
return null;
|
|
407
|
-
}
|
|
408
|
-
return {
|
|
409
|
-
tenantId: readYamlScalar(text, "tenant_id"),
|
|
410
|
-
generationRunId: readYamlScalar(text, "generation_run_id"),
|
|
411
|
-
generationVersion: Number(readYamlScalar(text, "generation_version") || 0),
|
|
412
|
-
releaseKey: readYamlScalar(text, "release_key"),
|
|
413
|
-
statusFilter: readYamlScalar(text, "status_filter"),
|
|
414
|
-
releasedAt: readYamlScalar(text, "released_at"),
|
|
415
|
-
managedPaths: readManagedPaths(text),
|
|
416
|
-
records: readSkillRecords(text)
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
function manifestHeaderFrom(manifest) {
|
|
420
|
-
return {
|
|
421
|
-
tenantId: manifest?.tenantId ?? "",
|
|
422
|
-
releaseKey: manifest?.releaseKey ?? "",
|
|
423
|
-
statusFilter: manifest?.statusFilter ?? "",
|
|
424
|
-
generationRunId: manifest?.generationRunId ?? "",
|
|
425
|
-
generationVersion: manifest?.generationVersion ?? 0,
|
|
426
|
-
releasedAt: manifest?.releasedAt ?? ""
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
function writeSkillsManifest(cwd, header, records) {
|
|
430
|
-
const manifestPath = join4(cwd, SKILLS_MANIFEST_PATH);
|
|
431
|
-
const managedPaths = [...new Set(records.flatMap((record) => record.paths))].sort();
|
|
432
|
-
const lines = [
|
|
433
|
-
"version: 1",
|
|
434
|
-
"source: moda",
|
|
435
|
-
`tenant_id: ${yamlQuote(header.tenantId)}`,
|
|
436
|
-
`release_key: ${yamlQuote(header.releaseKey)}`,
|
|
437
|
-
`status_filter: ${yamlQuote(header.statusFilter)}`,
|
|
438
|
-
`generation_run_id: ${yamlQuote(header.generationRunId)}`,
|
|
439
|
-
`generation_version: ${header.generationVersion}`,
|
|
440
|
-
`released_at: ${yamlQuote(header.releasedAt)}`,
|
|
441
|
-
`installed_at: ${yamlQuote(new Date().toISOString())}`,
|
|
442
|
-
"managed_paths:",
|
|
443
|
-
...managedPaths.map((path) => ` - ${yamlQuote(path)}`),
|
|
444
|
-
"skills:",
|
|
445
|
-
...records.flatMap((record) => [
|
|
446
|
-
` - skill_id: ${yamlQuote(record.skillId)}`,
|
|
447
|
-
` content_hash: ${yamlQuote(record.contentHash)}`,
|
|
448
|
-
` source: ${yamlQuote(record.source)}`,
|
|
449
|
-
" paths:",
|
|
450
|
-
...record.paths.map((path) => ` - ${yamlQuote(path)}`)
|
|
451
|
-
]),
|
|
452
|
-
""
|
|
453
|
-
];
|
|
454
|
-
mkdirSync2(dirname3(manifestPath), { recursive: true });
|
|
455
|
-
writeFileSync2(manifestPath, lines.join(`
|
|
456
|
-
`));
|
|
457
|
-
}
|
|
458
|
-
function upsertSkillManifestRecord(cwd, record) {
|
|
459
|
-
const manifest = readSkillsManifest(cwd);
|
|
460
|
-
const records = (manifest?.records ?? []).filter((existing) => !(existing.skillId === record.skillId && existing.source === record.source));
|
|
461
|
-
records.push(record);
|
|
462
|
-
writeSkillsManifest(cwd, manifestHeaderFrom(manifest), records);
|
|
463
|
-
}
|
|
464
|
-
function pruneManagedSkillPaths(cwd, managedPaths, log) {
|
|
465
|
-
let pruned = 0;
|
|
466
|
-
for (const rawPath of [...new Set(managedPaths)]) {
|
|
467
|
-
const safePath = safeManagedRelativePath(rawPath);
|
|
468
|
-
if (!safePath)
|
|
469
|
-
continue;
|
|
470
|
-
const absolutePath = absoluteManagedPath(cwd, safePath);
|
|
471
|
-
if (!absolutePath || !existsSync3(absolutePath))
|
|
472
|
-
continue;
|
|
473
|
-
try {
|
|
474
|
-
unlinkSync2(absolutePath);
|
|
475
|
-
pruned++;
|
|
476
|
-
log(` pruned ${safePath}`);
|
|
477
|
-
pruneEmptyClaudeSkillDir(cwd, safePath);
|
|
478
|
-
} catch {}
|
|
479
|
-
}
|
|
480
|
-
return pruned;
|
|
481
|
-
}
|
|
482
|
-
function pruneEmptyClaudeSkillDir(cwd, relativePath) {
|
|
483
|
-
if (!relativePath.startsWith(".claude/skills/") || !relativePath.endsWith("/SKILL.md"))
|
|
484
|
-
return;
|
|
485
|
-
const dir = dirname3(join4(cwd, relativePath));
|
|
486
|
-
try {
|
|
487
|
-
if (existsSync3(dir) && readdirSync2(dir).length === 0) {
|
|
488
|
-
rmdirSync(dir);
|
|
489
|
-
}
|
|
490
|
-
} catch {}
|
|
491
|
-
}
|
|
492
|
-
function absoluteManagedPath(cwd, relativePath) {
|
|
493
|
-
const abs = resolve(cwd, relativePath);
|
|
494
|
-
const root = resolve(cwd);
|
|
495
|
-
const rel = relative(root, abs);
|
|
496
|
-
if (rel.startsWith("..") || isAbsolute(rel))
|
|
497
|
-
return null;
|
|
498
|
-
return abs;
|
|
499
|
-
}
|
|
500
|
-
function safeManagedRelativePath(rawPath) {
|
|
501
|
-
if (!rawPath || rawPath.includes("\x00") || isAbsolute(rawPath))
|
|
502
|
-
return null;
|
|
503
|
-
const normalized = normalize(rawPath).replace(/\\/g, "/");
|
|
504
|
-
if (normalized.startsWith("../") || normalized === ".." || normalized.includes("/../"))
|
|
505
|
-
return null;
|
|
506
|
-
const isClaudeSkill = normalized.startsWith(".claude/skills/") && normalized.endsWith("/SKILL.md");
|
|
507
|
-
const isCursorRule = normalized.startsWith(".cursor/rules/") && normalized.endsWith(".mdc");
|
|
508
|
-
return isClaudeSkill || isCursorRule ? normalized : null;
|
|
509
|
-
}
|
|
510
|
-
function readYamlScalar(text, key) {
|
|
511
|
-
const match = text.match(new RegExp(`^${key}:\\s*(.*?)\\s*$`, "m"));
|
|
512
|
-
return match ? parseYamlScalar(match[1]) : "";
|
|
513
|
-
}
|
|
514
|
-
function readSkillRecords(text) {
|
|
515
|
-
const records = [];
|
|
516
|
-
let inSkills = false;
|
|
517
|
-
let current = null;
|
|
518
|
-
let inPaths = false;
|
|
519
|
-
for (const line of text.split(/\r?\n/)) {
|
|
520
|
-
if (line.trim() === "skills:") {
|
|
521
|
-
inSkills = true;
|
|
522
|
-
continue;
|
|
523
|
-
}
|
|
524
|
-
if (!inSkills)
|
|
525
|
-
continue;
|
|
526
|
-
if (line.length > 0 && !line.startsWith(" "))
|
|
527
|
-
break;
|
|
528
|
-
const skillIdMatch = line.match(/^ {2}- skill_id:\s*(.*?)\s*$/);
|
|
529
|
-
if (skillIdMatch) {
|
|
530
|
-
if (current)
|
|
531
|
-
records.push(current);
|
|
532
|
-
current = {
|
|
533
|
-
skillId: parseYamlScalar(skillIdMatch[1]),
|
|
534
|
-
contentHash: "",
|
|
535
|
-
source: SKILL_SOURCE_HARNESS,
|
|
536
|
-
paths: []
|
|
537
|
-
};
|
|
538
|
-
inPaths = false;
|
|
539
|
-
continue;
|
|
540
|
-
}
|
|
541
|
-
if (!current)
|
|
542
|
-
continue;
|
|
543
|
-
const hashMatch = line.match(/^ {4}content_hash:\s*(.*?)\s*$/);
|
|
544
|
-
if (hashMatch) {
|
|
545
|
-
current.contentHash = parseYamlScalar(hashMatch[1]);
|
|
546
|
-
continue;
|
|
547
|
-
}
|
|
548
|
-
const sourceMatch = line.match(/^ {4}source:\s*(.*?)\s*$/);
|
|
549
|
-
if (sourceMatch) {
|
|
550
|
-
current.source = parseYamlScalar(sourceMatch[1]) || SKILL_SOURCE_HARNESS;
|
|
551
|
-
continue;
|
|
552
|
-
}
|
|
553
|
-
if (/^ {4}paths:\s*$/.test(line)) {
|
|
554
|
-
inPaths = true;
|
|
555
|
-
continue;
|
|
556
|
-
}
|
|
557
|
-
const pathMatch = inPaths ? line.match(/^ {6}-\s*(.*?)\s*$/) : null;
|
|
558
|
-
if (pathMatch) {
|
|
559
|
-
const value = parseYamlScalar(pathMatch[1]);
|
|
560
|
-
if (value)
|
|
561
|
-
current.paths.push(value);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
if (current)
|
|
565
|
-
records.push(current);
|
|
566
|
-
return records;
|
|
567
|
-
}
|
|
568
|
-
function readManagedPaths(text) {
|
|
569
|
-
const paths = [];
|
|
570
|
-
let inManagedPaths = false;
|
|
571
|
-
for (const line of text.split(/\r?\n/)) {
|
|
572
|
-
if (line.trim() === "managed_paths:") {
|
|
573
|
-
inManagedPaths = true;
|
|
574
|
-
continue;
|
|
575
|
-
}
|
|
576
|
-
if (inManagedPaths && line.length > 0 && !line.startsWith(" "))
|
|
577
|
-
break;
|
|
578
|
-
if (!inManagedPaths)
|
|
579
|
-
continue;
|
|
580
|
-
const match = line.match(/^\s*-\s*(.*?)\s*$/);
|
|
581
|
-
if (match)
|
|
582
|
-
paths.push(parseYamlScalar(match[1]));
|
|
583
|
-
}
|
|
584
|
-
return paths.filter(Boolean);
|
|
585
|
-
}
|
|
586
|
-
function parseYamlScalar(raw) {
|
|
587
|
-
const value = raw.trim();
|
|
588
|
-
if (!value)
|
|
589
|
-
return "";
|
|
590
|
-
if (value.startsWith('"')) {
|
|
591
|
-
try {
|
|
592
|
-
return String(JSON.parse(value));
|
|
593
|
-
} catch {
|
|
594
|
-
return value.replace(/^"|"$/g, "");
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
return value.replace(/^'|'$/g, "");
|
|
598
|
-
}
|
|
599
|
-
function yamlQuote(value) {
|
|
600
|
-
return JSON.stringify(value);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
export { packageAssetPath, claudeSkillToCursorRule, summarizeInstall, installRules, detectAgents, SKILL_SOURCE_HARNESS, SKILL_SOURCE_INTEGRATION_BUNDLED, readSkillsManifest, writeSkillsManifest, upsertSkillManifestRecord, pruneManagedSkillPaths };
|
|
369
|
+
export { packageAssetPath, claudeSkillToCursorRule, bundledSkillVersion, summarizeInstall, installRules };
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SKILL_SOURCE_HARNESS,
|
|
3
3
|
SKILL_SOURCE_INTEGRATION_BUNDLED,
|
|
4
|
-
claudeSkillToCursorRule,
|
|
5
|
-
detectAgents,
|
|
6
4
|
pruneManagedSkillPaths,
|
|
7
5
|
readSkillsManifest,
|
|
8
6
|
writeSkillsManifest
|
|
9
|
-
} from "./cli-
|
|
7
|
+
} from "./cli-qm5b1vck.js";
|
|
8
|
+
import {
|
|
9
|
+
detectAgents
|
|
10
|
+
} from "./cli-rjav0vj1.js";
|
|
11
|
+
import {
|
|
12
|
+
claudeSkillToCursorRule
|
|
13
|
+
} from "./cli-hcwdz9sd.js";
|
|
10
14
|
import {
|
|
11
15
|
createCommandContext
|
|
12
16
|
} from "./cli-dr6x0zv7.js";
|
|
@@ -901,19 +905,43 @@ function manifestHeaderFor(fetched) {
|
|
|
901
905
|
function sha256(input) {
|
|
902
906
|
return createHash("sha256").update(input).digest("hex");
|
|
903
907
|
}
|
|
908
|
+
var MODA_CLI_SKILL_ID = "moda-cli";
|
|
909
|
+
async function installModaCliSkill(context) {
|
|
910
|
+
const { detectAgents: detectAgents2 } = await import("./mcp-1qre9f0n.js");
|
|
911
|
+
const { installRules, summarizeInstall } = await import("./rules-qddx5t0j.js");
|
|
912
|
+
const result = await installRules(detectAgents2(), { yes: true, silent: true });
|
|
913
|
+
if (result.status === "skipped") {
|
|
914
|
+
throw new CliInputError("No supported coding agent was detected here, so the moda-cli skill was not installed.", "Run this from a project with a .claude or .cursor directory, or with Claude Code on PATH. Only Claude Code and Cursor are supported.");
|
|
915
|
+
}
|
|
916
|
+
const summary = summarizeInstall(result);
|
|
917
|
+
if (context.outputMode !== "human") {
|
|
918
|
+
context.output.writeData({ skill: MODA_CLI_SKILL_ID, status: result.status, agents: result.agents, counts: result.counts, summary, outcomes: result.outcomes, notes: result.notes }, { ...result.status === "failed" ? { status: "error" } : {}, warnings: result.warnings });
|
|
919
|
+
} else {
|
|
920
|
+
console.log(`moda-cli skill for ${result.agents.join(", ")}: ${summary}`);
|
|
921
|
+
for (const note of result.notes)
|
|
922
|
+
console.log(` ${note}`);
|
|
923
|
+
for (const warning of result.warnings)
|
|
924
|
+
console.error(` ${warning}`);
|
|
925
|
+
}
|
|
926
|
+
return result.status === "failed" ? 1 : 0;
|
|
927
|
+
}
|
|
904
928
|
async function runSkillsInstall(context, ids) {
|
|
905
|
-
const { installIntegrationSkill, readBundledIndex, readBundledSkillMd } = await import("./skills-bundled-
|
|
929
|
+
const { installIntegrationSkill, readBundledIndex, readBundledSkillMd } = await import("./skills-bundled-7jes6hqx.js");
|
|
906
930
|
const index = readBundledIndex();
|
|
907
931
|
if (context.flags.list === "true" || ids.length === 0) {
|
|
932
|
+
const { bundledSkillVersion } = await import("./rules-qddx5t0j.js");
|
|
908
933
|
if (!index) {
|
|
909
934
|
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.");
|
|
910
935
|
}
|
|
911
|
-
const skills =
|
|
912
|
-
id:
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
936
|
+
const skills = [
|
|
937
|
+
{ id: MODA_CLI_SKILL_ID, version: bundledSkillVersion(MODA_CLI_SKILL_ID), language: "any", summary: "This CLI's own guide for coding agents; installs or refreshes it for each detected agent (copies edited locally are kept)." },
|
|
938
|
+
...index.skills.map((skill) => ({
|
|
939
|
+
id: skill.id,
|
|
940
|
+
version: skill.version,
|
|
941
|
+
language: skill.targets.language,
|
|
942
|
+
summary: skill.summary
|
|
943
|
+
}))
|
|
944
|
+
];
|
|
917
945
|
if (context.outputMode !== "human") {
|
|
918
946
|
context.output.writeData({ skills });
|
|
919
947
|
} else {
|
|
@@ -925,6 +953,13 @@ async function runSkillsInstall(context, ids) {
|
|
|
925
953
|
}
|
|
926
954
|
return 0;
|
|
927
955
|
}
|
|
956
|
+
if (ids.includes(MODA_CLI_SKILL_ID)) {
|
|
957
|
+
const others = ids.filter((id) => id !== MODA_CLI_SKILL_ID);
|
|
958
|
+
if (others.length > 0) {
|
|
959
|
+
throw new CliInputError(`Install ${MODA_CLI_SKILL_ID} on its own, so nothing was written.`, `Run \`moda skills install ${MODA_CLI_SKILL_ID}\`, then \`moda skills install ${others.join(" ")}\`.`);
|
|
960
|
+
}
|
|
961
|
+
return installModaCliSkill(context);
|
|
962
|
+
}
|
|
928
963
|
const unknown = ids.filter((id) => !/^[a-z0-9-]+$/.test(id) || readBundledSkillMd(id) === null);
|
|
929
964
|
if (unknown.length > 0) {
|
|
930
965
|
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");
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SKILL_SOURCE_INTEGRATION_BUNDLED,
|
|
3
|
-
claudeSkillToCursorRule,
|
|
4
|
-
detectAgents,
|
|
5
|
-
packageAssetPath,
|
|
6
3
|
readSkillsManifest,
|
|
7
4
|
upsertSkillManifestRecord
|
|
8
|
-
} from "./cli-
|
|
5
|
+
} from "./cli-qm5b1vck.js";
|
|
6
|
+
import {
|
|
7
|
+
detectAgents
|
|
8
|
+
} from "./cli-rjav0vj1.js";
|
|
9
|
+
import {
|
|
10
|
+
claudeSkillToCursorRule,
|
|
11
|
+
packageAssetPath
|
|
12
|
+
} from "./cli-hcwdz9sd.js";
|
|
9
13
|
|
|
10
14
|
// src/skills-bundled.ts
|
|
11
15
|
import { createHash } from "node:crypto";
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// src/skills-manifest.ts
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
rmdirSync,
|
|
8
|
+
unlinkSync,
|
|
9
|
+
writeFileSync
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { dirname, isAbsolute, join, normalize, relative, resolve } from "node:path";
|
|
12
|
+
var SKILLS_MANIFEST_PATH = join(".moda", "skills.yml");
|
|
13
|
+
var SKILL_SOURCE_HARNESS = "moda";
|
|
14
|
+
var SKILL_SOURCE_INTEGRATION_BUNDLED = "integration-bundled";
|
|
15
|
+
function readSkillsManifest(cwd) {
|
|
16
|
+
const manifestPath = join(cwd, SKILLS_MANIFEST_PATH);
|
|
17
|
+
if (!existsSync(manifestPath))
|
|
18
|
+
return null;
|
|
19
|
+
let text = "";
|
|
20
|
+
try {
|
|
21
|
+
text = readFileSync(manifestPath, "utf8");
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
tenantId: readYamlScalar(text, "tenant_id"),
|
|
27
|
+
generationRunId: readYamlScalar(text, "generation_run_id"),
|
|
28
|
+
generationVersion: Number(readYamlScalar(text, "generation_version") || 0),
|
|
29
|
+
releaseKey: readYamlScalar(text, "release_key"),
|
|
30
|
+
statusFilter: readYamlScalar(text, "status_filter"),
|
|
31
|
+
releasedAt: readYamlScalar(text, "released_at"),
|
|
32
|
+
managedPaths: readManagedPaths(text),
|
|
33
|
+
records: readSkillRecords(text)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function manifestHeaderFrom(manifest) {
|
|
37
|
+
return {
|
|
38
|
+
tenantId: manifest?.tenantId ?? "",
|
|
39
|
+
releaseKey: manifest?.releaseKey ?? "",
|
|
40
|
+
statusFilter: manifest?.statusFilter ?? "",
|
|
41
|
+
generationRunId: manifest?.generationRunId ?? "",
|
|
42
|
+
generationVersion: manifest?.generationVersion ?? 0,
|
|
43
|
+
releasedAt: manifest?.releasedAt ?? ""
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function writeSkillsManifest(cwd, header, records) {
|
|
47
|
+
const manifestPath = join(cwd, SKILLS_MANIFEST_PATH);
|
|
48
|
+
const managedPaths = [...new Set(records.flatMap((record) => record.paths))].sort();
|
|
49
|
+
const lines = [
|
|
50
|
+
"version: 1",
|
|
51
|
+
"source: moda",
|
|
52
|
+
`tenant_id: ${yamlQuote(header.tenantId)}`,
|
|
53
|
+
`release_key: ${yamlQuote(header.releaseKey)}`,
|
|
54
|
+
`status_filter: ${yamlQuote(header.statusFilter)}`,
|
|
55
|
+
`generation_run_id: ${yamlQuote(header.generationRunId)}`,
|
|
56
|
+
`generation_version: ${header.generationVersion}`,
|
|
57
|
+
`released_at: ${yamlQuote(header.releasedAt)}`,
|
|
58
|
+
`installed_at: ${yamlQuote(new Date().toISOString())}`,
|
|
59
|
+
"managed_paths:",
|
|
60
|
+
...managedPaths.map((path) => ` - ${yamlQuote(path)}`),
|
|
61
|
+
"skills:",
|
|
62
|
+
...records.flatMap((record) => [
|
|
63
|
+
` - skill_id: ${yamlQuote(record.skillId)}`,
|
|
64
|
+
` content_hash: ${yamlQuote(record.contentHash)}`,
|
|
65
|
+
` source: ${yamlQuote(record.source)}`,
|
|
66
|
+
" paths:",
|
|
67
|
+
...record.paths.map((path) => ` - ${yamlQuote(path)}`)
|
|
68
|
+
]),
|
|
69
|
+
""
|
|
70
|
+
];
|
|
71
|
+
mkdirSync(dirname(manifestPath), { recursive: true });
|
|
72
|
+
writeFileSync(manifestPath, lines.join(`
|
|
73
|
+
`));
|
|
74
|
+
}
|
|
75
|
+
function upsertSkillManifestRecord(cwd, record) {
|
|
76
|
+
const manifest = readSkillsManifest(cwd);
|
|
77
|
+
const records = (manifest?.records ?? []).filter((existing) => !(existing.skillId === record.skillId && existing.source === record.source));
|
|
78
|
+
records.push(record);
|
|
79
|
+
writeSkillsManifest(cwd, manifestHeaderFrom(manifest), records);
|
|
80
|
+
}
|
|
81
|
+
function pruneManagedSkillPaths(cwd, managedPaths, log) {
|
|
82
|
+
let pruned = 0;
|
|
83
|
+
for (const rawPath of [...new Set(managedPaths)]) {
|
|
84
|
+
const safePath = safeManagedRelativePath(rawPath);
|
|
85
|
+
if (!safePath)
|
|
86
|
+
continue;
|
|
87
|
+
const absolutePath = absoluteManagedPath(cwd, safePath);
|
|
88
|
+
if (!absolutePath || !existsSync(absolutePath))
|
|
89
|
+
continue;
|
|
90
|
+
try {
|
|
91
|
+
unlinkSync(absolutePath);
|
|
92
|
+
pruned++;
|
|
93
|
+
log(` pruned ${safePath}`);
|
|
94
|
+
pruneEmptyClaudeSkillDir(cwd, safePath);
|
|
95
|
+
} catch {}
|
|
96
|
+
}
|
|
97
|
+
return pruned;
|
|
98
|
+
}
|
|
99
|
+
function pruneEmptyClaudeSkillDir(cwd, relativePath) {
|
|
100
|
+
if (!relativePath.startsWith(".claude/skills/") || !relativePath.endsWith("/SKILL.md"))
|
|
101
|
+
return;
|
|
102
|
+
const dir = dirname(join(cwd, relativePath));
|
|
103
|
+
try {
|
|
104
|
+
if (existsSync(dir) && readdirSync(dir).length === 0) {
|
|
105
|
+
rmdirSync(dir);
|
|
106
|
+
}
|
|
107
|
+
} catch {}
|
|
108
|
+
}
|
|
109
|
+
function absoluteManagedPath(cwd, relativePath) {
|
|
110
|
+
const abs = resolve(cwd, relativePath);
|
|
111
|
+
const root = resolve(cwd);
|
|
112
|
+
const rel = relative(root, abs);
|
|
113
|
+
if (rel.startsWith("..") || isAbsolute(rel))
|
|
114
|
+
return null;
|
|
115
|
+
return abs;
|
|
116
|
+
}
|
|
117
|
+
function safeManagedRelativePath(rawPath) {
|
|
118
|
+
if (!rawPath || rawPath.includes("\x00") || isAbsolute(rawPath))
|
|
119
|
+
return null;
|
|
120
|
+
const normalized = normalize(rawPath).replace(/\\/g, "/");
|
|
121
|
+
if (normalized.startsWith("../") || normalized === ".." || normalized.includes("/../"))
|
|
122
|
+
return null;
|
|
123
|
+
const isClaudeSkill = normalized.startsWith(".claude/skills/") && normalized.endsWith("/SKILL.md");
|
|
124
|
+
const isCursorRule = normalized.startsWith(".cursor/rules/") && normalized.endsWith(".mdc");
|
|
125
|
+
return isClaudeSkill || isCursorRule ? normalized : null;
|
|
126
|
+
}
|
|
127
|
+
function readYamlScalar(text, key) {
|
|
128
|
+
const match = text.match(new RegExp(`^${key}:\\s*(.*?)\\s*$`, "m"));
|
|
129
|
+
return match ? parseYamlScalar(match[1]) : "";
|
|
130
|
+
}
|
|
131
|
+
function readSkillRecords(text) {
|
|
132
|
+
const records = [];
|
|
133
|
+
let inSkills = false;
|
|
134
|
+
let current = null;
|
|
135
|
+
let inPaths = false;
|
|
136
|
+
for (const line of text.split(/\r?\n/)) {
|
|
137
|
+
if (line.trim() === "skills:") {
|
|
138
|
+
inSkills = true;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (!inSkills)
|
|
142
|
+
continue;
|
|
143
|
+
if (line.length > 0 && !line.startsWith(" "))
|
|
144
|
+
break;
|
|
145
|
+
const skillIdMatch = line.match(/^ {2}- skill_id:\s*(.*?)\s*$/);
|
|
146
|
+
if (skillIdMatch) {
|
|
147
|
+
if (current)
|
|
148
|
+
records.push(current);
|
|
149
|
+
current = {
|
|
150
|
+
skillId: parseYamlScalar(skillIdMatch[1]),
|
|
151
|
+
contentHash: "",
|
|
152
|
+
source: SKILL_SOURCE_HARNESS,
|
|
153
|
+
paths: []
|
|
154
|
+
};
|
|
155
|
+
inPaths = false;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (!current)
|
|
159
|
+
continue;
|
|
160
|
+
const hashMatch = line.match(/^ {4}content_hash:\s*(.*?)\s*$/);
|
|
161
|
+
if (hashMatch) {
|
|
162
|
+
current.contentHash = parseYamlScalar(hashMatch[1]);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const sourceMatch = line.match(/^ {4}source:\s*(.*?)\s*$/);
|
|
166
|
+
if (sourceMatch) {
|
|
167
|
+
current.source = parseYamlScalar(sourceMatch[1]) || SKILL_SOURCE_HARNESS;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (/^ {4}paths:\s*$/.test(line)) {
|
|
171
|
+
inPaths = true;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const pathMatch = inPaths ? line.match(/^ {6}-\s*(.*?)\s*$/) : null;
|
|
175
|
+
if (pathMatch) {
|
|
176
|
+
const value = parseYamlScalar(pathMatch[1]);
|
|
177
|
+
if (value)
|
|
178
|
+
current.paths.push(value);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (current)
|
|
182
|
+
records.push(current);
|
|
183
|
+
return records;
|
|
184
|
+
}
|
|
185
|
+
function readManagedPaths(text) {
|
|
186
|
+
const paths = [];
|
|
187
|
+
let inManagedPaths = false;
|
|
188
|
+
for (const line of text.split(/\r?\n/)) {
|
|
189
|
+
if (line.trim() === "managed_paths:") {
|
|
190
|
+
inManagedPaths = true;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (inManagedPaths && line.length > 0 && !line.startsWith(" "))
|
|
194
|
+
break;
|
|
195
|
+
if (!inManagedPaths)
|
|
196
|
+
continue;
|
|
197
|
+
const match = line.match(/^\s*-\s*(.*?)\s*$/);
|
|
198
|
+
if (match)
|
|
199
|
+
paths.push(parseYamlScalar(match[1]));
|
|
200
|
+
}
|
|
201
|
+
return paths.filter(Boolean);
|
|
202
|
+
}
|
|
203
|
+
function parseYamlScalar(raw) {
|
|
204
|
+
const value = raw.trim();
|
|
205
|
+
if (!value)
|
|
206
|
+
return "";
|
|
207
|
+
if (value.startsWith('"')) {
|
|
208
|
+
try {
|
|
209
|
+
return String(JSON.parse(value));
|
|
210
|
+
} catch {
|
|
211
|
+
return value.replace(/^"|"$/g, "");
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return value.replace(/^'|'$/g, "");
|
|
215
|
+
}
|
|
216
|
+
function yamlQuote(value) {
|
|
217
|
+
return JSON.stringify(value);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export { SKILL_SOURCE_HARNESS, SKILL_SOURCE_INTEGRATION_BUNDLED, readSkillsManifest, writeSkillsManifest, upsertSkillManifestRecord, pruneManagedSkillPaths };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/init/mcp.ts
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
5
|
+
function isOnPath(cmd) {
|
|
6
|
+
try {
|
|
7
|
+
execSync(`which ${cmd}`, { stdio: "pipe" });
|
|
8
|
+
return true;
|
|
9
|
+
} catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function detectAgents() {
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
const agents = [];
|
|
16
|
+
if (existsSync(join(cwd, ".claude")) || isOnPath("claude")) {
|
|
17
|
+
agents.push("Claude Code");
|
|
18
|
+
}
|
|
19
|
+
if (existsSync(join(cwd, ".cursor"))) {
|
|
20
|
+
agents.push("Cursor");
|
|
21
|
+
}
|
|
22
|
+
return agents;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { detectAgents };
|
package/dist/cli.js
CHANGED
|
@@ -1084,7 +1084,7 @@ function getUpdateNotice(options = {}) {
|
|
|
1084
1084
|
return [
|
|
1085
1085
|
"",
|
|
1086
1086
|
`Update available: moda-cli ${VERSION} → ${latest}`,
|
|
1087
|
-
"Run `npm install -g @moda-ai/cli@latest` to update."
|
|
1087
|
+
"Run `npm install -g @moda-ai/cli@latest` to update, then `moda skills install moda-cli` to refresh the agent skill."
|
|
1088
1088
|
].join(`
|
|
1089
1089
|
`);
|
|
1090
1090
|
}
|
|
@@ -1911,10 +1911,10 @@ var SKILLS_SUBCOMMAND_HELP = [
|
|
|
1911
1911
|
},
|
|
1912
1912
|
{
|
|
1913
1913
|
name: "install",
|
|
1914
|
-
description: "Install bundled integration guides into .claude/skills (offline; no API key)",
|
|
1915
|
-
usage: "moda skills install [--list] <id...>",
|
|
1914
|
+
description: "Install bundled integration guides into .claude/skills, or refresh the moda-cli skill (offline; no API key)",
|
|
1915
|
+
usage: "moda skills install [--list] <id...> | moda skills install moda-cli",
|
|
1916
1916
|
flags: [{ name: "--list", description: "List the bundled guides." }],
|
|
1917
|
-
examples: ["moda skills install --list", "moda skills install integration-node-openai"]
|
|
1917
|
+
examples: ["moda skills install moda-cli", "moda skills install --list", "moda skills install integration-node-openai"]
|
|
1918
1918
|
}
|
|
1919
1919
|
];
|
|
1920
1920
|
var PROMPTS_REGISTRY_SUBCOMMAND_HELP = [
|
|
@@ -2959,10 +2959,10 @@ function leafManifestFields(leaf, commands) {
|
|
|
2959
2959
|
import { fileURLToPath } from "url";
|
|
2960
2960
|
import { realpathSync } from "fs";
|
|
2961
2961
|
async function runPromptsCommand(...args) {
|
|
2962
|
-
return (await import("./prompts-
|
|
2962
|
+
return (await import("./prompts-b8qx3mrv.js")).runPromptsCommand(...args);
|
|
2963
2963
|
}
|
|
2964
2964
|
async function runSkillsCommand(...args) {
|
|
2965
|
-
return (await import("./skills-
|
|
2965
|
+
return (await import("./skills-96z55tj4.js")).runSkillsCommand(...args);
|
|
2966
2966
|
}
|
|
2967
2967
|
async function runFixCommand(...args) {
|
|
2968
2968
|
return (await import("./fix-w5z02wt4.js")).runFixCommand(...args);
|
|
@@ -2974,16 +2974,16 @@ async function runHarnessCommand(...args) {
|
|
|
2974
2974
|
return (await import("./harness-6vwehf47.js")).runHarnessCommand(...args);
|
|
2975
2975
|
}
|
|
2976
2976
|
async function runSyncCommand(...args) {
|
|
2977
|
-
return (await import("./sync-
|
|
2977
|
+
return (await import("./sync-drz7req6.js")).runSyncCommand(...args);
|
|
2978
2978
|
}
|
|
2979
2979
|
async function runRegistryCommand(...args) {
|
|
2980
2980
|
return (await import("./registry-ctnn8e26.js")).runRegistryCommand(...args);
|
|
2981
2981
|
}
|
|
2982
2982
|
async function runDoctorCommand(...args) {
|
|
2983
|
-
return (await import("./doctor-
|
|
2983
|
+
return (await import("./doctor-tvre7zvk.js")).runDoctorCommand(...args);
|
|
2984
2984
|
}
|
|
2985
2985
|
async function runStatusCommand(...args) {
|
|
2986
|
-
return (await import("./doctor-
|
|
2986
|
+
return (await import("./doctor-tvre7zvk.js")).runStatusCommand(...args);
|
|
2987
2987
|
}
|
|
2988
2988
|
async function runAuthCommand(...args) {
|
|
2989
2989
|
return (await import("./profile-commands-h8evz9x0.js")).runAuthCommand(...args);
|
|
@@ -2995,7 +2995,7 @@ async function runProfilesCommand(...args) {
|
|
|
2995
2995
|
return (await import("./profile-commands-h8evz9x0.js")).runProfilesCommand(...args);
|
|
2996
2996
|
}
|
|
2997
2997
|
async function runAgentCommand(...args) {
|
|
2998
|
-
return (await import("./agent-
|
|
2998
|
+
return (await import("./agent-g07tg64w.js")).runAgentCommand(...args);
|
|
2999
2999
|
}
|
|
3000
3000
|
async function runProductionCommand(...args) {
|
|
3001
3001
|
return (await import("./production-intelligence-7wb1djy2.js")).runProductionCommand(...args);
|
|
@@ -6143,7 +6143,7 @@ var commandRegistry = createCommandRegistry([
|
|
|
6143
6143
|
resetApiRequestCountBeforeRun: true,
|
|
6144
6144
|
telemetry: "result-and-error",
|
|
6145
6145
|
handler: async (context) => {
|
|
6146
|
-
const { runInit } = await import("./index-
|
|
6146
|
+
const { runInit } = await import("./index-75vcntpe.js");
|
|
6147
6147
|
if (context.outputMode === "agent-stream") {
|
|
6148
6148
|
context.output.writeEvent({
|
|
6149
6149
|
event: "started",
|
|
@@ -7074,6 +7074,7 @@ var commandRegistry = createCommandRegistry([
|
|
|
7074
7074
|
"moda skills status",
|
|
7075
7075
|
"moda skills pull",
|
|
7076
7076
|
"moda skills push",
|
|
7077
|
+
"moda skills install moda-cli",
|
|
7077
7078
|
"moda skills install --list",
|
|
7078
7079
|
"moda skills install integration-node-openai"
|
|
7079
7080
|
],
|
|
@@ -7352,6 +7353,56 @@ function assertRoutedFlags(route, flags) {
|
|
|
7352
7353
|
const args = Object.fromEntries(Object.keys(flags).filter((flag) => !aliases.has(flag.replace(/_/g, "-"))).map((flag) => [flag.replace(/-/g, "_"), true]));
|
|
7353
7354
|
parseCommandFlags(shape.passthrough(), args, route.label, route.flagAliases ? { flagAliases: route.flagAliases } : {});
|
|
7354
7355
|
}
|
|
7356
|
+
var DAYS_TO_TIME_RANGE = { 1: "24h", 3: "3d", 7: "7d", 30: "30d", 90: "90d" };
|
|
7357
|
+
function timeRangeAsDays(value) {
|
|
7358
|
+
if (value === "24h")
|
|
7359
|
+
return 1;
|
|
7360
|
+
const match = /^(\d+)d$/.exec(value.trim());
|
|
7361
|
+
const days = match ? Number(match[1]) : NaN;
|
|
7362
|
+
return Number.isInteger(days) && days >= 1 && days <= 90 ? days : undefined;
|
|
7363
|
+
}
|
|
7364
|
+
function daysBackAcceptsAll(handler) {
|
|
7365
|
+
return specsOf(handler).some((spec) => {
|
|
7366
|
+
return schemaShape(spec.schema).days_back?.safeParse("all").success === true;
|
|
7367
|
+
});
|
|
7368
|
+
}
|
|
7369
|
+
function foldWindowFlag(handler, route, ...targets) {
|
|
7370
|
+
const flags = targets[0];
|
|
7371
|
+
const accepted = new Set([
|
|
7372
|
+
...specsOf(handler).flatMap((spec) => specFlagNames(spec)),
|
|
7373
|
+
...route?.allowedFlags ?? []
|
|
7374
|
+
]);
|
|
7375
|
+
const takesDays = accepted.has("days-back");
|
|
7376
|
+
const takesRange = accepted.has("time-range");
|
|
7377
|
+
if (takesDays === takesRange)
|
|
7378
|
+
return;
|
|
7379
|
+
const [from, to] = takesRange ? ["days-back", "time-range"] : ["time-range", "days-back"];
|
|
7380
|
+
const raw = flags[from];
|
|
7381
|
+
if (raw === undefined || raw === "true" || raw.trim() === "")
|
|
7382
|
+
return;
|
|
7383
|
+
const shown = canonicalCommandText(`moda ${route?.leaf.path ?? handler}`);
|
|
7384
|
+
if (flags[to] !== undefined) {
|
|
7385
|
+
throw new CliInputError(`Pass one time window: --${to} or --${from}, not both.`, `'${shown}' takes --${to}. See '${shown} --help'.`, `conflicting_window:${from}`, { field: `--${from}`, suggestedCommands: [`${shown} --help`] });
|
|
7386
|
+
}
|
|
7387
|
+
let value;
|
|
7388
|
+
if (takesRange) {
|
|
7389
|
+
value = /^\d+$/.test(raw.trim()) ? DAYS_TO_TIME_RANGE[Number(raw.trim())] : undefined;
|
|
7390
|
+
} else if (raw.trim() === "all" && daysBackAcceptsAll(handler)) {
|
|
7391
|
+
value = "all";
|
|
7392
|
+
} else {
|
|
7393
|
+
const days = timeRangeAsDays(raw);
|
|
7394
|
+
value = days === undefined ? undefined : String(days);
|
|
7395
|
+
}
|
|
7396
|
+
if (value === undefined) {
|
|
7397
|
+
const allowed = takesRange ? Object.values(DAYS_TO_TIME_RANGE).map((range) => `--time-range=${range}`) : ["--days-back=N (1-90)"];
|
|
7398
|
+
throw new CliInputError(`--${from}=${raw} has no exact --${to} equivalent for '${shown}', so nothing was run.`, takesRange ? `'${shown}' takes --time-range=1h|24h|3d|7d|30d|90d|all; --days-back maps only to 1, 3, 7, 30 or 90 days.` : `'${shown}' takes --days-back=N (1-90); --time-range maps only to 24h or a whole number of days up to 90d.`, `unknown_flag:${from}`, { field: `--${from}`, allowed, suggestedCommands: [`${shown} --help`] });
|
|
7399
|
+
}
|
|
7400
|
+
for (const target of new Set(targets)) {
|
|
7401
|
+
delete target[from];
|
|
7402
|
+
target[to] = value;
|
|
7403
|
+
}
|
|
7404
|
+
return from;
|
|
7405
|
+
}
|
|
7355
7406
|
var EMPTY_VALUE_ALLOWED = {
|
|
7356
7407
|
context: new Set(["msg-index", "message-index"])
|
|
7357
7408
|
};
|
|
@@ -7416,6 +7467,8 @@ async function dispatchParsedCommandInner(typedParsed, startedAt) {
|
|
|
7416
7467
|
canonicalizeFlagKeys(flags, typedParsed.bareFlags);
|
|
7417
7468
|
const switchError = normalizeBooleanFlags(flags);
|
|
7418
7469
|
const typedFlags = stripUpdateCheckFlag(stripTelemetryFlag(flags));
|
|
7470
|
+
const reportedFlags = { ...typedFlags };
|
|
7471
|
+
let foldedWindowFlag;
|
|
7419
7472
|
const telemetryDisabled = isTelemetryDisabled(flags);
|
|
7420
7473
|
if (telemetryDisabled)
|
|
7421
7474
|
discardSpoolOnEnvOptOut();
|
|
@@ -7506,7 +7559,7 @@ async function dispatchParsedCommandInner(typedParsed, startedAt) {
|
|
|
7506
7559
|
apiKey: input.apiKey ?? resolveApiKey({ profile: telemetryProfile }),
|
|
7507
7560
|
profile: telemetryProfile,
|
|
7508
7561
|
command: identity2?.command ?? handlerTelemetry,
|
|
7509
|
-
flags:
|
|
7562
|
+
flags: reportedFlags,
|
|
7510
7563
|
status: input.status,
|
|
7511
7564
|
...input.error !== undefined ? { errorType: classifyCliError(input.error), validationReason: cliValidationReason(input.error) } : {},
|
|
7512
7565
|
exitCode: input.exitCode,
|
|
@@ -7514,7 +7567,7 @@ async function dispatchParsedCommandInner(typedParsed, startedAt) {
|
|
|
7514
7567
|
apiRequestCount: input.apiRequestCount,
|
|
7515
7568
|
adoption,
|
|
7516
7569
|
...identity2?.legacyAlias ? { legacyAlias: identity2.legacyAlias } : {},
|
|
7517
|
-
acceptedFlags: telemetryAcceptedFlags(definition2, route),
|
|
7570
|
+
acceptedFlags: new Set([...telemetryAcceptedFlags(definition2, route), ...foldedWindowFlag ? [foldedWindowFlag] : []]),
|
|
7518
7571
|
knownFlags: telemetryKnownFlags()
|
|
7519
7572
|
});
|
|
7520
7573
|
};
|
|
@@ -7556,6 +7609,7 @@ async function dispatchParsedCommandInner(typedParsed, startedAt) {
|
|
|
7556
7609
|
if (switchError)
|
|
7557
7610
|
return failEarly(switchError, definition, identity);
|
|
7558
7611
|
try {
|
|
7612
|
+
foldedWindowFlag = foldWindowFlag(route?.handler ?? definition.name, route, typedFlags, commandFlags);
|
|
7559
7613
|
if (route)
|
|
7560
7614
|
assertRoutedFlags(route, typedFlags);
|
|
7561
7615
|
assertFlagValues(definition.name, parsed.positionals, typedFlags, typedParsed.bareFlags, route);
|
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
promptsDiscoveredCheck,
|
|
4
4
|
runDoctorCommand,
|
|
5
5
|
runStatusCommand
|
|
6
|
-
} from "./cli-
|
|
6
|
+
} from "./cli-5fstvkva.js";
|
|
7
7
|
import"./cli-9bgnvth2.js";
|
|
8
|
-
import"./cli-
|
|
8
|
+
import"./cli-6j8h2ave.js";
|
|
9
9
|
import"./cli-mm48br00.js";
|
|
10
10
|
import"./cli-xq45etg1.js";
|
|
11
11
|
import"./cli-dr6x0zv7.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildDoctorReport
|
|
3
|
-
} from "./cli-
|
|
3
|
+
} from "./cli-5fstvkva.js";
|
|
4
4
|
import {
|
|
5
5
|
selectTenantAndCreateKey
|
|
6
6
|
} from "./cli-9bgk5jeh.js";
|
|
@@ -16,20 +16,23 @@ import {
|
|
|
16
16
|
bundledSkillsDir,
|
|
17
17
|
installIntegrationSkill,
|
|
18
18
|
readBundledIndex
|
|
19
|
-
} from "./cli-
|
|
19
|
+
} from "./cli-mc3z77bv.js";
|
|
20
20
|
import {
|
|
21
21
|
agentsMdProblem,
|
|
22
22
|
runSkillSync
|
|
23
|
-
} from "./cli-
|
|
23
|
+
} from "./cli-k007ztbs.js";
|
|
24
|
+
import"./cli-qm5b1vck.js";
|
|
25
|
+
import {
|
|
26
|
+
detectAgents
|
|
27
|
+
} from "./cli-rjav0vj1.js";
|
|
24
28
|
import {
|
|
25
|
-
detectAgents,
|
|
26
29
|
installRules,
|
|
27
30
|
summarizeInstall
|
|
28
|
-
} from "./cli-
|
|
31
|
+
} from "./cli-hcwdz9sd.js";
|
|
29
32
|
import {
|
|
30
33
|
initPrompts,
|
|
31
34
|
runPromptSync
|
|
32
|
-
} from "./cli-
|
|
35
|
+
} from "./cli-6j8h2ave.js";
|
|
33
36
|
import"./cli-mm48br00.js";
|
|
34
37
|
import {
|
|
35
38
|
codingAgentDisplayName,
|
|
@@ -23,9 +23,9 @@ import {
|
|
|
23
23
|
runPromptAb,
|
|
24
24
|
selectProblemCases,
|
|
25
25
|
waitLabel
|
|
26
|
-
} from "./cli-
|
|
26
|
+
} from "./cli-602yx84w.js";
|
|
27
|
+
import"./cli-6j8h2ave.js";
|
|
27
28
|
import"./cli-yz8xcpyk.js";
|
|
28
|
-
import"./cli-zgj5bakm.js";
|
|
29
29
|
import"./cli-mm48br00.js";
|
|
30
30
|
import"./cli-dr6x0zv7.js";
|
|
31
31
|
import"./cli-n2hng3gv.js";
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
formatVerdict,
|
|
8
8
|
rerunWithYes,
|
|
9
9
|
resolvePromptArmSpec
|
|
10
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-602yx84w.js";
|
|
11
|
+
import"./cli-6j8h2ave.js";
|
|
11
12
|
import"./cli-yz8xcpyk.js";
|
|
12
|
-
import"./cli-zgj5bakm.js";
|
|
13
13
|
import"./cli-mm48br00.js";
|
|
14
14
|
import"./cli-dr6x0zv7.js";
|
|
15
15
|
import"./cli-n2hng3gv.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bundledSkillVersion,
|
|
3
|
+
claudeSkillToCursorRule,
|
|
4
|
+
installRules,
|
|
5
|
+
summarizeInstall
|
|
6
|
+
} from "./cli-hcwdz9sd.js";
|
|
7
|
+
import"./cli-0v6na3yp.js";
|
|
8
|
+
export {
|
|
9
|
+
summarizeInstall,
|
|
10
|
+
installRules,
|
|
11
|
+
claudeSkillToCursorRule,
|
|
12
|
+
bundledSkillVersion
|
|
13
|
+
};
|
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
pullSkills,
|
|
4
4
|
runSkillSync,
|
|
5
5
|
runSkillsCommand
|
|
6
|
-
} from "./cli-
|
|
7
|
-
import"./cli-
|
|
6
|
+
} from "./cli-k007ztbs.js";
|
|
7
|
+
import"./cli-qm5b1vck.js";
|
|
8
|
+
import"./cli-rjav0vj1.js";
|
|
9
|
+
import"./cli-hcwdz9sd.js";
|
|
8
10
|
import"./cli-dr6x0zv7.js";
|
|
9
11
|
import"./cli-n2hng3gv.js";
|
|
10
12
|
import"./cli-e23g9ywa.js";
|
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
installIntegrationSkill,
|
|
4
4
|
readBundledIndex,
|
|
5
5
|
readBundledSkillMd
|
|
6
|
-
} from "./cli-
|
|
7
|
-
import"./cli-
|
|
6
|
+
} from "./cli-mc3z77bv.js";
|
|
7
|
+
import"./cli-qm5b1vck.js";
|
|
8
|
+
import"./cli-rjav0vj1.js";
|
|
9
|
+
import"./cli-hcwdz9sd.js";
|
|
8
10
|
import"./cli-0v6na3yp.js";
|
|
9
11
|
export {
|
|
10
12
|
readBundledSkillMd,
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runSkillsCommand
|
|
3
|
-
} from "./cli-
|
|
4
|
-
import"./cli-
|
|
3
|
+
} from "./cli-k007ztbs.js";
|
|
4
|
+
import"./cli-qm5b1vck.js";
|
|
5
|
+
import"./cli-rjav0vj1.js";
|
|
6
|
+
import"./cli-hcwdz9sd.js";
|
|
5
7
|
import {
|
|
6
8
|
runPromptsCommand
|
|
7
|
-
} from "./cli-
|
|
9
|
+
} from "./cli-6j8h2ave.js";
|
|
8
10
|
import"./cli-mm48br00.js";
|
|
9
11
|
import {
|
|
10
12
|
runHarnessCommand
|
package/package.json
CHANGED
package/skills/moda-cli/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: moda-cli
|
|
3
|
-
version: 3.0.
|
|
3
|
+
version: 3.0.2
|
|
4
4
|
description: Investigate a production AI agent with the Moda CLI (`moda`) - find where something happened in real traces (traces search, then traces get), check agent health, rank what to fix (investigate, problems), and dig into tool failures, user frustration, and hallucinations. Also prompt registry sync/promote, replays, custom signals, and false-positive marking. Use when the user mentions Moda, production traces or conversations of their agent, agent failures, or asks what to fix.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -24,7 +24,9 @@ in `references/`; read one only when the task needs it.
|
|
|
24
24
|
interactive wizard that writes to the repo.
|
|
25
25
|
- Not installed: `npx -y -p @moda-ai/cli moda <command>`.
|
|
26
26
|
- If a response carries `meta.update`, the installed CLI is out of date and
|
|
27
|
-
may not match this guide; tell the user how to update
|
|
27
|
+
may not match this guide; tell the user how to update
|
|
28
|
+
(`npm install -g @moda-ai/cli@latest`, then `moda skills install moda-cli`
|
|
29
|
+
to refresh this guide).
|
|
28
30
|
- Flags: `moda <noun> <verb> --help` lists every flag the command accepts, with
|
|
29
31
|
types, ranges, and defaults. Each command rejects flags it does not take and
|
|
30
32
|
the error lists the valid ones; read that list instead of guessing.
|
|
@@ -74,7 +76,9 @@ Details: `references/output-protocol.md`.
|
|
|
74
76
|
|
|
75
77
|
Time windows differ by command: `--days-back=N` (1-90) on overview,
|
|
76
78
|
investigate, problems, and detection commands; `--time-range=1h|24h|3d|7d|30d|90d|all`
|
|
77
|
-
on search, traces, and clusters.
|
|
79
|
+
on search, traces, and clusters. The other spelling works when it names the
|
|
80
|
+
same span (`--days-back=7` is `--time-range=7d`); anything else fails and
|
|
81
|
+
names the closest values. `moda <noun> <verb> --help` shows which.
|
|
78
82
|
|
|
79
83
|
## Fixing a Problem
|
|
80
84
|
|
|
@@ -237,8 +237,12 @@ Treat an answer as degraded if **any** of these hold:
|
|
|
237
237
|
|
|
238
238
|
#### Time-window flags by command
|
|
239
239
|
|
|
240
|
-
A command
|
|
241
|
-
|
|
240
|
+
A command takes one window flag. The other spelling is rewritten when it names
|
|
241
|
+
the same span: `--days-back=1|3|7|30|90` is `--time-range=24h|3d|7d|30d|90d`,
|
|
242
|
+
and `--time-range=24h` or `Nd` (up to 90d) is `--days-back`. A value with no
|
|
243
|
+
exact match (`--days-back=14` on `traces search`, `--time-range=all` or `1h`
|
|
244
|
+
on a detection command) fails, as does passing both. Out-of-range values fail
|
|
245
|
+
instead of being clamped.
|
|
242
246
|
|
|
243
247
|
| Flag | Commands | Allowed values | Default |
|
|
244
248
|
| --- | --- | --- | --- |
|