@wrongstack/cli 0.273.1 → 0.274.0

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/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { color, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, resolveHqDataDir, ensureHqFirstRunAuthFile, DEFAULT_HQ_REDACTION_POLICY, watchHqAuthFile, parseHqFrame, HQ_PROTOCOL_VERSION, parseHqEventPayload, scrubAndTruncateHqPreview, DefaultSecretScrubber, resolveWstackPaths, projectHash, wstackGlobalRoot, TOKENS, ToolRegistry, GlobalMailbox, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, normalizeTokenSavingTier, SlashCommandRegistry, attachDepWatcherBridge, createFallbackModelExtension, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, SddRunRegistry, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, buildTranscriptFromEvents, DEFAULT_CONTEXT_WINDOW_MODE_ID, resolveProjectDir, DefaultLogger, DefaultModelsRegistry, isStdinTTY, atomicWrite, DefaultPathResolver, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, applyToolDescriptionModes, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, createHqPublisherFromEnv, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, Context, QueueStore, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, applyModelRuntime, resolveContextWindowPolicy, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, AdaptiveConcurrencyController, resolveModelMatrix, DEFAULT_SUBAGENT_BASELINE, DefaultTokenCounter, AutoApprovePermissionPolicy, WIDE_SUBAGENT_CAPABILITIES, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, assignNickname, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, CHIMERA_REVIEW_PROMPT, setQueuedMessagesSnapshot, projectSlug, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, repairToolUseAdjacency, mutateTasks, loadTasks, mutatePlan, setPlanItemStatus, getPlanTemplate, emptyPlan, addPlanItem, savePlan, resolveProviderModelList, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore as DefaultSessionStore$1, ProviderRegistry, makeAgentSubagentRunner, NULL_FLEET_BUS, FsError, ERROR_CODES, buildChildEnv, formatContextWindowModeList, getContextWindowMode, AGENT_CATALOG, dispatchAgent, normalizeToolDescriptionMode, setToolDescriptionMode, getToolDescriptionMode, noOpVault, formatTodosList, formatTaskList, formatTaskProgress, formatPlan, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, readHqRuntimeFileSync, resolveHqConfig, MATRIX_PHASE_KEYS, matrixKeyKind, phaseForRole, onResize, InputBuilder, DefaultSecretVault, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, HQ_AUTH_FILE_VERSION, truncate, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, ConfigError, resolveMailboxIdentity, smartDefaultFallbackChain, AutonomousCoordinator, mutateHqAuthFile, mintHqToken, readHqAuthFile, isSecretField as isSecretField$1, startSessionTelemetryBridge } from '@wrongstack/core';
2
+ import { color, CODEX_MODELS, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, resolveHqDataDir, ensureHqFirstRunAuthFile, DEFAULT_HQ_REDACTION_POLICY, watchHqAuthFile, parseHqFrame, HQ_PROTOCOL_VERSION, parseHqEventPayload, scrubAndTruncateHqPreview, DefaultSecretScrubber, resolveWstackPaths, projectHash, wstackGlobalRoot, TOKENS, ToolRegistry, GlobalMailbox, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, normalizeTokenSavingTier, SlashCommandRegistry, attachDepWatcherBridge, createFallbackModelExtension, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, SddRunRegistry, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, buildTranscriptFromEvents, DEFAULT_CONTEXT_WINDOW_MODE_ID, resolveProjectDir, DefaultLogger, DefaultModelsRegistry, isStdinTTY, atomicWrite, DefaultPathResolver, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, applyToolDescriptionModes, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, createHqPublisherFromEnv, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, Context, QueueStore, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, applyModelRuntime, resolveContextWindowPolicy, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, AdaptiveConcurrencyController, resolveModelMatrix, DEFAULT_SUBAGENT_BASELINE, DefaultTokenCounter, AutoApprovePermissionPolicy, WIDE_SUBAGENT_CAPABILITIES, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, assignNickname, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, CHIMERA_REVIEW_PROMPT, setQueuedMessagesSnapshot, projectSlug, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, repairToolUseAdjacency, mutateTasks, loadTasks, mutatePlan, setPlanItemStatus, getPlanTemplate, emptyPlan, addPlanItem, savePlan, resolveProviderModelList, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore as DefaultSessionStore$1, ProviderRegistry, makeAgentSubagentRunner, NULL_FLEET_BUS, FsError, ERROR_CODES, buildChildEnv, formatContextWindowModeList, getContextWindowMode, AGENT_CATALOG, dispatchAgent, normalizeToolDescriptionMode, setToolDescriptionMode, getToolDescriptionMode, noOpVault, formatTodosList, formatTaskList, formatTaskProgress, formatPlan, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, readHqRuntimeFileSync, resolveHqConfig, MATRIX_PHASE_KEYS, matrixKeyKind, phaseForRole, onResize, InputBuilder, DefaultSecretVault, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, HQ_AUTH_FILE_VERSION, truncate, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, ConfigError, resolveMailboxIdentity, smartDefaultFallbackChain, AutonomousCoordinator, mutateHqAuthFile, mintHqToken, readHqAuthFile, isSecretField as isSecretField$1, startSessionTelemetryBridge } from '@wrongstack/core';
3
3
  import * as fs4 from 'fs/promises';
4
+ import { access, readFile } from 'fs/promises';
4
5
  import { decryptConfigSecrets, encryptConfigSecrets, DefaultSecretVault as DefaultSecretVault$1, isSecretField } from '@wrongstack/core/security';
5
6
  import * as path4 from 'path';
6
7
  import { join } from 'path';
@@ -12,14 +13,14 @@ import * as os7 from 'os';
12
13
  import os7__default from 'os';
13
14
  import { WebSocketServer, WebSocket } from 'ws';
14
15
  import { createRequire } from 'module';
