@mutagent/cli 0.1.247 → 0.1.249
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/cli.js +682 -172
- package/dist/bin/cli.js.map +15 -10
- package/dist/index.js +19 -7
- package/dist/index.js.map +5 -4
- package/package.json +2 -2
package/dist/bin/cli.js
CHANGED
|
@@ -28,12 +28,23 @@ var __export = (target, all) => {
|
|
|
28
28
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
29
29
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
30
30
|
|
|
31
|
+
// src/lib/secure-file.ts
|
|
32
|
+
import { writeFileSync, chmodSync } from "fs";
|
|
33
|
+
function writeSecureFile(path, contents) {
|
|
34
|
+
writeFileSync(path, contents, { mode: SECURE_FILE_MODE });
|
|
35
|
+
try {
|
|
36
|
+
chmodSync(path, SECURE_FILE_MODE);
|
|
37
|
+
} catch {}
|
|
38
|
+
}
|
|
39
|
+
var SECURE_FILE_MODE = 384;
|
|
40
|
+
var init_secure_file = () => {};
|
|
41
|
+
|
|
31
42
|
// src/lib/config.ts
|
|
32
43
|
import { cosmiconfigSync } from "cosmiconfig";
|
|
33
44
|
import { z } from "zod";
|
|
34
45
|
import { homedir } from "os";
|
|
35
46
|
import { join } from "path";
|
|
36
|
-
import { existsSync, readFileSync,
|
|
47
|
+
import { existsSync, readFileSync, mkdirSync } from "fs";
|
|
37
48
|
function parseJsonSafe(content, schema) {
|
|
38
49
|
try {
|
|
39
50
|
const parsed = JSON.parse(content);
|
|
@@ -72,7 +83,7 @@ function saveCredentials(apiKey, endpoint) {
|
|
|
72
83
|
apiKey,
|
|
73
84
|
endpoint: endpoint ?? config.endpoint
|
|
74
85
|
};
|
|
75
|
-
|
|
86
|
+
writeSecureFile(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
|
|
76
87
|
}
|
|
77
88
|
function saveFullCredentials(creds) {
|
|
78
89
|
if (!existsSync(CREDENTIALS_DIR)) {
|
|
@@ -96,11 +107,11 @@ function saveFullCredentials(creds) {
|
|
|
96
107
|
defaultOrganization: creds.organizationId ?? existingCredentials.defaultOrganization,
|
|
97
108
|
expiresAt: creds.expiresAt
|
|
98
109
|
};
|
|
99
|
-
|
|
110
|
+
writeSecureFile(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
|
|
100
111
|
}
|
|
101
112
|
function clearCredentials() {
|
|
102
113
|
if (existsSync(CREDENTIALS_FILE)) {
|
|
103
|
-
|
|
114
|
+
writeSecureFile(CREDENTIALS_FILE, JSON.stringify({}));
|
|
104
115
|
}
|
|
105
116
|
}
|
|
106
117
|
function hasCredentials() {
|
|
@@ -129,7 +140,7 @@ function setDefaultWorkspace(workspaceId, source = "user") {
|
|
|
129
140
|
defaultWorkspace: workspaceId,
|
|
130
141
|
defaultWorkspaceSource: source
|
|
131
142
|
};
|
|
132
|
-
|
|
143
|
+
writeSecureFile(CREDENTIALS_FILE, JSON.stringify(updated, null, 2));
|
|
133
144
|
}
|
|
134
145
|
function setDefaultOrganization(organizationId) {
|
|
135
146
|
if (!existsSync(CREDENTIALS_DIR)) {
|
|
@@ -145,10 +156,11 @@ function setDefaultOrganization(organizationId) {
|
|
|
145
156
|
...existingCredentials,
|
|
146
157
|
defaultOrganization: organizationId
|
|
147
158
|
};
|
|
148
|
-
|
|
159
|
+
writeSecureFile(CREDENTIALS_FILE, JSON.stringify(updated, null, 2));
|
|
149
160
|
}
|
|
150
161
|
var configSchema, credentialsSchema, CREDENTIALS_DIR, CREDENTIALS_FILE;
|
|
151
162
|
var init_config = __esm(() => {
|
|
163
|
+
init_secure_file();
|
|
152
164
|
configSchema = z.object({
|
|
153
165
|
apiKey: z.string().optional(),
|
|
154
166
|
endpoint: z.string().default("https://api.mutagent.io"),
|
|
@@ -866,10 +878,10 @@ var init_sdk_client = __esm(() => {
|
|
|
866
878
|
});
|
|
867
879
|
|
|
868
880
|
// src/bin/cli.ts
|
|
869
|
-
import { Command as
|
|
870
|
-
import
|
|
881
|
+
import { Command as Command13 } from "commander";
|
|
882
|
+
import chalk19 from "chalk";
|
|
871
883
|
import { readFileSync as readFileSync10 } from "fs";
|
|
872
|
-
import { join as
|
|
884
|
+
import { join as join14, dirname as dirname3 } from "path";
|
|
873
885
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
874
886
|
|
|
875
887
|
// src/commands/auth.ts
|
|
@@ -878,7 +890,7 @@ init_sdk_client();
|
|
|
878
890
|
import { Command } from "commander";
|
|
879
891
|
import chalk4 from "chalk";
|
|
880
892
|
import { existsSync as existsSync5 } from "fs";
|
|
881
|
-
import { join as
|
|
893
|
+
import { join as join7 } from "path";
|
|
882
894
|
|
|
883
895
|
// src/lib/output.ts
|
|
884
896
|
import chalk from "chalk";
|
|
@@ -1117,6 +1129,9 @@ function scaffoldRcConfig(cwd = process.cwd(), deps = {}) {
|
|
|
1117
1129
|
init_errors();
|
|
1118
1130
|
init_config();
|
|
1119
1131
|
import { spawn as spawn2 } from "child_process";
|
|
1132
|
+
import { mkdirSync as mkdirSync3, readdirSync } from "fs";
|
|
1133
|
+
import { homedir as homedir3 } from "os";
|
|
1134
|
+
import { join as join6 } from "path";
|
|
1120
1135
|
|
|
1121
1136
|
// src/lib/installer-helix.ts
|
|
1122
1137
|
init_errors();
|
|
@@ -1200,8 +1215,6 @@ async function installHelix(opts, deps = {}) {
|
|
|
1200
1215
|
const locateInitBin = deps.locateInitBin ?? defaultLocateInitBin;
|
|
1201
1216
|
const binPath = await locateInitBin(versionDir);
|
|
1202
1217
|
const initArgs = ["init"];
|
|
1203
|
-
if (opts.global)
|
|
1204
|
-
initArgs.push("--global");
|
|
1205
1218
|
const runInit = deps.runInit ?? defaultRunInit;
|
|
1206
1219
|
const code = await runInit(binPath, initArgs, process.cwd());
|
|
1207
1220
|
if (code !== 0) {
|
|
@@ -1277,7 +1290,7 @@ function defaultLocateInitBin(extractedDir) {
|
|
|
1277
1290
|
const pkgDir = existsSync4(join5(extractedDir, "package", "package.json")) ? join5(extractedDir, "package") : extractedDir;
|
|
1278
1291
|
const pkgJsonPath = join5(pkgDir, "package.json");
|
|
1279
1292
|
if (!existsSync4(pkgJsonPath)) {
|
|
1280
|
-
return Promise.reject(new MutagentError("INSTALL_FAILED",
|
|
1293
|
+
return Promise.reject(new MutagentError("INSTALL_FAILED", `Could not find package.json under ${pkgDir}.`, "The package layout is unexpected — retry the install, or report it."));
|
|
1281
1294
|
}
|
|
1282
1295
|
const raw = JSON.parse(readFileSync4(pkgJsonPath, "utf-8"));
|
|
1283
1296
|
const binRel = resolveBinField(raw);
|
|
@@ -1292,9 +1305,9 @@ function defaultLocateInitBin(extractedDir) {
|
|
|
1292
1305
|
}
|
|
1293
1306
|
function defaultRunInit(binPath, args, cwd) {
|
|
1294
1307
|
return new Promise((resolve, reject) => {
|
|
1295
|
-
const child = spawn("node", [binPath, ...args], { cwd, stdio: "
|
|
1308
|
+
const child = spawn("node", [binPath, ...args], { cwd, stdio: ["ignore", 2, 2] });
|
|
1296
1309
|
child.on("error", (err) => {
|
|
1297
|
-
reject(new MutagentError("INSTALL_FAILED", `Failed to run the
|
|
1310
|
+
reject(new MutagentError("INSTALL_FAILED", `Failed to run the package init: ${err.message}`, 'Ensure "node" is installed and available on your PATH.'));
|
|
1298
1311
|
});
|
|
1299
1312
|
child.on("close", (code) => {
|
|
1300
1313
|
resolve(code ?? 1);
|
|
@@ -1356,6 +1369,10 @@ var NPM_PACKAGES = {
|
|
|
1356
1369
|
diagnostics: "@mutagent/diagnostics",
|
|
1357
1370
|
evaluator: "@mutagent/evaluator"
|
|
1358
1371
|
};
|
|
1372
|
+
var INIT_BINS = {
|
|
1373
|
+
diagnostics: "mutagent-diagnostics",
|
|
1374
|
+
evaluator: "mutagent-evaluator"
|
|
1375
|
+
};
|
|
1359
1376
|
var defaultRunner = (cmd, args) => new Promise((resolve, reject) => {
|
|
1360
1377
|
const child = spawn2(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
1361
1378
|
let stdout = "";
|
|
@@ -1373,6 +1390,29 @@ var defaultRunner = (cmd, args) => new Promise((resolve, reject) => {
|
|
|
1373
1390
|
resolve({ code: code ?? 1, stdout, stderr });
|
|
1374
1391
|
});
|
|
1375
1392
|
});
|
|
1393
|
+
function countProjectFiles(root) {
|
|
1394
|
+
const roots = [".claude", ".codex", ".agents"];
|
|
1395
|
+
let n = 0;
|
|
1396
|
+
const walk = (dir, depth) => {
|
|
1397
|
+
if (depth > 6)
|
|
1398
|
+
return;
|
|
1399
|
+
let entries;
|
|
1400
|
+
try {
|
|
1401
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
1402
|
+
} catch {
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
for (const e of entries) {
|
|
1406
|
+
if (e.isDirectory())
|
|
1407
|
+
walk(join6(dir, e.name), depth + 1);
|
|
1408
|
+
else
|
|
1409
|
+
n++;
|
|
1410
|
+
}
|
|
1411
|
+
};
|
|
1412
|
+
for (const r of roots)
|
|
1413
|
+
walk(join6(root, r), 0);
|
|
1414
|
+
return n;
|
|
1415
|
+
}
|
|
1376
1416
|
function isValidPackage(pkg) {
|
|
1377
1417
|
return VALID_PACKAGES.includes(pkg);
|
|
1378
1418
|
}
|
|
@@ -1384,6 +1424,16 @@ async function installPackage(pkg, opts, deps = {}) {
|
|
|
1384
1424
|
if (!isAuthed()) {
|
|
1385
1425
|
throw new MutagentError("AUTH_REQUIRED", `Authentication required to install ${pkg}.`, "Run: mutagent login");
|
|
1386
1426
|
}
|
|
1427
|
+
const projectPath = process.cwd();
|
|
1428
|
+
if (opts.global === true) {
|
|
1429
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--global is no longer supported: install scope is no longer selectable.", [
|
|
1430
|
+
"Every install is project-scoped now:",
|
|
1431
|
+
` the package resolves into ~/.mutagent/<pkg>/<version>, and its skill`,
|
|
1432
|
+
` is installed into the current project (${projectPath}).`,
|
|
1433
|
+
"Nothing is installed globally. Re-run without --global."
|
|
1434
|
+
].join(`
|
|
1435
|
+
`));
|
|
1436
|
+
}
|
|
1387
1437
|
if (!isValidPackage(pkg)) {
|
|
1388
1438
|
throw new MutagentError("INVALID_ARGUMENTS", `Unknown package "${pkg}". Valid: ${VALID_PACKAGES.join(", ")}`, "Run: mutagent install --help");
|
|
1389
1439
|
}
|
|
@@ -1392,27 +1442,41 @@ async function installPackage(pkg, opts, deps = {}) {
|
|
|
1392
1442
|
}
|
|
1393
1443
|
const version = opts.version ?? VERSION_MATRIX[pkg];
|
|
1394
1444
|
if (pkg === "helix") {
|
|
1395
|
-
const { version: resolved } = await installHelix({ harness: opts.harness,
|
|
1445
|
+
const { version: resolved } = await installHelix({ harness: opts.harness, version }, deps.helix ?? {});
|
|
1396
1446
|
return finish({
|
|
1397
1447
|
package: pkg,
|
|
1398
1448
|
version: resolved,
|
|
1399
1449
|
harness: opts.harness,
|
|
1400
|
-
|
|
1450
|
+
scope: "project",
|
|
1451
|
+
projectPath,
|
|
1452
|
+
skillInstalled: true
|
|
1401
1453
|
}, deps);
|
|
1402
1454
|
}
|
|
1403
1455
|
const npmPackage = NPM_PACKAGES[pkg];
|
|
1404
|
-
const
|
|
1456
|
+
const installRoot = join6(deps.homeDir ?? join6(homedir3(), ".mutagent", pkg), version);
|
|
1457
|
+
mkdirSync3(installRoot, { recursive: true });
|
|
1458
|
+
const args = ["install", "--prefix", installRoot, "--no-save", `${npmPackage}@${version}`];
|
|
1405
1459
|
const runner = deps.runner ?? defaultRunner;
|
|
1406
1460
|
const result = await runner("npm", args);
|
|
1407
1461
|
if (result.code !== 0) {
|
|
1408
1462
|
const detail = result.stderr.trim();
|
|
1409
|
-
throw new MutagentError("INSTALL_FAILED", `npm failed to install ${npmPackage}@${version} (exit ${String(result.code)}).${detail ? ` ${detail.slice(0, 200)}` : ""}`,
|
|
1410
|
-
}
|
|
1463
|
+
throw new MutagentError("INSTALL_FAILED", `npm failed to install ${npmPackage}@${version} (exit ${String(result.code)}).${detail ? ` ${detail.slice(0, 200)}` : ""}`, `Verify the package and version exist, and that ${installRoot} is writable.`);
|
|
1464
|
+
}
|
|
1465
|
+
const pkgDir = join6(installRoot, "node_modules", npmPackage);
|
|
1466
|
+
const initBin = await (deps.locateInitBin ?? defaultLocateInitBin)(pkgDir);
|
|
1467
|
+
const countFiles = deps.countProjectFiles ?? countProjectFiles;
|
|
1468
|
+
const before = countFiles(projectPath);
|
|
1469
|
+
const initCode = await (deps.runInit ?? defaultRunInit)(initBin, ["init"], projectPath);
|
|
1470
|
+
const landed = countFiles(projectPath) > before;
|
|
1411
1471
|
return finish({
|
|
1412
1472
|
package: pkg,
|
|
1413
1473
|
version,
|
|
1414
1474
|
harness: opts.harness,
|
|
1415
|
-
|
|
1475
|
+
scope: "project",
|
|
1476
|
+
projectPath,
|
|
1477
|
+
installRoot,
|
|
1478
|
+
skillInstalled: initCode === 0 && landed,
|
|
1479
|
+
...initCode === 0 && landed ? {} : { nextStep: `${INIT_BINS[pkg]} init` },
|
|
1416
1480
|
command: `npm ${args.join(" ")}`
|
|
1417
1481
|
}, deps);
|
|
1418
1482
|
}
|
|
@@ -1912,7 +1976,7 @@ Examples:
|
|
|
1912
1976
|
}
|
|
1913
1977
|
const isValid = await validateApiKey(apiKey, endpoint);
|
|
1914
1978
|
const cwd = process.cwd();
|
|
1915
|
-
const hasOnboarding = existsSync5(
|
|
1979
|
+
const hasOnboarding = existsSync5(join7(cwd, ".mutagentrc.json"));
|
|
1916
1980
|
if (isJson) {
|
|
1917
1981
|
const statusResult = {
|
|
1918
1982
|
authenticated: isValid,
|
|
@@ -3301,14 +3365,14 @@ init_config();
|
|
|
3301
3365
|
import { Command as Command7 } from "commander";
|
|
3302
3366
|
import chalk13 from "chalk";
|
|
3303
3367
|
import { readFileSync as readFileSync5 } from "fs";
|
|
3304
|
-
import { join as
|
|
3368
|
+
import { join as join9 } from "path";
|
|
3305
3369
|
init_errors();
|
|
3306
3370
|
|
|
3307
3371
|
// src/commands/skills.ts
|
|
3308
3372
|
import { Command as Command6 } from "commander";
|
|
3309
3373
|
import chalk12 from "chalk";
|
|
3310
|
-
import { existsSync as existsSync6, mkdirSync as
|
|
3311
|
-
import { dirname, join as
|
|
3374
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
3375
|
+
import { dirname, join as join8 } from "path";
|
|
3312
3376
|
import { execSync } from "child_process";
|
|
3313
3377
|
|
|
3314
3378
|
// src/generated/skill-content.ts
|
|
@@ -3523,15 +3587,23 @@ user's UI and they cannot read them.
|
|
|
3523
3587
|
When ANY mutagent CLI command returns a non-zero exit code or an error response:
|
|
3524
3588
|
|
|
3525
3589
|
1. **Show the error to the user** (always) -- reproduce the exact command and output.
|
|
3526
|
-
2. **ASK the user** if they want to file a bug report
|
|
3527
|
-
3. **On user approval**, send feedback
|
|
3590
|
+
2. **ASK the user** if they want to file a bug report.
|
|
3591
|
+
3. **On user approval**, send the feedback — **without a transcript**:
|
|
3528
3592
|
\`\`\`bash
|
|
3529
3593
|
mutagent feedback send "<one-line summary of the failure>" \\
|
|
3530
|
-
--category cli --
|
|
3594
|
+
--category cli --json
|
|
3531
3595
|
\`\`\`
|
|
3532
|
-
- \`--attach-transcript\` (bare) auto-detects the newest coding-agent session JSONL and uploads it as context.
|
|
3533
3596
|
- Use \`--category stage:<spec|build|evaluate|diagnose|optimize>\` when the failure is about a specific lifecycle stage rather than the CLI itself.
|
|
3534
3597
|
|
|
3598
|
+
**Do NOT pass \`--attach-transcript\` unless the user explicitly asks for it.** It uploads
|
|
3599
|
+
the coding-agent session JSONL — source code, absolute paths (including their username),
|
|
3600
|
+
branch and repository names, internal hostnames, and the business context of their prompts.
|
|
3601
|
+
Credentials are scanned for and redacted, but detection is **best-effort**.
|
|
3602
|
+
|
|
3603
|
+
Approval to "file a bug report" is **NOT** approval to upload a session. They are separate
|
|
3604
|
+
questions and must be asked separately. If you believe the transcript would genuinely help,
|
|
3605
|
+
say exactly what it contains and ask a second time.
|
|
3606
|
+
|
|
3535
3607
|
See [workflows/feedback.md](./workflows/feedback.md) for the full feedback surface.
|
|
3536
3608
|
|
|
3537
3609
|
### If \`mutagent feedback send\` itself fails
|
|
@@ -3615,9 +3687,9 @@ attached automatically — you never set it manually.
|
|
|
3615
3687
|
|
|
3616
3688
|
3. Compose the feedback body from the user's own words
|
|
3617
3689
|
→ pick --category (default cli; use stage:<x> for a lifecycle stage)
|
|
3618
|
-
→ offer --attach-transcript
|
|
3690
|
+
→ do NOT offer --attach-transcript unless the user explicitly asks for it (see Transcript safety below)
|
|
3619
3691
|
|
|
3620
|
-
4. mutagent feedback send "<body>" --category <c>
|
|
3692
|
+
4. mutagent feedback send "<body>" --category <c> --json
|
|
3621
3693
|
→ show the returned id + category + transcriptAttached to the user
|
|
3622
3694
|
\`\`\`
|
|
3623
3695
|
|
|
@@ -3629,9 +3701,12 @@ attached automatically — you never set it manually.
|
|
|
3629
3701
|
mutagent feedback send "Optimizer results were great" --json
|
|
3630
3702
|
mutagent feedback send "Eval gate was confusing" --category stage:evaluate --json
|
|
3631
3703
|
mutagent feedback send "CLI crashed on login" --category cli \\
|
|
3632
|
-
--title "login crash on first run" --
|
|
3704
|
+
--title "login crash on first run" --json
|
|
3633
3705
|
mutagent feedback send "Diagnose loop stalled" --category stage:diagnose \\
|
|
3634
|
-
--session run_abc123 --
|
|
3706
|
+
--session run_abc123 --json
|
|
3707
|
+
|
|
3708
|
+
# ONLY when the operator explicitly asked to attach their session — see Transcript safety:
|
|
3709
|
+
mutagent feedback send "CLI crashed on login" --category cli --attach-transcript --json
|
|
3635
3710
|
\`\`\`
|
|
3636
3711
|
|
|
3637
3712
|
---
|
|
@@ -3642,11 +3717,11 @@ When a mutagent command fails, this is the canonical bug-report path:
|
|
|
3642
3717
|
|
|
3643
3718
|
1. Show the user the failed command + error.
|
|
3644
3719
|
2. ASK if they want to file a report.
|
|
3645
|
-
3. On approval, send
|
|
3720
|
+
3. On approval, send the feedback — WITHOUT a transcript:
|
|
3646
3721
|
\`\`\`bash
|
|
3647
|
-
mutagent feedback send "<one-line failure summary>"
|
|
3648
|
-
--category cli --attach-transcript --json
|
|
3722
|
+
mutagent feedback send "<one-line failure summary>" --category cli --json
|
|
3649
3723
|
\`\`\`
|
|
3724
|
+
Approval to "file a report" is NOT approval to upload a session. See Transcript safety.
|
|
3650
3725
|
|
|
3651
3726
|
---
|
|
3652
3727
|
|
|
@@ -3670,6 +3745,27 @@ When a mutagent command fails, this is the canonical bug-report path:
|
|
|
3670
3745
|
|
|
3671
3746
|
- [SKILL.md](../SKILL.md) → Core Rules + Error Recovery
|
|
3672
3747
|
- [workflows/setup.md](./setup.md) → login (prerequisite for feedback)
|
|
3748
|
+
|
|
3749
|
+
|
|
3750
|
+
## Transcript safety (\`--attach-transcript\`)
|
|
3751
|
+
|
|
3752
|
+
**Never pass this flag on your own initiative.** Only when the operator explicitly asks to
|
|
3753
|
+
attach their session.
|
|
3754
|
+
|
|
3755
|
+
A session JSONL contains **source code, absolute paths including their username, branch and
|
|
3756
|
+
repository names, internal hostnames, and the business context of every prompt**. It is a
|
|
3757
|
+
separate artifact from the feedback text and is uploaded whole (last-200KB tail).
|
|
3758
|
+
|
|
3759
|
+
Credentials are scanned for and redacted before upload, and the byte size + source path are
|
|
3760
|
+
printed before the network call — but **detection is best-effort**. If the session handled
|
|
3761
|
+
secrets, do not attach it.
|
|
3762
|
+
|
|
3763
|
+
"Yes, file a bug report" is **not** consent to upload a session. Ask separately, state what
|
|
3764
|
+
the file contains, and accept no as the default.
|
|
3765
|
+
|
|
3766
|
+
**Response fields** when a transcript IS attached: \`transcriptAttached\`, \`transcriptBytes\`,
|
|
3767
|
+
\`transcriptSource\`, and \`redactions\`. **Always surface \`redactions\` to the operator** — it is
|
|
3768
|
+
how they learn something sensitive was in the file at all.
|
|
3673
3769
|
`,
|
|
3674
3770
|
"workflows/install.md": `---
|
|
3675
3771
|
name: mutagent-cli-workflows-install
|
|
@@ -3690,8 +3786,17 @@ triggers:
|
|
|
3690
3786
|
|
|
3691
3787
|
# Workflow — Install (Meta-Installer)
|
|
3692
3788
|
|
|
3693
|
-
> **Scope
|
|
3789
|
+
> **Scope differs by package** — read \`scope\` in the response, never assume:
|
|
3790
|
+
> - \`helix\` → installed into the CURRENT project directory (\`scope: "project"\`).
|
|
3791
|
+
> - \`diagnostics\` / \`evaluator\` → **npm installs the package GLOBALLY**
|
|
3792
|
+
> (\`scope: "global"\`, literally \`npm install -g\`). It does NOT go in the
|
|
3793
|
+
> user's project. Its skill init is the part you later run in the project.
|
|
3794
|
+
>
|
|
3694
3795
|
> This WRITES to the user's machine — confirm before running (Core Rule 5).
|
|
3796
|
+
>
|
|
3797
|
+
> **\`success: true\` does NOT mean the user is ready to go.** For \`diagnostics\`
|
|
3798
|
+
> and \`evaluator\` it means the npm package landed; the skill still needs the
|
|
3799
|
+
> package's own init. Branch on \`skillInstalled\` — see *Output handling* below.
|
|
3695
3800
|
|
|
3696
3801
|
Read the **Core Rules** in [SKILL.md](../SKILL.md) first. Key reminders:
|
|
3697
3802
|
- \`--json\` on every command
|
|
@@ -3707,7 +3812,7 @@ Read the **Core Rules** in [SKILL.md](../SKILL.md) first. Key reminders:
|
|
|
3707
3812
|
mutagent install --help # authoritative flags — read before first use
|
|
3708
3813
|
|
|
3709
3814
|
mutagent install <package> [--harness <claude-code|codex|omp>] \\
|
|
3710
|
-
[--
|
|
3815
|
+
[--version <v>] [--json]
|
|
3711
3816
|
\`\`\`
|
|
3712
3817
|
|
|
3713
3818
|
Where \`<package>\` is one of:
|
|
@@ -3720,7 +3825,7 @@ Where \`<package>\` is one of:
|
|
|
3720
3825
|
|
|
3721
3826
|
**Flags** (verify against \`--help\`):
|
|
3722
3827
|
- \`--harness <claude-code|codex|omp>\` -- target coding-agent harness (default \`claude-code\`).
|
|
3723
|
-
- \`--global\`
|
|
3828
|
+
- Install scope is FIXED PER PACKAGE and is not selectable: \`helix\` installs into the current directory; \`diagnostics\`/\`evaluator\` are installed GLOBALLY by npm. \`--global\` is retired: passing it fails and explains both paths.
|
|
3724
3829
|
- \`--version <v>\` -- pin a specific version (default: latest).
|
|
3725
3830
|
- \`--json\` -- structured output (Rule 1).
|
|
3726
3831
|
|
|
@@ -3737,11 +3842,23 @@ Where \`<package>\` is one of:
|
|
|
3737
3842
|
→ read the current packages + flags (Rule 2)
|
|
3738
3843
|
|
|
3739
3844
|
3. Confirm with the user WHAT will be installed and WHERE
|
|
3740
|
-
→
|
|
3845
|
+
→ state the REAL destination for the package being installed, e.g.
|
|
3846
|
+
helix: "I'll install helix into <current directory>. Proceed?"
|
|
3847
|
+
diagnostics: "I'll install @mutagent/diagnostics GLOBALLY via npm, then
|
|
3848
|
+
you'll run its init inside your project. Proceed?"
|
|
3741
3849
|
|
|
3742
3850
|
4. mutagent install <package> [--harness <h>] [--version <v>] --json
|
|
3743
3851
|
→ run the install
|
|
3744
|
-
→ show the command output
|
|
3852
|
+
→ show the command output to the user: package, version, harness, and
|
|
3853
|
+
\`scope\`. Surface \`projectPath\` ONLY when \`scope\` is \`"project"\` — it is
|
|
3854
|
+
ABSENT from the response on the global npm path.
|
|
3855
|
+
|
|
3856
|
+
5. CHECK \`skillInstalled\` in the JSON response — do NOT stop at \`success: true\`
|
|
3857
|
+
→ skillInstalled === true → done; the skill is installed and usable
|
|
3858
|
+
→ skillInstalled === false → NOT done. Tell the user plainly that the npm
|
|
3859
|
+
package is installed but the skill is not, then surface \`nextStep\`
|
|
3860
|
+
(e.g. \`mutagent-diagnostics init\`) as the command to run next.
|
|
3861
|
+
Confirm before running it — it writes to their project (Core Rule 5).
|
|
3745
3862
|
\`\`\`
|
|
3746
3863
|
|
|
3747
3864
|
---
|
|
@@ -3760,17 +3877,62 @@ mutagent install diagnostics --harness codex --json
|
|
|
3760
3877
|
|
|
3761
3878
|
## Output handling
|
|
3762
3879
|
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3880
|
+
Success shape:
|
|
3881
|
+
|
|
3882
|
+
\`\`\`json
|
|
3883
|
+
{
|
|
3884
|
+
"success": true,
|
|
3885
|
+
"package": "diagnostics",
|
|
3886
|
+
"version": "1.2.3",
|
|
3887
|
+
"harness": "claude-code",
|
|
3888
|
+
"scope": "global",
|
|
3889
|
+
"skillInstalled": false,
|
|
3890
|
+
"nextStep": "mutagent-diagnostics init",
|
|
3891
|
+
"_links": { "install": "...", "login": "..." }
|
|
3892
|
+
}
|
|
3893
|
+
\`\`\`
|
|
3894
|
+
|
|
3895
|
+
\`helix\` instead returns \`"scope": "project"\` with \`"projectPath"\`, and
|
|
3896
|
+
\`"skillInstalled": true\`.
|
|
3897
|
+
|
|
3898
|
+
- **\`skillInstalled\` is the field to branch on — NOT \`success\`.** \`success: true\`
|
|
3899
|
+
reports that the install step did its job, not that the user is ready.
|
|
3900
|
+
\`nextStep\` is present only when work remains.
|
|
3901
|
+
- \`skillInstalled: false\` (npm packages — \`diagnostics\`, \`evaluator\`): the npm
|
|
3902
|
+
package is installed; the harness skill is **not**. Say so explicitly and give
|
|
3903
|
+
the user \`nextStep\`. Reporting only "installed" here is a known past defect —
|
|
3904
|
+
users were told a skill had landed when none had.
|
|
3905
|
+
- \`skillInstalled: true\` (\`helix\`): the CLI resolved a signed download URL from
|
|
3906
|
+
the login broker, downloaded + sha256-verified the plugin, and ran its init
|
|
3907
|
+
into the project. Nothing further is needed. An \`INTEGRITY_ERROR\` means the
|
|
3908
|
+
download failed checksum verification — retry.
|
|
3909
|
+
- **\`scope\` tells you WHERE it went. Read it; do not assume.**
|
|
3910
|
+
- \`"global"\` (\`diagnostics\`, \`evaluator\`) — npm installed the package into its
|
|
3911
|
+
GLOBAL root (\`npm install -g\`). The package is **not** in the user's project;
|
|
3912
|
+
never tell the user it is. No path is reported — run \`npm root -g\` if the
|
|
3913
|
+
user needs the directory.
|
|
3914
|
+
- \`"project"\` (\`helix\`) — installed into \`projectPath\`. Surface it so the user
|
|
3915
|
+
can confirm the location.
|
|
3916
|
+
- On an auth error (including a broker \`AUTH_REQUIRED\`): route to the login
|
|
3917
|
+
workflow, then retry.
|
|
3918
|
+
- On \`INVALID_ARGUMENTS\` naming \`--global\`: the flag is retired. Re-run without
|
|
3919
|
+
it, or \`cd\` to the intended project first — the error names the path that
|
|
3920
|
+
would have been used.
|
|
3766
3921
|
|
|
3767
3922
|
---
|
|
3768
3923
|
|
|
3769
3924
|
## Common pitfalls
|
|
3770
3925
|
|
|
3926
|
+
- **Treating \`success: true\` as "the user is ready".** For \`diagnostics\` and
|
|
3927
|
+
\`evaluator\` it is not — check \`skillInstalled\` and surface \`nextStep\`. Stopping
|
|
3928
|
+
at \`success\` leaves the user with a package and no skill, believing it works.
|
|
3929
|
+
- **Telling the user a globally-installed package is in their project.**
|
|
3930
|
+
\`diagnostics\` and \`evaluator\` install globally (\`npm install -g\`). Report the
|
|
3931
|
+
location from \`scope\`, never from the directory you ran in.
|
|
3771
3932
|
- Running before login → auth error (install is login-gated).
|
|
3772
3933
|
- Assuming \`helix\` installs from public npm — it is fetched from a private registry via a login-brokered signed URL (the CLI holds no static secret).
|
|
3773
3934
|
- Installing without confirming with the user first (Core Rule 5).
|
|
3935
|
+
- Passing \`--global\` — retired; scope is fixed per package and is not selectable.
|
|
3774
3936
|
|
|
3775
3937
|
---
|
|
3776
3938
|
|
|
@@ -3954,7 +4116,7 @@ function getSkillFiles() {
|
|
|
3954
4116
|
}
|
|
3955
4117
|
function installCliSkill(targetRoot) {
|
|
3956
4118
|
const files = getSkillFiles();
|
|
3957
|
-
const skillDir =
|
|
4119
|
+
const skillDir = join8(targetRoot, SKILL_DIR);
|
|
3958
4120
|
const writtenFiles = [];
|
|
3959
4121
|
let totalBytes = 0;
|
|
3960
4122
|
const sortedKeys = Object.keys(files).sort((a, b) => {
|
|
@@ -3965,10 +4127,10 @@ function installCliSkill(targetRoot) {
|
|
|
3965
4127
|
return a.localeCompare(b);
|
|
3966
4128
|
});
|
|
3967
4129
|
for (const relPath of sortedKeys) {
|
|
3968
|
-
const destPath =
|
|
4130
|
+
const destPath = join8(skillDir, relPath);
|
|
3969
4131
|
const parentDir = dirname(destPath);
|
|
3970
4132
|
if (!existsSync6(parentDir)) {
|
|
3971
|
-
|
|
4133
|
+
mkdirSync4(parentDir, { recursive: true });
|
|
3972
4134
|
}
|
|
3973
4135
|
const raw = files[relPath] ?? "";
|
|
3974
4136
|
const finalContent = raw.endsWith(`
|
|
@@ -4059,7 +4221,7 @@ What it does:
|
|
|
4059
4221
|
let rcConfig;
|
|
4060
4222
|
if (rcExists) {
|
|
4061
4223
|
try {
|
|
4062
|
-
rcConfig = JSON.parse(readFileSync5(
|
|
4224
|
+
rcConfig = JSON.parse(readFileSync5(join9(cwd, ".mutagentrc.json"), "utf-8"));
|
|
4063
4225
|
} catch {
|
|
4064
4226
|
rcConfig = {};
|
|
4065
4227
|
}
|
|
@@ -4214,10 +4376,10 @@ import { randomUUID } from "crypto";
|
|
|
4214
4376
|
|
|
4215
4377
|
// src/commands/hooks/state.ts
|
|
4216
4378
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, renameSync as renameSync2, unlinkSync, existsSync as existsSync7 } from "fs";
|
|
4217
|
-
import { join as
|
|
4379
|
+
import { join as join10 } from "path";
|
|
4218
4380
|
import { tmpdir } from "os";
|
|
4219
4381
|
function stateFilePath(sessionId) {
|
|
4220
|
-
return
|
|
4382
|
+
return join10(tmpdir(), `mutagent-hook-${sessionId}.json`);
|
|
4221
4383
|
}
|
|
4222
4384
|
function readState(sessionId) {
|
|
4223
4385
|
const path = stateFilePath(sessionId);
|
|
@@ -4891,8 +5053,8 @@ async function handlePostToolUseFailure() {
|
|
|
4891
5053
|
}
|
|
4892
5054
|
|
|
4893
5055
|
// src/commands/hooks/install.ts
|
|
4894
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as
|
|
4895
|
-
import { join as
|
|
5056
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
5057
|
+
import { join as join11 } from "path";
|
|
4896
5058
|
|
|
4897
5059
|
class SettingsParseError extends Error {
|
|
4898
5060
|
settingsPath;
|
|
@@ -4954,8 +5116,8 @@ function migrateV1Hooks(settings) {
|
|
|
4954
5116
|
return migrated;
|
|
4955
5117
|
}
|
|
4956
5118
|
function installHooks(cwd) {
|
|
4957
|
-
const claudeDir =
|
|
4958
|
-
const settingsPath =
|
|
5119
|
+
const claudeDir = join11(cwd, ".claude");
|
|
5120
|
+
const settingsPath = join11(claudeDir, "settings.local.json");
|
|
4959
5121
|
const existed = existsSync8(settingsPath);
|
|
4960
5122
|
let settings = {};
|
|
4961
5123
|
if (existed) {
|
|
@@ -4995,7 +5157,7 @@ function installHooks(cwd) {
|
|
|
4995
5157
|
let userWarning;
|
|
4996
5158
|
if (added.length > 0 || migrated.length > 0) {
|
|
4997
5159
|
if (!existsSync8(claudeDir)) {
|
|
4998
|
-
|
|
5160
|
+
mkdirSync5(claudeDir, { recursive: true });
|
|
4999
5161
|
}
|
|
5000
5162
|
writeFileSync6(settingsPath, JSON.stringify(settings, null, 2) + `
|
|
5001
5163
|
`, "utf-8");
|
|
@@ -5141,10 +5303,10 @@ times is safe.
|
|
|
5141
5303
|
|
|
5142
5304
|
// src/commands/feedback.ts
|
|
5143
5305
|
import { Command as Command10 } from "commander";
|
|
5144
|
-
import
|
|
5306
|
+
import chalk17 from "chalk";
|
|
5145
5307
|
import { type as osType, release as osRelease } from "os";
|
|
5146
5308
|
import { readFileSync as readFileSync9 } from "fs";
|
|
5147
|
-
import { join as
|
|
5309
|
+
import { join as join13, dirname as dirname2 } from "path";
|
|
5148
5310
|
import { fileURLToPath } from "url";
|
|
5149
5311
|
init_errors();
|
|
5150
5312
|
init_config();
|
|
@@ -5152,12 +5314,12 @@ init_config();
|
|
|
5152
5314
|
// src/lib/transcript.ts
|
|
5153
5315
|
init_errors();
|
|
5154
5316
|
import { homedir as osHomedir } from "os";
|
|
5155
|
-
import { join as
|
|
5317
|
+
import { join as join12 } from "path";
|
|
5156
5318
|
import {
|
|
5157
5319
|
existsSync as fsExistsSync,
|
|
5158
5320
|
statSync as fsStatSync,
|
|
5159
5321
|
readFileSync as readFileSync8,
|
|
5160
|
-
readdirSync,
|
|
5322
|
+
readdirSync as readdirSync2,
|
|
5161
5323
|
openSync,
|
|
5162
5324
|
readSync,
|
|
5163
5325
|
closeSync
|
|
@@ -5185,12 +5347,12 @@ function defaultScan(dir) {
|
|
|
5185
5347
|
const walk = (current) => {
|
|
5186
5348
|
let entries;
|
|
5187
5349
|
try {
|
|
5188
|
-
entries =
|
|
5350
|
+
entries = readdirSync2(current, { withFileTypes: true });
|
|
5189
5351
|
} catch {
|
|
5190
5352
|
return;
|
|
5191
5353
|
}
|
|
5192
5354
|
for (const entry of entries) {
|
|
5193
|
-
const full =
|
|
5355
|
+
const full = join12(current, entry.name);
|
|
5194
5356
|
if (entry.isDirectory())
|
|
5195
5357
|
walk(full);
|
|
5196
5358
|
else if (entry.isFile() && entry.name.endsWith(".jsonl"))
|
|
@@ -5201,11 +5363,11 @@ function defaultScan(dir) {
|
|
|
5201
5363
|
return out;
|
|
5202
5364
|
}
|
|
5203
5365
|
function buildSources(env, home) {
|
|
5204
|
-
const ompBase = env.PI_CODING_AGENT_DIR ??
|
|
5366
|
+
const ompBase = env.PI_CODING_AGENT_DIR ?? join12(home, ".omp", "agent");
|
|
5205
5367
|
return [
|
|
5206
|
-
{ harness: "claude-code", dir:
|
|
5207
|
-
{ harness: "codex", dir:
|
|
5208
|
-
{ harness: "omp", dir:
|
|
5368
|
+
{ harness: "claude-code", dir: join12(home, ".claude", "projects") },
|
|
5369
|
+
{ harness: "codex", dir: join12(home, ".codex", "sessions") },
|
|
5370
|
+
{ harness: "omp", dir: join12(ompBase, "sessions") }
|
|
5209
5371
|
];
|
|
5210
5372
|
}
|
|
5211
5373
|
function resolveTranscript(attach, deps = {}) {
|
|
@@ -5251,6 +5413,276 @@ Or omit the path to auto-detect the newest coding-agent session.`);
|
|
|
5251
5413
|
};
|
|
5252
5414
|
}
|
|
5253
5415
|
|
|
5416
|
+
// src/lib/feedback-safety.ts
|
|
5417
|
+
import chalk16 from "chalk";
|
|
5418
|
+
|
|
5419
|
+
// src/lib/secret-scan.ts
|
|
5420
|
+
var PATTERNS = [
|
|
5421
|
+
{
|
|
5422
|
+
kind: "private-key",
|
|
5423
|
+
label: "Private key block",
|
|
5424
|
+
regex: /-----BEGIN(?: [A-Z]+)* PRIVATE KEY-----[\s\S]*?-----END(?: [A-Z]+)* PRIVATE KEY-----/g
|
|
5425
|
+
},
|
|
5426
|
+
{
|
|
5427
|
+
kind: "anthropic-api-key",
|
|
5428
|
+
label: "Anthropic API key",
|
|
5429
|
+
regex: /sk-ant-[A-Za-z0-9_-]{16,}/g
|
|
5430
|
+
},
|
|
5431
|
+
{
|
|
5432
|
+
kind: "openai-api-key",
|
|
5433
|
+
label: "OpenAI API key",
|
|
5434
|
+
regex: /sk-(?!ant-)(?:proj-)?[A-Za-z0-9_-]{20,}/g
|
|
5435
|
+
},
|
|
5436
|
+
{
|
|
5437
|
+
kind: "aws-access-key-id",
|
|
5438
|
+
label: "AWS access key ID",
|
|
5439
|
+
regex: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g
|
|
5440
|
+
},
|
|
5441
|
+
{
|
|
5442
|
+
kind: "github-token",
|
|
5443
|
+
label: "GitHub token",
|
|
5444
|
+
regex: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}\b|\bgithub_pat_[A-Za-z0-9_]{22,}\b/g
|
|
5445
|
+
},
|
|
5446
|
+
{
|
|
5447
|
+
kind: "google-api-key",
|
|
5448
|
+
label: "Google API key",
|
|
5449
|
+
regex: /\bAIza[0-9A-Za-z_-]{35}\b/g
|
|
5450
|
+
},
|
|
5451
|
+
{
|
|
5452
|
+
kind: "slack-token",
|
|
5453
|
+
label: "Slack token",
|
|
5454
|
+
regex: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g
|
|
5455
|
+
},
|
|
5456
|
+
{
|
|
5457
|
+
kind: "mutagent-api-key",
|
|
5458
|
+
label: "Mutagent API key",
|
|
5459
|
+
regex: /\bmt_[A-Za-z0-9]{16,}\b/g
|
|
5460
|
+
},
|
|
5461
|
+
{
|
|
5462
|
+
kind: "bearer-token",
|
|
5463
|
+
label: "Bearer token",
|
|
5464
|
+
regex: /\bBearer\s+([A-Za-z0-9._-]{20,})/g
|
|
5465
|
+
},
|
|
5466
|
+
{
|
|
5467
|
+
kind: "generic-assignment",
|
|
5468
|
+
label: "Credential assignment",
|
|
5469
|
+
regex: /(?:api[_-]?key|apikey|secret|password|passwd|token|access[_-]?key)["']?\s*[:=]\s*["']?([A-Za-z0-9._/+-]{16,})/gi
|
|
5470
|
+
}
|
|
5471
|
+
];
|
|
5472
|
+
function hasCredentialEntropy(candidate) {
|
|
5473
|
+
if (candidate.length < 16)
|
|
5474
|
+
return false;
|
|
5475
|
+
const distinct = new Set(candidate).size;
|
|
5476
|
+
if (distinct < 8)
|
|
5477
|
+
return false;
|
|
5478
|
+
return !/(.)\1{7,}/.test(candidate);
|
|
5479
|
+
}
|
|
5480
|
+
function maskSecret(secret) {
|
|
5481
|
+
const head = secret.slice(0, 6);
|
|
5482
|
+
return `${head}${"*".repeat(Math.min(12, Math.max(4, secret.length - head.length)))}`;
|
|
5483
|
+
}
|
|
5484
|
+
function collectHits(content) {
|
|
5485
|
+
const accepted = [];
|
|
5486
|
+
const overlaps = (start, end) => accepted.some((hit) => start < hit.end && end > hit.start);
|
|
5487
|
+
for (const pattern of PATTERNS) {
|
|
5488
|
+
const regex = new RegExp(pattern.regex.source, pattern.regex.flags);
|
|
5489
|
+
let match;
|
|
5490
|
+
while ((match = regex.exec(content)) !== null) {
|
|
5491
|
+
if (match[0].length === 0) {
|
|
5492
|
+
regex.lastIndex += 1;
|
|
5493
|
+
continue;
|
|
5494
|
+
}
|
|
5495
|
+
const captured = match[1];
|
|
5496
|
+
const secret = captured ?? match[0];
|
|
5497
|
+
const start = captured === undefined ? match.index : match.index + match[0].indexOf(captured);
|
|
5498
|
+
const end = start + secret.length;
|
|
5499
|
+
const exemptFromEntropy = pattern.kind === "private-key";
|
|
5500
|
+
if (!exemptFromEntropy && !hasCredentialEntropy(secret))
|
|
5501
|
+
continue;
|
|
5502
|
+
if (overlaps(start, end))
|
|
5503
|
+
continue;
|
|
5504
|
+
accepted.push({ kind: pattern.kind, label: pattern.label, start, end, secret });
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5507
|
+
return accepted.sort((a, b) => a.start - b.start);
|
|
5508
|
+
}
|
|
5509
|
+
function lineOf(content, offset) {
|
|
5510
|
+
let line = 1;
|
|
5511
|
+
for (let i = 0;i < offset && i < content.length; i++) {
|
|
5512
|
+
if (content[i] === `
|
|
5513
|
+
`)
|
|
5514
|
+
line++;
|
|
5515
|
+
}
|
|
5516
|
+
return line;
|
|
5517
|
+
}
|
|
5518
|
+
function redactSecrets(content) {
|
|
5519
|
+
if (!content)
|
|
5520
|
+
return { content, matches: [] };
|
|
5521
|
+
const hits = collectHits(content);
|
|
5522
|
+
if (hits.length === 0)
|
|
5523
|
+
return { content, matches: [] };
|
|
5524
|
+
let out = "";
|
|
5525
|
+
let cursor = 0;
|
|
5526
|
+
for (const hit of hits) {
|
|
5527
|
+
out += content.slice(cursor, hit.start);
|
|
5528
|
+
out += `[REDACTED:${hit.kind}]`;
|
|
5529
|
+
cursor = hit.end;
|
|
5530
|
+
}
|
|
5531
|
+
out += content.slice(cursor);
|
|
5532
|
+
return {
|
|
5533
|
+
content: out,
|
|
5534
|
+
matches: hits.map((hit) => ({
|
|
5535
|
+
kind: hit.kind,
|
|
5536
|
+
label: hit.label,
|
|
5537
|
+
line: lineOf(content, hit.start),
|
|
5538
|
+
preview: maskSecret(hit.secret)
|
|
5539
|
+
}))
|
|
5540
|
+
};
|
|
5541
|
+
}
|
|
5542
|
+
|
|
5543
|
+
// src/lib/by-design.ts
|
|
5544
|
+
var NOT_FOUND = String.raw`404|not\s+found|missing|unavailable|unpublished|doesn'?t\s+exist|does\s+not\s+exist|no\s+such\s+package|cannot\s+install|can'?t\s+install|fails?\s+to\s+install`;
|
|
5545
|
+
var NOT_A_COMMAND = String.raw`not\s+a\s+command|unknown\s+command|unrecognized|unrecognised|doesn'?t\s+exist|does\s+not\s+exist|not\s+found|missing|no\s+such\s+command|invalid\s+command`;
|
|
5546
|
+
function pairPatterns(subject, symptoms) {
|
|
5547
|
+
return [
|
|
5548
|
+
new RegExp(`${subject}[^\\n]{0,60}?(?:${symptoms})`, "i"),
|
|
5549
|
+
new RegExp(`(?:${symptoms})[^\\n]{0,60}?${subject}`, "i")
|
|
5550
|
+
];
|
|
5551
|
+
}
|
|
5552
|
+
var TOOLS_PKG = String.raw`@mutagent\/tools`;
|
|
5553
|
+
var HELIX_PKG = String.raw`@mutagent\/helix`;
|
|
5554
|
+
var BY_DESIGN_ENTRIES = [
|
|
5555
|
+
{
|
|
5556
|
+
id: "mutagent-tools-not-on-npm",
|
|
5557
|
+
title: "@mutagent/tools is not published to npm — on purpose",
|
|
5558
|
+
looksLike: pairPatterns(TOOLS_PKG, NOT_FOUND),
|
|
5559
|
+
reality: "@mutagent/tools is intentionally not published to the public npm registry, so a 404 there is the expected result rather than a broken release. Its functionality is compiled into the mutagent-cli binary that ships with the diagnostics and evaluator packages — installing either one already gives you the tooling, and there is nothing separate to install."
|
|
5560
|
+
},
|
|
5561
|
+
{
|
|
5562
|
+
id: "mutagent-helix-not-on-npm",
|
|
5563
|
+
title: "@mutagent/helix is not published to npm — on purpose",
|
|
5564
|
+
looksLike: pairPatterns(HELIX_PKG, NOT_FOUND),
|
|
5565
|
+
reality: "Helix is not distributed through the public npm registry, so a 404 there is expected. It is delivered by the Mutagent CLI itself, which authenticates you, fetches a signed download from private storage, verifies its checksum, and initializes it into your project. Install it with: mutagent install helix"
|
|
5566
|
+
},
|
|
5567
|
+
{
|
|
5568
|
+
id: "trace-lives-in-a-different-binary",
|
|
5569
|
+
title: "trace is a mutagent-cli command, not a mutagent command",
|
|
5570
|
+
looksLike: [
|
|
5571
|
+
...pairPatterns(String.raw`mutagent\s+trace`, NOT_A_COMMAND),
|
|
5572
|
+
/\btrace\b[^\n]{0,40}?(?:not\s+a\s+(?:valid\s+)?(?:sub)?command|unknown\s+command)/i
|
|
5573
|
+
],
|
|
5574
|
+
reality: "trace is not a subcommand of mutagent. It lives in mutagent-cli — a different binary — which is installed alongside @mutagent/diagnostics, @mutagent/evaluator, or helix. Run: mutagent-cli trace <args>"
|
|
5575
|
+
},
|
|
5576
|
+
{
|
|
5577
|
+
id: "two-separate-binaries",
|
|
5578
|
+
title: "mutagent and mutagent-cli are two different binaries",
|
|
5579
|
+
looksLike: [
|
|
5580
|
+
/(?:no|not|missing|can'?t\s+find|cannot\s+find|couldn'?t\s+find|where\s+is)[^\n]{0,40}?mutagent-cli\s+(?:binary|executable|command)/i,
|
|
5581
|
+
/mutagent-cli\s+(?:binary|executable|command)[^\n]{0,60}?(?:missing|not\s+found|doesn'?t\s+exist|does\s+not\s+exist|not\s+installed)/i,
|
|
5582
|
+
new RegExp(String.raw`no\s+mutagent-cli[^\n]{0,40}?in\s+@mutagent\/cli`, "i")
|
|
5583
|
+
],
|
|
5584
|
+
reality: "mutagent and mutagent-cli are separate binaries that come from separate packages. @mutagent/cli provides the mutagent binary only — account, configuration, and installation commands. The mutagent-cli binary ships with the diagnostics and evaluator packages and carries the trace tooling. Having both installed is normal and expected."
|
|
5585
|
+
}
|
|
5586
|
+
];
|
|
5587
|
+
function matchByDesign(draft) {
|
|
5588
|
+
if (!draft)
|
|
5589
|
+
return [];
|
|
5590
|
+
return BY_DESIGN_ENTRIES.filter((entry) => entry.looksLike.some((pattern) => pattern.test(draft)));
|
|
5591
|
+
}
|
|
5592
|
+
|
|
5593
|
+
// src/lib/feedback-safety.ts
|
|
5594
|
+
function sanitizeTranscript(transcript) {
|
|
5595
|
+
const { content, matches } = redactSecrets(transcript.content);
|
|
5596
|
+
return {
|
|
5597
|
+
transcript: { ...transcript, content },
|
|
5598
|
+
redactions: matches
|
|
5599
|
+
};
|
|
5600
|
+
}
|
|
5601
|
+
function buildPreview(transcript) {
|
|
5602
|
+
return {
|
|
5603
|
+
fileCount: 1,
|
|
5604
|
+
bytes: Buffer.byteLength(transcript.content, "utf-8"),
|
|
5605
|
+
sourcePath: transcript.sourcePath,
|
|
5606
|
+
truncated: transcript.truncated
|
|
5607
|
+
};
|
|
5608
|
+
}
|
|
5609
|
+
function buildTranscriptWarning(preview) {
|
|
5610
|
+
const kb = (preview.bytes / 1024).toFixed(1);
|
|
5611
|
+
return [
|
|
5612
|
+
chalk16.yellow("⚠ About to upload your coding-agent session transcript."),
|
|
5613
|
+
` ${chalk16.bold("Source:")} ${preview.sourcePath}`,
|
|
5614
|
+
` ${chalk16.bold("Sending:")} ${String(preview.fileCount)} file, ${kb} KB${preview.truncated ? " (tail only)" : ""}`,
|
|
5615
|
+
"",
|
|
5616
|
+
" A session transcript typically contains:",
|
|
5617
|
+
" • source code and file contents you or the agent opened",
|
|
5618
|
+
" • absolute file paths, including your username and directory layout",
|
|
5619
|
+
" • branch names, commit messages, and repository names",
|
|
5620
|
+
" • internal hostnames, service names, and URLs",
|
|
5621
|
+
" • business context from your prompts and the agent replies",
|
|
5622
|
+
"",
|
|
5623
|
+
` ${chalk16.dim("Credentials are scanned for and redacted automatically, but detection")}`,
|
|
5624
|
+
` ${chalk16.dim("is best-effort — review the source file if the session touched secrets.")}`,
|
|
5625
|
+
` ${chalk16.dim("Omit --attach-transcript to send feedback without it.")}`
|
|
5626
|
+
].join(`
|
|
5627
|
+
`);
|
|
5628
|
+
}
|
|
5629
|
+
function buildRedactionReport(redactions) {
|
|
5630
|
+
if (redactions.length === 0)
|
|
5631
|
+
return "";
|
|
5632
|
+
const byKind = new Map;
|
|
5633
|
+
for (const match of redactions) {
|
|
5634
|
+
byKind.set(match.label, (byKind.get(match.label) ?? 0) + 1);
|
|
5635
|
+
}
|
|
5636
|
+
const lines = [
|
|
5637
|
+
chalk16.green(`✓ Redacted ${String(redactions.length)} credential${redactions.length === 1 ? "" : "s"} from the transcript before upload:`)
|
|
5638
|
+
];
|
|
5639
|
+
for (const [label, count] of byKind) {
|
|
5640
|
+
lines.push(` • ${label} ×${String(count)}`);
|
|
5641
|
+
}
|
|
5642
|
+
lines.push(chalk16.dim(" These were replaced with [REDACTED:…] markers and never left your machine."));
|
|
5643
|
+
return lines.join(`
|
|
5644
|
+
`);
|
|
5645
|
+
}
|
|
5646
|
+
function buildByDesignNotice(entries) {
|
|
5647
|
+
if (entries.length === 0)
|
|
5648
|
+
return "";
|
|
5649
|
+
const lines = [
|
|
5650
|
+
chalk16.cyan("ℹ Heads up — this may describe intended behaviour:"),
|
|
5651
|
+
""
|
|
5652
|
+
];
|
|
5653
|
+
for (const entry of entries) {
|
|
5654
|
+
lines.push(` ${chalk16.bold(entry.title)}`);
|
|
5655
|
+
for (const line of wrap(entry.reality, 76)) {
|
|
5656
|
+
lines.push(` ${line}`);
|
|
5657
|
+
}
|
|
5658
|
+
lines.push("");
|
|
5659
|
+
}
|
|
5660
|
+
lines.push(chalk16.dim(" Sending anyway — if this is a regression rather than the design,"), chalk16.dim(" your report is exactly what we need. Nothing was withheld."));
|
|
5661
|
+
return lines.join(`
|
|
5662
|
+
`);
|
|
5663
|
+
}
|
|
5664
|
+
function findByDesignMatches(draft) {
|
|
5665
|
+
return matchByDesign(draft);
|
|
5666
|
+
}
|
|
5667
|
+
function wrap(text, width) {
|
|
5668
|
+
const words = text.split(/\s+/);
|
|
5669
|
+
const lines = [];
|
|
5670
|
+
let current = "";
|
|
5671
|
+
for (const word of words) {
|
|
5672
|
+
if (current.length === 0) {
|
|
5673
|
+
current = word;
|
|
5674
|
+
} else if (current.length + 1 + word.length <= width) {
|
|
5675
|
+
current += ` ${word}`;
|
|
5676
|
+
} else {
|
|
5677
|
+
lines.push(current);
|
|
5678
|
+
current = word;
|
|
5679
|
+
}
|
|
5680
|
+
}
|
|
5681
|
+
if (current.length > 0)
|
|
5682
|
+
lines.push(current);
|
|
5683
|
+
return lines;
|
|
5684
|
+
}
|
|
5685
|
+
|
|
5254
5686
|
// src/commands/feedback.ts
|
|
5255
5687
|
var MAX_FEEDBACK_LENGTH = 1e4;
|
|
5256
5688
|
var VALID_STAGES = ["spec", "build", "evaluate", "diagnose", "optimize"];
|
|
@@ -5259,7 +5691,7 @@ function getCliVersion() {
|
|
|
5259
5691
|
return process.env.CLI_VERSION;
|
|
5260
5692
|
try {
|
|
5261
5693
|
const __dirname2 = dirname2(fileURLToPath(import.meta.url));
|
|
5262
|
-
const pkgPath =
|
|
5694
|
+
const pkgPath = join13(__dirname2, "..", "..", "package.json");
|
|
5263
5695
|
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
5264
5696
|
return pkg.version ?? "0.1.1";
|
|
5265
5697
|
} catch {
|
|
@@ -5336,14 +5768,18 @@ function buildPayload(feedback, category, title, session, transcript) {
|
|
|
5336
5768
|
}
|
|
5337
5769
|
function createFeedbackCommand() {
|
|
5338
5770
|
const feedback = new Command10("feedback").description("Send product feedback to Mutagent").addHelpText("after", `
|
|
5339
|
-
${
|
|
5340
|
-
${
|
|
5341
|
-
${
|
|
5342
|
-
${
|
|
5771
|
+
${chalk17.bold("Examples:")}
|
|
5772
|
+
${chalk17.cyan('mutagent feedback send "Optimizer results were great"')}
|
|
5773
|
+
${chalk17.cyan('mutagent feedback send "Eval gate was confusing" --category stage:evaluate')}
|
|
5774
|
+
${chalk17.cyan('mutagent feedback send "CLI crashed on export" --category cli --json')}
|
|
5343
5775
|
|
|
5344
|
-
${
|
|
5776
|
+
${chalk17.yellow("AI Agent (MANDATORY):")}
|
|
5345
5777
|
ALWAYS use --json: mutagent feedback send "..." --category cli --json
|
|
5346
|
-
|
|
5778
|
+
${chalk17.yellow("Do NOT attach a transcript by default.")} --attach-transcript uploads the
|
|
5779
|
+
coding-agent SESSION JSONL — source code, absolute paths, branch and repository
|
|
5780
|
+
names, internal hostnames, and the business context of the operator's prompts.
|
|
5781
|
+
Use it only when the operator explicitly asks for it. See:
|
|
5782
|
+
${chalk17.dim("mutagent feedback send --help")}
|
|
5347
5783
|
`).action(() => {
|
|
5348
5784
|
feedback.help();
|
|
5349
5785
|
});
|
|
@@ -5352,33 +5788,39 @@ ${chalk16.yellow("AI Agent (MANDATORY):")}
|
|
|
5352
5788
|
}
|
|
5353
5789
|
function registerFeedbackSend(feedback) {
|
|
5354
5790
|
feedback.command("send").description("Send product feedback about the Mutagent platform or CLI").argument("<feedback>", "The feedback body (content), max 10000 characters").option("--title <string>", "Optional 5–8 word summary of the session timeline").option("--category <value>", `Feedback category: 'cli', 'helix', or 'stage:<${VALID_STAGES.join("|")}>'`, "cli").option("--session <id>", "Link this feedback to a session id (maps to server sessionId)").option("--attach-transcript [path]", "Attach the coding-agent session JSONL. Bare = auto-detect newest; or pass an explicit path.").addHelpText("after", `
|
|
5355
|
-
${
|
|
5356
|
-
${
|
|
5357
|
-
${
|
|
5358
|
-
${
|
|
5359
|
-
${
|
|
5360
|
-
${
|
|
5361
|
-
${
|
|
5362
|
-
${
|
|
5363
|
-
|
|
5364
|
-
${
|
|
5365
|
-
${
|
|
5366
|
-
${
|
|
5367
|
-
${
|
|
5368
|
-
${
|
|
5369
|
-
${
|
|
5370
|
-
|
|
5371
|
-
${
|
|
5372
|
-
${
|
|
5373
|
-
${
|
|
5374
|
-
${
|
|
5375
|
-
|
|
5376
|
-
${
|
|
5791
|
+
${chalk17.bold("Arguments & flags:")}
|
|
5792
|
+
${chalk17.bold("<feedback>")} Feedback body / content (required, ≤10000 chars)
|
|
5793
|
+
${chalk17.bold("--title <string>")} Optional 5–8 word summary of the session timeline
|
|
5794
|
+
${chalk17.bold("--category <value>")} ${chalk17.bold("cli")} (default) | ${chalk17.bold("helix")} | ${chalk17.bold("stage:<")}${VALID_STAGES.join("|")}${chalk17.bold(">")}
|
|
5795
|
+
${chalk17.dim("stage = the lifecycle skill (evaluator/diagnostics live under stage:*)")}
|
|
5796
|
+
${chalk17.bold("--session <id>")} Link feedback to a session id (server sessionId)
|
|
5797
|
+
${chalk17.bold("--attach-transcript")} ${chalk17.dim("[path]")} Attach the coding-agent session JSONL (bare = auto-detect newest)
|
|
5798
|
+
${chalk17.bold("--json")} Structured output (MANDATORY for AI agents)
|
|
5799
|
+
|
|
5800
|
+
${chalk17.bold("Examples:")}
|
|
5801
|
+
${chalk17.dim("$")} mutagent feedback send "The setup flow could show progress better"
|
|
5802
|
+
${chalk17.dim("$")} mutagent feedback send "Eval gate was confusing" --category stage:evaluate --title "eval gate unclear"
|
|
5803
|
+
${chalk17.dim("$")} mutagent feedback send "Diagnose loop stalled" --category stage:diagnose --session sess_abc123 --json
|
|
5804
|
+
${chalk17.dim("$")} mutagent feedback send "CLI crashed mid-run" --category cli --attach-transcript --json
|
|
5805
|
+
${chalk17.dim("$")} mutagent feedback send "Repro attached" --category cli --attach-transcript /tmp/session.jsonl
|
|
5806
|
+
|
|
5807
|
+
${chalk17.bold("Category:")}
|
|
5808
|
+
${chalk17.bold("cli")} Feedback about the CLI itself (default)
|
|
5809
|
+
${chalk17.bold("helix")} Feedback about Helix
|
|
5810
|
+
${chalk17.bold("stage:<x>")} Feedback about a lifecycle stage: ${VALID_STAGES.join(", ")}
|
|
5811
|
+
|
|
5812
|
+
${chalk17.bold("Transcript (--attach-transcript):")}
|
|
5377
5813
|
Uploads your coding-agent SESSION JSONL as context (raw last-200KB tail) — a
|
|
5378
5814
|
SEPARATE artifact from the feedback body. Bare flag auto-detects the newest
|
|
5379
5815
|
session across claude-code, codex, and omp; explicit path overrides.
|
|
5380
5816
|
|
|
5381
|
-
${
|
|
5817
|
+
${chalk17.yellow("A session transcript contains source code, absolute paths (including your")}
|
|
5818
|
+
${chalk17.yellow("username), branch and repository names, internal hostnames, and the business")}
|
|
5819
|
+
${chalk17.yellow("context of your prompts.")} Credentials are scanned for and redacted before
|
|
5820
|
+
upload, and the exact size + source path are printed before the network call.
|
|
5821
|
+
Detection is best-effort — omit the flag if the session handled secrets.
|
|
5822
|
+
|
|
5823
|
+
${chalk17.yellow("AI Agent (MANDATORY):")}
|
|
5382
5824
|
ALWAYS use --json: mutagent feedback send "..." --json
|
|
5383
5825
|
Auto-captured context (harness, CLI version, platform, OS, node version) is included automatically.
|
|
5384
5826
|
`).action(async (feedbackBody, options) => {
|
|
@@ -5394,7 +5836,26 @@ ${chalk16.yellow("AI Agent (MANDATORY):")}
|
|
|
5394
5836
|
throw new MutagentError("AUTH_REQUIRED", "Authentication required to send feedback.", `Authenticate first: mutagent auth login
|
|
5395
5837
|
Or set an API key: mutagent config set apiKey <key>`);
|
|
5396
5838
|
}
|
|
5397
|
-
const
|
|
5839
|
+
const byDesign = findByDesignMatches(feedbackBody);
|
|
5840
|
+
if (byDesign.length > 0 && !isJson) {
|
|
5841
|
+
console.log(buildByDesignNotice(byDesign));
|
|
5842
|
+
}
|
|
5843
|
+
const rawTranscript = resolveTranscript(options.attachTranscript);
|
|
5844
|
+
let transcript = null;
|
|
5845
|
+
let redactions = [];
|
|
5846
|
+
if (rawTranscript) {
|
|
5847
|
+
const safe = sanitizeTranscript(rawTranscript);
|
|
5848
|
+
transcript = safe.transcript;
|
|
5849
|
+
redactions = safe.redactions;
|
|
5850
|
+
const preview = buildPreview(transcript);
|
|
5851
|
+
if (!isJson) {
|
|
5852
|
+
console.log(buildTranscriptWarning(preview));
|
|
5853
|
+
const report = buildRedactionReport(redactions);
|
|
5854
|
+
if (report)
|
|
5855
|
+
console.log(report);
|
|
5856
|
+
console.log("");
|
|
5857
|
+
}
|
|
5858
|
+
}
|
|
5398
5859
|
const payload = buildPayload(feedbackBody, category, options.title, options.session, transcript);
|
|
5399
5860
|
const config = loadConfig();
|
|
5400
5861
|
const endpoint = config.endpoint ?? "https://api.mutagent.io";
|
|
@@ -5407,12 +5868,24 @@ Or set an API key: mutagent config set apiKey <key>`);
|
|
|
5407
5868
|
title: options.title,
|
|
5408
5869
|
sessionId: options.session,
|
|
5409
5870
|
transcriptAttached: transcript !== null,
|
|
5871
|
+
...transcript ? {
|
|
5872
|
+
transcriptBytes: Buffer.byteLength(transcript.content, "utf-8"),
|
|
5873
|
+
transcriptSource: transcript.sourcePath,
|
|
5874
|
+
redactions: redactions.map((r) => ({ kind: r.kind, line: r.line }))
|
|
5875
|
+
} : {},
|
|
5876
|
+
...byDesign.length > 0 ? {
|
|
5877
|
+
byDesignMatches: byDesign.map((e) => ({
|
|
5878
|
+
id: e.id,
|
|
5879
|
+
title: e.title,
|
|
5880
|
+
reality: e.reality
|
|
5881
|
+
}))
|
|
5882
|
+
} : {},
|
|
5410
5883
|
_links: {
|
|
5411
5884
|
send: "mutagent feedback send <feedback> [--title <s>] [--category cli|helix|stage:<x>] [--session <id>] [--attach-transcript [path]]"
|
|
5412
5885
|
}
|
|
5413
5886
|
});
|
|
5414
5887
|
} else {
|
|
5415
|
-
const suffix = transcript ? ` + transcript (${String(
|
|
5888
|
+
const suffix = transcript ? ` + transcript (${String(Buffer.byteLength(transcript.content, "utf-8"))}B${transcript.truncated ? ` tail, truncated at ${String(TAIL_BYTES)}B cap` : ""})` : "";
|
|
5416
5889
|
output.success(`Feedback sent! (${category})${suffix}`);
|
|
5417
5890
|
}
|
|
5418
5891
|
} catch (error) {
|
|
@@ -5423,33 +5896,38 @@ Or set an API key: mutagent config set apiKey <key>`);
|
|
|
5423
5896
|
|
|
5424
5897
|
// src/commands/install/index.ts
|
|
5425
5898
|
import { Command as Command11 } from "commander";
|
|
5426
|
-
import
|
|
5899
|
+
import chalk18 from "chalk";
|
|
5427
5900
|
init_errors();
|
|
5428
5901
|
function createInstallCommand(deps) {
|
|
5429
|
-
const install = new Command11("install").description("Install a Mutagent package (helix, diagnostics, evaluator)").argument("<package>", `Package to install: ${VALID_PACKAGES.join(", ")}`).option("--harness <harness>", `Target harness: ${VALID_HARNESSES.join(", ")}`, "claude-code").option("--global", "Install
|
|
5430
|
-
${
|
|
5431
|
-
${
|
|
5432
|
-
${
|
|
5433
|
-
${
|
|
5434
|
-
${
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
${
|
|
5438
|
-
${
|
|
5439
|
-
|
|
5440
|
-
${
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
${
|
|
5445
|
-
${
|
|
5902
|
+
const install = new Command11("install").description("Install a Mutagent package (helix, diagnostics, evaluator)").argument("<package>", `Package to install: ${VALID_PACKAGES.join(", ")}`).option("--harness <harness>", `Target harness: ${VALID_HARNESSES.join(", ")}`, "claude-code").option("--global", "(retired) Install scope is fixed per package").option("--version <version>", "Package version to install (default: latest)").addHelpText("after", `
|
|
5903
|
+
${chalk18.bold("Arguments & flags:")}
|
|
5904
|
+
${chalk18.bold("<package>")} ${VALID_PACKAGES.join(" | ")}
|
|
5905
|
+
${chalk18.bold("--harness <h>")} ${VALID_HARNESSES.join(" | ")} ${chalk18.dim("(default: claude-code)")}
|
|
5906
|
+
${chalk18.bold("--version <v>")} Pin a package version ${chalk18.dim("(default: latest)")}
|
|
5907
|
+
${chalk18.bold("--json")} Structured output
|
|
5908
|
+
|
|
5909
|
+
${chalk18.bold("Install scope")} ${chalk18.dim("(fixed per package — not selectable)")}:
|
|
5910
|
+
${chalk18.bold("helix")} → installed into the current directory
|
|
5911
|
+
${chalk18.bold("diagnostics")} | ${chalk18.bold("evaluator")} → ${chalk18.yellow("installed GLOBALLY")} by npm; run the
|
|
5912
|
+
package's own init from your project afterwards.
|
|
5913
|
+
${chalk18.dim("--global")} is retired: scope is no longer a choice.
|
|
5914
|
+
|
|
5915
|
+
${chalk18.bold("Examples:")}
|
|
5916
|
+
${chalk18.dim("$")} mutagent install helix
|
|
5917
|
+
${chalk18.dim("$")} mutagent install helix --harness codex
|
|
5918
|
+
${chalk18.dim("$")} mutagent install diagnostics
|
|
5919
|
+
${chalk18.dim("$")} mutagent install evaluator --version 1.2.3
|
|
5920
|
+
${chalk18.dim("$")} mutagent install diagnostics --harness codex --json
|
|
5921
|
+
|
|
5922
|
+
${chalk18.bold("Packages:")}
|
|
5923
|
+
${chalk18.bold("helix")} ${chalk18.green("(available)")} — the ADLC (Agent Development Life Cycle Orchestrator). Downloaded from a private
|
|
5446
5924
|
registry via a login-brokered signed URL, sha256-verified, then
|
|
5447
5925
|
initialized into your project. No static secret ships in the CLI.
|
|
5448
|
-
${
|
|
5449
|
-
${
|
|
5450
|
-
${
|
|
5926
|
+
${chalk18.dim("Installs every harness itself (Claude + Codex) — --harness does not apply.")}
|
|
5927
|
+
${chalk18.bold("diagnostics")} Public npm package @mutagent/diagnostics ${chalk18.green("(available)")}
|
|
5928
|
+
${chalk18.bold("evaluator")} Public npm package @mutagent/evaluator ${chalk18.green("(available)")}
|
|
5451
5929
|
|
|
5452
|
-
${
|
|
5930
|
+
${chalk18.yellow("Note:")} install is login-gated. Run ${chalk18.cyan("mutagent login")} first (else exits with a login directive).
|
|
5453
5931
|
`).action(async (pkg, options) => {
|
|
5454
5932
|
const isJson = getJsonFlag(install);
|
|
5455
5933
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -5461,14 +5939,23 @@ ${chalk17.yellow("Note:")} install is login-gated. Run ${chalk17.cyan("mutagent
|
|
|
5461
5939
|
package: result.package,
|
|
5462
5940
|
version: result.version,
|
|
5463
5941
|
harness: result.harness,
|
|
5464
|
-
|
|
5942
|
+
scope: result.scope,
|
|
5943
|
+
...result.projectPath ? { projectPath: result.projectPath } : {},
|
|
5944
|
+
skillInstalled: result.skillInstalled,
|
|
5945
|
+
...result.nextStep ? { nextStep: result.nextStep } : {},
|
|
5465
5946
|
_links: {
|
|
5466
5947
|
install: "mutagent install <package> [--harness <h>] [--version <v>]",
|
|
5467
5948
|
login: "mutagent login"
|
|
5468
5949
|
}
|
|
5469
5950
|
});
|
|
5951
|
+
} else if (result.skillInstalled) {
|
|
5952
|
+
output.success(`Installed ${result.package}@${result.version} for ${result.harness} in ${result.projectPath ?? "this project"}`);
|
|
5470
5953
|
} else {
|
|
5471
|
-
output.success(`Installed ${result.package}@${result.version}
|
|
5954
|
+
output.success(`Installed npm package ${result.package}@${result.version} globally` + (result.command ? ` (${result.command})` : ""));
|
|
5955
|
+
console.log(`${chalk18.yellow("⚠")} No skill was installed yet — the package ships its own setup step.`);
|
|
5956
|
+
if (result.nextStep) {
|
|
5957
|
+
console.log(`${chalk18.blue("→")} Run next: ${chalk18.cyan(result.nextStep)}`);
|
|
5958
|
+
}
|
|
5472
5959
|
}
|
|
5473
5960
|
} catch (error) {
|
|
5474
5961
|
handleError(error, isJson);
|
|
@@ -5477,6 +5964,28 @@ ${chalk17.yellow("Note:")} install is login-gated. Run ${chalk17.cyan("mutagent
|
|
|
5477
5964
|
return install;
|
|
5478
5965
|
}
|
|
5479
5966
|
|
|
5967
|
+
// src/commands/trace.ts
|
|
5968
|
+
import { Command as Command12 } from "commander";
|
|
5969
|
+
init_errors();
|
|
5970
|
+
var TRACE_RELOCATION_HINT = "trace lives in the `mutagent-cli` binary, installed with @mutagent/diagnostics, @mutagent/evaluator or helix";
|
|
5971
|
+
function createTraceCommand() {
|
|
5972
|
+
const trace = new Command12("trace").description("Not in this binary — trace lives in mutagent-cli").allowUnknownOption(true).allowExcessArguments(true).argument("[args...]", "Arguments intended for `mutagent-cli trace`").addHelpText("after", `
|
|
5973
|
+
This binary (mutagent) does not provide trace.
|
|
5974
|
+
|
|
5975
|
+
${TRACE_RELOCATION_HINT}
|
|
5976
|
+
|
|
5977
|
+
Install one of those, then run:
|
|
5978
|
+
|
|
5979
|
+
mutagent-cli trace <args>
|
|
5980
|
+
`).action((args) => {
|
|
5981
|
+
const isJson = getJsonFlag(trace);
|
|
5982
|
+
const forwarded = args.length > 0 ? ` ${args.join(" ")}` : " <args>";
|
|
5983
|
+
handleError(new MutagentError("INVALID_ARGUMENTS", `\`mutagent trace\` is not a command — ${TRACE_RELOCATION_HINT}.`, `Run it with the other binary instead: mutagent-cli trace${forwarded}
|
|
5984
|
+
If mutagent-cli is not installed: mutagent install diagnostics`), isJson);
|
|
5985
|
+
});
|
|
5986
|
+
return trace;
|
|
5987
|
+
}
|
|
5988
|
+
|
|
5480
5989
|
// src/bin/cli.ts
|
|
5481
5990
|
init_config();
|
|
5482
5991
|
|
|
@@ -5553,13 +6062,13 @@ if (process.env.CLI_VERSION) {
|
|
|
5553
6062
|
} else {
|
|
5554
6063
|
try {
|
|
5555
6064
|
const __dirname2 = dirname3(fileURLToPath2(import.meta.url));
|
|
5556
|
-
const pkgPath =
|
|
6065
|
+
const pkgPath = join14(__dirname2, "..", "..", "package.json");
|
|
5557
6066
|
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
5558
6067
|
cliVersion = pkg.version ?? cliVersion;
|
|
5559
6068
|
} catch {}
|
|
5560
6069
|
}
|
|
5561
6070
|
setCliVersion(cliVersion);
|
|
5562
|
-
var program = new
|
|
6071
|
+
var program = new Command13;
|
|
5563
6072
|
program.name("mutagent").description(`Mutagent CLI - command-line client for the Mutagent platform
|
|
5564
6073
|
|
|
5565
6074
|
Documentation: https://docs.mutagent.io/cli
|
|
@@ -5569,52 +6078,52 @@ program.name("mutagent").description(`Mutagent CLI - command-line client for the
|
|
|
5569
6078
|
});
|
|
5570
6079
|
program.addHelpText("beforeAll", () => renderBanner());
|
|
5571
6080
|
program.addHelpText("after", `
|
|
5572
|
-
${
|
|
5573
|
-
${
|
|
5574
|
-
${
|
|
5575
|
-
${
|
|
5576
|
-
|
|
5577
|
-
${
|
|
5578
|
-
${
|
|
5579
|
-
|
|
5580
|
-
${
|
|
5581
|
-
export MUTAGENT_API_KEY=mt_... ${
|
|
5582
|
-
--json ${
|
|
5583
|
-
|
|
5584
|
-
${
|
|
5585
|
-
mutagent login ${
|
|
5586
|
-
mutagent auth status ${
|
|
5587
|
-
mutagent init ${
|
|
5588
|
-
mutagent workspaces list --json ${
|
|
5589
|
-
mutagent config set workspace <id> ${
|
|
5590
|
-
mutagent usage --json ${
|
|
5591
|
-
|
|
5592
|
-
mutagent providers list --json ${
|
|
5593
|
-
mutagent providers list --models ${
|
|
5594
|
-
|
|
5595
|
-
mutagent install helix ${
|
|
5596
|
-
mutagent install evaluator --version 1.2.3 ${
|
|
5597
|
-
mutagent install --help ${
|
|
5598
|
-
|
|
5599
|
-
mutagent hooks --help ${
|
|
5600
|
-
|
|
5601
|
-
${
|
|
5602
|
-
Hit a bug? Run: ${
|
|
5603
|
-
Lifecycle-stage feedback: ${
|
|
5604
|
-
Link to a session: ${
|
|
5605
|
-
Attach your coding-agent session transcript: ${
|
|
5606
|
-
${
|
|
5607
|
-
|
|
5608
|
-
${
|
|
6081
|
+
${chalk19.bold.cyan("WORKFLOWS:")}
|
|
6082
|
+
${chalk19.bold("Setup")} mutagent login → mutagent init
|
|
6083
|
+
${chalk19.bold("Lifecycle Tools")} mutagent install <helix|diagnostics|evaluator> ${chalk19.dim("(login-gated)")}
|
|
6084
|
+
${chalk19.bold("Feedback")} mutagent feedback send "<what happened>" --category <cli|helix|stage:<x>> ${chalk19.dim("[--session <id>] [--attach-transcript]")}
|
|
6085
|
+
|
|
6086
|
+
${chalk19.dim("For CLI usage guidance for AI agents, see the Skill at")}
|
|
6087
|
+
${chalk19.cyan(".claude/skills/mutagent-cli/SKILL.md")}
|
|
6088
|
+
|
|
6089
|
+
${chalk19.yellow("Non-Interactive Mode (CI/CD & Coding Agents):")}
|
|
6090
|
+
export MUTAGENT_API_KEY=mt_... ${chalk19.dim("or")} --api-key mt_...
|
|
6091
|
+
--json ${chalk19.dim("for structured output")} --non-interactive ${chalk19.dim("to disable prompts")}
|
|
6092
|
+
|
|
6093
|
+
${chalk19.yellow("Command Navigation:")}
|
|
6094
|
+
mutagent login ${chalk19.dim("Login (browser OAuth — recommended)")}
|
|
6095
|
+
mutagent auth status ${chalk19.dim("Check auth + workspace")}
|
|
6096
|
+
mutagent init ${chalk19.dim("Initialize project (.mutagentrc.json)")}
|
|
6097
|
+
mutagent workspaces list --json ${chalk19.dim("List workspaces (verify ID)")}
|
|
6098
|
+
mutagent config set workspace <id> ${chalk19.dim("Set active workspace")}
|
|
6099
|
+
mutagent usage --json ${chalk19.dim("Show account usage + provider status")}
|
|
6100
|
+
|
|
6101
|
+
mutagent providers list --json ${chalk19.dim("List configured BYOK providers")}
|
|
6102
|
+
mutagent providers list --models ${chalk19.dim("See available models per provider")}
|
|
6103
|
+
|
|
6104
|
+
mutagent install helix ${chalk19.dim("Install the ADL lifecycle conductor (login-gated)")}
|
|
6105
|
+
mutagent install evaluator --version 1.2.3 ${chalk19.dim("Pin a version")}
|
|
6106
|
+
mutagent install --help ${chalk19.dim("helix | diagnostics | evaluator")}
|
|
6107
|
+
|
|
6108
|
+
mutagent hooks --help ${chalk19.dim("Hook setup for Claude Code session telemetry upload")}
|
|
6109
|
+
|
|
6110
|
+
${chalk19.bold.red("Report Issues:")}
|
|
6111
|
+
Hit a bug? Run: ${chalk19.cyan('mutagent feedback send "describe what went wrong" --category cli')}
|
|
6112
|
+
Lifecycle-stage feedback: ${chalk19.cyan('mutagent feedback send "eval gate unclear" --category stage:evaluate')}
|
|
6113
|
+
Link to a session: ${chalk19.cyan('mutagent feedback send "..." --session <session-id>')}
|
|
6114
|
+
Attach your coding-agent session transcript: ${chalk19.cyan('mutagent feedback send "..." --attach-transcript')}
|
|
6115
|
+
${chalk19.dim("--category accepts: cli | helix | stage:<spec|build|evaluate|diagnose|optimize>")}
|
|
6116
|
+
|
|
6117
|
+
${chalk19.yellow("Directive System:")}
|
|
5609
6118
|
Every --json response may include:
|
|
5610
|
-
${
|
|
5611
|
-
${
|
|
5612
|
-
${
|
|
5613
|
-
${
|
|
5614
|
-
${
|
|
5615
|
-
${
|
|
5616
|
-
|
|
5617
|
-
${
|
|
6119
|
+
${chalk19.bold("_directive.display")} Type tag — 'status_card' for card-kind directives (drives test/docs guards)
|
|
6120
|
+
${chalk19.bold("_directive.renderedCard")} Pre-formatted card ${chalk19.red("(MUST echo verbatim in chat whenever this field exists — see SKILL.md Verbatim Card Display Protocol)")}
|
|
6121
|
+
${chalk19.bold("_directive.instruction")} Next step for the agent (self-sufficient, no Skill required)
|
|
6122
|
+
${chalk19.bold("_directive.next")} Array of suggested follow-up commands
|
|
6123
|
+
${chalk19.bold("_links")} Dashboard/API URLs (format as markdown links)
|
|
6124
|
+
${chalk19.bold("_compat")} Compat metadata: cliVersion, skillVersion, skillMinCliVersion
|
|
6125
|
+
|
|
6126
|
+
${chalk19.yellow("AI Agent Rules (MANDATORY for coding agents):")}
|
|
5618
6127
|
1. Login (two paths):
|
|
5619
6128
|
- CI / fully automated: export MUTAGENT_API_KEY=mt_... then mutagent login --json
|
|
5620
6129
|
- Helping a user onboard: mutagent login --browser --json
|
|
@@ -5624,13 +6133,13 @@ ${chalk18.yellow("AI Agent Rules (MANDATORY for coding agents):")}
|
|
|
5624
6133
|
2. EVERY command MUST include --json (no exceptions)
|
|
5625
6134
|
3. Run <command> --help BEFORE first use of any command
|
|
5626
6135
|
4. Parse _directive.renderedCard and copy it into your CHAT RESPONSE verbatim
|
|
5627
|
-
${
|
|
6136
|
+
${chalk19.red("HARD STOP")}: do NOT run further commands until the card is rendered in chat
|
|
5628
6137
|
5. After mutagent init, verify workspace: mutagent workspaces list --json
|
|
5629
6138
|
6. ALL user interaction via AskUserQuestion — CLI is non-interactive
|
|
5630
6139
|
${!hasCredentials() ? `
|
|
5631
|
-
` +
|
|
6140
|
+
` + chalk19.yellow(" Warning: Not authenticated. Run: mutagent login") + `
|
|
5632
6141
|
` : ""}${!hasRcConfig() ? `
|
|
5633
|
-
` +
|
|
6142
|
+
` + chalk19.green(" Get started: mutagent init") + `
|
|
5634
6143
|
` : ""}`);
|
|
5635
6144
|
var rawArgs = process.argv.slice(2);
|
|
5636
6145
|
if (rawArgs.includes("-v") || rawArgs.includes("--version")) {
|
|
@@ -5667,7 +6176,8 @@ program.addCommand(createUsageCommand());
|
|
|
5667
6176
|
program.addCommand(createHooksCommand());
|
|
5668
6177
|
program.addCommand(createInstallCommand());
|
|
5669
6178
|
program.addCommand(createFeedbackCommand());
|
|
6179
|
+
program.addCommand(createTraceCommand());
|
|
5670
6180
|
program.parse();
|
|
5671
6181
|
|
|
5672
|
-
//# debugId=
|
|
6182
|
+
//# debugId=EB9C87E26AFCFB5064756E2164756E21
|
|
5673
6183
|
//# sourceMappingURL=cli.js.map
|