@trops/dash-core 0.1.551 → 0.1.552

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.
@@ -50368,145 +50368,6 @@ function safeSend(win, channel, data) {
50368
50368
  }
50369
50369
  }
50370
50370
 
50371
- /**
50372
- * Resolve the working directory the spawned CLI should run in.
50373
- *
50374
- * Pure / no I/O — extracted so it can be unit-tested. The actual
50375
- * mkdir + spawnOpts.cwd assignment happens at the spawn site.
50376
- *
50377
- * Why this exists: when the widget-builder modal launches the CLI in
50378
- * lockdown mode, the spawned process inherits the parent (Electron)
50379
- * process cwd. In a dev build that's the dash-electron project root,
50380
- * which contains a CLAUDE.md describing the 4-phase development
50381
- * workflow. The CLI auto-loads that file and the AI announces "Let me
50382
- * scan the existing project to understand what's already here." —
50383
- * exactly what the lockdown was meant to prevent.
50384
- *
50385
- * Resolution order:
50386
- * 1. Caller passed an explicit cwd → use that (caller wins).
50387
- * 2. disableTools: true and no cwd → scratch dir under os.tmpdir().
50388
- * 3. Otherwise → null (caller doesn't set spawnOpts.cwd; the child
50389
- * inherits the parent's cwd, preserving AssistantPanel behavior
50390
- * where project context may be wanted).
50391
- *
50392
- * @param {object} opts
50393
- * @param {string=} opts.cwd
50394
- * @param {boolean=} opts.disableTools
50395
- * @returns {string|null}
50396
- */
50397
- function resolveLockdownCwd({ cwd, disableTools = false } = {}) {
50398
- if (cwd) return cwd;
50399
- if (disableTools) {
50400
- const os = require$$2$1;
50401
- const path = require$$1$1;
50402
- return path.join(os.tmpdir(), "dash-widget-builder-cli");
50403
- }
50404
- return null;
50405
- }
50406
-
50407
- /**
50408
- * Resolve the plugin directory the spawned CLI should look in.
50409
- *
50410
- * Same shape as resolveLockdownCwd. When in lockdown, point Claude
50411
- * Code at an empty scratch directory so plugin auto-discovery finds
50412
- * nothing — sealing the gap left by `--bare` (which strips CLAUDE.md
50413
- * and auto-memory but NOT user plugins under ~/.claude/plugins/).
50414
- * The Skill tool gets loaded from those plugins, which is how it
50415
- * survived the slice 17f flag set.
50416
- *
50417
- * Sibling of the scratch cwd dir — DIFFERENT name so plugin discovery
50418
- * doesn't pick up the cwd itself.
50419
- *
50420
- * @param {object} opts
50421
- * @param {string=} opts.pluginDir
50422
- * @param {boolean=} opts.disableTools
50423
- * @returns {string|null}
50424
- */
50425
- function resolveLockdownPluginDir({ pluginDir, disableTools = false } = {}) {
50426
- if (pluginDir) return pluginDir;
50427
- if (disableTools) {
50428
- const os = require$$2$1;
50429
- const path = require$$1$1;
50430
- return path.join(os.tmpdir(), "dash-widget-builder-cli-plugins");
50431
- }
50432
- return null;
50433
- }
50434
-
50435
- /**
50436
- * Canonical denylist for the lockdown — every built-in tool name we
50437
- * know about, comma-separated (no whitespace) so the single arg is
50438
- * Windows-shell-safe. Adding a new built-in upstream means appending
50439
- * its name here; the flag is belt-and-suspenders alongside `--tools ""`,
50440
- * but it's the only layer that actually keeps the Skill tool from
50441
- * being callable when a user has plugins installed in
50442
- * ~/.claude/plugins/.
50443
- */
50444
- const LOCKDOWN_DISALLOWED_TOOLS = [
50445
- "Skill",
50446
- "Bash",
50447
- "BashOutput",
50448
- "Read",
50449
- "Edit",
50450
- "Write",
50451
- "Glob",
50452
- "Grep",
50453
- "WebFetch",
50454
- "WebSearch",
50455
- "NotebookRead",
50456
- "NotebookEdit",
50457
- "Task",
50458
- "TodoWrite",
50459
- "KillBash",
50460
- "SlashCommand",
50461
- ].join(",");
50462
-
50463
- /**
50464
- * Path to the e2e-only argv capture file. Only written when
50465
- * `process.env.DASH_E2E === "1"` (set by `e2e/helpers/electron-app.js`
50466
- * for Playwright runs). Production launches never set it, so no
50467
- * diagnostic is written on a real user's machine.
50468
- *
50469
- * The capture deterministically pins the chain ChatCore → IPC →
50470
- * cliController. Slice 18a's diagnostic revealed that the chain
50471
- * silently dropped `replaceSystemPrompt` and `disableTools` for
50472
- * months. Without an argv assertion, the next break would be
50473
- * invisible until a user filed a bug. With it, the e2e test
50474
- * `widget-builder-cli-flow.spec.js` reads this file and asserts the
50475
- * full lockdown flag set is present — fails immediately if any flag
50476
- * goes missing.
50477
- */
50478
- const E2E_LAST_SPAWN_LOG_PATH = (() => {
50479
- const os = require$$2$1;
50480
- const path = require$$1$1;
50481
- return path.join(os.tmpdir(), "dash-cli-last-spawn.e2e.json");
50482
- })();
50483
-
50484
- /**
50485
- * Slice 18b — gated argv capture. No-op outside e2e mode.
50486
- *
50487
- * Captures argv only (NOT env values, NOT env keys — production users
50488
- * are protected by the env-var gate; we don't need the keys-only
50489
- * privacy hedge anymore since this never runs for them).
50490
- */
50491
- function captureLastSpawnArgsForE2E({ args, disableTools = false } = {}) {
50492
- if (process.env.DASH_E2E !== "1") return;
50493
- try {
50494
- const fs = require("fs");
50495
- const payload = {
50496
- ts: Date.now(),
50497
- disableTools: disableTools === true,
50498
- args: Array.isArray(args) ? args.slice() : [],
50499
- };
50500
- fs.writeFileSync(
50501
- E2E_LAST_SPAWN_LOG_PATH,
50502
- JSON.stringify(payload, null, 2),
50503
- { encoding: "utf8" },
50504
- );
50505
- } catch (_e) {
50506
- // Capture must NEVER break the spawn.
50507
- }
50508
- }
50509
-
50510
50371
  /**
50511
50372
  * Build the argv array for spawning the Claude Code CLI.
50512
50373
  *
@@ -50515,19 +50376,28 @@ function captureLastSpawnArgsForE2E({ args, disableTools = false } = {}) {
50515
50376
  *
50516
50377
  * Defaults preserve AssistantPanel behavior: system prompt is APPENDED
50517
50378
  * to Claude Code's default (so the AI still sees its tool / MCP /
50518
- * skill preamble) and built-in tools stay available. The widget
50519
- * builder modal opts into a stricter mode by passing both
50520
- * `replaceSystemPrompt: true` (replace the default rather than append
50521
- * — strips the tool / skill preamble) and `disableTools: true` (deny
50522
- * all built-in tools so the AI cannot invoke Skill / Bash / Read /
50523
- * Glob / etc.).
50379
+ * skill preamble) and built-in tools / plugins / CLAUDE.md / auto-
50380
+ * memory all load normally.
50381
+ *
50382
+ * Callers may opt into `replaceSystemPrompt: true` to swap
50383
+ * `--append-system-prompt` for `--system-prompt`, replacing Claude
50384
+ * Code's default preamble with the caller's. The widget builder
50385
+ * modal does this so its terse "use the dash-widget-builder skill"
50386
+ * prompt isn't drowned out by Claude Code's default tool-advertising
50387
+ * preamble. The skill itself still auto-loads — that's the point.
50388
+ *
50389
+ * The `disableTools` prop survives for backwards compatibility with
50390
+ * existing callers, but no longer adds any CLI flag (slice 19B
50391
+ * dropped the flag-based lockdown after we discovered the project
50392
+ * skill carries the same constraints more reliably). The prop now
50393
+ * only gates the dash MCP auto-wire downstream of this helper.
50524
50394
  *
50525
50395
  * @param {object} opts
50526
50396
  * @param {string=} opts.model
50527
50397
  * @param {string=} opts.systemPrompt
50528
50398
  * @param {string=} opts.sessionId
50529
50399
  * @param {boolean=} opts.replaceSystemPrompt
50530
- * @param {boolean=} opts.disableTools
50400
+ * @param {boolean=} opts.disableTools (no-op for argv; gates MCP wiring elsewhere)
50531
50401
  * @returns {string[]}
50532
50402
  */