15
- import { findFreePort, AutoPhaseWebSocketHandler, WorktreeWebSocketHandler, SpecsWebSocketHandler, SddBoardWebSocketHandler, SddWizardWebSocketHandler, buildSddWizardDeps, generateAuthToken, verifyClient, createEternalSubscription, handleShellOpen, handleGitInfo, handleGitDiff, handleGitChanges, handleSkillsExport, handleSkillsEdit, handleSkillsCreate, handleSkillsUpdate, handleSkillsUninstall, handleSkillsInstall, handleSkillsContent, handleMcpRestart, handleMcpDisable, handleMcpEnable, handleMcpDiscover, handleMcpSleep, handleMcpWake, handleMcpUpdate, handleMcpRemove, handleMcpAdd, handleMcpList, handleMemoryForget, handleMemoryRemember, handleMemoryList, handleCompletionRequest, createToolLspCompletionSource, handleFilesWrite, handleFilesRead, handleFilesTree, handleFilesList, createHttpServer, registerInstance, openBrowser as openBrowser$1, unregisterInstance, estimateTokens as estimateTokens$1, stringifyContent, messagePreview, messageTokens, createCustomModeStore } from '@wrongstack/webui/server';
16
+ import { envFlag, findFreePort, AutoPhaseWebSocketHandler, WorktreeWebSocketHandler, SpecsWebSocketHandler, SddBoardWebSocketHandler, SddWizardWebSocketHandler, buildSddWizardDeps, resolveAuthToken, buildWebUIAccessUrl, verifyClient, createEternalSubscription, handleShellOpen, handleGitInfo, handleGitDiff, handleGitChanges, handleSkillsExport, handleSkillsEdit, handleSkillsCreate, handleSkillsUpdate, handleSkillsUninstall, handleSkillsInstall, handleSkillsContent, handleMcpRestart, handleMcpDisable, handleMcpEnable, handleMcpDiscover, handleMcpSleep, handleMcpWake, handleMcpUpdate, handleMcpRemove, handleMcpAdd, handleMcpList, handleMemoryForget, handleMemoryRemember, handleMemoryList, handleCompletionRequest, createToolLspCompletionSource, handleFilesWrite, handleFilesRead, handleFilesTree, handleFilesList, createHttpServer, registerInstance, openBrowser as openBrowser$1, unregisterInstance, estimateTokens as estimateTokens$1, stringifyContent, messagePreview, messageTokens, createCustomModeStore } from '@wrongstack/webui/server';
16
17
  import { toErrorMessage, readJsonObjectFile, updateJsonObjectFile, setJsonPath, jsonObjectFileExists, removeJsonPath } from '@wrongstack/core/utils';
17
- import { setOAuthTokenPersister, capabilitiesFor, makeProviderFromConfig, buildProviderFactoriesFromRegistry, refreshCopilotToken, copilotBaseUrlFromToken } from '@wrongstack/providers';
18
+ import { setOAuthTokenPersister, capabilitiesFor, makeProviderFromConfig, buildProviderFactoriesFromRegistry, withCatalogCapabilities, refreshCopilotToken, copilotBaseUrlFromToken } from '@wrongstack/providers';
18
19
  import { ensureSessionShell, builtinToolsPack, rememberTool, forgetTool, searchMemoryTool, relatedMemoryTool, configureExecPolicy, runStartupIndex, isIndexableFile, enqueueReindex, cancelPendingReindexes, shutdownCodebaseIndexHost, getProcessRegistry, TIER2_TOOLS, TIER3_TOOLS, TIER1_TOOLS, resetIndexCircuitBreaker } from '@wrongstack/tools';
19
20
  import { DefaultSessionStore } from '@wrongstack/core/storage';
20
21
  import { probeLocalLlm } from '@wrongstack/runtime/probe';
21
- import * as fs3 from 'fs';
22
- import { watch, writeFileSync, existsSync, realpathSync, readFileSync } from 'fs';
22
+ import * as fs37 from 'fs';
23
+ import { watch, writeFileSync, existsSync, realpathSync } from 'fs';
23
24
  import { SkillInstaller } from '@wrongstack/core/skills';
24
25
  import { spawn, execFile, execFileSync } from 'child_process';
25
26
  import { MCPRegistry, MCPServer, serveHttp, serveStdio } from '@wrongstack/mcp';
@@ -166,7 +167,7 @@ function unknownSubcommand(cmd, valid, name) {
166
167
  const list = valid.join(", ");
167
168
  return name ? `Unknown subcommand "${cmd}" for /${name}. Valid: ${list}.` : `Unknown subcommand "${cmd}". Valid: ${list}.`;
168
169
  }
