@mutagent/cli 0.1.248 → 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 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, writeFileSync, mkdirSync } from "fs";
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
- writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
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
- writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
110
+ writeSecureFile(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2));
100
111
  }
101
112
  function clearCredentials() {
102
113
  if (existsSync(CREDENTIALS_FILE)) {
103
- writeFileSync(CREDENTIALS_FILE, JSON.stringify({}));
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
- writeFileSync(CREDENTIALS_FILE, JSON.stringify(updated, null, 2));
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
- writeFileSync(CREDENTIALS_FILE, JSON.stringify(updated, null, 2));
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 Command12 } from "commander";
870
- import chalk18 from "chalk";
881
+ import { Command as Command13 } from "commander";
882
+ import chalk19 from "chalk";
871
883
  import { readFileSync as readFileSync10 } from "fs";
872
- import { join as join13, dirname as dirname3 } from "path";
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 join6 } from "path";
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", "Could not find package.json in the extracted helix plugin.", "The archive layout is unexpected — retry or report: mutagent install helix"));
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: "inherit" });
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 helix plugin init: ${err.message}`, 'Ensure "node" is installed and available on your PATH.'));
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, global: opts.global, version }, deps.helix ?? {});
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
- global: opts.global
1450
+ scope: "project",
1451
+ projectPath,
1452
+ skillInstalled: true
1401
1453
  }, deps);
1402
1454
  }
1403
1455
  const npmPackage = NPM_PACKAGES[pkg];
1404
- const args = ["install", "-g", `${npmPackage}@${version}`];
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)}` : ""}`, "Verify the package and version exist and that you have permission for a global npm install.");
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
- global: opts.global,
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(join6(cwd, ".mutagentrc.json"));
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 join8 } from "path";
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 mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
3311
- import { dirname, join as join7 } from "path";
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 with session context.
3527
- 3. **On user approval**, send feedback with the session transcript attached:
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 --attach-transcript --json
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 when reporting a bug or a session-specific issue
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> [--attach-transcript] --json
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" --attach-transcript --json
3704
+ --title "login crash on first run" --json
3633
3705
  mutagent feedback send "Diagnose loop stalled" --category stage:diagnose \\
3634
- --session run_abc123 --attach-transcript /tmp/session.jsonl --json
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 feedback with the transcript attached so the team gets full context:
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**: installs a Mutagent package globally (or per the target harness).
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
- [--global] [--version <v>] [--json]
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\` -- install globally (default on).
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
- e.g. "I'll install @mutagent/diagnostics globally for claude-code. Proceed?"
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 (package, version, harness) to the user
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
- - On success (\`{ success: true, package, version, harness, global }\`): tell the user what was installed and the resolved version. Surface \`_links.install\` / \`_links.login\`.
3764
- - For \`helix\`: the CLI resolves a signed download URL from the login broker, downloads + sha256-verifies the plugin, then runs its init into the project. An \`INTEGRITY_ERROR\` means the download failed checksum verification — retry.
3765
- - On an auth error (including a broker \`AUTH_REQUIRED\`): route to the login workflow, then retry.
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 = join7(targetRoot, SKILL_DIR);
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 = join7(skillDir, relPath);
4130
+ const destPath = join8(skillDir, relPath);
3969
4131
  const parentDir = dirname(destPath);
3970
4132
  if (!existsSync6(parentDir)) {
3971
- mkdirSync3(parentDir, { recursive: true });
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(join8(cwd, ".mutagentrc.json"), "utf-8"));
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 join9 } from "path";
4379
+ import { join as join10 } from "path";
4218
4380
  import { tmpdir } from "os";
4219
4381
  function stateFilePath(sessionId) {
4220
- return join9(tmpdir(), `mutagent-hook-${sessionId}.json`);
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 mkdirSync4 } from "fs";
4895
- import { join as join10 } from "path";
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 = join10(cwd, ".claude");
4958
- const settingsPath = join10(claudeDir, "settings.local.json");
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
- mkdirSync4(claudeDir, { recursive: true });
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 chalk16 from "chalk";
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 join12, dirname as dirname2 } from "path";
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 join11 } from "path";
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 = readdirSync(current, { withFileTypes: true });
5350
+ entries = readdirSync2(current, { withFileTypes: true });
5189
5351
  } catch {
5190
5352
  return;
5191
5353
  }
5192
5354
  for (const entry of entries) {
5193
- const full = join11(current, entry.name);
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 ?? join11(home, ".omp", "agent");
5366
+ const ompBase = env.PI_CODING_AGENT_DIR ?? join12(home, ".omp", "agent");
5205
5367
  return [
5206
- { harness: "claude-code", dir: join11(home, ".claude", "projects") },
5207
- { harness: "codex", dir: join11(home, ".codex", "sessions") },
5208
- { harness: "omp", dir: join11(ompBase, "sessions") }
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 = join12(__dirname2, "..", "..", "package.json");
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
- ${chalk16.bold("Examples:")}
5340
- ${chalk16.cyan('mutagent feedback send "Optimizer results were great"')}
5341
- ${chalk16.cyan('mutagent feedback send "Eval gate was confusing" --category stage:evaluate')}
5342
- ${chalk16.cyan('mutagent feedback send "CLI crashed on export" --category cli --attach-transcript --json')}
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
- ${chalk16.yellow("AI Agent (MANDATORY):")}
5776
+ ${chalk17.yellow("AI Agent (MANDATORY):")}
5345
5777
  ALWAYS use --json: mutagent feedback send "..." --category cli --json
5346
- Attach the coding-agent session with --attach-transcript (auto-detects the newest session).
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
- ${chalk16.bold("Arguments & flags:")}
5356
- ${chalk16.bold("<feedback>")} Feedback body / content (required, ≤10000 chars)
5357
- ${chalk16.bold("--title <string>")} Optional 5–8 word summary of the session timeline
5358
- ${chalk16.bold("--category <value>")} ${chalk16.bold("cli")} (default) | ${chalk16.bold("helix")} | ${chalk16.bold("stage:<")}${VALID_STAGES.join("|")}${chalk16.bold(">")}
5359
- ${chalk16.dim("stage = the lifecycle skill (evaluator/diagnostics live under stage:*)")}
5360
- ${chalk16.bold("--session <id>")} Link feedback to a session id (server sessionId)
5361
- ${chalk16.bold("--attach-transcript")} ${chalk16.dim("[path]")} Attach the coding-agent session JSONL (bare = auto-detect newest)
5362
- ${chalk16.bold("--json")} Structured output (MANDATORY for AI agents)
5363
-
5364
- ${chalk16.bold("Examples:")}
5365
- ${chalk16.dim("$")} mutagent feedback send "The setup flow could show progress better"
5366
- ${chalk16.dim("$")} mutagent feedback send "Eval gate was confusing" --category stage:evaluate --title "eval gate unclear"
5367
- ${chalk16.dim("$")} mutagent feedback send "Diagnose loop stalled" --category stage:diagnose --session sess_abc123 --json
5368
- ${chalk16.dim("$")} mutagent feedback send "CLI crashed mid-run" --category cli --attach-transcript --json
5369
- ${chalk16.dim("$")} mutagent feedback send "Repro attached" --category cli --attach-transcript /tmp/session.jsonl
5370
-
5371
- ${chalk16.bold("Category:")}
5372
- ${chalk16.bold("cli")} Feedback about the CLI itself (default)
5373
- ${chalk16.bold("helix")} Feedback about Helix
5374
- ${chalk16.bold("stage:<x>")} Feedback about a lifecycle stage: ${VALID_STAGES.join(", ")}
5375
-
5376
- ${chalk16.bold("Transcript (--attach-transcript):")}
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
- ${chalk16.yellow("AI Agent (MANDATORY):")}
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 transcript = resolveTranscript(options.attachTranscript);
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(TAIL_BYTES)}B tail${transcript.truncated ? ", truncated" : ""})` : "";
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 chalk17 from "chalk";
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 to ~/.claude instead of this project", false).option("--version <version>", "Package version to install (default: latest)").addHelpText("after", `
5430
- ${chalk17.bold("Arguments & flags:")}
5431
- ${chalk17.bold("<package>")} ${VALID_PACKAGES.join(" | ")}
5432
- ${chalk17.bold("--harness <h>")} ${VALID_HARNESSES.join(" | ")} ${chalk17.dim("(default: claude-code)")}
5433
- ${chalk17.bold("--global")} Install to ~/.claude ${chalk17.dim("(default: this project)")}
5434
- ${chalk17.bold("--version <v>")} Pin a package version ${chalk17.dim("(default: latest)")}
5435
- ${chalk17.bold("--json")} Structured output
5436
-
5437
- ${chalk17.bold("Examples:")}
5438
- ${chalk17.dim("$")} mutagent install helix
5439
- ${chalk17.dim("$")} mutagent install helix --harness codex
5440
- ${chalk17.dim("$")} mutagent install diagnostics
5441
- ${chalk17.dim("$")} mutagent install evaluator --version 1.2.3
5442
- ${chalk17.dim("$")} mutagent install diagnostics --harness codex --json
5443
-
5444
- ${chalk17.bold("Packages:")}
5445
- ${chalk17.bold("helix")} ${chalk17.green("(available)")} the ADLC (Agent Development Life Cycle Orchestrator). Downloaded from a private
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
- ${chalk17.dim("Installs every harness itself (Claude + Codex) — --harness does not apply.")}
5449
- ${chalk17.bold("diagnostics")} Public npm package @mutagent/diagnostics ${chalk17.green("(available)")}
5450
- ${chalk17.bold("evaluator")} Public npm package @mutagent/evaluator ${chalk17.green("(available)")}
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
- ${chalk17.yellow("Note:")} install is login-gated. Run ${chalk17.cyan("mutagent login")} first (else exits with a login directive).
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
- global: result.global,
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} for ${result.harness}` + (result.command ? ` (${result.command})` : ""));
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 = join13(__dirname2, "..", "..", "package.json");
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 Command12;
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
- ${chalk18.bold.cyan("WORKFLOWS:")}
5573
- ${chalk18.bold("Setup")} mutagent login → mutagent init
5574
- ${chalk18.bold("Lifecycle Tools")} mutagent install <helix|diagnostics|evaluator> ${chalk18.dim("(login-gated)")}
5575
- ${chalk18.bold("Feedback")} mutagent feedback send "<what happened>" --category <cli|helix|stage:<x>> ${chalk18.dim("[--session <id>] [--attach-transcript]")}
5576
-
5577
- ${chalk18.dim("For CLI usage guidance for AI agents, see the Skill at")}
5578
- ${chalk18.cyan(".claude/skills/mutagent-cli/SKILL.md")}
5579
-
5580
- ${chalk18.yellow("Non-Interactive Mode (CI/CD & Coding Agents):")}
5581
- export MUTAGENT_API_KEY=mt_... ${chalk18.dim("or")} --api-key mt_...
5582
- --json ${chalk18.dim("for structured output")} --non-interactive ${chalk18.dim("to disable prompts")}
5583
-
5584
- ${chalk18.yellow("Command Navigation:")}
5585
- mutagent login ${chalk18.dim("Login (browser OAuth — recommended)")}
5586
- mutagent auth status ${chalk18.dim("Check auth + workspace")}
5587
- mutagent init ${chalk18.dim("Initialize project (.mutagentrc.json)")}
5588
- mutagent workspaces list --json ${chalk18.dim("List workspaces (verify ID)")}
5589
- mutagent config set workspace <id> ${chalk18.dim("Set active workspace")}
5590
- mutagent usage --json ${chalk18.dim("Show account usage + provider status")}
5591
-
5592
- mutagent providers list --json ${chalk18.dim("List configured BYOK providers")}
5593
- mutagent providers list --models ${chalk18.dim("See available models per provider")}
5594
-
5595
- mutagent install helix ${chalk18.dim("Install the ADL lifecycle conductor (login-gated)")}
5596
- mutagent install evaluator --version 1.2.3 ${chalk18.dim("Pin a version")}
5597
- mutagent install --help ${chalk18.dim("helix | diagnostics | evaluator")}
5598
-
5599
- mutagent hooks --help ${chalk18.dim("Hook setup for Claude Code session telemetry upload")}
5600
-
5601
- ${chalk18.bold.red("Report Issues:")}
5602
- Hit a bug? Run: ${chalk18.cyan('mutagent feedback send "describe what went wrong" --category cli')}
5603
- Lifecycle-stage feedback: ${chalk18.cyan('mutagent feedback send "eval gate unclear" --category stage:evaluate')}
5604
- Link to a session: ${chalk18.cyan('mutagent feedback send "..." --session <session-id>')}
5605
- Attach your coding-agent session transcript: ${chalk18.cyan('mutagent feedback send "..." --attach-transcript')}
5606
- ${chalk18.dim("--category accepts: cli | helix | stage:<spec|build|evaluate|diagnose|optimize>")}
5607
-
5608
- ${chalk18.yellow("Directive System:")}
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
- ${chalk18.bold("_directive.display")} Type tag — 'status_card' for card-kind directives (drives test/docs guards)
5611
- ${chalk18.bold("_directive.renderedCard")} Pre-formatted card ${chalk18.red("(MUST echo verbatim in chat whenever this field exists — see SKILL.md Verbatim Card Display Protocol)")}
5612
- ${chalk18.bold("_directive.instruction")} Next step for the agent (self-sufficient, no Skill required)
5613
- ${chalk18.bold("_directive.next")} Array of suggested follow-up commands
5614
- ${chalk18.bold("_links")} Dashboard/API URLs (format as markdown links)
5615
- ${chalk18.bold("_compat")} Compat metadata: cliVersion, skillVersion, skillMinCliVersion
5616
-
5617
- ${chalk18.yellow("AI Agent Rules (MANDATORY for coding agents):")}
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
- ${chalk18.red("HARD STOP")}: do NOT run further commands until the card is rendered in chat
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
- ` + chalk18.yellow(" Warning: Not authenticated. Run: mutagent login") + `
6140
+ ` + chalk19.yellow(" Warning: Not authenticated. Run: mutagent login") + `
5632
6141
  ` : ""}${!hasRcConfig() ? `
5633
- ` + chalk18.green(" Get started: mutagent init") + `
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=270F5210E72B26F864756E2164756E21
6182
+ //# debugId=EB9C87E26AFCFB5064756E2164756E21
5673
6183
  //# sourceMappingURL=cli.js.map