50533
50403
  function buildClaudeCliArgs({
@@ -50535,6 +50405,7 @@ function buildClaudeCliArgs({
50535
50405
  systemPrompt,
50536
50406
  sessionId,
50537
50407
  replaceSystemPrompt = false,
50408
+ // eslint-disable-next-line no-unused-vars
50538
50409
  disableTools = false,
50539
50410
  } = {}) {
50540
50411
  const args = [
@@ -50547,39 +50418,6 @@ function buildClaudeCliArgs({
50547
50418
  "--verbose",
50548
50419
  ];
50549
50420
 
50550
- if (disableTools) {
50551
- // Empty-string value documented in `claude --help` as "disable
50552
- // all tools". Catches Skill / Bash / Read / Glob / Grep / Edit /
50553
- // Write — every built-in tool the AI might otherwise invoke from
50554
- // inside the modal where only text + code-block output is wanted.
50555
- args.push("--tools", "");
50556
-
50557
- // `--tools ""` alone leaks ambient context: in 2.1.138 we saw the
50558
- // AI mention `mcp__dash__*` tool names by name, and the Skill
50559
- // tool kept firing despite the empty allowlist. The leak comes
50560
- // from outside cwd — global ~/.claude/CLAUDE.md, project memory
50561
- // under ~/.claude/projects/<key>/memory/, and user-installed
50562
- // skills. `--bare` is documented as "skip hooks, LSP, plugin
50563
- // sync, attribution, auto-memory, background prefetches, keychain
50564
- // reads, and CLAUDE.md auto-discovery." Combined with
50565
- // `--strict-mcp-config` (which suppresses every MCP server we
50566
- // don't explicitly pass via --mcp-config), the spawned CLI gets
50567
- // exactly our --system-prompt and nothing else.
50568
- args.push("--bare");
50569
- args.push("--strict-mcp-config");
50570
-
50571
- // `--bare` strips CLAUDE.md and auto-memory but NOT user plugins
50572
- // (under ~/.claude/plugins/). The Skill TOOL gets loaded from
50573
- // those plugins, which is why it kept firing despite slices 17e
50574
- // and 17f. Two layers seal it:
50575
- // 1. --disallowed-tools denies every known built-in by name
50576
- // (comma-separated, no whitespace, Windows-shell-safe).
50577
- // 2. --plugin-dir points at an empty scratch dir under
50578
- // os.tmpdir() so plugin auto-discovery finds nothing.
50579
- args.push("--disallowed-tools", LOCKDOWN_DISALLOWED_TOOLS);
50580
- args.push("--plugin-dir", resolveLockdownPluginDir({ disableTools: true }));
50581
- }
50582
-
50583
50421
  if (model) {
50584
50422
  args.push("--model", model);
50585
50423
  }
@@ -50783,30 +50621,20 @@ const cliController$2 = {
50783
50621
  // files directly without a shell — ENOENT otherwise.
50784
50622
  shell: IS_WINDOWS,
50785
50623
  };
50786
- const resolvedCwd = resolveLockdownCwd({ cwd, disableTools });
50787
- if (resolvedCwd) {
50788
- const fs = require("fs");
50789
- if (!fs.existsSync(resolvedCwd)) {
50790
- fs.mkdirSync(resolvedCwd, { recursive: true });
50791
- }
50792
- spawnOpts.cwd = resolvedCwd;
50793
- }
50794
- // The --plugin-dir argv we built above points at a scratch
50795
- // directory; create it on first run so the CLI doesn't error
50796
- // out trying to read a missing path.
50797
- if (disableTools) {
50624
+ // Caller may pass an explicit cwd (e.g., the widget-builder
50625
+ // modal points at the dash-electron project root so its
50626
+ // dash-widget-builder skill auto-loads). When absent, the child
50627
+ // inherits the parent process's cwd — which is what the
50628
+ // AssistantPanel relies on.
50629
+ if (cwd) {
50798
50630
  const fs = require("fs");
50799
- const lockdownPluginDir = resolveLockdownPluginDir({
50800
- disableTools: true,
50801
- });
50802
- if (lockdownPluginDir && !fs.existsSync(lockdownPluginDir)) {
50803
- fs.mkdirSync(lockdownPluginDir, { recursive: true });
50631
+ if (!fs.existsSync(cwd)) {
50632
+ fs.mkdirSync(cwd, { recursive: true });
50804
50633
  }
50634
+ spawnOpts.cwd = cwd;
50805
50635
  }
50806
50636
  const spawnCmd = IS_WINDOWS ? windowsQuote(binaryPath) : binaryPath;
50807
50637
  const spawnArgs = IS_WINDOWS ? args.map(windowsQuote) : args;
50808
- // No-op outside e2e mode (gated on process.env.DASH_E2E === "1").
50809
- captureLastSpawnArgsForE2E({ args, disableTools });
50810
50638
  const child = spawn(spawnCmd, spawnArgs, spawnOpts);
50811
50639
 
50812
50640
  activeProcesses.set(requestId, child);
@@ -51125,10 +50953,6 @@ const cliController$2 = {
51125
50953
 
51126
50954
  cliController$3.exports = cliController$2;
51127
50955
  cliController$3.exports.buildClaudeCliArgs = buildClaudeCliArgs;
51128
- cliController$3.exports.resolveLockdownCwd = resolveLockdownCwd;
51129
- cliController$3.exports.resolveLockdownPluginDir = resolveLockdownPluginDir;
51130
- cliController$3.exports.LOCKDOWN_DISALLOWED_TOOLS = LOCKDOWN_DISALLOWED_TOOLS;
51131
- cliController$3.exports.E2E_LAST_SPAWN_LOG_PATH = E2E_LAST_SPAWN_LOG_PATH;
51132
50956
 
51133
50957
  var cliControllerExports = cliController$3.exports;
51134
50958