169
- async function pathExists(file) {
170
+ async function pathExists2(file) {
170
171
  try {
171
172
  await fs4.access(file);
172
173
  return true;
@@ -221,10 +222,10 @@ async function detectPackageManager2(root, declared) {
221
222
  const name = declared.split("@")[0];
222
223
  if (name) return name;
223
224
  }
224
- if (await pathExists(path4.join(root, "pnpm-lock.yaml"))) return "pnpm";
225
- if (await pathExists(path4.join(root, "bun.lockb"))) return "bun";
226
- if (await pathExists(path4.join(root, "bun.lock"))) return "bun";
227
- if (await pathExists(path4.join(root, "yarn.lock"))) return "yarn";
225
+ if (await pathExists2(path4.join(root, "pnpm-lock.yaml"))) return "pnpm";
226
+ if (await pathExists2(path4.join(root, "bun.lockb"))) return "bun";
227
+ if (await pathExists2(path4.join(root, "bun.lock"))) return "bun";
228
+ if (await pathExists2(path4.join(root, "yarn.lock"))) return "yarn";
228
229
  return "npm";
229
230
  }
230
231
  function hasUsableScript(scripts, name) {
@@ -299,14 +300,14 @@ async function detectProjectFacts(root) {
299
300
  } catch {
300
301
  }
301
302
  try {
302
- if (!await pathExists(path4.join(root, "pyproject.toml"))) throw new Error("not python");
303
+ if (!await pathExists2(path4.join(root, "pyproject.toml"))) throw new Error("not python");
303
304
  facts.test ??= "pytest";
304
305
  facts.lint ??= "ruff check .";
305
306
  facts.hints.push("pyproject.toml");
306
307
  } catch {
307
308
  }
308
309
  try {
309
- if (!await pathExists(path4.join(root, "go.mod"))) throw new Error("not go");
310
+ if (!await pathExists2(path4.join(root, "go.mod"))) throw new Error("not go");
310
311
  facts.build ??= "go build ./...";
311
312
  facts.test ??= "go test ./...";
312
313
  facts.run ??= "go run .";
@@ -314,7 +315,7 @@ async function detectProjectFacts(root) {
314
315
  } catch {
315
316
  }
316
317
  try {
317
- if (!await pathExists(path4.join(root, "Cargo.toml"))) throw new Error("not rust");
318
+ if (!await pathExists2(path4.join(root, "Cargo.toml"))) throw new Error("not rust");
318
319
  facts.build ??= "cargo build";
319
320
  facts.test ??= "cargo test";
320
321
  facts.lint ??= "cargo clippy";
@@ -342,23 +343,23 @@ async function detectProjectFacts(root) {
342
343
  } catch {
343
344
  }
344
345
  try {
345
- if (!await pathExists(path4.join(root, "pom.xml"))) throw new Error("not maven");
346
+ if (!await pathExists2(path4.join(root, "pom.xml"))) throw new Error("not maven");
346
347
  facts.build ??= "mvn package";
347
348
  facts.test ??= "mvn test";
348
349
  facts.hints.push("pom.xml");
349
350
  } catch {
350
351
  }
351
352
  try {
352
- const hasGradle = await pathExists(path4.join(root, "build.gradle")) || await pathExists(path4.join(root, "build.gradle.kts"));
353
+ const hasGradle = await pathExists2(path4.join(root, "build.gradle")) || await pathExists2(path4.join(root, "build.gradle.kts"));
353
354
  if (!hasGradle) throw new Error("not gradle");
354
- const g = await pathExists(path4.join(root, "gradlew")) ? "./gradlew" : "gradle";
355
+ const g = await pathExists2(path4.join(root, "gradlew")) ? "./gradlew" : "gradle";
355
356
  facts.build ??= `${g} build`;
356
357
  facts.test ??= `${g} test`;
357
358
  facts.hints.push("Gradle");
358
359
  } catch {
359
360
  }
360
361
  try {
361
- const hasDotnet = await pathExists(path4.join(root, "global.json")) || await hasRootFileWithSuffix(root, [".csproj", ".fsproj", ".sln"]);
362
+ const hasDotnet = await pathExists2(path4.join(root, "global.json")) || await hasRootFileWithSuffix(root, [".csproj", ".fsproj", ".sln"]);
362
363
  if (!hasDotnet) throw new Error("not dotnet");
363
364
  facts.build ??= "dotnet build";
364
365
  facts.test ??= "dotnet test";
@@ -367,7 +368,7 @@ async function detectProjectFacts(root) {
367
368
  } catch {
368
369
  }
369
370
  try {
370
- if (!await pathExists(path4.join(root, "mix.exs"))) throw new Error("not elixir");
371
+ if (!await pathExists2(path4.join(root, "mix.exs"))) throw new Error("not elixir");
371
372
  facts.build ??= "mix compile";
372
373
  facts.test ??= "mix test";
373
374
  facts.lint ??= "mix format --check-formatted";
@@ -376,14 +377,14 @@ async function detectProjectFacts(root) {
376
377
  } catch {
377
378
  }
378
379
  try {
379
- if (!await pathExists(path4.join(root, "pubspec.yaml"))) throw new Error("not dart");
380
+ if (!await pathExists2(path4.join(root, "pubspec.yaml"))) throw new Error("not dart");
380
381
  facts.test ??= "dart test";
381
382
  facts.lint ??= "dart analyze";
382
383
  facts.hints.push("pubspec.yaml");
383
384
  } catch {
384
385
  }
385
386
  try {
386
- const hasDeno = await pathExists(path4.join(root, "deno.json")) || await pathExists(path4.join(root, "deno.jsonc"));
387
+ const hasDeno = await pathExists2(path4.join(root, "deno.json")) || await pathExists2(path4.join(root, "deno.jsonc"));
387
388
  if (!hasDeno) throw new Error("not deno");
388
389
  facts.test ??= "deno test";
389
390
  facts.lint ??= "deno lint";
@@ -391,7 +392,7 @@ async function detectProjectFacts(root) {
391
392
  } catch {
392
393
  }
393
394
  try {
394
- if (!await pathExists(path4.join(root, "Package.swift"))) throw new Error("not swift");
395
+ if (!await pathExists2(path4.join(root, "Package.swift"))) throw new Error("not swift");
395
396
  facts.build ??= "swift build";
396
397
  facts.test ??= "swift test";
397
398
  facts.run ??= "swift run";
@@ -399,20 +400,20 @@ async function detectProjectFacts(root) {
399
400
  } catch {
400
401
  }
401
402
  try {
402
- if (!await pathExists(path4.join(root, "Gemfile"))) throw new Error("not ruby");
403
- if (await pathExists(path4.join(root, "Rakefile"))) facts.test ??= "bundle exec rake test";
403
+ if (!await pathExists2(path4.join(root, "Gemfile"))) throw new Error("not ruby");
404
+ if (await pathExists2(path4.join(root, "Rakefile"))) facts.test ??= "bundle exec rake test";
404
405
  facts.hints.push("Gemfile");
405
406
  } catch {
406
407
  }
407
408
  try {
408
- if (!await pathExists(path4.join(root, "CMakeLists.txt"))) throw new Error("not cmake");
409
+ if (!await pathExists2(path4.join(root, "CMakeLists.txt"))) throw new Error("not cmake");
409
410
  facts.build ??= "cmake -B build && cmake --build build";
410
411
  facts.test ??= "ctest --test-dir build";
411
412
  facts.hints.push("CMakeLists.txt");
412
413
  } catch {
413
414
  }
414
415
  try {
415
- const hasPip = await pathExists(path4.join(root, "requirements.txt")) || await pathExists(path4.join(root, "setup.py")) || await pathExists(path4.join(root, "setup.cfg"));
416
+ const hasPip = await pathExists2(path4.join(root, "requirements.txt")) || await pathExists2(path4.join(root, "setup.py")) || await pathExists2(path4.join(root, "setup.cfg"));
416
417
  if (!hasPip) throw new Error("not pip");
417
418
  facts.test ??= "pytest";
418
419
  facts.hints.push("requirements.txt");
@@ -5521,7 +5522,11 @@ function registerWebuiInstance(p, deps = {}) {
5521
5522
  projectRoot: p.projectRoot,
5522
5523
  projectName: path4.basename(p.projectRoot) || p.projectRoot,
5523
5524
  startedAt: p.startedAt,
5524
- url: `http://${p.host}:${p.httpPort}`
5525
+ url: buildWebUIAccessUrl({
5526
+ host: p.host,
5527
+ port: p.httpPort,
5528
+ publicUrl: p.publicUrl
5529
+ })
5525
5530
  },
5526
5531
  p.registryBaseDir
5527
5532
  ).catch(() => {
@@ -5530,7 +5535,12 @@ function registerWebuiInstance(p, deps = {}) {
5530
5535
  function announceWebuiReady(p) {
5531
5536
  const log = p.log ?? ((m) => console.log(m));
5532
5537
  const launch = p.openBrowserFn ?? openBrowser$1;
5533
- const openUrl = p.wsToken ? `http://${p.host}:${p.httpPort}?token=${encodeURIComponent(p.wsToken)}` : `http://${p.host}:${p.httpPort}`;
5538
+ const openUrl = buildWebUIAccessUrl({
5539
+ host: p.host,
5540
+ port: p.httpPort,
5541
+ token: p.wsToken,
5542
+ publicUrl: p.publicUrl
5543
+ });
5534
5544
  p.server.on("listening", () => {
5535
5545
  log(
5536
5546
  `
@@ -5667,7 +5677,9 @@ function startStaticServe(opts, deps = {}) {
5667
5677
  wsPort: opts.wsPort,
5668
5678
  globalRoot: opts.globalRoot,
5669
5679
  onFleetPing: opts.onFleetPing,
5670
- apiToken: opts.apiToken
5680
+ publicWsUrl: opts.publicWsUrl,
5681
+ apiToken: opts.apiToken,
5682
+ requireToken: opts.requireToken
5671
5683
  });
5672
5684
  server.listen(opts.httpPort, opts.host);
5673
5685
  return { server, port: opts.httpPort };
@@ -7298,7 +7310,10 @@ __export(webui_server_exports, {
7298
7310
  runWebUI: () => runWebUI
7299
7311
  });
7300
7312
  async function runWebUI(opts) {
7301
- const host = "127.0.0.1";
7313
+ const host = opts.host ?? process.env["WEBUI_HOST"] ?? process.env["WS_HOST"] ?? "127.0.0.1";
7314
+ const publicUrl = opts.publicUrl ?? process.env["WEBUI_PUBLIC_URL"];
7315
+ const publicWsUrl = opts.publicWsUrl ?? process.env["WEBUI_PUBLIC_WS_URL"];
7316
+ const requireToken = opts.requireToken ?? envFlag("WEBUI_REQUIRE_TOKEN");
7302
7317
  const requestedWsPort = opts.port ?? 3457;
7303
7318
  const requestedHttpPort = opts.httpPort ?? 3456;
7304
7319
  const strictPort = process.env["WEBUI_STRICT_PORT"] === "1" || process.env["WEBUI_STRICT_PORT"] === "true";
@@ -7472,7 +7487,21 @@ async function runWebUI(opts) {
7472
7487
  webuiHqConnection = void 0;
7473
7488
  };
7474
7489
  registerWebuiClient();
7475
- const wsToken = generateAuthToken();
7490
+ const wsToken = resolveAuthToken(opts.accessToken);
7491
+ const publicHostnames = [publicUrl, publicWsUrl].map((value) => {
7492
+ if (!value) return void 0;
7493
+ try {
7494
+ return new URL(value).hostname;
7495
+ } catch {
7496
+ return void 0;
7497
+ }
7498
+ }).filter((value) => Boolean(value));
7499
+ const accessUrl = buildWebUIAccessUrl({
7500
+ host,
7501
+ port: httpPort,
7502
+ token: wsToken,
7503
+ publicUrl
7504
+ });
7476
7505
  const wss = new WebSocketServer({ port, host, maxPayload: 1 * 1024 * 1024 });
7477
7506
  console.log(`[WebUI] WebSocket server starting on ws://${host}:${port}`);
7478
7507
  let fleetBroadcastCli = null;
@@ -7484,7 +7513,9 @@ async function runWebUI(opts) {
7484
7513
  onFleetPing: () => {
7485
7514
  void fleetBroadcastCli?.();
7486
7515
  },
7487
- apiToken: wsToken
7516
+ publicWsUrl,
7517
+ apiToken: wsToken,
7518
+ requireToken
7488
7519
  });
7489
7520
  if (httpServer) {
7490
7521
  announceWebuiReady({
@@ -7493,7 +7524,8 @@ async function runWebUI(opts) {
7493
7524
  httpPort,
7494
7525
  wsPort,
7495
7526
  open: !!opts.open,
7496
- wsToken
7527
+ wsToken,
7528
+ publicUrl
7497
7529
  });
7498
7530
  } else {
7499
7531
  console.warn(
@@ -7507,6 +7539,7 @@ async function runWebUI(opts) {
7507
7539
  host,
7508
7540
  httpPort,
7509
7541
  wsPort,
7542
+ publicUrl,
7510
7543
  projectRoot: opts.projectRoot,
7511
7544
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
7512
7545
  registryBaseDir
@@ -8278,7 +8311,7 @@ ${text}` : text;
8278
8311
  wss.on("listening", () => {
8279
8312
  console.log(`[WebUI] WebSocket server running on ws://${host}:${port}`);
8280
8313
  setupEvents();
8281
- opts.onListening?.({ httpPort, wsPort, host });
8314
+ opts.onListening?.({ httpPort, wsPort, host, url: accessUrl });
8282
8315
  const globalRoot = opts.globalConfigPath ? path4.dirname(opts.globalConfigPath) : void 0;
8283
8316
  if (globalRoot) {
8284
8317
  const broadcastSessions = async () => {
@@ -8348,7 +8381,10 @@ ${text}` : text;
8348
8381
  remoteAddress: req2.socket.remoteAddress,
8349
8382
  cookieHeader: req2.headers.cookie,
8350
8383
  wsHost: host,
8351
- expectedToken: wsToken
8384
+ expectedToken: wsToken,
8385
+ requireToken,
8386
+ allowedHostnames: publicHostnames,
8387
+ allowBrowserUrlToken: Boolean(publicWsUrl)
8352
8388
  });
8353
8389
  if (!ok) {
8354
8390
  ws.close(4003, "Forbidden");
@@ -8794,6 +8830,14 @@ var init_webui_server = __esm({
8794
8830
 
8795
8831
  // src/cli-main.ts
8796
8832
  init_provider_config_utils();
8833
+ async function pathExists(p) {
8834
+ try {
8835
+ await access(p);
8836
+ return true;
8837
+ } catch {
8838
+ return false;
8839
+ }
8840
+ }
8797
8841
  var DEFAULT_TASK_CONCURRENCY = 2;
8798
8842
  function resolveTaskConcurrency() {
8799
8843
  const raw = Number.parseInt(process.env["WRONGSTACK_AUTOPHASE_TASK_CONCURRENCY"] ?? "", 10);
@@ -8909,15 +8953,21 @@ function runCmd(cmd, args, cwd, shell = false) {
8909
8953
  });
8910
8954
  });
8911
8955
  }
8912
- function detectPackageManager(root) {
8913
- if (existsSync(join(root, "pnpm-lock.yaml"))) return "pnpm";
8914
- if (existsSync(join(root, "yarn.lock"))) return "yarn";
8915
- if (existsSync(join(root, "bun.lockb"))) return "bun";
8956
+ async function detectPackageManager(root) {
8957
+ const [pnpm, yarn, bun] = await Promise.all([
8958
+ pathExists(join(root, "pnpm-lock.yaml")),
8959
+ pathExists(join(root, "yarn.lock")),
8960
+ pathExists(join(root, "bun.lockb"))
8961
+ ]);
8962
+ if (pnpm) return "pnpm";
8963
+ if (yarn) return "yarn";
8964
+ if (bun) return "bun";
8916
8965
  return "npm";
8917
8966
  }
8918
- function readScripts(cwd) {
8967
+ async function readScripts(cwd) {
8919
8968
  try {
8920
- const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
8969
+ const raw = await readFile(join(cwd, "package.json"), "utf8");
8970
+ const pkg = JSON.parse(raw);
8921
8971
  return pkg.scripts ?? {};
8922
8972
  } catch {
8923
8973
  return {};
@@ -8993,7 +9043,7 @@ function createAutoPhaseHost(deps) {
8993
9043
  ].join("\n");
8994
9044
  }
8995
9045
  async function runVerify(cwd) {
8996
- if (!existsSync(join(cwd, "node_modules")) && !existsSync(join(deps.projectRoot, "node_modules"))) {
9046
+ if (!await pathExists(join(cwd, "node_modules")) && !await pathExists(join(deps.projectRoot, "node_modules"))) {
8997
9047
  return { ok: true, output: "verify skipped: node_modules not found" };
8998
9048
  }
8999
9049
  const custom = process.env["WRONGSTACK_AUTOPHASE_VERIFY_CMD"]?.trim();
@@ -9002,8 +9052,8 @@ function createAutoPhaseHost(deps) {
9002
9052
  return res.code === 0 ? { ok: true } : { ok: false, output: `[verify] exited ${res.code}
9003
9053
  ${res.out}` };
9004
9054
  }
9005
- const pm = detectPackageManager(deps.projectRoot);
9006
- const scripts = readScripts(cwd);
9055
+ const pm = await detectPackageManager(deps.projectRoot);
9056
+ const scripts = await readScripts(cwd);
9007
9057
  const steps = ["typecheck", "lint"].filter((s) => typeof scripts[s] === "string");
9008
9058
  if (steps.length === 0) return { ok: true, output: "verify skipped: no typecheck/lint script" };
9009
9059
  for (const step of steps) {
@@ -9283,6 +9333,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
9283
9333
  "metrics",
9284
9334
  "webui",
9285
9335
  "open",
9336
+ "webui-require-token",
9337
+ "require-token",
9286
9338
  "no-check",
9287
9339
  "no-models-refresh",
9288
9340
  "director",
@@ -9751,27 +9803,28 @@ function shouldSuppress(flags) {
9751
9803
  return false;
9752
9804
  }
9753
9805
  var wrap = (n) => (n % GROUPS.length + GROUPS.length) % GROUPS.length;
9754
- function pickGroupIndex(opts) {
9806
+ async function pickGroupIndex(opts) {
9755
9807
  if (typeof opts.groupIndex === "number") return wrap(opts.groupIndex);
9756
9808
  if (opts.cursorFile) {
9757
9809
  try {
9758
9810
  let current = 0;
9759
9811
  try {
9760
- const parsed = Number.parseInt(fs3.readFileSync(opts.cursorFile, "utf8").trim(), 10);
9812
+ const raw = await fs4.readFile(opts.cursorFile, "utf8");
9813
+ const parsed = Number.parseInt(raw.trim(), 10);
9761
9814
  if (Number.isFinite(parsed)) current = wrap(parsed);
9762
9815
  } catch {
9763
9816
  }
9764
- fs3.mkdirSync(path4.dirname(opts.cursorFile), { recursive: true });
9765
- fs3.writeFileSync(opts.cursorFile, String(wrap(current + 1)));
9817
+ await fs4.mkdir(path4.dirname(opts.cursorFile), { recursive: true });
9818
+ await fs4.writeFile(opts.cursorFile, String(wrap(current + 1)));
9766
9819
  return current;
9767
9820
  } catch {
9768
9821
  }
9769
9822
  }
9770
9823
  return Math.floor(Math.random() * GROUPS.length);
9771
9824
  }
9772
- function printLaunchHints(renderer, flags, opts = {}) {
9825
+ async function printLaunchHints(renderer, flags, opts = {}) {
9773
9826
  if (shouldSuppress(flags)) return;
9774
- const idx = pickGroupIndex(opts);
9827
+ const idx = await pickGroupIndex(opts);
9775
9828
  const group = GROUPS[idx];
9776
9829
  if (!group) return;
9777
9830
  const lines = [];
@@ -21861,12 +21914,9 @@ function extractAccountId(token) {
21861
21914
  const id = auth?.chatgpt_account_id;
21862
21915
  return typeof id === "string" && id.length > 0 ? id : null;
21863
21916
  }
21864
- var FALLBACK_CODEX_MODELS = [
21865
- { id: "gpt-5.5", name: "gpt-5.5" },
21866
- { id: "gpt-5.4", name: "gpt-5.4" },
21867
- { id: "gpt-5.4-mini", name: "gpt-5.4-mini" },
21868
- { id: "gpt-5.3-codex-spark", name: "gpt-5.3-codex-spark" }
21869
- ];
21917
+ var FALLBACK_CODEX_MODELS = CODEX_MODELS.map(
21918
+ (m) => ({ id: m.id, name: m.name })
21919
+ );
21870
21920
  var CODEX_CATALOG_FAMILIES = /* @__PURE__ */ new Set(["gpt-codex", "gpt-codex-spark"]);
21871
21921
  function fallbackCodexModelIds() {
21872
21922
  return FALLBACK_CODEX_MODELS.map((m) => m.id);
@@ -22274,6 +22324,15 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
22274
22324
  } catch (err) {
22275
22325
  throw new Error(`Failed to create provider: ${err instanceof Error ? err.message : err}`);
22276
22326
  }
22327
+ if (config.features.modelsRegistry) {
22328
+ provider = await withCatalogCapabilities(
22329
+ modelsRegistry,
22330
+ config.provider,
22331
+ provider,
22332
+ { ...providerConfig, type: config.provider, model: config.model },
22333
+ logger
22334
+ );
22335
+ }
22277
22336
  return { resolvedProvider, provider, providerRegistry };
22278
22337
  }
22279
22338
 
@@ -27124,37 +27183,38 @@ async function listFleetRuns(deps) {
27124
27183
  return 1;
27125
27184
  }
27126
27185
  const runs = [];
27127
- for (const id of entries) {
27128
- const runDir = path4.join(deps.paths.projectSessions, id);
27129
- let stat8;
27130
- try {
27131
- stat8 = await fs4.stat(runDir);
27132
- } catch {
27133
- continue;
27134
- }
27135
- if (!stat8.isDirectory()) continue;
27136
- let manifest = false;
27137
- let checkpoint = false;
27138
- let subagentCount = 0;
27139
- let subagentsDir;
27140
- try {
27141
- await fs4.access(path4.join(runDir, "fleet.json"));
27142
- manifest = true;
27143
- } catch {
27144
- }
27145
- try {
27146
- await fs4.access(path4.join(runDir, "checkpoint.json"));
27147
- checkpoint = true;
27148
- } catch {
27149
- }
27150
- try {
27151
- subagentsDir = path4.join(runDir, "subagents");
27152
- const files = await fs4.readdir(subagentsDir);
27153
- subagentCount = files.filter((f) => f.endsWith(".jsonl")).length;
27154
- } catch {
27155
- }
27156
- runs.push({ id, manifest, checkpoint, subagents: subagentCount });
27157
- }
27186
+ const perEntry = await Promise.all(
27187
+ entries.map(async (id) => {
27188
+ const runDir = path4.join(deps.paths.projectSessions, id);
27189
+ try {
27190
+ const stat8 = await fs4.stat(runDir);
27191
+ if (!stat8.isDirectory()) return null;
27192
+ } catch {
27193
+ return null;
27194
+ }
27195
+ const [manifestResult, checkpointResult, subagentFilesResult] = await Promise.all([
27196
+ fs4.access(path4.join(runDir, "fleet.json")).then(
27197
+ () => true,
27198
+ () => false
27199
+ ),
27200
+ fs4.access(path4.join(runDir, "checkpoint.json")).then(
27201
+ () => true,
27202
+ () => false
27203
+ ),
27204
+ fs4.readdir(path4.join(runDir, "subagents")).then(
27205
+ (files) => files.filter((f) => f.endsWith(".jsonl")).length,
27206
+ () => 0
27207
+ )
27208
+ ]);
27209
+ return {
27210
+ id,
27211
+ manifest: manifestResult,
27212
+ checkpoint: checkpointResult,
27213
+ subagents: subagentFilesResult
27214
+ };
27215
+ })
27216
+ );
27217
+ for (const r of perEntry) if (r) runs.push(r);
27158
27218
  if (runs.length === 0) {
27159
27219
  deps.renderer.write("No fleet runs found.\n");
27160
27220
  return 0;
@@ -27268,17 +27328,22 @@ Fleet Run: ${runId}
27268
27328
  deps.renderer.write(`
27269
27329
  Subagent transcripts (${subagentFiles.length}):
27270
27330
  `);
27271
- for (const f of subagentFiles.sort()) {
27272
- const filePath = path4.join(subagentsDir, f);
27273
- let size;
27274
- try {
27275
- const s = await fs4.stat(filePath);
27276
- size = s.size;
27277
- } catch {
27278
- size = 0;
27279
- }
27331
+ const sortedFiles = subagentFiles.sort();
27332
+ const sizes = await Promise.all(
27333
+ sortedFiles.map(async (f) => {
27334
+ const filePath = path4.join(subagentsDir, f);
27335
+ try {
27336
+ const s = await fs4.stat(filePath);
27337
+ return s.size;
27338
+ } catch {
27339
+ return 0;
27340
+ }
27341
+ })
27342
+ );
27343
+ for (let i = 0; i < sortedFiles.length; i++) {
27344
+ const size = sizes[i] ?? 0;
27280
27345
  const sizeStr = size > 1024 * 1024 ? `${(size / 1024 / 1024).toFixed(1)}MB` : `${(size / 1024).toFixed(0)}KB`;
27281
- deps.renderer.write(` ${color.dim(f)} ${color.dim(sizeStr)}
27346
+ deps.renderer.write(` ${color.dim(sortedFiles[i] ?? "")} ${color.dim(sizeStr)}
27282
27347
  `);
27283
27348
  }
27284
27349
  } else {
@@ -27682,9 +27747,10 @@ var helpCmd = async (_args, deps) => {
27682
27747
  " --yolo-destructive Deprecated \u2014 YOLO now auto-approves everything by default",
27683
27748
  " --tui / --no-tui Force or disable TUI mode",
27684
27749
  " --mouse Full mouse mode in the TUI (in-app scroll + clickable UI)",
27685
- " --webui [--port <n>] [--open] Serve the browser UI + WS bridge (prints a URL,",
27686
- " --open pops the browser; shares this terminal's",
27687
- " agent; auto-picks free ports)",
27750
+ " --webui [--host <h>] [--webui-port <n>] [--ws-port <n>] [--webui-token <t>] [--open]",
27751
+ " [--webui-public-url <url>] [--webui-public-ws-url <url>] [--webui-require-token]",
27752
+ " Serve the browser UI + WS bridge (prints a token URL,",
27753
+ " --open pops the browser; shares this terminal's agent)",
27688
27754
  ' --eternal "<mission>" Start an eternal-autonomy loop',
27689
27755
  " --no-hints Hide launch hints",
27690
27756
  " --skip-index Skip codebase indexing and the large-codebase prompt"
@@ -28039,7 +28105,7 @@ async function boot(argv) {
28039
28105
  await persistLaunchChoices(wpaths.globalConfig, toPersist);
28040
28106
  } catch {
28041
28107
  }
28042
- printLaunchHints(renderer, flags, {
28108
+ await printLaunchHints(renderer, flags, {
28043
28109
  cursorFile: path4.join(wpaths.cacheDir, "hint-cursor")
28044
28110
  });
28045
28111
  } else {
@@ -28785,11 +28851,79 @@ async function runWebUIDispatch(ctx) {
28785
28851
  agent.disableInteractiveConfirmation();
28786
28852
  renderer.setSilent(true);
28787
28853
  const { runWebUI: runWebUI2 } = await Promise.resolve().then(() => (init_webui_server(), webui_server_exports));
28854
+ const flagValue = (names) => {
28855
+ for (const name of names) {
28856
+ if (!Object.prototype.hasOwnProperty.call(flags, name)) continue;
28857
+ const value = flags[name];
28858
+ if (typeof value === "string" && value.trim() !== "") return value.trim();
28859
+ throw new Error(`--${name} requires a value`);
28860
+ }
28861
+ return void 0;
28862
+ };
28863
+ const flagBoolean = (names) => {
28864
+ for (const name of names) {
28865
+ if (!Object.prototype.hasOwnProperty.call(flags, name)) continue;
28866
+ const value = flags[name];
28867
+ if (value === void 0) continue;
28868
+ if (typeof value === "boolean") return value;
28869
+ const normalized = value.trim().toLowerCase();
28870
+ if (["1", "true", "yes", "on"].includes(normalized)) return true;
28871
+ if (["0", "false", "no", "off"].includes(normalized)) return false;
28872
+ throw new Error(`--${name} must be a boolean value`);
28873
+ }
28874
+ return void 0;
28875
+ };
28876
+ const envFlag2 = (name) => {
28877
+ const value = process.env[name]?.trim().toLowerCase();
28878
+ return value === "1" || value === "true" || value === "yes" || value === "on";
28879
+ };
28880
+ const parsePort = (value, fallback, label) => {
28881
+ if (value === void 0) return fallback;
28882
+ const parsed = Number.parseInt(value, 10);
28883
+ if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
28884
+ throw new Error(`${label} must be a port between 1 and 65535`);
28885
+ }
28886
+ return parsed;
28887
+ };
28888
+ let webuiHost;
28889
+ let webuiHttpPort;
28890
+ let webuiWsPort;
28891
+ let webuiAccessToken;
28892
+ let webuiPublicUrl;
28893
+ let webuiPublicWsUrl;
28894
+ let webuiRequireToken;
28895
+ try {
28896
+ webuiHost = flagValue(["webui-host", "host"]) ?? process.env["WEBUI_HOST"] ?? process.env["WS_HOST"] ?? "127.0.0.1";
28897
+ webuiHttpPort = parsePort(
28898
+ flagValue(["webui-port", "http-port"]) ?? process.env["WEBUI_PORT"] ?? process.env["PORT"],
28899
+ 3456,
28900
+ "--webui-port"
28901
+ );
28902
+ webuiWsPort = parsePort(
28903
+ flagValue(["ws-port"]) ?? flagValue(["port"]) ?? process.env["WS_PORT"],
28904
+ 3457,
28905
+ "--ws-port"
28906
+ );
28907
+ webuiAccessToken = flagValue(["webui-token"]) ?? process.env["WEBUI_TOKEN"] ?? process.env["WEBUI_AUTH_TOKEN"];
28908
+ webuiPublicUrl = flagValue(["webui-public-url", "public-url"]) ?? process.env["WEBUI_PUBLIC_URL"];
28909
+ webuiPublicWsUrl = flagValue(["webui-public-ws-url", "public-ws-url"]) ?? process.env["WEBUI_PUBLIC_WS_URL"];
28910
+ webuiRequireToken = flagBoolean(["webui-require-token", "require-token"]) ?? envFlag2("WEBUI_REQUIRE_TOKEN");
28911
+ } catch (err) {
28912
+ renderer.setSilent(false);
28913
+ renderer.writeInfo(color.red(` ${err instanceof Error ? err.message : String(err)}`));
28914
+ return 1;
28915
+ }
28788
28916
  const webuiPromise = runWebUI2({
28789
28917
  agent,
28790
28918
  events,
28791
28919
  session,
28792
- port: Number.parseInt(String(flags.port ?? "3457"), 10),
28920
+ host: webuiHost,
28921
+ port: webuiWsPort,
28922
+ httpPort: webuiHttpPort,
28923
+ accessToken: webuiAccessToken,
28924
+ publicUrl: webuiPublicUrl,
28925
+ publicWsUrl: webuiPublicWsUrl,
28926
+ requireToken: webuiRequireToken,
28793
28927
  projectRoot,
28794
28928
  appConfig: config,
28795
28929
  open: !!flags.open,
@@ -28813,16 +28947,13 @@ async function runWebUIDispatch(ctx) {
28813
28947
  needsSetup,
28814
28948
  sddSubagentFactory,
28815
28949
  // Print the "open this" banner only once the server is actually
28816
- // listening, using the RESOLVED ports. The requested port
28817
- // (flags.port) auto-advances past busy ports inside runWebUI, so a
28818
- // banner printed up-front with flags.port lies whenever 3456/3457 are
28819
- // taken (a second instance, leftover sockets). Bind is 127.0.0.1-only,
28820
- // so the host must be the literal IPv4 loopback — `localhost` resolves
28821
- // to `::1` first on Windows and never reaches the server.
28822
- onListening: ({ httpPort: boundHttpPort }) => {
28950
+ // listening, using the RESOLVED ports. Requested ports auto-advance past
28951
+ // busy ports inside runWebUI, so a banner printed up-front lies whenever
28952
+ // 3456/3457 are taken (a second instance, leftover sockets).
28953
+ onListening: ({ url }) => {
28823
28954
  renderer.writeInfo(
28824
28955
  color.green(
28825
- ` \u2726 WebUI running \u2192 ${color.bold(`http://127.0.0.1:${boundHttpPort}`)}`
28956
+ ` \u2726 WebUI running \u2192 ${color.bold(url)}`
28826
28957
  )
28827
28958
  );
28828
28959
  renderer.writeInfo(
@@ -32989,7 +33120,7 @@ async function setupCodebaseIndexing(deps) {
32989
33120
  let watcher;
32990
33121
  if (idx.watchExternal) {
32991
33122
  try {
32992
- watcher = fs3.watch(projectRoot, { recursive: true }, (_event, filename) => {
33123
+ watcher = fs37.watch(projectRoot, { recursive: true }, (_event, filename) => {
32993
33124
  if (!filename) return;
32994
33125
  const rel = filename.toString();
32995
33126
  if (isIgnored(rel)) return;
@@ -34248,6 +34379,7 @@ async function main(argv) {
34248
34379
  config = patchConfig(config, { provider: providerId, model: modelId });
34249
34380
  configStore.update({ provider: providerId, model: modelId });
34250
34381
  await refreshMaxContextFor(providerId, modelId);
34382
+ await refreshActiveReasoningConfig(providerId, modelId);
34251
34383
  return null;
34252
34384
  } catch (err) {
34253
34385
  return err instanceof Error ? err.message : String(err);
@@ -34941,29 +35073,36 @@ ${color.dim("\u2500".repeat(40))}` : "";
34941
35073
  return "No fleet transcripts on disk \u2014 no subagents have been spawned for this session.";
34942
35074
  }
34943
35075
  const found = [];
34944
- for (const runId of runDirs) {
34945
- const runDir = path4.join(subagentsRoot, runId);
34946
- let files;
34947
- try {
34948
- files = await fs4.readdir(runDir);
34949
- } catch {
34950
- continue;
34951
- }
34952
- for (const f of files) {
34953
- if (!f.endsWith(".jsonl")) continue;
34954
- const full = path4.join(runDir, f);
35076
+ const runResults = await Promise.all(
35077
+ runDirs.map(async (runId) => {
35078
+ const runDir = path4.join(subagentsRoot, runId);
35079
+ let files;
34955
35080
  try {
34956
- const stat8 = await fs4.stat(full);
34957
- found.push({
34958
- runId,
34959
- subagentId: f.replace(/\.jsonl$/, ""),
34960
- file: full,
34961
- size: stat8.size
34962
- });
35081
+ files = await fs4.readdir(runDir);
34963
35082
  } catch {
35083
+ return [];
34964
35084
  }
34965
- }
34966
- }
35085
+ const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
35086
+ const fileResults = await Promise.all(
35087
+ jsonlFiles.map(async (f) => {
35088
+ const full = path4.join(runDir, f);
35089
+ try {
35090
+ const stat8 = await fs4.stat(full);
35091
+ return {
35092
+ runId,
35093
+ subagentId: f.replace(/\.jsonl$/, ""),
35094
+ file: full,
35095
+ size: stat8.size
35096
+ };
35097
+ } catch {
35098
+ return null;
35099
+ }
35100
+ })
35101
+ );
35102
+ return fileResults.filter((r) => r !== null);
35103
+ })
35104
+ );
35105
+ for (const runResult of runResults) found.push(...runResult);
34967
35106
  if (found.length === 0) {
34968
35107
  return "No subagent transcripts found on disk.";
34969
35108
  }
@@ -35355,12 +35494,28 @@ Restart WrongStack to load or unload plugin code in this session.`;
35355
35494
  const core = await import('@wrongstack/core');
35356
35495
  let worktrees;
35357
35496
  if (process.env["WRONGSTACK_SDD_WORKTREES"] !== "0") {
35358
- const { spawnSync } = await import('child_process');
35359
- const inGit = spawnSync("git", ["rev-parse", "--is-inside-work-tree"], {
35360
- cwd: projectRoot,
35361
- encoding: "utf8",
35362
- windowsHide: true
35363
- }).stdout?.trim() === "true";
35497
+ const inGit = await new Promise((resolve13) => {
35498
+ let resolved = false;
35499
+ const settle = (v) => {
35500
+ if (resolved) return;
35501
+ resolved = true;
35502
+ resolve13(v);
35503
+ };
35504
+ try {
35505
+ const child = spawn("git", ["rev-parse", "--is-inside-work-tree"], {
35506
+ cwd: projectRoot,
35507
+ env: process.env,
35508
+ stdio: ["ignore", "pipe", "pipe"],
35509
+ windowsHide: true
35510
+ });
35511
+ const chunks = [];
35512
+ child.stdout?.on("data", (c) => chunks.push(c.toString()));
35513
+ child.on("error", () => settle(false));
35514
+ child.on("close", (code) => settle(code === 0 && chunks.join("").trim() === "true"));
35515
+ } catch {
35516
+ settle(false);
35517
+ }
35518
+ });
35364
35519
  if (inGit) worktrees = new core.WorktreeManager({ projectRoot, events });
35365
35520
  }
35366
35521
  const boardStore = new core.SddBoardStore({ baseDir: wpaths.projectSddBoards });