@trops/dash-core 0.1.546 → 0.1.548

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.
@@ -29742,6 +29742,8 @@ const menuItemsController$1 = {
29742
29742
 
29743
29743
  var menuItemsController_1 = menuItemsController$1;
29744
29744
 
29745
+ var cliController$3 = {exports: {}};
29746
+
29745
29747
  var mcp = {};
29746
29748
 
29747
29749
  var server$1 = {};
@@ -50366,6 +50368,114 @@ function safeSend(win, channel, data) {
50366
50368
  }
50367
50369
  }
50368
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
+ * Build the argv array for spawning the Claude Code CLI.
50409
+ *
50410
+ * Pure / no I/O — extracted as a top-level helper so it can be unit-
50411
+ * tested without spawning a child process.
50412
+ *
50413
+ * Defaults preserve AssistantPanel behavior: system prompt is APPENDED
50414
+ * to Claude Code's default (so the AI still sees its tool / MCP /
50415
+ * skill preamble) and built-in tools stay available. The widget
50416
+ * builder modal opts into a stricter mode by passing both
50417
+ * `replaceSystemPrompt: true` (replace the default rather than append
50418
+ * — strips the tool / skill preamble) and `disableTools: true` (deny
50419
+ * all built-in tools so the AI cannot invoke Skill / Bash / Read /
50420
+ * Glob / etc.).
50421
+ *
50422
+ * @param {object} opts
50423
+ * @param {string=} opts.model
50424
+ * @param {string=} opts.systemPrompt
50425
+ * @param {string=} opts.sessionId
50426
+ * @param {boolean=} opts.replaceSystemPrompt
50427
+ * @param {boolean=} opts.disableTools
50428
+ * @returns {string[]}
50429
+ */
50430
+ function buildClaudeCliArgs({
50431
+ model,
50432
+ systemPrompt,
50433
+ sessionId,
50434
+ replaceSystemPrompt = false,
50435
+ disableTools = false,
50436
+ } = {}) {
50437
+ const args = [
50438
+ "-p",
50439
+ "--disable-slash-commands",
50440
+ "--permission-mode",
50441
+ "bypassPermissions",
50442
+ "--output-format",
50443
+ "stream-json",
50444
+ "--verbose",
50445
+ ];
50446
+
50447
+ if (disableTools) {
50448
+ // Empty-string value documented in `claude --help` as "disable
50449
+ // all tools". Catches Skill / Bash / Read / Glob / Grep / Edit /
50450
+ // Write — every built-in tool the AI might otherwise invoke from
50451
+ // inside the modal where only text + code-block output is wanted.
50452
+ args.push("--tools", "");
50453
+ }
50454
+
50455
+ if (model) {
50456
+ args.push("--model", model);
50457
+ }
50458
+
50459
+ if (systemPrompt) {
50460
+ // --system-prompt REPLACES the default Claude Code preamble (the
50461
+ // one that introduces tools and auto-loads project skills by
50462
+ // description match). --append-system-prompt KEEPS the default
50463
+ // and adds ours below — fine for the assistant panel where MCP
50464
+ // tools are wanted, wrong for the widget builder modal where
50465
+ // any tool advertisement undermines the no-tools rule.
50466
+ args.push(
50467
+ replaceSystemPrompt ? "--system-prompt" : "--append-system-prompt",
50468
+ systemPrompt,
50469
+ );
50470
+ }
50471
+
50472
+ if (sessionId) {
50473
+ args.push("--resume", sessionId);
50474
+ }
50475
+
50476
+ return args;
50477
+ }
50478
+
50369
50479
  const cliController$2 = {
50370
50480
  /**
50371
50481
  * isAvailable
@@ -50390,7 +50500,15 @@ const cliController$2 = {
50390
50500
  * @param {object} params - { model, messages, systemPrompt, maxToolRounds, widgetUuid }
50391
50501
  */
50392
50502
  sendMessage: async (win, requestId, params) => {
50393
- const { model, messages, systemPrompt, widgetUuid, cwd } = params;
50503
+ const {
50504
+ model,
50505
+ messages,
50506
+ systemPrompt,
50507
+ widgetUuid,
50508
+ cwd,
50509
+ replaceSystemPrompt = false,
50510
+ disableTools = false,
50511
+ } = params;
50394
50512
 
50395
50513
  const binaryPath = resolveCliBinary();
50396
50514
  if (!binaryPath) {
@@ -50403,34 +50521,23 @@ const cliController$2 = {
50403
50521
  return;
50404
50522
  }
50405
50523
 
50406
- // Build CLI args.
50524
+ // Build CLI args via the pure helper (see top of file). Defaults
50525
+ // preserve the AssistantPanel behavior (append-system-prompt,
50526
+ // tools available, MCP wired). The widget builder modal opts
50527
+ // into `replaceSystemPrompt + disableTools` to lock the AI to
50528
+ // text + code-block output with no tool invocations.
50407
50529
  //
50408
- // --disable-slash-commands: prevent Claude from auto-triggering project
50409
- // skills by description match. When running in a project with a
50410
- // `.claude/skills/<name>/SKILL.md`, Claude would otherwise internalize
50411
- // that skill's content even when the host app's system prompt says not
50412
- // to — causing long reasoning loops or silent hangs. We pass only the
50413
- // caller's `systemPrompt` as context.
50414
- //
50415
- // --permission-mode bypassPermissions: in an embedded in-app assistant,
50416
- // the user has already opted into the configured MCP servers (they
50417
- // ran `claude mcp add` themselves). Prompting for tool-use approval
50418
- // on every call produces "I need permission to..." replies instead of
50419
- // actual actions. Bypassing matches the user's intent — if they
50420
- // didn't want the assistant to use a tool, they wouldn't have
50421
- // configured it.
50422
- //
50423
- // (We intentionally avoid `--bare` — it also disables keychain reads,
50424
- // which breaks OAuth login for users authenticated via `claude login`.)
50425
- const args = [
50426
- "-p",
50427
- "--disable-slash-commands",
50428
- "--permission-mode",
50429
- "bypassPermissions",
50430
- "--output-format",
50431
- "stream-json",
50432
- "--verbose",
50433
- ];
50530
+ // The model + systemPrompt + sessionId pieces are added inside
50531
+ // buildClaudeCliArgs; below we only add args that depend on
50532
+ // runtime state (MCP config file paths) the helper can't know.
50533
+ const sessionIdForResume = widgetUuid ? sessions.get(widgetUuid) : null;
50534
+ const args = buildClaudeCliArgs({
50535
+ model,
50536
+ systemPrompt,
50537
+ sessionId: sessionIdForResume,
50538
+ replaceSystemPrompt,
50539
+ disableTools,
50540
+ });
50434
50541
 
50435
50542
  // Auto-wire the hosted Dash MCP server so the assistant can use Dash
50436
50543
  // tools (apply_theme, create_dashboard, add_widget, etc.) without
@@ -50454,9 +50561,15 @@ const cliController$2 = {
50454
50561
  // works for non-Dash queries, and the setup banner remains visible
50455
50562
  // as a manual fallback.
50456
50563
  let mcpConfigFilePath = null;
50564
+ // Skip MCP wiring entirely when the caller has asked for a
50565
+ // tool-free invocation. There's no point loading remote tools
50566
+ // the AI cannot call, and avoiding the temp-file write keeps
50567
+ // the lockdown mode side-effect-free.
50457
50568
  try {
50458
50569
  const mcpDashServerController = mcpDashServerController_1;
50459
- const status = mcpDashServerController.getStatus?.(win);
50570
+ const status = disableTools
50571
+ ? null
50572
+ : mcpDashServerController.getStatus?.(win);
50460
50573
  if (status?.running) {
50461
50574
  const token = mcpDashServerController.getOrCreateToken?.(win);
50462
50575
  if (token) {
@@ -50511,20 +50624,6 @@ const cliController$2 = {
50511
50624
  mcpConfigFilePath = null;
50512
50625
  };
50513
50626
 
50514
- if (model) {
50515
- args.push("--model", model);
50516
- }
50517
-
50518
- if (systemPrompt) {
50519
- args.push("--append-system-prompt", systemPrompt);
50520
- }
50521
-
50522
- // Resume existing session for conversation continuity
50523
- const sessionId = widgetUuid ? sessions.get(widgetUuid) : null;
50524
- if (sessionId) {
50525
- args.push("--resume", sessionId);
50526
- }
50527
-
50528
50627
  // Extract the user message (last user message in the array)
50529
50628
  const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
50530
50629
  const userText =
@@ -50556,12 +50655,13 @@ const cliController$2 = {
50556
50655
  // files directly without a shell — ENOENT otherwise.
50557
50656
  shell: IS_WINDOWS,
50558
50657
  };
50559
- if (cwd) {
50658
+ const resolvedCwd = resolveLockdownCwd({ cwd, disableTools });
50659
+ if (resolvedCwd) {
50560
50660
  const fs = require("fs");
50561
- if (!fs.existsSync(cwd)) {
50562
- fs.mkdirSync(cwd, { recursive: true });
50661
+ if (!fs.existsSync(resolvedCwd)) {
50662
+ fs.mkdirSync(resolvedCwd, { recursive: true });
50563
50663
  }
50564
- spawnOpts.cwd = cwd;
50664
+ spawnOpts.cwd = resolvedCwd;
50565
50665
  }
50566
50666
  const spawnCmd = IS_WINDOWS ? windowsQuote(binaryPath) : binaryPath;
50567
50667
  const spawnArgs = IS_WINDOWS ? args.map(windowsQuote) : args;
@@ -50754,7 +50854,11 @@ const cliController$2 = {
50754
50854
 
50755
50855
  if (code !== 0 && code !== null) {
50756
50856
  // Check if resume failed and retry without it
50757
- if (sessionId && !retried && stderrBuffer.includes("session")) {
50857
+ if (
50858
+ sessionIdForResume &&
50859
+ !retried &&
50860
+ stderrBuffer.includes("session")
50861
+ ) {
50758
50862
  retried = true;
50759
50863
  if (widgetUuid) sessions.delete(widgetUuid);
50760
50864
  // Retry without --resume
@@ -50877,7 +50981,11 @@ const cliController$2 = {
50877
50981
  },
50878
50982
  };
50879
50983
 
50880
- var cliController_1 = cliController$2;
50984
+ cliController$3.exports = cliController$2;
50985
+ cliController$3.exports.buildClaudeCliArgs = buildClaudeCliArgs;
50986
+ cliController$3.exports.resolveLockdownCwd = resolveLockdownCwd;
50987
+
50988
+ var cliControllerExports = cliController$3.exports;
50881
50989
 
50882
50990
  /**
50883
50991
  * toolDefinitions.js
@@ -56092,7 +56200,7 @@ var toolHandlers$1 = {
56092
56200
 
56093
56201
  const Anthropic = require$$0$b;
56094
56202
  const mcpController$2 = mcpControllerExports;
56095
- const cliController$1 = cliController_1;
56203
+ const cliController$1 = cliControllerExports;
56096
56204
  const toolDefinitions = toolDefinitions$1;
56097
56205
  const toolHandlers = toolHandlers$1;
56098
56206
  const {
@@ -68067,7 +68175,7 @@ const algoliaController = algoliaController_1;
68067
68175
  const openaiController = openaiController_1;
68068
68176
  const menuItemsController = menuItemsController_1;
68069
68177
  const llmController = llmController_1;
68070
- const cliController = cliController_1;
68178
+ const cliController = cliControllerExports;
68071
68179
  const dashboardConfigController = dashboardConfigController$1;
68072
68180
  const registryAuthController = registryAuthController$2;
68073
68181
  const registryApiController = registryApiController$3;