@wrongstack/cli 0.302.2 → 0.305.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.
Files changed (35) hide show
  1. package/dist/{acp-XCE4JJF3.js → acp-5ZLGFHWP.js} +5 -2
  2. package/dist/boot/system-prompt-builder.d.ts +24 -1
  3. package/dist/boot/tui-project-switch.d.ts +1 -1
  4. package/dist/boot/tui-runtime-state.d.ts +14 -0
  5. package/dist/boot/tui-theme-adapter.d.ts +33 -0
  6. package/dist/{chunk-BXQG3H2Y.js → chunk-53STH6CX.js} +87 -13
  7. package/dist/{chunk-JW75HY4F.js → chunk-FKWHSFX4.js} +33 -4
  8. package/dist/{chunk-WOXUE2CN.js → chunk-RHLQT3EV.js} +12 -2
  9. package/dist/{cli-main-PJMDZHDK.js → cli-main-W3T56YCY.js} +1498 -891
  10. package/dist/execute-deps.d.ts +16 -0
  11. package/dist/{execution-LZRYRGDD.js → execution-YMBD7YWC.js} +149 -37
  12. package/dist/fleet/host-context.d.ts +11 -0
  13. package/dist/fleet/host-learning-tracker.d.ts +2 -2
  14. package/dist/fleet/host-learning.d.ts +12 -1
  15. package/dist/fleet/host-session-writer.d.ts +33 -0
  16. package/dist/fleet/host-subagent-factory.d.ts +1 -1
  17. package/dist/fleet/host.d.ts +32 -0
  18. package/dist/{hq-AHPAE7EN.js → hq-5G6D5YIU.js} +2 -2
  19. package/dist/{hq-server-KU2Z54QU.js → hq-server-IVZLVCFV.js} +2 -2
  20. package/dist/index.js +15 -58
  21. package/dist/{mailbox-serve-DBOOOOWZ.js → mailbox-serve-V3QIJBDH.js} +2 -2
  22. package/dist/{mcp-E5EBGOHF.js → mcp-MSARYOPN.js} +5 -2
  23. package/dist/replay-XU6ZD7GM.js +34 -0
  24. package/dist/slash-commands/agent-improve.d.ts +4 -1
  25. package/dist/slash-commands/memory-triage.d.ts +4 -17
  26. package/dist/slash-commands/theme.d.ts +6 -0
  27. package/dist/subcommands/handlers/update.d.ts +2 -0
  28. package/dist/{update-LTDJGAFU.js → update-VZSOZGYC.js} +2 -2
  29. package/dist/{webui-server-E3OPEKU5.js → webui-server-FTG7JAZU.js} +5 -5
  30. package/dist/wiring/domain-glossary.d.ts +58 -0
  31. package/dist/wiring/domain-terms-mirror.d.ts +52 -0
  32. package/dist/wiring/pipeline.d.ts +2 -0
  33. package/dist/wiring/replay.d.ts +11 -1
  34. package/dist/wiring/sage.d.ts +4 -16
  35. package/package.json +23 -23
@@ -29,7 +29,7 @@ import {
29
29
  } from "./chunk-V3XH6XBJ.js";
30
30
  import {
31
31
  startCliHqConnection
32
- } from "./chunk-JW75HY4F.js";
32
+ } from "./chunk-FKWHSFX4.js";
33
33
  import {
34
34
  advanceToNextTask,
35
35
  findSpec,
@@ -117,7 +117,7 @@ import { gatedEnhancerReasoning } from "@wrongstack/core/execution";
117
117
  import { TOKENS as TOKENS9 } from "@wrongstack/core/kernel";
118
118
  import { ToolRegistry as ToolRegistry2 } from "@wrongstack/core/registry";
119
119
  import { getSessionRegistry } from "@wrongstack/core/storage";
120
- import { startSharedHeapWatchdog, writeErr as writeErr2 } from "@wrongstack/core/utils";
120
+ import { startSharedHeapWatchdog, writeErr as writeErr3 } from "@wrongstack/core/utils";
121
121
 
122
122
  // src/auth-menu/panel-service.ts
123
123
  var ANSI_RE = /\u001b\[[0-9;]*m/g;
@@ -900,6 +900,12 @@ function bindSystemPromptBuilder(deps) {
900
900
  // full-shape interfaces; the helper just needs the
901
901
  // passthrough.
902
902
  memoryStore: deps.memoryStore,
903
+ // Thread the narrow domain-term adapter so the builder emits a
904
+ // compact `[Project Jargon Dictionary]` block. `deps.domainGlossary`
905
+ // is an optional closure over the resolved SAGE `memoryStore`
906
+ // that returns only entries tagged `domain-term`; when omitted
907
+ // (e.g. in tests or subagent prompts) the builder emits no block.
908
+ domainGlossary: deps.domainGlossary,
903
909
  // SAGE's turn middleware is the single memory-injection channel.
904
910
  // Disable the builder's static "# Relevant Memory" section so memories
905
911
  // are injected once, per-turn, relevance-scored — not duplicated here.
@@ -2880,7 +2886,12 @@ function subscribeBrainDecisionLog(events) {
2880
2886
 
2881
2887
  // src/fleet/host.ts
2882
2888
  import { randomUUID as randomUUID4 } from "node:crypto";
2883
- import { createProjectAgentRoster } from "@wrongstack/core/agent-catalog";
2889
+ import {
2890
+ createProjectAgentRoster,
2891
+ LearningOptimizationScheduler,
2892
+ listProjectAgentRoles,
2893
+ resolveAutoOptimizePolicy
2894
+ } from "@wrongstack/core/agent-catalog";
2884
2895
  import {
2885
2896
  AdaptiveConcurrencyController,
2886
2897
  DEFAULT_MAX_FLEET_SPAWNS as DEFAULT_MAX_FLEET_SPAWNS2,
@@ -2889,7 +2900,8 @@ import {
2889
2900
  HARD_MAX_SPAWN_DEPTH as HARD_MAX_SPAWN_DEPTH2,
2890
2901
  makeDirectorSessionFactory,
2891
2902
  makeFleetEmitTool,
2892
- resolveProjectDir
2903
+ resolveProjectDir,
2904
+ resolveSubagentModelTarget as resolveSubagentModelTarget2
2893
2905
  } from "@wrongstack/core/coordination";
2894
2906
  import { TOKENS as TOKENS5 } from "@wrongstack/core/kernel";
2895
2907
  import { ToolRegistry } from "@wrongstack/core/registry";
@@ -2898,6 +2910,38 @@ import {
2898
2910
  } from "@wrongstack/core/types";
2899
2911
  import { wstackGlobalRoot } from "@wrongstack/core/utils";
2900
2912
 
2913
+ // src/services/dispatch-classifier.ts
2914
+ import { makeLLMClassifier } from "@wrongstack/core/coordination";
2915
+ function makeProviderClassifier(provider, model) {
2916
+ return makeLLMClassifier(async (prompt) => {
2917
+ const ctrl = new AbortController();
2918
+ const timeout = setTimeout(() => ctrl.abort(), 15e3);
2919
+ try {
2920
+ const resp = await provider.complete(
2921
+ {
2922
+ model,
2923
+ system: [
2924
+ {
2925
+ type: "text",
2926
+ text: 'You are an agent router. Choose the single best agent for the task. Reply with ONLY a compact JSON object {"role":"...","reason":"..."}.'
2927
+ }
2928
+ ],
2929
+ messages: [{ role: "user", content: [{ type: "text", text: prompt }] }],
2930
+ maxTokens: 120,
2931
+ temperature: 0
2932
+ },
2933
+ { signal: ctrl.signal }
2934
+ );
2935
+ const content = resp.content;
2936
+ return Array.isArray(content) ? content[0]?.text ?? "" : "";
2937
+ } catch {
2938
+ return "";
2939
+ } finally {
2940
+ clearTimeout(timeout);
2941
+ }
2942
+ });
2943
+ }
2944
+
2901
2945
  // src/fleet/host-acp.ts
2902
2946
  import {
2903
2947
  ACP_AGENT_COMMANDS,
@@ -3528,24 +3572,72 @@ function createHostStatusBroadcaster(input) {
3528
3572
  }
3529
3573
 
3530
3574
  // src/fleet/host-learning.ts
3531
- import { captureLearnedFromAgentOutputDetailed } from "@wrongstack/core/agent-catalog";
3575
+ import {
3576
+ captureLearnedFromAgentOutputDetailed,
3577
+ recordDirectiveOutcomes,
3578
+ recordSkillOutcome
3579
+ } from "@wrongstack/core/agent-catalog";
3532
3580
  import { TOKENS as TOKENS2 } from "@wrongstack/core/kernel";
3533
- function captureCompletedTaskLearningForHost(result, deps, subagentLearningRoles) {
3534
- if (result.status !== "success") return;
3535
- const role = subagentLearningRoles.get(result.subagentId);
3581
+ function captureCompletedTaskLearningForHost(result, deps, subjects, onCaptured) {
3582
+ const subject = subjects.get(result.subagentId);
3583
+ if (!subject) return;
3584
+ const logger = deps.container.safeResolve(TOKENS2.Logger);
3585
+ const graded = result.status !== "stopped";
3586
+ const succeeded = result.status === "success";
3587
+ if (graded && subject.skills.length > 0) {
3588
+ try {
3589
+ recordSkillOutcome(subject.role, subject.skills, succeeded, deps.projectRoot);
3590
+ } catch (error) {
3591
+ logger?.debug?.(
3592
+ `skill affinity update failed for role "${subject.role}": ${describe(error)}`
3593
+ );
3594
+ }
3595
+ }
3536
3596
  const finalText = typeof result.result === "string" ? result.result : result.partial?.text;
3537
- if (!role || !finalText) return;
3597
+ if (!finalText) return;
3598
+ if (graded) {
3599
+ try {
3600
+ const outcome = recordDirectiveOutcomes(subject.role, finalText, succeeded, deps.projectRoot);
3601
+ if (outcome.attributed > 0) {
3602
+ logger?.debug?.(
3603
+ `directive outcomes: ${outcome.attributed} applied for role "${subject.role}" (${succeeded ? "success" : "failure"})`
3604
+ );
3605
+ }
3606
+ for (const retired of outcome.quarantined) {
3607
+ logger?.info?.(
3608
+ `retired a directive for role "${subject.role}" after repeated failures: ${retired.slice(0, 120)}`
3609
+ );
3610
+ }
3611
+ } catch (error) {
3612
+ logger?.debug?.(
3613
+ `directive outcome update failed for role "${subject.role}": ${describe(error)}`
3614
+ );
3615
+ }
3616
+ }
3538
3617
  try {
3539
- const capture = captureLearnedFromAgentOutputDetailed(finalText, role, deps.projectRoot, false);
3618
+ const capture = captureLearnedFromAgentOutputDetailed(
3619
+ finalText,
3620
+ subject.role,
3621
+ deps.projectRoot,
3622
+ false
3623
+ );
3540
3624
  if (capture.captured > 0) {
3541
- deps.container.safeResolve(TOKENS2.Logger)?.debug(`agent learning captured ${capture.captured} item(s) for role "${role}"`);
3625
+ const routed = capture.skills?.length ? ` (skills: ${capture.skills.join(", ")})` : "";
3626
+ logger?.debug(
3627
+ `agent learning captured ${capture.captured} item(s) for role "${subject.role}"${routed}`
3628
+ );
3629
+ try {
3630
+ onCaptured?.(subject.role);
3631
+ } catch {
3632
+ }
3542
3633
  }
3543
3634
  } catch (error) {
3544
- deps.container.safeResolve(TOKENS2.Logger)?.warn(
3545
- `agent learning capture failed for role "${role}": ${error instanceof Error ? error.message : String(error)}`
3546
- );
3635
+ logger?.warn(`agent learning capture failed for role "${subject.role}": ${describe(error)}`);
3547
3636
  }
3548
3637
  }
3638
+ function describe(error) {
3639
+ return error instanceof Error ? error.message : String(error);
3640
+ }
3549
3641
 
3550
3642
  // src/fleet/host-learning-tracker.ts
3551
3643
  var HostLearningRoleTracker = class {
@@ -3555,11 +3647,17 @@ var HostLearningRoleTracker = class {
3555
3647
  maxEntries;
3556
3648
  roles = /* @__PURE__ */ new Map();
3557
3649
  accessOrder = [];
3558
- record(subagentId, role) {
3559
- setBoundedLruEntry(this.roles, this.accessOrder, subagentId, role, this.maxEntries);
3650
+ record(subagentId, role, skills = []) {
3651
+ setBoundedLruEntry(
3652
+ this.roles,
3653
+ this.accessOrder,
3654
+ subagentId,
3655
+ { role, skills: [...skills] },
3656
+ this.maxEntries
3657
+ );
3560
3658
  }
3561
- capture(result, deps) {
3562
- captureCompletedTaskLearningForHost(result, deps, this.roles);
3659
+ capture(result, deps, onCaptured) {
3660
+ captureCompletedTaskLearningForHost(result, deps, this.roles, onCaptured);
3563
3661
  }
3564
3662
  };
3565
3663
 
@@ -3595,6 +3693,84 @@ function applyFleetRootDefaults(opts) {
3595
3693
  }
3596
3694
  }
3597
3695
 
3696
+ // src/fleet/host-provider.ts
3697
+ import { makeProviderFromConfig, withCatalogCapabilities } from "@wrongstack/providers";
3698
+ async function buildHostSubagentProvider(deps, config, overrideId, model) {
3699
+ const requestedProviderId = overrideId ?? config.provider;
3700
+ const providerId = requestedProviderId === config.provider || config.providers?.[requestedProviderId] !== void 0 || deps.providerRegistry.has(requestedProviderId) ? requestedProviderId : config.provider;
3701
+ const newCfg = config.providers?.[providerId] ?? {
3702
+ type: providerId,
3703
+ apiKey: config.apiKey,
3704
+ baseUrl: config.baseUrl
3705
+ };
3706
+ const cfgWithType = {
3707
+ ...newCfg,
3708
+ type: providerId,
3709
+ ...model ? { model } : {}
3710
+ };
3711
+ let provider = deps.providerRegistry.has(providerId) ? deps.providerRegistry.create(cfgWithType) : makeProviderFromConfig(providerId, cfgWithType);
3712
+ if (deps.modelsRegistry) {
3713
+ const resolvedModel = model ?? config.model;
3714
+ provider = await withCatalogCapabilities(deps.modelsRegistry, providerId, provider, {
3715
+ ...cfgWithType,
3716
+ model: resolvedModel
3717
+ });
3718
+ await refreshRuntimeModelCatalog({
3719
+ modelsRegistry: deps.modelsRegistry,
3720
+ reason: `${providerId}/${resolvedModel}`
3721
+ });
3722
+ const mc = await resolveRuntimeMaxContext({
3723
+ modelsRegistry: deps.modelsRegistry,
3724
+ config,
3725
+ provider,
3726
+ runtimeProviderConfig: cfgWithType,
3727
+ providerId,
3728
+ modelId: resolvedModel
3729
+ });
3730
+ if (mc && mc > 0) provider.capabilities.maxContext = mc;
3731
+ }
3732
+ return provider;
3733
+ }
3734
+ async function resolveHostSubagentReasoningConfig(deps, providerId, modelId) {
3735
+ if (!deps.modelsRegistry) return void 0;
3736
+ try {
3737
+ return (await deps.modelsRegistry.getModel(providerId, modelId))?.capabilities.reasoningConfig;
3738
+ } catch {
3739
+ return void 0;
3740
+ }
3741
+ }
3742
+ function resolveHostSubagentModelSelection(liveConfig, effectiveCfg, matrixTarget) {
3743
+ let effProvider = effectiveCfg.provider ?? matrixTarget?.provider ?? liveConfig.provider;
3744
+ let effModel = effectiveCfg.model ?? matrixTarget?.model ?? liveConfig.model;
3745
+ const modelPolicy = effectiveCfg.modelPolicy;
3746
+ const closedModelPolicy = modelPolicy?.strict === true;
3747
+ const allowedModels = modelPolicy?.allowed ?? [];
3748
+ if (modelPolicy && !allowedModels.some((target) => target.provider === effProvider && target.model === effModel)) {
3749
+ const firstAllowed = allowedModels[0];
3750
+ if (!firstAllowed) throw new Error(`Agent "${effectiveCfg.role}" has no allowed models.`);
3751
+ effProvider = firstAllowed.provider;
3752
+ effModel = firstAllowed.model;
3753
+ }
3754
+ const fallbackProfile = modelPolicy ? void 0 : effectiveCfg.fallbackProfile ?? matrixTarget?.fallbackProfile;
3755
+ const runtimeOverride = effectiveCfg.modelRuntime ?? matrixTarget?.modelRuntime;
3756
+ const startupTargets = modelPolicy ? [
3757
+ { provider: effProvider, model: effModel },
3758
+ ...modelPolicy.fallbacks ?? [],
3759
+ ...closedModelPolicy || effProvider === liveConfig.provider && effModel === liveConfig.model ? [] : [{ provider: liveConfig.provider, model: liveConfig.model }]
3760
+ ] : [
3761
+ { provider: effProvider, model: effModel },
3762
+ ...effProvider === liveConfig.provider && effModel === liveConfig.model ? [] : [{ provider: liveConfig.provider, model: liveConfig.model }]
3763
+ ];
3764
+ return {
3765
+ effProvider,
3766
+ effModel,
3767
+ fallbackProfile,
3768
+ runtimeOverride,
3769
+ closedModelPolicy,
3770
+ startupTargets
3771
+ };
3772
+ }
3773
+
3598
3774
  // src/fleet/host-shadow-pass.ts
3599
3775
  async function runHostShadowPass(ctx, reason) {
3600
3776
  try {
@@ -3795,44 +3971,103 @@ import { AutoApprovePermissionPolicy } from "@wrongstack/core/security";
3795
3971
 
3796
3972
  // src/fleet/host-context.ts
3797
3973
  import {
3974
+ DEFAULT_EAGER_SKILL_LIMIT,
3975
+ loadProjectSkillAugmentation,
3798
3976
  missingRequiredRuntimeTools,
3799
3977
  missingRuntimeCapabilities,
3978
+ rankRoleSkills,
3979
+ recordSkillLoad,
3980
+ resolveRoleSkillCandidates,
3800
3981
  runtimeToolReferencesFromText
3801
3982
  } from "@wrongstack/core/agent-catalog";
3802
3983
  import { TOKENS as TOKENS3 } from "@wrongstack/core/kernel";
3803
3984
  import { getSageRetrieval } from "@wrongstack/sage";
3804
- async function resolveHostSubagentSkillContent(deps, roster, subCfg, availableToolNames = []) {
3805
- const rosterSkillNames = subCfg.role ? roster[subCfg.role]?.skillNames : void 0;
3806
- const skillNames = [...new Set(subCfg.skillNames ?? rosterSkillNames ?? [])];
3985
+ var EAGER_SKILL_LIMIT = DEFAULT_EAGER_SKILL_LIMIT;
3986
+ var MIN_TRIMMED_BODY_CHARS = 800;
3987
+ async function resolveHostSubagentSkillResolution(deps, roster, subCfg, availableToolNames = []) {
3988
+ const role = subCfg.role;
3989
+ const rosterEntry = role ? roster[role] : void 0;
3807
3990
  const directContent = subCfg.skillContent?.trim();
3808
- if (skillNames.length === 0 || !deps.skillLoader) return directContent ?? "";
3991
+ const dropped = {};
3992
+ const pool = [
3993
+ ...new Set(
3994
+ subCfg.skillNames ?? (role ? [
3995
+ ...rosterEntry?.skillPool ?? rosterEntry?.skillNames ?? [],
3996
+ ...resolveRoleSkillCandidates(role, deps.projectRoot)
3997
+ ] : [])
3998
+ )
3999
+ ];
4000
+ const skillNames = role ? rankRoleSkills(role, pool, deps.projectRoot, EAGER_SKILL_LIMIT) : pool.slice(0, EAGER_SKILL_LIMIT);
4001
+ if (skillNames.length === 0 || !deps.skillLoader) {
4002
+ return { content: directContent ?? "", selected: [], dropped, trimmed: [] };
4003
+ }
3809
4004
  const resolved = [];
4005
+ const selected = [];
4006
+ const trimmed = [];
3810
4007
  let usedChars = 0;
3811
4008
  const maxChars = 16e3;
3812
4009
  const maxCharsPerSkill = 4e3;
3813
4010
  for (const skillName of skillNames) {
3814
4011
  try {
3815
4012
  const manifest = await deps.skillLoader.find(skillName);
3816
- if (!manifest) continue;
3817
- if (missingRuntimeCapabilities(manifest.requiredCapabilities, availableToolNames).length > 0 || missingRequiredRuntimeTools(manifest.requiredTools, availableToolNames).length > 0) {
4013
+ if (!manifest) {
4014
+ dropped[skillName] = "not-found";
4015
+ continue;
4016
+ }
4017
+ if (missingRuntimeCapabilities(manifest.requiredCapabilities, availableToolNames).length > 0) {
4018
+ dropped[skillName] = "missing-capability";
4019
+ continue;
4020
+ }
4021
+ if (missingRequiredRuntimeTools(manifest.requiredTools, availableToolNames).length > 0) {
4022
+ dropped[skillName] = "missing-tool";
3818
4023
  continue;
3819
4024
  }
3820
4025
  const body = (await deps.skillLoader.readSaveBody(skillName)).trim();
3821
- if (!body) continue;
4026
+ if (!body) {
4027
+ dropped[skillName] = "empty";
4028
+ continue;
4029
+ }
3822
4030
  if (missingRequiredRuntimeTools(runtimeToolReferencesFromText(body), availableToolNames).length > 0) {
4031
+ dropped[skillName] = "missing-tool";
3823
4032
  continue;
3824
4033
  }
3825
- const entry = `## Skill: ${skillName}
4034
+ const augmentation = role ? loadProjectSkillAugmentation(role, skillName, deps.projectRoot) : "";
4035
+ const compose = (bodyChars) => [
4036
+ `## Skill: ${skillName}`,
4037
+ "",
4038
+ body.length > bodyChars ? `${body.slice(0, bodyChars).trimEnd()}
3826
4039
 
3827
- ${body.slice(0, maxCharsPerSkill)}`;
4040
+ _(body trimmed)_` : body,
4041
+ ...augmentation ? [
4042
+ "",
4043
+ `### Project practice for \`${skillName}\``,
4044
+ "",
4045
+ "Learned in this project. Where this differs from the general method above, follow this.",
4046
+ "",
4047
+ augmentation
4048
+ ] : []
4049
+ ].join("\n");
4050
+ let entry = compose(maxCharsPerSkill);
3828
4051
  if (usedChars + entry.length > maxChars) {
3829
- console.warn(
3830
- `[MultiAgentHost] resolveSubagentSkillContent: budget (${maxChars}) exhausted after ${resolved.length} skill(s); dropping "${skillName}" and remaining skills`
3831
- );
3832
- break;
4052
+ const overhead = entry.length - Math.min(body.length, maxCharsPerSkill);
4053
+ const room = maxChars - usedChars - overhead;
4054
+ if (!augmentation || room < MIN_TRIMMED_BODY_CHARS) {
4055
+ dropped[skillName] = "budget";
4056
+ continue;
4057
+ }
4058
+ entry = compose(room);
4059
+ trimmed.push(skillName);
3833
4060
  }
3834
4061
  resolved.push(entry);
4062
+ selected.push(skillName);
3835
4063
  usedChars += entry.length;
4064
+ } catch {
4065
+ dropped[skillName] = "not-found";
4066
+ }
4067
+ }
4068
+ if (role && selected.length > 0) {
4069
+ try {
4070
+ recordSkillLoad(role, selected, deps.projectRoot);
3836
4071
  } catch {
3837
4072
  }
3838
4073
  }
@@ -3844,7 +4079,7 @@ Apply these skills first for this assignment.
3844
4079
 
3845
4080
  ${resolved.join("\n\n---\n\n")}` : void 0
3846
4081
  ].filter((section) => Boolean(section));
3847
- return sections.join("\n\n");
4082
+ return { content: sections.join("\n\n"), selected, dropped, trimmed };
3848
4083
  }
3849
4084
  async function retrieveHostSubagentMemory(deps, getLeaderMode, subCfg, taskContext) {
3850
4085
  const memoryPort = deps.container.safeResolve(TOKENS3.MemoryStore);
@@ -4043,84 +4278,6 @@ function installSubagentEventBridge(opts) {
4043
4278
  };
4044
4279
  }
4045
4280
 
4046
- // src/fleet/host-provider.ts
4047
- import { makeProviderFromConfig, withCatalogCapabilities } from "@wrongstack/providers";
4048
- async function buildHostSubagentProvider(deps, config, overrideId, model) {
4049
- const requestedProviderId = overrideId ?? config.provider;
4050
- const providerId = requestedProviderId === config.provider || config.providers?.[requestedProviderId] !== void 0 || deps.providerRegistry.has(requestedProviderId) ? requestedProviderId : config.provider;
4051
- const newCfg = config.providers?.[providerId] ?? {
4052
- type: providerId,
4053
- apiKey: config.apiKey,
4054
- baseUrl: config.baseUrl
4055
- };
4056
- const cfgWithType = {
4057
- ...newCfg,
4058
- type: providerId,
4059
- ...model ? { model } : {}
4060
- };
4061
- let provider = deps.providerRegistry.has(providerId) ? deps.providerRegistry.create(cfgWithType) : makeProviderFromConfig(providerId, cfgWithType);
4062
- if (deps.modelsRegistry) {
4063
- const resolvedModel = model ?? config.model;
4064
- provider = await withCatalogCapabilities(deps.modelsRegistry, providerId, provider, {
4065
- ...cfgWithType,
4066
- model: resolvedModel
4067
- });
4068
- await refreshRuntimeModelCatalog({
4069
- modelsRegistry: deps.modelsRegistry,
4070
- reason: `${providerId}/${resolvedModel}`
4071
- });
4072
- const mc = await resolveRuntimeMaxContext({
4073
- modelsRegistry: deps.modelsRegistry,
4074
- config,
4075
- provider,
4076
- runtimeProviderConfig: cfgWithType,
4077
- providerId,
4078
- modelId: resolvedModel
4079
- });
4080
- if (mc && mc > 0) provider.capabilities.maxContext = mc;
4081
- }
4082
- return provider;
4083
- }
4084
- async function resolveHostSubagentReasoningConfig(deps, providerId, modelId) {
4085
- if (!deps.modelsRegistry) return void 0;
4086
- try {
4087
- return (await deps.modelsRegistry.getModel(providerId, modelId))?.capabilities.reasoningConfig;
4088
- } catch {
4089
- return void 0;
4090
- }
4091
- }
4092
- function resolveHostSubagentModelSelection(liveConfig, effectiveCfg, matrixTarget) {
4093
- let effProvider = effectiveCfg.provider ?? matrixTarget?.provider ?? liveConfig.provider;
4094
- let effModel = effectiveCfg.model ?? matrixTarget?.model ?? liveConfig.model;
4095
- const modelPolicy = effectiveCfg.modelPolicy;
4096
- const closedModelPolicy = modelPolicy?.strict === true;
4097
- const allowedModels = modelPolicy?.allowed ?? [];
4098
- if (modelPolicy && !allowedModels.some((target) => target.provider === effProvider && target.model === effModel)) {
4099
- const firstAllowed = allowedModels[0];
4100
- if (!firstAllowed) throw new Error(`Agent "${effectiveCfg.role}" has no allowed models.`);
4101
- effProvider = firstAllowed.provider;
4102
- effModel = firstAllowed.model;
4103
- }
4104
- const fallbackProfile = modelPolicy ? void 0 : effectiveCfg.fallbackProfile ?? matrixTarget?.fallbackProfile;
4105
- const runtimeOverride = effectiveCfg.modelRuntime ?? matrixTarget?.modelRuntime;
4106
- const startupTargets = modelPolicy ? [
4107
- { provider: effProvider, model: effModel },
4108
- ...modelPolicy.fallbacks ?? [],
4109
- ...closedModelPolicy || effProvider === liveConfig.provider && effModel === liveConfig.model ? [] : [{ provider: liveConfig.provider, model: liveConfig.model }]
4110
- ] : [
4111
- { provider: effProvider, model: effModel },
4112
- ...effProvider === liveConfig.provider && effModel === liveConfig.model ? [] : [{ provider: liveConfig.provider, model: liveConfig.model }]
4113
- ];
4114
- return {
4115
- effProvider,
4116
- effModel,
4117
- fallbackProfile,
4118
- runtimeOverride,
4119
- closedModelPolicy,
4120
- startupTargets
4121
- };
4122
- }
4123
-
4124
4281
  // src/fleet/host-session-writer.ts
4125
4282
  function createParentSubagentSessionWriter(parentSession) {
4126
4283
  return {
@@ -4134,10 +4291,8 @@ function createParentSubagentSessionWriter(parentSession) {
4134
4291
  flush: () => parentSession.flush(),
4135
4292
  close: async () => {
4136
4293
  },
4137
- recordFileChange: () => {
4138
- },
4139
- recordSideEffect: () => {
4140
- },
4294
+ recordFileChange: (input) => parentSession.recordFileChange(input),
4295
+ recordSideEffect: (input) => parentSession.recordSideEffect(input),
4141
4296
  writeCheckpoint: async () => {
4142
4297
  },
4143
4298
  writeFileSnapshot: async () => {
@@ -4151,6 +4306,21 @@ function createParentSubagentSessionWriter(parentSession) {
4151
4306
  }
4152
4307
  };
4153
4308
  }
4309
+ function withParentFileSnapshots(subagentSession, parentSession) {
4310
+ if (subagentSession === parentSession) return subagentSession;
4311
+ return new Proxy(subagentSession, {
4312
+ get(target, property, receiver) {
4313
+ if (property === "recordFileChange") {
4314
+ return (input) => {
4315
+ target.recordFileChange(input);
4316
+ parentSession.recordFileChange(input);
4317
+ };
4318
+ }
4319
+ const value = Reflect.get(target, property, receiver);
4320
+ return typeof value === "function" ? value.bind(target) : value;
4321
+ }
4322
+ });
4323
+ }
4154
4324
 
4155
4325
  // src/fleet/host-subagent-factory.ts
4156
4326
  function createHostSubagentFactory(config, host) {
@@ -4269,17 +4439,27 @@ ${message} Falling back to the assigned checkout.` : `${message} Falling back to
4269
4439
  ${audienceMemory.map((text) => `- ${text}`).join("\n")}`
4270
4440
  });
4271
4441
  }
4272
- const roleSkillContent = await resolveHostSubagentSkillContent(
4442
+ const skillResolution = await resolveHostSubagentSkillResolution(
4273
4443
  host.deps,
4274
4444
  host.roster,
4275
4445
  effectiveCfg,
4276
4446
  subagentTools.map((tool) => tool.name)
4277
4447
  );
4278
- if (roleSkillContent) {
4448
+ if (skillResolution.content) {
4279
4449
  for (let index = baseSystem.length - 1; index >= 0; index--) {
4280
4450
  if (baseSystem[index]?.text.includes("# Active Skills")) baseSystem.splice(index, 1);
4281
4451
  }
4282
- baseSystem.push({ type: "text", text: roleSkillContent });
4452
+ baseSystem.push({ type: "text", text: skillResolution.content });
4453
+ }
4454
+ const droppedSkills = Object.entries(skillResolution.dropped);
4455
+ if (droppedSkills.length > 0 || skillResolution.trimmed.length > 0) {
4456
+ host.deps.events.emit("subagent.skills.dropped", {
4457
+ sessionId: host.deps.session.id,
4458
+ role: effectiveCfg.role,
4459
+ selected: skillResolution.selected,
4460
+ dropped: Object.fromEntries(droppedSkills),
4461
+ ...skillResolution.trimmed.length > 0 ? { trimmed: skillResolution.trimmed } : {}
4462
+ });
4283
4463
  }
4284
4464
  const rawRolePrompt = effectiveCfg.systemPromptOverride ?? effectiveCfg.prompt ?? (effectiveCfg.role ? host.roster[effectiveCfg.role]?.prompt : void 0);
4285
4465
  const rolePrompt = rawRolePrompt && effectiveCfg.role ? buildProjectContextualizedPrompt(rawRolePrompt, effectiveCfg.role, projectRoot, {
@@ -4290,16 +4470,19 @@ ${audienceMemory.map((text) => `- ${text}`).join("\n")}`
4290
4470
  }
4291
4471
  const subagentName = effectiveCfg.id ?? effectiveCfg.name ?? `sub_${randomUUID2().slice(0, 8)}`;
4292
4472
  if (effectiveCfg.role) {
4293
- host.recordLearningRole(subagentName, effectiveCfg.role);
4473
+ host.recordLearningRole(subagentName, effectiveCfg.role, skillResolution.selected);
4294
4474
  }
4295
4475
  let subSession;
4296
4476
  if (host.sessionFactory) {
4297
- subSession = await host.sessionFactory.createSubagentSession({
4298
- subagentId: subagentName,
4299
- provider: effProvider,
4300
- model: effModel,
4301
- title: `subagent: ${subagentName}`
4302
- });
4477
+ subSession = withParentFileSnapshots(
4478
+ await host.sessionFactory.createSubagentSession({
4479
+ subagentId: subagentName,
4480
+ provider: effProvider,
4481
+ model: effModel,
4482
+ title: `subagent: ${subagentName}`
4483
+ }),
4484
+ host.deps.session
4485
+ );
4303
4486
  } else {
4304
4487
  subSession = createParentSubagentSessionWriter(host.deps.session);
4305
4488
  }
@@ -4347,7 +4530,12 @@ ${audienceMemory.map((text) => `- ${text}`).join("\n")}`
4347
4530
  iterationTimeoutMs: config.tools?.iterationTimeoutMs ?? 12e4,
4348
4531
  maxToolTimeoutMs: config.tools?.maxToolTimeoutMs ?? 3e5,
4349
4532
  perIterationOutputCapBytes: config.tools?.perIterationOutputCapBytes ?? 1e5,
4350
- tracer: void 0
4533
+ tracer: void 0,
4534
+ // Kanban tracks work; it does not gate it. Off unless the operator opts
4535
+ // in — and then subagents inherit it, because a worker dispatched by
4536
+ // `kanban_queue` already carries board/task/lease identity in ctx.meta
4537
+ // and can satisfy the same gate its leader was held to.
4538
+ requireKanbanGovernance: config.tools?.kanbanGovernance ?? false
4351
4539
  });
4352
4540
  const subagentConfigStore = host.deps.configStore;
4353
4541
  const pipelines = createDefaultPipelines();
@@ -4623,6 +4811,9 @@ var MultiAgentHost = class {
4623
4811
  * Bounded to 20 entries with LRU eviction to prevent unbounded memory growth. */
4624
4812
  acpRunnerCache = new HostAcpRunnerCache();
4625
4813
  learningRoles = new HostLearningRoleTracker();
4814
+ /** Background distillation of captured learning into per-skill addenda. */
4815
+ learningOptimizer = null;
4816
+ learningSwept = false;
4626
4817
  /** Adaptive concurrency controller — created in buildDirector() when config has
4627
4818
  * adaptiveConcurrency.enabled = true. Monitors FleetBus for 429 errors and
4628
4819
  * automatically adjusts maxConcurrent to prevent rate limiting. */
@@ -4688,6 +4879,7 @@ var MultiAgentHost = class {
4688
4879
  async buildDirector() {
4689
4880
  if (this.director) return;
4690
4881
  const config = this.deps.configStore.get();
4882
+ this.sweepLearningOptimization();
4691
4883
  const fleetManager = createHostFleetManager(this.opts);
4692
4884
  this.fleetManager = fleetManager;
4693
4885
  if (this.opts.sessionsRoot && !this.sessionFactory) {
@@ -4734,6 +4926,12 @@ var MultiAgentHost = class {
4734
4926
  // pass so director.fleetManager is never undefined
4735
4927
  brain: this.opts.brain,
4736
4928
  roster: this.roster,
4929
+ // The dispatcher is two-stage by design — keyword heuristic, then a model
4930
+ // to break the tie — but nothing ever supplied stage two here, so every
4931
+ // description the keywords could not resolve fell through to the
4932
+ // `executor` generalist. That is one half of why a 77-role roster was
4933
+ // being served by a handful of agents.
4934
+ dispatchClassifier: (task, candidates) => this.classifyDispatch(task, candidates),
4737
4935
  // Fire-and-forget report-back: when an assign_task completes with no
4738
4936
  // pending await, post the result to this session's leader via the
4739
4937
  // project mailbox (injected inline before the leader's next step).
@@ -4959,7 +5157,7 @@ var MultiAgentHost = class {
4959
5157
  sessionFactory: this.sessionFactory,
4960
5158
  filterTools: (allow) => this.filterTools(allow),
4961
5159
  mailboxProjectDir: () => this.mailboxProjectDir(),
4962
- recordLearningRole: (subagentId, role) => this.recordLearningRole(subagentId, role),
5160
+ recordLearningRole: (subagentId, role, skills) => this.recordLearningRole(subagentId, role, skills),
4963
5161
  subagentToolRegistry: (allow) => this.subagentToolRegistry(allow)
4964
5162
  });
4965
5163
  }
@@ -4982,6 +5180,7 @@ var MultiAgentHost = class {
4982
5180
  await this.ensureCoordinator(config);
4983
5181
  const coordinator = this.getCoordinator();
4984
5182
  const acpRunner = await this.buildACPRunner(subagentId);
5183
+ this.recordLearningRole(subagentId, subagentId);
4985
5184
  coordinator.setRunner(acpRunner);
4986
5185
  this.directorRunnerSet = true;
4987
5186
  await coordinator.spawn({
@@ -5184,11 +5383,97 @@ var MultiAgentHost = class {
5184
5383
  emitLifecycleCompleted(taskId, result) {
5185
5384
  emitHostLifecycleCompleted(this.deps.events, this.deps.session.id, taskId, result);
5186
5385
  }
5187
- recordLearningRole(subagentId, role) {
5188
- this.learningRoles.record(subagentId, role);
5386
+ recordLearningRole(subagentId, role, skills = []) {
5387
+ this.learningRoles.record(subagentId, role, skills);
5189
5388
  }
5190
5389
  captureCompletedTaskLearning(result) {
5191
- this.learningRoles.capture(result, this.deps);
5390
+ this.learningRoles.capture(
5391
+ result,
5392
+ this.deps,
5393
+ (role) => this.getLearningOptimizer()?.notifyCaptured(role)
5394
+ );
5395
+ }
5396
+ /**
5397
+ * Lazily built so a session that never spawns a subagent pays nothing, and
5398
+ * so the policy is read from the live config rather than frozen at boot.
5399
+ * Returns null when auto-optimization is switched off.
5400
+ */
5401
+ getLearningOptimizer() {
5402
+ const settings = this.deps.configStore.get().fleet?.learning?.autoOptimize;
5403
+ if (settings?.enabled === false) return null;
5404
+ if (!this.learningOptimizer) {
5405
+ this.learningOptimizer = new LearningOptimizationScheduler({
5406
+ projectRoot: this.deps.projectRoot,
5407
+ getPolicy: () => resolveAutoOptimizePolicy(this.deps.configStore.get().fleet?.learning?.autoOptimize),
5408
+ getLlm: () => this.resolveOptimizerLlm(),
5409
+ onEvent: (event) => {
5410
+ this.deps.events.emit("agent.learning.optimized", {
5411
+ sessionId: this.deps.session.id,
5412
+ role: event.role,
5413
+ trigger: event.trigger,
5414
+ status: event.result?.status ?? "failed",
5415
+ skills: event.result?.skills ?? [],
5416
+ ...event.error ? { error: event.error } : {}
5417
+ });
5418
+ }
5419
+ });
5420
+ }
5421
+ return this.learningOptimizer;
5422
+ }
5423
+ /**
5424
+ * Break a dispatch tie with a model when the keyword heuristic is ambiguous.
5425
+ *
5426
+ * Routes through the `dispatcher` model-matrix slot when one is configured —
5427
+ * picking a role is a short classification, so it belongs on a cheap fast
5428
+ * model rather than the leader's — and the session default otherwise.
5429
+ * Returning `null` on any failure leaves the dispatcher on its heuristic
5430
+ * result, so routing degrades rather than breaking a spawn.
5431
+ */
5432
+ async classifyDispatch(task, candidates) {
5433
+ try {
5434
+ const config = this.deps.configStore.get();
5435
+ const target = resolveSubagentModelTarget2(config, "dispatcher");
5436
+ const providerId = target?.provider ?? config.provider;
5437
+ const model = target?.model ?? config.model;
5438
+ if (!providerId || !model) return null;
5439
+ const provider = await buildHostSubagentProvider(this.deps, config, providerId, model);
5440
+ return await makeProviderClassifier(provider, model)(task, candidates);
5441
+ } catch {
5442
+ return null;
5443
+ }
5444
+ }
5445
+ /**
5446
+ * Resolve a model for the distillation pass. Uses the `memory-curator` slot
5447
+ * of the model matrix when one is configured — curating learned knowledge is
5448
+ * exactly that role's job — and the session default otherwise. A failure
5449
+ * here is not fatal: the pass still writes the deterministic skill addenda.
5450
+ */
5451
+ async resolveOptimizerLlm() {
5452
+ try {
5453
+ const config = this.deps.configStore.get();
5454
+ const target = resolveSubagentModelTarget2(config, "memory-curator");
5455
+ const providerId = target?.provider ?? config.provider;
5456
+ const model = target?.model ?? config.model;
5457
+ if (!providerId || !model) return void 0;
5458
+ const provider = await buildHostSubagentProvider(this.deps, config, providerId, model);
5459
+ return { provider, model };
5460
+ } catch {
5461
+ return void 0;
5462
+ }
5463
+ }
5464
+ /**
5465
+ * Evaluate every role with learning data once per session. Without it, a
5466
+ * role that became eligible before this session would wait for its next
5467
+ * capture — which for a rarely-used role can be never.
5468
+ */
5469
+ sweepLearningOptimization() {
5470
+ if (this.learningSwept) return;
5471
+ this.learningSwept = true;
5472
+ if (this.deps.configStore.get().fleet?.learning?.autoOptimize?.sweepOnStart === false) return;
5473
+ try {
5474
+ this.getLearningOptimizer()?.sweep(listProjectAgentRoles(this.deps.projectRoot));
5475
+ } catch {
5476
+ }
5192
5477
  }
5193
5478
  status() {
5194
5479
  return buildFleetHostStatus({
@@ -5285,6 +5570,8 @@ var MultiAgentHost = class {
5285
5570
  * Safe to call multiple times — subsequent calls are no-ops.
5286
5571
  */
5287
5572
  async dispose() {
5573
+ this.learningOptimizer?.dispose();
5574
+ this.learningOptimizer = null;
5288
5575
  this.clearShadowAgent();
5289
5576
  for (const off of this.shadowActivityOffHandles) {
5290
5577
  off();
@@ -6248,6 +6535,58 @@ function setupDirectorAndAutonomy(deps) {
6248
6535
  };
6249
6536
  }
6250
6537
 
6538
+ // src/wiring/domain-glossary.ts
6539
+ import { getSageService } from "@wrongstack/sage";
6540
+ var DOMAIN_GLOSSARY_LIMIT = 16;
6541
+ function createDomainGlossaryAdapter(memoryStore) {
6542
+ return {
6543
+ async list(scope, limit) {
6544
+ try {
6545
+ const service = getSageService(memoryStore);
6546
+ if (!service) return [];
6547
+ if (scope !== "project-memory") return [];
6548
+ const effectiveLimit = Math.min(
6549
+ Math.max(1, limit ?? DOMAIN_GLOSSARY_LIMIT),
6550
+ DOMAIN_GLOSSARY_LIMIT
6551
+ );
6552
+ const hits = await service.searchSage("domain-term", { limit: effectiveLimit });
6553
+ return hits.map((hit) => sageToGlossaryEntry(hit));
6554
+ } catch {
6555
+ return [];
6556
+ }
6557
+ }
6558
+ };
6559
+ }
6560
+ function sageToGlossaryEntry(sage) {
6561
+ const priority = sage.importance >= 0.9 ? "critical" : sage.importance >= 0.75 ? "high" : sage.importance >= 0.4 ? "medium" : "low";
6562
+ return {
6563
+ scope: "project-memory",
6564
+ text: sage.text,
6565
+ ts: sage.updatedAt || sage.createdAt,
6566
+ type: "reference",
6567
+ tags: sage.tags.slice(),
6568
+ priority,
6569
+ confidence: sage.confidence,
6570
+ lastAccessed: sage.lastAccessedAt
6571
+ };
6572
+ }
6573
+
6574
+ // src/wiring/domain-terms-mirror.ts
6575
+ import { writeErr as writeErr2 } from "@wrongstack/core/utils";
6576
+ import { SageDomainTermExtractor } from "@wrongstack/sage";
6577
+ async function refreshDomainTermsMirror(options) {
6578
+ try {
6579
+ const extractor = new SageDomainTermExtractor();
6580
+ return await extractor.writeDomainTermsFile(options.projectRoot, options.memoryStore);
6581
+ } catch (err) {
6582
+ writeErr2(
6583
+ `[wrongstack] domain-terms mirror refresh failed: ${err instanceof Error ? err.message : String(err)}
6584
+ `
6585
+ );
6586
+ return null;
6587
+ }
6588
+ }
6589
+
6251
6590
  // src/wiring/eternal-command-handlers.ts
6252
6591
  import { EternalAutonomyEngine as EternalAutonomyEngine2, ParallelEternalEngine } from "@wrongstack/core/execution";
6253
6592
  function createEternalCommandHandlers(input) {
@@ -7580,7 +7919,21 @@ function createAgent(params) {
7580
7919
  perIterationOutputCapBytes: params.config.tools.perIterationOutputCapBytes,
7581
7920
  tracer: params.tracer,
7582
7921
  logger,
7583
- hookRunner: params.hookRunner
7922
+ hookRunner: params.hookRunner,
7923
+ // Kanban tracks work; it does not gate it. When this was hard-wired `true`
7924
+ // a mutating tool was refused until a managed card existed, carried a
7925
+ // description and acceptance criteria, and had been started — so the
7926
+ // session spent its effort on card ceremony instead of the work the card
7927
+ // describes. Cards still advance and boards still mirror; path-scoped
7928
+ // `boundary` policies (the actual access control) are unaffected and still
7929
+ // enforced.
7930
+ //
7931
+ // It stays OFF by default, but is now an operator switch rather than a
7932
+ // literal: `tools.kanbanGovernance: true` opts an installation in. The
7933
+ // four hosts (this pipeline, mcp-serve, acp-server-agent, and the fleet
7934
+ // subagent factory) must resolve it identically, or a subagent would run
7935
+ // under a different contract than the leader that dispatched it.
7936
+ requireKanbanGovernance: params.config.tools.kanbanGovernance ?? false
7584
7937
  };
7585
7938
  const toolExecutor = new ToolExecutor2(params.tools, toolExecutorOptions);
7586
7939
  void bootstrapMailboxBridgeAtStartup({
@@ -7601,6 +7954,7 @@ function createAgent(params) {
7601
7954
  events: params.events,
7602
7955
  pipelines: params.pipelines,
7603
7956
  context: params.context,
7957
+ refreshSystemPrompt: true,
7604
7958
  maxIterations: params.config.tools.maxIterations,
7605
7959
  iterationTimeoutMs: params.config.tools.iterationTimeoutMs,
7606
7960
  executionStrategy: params.config.tools.defaultExecutionStrategy,
@@ -8092,7 +8446,7 @@ import {
8092
8446
  watchProviderConfig
8093
8447
  } from "@wrongstack/core/storage";
8094
8448
  import { withCatalogCapabilities as withCatalogCapabilities2 } from "@wrongstack/providers";
8095
- import { getSageService } from "@wrongstack/sage";
8449
+ import { getSageService as getSageService2 } from "@wrongstack/sage";
8096
8450
 
8097
8451
  // src/wiring/fallback-gate.ts
8098
8452
  function createFallbackGate(_events) {
@@ -8231,7 +8585,7 @@ function setupProviderRuntime(deps) {
8231
8585
  })
8232
8586
  );
8233
8587
  if (cfg.features.memory && cfg.features.memoryConsolidation !== false) {
8234
- const consSage = getSageService(memoryStore);
8588
+ const consSage = getSageService2(memoryStore);
8235
8589
  agent.extensions.register(
8236
8590
  new SessionMemoryConsolidator({
8237
8591
  memoryStore,
@@ -8648,88 +9002,11 @@ async function setupCodebaseIndexing(deps) {
8648
9002
 
8649
9003
  // src/wiring/sage.ts
8650
9004
  import {
8651
- createSageContextMonitorMiddleware,
8652
- createSageToolCallMiddleware,
8653
- createSageTurnMiddleware,
8654
- getSageRetrieval as getSageRetrieval2,
8655
- InjectionTracker
9005
+ AUTO_HYGIENE_INTERVAL_MS,
9006
+ _resetAutoHygieneThrottleForTesting,
9007
+ sageHygieneOptionsFromConfig,
9008
+ setupSage
8656
9009
  } from "@wrongstack/sage";
8657
- var lastAutoHygieneAt = 0;
8658
- var AUTO_HYGIENE_INTERVAL_MS = 60 * 6e4;
8659
- function setupSage(deps) {
8660
- const cfg = deps.config.Sage;
8661
- const noop = async () => {
8662
- };
8663
- if (deps.config.features.memory === false) return noop;
8664
- if (cfg?.enabled === false) return noop;
8665
- if (!deps.memoryStore) return noop;
8666
- const memoryStore = deps.memoryStore;
8667
- const retrieval = getSageRetrieval2(memoryStore);
8668
- if (!retrieval) {
8669
- deps.logger.debug("sage middleware skipped: memory store does not support retrieval");
8670
- return noop;
8671
- }
8672
- const injectionTracker = new InjectionTracker();
8673
- if (cfg?.inject?.toolResults !== false) {
8674
- deps.pipelines.toolCall.use(
8675
- createSageToolCallMiddleware({
8676
- memory: retrieval,
8677
- maxHintsPerTool: cfg?.inject?.maxHintsPerTool,
8678
- maxCharsPerTool: cfg?.inject?.maxCharsPerTool,
8679
- taskAware: cfg?.inject?.taskAware,
8680
- minScore: cfg?.inject?.minScore,
8681
- minImportance: cfg?.inject?.minImportance,
8682
- relationFloor: cfg?.inject?.relationFloor,
8683
- repeatCooldownMs: cfg?.inject?.repeatCooldownMs,
8684
- verifyOnMutation: cfg?.hygiene?.autoOnFileChange,
8685
- triggers: cfg?.inject?.triggers,
8686
- tracker: injectionTracker,
8687
- events: deps.events,
8688
- getSessionId: deps.getSessionId
8689
- })
8690
- );
8691
- }
8692
- if (cfg?.inject?.turnContext === true) {
8693
- deps.pipelines.request.use(
8694
- createSageTurnMiddleware({
8695
- memory: retrieval,
8696
- maxMemories: cfg?.inject?.maxTurnMemories,
8697
- maxChars: cfg?.inject?.maxCharsPerTurn,
8698
- minScore: cfg?.inject?.minScore,
8699
- metadataWeight: cfg?.retrieval?.metadataWeight,
8700
- tracker: injectionTracker,
8701
- getSessionId: deps.getSessionId
8702
- })
8703
- );
8704
- }
8705
- deps.pipelines.request.use(
8706
- createSageContextMonitorMiddleware({
8707
- tracker: injectionTracker,
8708
- events: deps.events,
8709
- getSessionId: deps.getSessionId
8710
- })
8711
- );
8712
- return async () => {
8713
- await retrieval.flushPendingCounters?.();
8714
- if (cfg?.hygiene?.autoAfterSession === false) return;
8715
- const now = Date.now();
8716
- if (now - lastAutoHygieneAt < AUTO_HYGIENE_INTERVAL_MS) {
8717
- deps.logger.debug(
8718
- `sage auto-hygiene skipped: last run ${Math.round((now - lastAutoHygieneAt) / 1e3)}s ago (throttle: ${AUTO_HYGIENE_INTERVAL_MS / 1e3}s)`
8719
- );
8720
- return;
8721
- }
8722
- await memoryStore.hygiene?.({
8723
- retentionDays: cfg?.hygiene?.retentionDays,
8724
- sessionRetentionDays: cfg?.hygiene?.sessionRetentionDays,
8725
- archiveLowConfidenceAfterDays: cfg?.hygiene?.archiveLowConfidenceAfterDays,
8726
- archiveUnusedAfterDays: cfg?.hygiene?.archiveUnusedAfterDays,
8727
- unusedMinInjections: cfg?.hygiene?.unusedMinInjections,
8728
- purgeDeletedAfterDays: cfg?.hygiene?.purgeDeletedAfterDays
8729
- });
8730
- lastAutoHygieneAt = now;
8731
- };
8732
- }
8733
9010
 
8734
9011
  // src/wiring/runtime-dispatch-state.ts
8735
9012
  async function prepareRuntimeDispatch(input) {
@@ -8739,7 +9016,8 @@ async function prepareRuntimeDispatch(input) {
8739
9016
  memoryStore: input.memoryStore,
8740
9017
  logger: input.logger,
8741
9018
  events: input.events,
8742
- getSessionId: () => input.agent.ctx.session.id
9019
+ getSessionId: () => input.agent.ctx.session.id,
9020
+ projectRoot: input.projectRoot
8743
9021
  });
8744
9022
  const disposeIndexing = await setupCodebaseIndexing({
8745
9023
  config: input.getConfig(),
@@ -8784,6 +9062,7 @@ async function prepareRuntimeDispatch(input) {
8784
9062
  owner,
8785
9063
  category: tool.category ?? "Other",
8786
9064
  enabled: !input.toolRegistry.isDisabled(tool.name),
9065
+ exposure: input.toolRegistry.isDisabled(tool.name) ? "disabled" : input.toolRegistry.isExposedToProvider(tool.name) ? "direct" : "lazy",
8787
9066
  mutating: tool.mutating,
8788
9067
  permission: tool.permission,
8789
9068
  descMode: getToolDescriptionMode(input.toolRegistry, tool.name),
@@ -9497,57 +9776,25 @@ ${diff}`;
9497
9776
  const signal = new AbortController();
9498
9777
  const timeout = setTimeout(() => signal.abort(), 15e3);
9499
9778
  const resp = await opts.provider.complete(
9500
- {
9501
- model: opts.model,
9502
- system: [{ type: "text", text: systemPrompt }],
9503
- messages: [{ role: "user", content: [{ type: "text", text: userPrompt }] }],
9504
- maxTokens: 80,
9505
- temperature: 0.3
9506
- },
9507
- { signal: signal.signal }
9508
- );
9509
- clearTimeout(timeout);
9510
- const rawContent = resp.content;
9511
- const text = Array.isArray(rawContent) ? rawContent[0]?.text ?? "" : typeof rawContent === "object" && rawContent !== null ? rawContent.text ?? "" : String(rawContent);
9512
- const message = expectDefined5(text.trim().split("\n")[0]);
9513
- if (message.length > 0 && message.length < 200) {
9514
- return message;
9515
- }
9516
- } catch {
9517
- }
9518
- return "chore: update";
9519
- }
9520
-
9521
- // src/services/dispatch-classifier.ts
9522
- import { makeLLMClassifier } from "@wrongstack/core/coordination";
9523
- function makeProviderClassifier(provider, model) {
9524
- return makeLLMClassifier(async (prompt) => {
9525
- const ctrl = new AbortController();
9526
- const timeout = setTimeout(() => ctrl.abort(), 15e3);
9527
- try {
9528
- const resp = await provider.complete(
9529
- {
9530
- model,
9531
- system: [
9532
- {
9533
- type: "text",
9534
- text: 'You are an agent router. Choose the single best agent for the task. Reply with ONLY a compact JSON object {"role":"...","reason":"..."}.'
9535
- }
9536
- ],
9537
- messages: [{ role: "user", content: [{ type: "text", text: prompt }] }],
9538
- maxTokens: 120,
9539
- temperature: 0
9540
- },
9541
- { signal: ctrl.signal }
9542
- );
9543
- const content = resp.content;
9544
- return Array.isArray(content) ? content[0]?.text ?? "" : "";
9545
- } catch {
9546
- return "";
9547
- } finally {
9548
- clearTimeout(timeout);
9779
+ {
9780
+ model: opts.model,
9781
+ system: [{ type: "text", text: systemPrompt }],
9782
+ messages: [{ role: "user", content: [{ type: "text", text: userPrompt }] }],
9783
+ maxTokens: 80,
9784
+ temperature: 0.3
9785
+ },
9786
+ { signal: signal.signal }
9787
+ );
9788
+ clearTimeout(timeout);
9789
+ const rawContent = resp.content;
9790
+ const text = Array.isArray(rawContent) ? rawContent[0]?.text ?? "" : typeof rawContent === "object" && rawContent !== null ? rawContent.text ?? "" : String(rawContent);
9791
+ const message = expectDefined5(text.trim().split("\n")[0]);
9792
+ if (message.length > 0 && message.length < 200) {
9793
+ return message;
9549
9794
  }
9550
- });
9795
+ } catch {
9796
+ }
9797
+ return "chore: update";
9551
9798
  }
9552
9799
 
9553
9800
  // src/services/mcp-management.ts
@@ -10412,17 +10659,21 @@ function stripQuotes(s) {
10412
10659
 
10413
10660
  // src/slash-commands/agent-improve.ts
10414
10661
  import {
10415
- buildConsolidationInstruction,
10416
- captureLearnedFromAgentOutput,
10662
+ captureLearnedFromAgentOutputDetailed as captureLearnedFromAgentOutputDetailed2,
10417
10663
  getProjectAgentLearnStats,
10418
- isConsolidated,
10664
+ listProjectSkillAugmentations,
10419
10665
  loadConsolidationMetadata,
10420
10666
  loadProjectAgentConfig as loadProjectAgentConfig2,
10421
10667
  loadProjectAgentIdentity,
10422
10668
  loadProjectAgentLearned,
10669
+ loadProjectSkillAugmentation as loadProjectSkillAugmentation2,
10423
10670
  loadRoleKnowledgeManifest,
10671
+ loadSkillAffinity,
10672
+ optimizeProjectAgentLearning,
10424
10673
  refreshProjectAgentIdentity,
10425
10674
  resetProjectAgentIdentity,
10675
+ resolveRoleSkillCandidates as resolveRoleSkillCandidates2,
10676
+ setSkillPinned,
10426
10677
  updateProjectAgentIdentity
10427
10678
  } from "@wrongstack/core/agent-catalog";
10428
10679
  import { color as color11 } from "@wrongstack/core/utils";
@@ -10440,7 +10691,7 @@ function buildAgentImproveCommand(opts) {
10440
10691
  return {
10441
10692
  name: "agent-improve",
10442
10693
  category: "Config",
10443
- description: "Manage project-custom agent identities: show, update, refresh or reset per-role overrides.",
10694
+ description: "Inspect and develop a roster agent for this project: learned directives, per-skill project addenda and the optimization pass.",
10444
10695
  help: [
10445
10696
  "Usage:",
10446
10697
  " /agent-improve [role] Show customization for a role (or all roles)",
@@ -10448,20 +10699,26 @@ function buildAgentImproveCommand(opts) {
10448
10699
  " /agent-improve [role] update Update identity + learned content",
10449
10700
  " /agent-improve [role] refresh Reset identity + learned to empty templates",
10450
10701
  " /agent-improve [role] capture Scan last output for ## LEARNED blocks and persist them",
10451
- " /agent-improve [role] consolidate Optimize raw learned entries into a consolidated document",
10702
+ " /agent-improve [role] optimize Distil learning into skill addenda + a consolidated doc",
10703
+ " /agent-improve [role] skills Show the role's skills, affinity and project addenda",
10704
+ " /agent-improve [role] skills <skill> Show one skill's project addendum",
10705
+ " /agent-improve [role] skills <skill> pin|unpin Always/never keep this skill loaded",
10452
10706
  " /agent-improve [role] reset Delete ALL custom files for this role",
10453
10707
  " /agent-improve * reset Delete ALL custom files for every role",
10454
10708
  "",
10455
10709
  "Use without arguments to see which roles have project-customizations.",
10456
10710
  "",
10457
- "Knowledge automation:",
10458
- " Agents output a ## LEARNED section in their response to persist",
10459
- " project-specific patterns. The runtime captures these automatically.",
10460
- ' Use "capture" to manually re-scan any text for LEARNED blocks.',
10461
- ' Use "consolidate" to synthesize all raw entries into a single',
10462
- " narrowly-scoped document that replaces raw entries in the agent prompt.",
10711
+ "Learning loop (runs on its own):",
10712
+ " Agents end a run with a ## LEARNED block, optionally tagged with the",
10713
+ " skill it refines: `## LEARNED [skill: testing]`. The runtime captures it,",
10714
+ " routes it to that skill, and a background pass distils it into that skill\u2019s",
10715
+ " project addendum \u2014 injected beneath the bundled skill body on the next spawn.",
10716
+ ' Capture also runs on failed tasks. Use "capture" to re-scan the last turn',
10717
+ ' by hand and "optimize" to force a distillation early.',
10463
10718
  "",
10464
- "Files live under: .wrongstack/agents/<role>/"
10719
+ "Files live under: .wrongstack/agents/<role>/ (committed; skills/affinity.json",
10720
+ "and archive/ stay local). Disable the automatic pass with",
10721
+ "fleet.learning.autoOptimize.enabled = false."
10465
10722
  ].join("\n"),
10466
10723
  async run(args) {
10467
10724
  const parts = args.trim().split(/\s+/).filter(Boolean);
@@ -10500,7 +10757,18 @@ ${lines.join("\n")}`;
10500
10757
  const idText = loadProjectAgentIdentity(role, projectRoot);
10501
10758
  const learned = loadProjectAgentLearned(role, projectRoot);
10502
10759
  const kn = loadRoleKnowledgeManifest(role, projectRoot);
10760
+ const developedSkills = listProjectSkillAugmentations(role, projectRoot);
10761
+ const stats = getProjectAgentLearnStats(role, projectRoot);
10503
10762
  const lines = [`${color11.bold(role)} project identity:`];
10763
+ if (developedSkills.length > 0) {
10764
+ lines.push(` skills developed: ${developedSkills.join(", ")}`);
10765
+ }
10766
+ if (stats.entryCount > 0) {
10767
+ const hitRate = stats.directiveHitRate === null ? "no directive exercised yet" : `${Math.round(stats.directiveHitRate * 100)}% hit rate over ${stats.appliedEntryCount} exercised`;
10768
+ lines.push(
10769
+ ` directives: ${stats.entryCount} buffered \xB7 ${hitRate} \xB7 ${stats.deadEntryCount} never used`
10770
+ );
10771
+ }
10504
10772
  if (cfg) lines.push(` config: ${JSON.stringify(cfg)}`);
10505
10773
  if (kn)
10506
10774
  lines.push(
@@ -10535,33 +10803,108 @@ Or edit files directly under .wrongstack/agents/${role}/`
10535
10803
  case "capture": {
10536
10804
  const storedOutput = opts.context?.meta["lastAgentOutput"];
10537
10805
  const recentOutput = typeof storedOutput === "string" ? storedOutput : "";
10538
- const captured = captureLearnedFromAgentOutput(recentOutput, role, projectRoot, true);
10539
- if (captured > 0) {
10540
- const msg = `Captured ${captured} learned item(s) for role "${role}". Use /agent-improve ${role} to see them.`;
10806
+ if (!recentOutput) {
10807
+ return {
10808
+ message: "No agent output recorded yet in this session. Run a turn first, then re-run capture."
10809
+ };
10810
+ }
10811
+ const result = captureLearnedFromAgentOutputDetailed2(
10812
+ recentOutput,
10813
+ role,
10814
+ projectRoot,
10815
+ true
10816
+ );
10817
+ if (result.captured > 0) {
10818
+ const routed = result.skills?.length ? ` Routed to skill(s): ${result.skills.join(", ")}.` : "";
10819
+ const msg = `Captured ${result.captured} learned item(s) for role "${role}".${routed} Use /agent-improve ${role} to see them.`;
10541
10820
  opts.renderer.write(msg);
10542
10821
  return { message: msg };
10543
10822
  }
10544
10823
  return {
10545
- message: `No ## LEARNED blocks found in recent output for role "${role}".`
10824
+ message: result.reason ?? `No ## LEARNED blocks found in recent output for role "${role}" (status: ${result.status}).`
10546
10825
  };
10547
10826
  }
10827
+ case "optimize":
10548
10828
  case "consolidate": {
10549
10829
  const stats = getProjectAgentLearnStats(role, projectRoot);
10550
10830
  if (stats.entryCount === 0) {
10551
- const msg = `No raw learned entries for role "${role}" to consolidate.`;
10831
+ const msg = `No raw learned entries for role "${role}" to optimize.`;
10552
10832
  opts.renderer.write(msg);
10553
10833
  return { message: msg };
10554
10834
  }
10555
- const { instruction } = buildConsolidationInstruction(role, projectRoot);
10556
- const prompt = `Optimize what the "${role}" agent has learned for its skills. ${instruction}`;
10557
- const consolidatedAlready = isConsolidated(role, projectRoot);
10558
- const meta = loadConsolidationMetadata(role, projectRoot);
10559
- const summary = `${color11.cyan(role)}: ${stats.entryCount} raw entries (${stats.totalBytes}B)` + (consolidatedAlready && meta ? ` \xB7 last consolidated ${meta.consolidatedAt.slice(0, 10)} (${meta.sourceBytes}B \u2192 ${meta.consolidatedBytes}B)` : " \xB7 no prior consolidation") + "\n\nSending consolidation instruction to the agent...";
10560
- opts.renderer.write(summary);
10561
- return {
10562
- message: summary,
10563
- runText: prompt
10564
- };
10835
+ const before = loadConsolidationMetadata(role, projectRoot);
10836
+ opts.renderer.write(
10837
+ `${color11.cyan(role)}: optimizing ${stats.entryCount} directive(s) (${stats.totalBytes}B)` + (before ? ` \xB7 last optimized ${before.consolidatedAt.slice(0, 10)}` : " \xB7 no prior optimization") + "\u2026"
10838
+ );
10839
+ const llm = opts.llmProvider && opts.llmModel ? { provider: opts.llmProvider, model: opts.llmModel } : void 0;
10840
+ const result = await optimizeProjectAgentLearning(role, projectRoot, {
10841
+ ...llm ? { llm } : {},
10842
+ trigger: "manual"
10843
+ });
10844
+ const skillNote = result.skills.length ? ` Skill addenda refreshed: ${result.skills.join(", ")}.` : "";
10845
+ switch (result.status) {
10846
+ case "optimized": {
10847
+ const after = loadConsolidationMetadata(role, projectRoot);
10848
+ const msg = `Optimized "${role}": ${result.rawEntryCount} directive(s) \u2192 ${after?.consolidatedBytes ?? 0}B consolidated.` + skillNote + (result.pruned ? " Raw buffer archived and reset." : "");
10849
+ opts.renderer.write(msg);
10850
+ return { message: msg };
10851
+ }
10852
+ case "empty-synthesis":
10853
+ return {
10854
+ message: `Model "${result.model}" returned an empty document; the existing knowledge for "${role}" was left untouched.${skillNote}`
10855
+ };
10856
+ case "failed":
10857
+ return { message: `Optimization failed for "${role}": ${result.error}${skillNote}` };
10858
+ case "no-llm":
10859
+ return {
10860
+ message: `No active model; routing the "${role}" consolidation through the agent.${skillNote}`,
10861
+ runText: `${result.instruction}
10862
+
10863
+ ---
10864
+
10865
+ When the document is ready, save it to .wrongstack/agents/${role}/consolidated.md \u2014 or re-run the optimization with a model configured so the runtime persists it for you.`
10866
+ };
10867
+ default:
10868
+ return { message: `Nothing to optimize for "${role}".` };
10869
+ }
10870
+ }
10871
+ case "skills": {
10872
+ const candidates = resolveRoleSkillCandidates2(role, projectRoot);
10873
+ const developed = listProjectSkillAugmentations(role, projectRoot);
10874
+ const affinity = loadSkillAffinity(role, projectRoot);
10875
+ const target = parts[2];
10876
+ if (target && parts[3]) {
10877
+ const skillAction = parts[3];
10878
+ if (skillAction !== "pin" && skillAction !== "unpin") {
10879
+ return { message: `Unknown skill action "${skillAction}". Use pin or unpin.` };
10880
+ }
10881
+ setSkillPinned(role, target, skillAction === "pin", projectRoot);
10882
+ const msg2 = `${skillAction === "pin" ? "Pinned" : "Unpinned"} skill "${target}" for role "${role}".`;
10883
+ opts.renderer.write(msg2);
10884
+ return { message: msg2 };
10885
+ }
10886
+ if (target) {
10887
+ const body = loadProjectSkillAugmentation2(role, target, projectRoot);
10888
+ const msg2 = body ? `${color11.bold(`${role} \xB7 ${target}`)}
10889
+
10890
+ ${body}` : `No project addendum for skill "${target}" on role "${role}" yet.`;
10891
+ opts.renderer.write(msg2);
10892
+ return { message: msg2 };
10893
+ }
10894
+ const skillLines = candidates.map((skill) => {
10895
+ const entry = affinity.entries[skill];
10896
+ const marks = [
10897
+ developed.includes(skill) ? "developed" : "",
10898
+ entry?.pinned ? "pinned" : "",
10899
+ entry?.learned ? `${entry.learned} learned` : "",
10900
+ entry ? `${entry.succeeded}\u2713/${entry.failed}\u2717 of ${entry.loaded} loads` : "unused"
10901
+ ].filter(Boolean);
10902
+ return ` ${color11.cyan(skill.padEnd(22))} ${marks.join(" \xB7 ")}`;
10903
+ });
10904
+ const msg = candidates.length ? `Skills for ${color11.bold(role)}:
10905
+ ${skillLines.join("\n")}` : `Role "${role}" has no curated skills.`;
10906
+ opts.renderer.write(msg);
10907
+ return { message: msg };
10565
10908
  }
10566
10909
  case "reset": {
10567
10910
  const removed = resetProjectAgentIdentity(role === "*" ? void 0 : role, projectRoot);
@@ -10577,7 +10920,7 @@ Or edit files directly under .wrongstack/agents/${role}/`
10577
10920
  }
10578
10921
  default:
10579
10922
  return {
10580
- message: `Unknown action "${action}". Use: show, update, refresh, capture, consolidate, reset, or reset-all.`
10923
+ message: `Unknown action "${action}". Use: show, update, refresh, capture, optimize, skills, reset, or reset-all.`
10581
10924
  };
10582
10925
  }
10583
10926
  }
@@ -11122,7 +11465,7 @@ function buildBrainCommand(opts) {
11122
11465
  const trimmed = args.trim();
11123
11466
  const [sub, ...rest] = trimmed.split(/\s+/);
11124
11467
  const subcommand = (sub ?? "").toLowerCase();
11125
- const applyPatch = async (patch, describe) => {
11468
+ const applyPatch = async (patch, describe2) => {
11126
11469
  if (!opts.brainRuntime) {
11127
11470
  const msg2 = "The Brain runtime is not available in this session.";
11128
11471
  opts.renderer.writeWarning(msg2);
@@ -11132,7 +11475,7 @@ function buildBrainCommand(opts) {
11132
11475
  const { snapshot, persisted } = opts.brainRuntime.apply(patch);
11133
11476
  const result = await persisted;
11134
11477
  const note = result.ok ? color14.dim(" \u2014 saved to the active profile config") : ` \u2014 applied live but NOT saved: ${result.error ?? "unknown error"}`;
11135
- const msg2 = `${describe(snapshot)}${note}`;
11478
+ const msg2 = `${describe2(snapshot)}${note}`;
11136
11479
  if (result.ok) opts.renderer.write(msg2);
11137
11480
  else opts.renderer.writeWarning(msg2);
11138
11481
  return { message: msg2 };
@@ -11941,6 +12284,7 @@ function buildBtwCommand(opts) {
11941
12284
  }
11942
12285
 
11943
12286
  // src/slash-commands/clear.ts
12287
+ import { resetCaptureWindows } from "@wrongstack/core/agent-catalog";
11944
12288
  import { createContextEvidenceState } from "@wrongstack/core/utils";
11945
12289
 
11946
12290
  // src/slash-commands/interrupt.ts
@@ -12040,6 +12384,7 @@ function buildClearCommand(opts) {
12040
12384
  if (opts.sessionStore) {
12041
12385
  await opts.sessionStore.clearHistory(ctx?.session.id ?? "");
12042
12386
  }
12387
+ resetCaptureWindows();
12043
12388
  opts.onClear?.();
12044
12389
  await opts.onNewSession?.();
12045
12390
  opts.renderer.clear();
@@ -12625,6 +12970,7 @@ var SOURCE_LABELS = {
12625
12970
  "leader-after-task": "leader-after-task (post-tool summary)",
12626
12971
  contributor: "contributor",
12627
12972
  ledger: "completed-work ledger",
12973
+ glossary: "project jargon dictionary",
12628
12974
  nextsteps: "next-steps gate",
12629
12975
  other: "other (untagged)"
12630
12976
  };
@@ -13436,6 +13782,8 @@ var KNOWN_TOP_LEVEL_KEYS = [
13436
13782
  "fallbackBridge",
13437
13783
  "fallbackProfiles",
13438
13784
  "fallbackAuto",
13785
+ "fallbackMaxLastResortCandidates",
13786
+ "fallbackStickiness",
13439
13787
  "hooks",
13440
13788
  "plugins",
13441
13789
  "pluginManager",
@@ -13581,6 +13929,37 @@ function diagnoseConfig(cfg, plugins = []) {
13581
13929
  }
13582
13930
  }
13583
13931
  }
13932
+ if ("fallbackMaxLastResortCandidates" in fixed) {
13933
+ const v = fixed["fallbackMaxLastResortCandidates"];
13934
+ const n = coerceNumber(v);
13935
+ if (n === void 0) {
13936
+ delete fixed["fallbackMaxLastResortCandidates"];
13937
+ findings.push({
13938
+ path: "fallbackMaxLastResortCandidates",
13939
+ problem: `expected a non-negative number, got ${JSON.stringify(v)}`,
13940
+ severity: "error",
13941
+ fix: "removed (built-in default of 12 applies)"
13942
+ });
13943
+ } else {
13944
+ const clamped = Math.max(0, Math.floor(n));
13945
+ if (clamped !== v) {
13946
+ fixed["fallbackMaxLastResortCandidates"] = clamped;
13947
+ findings.push({
13948
+ path: "fallbackMaxLastResortCandidates",
13949
+ problem: `expected a non-negative integer, got ${JSON.stringify(v)}`,
13950
+ severity: "error",
13951
+ fix: `set to ${clamped}`
13952
+ });
13953
+ } else if (clamped === 0) {
13954
+ findings.push({
13955
+ path: "fallbackMaxLastResortCandidates",
13956
+ problem: "is 0 \u2014 last-resort auto-discovery append is disabled",
13957
+ severity: "warning",
13958
+ fix: "no change (explicit user choice)"
13959
+ });
13960
+ }
13961
+ }
13962
+ }
13584
13963
  if ("fleet" in fixed) {
13585
13964
  if (!isPlainObject(fixed["fleet"])) {
13586
13965
  findings.push({
@@ -14412,6 +14791,9 @@ function buildFallbackCommand(opts) {
14412
14791
  const favorites = config.favoriteModels ?? [];
14413
14792
  const bridge = config.fallbackBridge?.trim();
14414
14793
  const auto = config.fallbackAuto !== false;
14794
+ const rawCap = config.fallbackMaxLastResortCandidates;
14795
+ const capValue = typeof rawCap === "number" && Number.isFinite(rawCap) && rawCap >= 0 ? Math.floor(rawCap) : 12;
14796
+ const capLabel = capValue === 0 ? color24.dim("disabled") : color24.green(String(capValue));
14415
14797
  const filteredReason = (ref) => refInactiveReason(ref, config);
14416
14798
  const lines = [
14417
14799
  `${color24.bold("WrongStack")} ${color24.dim("\u2014 Fallback chain")}`,
@@ -14449,6 +14831,7 @@ function buildFallbackCommand(opts) {
14449
14831
  "",
14450
14832
  ` ${color24.bold("auto")} ${auto ? color24.green("on") : color24.dim("off")} ${color24.dim("/fallback auto on|off")}`,
14451
14833
  ` ${color24.bold("favorites only")} ${config.favoriteModelsOnly ? color24.green("on") : color24.dim("off")} ${color24.dim("/fallback fav only on|off")}`,
14834
+ ` ${color24.bold("last-resort cap")} ${capLabel} ${color24.dim("(max auto-discovered models appended, 0=disabled)")}`,
14452
14835
  "",
14453
14836
  ` ${color24.bold("profiles")} ${Object.keys(profiles).length ? "" : color24.dim("(none)")}`,
14454
14837
  ...Object.entries(profiles).sort(([a], [b]) => a.localeCompare(b)).flatMap(([name, chain]) => {
@@ -17037,7 +17420,6 @@ function buildIntakeCommand(opts) {
17037
17420
  import { color as color32 } from "@wrongstack/core/utils";
17038
17421
  import {
17039
17422
  addCheckToTask,
17040
- addColumn,
17041
17423
  addDependency,
17042
17424
  addGoalMetricToTask,
17043
17425
  addNoteToTask,
@@ -17063,7 +17445,6 @@ import {
17063
17445
  parseLinesIntoTasks,
17064
17446
  releaseTaskClaim,
17065
17447
  removeBoard,
17066
- removeColumn,
17067
17448
  removeTask,
17068
17449
  setTaskChain,
17069
17450
  splitTask,
@@ -17196,6 +17577,8 @@ function stripQuotes2(value) {
17196
17577
  import { color as color31 } from "@wrongstack/core/utils";
17197
17578
  import {
17198
17579
  areDependenciesMet,
17580
+ evaluateContractGraph,
17581
+ evaluateContractGraphReadiness,
17199
17582
  findBlockedTasks
17200
17583
  } from "@wrongstack/kanban";
17201
17584
  var HEADING = (s) => color31.bold(s);
@@ -17351,6 +17734,19 @@ function formatTaskDetail(board, task) {
17351
17734
  if (metric.notes) lines.push(` ${DIM(metric.notes)}`);
17352
17735
  }
17353
17736
  }
17737
+ const readiness = evaluateContractGraphReadiness(board, task.id);
17738
+ const completion = evaluateContractGraph(board, task.id);
17739
+ lines.push("");
17740
+ lines.push(HEADING(" Contract Map:"));
17741
+ lines.push(
17742
+ ` ${readiness.ready ? color31.green("\u25CF Start ready") : color31.yellow(`\u25B3 ${readiness.issues.length} setup gaps`)} \xB7 ${completion.issues.length === 0 ? color31.green("closed") : color31.yellow(`${completion.issues.length} completion open`)}`
17743
+ );
17744
+ for (const issue of readiness.issues.slice(0, 5)) {
17745
+ lines.push(` ${color31.yellow("!")} ${issue.message}`);
17746
+ }
17747
+ if (readiness.issues.length > 5) {
17748
+ lines.push(` ${DIM(`+${readiness.issues.length - 5} more setup gaps`)}`);
17749
+ }
17354
17750
  if (task.estimatedHours || task.actualHours) {
17355
17751
  lines.push("");
17356
17752
  lines.push(
@@ -17476,9 +17872,6 @@ var KANBAN_COMMAND_HELP = [
17476
17872
  "",
17477
17873
  " /kanban task check add <boardId> <taskId> <desc> Add success check",
17478
17874
  "",
17479
- " /kanban column add <boardId> <title> Add column",
17480
- " /kanban column rm <boardId> <colId> Remove column",
17481
- "",
17482
17875
  " /kanban generate <description> Auto-generate board from text",
17483
17876
  " /kanban export <boardId> Export board as markdown",
17484
17877
  " /kanban graph export <boardId> [graphId] Save board as SDD TaskGraph",
@@ -17654,9 +18047,6 @@ function buildKanbanCommand(opts) {
17654
18047
  if (cmd === "task" || cmd === "t") {
17655
18048
  return handleTaskSubcommand(opts, projectRoot, rest, showHelp);
17656
18049
  }
17657
- if (cmd === "column" || cmd === "col" || cmd === "c") {
17658
- return handleColumnSubcommand(projectRoot, rest, showHelp);
17659
- }
17660
18050
  return {
17661
18051
  message: unknownSubcommand(
17662
18052
  cmd,
@@ -18336,27 +18726,6 @@ ${summary}` };
18336
18726
  }
18337
18727
  return showHelp();
18338
18728
  }
18339
- async function handleColumnSubcommand(projectRoot, args, showHelp) {
18340
- const [sub, boardId, ...rest] = args;
18341
- if (!sub || !boardId) return showHelp();
18342
- if (sub === "add") {
18343
- const title = rest.join(" ");
18344
- if (!title) return { message: color32.red("Usage: /kanban column add <boardId> <title>") };
18345
- const result = await addColumn(projectRoot, boardId, { title });
18346
- if (!result) return { message: color32.red(`Board not found: ${boardId}`) };
18347
- return { message: color32.green(`\u2705 Column added: ${result.column.title}`) };
18348
- }
18349
- if (sub === "rm" || sub === "remove" || sub === "delete") {
18350
- const colId = rest[0];
18351
- if (!colId) return { message: color32.red("Usage: /kanban column rm <boardId> <columnId>") };
18352
- const updated = await removeColumn(projectRoot, boardId, colId, {
18353
- moveTasksToColumnId: rest[1]
18354
- });
18355
- if (!updated) return { message: color32.red(`Column not found: ${colId}`) };
18356
- return { message: color32.green(`\u2705 Column removed: ${colId}`) };
18357
- }
18358
- return showHelp();
18359
- }
18360
18729
  function extractSpawnedSubagentId(summary) {
18361
18730
  return summary.match(/Spawned subagent\s+([^\s]+)/)?.[1];
18362
18731
  }
@@ -18374,12 +18743,6 @@ function formatLifecycleDiagnosis(err, action) {
18374
18743
  if (field === "assignee") {
18375
18744
  return `\u274C /kanban task ${action} needs an assignee. Run \`/kanban task assign <boardId> <taskId> <agent>\` first.`;
18376
18745
  }
18377
- if (field === "dueDate") {
18378
- return `\u274C /kanban task ${action} needs a valid due date. Add one via the \`kanban.update_task\` tool action (patch.dueDate = "YYYY-MM-DD").`;
18379
- }
18380
- if (field === "labels") {
18381
- return `\u274C /kanban task ${action} needs at least one label. Add one via the \`kanban.update_task\` tool action (patch.labels = ["..."]).`;
18382
- }
18383
18746
  if (field === "childTaskIds") {
18384
18747
  return `\u274C /kanban task ${action} is an atomic parent with no persisted children. Decompose it via the kanban tool (\`split_atomic\`) before moving it forward.`;
18385
18748
  }
@@ -19977,7 +20340,11 @@ function formatSageShow(stats, memories) {
19977
20340
 
19978
20341
  // src/slash-commands/memory-triage.ts
19979
20342
  import { toErrorMessage as toErrorMessage17 } from "@wrongstack/core/utils";
19980
- import { formatTriageReport, runTriage } from "@wrongstack/sage";
20343
+ import {
20344
+ fileTriageProposals,
20345
+ formatTriageReport,
20346
+ runTriage
20347
+ } from "@wrongstack/sage";
19981
20348
  var DEFAULT_MAX_PHASE3 = 1e3;
19982
20349
  var DEFAULT_MAX_PHASE4_PAIRS = 50;
19983
20350
  async function runTriageCommand(opts, args) {
@@ -20174,41 +20541,7 @@ async function applyDispatch(Sage, report) {
20174
20541
  return lines.join("\n");
20175
20542
  }
20176
20543
  async function fileProposals(Sage, proposals) {
20177
- const inputs = [];
20178
- const failures = [];
20179
- let filed = 0;
20180
- for (const proposal of proposals) {
20181
- const input = {
20182
- text: proposal.memoryText,
20183
- targetMemoryId: proposal.memoryId,
20184
- reviewReason: proposal.reason,
20185
- suggestedAction: proposal.suggestedAction,
20186
- kind: "memory_review",
20187
- scope: "project",
20188
- importance: 0.5,
20189
- confidence: 0.9,
20190
- tags: ["triage"],
20191
- anchors: [],
20192
- sources: [{ type: "project_instruction" }]
20193
- };
20194
- inputs.push(input);
20195
- try {
20196
- await Sage.createCandidate(input);
20197
- filed++;
20198
- } catch (err) {
20199
- failures.push({
20200
- memoryId: proposal.memoryId,
20201
- error: toErrorMessage17(err)
20202
- });
20203
- }
20204
- }
20205
- return {
20206
- filed,
20207
- failed: failures.length,
20208
- total: proposals.length,
20209
- failures,
20210
- inputs
20211
- };
20544
+ return fileTriageProposals(Sage, proposals);
20212
20545
  }
20213
20546
 
20214
20547
  // src/slash-commands/memory.ts
@@ -21940,6 +22273,7 @@ import {
21940
22273
  setPlanItemStatus
21941
22274
  } from "@wrongstack/core/storage";
21942
22275
  import { formatTaskList, formatTodosList } from "@wrongstack/core/utils";
22276
+ import { todoTool } from "@wrongstack/tools";
21943
22277
  function findPlanItemIndex(plan, query) {
21944
22278
  const asNum = Number.parseInt(query, 10);
21945
22279
  if (!Number.isNaN(asNum) && asNum >= 1 && asNum <= plan.items.length) return asNum - 1;
@@ -22043,6 +22377,7 @@ ${formatTaskList(taskFile.tasks)}`,
22043
22377
  let outputMessage = "";
22044
22378
  let outputError;
22045
22379
  let outputExtra = {};
22380
+ let todosToReplace = null;
22046
22381
  const finalPlan = await mutatePlan(planPath, sessionId, async (plan) => {
22047
22382
  switch (verb) {
22048
22383
  case "add": {
@@ -22087,6 +22422,18 @@ ${formatPlan(updated)}`;
22087
22422
  outputError = { code: "usage", message: outputMessage };
22088
22423
  return plan;
22089
22424
  }
22425
+ const itemIdx = findPlanItemIndex(plan, restJoined);
22426
+ const item = itemIdx >= 0 ? plan.items[itemIdx] : void 0;
22427
+ if (!item) {
22428
+ outputMessage = `No plan item matched "${restJoined}".`;
22429
+ outputError = { code: "item_not_found", message: outputMessage };
22430
+ return plan;
22431
+ }
22432
+ if (item.status !== "done") {
22433
+ outputMessage = `Plan item "${item.title}" is unfinished and cannot be removed. Complete it first.`;
22434
+ outputError = { code: "unfinished_item", message: outputMessage };
22435
+ return plan;
22436
+ }
22090
22437
  const updated = removePlanItem(plan, restJoined);
22091
22438
  outputMessage = formatPlan(updated);
22092
22439
  return updated;
@@ -22114,7 +22461,7 @@ ${formatPlan(updated)}`;
22114
22461
  outputError = { code: "item_not_found", message: outputMessage };
22115
22462
  return plan;
22116
22463
  }
22117
- opts.context?.state?.replaceTodos(derived.todos);
22464
+ todosToReplace = derived.todos;
22118
22465
  const label = verb === "derive" ? "Derived" : "Promoted to";
22119
22466
  outputMessage = `${label} ${derived.todos.length} todo(s):
22120
22467
  ${formatTodosList(derived.todos)}
@@ -22152,6 +22499,12 @@ ${formatPlan(updated)}`;
22152
22499
  return plan;
22153
22500
  }
22154
22501
  case "clear": {
22502
+ const unfinished = plan.items.filter((item) => item.status !== "done");
22503
+ if (unfinished.length > 0) {
22504
+ outputMessage = `Plan contains unfinished items and cannot be cleared: ${unfinished.map((item) => item.id).join(", ")}.`;
22505
+ outputError = { code: "unfinished_items", message: outputMessage };
22506
+ return plan;
22507
+ }
22155
22508
  const updated = clearPlan(plan);
22156
22509
  outputMessage = "Plan cleared.";
22157
22510
  return updated;
@@ -22162,6 +22515,11 @@ ${formatPlan(updated)}`;
22162
22515
  return plan;
22163
22516
  }
22164
22517
  });
22518
+ if (todosToReplace && opts.context) {
22519
+ await todoTool.execute({ todos: todosToReplace }, opts.context, {
22520
+ signal: AbortSignal.timeout(3e4)
22521
+ });
22522
+ }
22165
22523
  const payload = outputError ? { ok: false, plan: finalPlan, error: outputError } : { ok: true, plan: finalPlan, ...outputExtra };
22166
22524
  return result(outputMessage, payload, json);
22167
22525
  }
@@ -25287,6 +25645,134 @@ function buildWebuiCommand() {
25287
25645
  };
25288
25646
  }
25289
25647
 
25648
+ // src/slash-commands/theme.ts
25649
+ import { color as color46, writeOut as writeOut3 } from "@wrongstack/core/utils";
25650
+ var THEME_OPTIONS = [
25651
+ { id: "catppuccin", name: "Catppuccin Mocha", desc: "Soft pastel dark theme (Default)" },
25652
+ { id: "tokyo-night", name: "Tokyo Night", desc: "Deep violet, neon orange & cyan" },
25653
+ { id: "nord", name: "Nord", desc: "Cool arctic blue & slate pastels" },
25654
+ { id: "cyberpunk", name: "Cyberpunk Neon", desc: "High-contrast magenta, cyan & yellow" },
25655
+ { id: "dracula", name: "Dracula", desc: "Vibrant purple, pink & green" },
25656
+ { id: "gruvbox-dark", name: "Gruvbox Dark", desc: "Warm earthy retro \u2014 orange, olive & aqua" },
25657
+ { id: "solarized-dark", name: "Solarized Dark", desc: "Base16 classic \u2014 teal & ochre precision" },
25658
+ { id: "one-dark", name: "One Dark", desc: "Atom's iconic palette \u2014 blue-led, warm accents" },
25659
+ { id: "monokai", name: "Monokai", desc: "Sublime classic \u2014 vivid magenta, cyan & lime" },
25660
+ { id: "rose-pine", name: "Ros\xE9 Pine", desc: "Aesthetic pine & foam \u2014 soft evening pastels" },
25661
+ { id: "kanagawa", name: "Kanagawa", desc: "Hokusai-inspired waves \u2014 sumi ink on washi" },
25662
+ { id: "ayu-dark", name: "Ayu Dark", desc: "Simple pleasant dark \u2014 warm orange + cool blue" },
25663
+ { id: "everforest", name: "Everforest", desc: "Green-based comfort \u2014 forest greens & warm tans" },
25664
+ { id: "night-owl", name: "Night Owl", desc: "Sarah Drasner's night \u2014 deep navy + bold accents" },
25665
+ { id: "synthwave", name: "Synthwave '84", desc: "Hot pink + neon cyan on deep purple" }
25666
+ ];
25667
+ async function runThemePicker(reader, activeId) {
25668
+ let cursor = THEME_OPTIONS.findIndex((p) => p.id === activeId);
25669
+ if (cursor < 0) cursor = 0;
25670
+ const render = (currentCursor) => {
25671
+ const lines = [];
25672
+ lines.push(`
25673
+ ${color46.bold(color46.amber("WrongStack") + color46.dim(" \u2014 TUI Theme Selection"))}
25674
+ `);
25675
+ lines.push(color46.dim(" \u2191\u2193 navigate Enter select q quit\n"));
25676
+ lines.push("");
25677
+ for (const [i, p] of THEME_OPTIONS.entries()) {
25678
+ const mark = p.id === activeId ? color46.green(" [active]") : "";
25679
+ const prefix = i === currentCursor ? color46.bold("\u276F ") : " ";
25680
+ const name = i === currentCursor ? color46.bold(p.name) : p.name;
25681
+ lines.push(` ${prefix}${name.padEnd(18)} ${color46.dim(p.desc)}${mark}`);
25682
+ }
25683
+ lines.push("");
25684
+ return lines.join("\n");
25685
+ };
25686
+ writeOut3(render(cursor));
25687
+ const options = [
25688
+ { key: "\x1B[A", label: "\u2191", value: "up" },
25689
+ { key: "\x1B[B", label: "\u2193", value: "down" },
25690
+ { key: "\r", label: "Enter", value: "enter" },
25691
+ { key: "q", label: "q", value: "quit" }
25692
+ ];
25693
+ while (true) {
25694
+ const answer = await reader.readKey("", options);
25695
+ if (answer === "quit") return void 0;
25696
+ if (answer === "enter") return THEME_OPTIONS[cursor]?.id ?? THEME_OPTIONS[0].id;
25697
+ if (answer === "up") {
25698
+ cursor = cursor > 0 ? cursor - 1 : THEME_OPTIONS.length - 1;
25699
+ } else if (answer === "down") {
25700
+ cursor = cursor < THEME_OPTIONS.length - 1 ? cursor + 1 : 0;
25701
+ }
25702
+ writeOut3("\x1B[J");
25703
+ writeOut3(render(cursor));
25704
+ }
25705
+ }
25706
+ function buildThemeCommand(opts) {
25707
+ return {
25708
+ name: "theme",
25709
+ category: "Config",
25710
+ description: "Switch or select the TUI color theme preset interactively",
25711
+ argsHint: "[catppuccin | tokyo-night | nord | cyberpunk | dracula | gruvbox-dark | solarized-dark | one-dark | monokai | rose-pine | kanagawa | ayu-dark | everforest | night-owl | synthwave]",
25712
+ help: [
25713
+ "Usage:",
25714
+ " /theme Interactive menu selection or view available theme presets",
25715
+ " /theme <preset> Switch directly to a theme preset",
25716
+ "",
25717
+ "Available presets:",
25718
+ " catppuccin, tokyo-night, nord, cyberpunk, dracula,",
25719
+ " gruvbox-dark, solarized-dark, one-dark, monokai, rose-pine,",
25720
+ " kanagawa, ayu-dark, everforest, night-owl, synthwave"
25721
+ ].join("\n"),
25722
+ async run(args) {
25723
+ const validPresets = THEME_OPTIONS.map((t) => t.id);
25724
+ const currentConfig = opts.configStore?.get();
25725
+ const activePreset = currentConfig?.themePreset ?? "catppuccin";
25726
+ if (!args.trim()) {
25727
+ if (opts.inputReader) {
25728
+ const selected = await runThemePicker(opts.inputReader, activePreset);
25729
+ if (!selected) {
25730
+ return { message: "Theme selection cancelled." };
25731
+ }
25732
+ if (opts.configStore) {
25733
+ try {
25734
+ opts.configStore.update({ themePreset: selected });
25735
+ } catch {
25736
+ }
25737
+ }
25738
+ return {
25739
+ message: color46.green(`Switched TUI theme preset to "${selected}" (saved to config).`),
25740
+ metadata: { themePreset: selected }
25741
+ };
25742
+ }
25743
+ const lines = [
25744
+ color46.bold(`Current TUI theme: ${activePreset}`),
25745
+ "",
25746
+ color46.bold("Available Theme Presets:")
25747
+ ];
25748
+ for (const t of THEME_OPTIONS) {
25749
+ const mark = t.id === activePreset ? color46.green(" [active]") : "";
25750
+ lines.push(` \u2022 ${color46.bold(t.id.padEnd(14))} ${t.desc}${mark}`);
25751
+ }
25752
+ lines.push("");
25753
+ lines.push(color46.dim("Usage: /theme <preset>"));
25754
+ return { message: lines.join("\n") };
25755
+ }
25756
+ const preset = args.trim().toLowerCase();
25757
+ if (!validPresets.includes(preset)) {
25758
+ return {
25759
+ message: `Unknown theme preset "${preset}". Available options: ${validPresets.join(", ")}`
25760
+ };
25761
+ }
25762
+ if (opts.configStore) {
25763
+ try {
25764
+ opts.configStore.update({ themePreset: preset });
25765
+ } catch {
25766
+ }
25767
+ }
25768
+ return {
25769
+ message: color46.green(`Switched TUI theme preset to "${preset}" (saved to config).`),
25770
+ metadata: { themePreset: preset }
25771
+ };
25772
+ }
25773
+ };
25774
+ }
25775
+
25290
25776
  // src/slash-commands/agents.ts
25291
25777
  import { noOpVault as noOpVault7 } from "@wrongstack/core/security";
25292
25778
  function formatAgentLine(a) {
@@ -25528,7 +26014,7 @@ ${lines.join("\n")}` };
25528
26014
  // src/slash-commands/hq.ts
25529
26015
  import { readHqRuntimeFileSync, resolveHqConfig, resolveHqDataDir } from "@wrongstack/core/hq";
25530
26016
  import { noOpVault as noOpVault8 } from "@wrongstack/core/security";
25531
- import { color as color46 } from "@wrongstack/core/utils";
26017
+ import { color as color47 } from "@wrongstack/core/utils";
25532
26018
  function maskToken(t) {
25533
26019
  if (t.length <= 10) return `${t.slice(0, 2)}\u2026(${t.length})`;
25534
26020
  return `${t.slice(0, 6)}\u2026${t.slice(-4)} (${t.length} chars)`;
@@ -25539,12 +26025,12 @@ async function probeHq(url) {
25539
26025
  const timer = setTimeout(() => ctrl.abort(), 2500);
25540
26026
  const res = await fetch(url, { signal: ctrl.signal, redirect: "manual" }).catch(() => null);
25541
26027
  clearTimeout(timer);
25542
- if (!res) return color46.red("unreachable");
25543
- if (res.ok) return color46.green("reachable");
25544
- if (res.status === 401) return color46.green("reachable") + color46.dim(" (token required)");
25545
- return color46.amber(`reachable (HTTP ${res.status})`);
26028
+ if (!res) return color47.red("unreachable");
26029
+ if (res.ok) return color47.green("reachable");
26030
+ if (res.status === 401) return color47.green("reachable") + color47.dim(" (token required)");
26031
+ return color47.amber(`reachable (HTTP ${res.status})`);
25546
26032
  } catch {
25547
- return color46.red("unreachable");
26033
+ return color47.red("unreachable");
25548
26034
  }
25549
26035
  }
25550
26036
  function buildHqCommand(opts) {
@@ -25578,7 +26064,7 @@ function buildHqCommand(opts) {
25578
26064
  const { cmd, rest } = parseSubcommand(args);
25579
26065
  const sub = cmd;
25580
26066
  if (!opts.configStore || !opts.paths) {
25581
- return { message: `${color46.red("\u2717")} HQ config is unavailable in this surface.` };
26067
+ return { message: `${color47.red("\u2717")} HQ config is unavailable in this surface.` };
25582
26068
  }
25583
26069
  const persistDeps = {
25584
26070
  configStore: opts.configStore,
@@ -25592,14 +26078,14 @@ function buildHqCommand(opts) {
25592
26078
  const url = (rest[0] ?? "").trim();
25593
26079
  const token = rest.slice(1).join(" ").trim();
25594
26080
  if (!url) {
25595
- return { message: `${color46.amber("Usage:")} /hq set <http://host:3499> [client-token]` };
26081
+ return { message: `${color47.amber("Usage:")} /hq set <http://host:3499> [client-token]` };
25596
26082
  }
25597
26083
  try {
25598
26084
  const parsed = new URL(url);
25599
26085
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("proto");
25600
26086
  } catch {
25601
26087
  return {
25602
- message: `${color46.red("Invalid URL:")} ${url} ${color46.dim("(expected http://host:3499)")}`
26088
+ message: `${color47.red("Invalid URL:")} ${url} ${color47.dim("(expected http://host:3499)")}`
25603
26089
  };
25604
26090
  }
25605
26091
  await persistConfigSetting(persistDeps, (cfg) => {
@@ -25611,16 +26097,16 @@ function buildHqCommand(opts) {
25611
26097
  });
25612
26098
  const reach = await probeHq(url);
25613
26099
  const tokLine = token ? `
25614
- token: ${color46.dim(maskToken(token))}` : "";
26100
+ token: ${color47.dim(maskToken(token))}` : "";
25615
26101
  return {
25616
- message: `${color46.green("\u2713")} HQ set \u2192 ${color46.cyan(url)}${tokLine}
26102
+ message: `${color47.green("\u2713")} HQ set \u2192 ${color47.cyan(url)}${tokLine}
25617
26103
  status: ${reach}
25618
- ${color46.dim("Connects on the next session start.")}`
26104
+ ${color47.dim("Connects on the next session start.")}`
25619
26105
  };
25620
26106
  }
25621
26107
  if (sub === "token") {
25622
26108
  const token = rest.join(" ").trim();
25623
- if (!token) return { message: `${color46.amber("Usage:")} /hq token <client-token>` };
26109
+ if (!token) return { message: `${color47.amber("Usage:")} /hq token <client-token>` };
25624
26110
  await persistConfigSetting(persistDeps, (cfg) => {
25625
26111
  const hq = cfg.hq ?? {};
25626
26112
  hq.token = token;
@@ -25628,14 +26114,14 @@ function buildHqCommand(opts) {
25628
26114
  cfg.hq = hq;
25629
26115
  });
25630
26116
  return {
25631
- message: `${color46.green("\u2713")} HQ client token saved ${color46.dim(maskToken(token))}`
26117
+ message: `${color47.green("\u2713")} HQ client token saved ${color47.dim(maskToken(token))}`
25632
26118
  };
25633
26119
  }
25634
26120
  if (sub === "raw") {
25635
26121
  const mode = (rest[0] ?? "").trim().toLowerCase();
25636
26122
  if (mode !== "on" && mode !== "off") {
25637
26123
  return {
25638
- message: `${color46.amber("Usage:")} /hq raw on|off ${color46.dim("(publish raw chat/tool content to HQ)")}`
26124
+ message: `${color47.amber("Usage:")} /hq raw on|off ${color47.dim("(publish raw chat/tool content to HQ)")}`
25639
26125
  };
25640
26126
  }
25641
26127
  const on = mode === "on";
@@ -25645,8 +26131,8 @@ function buildHqCommand(opts) {
25645
26131
  cfg.hq = hq;
25646
26132
  });
25647
26133
  return {
25648
- message: `${color46.green("\u2713")} HQ raw content \u2192 ${on ? color46.amber("on (unredacted)") : color46.dim("off (redacted)")}
25649
- ${color46.dim("Applies to sessions started after this change. Only enable for HQ servers you trust.")}`
26134
+ message: `${color47.green("\u2713")} HQ raw content \u2192 ${on ? color47.amber("on (unredacted)") : color47.dim("off (redacted)")}
26135
+ ${color47.dim("Applies to sessions started after this change. Only enable for HQ servers you trust.")}`
25650
26136
  };
25651
26137
  }
25652
26138
  if (sub === "on" || sub === "off") {
@@ -25657,29 +26143,29 @@ function buildHqCommand(opts) {
25657
26143
  cfg.hq = hq;
25658
26144
  });
25659
26145
  return {
25660
- message: `${color46.green("\u2713")} HQ publishing \u2192 ${on ? color46.cyan("on") : color46.dim("off")}`
26146
+ message: `${color47.green("\u2713")} HQ publishing \u2192 ${on ? color47.cyan("on") : color47.dim("off")}`
25661
26147
  };
25662
26148
  }
25663
26149
  if (sub === "clear") {
25664
26150
  await persistConfigSetting(persistDeps, (cfg) => {
25665
26151
  delete cfg.hq;
25666
26152
  });
25667
- return { message: `${color46.green("\u2713")} HQ configuration cleared.` };
26153
+ return { message: `${color47.green("\u2713")} HQ configuration cleared.` };
25668
26154
  }
25669
26155
  if (sub === "" || sub === "status") {
25670
26156
  const dataDir = resolveHqDataDir(currentHq?.dataDir);
25671
26157
  const runtime = readHqRuntimeFileSync(dataDir);
25672
26158
  const resolved = resolveHqConfig({ config: currentHq });
25673
- const lines = [`${color46.bold("\u{1F4CB} WrongStack HQ \u2014 connection")}`, ""];
26159
+ const lines = [`${color47.bold("\u{1F4CB} WrongStack HQ \u2014 connection")}`, ""];
25674
26160
  if (!resolved) {
25675
26161
  lines.push(
25676
- ` ${color46.dim("Not configured.")} Use ${color46.cyan("/hq set <url> [token]")} to connect,`
26162
+ ` ${color47.dim("Not configured.")} Use ${color47.cyan("/hq set <url> [token]")} to connect,`
25677
26163
  );
25678
- lines.push(` or run ${color46.cyan("wstack --hq")} locally (auto-discovered).`);
26164
+ lines.push(` or run ${color47.cyan("wstack --hq")} locally (auto-discovered).`);
25679
26165
  if (runtime) {
25680
26166
  lines.push("");
25681
26167
  lines.push(
25682
- ` ${color46.green("A local HQ is running")} at ${color46.cyan(runtime.url)} ${color46.dim("(start a new session to attach)")}`
26168
+ ` ${color47.green("A local HQ is running")} at ${color47.cyan(runtime.url)} ${color47.dim("(start a new session to attach)")}`
25683
26169
  );
25684
26170
  }
25685
26171
  const message = lines.join("\n");
@@ -25687,35 +26173,35 @@ function buildHqCommand(opts) {
25687
26173
  }
25688
26174
  if (resolved.discover && !runtime) {
25689
26175
  lines.push(
25690
- ` mode: ${color46.cyan("auto-discovery")} ${color46.dim("(no local HQ running yet)")}`
26176
+ ` mode: ${color47.cyan("auto-discovery")} ${color47.dim("(no local HQ running yet)")}`
25691
26177
  );
25692
26178
  lines.push(
25693
- ` ${color46.dim("This session will attach automatically when `wstack --hq` starts")}`
26179
+ ` ${color47.dim("This session will attach automatically when `wstack --hq` starts")}`
25694
26180
  );
25695
- lines.push(` ${color46.dim(`on this machine (watching ${dataDir}).`)}`);
25696
- lines.push(` ${color46.dim("Disable with WRONGSTACK_HQ_ENABLED=0 or /hq off.")}`);
26181
+ lines.push(` ${color47.dim(`on this machine (watching ${dataDir}).`)}`);
26182
+ lines.push(` ${color47.dim("Disable with WRONGSTACK_HQ_ENABLED=0 or /hq off.")}`);
25697
26183
  return { message: lines.join("\n") };
25698
26184
  }
25699
26185
  const source = process.env["WRONGSTACK_HQ_URL"] ? "WRONGSTACK_HQ_URL env" : currentHq?.url ? "config.json" : runtime ? `local HQ marker (pid ${runtime.pid ?? "?"})` : "default";
25700
- lines.push(` url: ${color46.cyan(resolved.url)}`);
26186
+ lines.push(` url: ${color47.cyan(resolved.url)}`);
25701
26187
  lines.push(
25702
- ` enabled: ${resolved.enabled === false ? color46.dim("false") : color46.green("true")}`
26188
+ ` enabled: ${resolved.enabled === false ? color47.dim("false") : color47.green("true")}`
25703
26189
  );
25704
- if (resolved.discover) lines.push(` mode: ${color46.cyan("auto-discovery")}`);
25705
- lines.push(` source: ${color46.dim(source)}`);
26190
+ if (resolved.discover) lines.push(` mode: ${color47.cyan("auto-discovery")}`);
26191
+ lines.push(` source: ${color47.dim(source)}`);
25706
26192
  lines.push(
25707
- ` token: ${resolved.token ? color46.dim(maskToken(resolved.token)) : color46.dim("none (open mode)")}`
26193
+ ` token: ${resolved.token ? color47.dim(maskToken(resolved.token)) : color47.dim("none (open mode)")}`
25708
26194
  );
25709
26195
  lines.push(
25710
- ` content: ${resolved.rawContent === true ? color46.amber("raw (unredacted)") : color46.dim("redacted \u2014 explicitly disabled; enable with /hq raw on")}`
26196
+ ` content: ${resolved.rawContent === true ? color47.amber("raw (unredacted)") : color47.dim("redacted \u2014 explicitly disabled; enable with /hq raw on")}`
25711
26197
  );
25712
- if (resolved.projectAlias) lines.push(` alias: ${color46.cyan(resolved.projectAlias)}`);
26198
+ if (resolved.projectAlias) lines.push(` alias: ${color47.cyan(resolved.projectAlias)}`);
25713
26199
  lines.push(` status: ${await probeHq(resolved.url)}`);
25714
26200
  return { message: lines.join("\n") };
25715
26201
  }
25716
26202
  return {
25717
- message: `${color46.red("Unknown subcommand:")} ${sub}
25718
- ${color46.dim("Try /hq, /hq set <url> [token], /hq raw on|off, /hq on|off, /hq clear, or /help hq")}`
26203
+ message: `${color47.red("Unknown subcommand:")} ${sub}
26204
+ ${color47.dim("Try /hq, /hq set <url> [token], /hq raw on|off, /hq on|off, /hq clear, or /help hq")}`
25719
26205
  };
25720
26206
  }
25721
26207
  };
@@ -25762,7 +26248,7 @@ import * as fs17 from "node:fs/promises";
25762
26248
  import { createRequire } from "node:module";
25763
26249
  import * as path26 from "node:path";
25764
26250
  import {
25765
- color as color47,
26251
+ color as color48,
25766
26252
  ensureProjectGitignore,
25767
26253
  ensureProjectIdentity as ensureProjectIdentity2,
25768
26254
  projectIdentityPath,
@@ -25895,17 +26381,17 @@ async function projectIdentityCommand(opts, ctx, action, confirmed) {
25895
26381
  if (action === "show") {
25896
26382
  const identity = await readProjectIdentity(root);
25897
26383
  return identity ? {
25898
- message: `${color47.bold("Project ID:")} ${color47.cyan(identity.projectId)}
25899
- ${color47.dim(filePath)}`
25900
- } : { message: `No committed project identity. Run ${color47.cyan("/project init")}.` };
26384
+ message: `${color48.bold("Project ID:")} ${color48.cyan(identity.projectId)}
26385
+ ${color48.dim(filePath)}`
26386
+ } : { message: `No committed project identity. Run ${color48.cyan("/project init")}.` };
25901
26387
  }
25902
26388
  if (action === "init") {
25903
26389
  const result2 = await ensureProjectIdentity2(root);
25904
26390
  await ensureProjectGitignore(root);
25905
26391
  return {
25906
- message: result2.created ? `${color47.green("Created")} ${filePath}
25907
- ${color47.cyan(result2.identity.projectId)}
25908
- ${color47.dim("Commit this file so every clone and machine shares the same HQ project.")}` : `${color47.dim("Project identity already exists:")} ${color47.cyan(result2.identity.projectId)}`
26392
+ message: result2.created ? `${color48.green("Created")} ${filePath}
26393
+ ${color48.cyan(result2.identity.projectId)}
26394
+ ${color48.dim("Commit this file so every clone and machine shares the same HQ project.")}` : `${color48.dim("Project identity already exists:")} ${color48.cyan(result2.identity.projectId)}`
25909
26395
  };
25910
26396
  }
25911
26397
  if (!confirmed && opts.confirm) {
@@ -25920,17 +26406,17 @@ ${color47.dim("Commit this file so every clone and machine shares the same HQ pr
25920
26406
  }
25921
26407
  if (!confirmed) {
25922
26408
  return {
25923
- message: `Rekey requires confirmation. Pass ${color47.cyan("--yes")} or ${color47.cyan("-y")}.`
26409
+ message: `Rekey requires confirmation. Pass ${color48.cyan("--yes")} or ${color48.cyan("-y")}.`
25924
26410
  };
25925
26411
  }
25926
26412
  const result = await rekeyProjectIdentity(root);
25927
26413
  await ensureProjectGitignore(root);
25928
26414
  return {
25929
26415
  message: [
25930
- result.previous ? color47.dim(`Previous: ${result.previous.projectId}`) : color47.dim("Previous: none"),
25931
- `${color47.green("New project ID:")} ${color47.cyan(result.identity.projectId)}`,
25932
- color47.dim(`Commit ${filePath} to make this fork independent on every machine.`),
25933
- color47.dim("Restart WrongStack before publishing HQ or Kanban updates under the new ID.")
26416
+ result.previous ? color48.dim(`Previous: ${result.previous.projectId}`) : color48.dim("Previous: none"),
26417
+ `${color48.green("New project ID:")} ${color48.cyan(result.identity.projectId)}`,
26418
+ color48.dim(`Commit ${filePath} to make this fork independent on every machine.`),
26419
+ color48.dim("Restart WrongStack before publishing HQ or Kanban updates under the new ID.")
25934
26420
  ].join("\n")
25935
26421
  };
25936
26422
  }
@@ -25939,7 +26425,7 @@ async function listProjectsCommand(opts, ctx) {
25939
26425
  const currentRoot = ctx?.projectRoot;
25940
26426
  if (manifest.projects.length === 0) {
25941
26427
  return {
25942
- message: color47.dim("No projects registered. Add one: /project add <path> [name]")
26428
+ message: color48.dim("No projects registered. Add one: /project add <path> [name]")
25943
26429
  };
25944
26430
  }
25945
26431
  const sorted = [...manifest.projects].sort((a, b) => {
@@ -25951,19 +26437,19 @@ async function listProjectsCommand(opts, ctx) {
25951
26437
  const lines = [`Projects (${sorted.length}) registered in projects.json:`, ""];
25952
26438
  for (const p of sorted) {
25953
26439
  const isCurrent = p.root === currentRoot;
25954
- const marker = isCurrent ? color47.green("\u25CF") : color47.dim("\u25CB");
25955
- const name = isCurrent ? color47.bold(p.name) : p.name;
25956
- const slug = color47.dim(`[${p.slug}]`);
25957
- const last = color47.dim(fmtLastSeen(p.lastSeen));
26440
+ const marker = isCurrent ? color48.green("\u25CF") : color48.dim("\u25CB");
26441
+ const name = isCurrent ? color48.bold(p.name) : p.name;
26442
+ const slug = color48.dim(`[${p.slug}]`);
26443
+ const last = color48.dim(fmtLastSeen(p.lastSeen));
25958
26444
  lines.push(` ${marker} ${name} ${slug} ${last}`);
25959
26445
  lines.push(` ${p.root}`);
25960
26446
  if (isCurrent) {
25961
- lines.push(` ${color47.green("\u2190 active session")}`);
26447
+ lines.push(` ${color48.green("\u2190 active session")}`);
25962
26448
  }
25963
26449
  lines.push("");
25964
26450
  }
25965
26451
  lines.push(
25966
- color47.dim(
26452
+ color48.dim(
25967
26453
  "Commands: add <path> [name] | rename <slug> <name> | remove <slug> | switch [dir] (no args = picker)"
25968
26454
  )
25969
26455
  );
@@ -25974,17 +26460,17 @@ async function addProjectCommand(opts, ctx, targetPath, displayName) {
25974
26460
  try {
25975
26461
  await fs17.access(resolved);
25976
26462
  } catch {
25977
- return { message: color47.red(`Directory not found: ${resolved}`) };
26463
+ return { message: color48.red(`Directory not found: ${resolved}`) };
25978
26464
  }
25979
26465
  const stat5 = await fs17.stat(resolved);
25980
26466
  if (!stat5.isDirectory()) {
25981
- return { message: color47.red(`Not a directory: ${resolved}`) };
26467
+ return { message: color48.red(`Not a directory: ${resolved}`) };
25982
26468
  }
25983
26469
  const manifest = await loadManifest(opts.paths?.globalConfig);
25984
26470
  const existing = manifest.projects.find((p) => p.root === resolved);
25985
26471
  if (existing) {
25986
26472
  return {
25987
- message: color47.yellow(`Project already registered: "${existing.name}" (${existing.slug})`)
26473
+ message: color48.yellow(`Project already registered: "${existing.name}" (${existing.slug})`)
25988
26474
  };
25989
26475
  }
25990
26476
  const name = displayName?.trim() || path26.basename(resolved);
@@ -25996,9 +26482,9 @@ async function addProjectCommand(opts, ctx, targetPath, displayName) {
25996
26482
  return {
25997
26483
  message: [
25998
26484
  "",
25999
- color47.green(` Added project: ${name}`),
26000
- color47.dim(` Root: ${resolved}`),
26001
- color47.dim(` Slug: ${slug}`),
26485
+ color48.green(` Added project: ${name}`),
26486
+ color48.dim(` Root: ${resolved}`),
26487
+ color48.dim(` Slug: ${slug}`),
26002
26488
  ""
26003
26489
  ].join("\n")
26004
26490
  };
@@ -26008,7 +26494,7 @@ async function renameProjectCommand(opts, _ctx, slugOrName, newName) {
26008
26494
  const project = findProject(manifest, slugOrName);
26009
26495
  if (!project) {
26010
26496
  return {
26011
- message: color47.red(
26497
+ message: color48.red(
26012
26498
  `Project not found: "${slugOrName}". Use /project list to see available projects.`
26013
26499
  )
26014
26500
  };
@@ -26016,7 +26502,7 @@ async function renameProjectCommand(opts, _ctx, slugOrName, newName) {
26016
26502
  const oldName = project.name;
26017
26503
  project.name = newName;
26018
26504
  await saveManifest(manifest, opts.paths?.globalConfig);
26019
- return { message: color47.green(`Renamed: "${oldName}" \u2192 "${newName}" (${project.slug})`) };
26505
+ return { message: color48.green(`Renamed: "${oldName}" \u2192 "${newName}" (${project.slug})`) };
26020
26506
  }
26021
26507
  async function removeProjectCommand(opts, _ctx, slugOrName) {
26022
26508
  const manifest = await loadManifest(opts.paths?.globalConfig);
@@ -26025,7 +26511,7 @@ async function removeProjectCommand(opts, _ctx, slugOrName) {
26025
26511
  );
26026
26512
  if (idx === -1) {
26027
26513
  return {
26028
- message: color47.red(
26514
+ message: color48.red(
26029
26515
  `Project not found: "${slugOrName}". Use /project list to see available projects.`
26030
26516
  )
26031
26517
  };
@@ -26034,7 +26520,7 @@ async function removeProjectCommand(opts, _ctx, slugOrName) {
26034
26520
  manifest.projects.splice(idx, 1);
26035
26521
  await saveManifest(manifest, opts.paths?.globalConfig);
26036
26522
  return {
26037
- message: color47.dim(
26523
+ message: color48.dim(
26038
26524
  `Removed: "${removed.name}" (${removed.root}) \u2014 data directory kept at ~/.wrongstack/projects/${removed.slug}/`
26039
26525
  )
26040
26526
  };
@@ -26044,11 +26530,11 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
26044
26530
  try {
26045
26531
  await fs17.access(resolved);
26046
26532
  } catch {
26047
- return { message: color47.red(`Directory not found: ${resolved}`) };
26533
+ return { message: color48.red(`Directory not found: ${resolved}`) };
26048
26534
  }
26049
26535
  const stat5 = await fs17.stat(resolved);
26050
26536
  if (!stat5.isDirectory()) {
26051
- return { message: color47.red(`Not a directory: ${resolved}`) };
26537
+ return { message: color48.red(`Not a directory: ${resolved}`) };
26052
26538
  }
26053
26539
  let cliPath;
26054
26540
  try {
@@ -26061,7 +26547,7 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
26061
26547
  cliPath = process.argv[1] ?? "";
26062
26548
  if (!cliPath) {
26063
26549
  return {
26064
- message: color47.red(
26550
+ message: color48.red(
26065
26551
  "Could not locate the CLI entry point. Run `wstack` manually in the target directory."
26066
26552
  )
26067
26553
  };
@@ -26094,14 +26580,14 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
26094
26580
  // and boot/tui-project-spawn.ts documents this exact bug being removed.
26095
26581
  });
26096
26582
  child.on("error", (err) => {
26097
- console.error(color47.red(`Failed to spawn wstack: ${err.message}`));
26583
+ console.error(color48.red(`Failed to spawn wstack: ${err.message}`));
26098
26584
  });
26099
26585
  child.unref();
26100
26586
  return {
26101
26587
  message: [
26102
26588
  "",
26103
- color47.green(` Spawning wstack in ${resolved} ...`),
26104
- color47.dim(" (current session stays open \u2014 Ctrl+C to return)"),
26589
+ color48.green(` Spawning wstack in ${resolved} ...`),
26590
+ color48.dim(" (current session stays open \u2014 Ctrl+C to return)"),
26105
26591
  ""
26106
26592
  ].join("\n")
26107
26593
  };
@@ -26115,45 +26601,45 @@ async function confirmProjectSwitch(opts, targetName) {
26115
26601
  const parallelActive = parallelEngine?.currentState === "running";
26116
26602
  const hasActiveAgents = fleetRunning > 0 || eternalActive || parallelActive;
26117
26603
  if (!hasActiveAgents) return true;
26118
- const parts = [color47.yellow(`\u26A0 Switching projects will stop all running agents.`), ""];
26604
+ const parts = [color48.yellow(`\u26A0 Switching projects will stop all running agents.`), ""];
26119
26605
  if (fleetRunning > 0) {
26120
- parts.push(color47.dim(` \u2022 ${fleetRunning} subagent(s) currently running`));
26606
+ parts.push(color48.dim(` \u2022 ${fleetRunning} subagent(s) currently running`));
26121
26607
  }
26122
26608
  if (eternalActive) {
26123
- parts.push(color47.dim(" \u2022 Eternal engine is active"));
26609
+ parts.push(color48.dim(" \u2022 Eternal engine is active"));
26124
26610
  }
26125
26611
  if (parallelActive) {
26126
- parts.push(color47.dim(" \u2022 Parallel engine is active"));
26612
+ parts.push(color48.dim(" \u2022 Parallel engine is active"));
26127
26613
  }
26128
26614
  parts.push("");
26129
- parts.push(color47.dim(` Target: ${targetName}`));
26615
+ parts.push(color48.dim(` Target: ${targetName}`));
26130
26616
  opts.renderer.write(`
26131
26617
  ${parts.join("\n")}
26132
26618
  `);
26133
26619
  if (!opts.confirm) return true;
26134
26620
  const confirmed = await opts.confirm(
26135
- color47.yellow(`Stop all agents and switch to "${targetName}"?`),
26621
+ color48.yellow(`Stop all agents and switch to "${targetName}"?`),
26136
26622
  false
26137
26623
  // default to No for safety
26138
26624
  );
26139
26625
  if (!confirmed) {
26140
- opts.renderer.write(color47.dim(" Switch cancelled.\n"));
26626
+ opts.renderer.write(color48.dim(" Switch cancelled.\n"));
26141
26627
  return false;
26142
26628
  }
26143
26629
  if (fleetRunning > 0) {
26144
26630
  const killed = opts.onFleetKill ? await opts.onFleetKill() : 0;
26145
26631
  if (killed > 0) {
26146
- opts.renderer.write(color47.dim(` Stopped ${killed} subagent(s).
26632
+ opts.renderer.write(color48.dim(` Stopped ${killed} subagent(s).
26147
26633
  `));
26148
26634
  }
26149
26635
  }
26150
26636
  if (eternalActive) {
26151
26637
  eternalEngine?.stop();
26152
- opts.renderer.write(color47.dim(" Stopped eternal engine.\n"));
26638
+ opts.renderer.write(color48.dim(" Stopped eternal engine.\n"));
26153
26639
  }
26154
26640
  if (parallelActive) {
26155
26641
  parallelEngine?.stop();
26156
- opts.renderer.write(color47.dim(" Stopped parallel engine.\n"));
26642
+ opts.renderer.write(color48.dim(" Stopped parallel engine.\n"));
26157
26643
  }
26158
26644
  return true;
26159
26645
  }
@@ -26165,16 +26651,16 @@ async function switchInteractiveCommand(opts, ctx) {
26165
26651
  currentProjectRoot: currentRoot
26166
26652
  });
26167
26653
  if (!result) {
26168
- return { message: color47.dim("Cancelled.") };
26654
+ return { message: color48.dim("Cancelled.") };
26169
26655
  }
26170
26656
  switch (result.kind) {
26171
26657
  case "project": {
26172
26658
  const project = manifest.projects.find((p) => p.slug === result.key);
26173
26659
  if (!project) {
26174
- return { message: color47.red(`Project not found: ${result.key}`) };
26660
+ return { message: color48.red(`Project not found: ${result.key}`) };
26175
26661
  }
26176
26662
  if (project.root === currentRoot) {
26177
- return { message: color47.dim(`Already in ${project.name} (${project.root})`) };
26663
+ return { message: color48.dim(`Already in ${project.name} (${project.root})`) };
26178
26664
  }
26179
26665
  const canSwitch = await confirmProjectSwitch(opts, project.name);
26180
26666
  if (!canSwitch) return { message: "" };
@@ -26187,11 +26673,11 @@ async function switchInteractiveCommand(opts, ctx) {
26187
26673
  case "prev-sessions":
26188
26674
  return handlePrevSessions(opts, ctx);
26189
26675
  default:
26190
- return { message: color47.dim("Cancelled.") };
26676
+ return { message: color48.dim("Cancelled.") };
26191
26677
  }
26192
26678
  }
26193
26679
  default:
26194
- return { message: color47.dim("Cancelled.") };
26680
+ return { message: color48.dim("Cancelled.") };
26195
26681
  }
26196
26682
  }
26197
26683
  async function spawnInProject(opts, _ctx, root, projectName) {
@@ -26206,7 +26692,7 @@ async function spawnInProject(opts, _ctx, root, projectName) {
26206
26692
  cliPath = process.argv[1] ?? "";
26207
26693
  if (!cliPath) {
26208
26694
  return {
26209
- message: color47.red(
26695
+ message: color48.red(
26210
26696
  "Could not locate the CLI entry point. Run `wstack` manually in the target directory."
26211
26697
  )
26212
26698
  };
@@ -26236,15 +26722,15 @@ async function spawnInProject(opts, _ctx, root, projectName) {
26236
26722
  // and boot/tui-project-spawn.ts documents this exact bug being removed.
26237
26723
  });
26238
26724
  child.on("error", (err) => {
26239
- console.error(color47.red(`Failed to spawn wstack: ${err.message}`));
26725
+ console.error(color48.red(`Failed to spawn wstack: ${err.message}`));
26240
26726
  });
26241
26727
  child.unref();
26242
26728
  return {
26243
26729
  message: [
26244
26730
  "",
26245
- color47.green(` Switched to ${projectName}`),
26246
- color47.dim(` Root: ${root}`),
26247
- color47.dim(" (current session stays open \u2014 Ctrl+C to return)"),
26731
+ color48.green(` Switched to ${projectName}`),
26732
+ color48.dim(` Root: ${root}`),
26733
+ color48.dim(" (current session stays open \u2014 Ctrl+C to return)"),
26248
26734
  ""
26249
26735
  ].join("\n")
26250
26736
  };
@@ -26261,7 +26747,7 @@ async function handleNewSession(_opts, _ctx) {
26261
26747
  cliPath = process.argv[1] ?? "";
26262
26748
  if (!cliPath) {
26263
26749
  return {
26264
- message: color47.red("Could not locate the CLI entry point. Run `wstack` manually.")
26750
+ message: color48.red("Could not locate the CLI entry point. Run `wstack` manually.")
26265
26751
  };
26266
26752
  }
26267
26753
  }
@@ -26278,14 +26764,14 @@ async function handleNewSession(_opts, _ctx) {
26278
26764
  // and boot/tui-project-spawn.ts documents this exact bug being removed.
26279
26765
  });
26280
26766
  child.on("error", (err) => {
26281
- console.error(color47.red(`Failed to spawn wstack: ${err.message}`));
26767
+ console.error(color48.red(`Failed to spawn wstack: ${err.message}`));
26282
26768
  });
26283
26769
  child.unref();
26284
26770
  return {
26285
26771
  message: [
26286
26772
  "",
26287
- color47.green(" Starting new session ..."),
26288
- color47.dim(" (current session stays open \u2014 Ctrl+C to return)"),
26773
+ color48.green(" Starting new session ..."),
26774
+ color48.dim(" (current session stays open \u2014 Ctrl+C to return)"),
26289
26775
  ""
26290
26776
  ].join("\n")
26291
26777
  };
@@ -26296,25 +26782,25 @@ async function handlePrevSessions(opts, _ctx) {
26296
26782
  }
26297
26783
  const list = await opts.sessionStore.list(15);
26298
26784
  if (list.length === 0) {
26299
- return { message: color47.dim("No saved sessions.") };
26785
+ return { message: color48.dim("No saved sessions.") };
26300
26786
  }
26301
26787
  const currentId = opts.context?.session?.id;
26302
- const lines = [color47.bold(`Recent sessions (${list.length}):`), ""];
26788
+ const lines = [color48.bold(`Recent sessions (${list.length}):`), ""];
26303
26789
  for (const s of list) {
26304
26790
  const isCurrent = s.id === currentId;
26305
- const marker = isCurrent ? color47.cyan("\u25CF") : " ";
26306
- const date = color47.dim(s.startedAt.slice(0, 16).replace("T", " "));
26791
+ const marker = isCurrent ? color48.cyan("\u25CF") : " ";
26792
+ const date = color48.dim(s.startedAt.slice(0, 16).replace("T", " "));
26307
26793
  const stats = [
26308
- color47.dim(`${s.tokenTotal.toLocaleString()} tok`),
26309
- s.toolCallCount ? color47.cyan(`${s.toolCallCount} calls`) : "",
26310
- s.iterationCount ? color47.dim(`${s.iterationCount} iter`) : ""
26794
+ color48.dim(`${s.tokenTotal.toLocaleString()} tok`),
26795
+ s.toolCallCount ? color48.cyan(`${s.toolCallCount} calls`) : "",
26796
+ s.iterationCount ? color48.dim(`${s.iterationCount} iter`) : ""
26311
26797
  ].filter(Boolean).join(" ");
26312
- const outcome = s.outcome === "completed" ? color47.green("\u2713") : s.outcome === "aborted" ? color47.yellow("\u26A0") : s.outcome === "error" ? color47.red("\u2717") : color47.dim("?");
26313
- lines.push(` ${marker} ${color47.bold(s.id)} ${date}`);
26314
- lines.push(` ${stats} ${outcome} ${color47.dim(s.title)}`);
26798
+ const outcome = s.outcome === "completed" ? color48.green("\u2713") : s.outcome === "aborted" ? color48.yellow("\u26A0") : s.outcome === "error" ? color48.red("\u2717") : color48.dim("?");
26799
+ lines.push(` ${marker} ${color48.bold(s.id)} ${date}`);
26800
+ lines.push(` ${stats} ${outcome} ${color48.dim(s.title)}`);
26315
26801
  lines.push("");
26316
26802
  }
26317
- lines.push(color47.dim("Resume: /sessions or wstack resume <id>"));
26803
+ lines.push(color48.dim("Resume: /sessions or wstack resume <id>"));
26318
26804
  return { message: lines.join("\n") };
26319
26805
  }
26320
26806
 
@@ -26522,7 +27008,7 @@ function buildSecurityCommand(opts) {
26522
27008
  // src/slash-commands/settings.ts
26523
27009
  import { noOpVault as noOpVault9 } from "@wrongstack/core/security";
26524
27010
  import { resolveFleetChatVerbosity as resolveFleetChatVerbosity2 } from "@wrongstack/core/types";
26525
- import { color as color48, toErrorMessage as toErrorMessage24 } from "@wrongstack/core/utils";
27011
+ import { color as color49, toErrorMessage as toErrorMessage24 } from "@wrongstack/core/utils";
26526
27012
  import { getProcessRegistry } from "@wrongstack/tools";
26527
27013
 
26528
27014
  // src/utils/delay-format.ts
@@ -26630,56 +27116,56 @@ function buildSettingsCommand(opts) {
26630
27116
  const idx = opts.configStore.get().indexing;
26631
27117
  const sess = opts.configStore.get().session;
26632
27118
  return [
26633
- `${color48.bold("WrongStack")} ${color48.dim("\u2014 Settings")}`,
27119
+ `${color49.bold("WrongStack")} ${color49.dim("\u2014 Settings")}`,
26634
27120
  "",
26635
- ` auto-proceed delay: ${color48.cyan(formatDelay(delay))} ${color48.dim("change: /settings delay <seconds>")}`,
26636
- ` default autonomy mode: ${color48.cyan(mode)} ${color48.dim("change: /settings mode off|suggest|auto")}`,
26637
- ` fleet chat: ${color48.cyan(resolveFleetChatVerbosity2(au))} ${color48.dim("change: /settings stream-fleet off|full")}`,
26638
- ` completion chime: ${au?.chime === true ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings chime on|off")}`,
26639
- ` confirm before exit: ${au?.confirmExit !== false ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings confirm-exit on|off")}`,
26640
- ` launch hints: ${hints ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings hints on|off")}`,
26641
- ` debug stream: ${debugStream ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings debug-stream on|off")}`,
26642
- ` config scope: ${color48.cyan(configScope)} ${color48.dim("change: /settings config-scope global|project")}`,
26643
- ` filesystem access: ${color48.cyan(fsAccess)} ${color48.dim("change: /settings fs-access unrestricted|project")}`,
26644
- ` refine: ${enhanceEnabled ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings refine on|off")}`,
26645
- ` refine-delay: ${color48.cyan(formatDelay(enhanceDelay))} ${color48.dim("change: /settings refine-delay <seconds>")}`,
26646
- ` refine-language: ${color48.cyan(enhanceLanguage)} ${color48.dim("change: /settings refine-language original|english")}`,
26647
- ` refiner-provider: ${color48.cyan(au?.refinerProvider ?? color48.dim("(unset)"))} ${color48.dim("change: /settings refiner-provider <id>")}`,
26648
- ` refiner-model: ${color48.cyan(au?.refinerModel ?? color48.dim("(unset)"))} ${color48.dim("change: /settings refiner-model <model>")}`,
26649
- ` refiner-fallback-profile: ${color48.cyan(au?.refinerFallbackProfile ?? color48.dim("(unset)"))} ${color48.dim("change: /settings refiner-fallback-profile <name>")}`,
26650
- ` semver default part: ${color48.cyan(semverPart)} ${color48.dim("change: /settings semver-part patch|minor|major|auto")}`,
26651
- ` circuit breaker: ${breakerEnabled ? color48.cyan("on") : color48.dim("off")} (${breakerTimeout > 0 ? formatDelay(breakerTimeout) : color48.dim("manual")}) ${color48.dim("change: /settings breaker on|off")}`,
26652
- ` context mode: ${color48.cyan(contextMode)} ${color48.dim("change: /settings context-mode balanced|frugal|deep")}`,
26653
- ` context strategy: ${color48.cyan(contextStrategy)} ${color48.dim("change: /settings context-strategy hybrid|intelligent|selective")}`,
26654
- ` context auto-compact: ${contextAutoCompact ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings context-auto-compact on|off")}`,
26655
- ` token-saving: ${color48.cyan(tokenSavingTier)} ${color48.dim("change: /settings token-saving off|minimal|light|medium|aggressive")}`,
26656
- ` nextsteps tool: ${nextStepsToolEnabled ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings nextsteps-tool on|off")}`,
26657
- ` MCP features: ${feats?.mcp !== false ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings mcp on|off")}`,
26658
- ` plugin features: ${feats?.plugins !== false ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings plugins on|off")}`,
26659
- ` memory features: ${feats?.memory !== false ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings memory on|off")}`,
26660
- ` skills features: ${feats?.skills !== false ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings skills on|off")}`,
26661
- ` models registry: ${feats?.modelsRegistry !== false ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings models-registry on|off")}`,
26662
- ` max concurrent: ${color48.cyan(maxConcurrent === 0 ? "default" : String(maxConcurrent))} ${color48.dim("change: /settings max-concurrent <n>")}`,
26663
- ` max iterations: ${color48.cyan(String(tools?.maxIterations ?? "default"))} ${color48.dim("change: /settings max-iterations <n>")}`,
26664
- ` auto-proceed max iters: ${color48.cyan(String(au?.autoProceedMaxIterations ?? "unlimited"))} ${color48.dim("change: /settings auto-proceed-max-iterations <n>")}`,
26665
- ` title animation: ${titleAnimation ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings title-animation on|off")}`,
26666
- ` thinking word: ${color48.cyan(au?.thinkingWord ?? "thinking")} ${color48.dim("change: /settings thinking-word <word>")}`,
26667
- ` statusline mode: ${color48.cyan(au?.statuslineMode ?? "minimum")} ${color48.dim("change: /settings statusline minimum|detailed|no-color")}`,
26668
- ` animation style: ${color48.cyan(au?.animationStyle ?? "rainbow")} ${color48.dim("change: /settings animation rainbow|wave|pulse|dots|breathe|cycle")}`,
26669
- ` read symbols: ${au?.readAdvancedMode === true ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings read-symbols on|off")}`,
26670
- ` reasoning mode: ${color48.cyan(reasoningMode)} ${color48.dim("change: /settings reasoning auto|on|off")}`,
26671
- ` reasoning effort: ${color48.cyan(reasoningEffort)} ${color48.dim("change: /settings reasoning-effort <level>")}`,
26672
- ` reasoning preserve: ${reasoningPreserve ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings reasoning-preserve on|off")}`,
26673
- ` cache TTL: ${color48.cyan(cacheTtl)} ${color48.dim("change: /settings cache-ttl 5m|1h")}`,
26674
- ` index on start: ${idx?.onSessionStart !== false ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings index-on-start on|off")}`,
26675
- ` log level: ${color48.cyan(log?.level ?? "info")} ${color48.dim("change: /settings log-level error|warn|info|debug|trace")}`,
26676
- ` audit level: ${color48.cyan(sess?.auditLevel ?? "standard")} ${color48.dim("change: /settings audit-level minimal|standard|full")}`,
26677
- ` HQ publishing: ${hqEnabled ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings hq on|off")}`,
26678
- ` HQ URL: ${color48.cyan(hqUrl)} ${color48.dim("change: /settings hq-url <url>")}`,
26679
- ` HQ token: ${color48.cyan(hqToken)} ${color48.dim("change: /settings hq-token <token>")}`,
26680
- ` HQ raw content: ${hq?.rawContent === true ? color48.cyan("on") : color48.dim("off")} ${color48.dim("change: /settings hq-raw on|off")}`,
27121
+ ` auto-proceed delay: ${color49.cyan(formatDelay(delay))} ${color49.dim("change: /settings delay <seconds>")}`,
27122
+ ` default autonomy mode: ${color49.cyan(mode)} ${color49.dim("change: /settings mode off|suggest|auto")}`,
27123
+ ` fleet chat: ${color49.cyan(resolveFleetChatVerbosity2(au))} ${color49.dim("change: /settings stream-fleet off|full")}`,
27124
+ ` completion chime: ${au?.chime === true ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings chime on|off")}`,
27125
+ ` confirm before exit: ${au?.confirmExit !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings confirm-exit on|off")}`,
27126
+ ` launch hints: ${hints ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings hints on|off")}`,
27127
+ ` debug stream: ${debugStream ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings debug-stream on|off")}`,
27128
+ ` config scope: ${color49.cyan(configScope)} ${color49.dim("change: /settings config-scope global|project")}`,
27129
+ ` filesystem access: ${color49.cyan(fsAccess)} ${color49.dim("change: /settings fs-access unrestricted|project")}`,
27130
+ ` refine: ${enhanceEnabled ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings refine on|off")}`,
27131
+ ` refine-delay: ${color49.cyan(formatDelay(enhanceDelay))} ${color49.dim("change: /settings refine-delay <seconds>")}`,
27132
+ ` refine-language: ${color49.cyan(enhanceLanguage)} ${color49.dim("change: /settings refine-language original|english")}`,
27133
+ ` refiner-provider: ${color49.cyan(au?.refinerProvider ?? color49.dim("(unset)"))} ${color49.dim("change: /settings refiner-provider <id>")}`,
27134
+ ` refiner-model: ${color49.cyan(au?.refinerModel ?? color49.dim("(unset)"))} ${color49.dim("change: /settings refiner-model <model>")}`,
27135
+ ` refiner-fallback-profile: ${color49.cyan(au?.refinerFallbackProfile ?? color49.dim("(unset)"))} ${color49.dim("change: /settings refiner-fallback-profile <name>")}`,
27136
+ ` semver default part: ${color49.cyan(semverPart)} ${color49.dim("change: /settings semver-part patch|minor|major|auto")}`,
27137
+ ` circuit breaker: ${breakerEnabled ? color49.cyan("on") : color49.dim("off")} (${breakerTimeout > 0 ? formatDelay(breakerTimeout) : color49.dim("manual")}) ${color49.dim("change: /settings breaker on|off")}`,
27138
+ ` context mode: ${color49.cyan(contextMode)} ${color49.dim("change: /settings context-mode balanced|frugal|deep")}`,
27139
+ ` context strategy: ${color49.cyan(contextStrategy)} ${color49.dim("change: /settings context-strategy hybrid|intelligent|selective")}`,
27140
+ ` context auto-compact: ${contextAutoCompact ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings context-auto-compact on|off")}`,
27141
+ ` token-saving: ${color49.cyan(tokenSavingTier)} ${color49.dim("change: /settings token-saving off|minimal|light|medium|aggressive")}`,
27142
+ ` nextsteps tool: ${nextStepsToolEnabled ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings nextsteps-tool on|off")}`,
27143
+ ` MCP features: ${feats?.mcp !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings mcp on|off")}`,
27144
+ ` plugin features: ${feats?.plugins !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings plugins on|off")}`,
27145
+ ` memory features: ${feats?.memory !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings memory on|off")}`,
27146
+ ` skills features: ${feats?.skills !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings skills on|off")}`,
27147
+ ` models registry: ${feats?.modelsRegistry !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings models-registry on|off")}`,
27148
+ ` max concurrent: ${color49.cyan(maxConcurrent === 0 ? "default" : String(maxConcurrent))} ${color49.dim("change: /settings max-concurrent <n>")}`,
27149
+ ` max iterations: ${color49.cyan(String(tools?.maxIterations ?? "default"))} ${color49.dim("change: /settings max-iterations <n>")}`,
27150
+ ` auto-proceed max iters: ${color49.cyan(String(au?.autoProceedMaxIterations ?? "unlimited"))} ${color49.dim("change: /settings auto-proceed-max-iterations <n>")}`,
27151
+ ` title animation: ${titleAnimation ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings title-animation on|off")}`,
27152
+ ` thinking word: ${color49.cyan(au?.thinkingWord ?? "thinking")} ${color49.dim("change: /settings thinking-word <word>")}`,
27153
+ ` statusline mode: ${color49.cyan(au?.statuslineMode ?? "minimum")} ${color49.dim("change: /settings statusline minimum|detailed|no-color")}`,
27154
+ ` animation style: ${color49.cyan(au?.animationStyle ?? "rainbow")} ${color49.dim("change: /settings animation rainbow|wave|pulse|dots|breathe|cycle")}`,
27155
+ ` read symbols: ${au?.readAdvancedMode === true ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings read-symbols on|off")}`,
27156
+ ` reasoning mode: ${color49.cyan(reasoningMode)} ${color49.dim("change: /settings reasoning auto|on|off")}`,
27157
+ ` reasoning effort: ${color49.cyan(reasoningEffort)} ${color49.dim("change: /settings reasoning-effort <level>")}`,
27158
+ ` reasoning preserve: ${reasoningPreserve ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings reasoning-preserve on|off")}`,
27159
+ ` cache TTL: ${color49.cyan(cacheTtl)} ${color49.dim("change: /settings cache-ttl 5m|1h")}`,
27160
+ ` index on start: ${idx?.onSessionStart !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings index-on-start on|off")}`,
27161
+ ` log level: ${color49.cyan(log?.level ?? "info")} ${color49.dim("change: /settings log-level error|warn|info|debug|trace")}`,
27162
+ ` audit level: ${color49.cyan(sess?.auditLevel ?? "standard")} ${color49.dim("change: /settings audit-level minimal|standard|full")}`,
27163
+ ` HQ publishing: ${hqEnabled ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings hq on|off")}`,
27164
+ ` HQ URL: ${color49.cyan(hqUrl)} ${color49.dim("change: /settings hq-url <url>")}`,
27165
+ ` HQ token: ${color49.cyan(hqToken)} ${color49.dim("change: /settings hq-token <token>")}`,
27166
+ ` HQ raw content: ${hq?.rawContent === true ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings hq-raw on|off")}`,
26681
27167
  "",
26682
- color48.dim(` Persisted to ${persistedTo} \xB7 /settings help for more`)
27168
+ color49.dim(` Persisted to ${persistedTo} \xB7 /settings help for more`)
26683
27169
  ].join("\n");
26684
27170
  }
26685
27171
  return {
@@ -26694,7 +27180,7 @@ function buildSettingsCommand(opts) {
26694
27180
  return { message: this.help ?? "" };
26695
27181
  }
26696
27182
  if (!opts.configStore || !opts.paths) {
26697
- return { message: `${color48.red("Error")} config store not available.` };
27183
+ return { message: `${color49.red("Error")} config store not available.` };
26698
27184
  }
26699
27185
  if (!sub) {
26700
27186
  return { message: currentView() };
@@ -26702,16 +27188,16 @@ function buildSettingsCommand(opts) {
26702
27188
  if (sub === "defaults") {
26703
27189
  return {
26704
27190
  message: [
26705
- `${color48.bold("Default Values")}`,
27191
+ `${color49.bold("Default Values")}`,
26706
27192
  "",
26707
- ` auto-proceed delay: ${color48.cyan("45s")} ${color48.dim("(WRONGSTACK_AUTO_PROCEED_DELAY_MS env)")}`,
26708
- ` default autonomy mode: ${color48.cyan("off")}`,
26709
- ` launch hints: ${color48.cyan("on")}`,
26710
- ` iteration timeout: ${color48.cyan("5 min")}`,
26711
- ` session timeout: ${color48.cyan("30 min")}`,
26712
- ` max iterations: ${color48.cyan("100")}`,
26713
- ` max concurrent: ${color48.cyan("4")}`,
26714
- ` semver default part: ${color48.cyan("patch")}`
27193
+ ` auto-proceed delay: ${color49.cyan("45s")} ${color49.dim("(WRONGSTACK_AUTO_PROCEED_DELAY_MS env)")}`,
27194
+ ` default autonomy mode: ${color49.cyan("off")}`,
27195
+ ` launch hints: ${color49.cyan("on")}`,
27196
+ ` iteration timeout: ${color49.cyan("5 min")}`,
27197
+ ` session timeout: ${color49.cyan("30 min")}`,
27198
+ ` max iterations: ${color49.cyan("100")}`,
27199
+ ` max concurrent: ${color49.cyan("4")}`,
27200
+ ` semver default part: ${color49.cyan("patch")}`
26715
27201
  ].join("\n")
26716
27202
  };
26717
27203
  }
@@ -26726,7 +27212,7 @@ function buildSettingsCommand(opts) {
26726
27212
  if (sub === "hq") {
26727
27213
  const raw = (rest[0] ?? "").toLowerCase();
26728
27214
  if (!["on", "off"].includes(raw)) {
26729
- return { message: `${color48.amber("Usage:")} /settings hq on|off` };
27215
+ return { message: `${color49.amber("Usage:")} /settings hq on|off` };
26730
27216
  }
26731
27217
  const on = raw === "on";
26732
27218
  await persistConfigSetting({ ...persistDeps, forceGlobal: true }, (cfg) => {
@@ -26735,19 +27221,19 @@ function buildSettingsCommand(opts) {
26735
27221
  cfg.hq = hq;
26736
27222
  });
26737
27223
  return {
26738
- message: `${color48.green("\u2713")} HQ publishing \u2192 ${on ? color48.cyan("on") : color48.dim("off")}`
27224
+ message: `${color49.green("\u2713")} HQ publishing \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
26739
27225
  };
26740
27226
  }
26741
27227
  if (sub === "hq-url") {
26742
27228
  const raw = rest.join(" ").trim();
26743
27229
  if (!raw)
26744
- return { message: `${color48.amber("Usage:")} /settings hq-url <http://host:3499>` };
27230
+ return { message: `${color49.amber("Usage:")} /settings hq-url <http://host:3499>` };
26745
27231
  try {
26746
27232
  const url = new URL(raw);
26747
27233
  if (url.protocol !== "http:" && url.protocol !== "https:")
26748
27234
  throw new Error("bad protocol");
26749
27235
  } catch {
26750
- return { message: `${color48.red("Invalid URL")}: ${raw}` };
27236
+ return { message: `${color49.red("Invalid URL")}: ${raw}` };
26751
27237
  }
26752
27238
  await persistConfigSetting({ ...persistDeps, forceGlobal: true }, (cfg) => {
26753
27239
  const hq = cfg.hq ?? {};
@@ -26755,12 +27241,12 @@ function buildSettingsCommand(opts) {
26755
27241
  hq.enabled = true;
26756
27242
  cfg.hq = hq;
26757
27243
  });
26758
- return { message: `${color48.green("\u2713")} HQ URL \u2192 ${color48.cyan(raw)}` };
27244
+ return { message: `${color49.green("\u2713")} HQ URL \u2192 ${color49.cyan(raw)}` };
26759
27245
  }
26760
27246
  if (sub === "hq-token") {
26761
27247
  const token = rest.join(" ").trim();
26762
27248
  if (!token)
26763
- return { message: `${color48.amber("Usage:")} /settings hq-token <client-token>` };
27249
+ return { message: `${color49.amber("Usage:")} /settings hq-token <client-token>` };
26764
27250
  await persistConfigSetting({ ...persistDeps, forceGlobal: true }, (cfg) => {
26765
27251
  const hq = cfg.hq ?? {};
26766
27252
  hq.token = token;
@@ -26768,13 +27254,13 @@ function buildSettingsCommand(opts) {
26768
27254
  cfg.hq = hq;
26769
27255
  });
26770
27256
  return {
26771
- message: `${color48.green("\u2713")} HQ token saved ${color48.dim("(active profile config)")}`
27257
+ message: `${color49.green("\u2713")} HQ token saved ${color49.dim("(active profile config)")}`
26772
27258
  };
26773
27259
  }
26774
27260
  if (sub === "hq-raw") {
26775
27261
  const raw = (rest[0] ?? "").toLowerCase();
26776
27262
  if (!["on", "off"].includes(raw)) {
26777
- return { message: `${color48.amber("Usage:")} /settings hq-raw on|off` };
27263
+ return { message: `${color49.amber("Usage:")} /settings hq-raw on|off` };
26778
27264
  }
26779
27265
  const on = raw === "on";
26780
27266
  await persistConfigSetting({ ...persistDeps, forceGlobal: true }, (cfg) => {
@@ -26783,56 +27269,56 @@ function buildSettingsCommand(opts) {
26783
27269
  cfg.hq = hq;
26784
27270
  });
26785
27271
  return {
26786
- message: `${color48.green("\u2713")} HQ raw content \u2192 ${on ? color48.cyan("on") : color48.dim("off")}`
27272
+ message: `${color49.green("\u2713")} HQ raw content \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
26787
27273
  };
26788
27274
  }
26789
27275
  if (sub === "delay") {
26790
27276
  const raw = rest[0];
26791
27277
  if (raw === void 0) {
26792
27278
  return {
26793
- message: `${color48.amber("Usage:")} /settings delay <seconds> ${color48.dim("(0 disables)")}`
27279
+ message: `${color49.amber("Usage:")} /settings delay <seconds> ${color49.dim("(0 disables)")}`
26794
27280
  };
26795
27281
  }
26796
27282
  const seconds = Number.parseFloat(raw);
26797
27283
  if (Number.isNaN(seconds) || seconds < 0) {
26798
27284
  return {
26799
- message: `${color48.red("Invalid number")}: "${raw}". Enter seconds, e.g. /settings delay 30`
27285
+ message: `${color49.red("Invalid number")}: "${raw}". Enter seconds, e.g. /settings delay 30`
26800
27286
  };
26801
27287
  }
26802
27288
  const ms = Math.round(seconds * 1e3);
26803
27289
  await persistAutonomySetting(persistDeps, (autonomy) => {
26804
27290
  autonomy.autoProceedDelayMs = ms;
26805
27291
  });
26806
- return { message: `${color48.green("\u2713")} auto-proceed delay \u2192 ${formatDelay(ms)}` };
27292
+ return { message: `${color49.green("\u2713")} auto-proceed delay \u2192 ${formatDelay(ms)}` };
26807
27293
  }
26808
27294
  if (sub === "mode") {
26809
27295
  const raw = (rest[0] ?? "").toLowerCase();
26810
27296
  const modes = ["off", "suggest", "auto"];
26811
27297
  if (!modes.includes(raw)) {
26812
- return { message: `${color48.amber("Usage:")} /settings mode off|suggest|auto` };
27298
+ return { message: `${color49.amber("Usage:")} /settings mode off|suggest|auto` };
26813
27299
  }
26814
27300
  await persistAutonomySetting(persistDeps, (autonomy) => {
26815
27301
  autonomy.defaultMode = raw;
26816
27302
  });
26817
- return { message: `${color48.green("\u2713")} default autonomy \u2192 ${color48.bold(raw)}` };
27303
+ return { message: `${color49.green("\u2713")} default autonomy \u2192 ${color49.bold(raw)}` };
26818
27304
  }
26819
27305
  if (sub === "hints") {
26820
27306
  const raw = (rest[0] ?? "").toLowerCase();
26821
27307
  if (!["on", "off"].includes(raw)) {
26822
- return { message: `${color48.amber("Usage:")} /settings hints on|off` };
27308
+ return { message: `${color49.amber("Usage:")} /settings hints on|off` };
26823
27309
  }
26824
27310
  const on = raw === "on";
26825
27311
  await persistConfigSetting(persistDeps, (cfg) => {
26826
27312
  cfg.hints = on;
26827
27313
  });
26828
27314
  return {
26829
- message: `${color48.green("\u2713")} launch hints \u2192 ${on ? color48.cyan("on") : color48.dim("off")}`
27315
+ message: `${color49.green("\u2713")} launch hints \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
26830
27316
  };
26831
27317
  }
26832
27318
  if (sub === "debug-stream") {
26833
27319
  const raw = (rest[0] ?? "").toLowerCase();
26834
27320
  if (!["on", "off"].includes(raw)) {
26835
- return { message: `${color48.amber("Usage:")} /settings debug-stream on|off` };
27321
+ return { message: `${color49.amber("Usage:")} /settings debug-stream on|off` };
26836
27322
  }
26837
27323
  const on = raw === "on";
26838
27324
  const { setDebugStreamEnabled } = await import("@wrongstack/providers");
@@ -26841,24 +27327,24 @@ function buildSettingsCommand(opts) {
26841
27327
  cfg.debugStream = on;
26842
27328
  });
26843
27329
  return {
26844
- message: `${color48.green("\u2713")} debug stream \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim("raw SSE hex-dump to stderr")}`
27330
+ message: `${color49.green("\u2713")} debug stream \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("raw SSE hex-dump to stderr")}`
26845
27331
  };
26846
27332
  }
26847
27333
  if (sub === "config-scope") {
26848
27334
  const raw = (rest[0] ?? "").toLowerCase();
26849
27335
  if (!["global", "project"].includes(raw)) {
26850
- return { message: `${color48.amber("Usage:")} /settings config-scope global|project` };
27336
+ return { message: `${color49.amber("Usage:")} /settings config-scope global|project` };
26851
27337
  }
26852
27338
  await persistConfigSetting(persistDeps, (cfg) => {
26853
27339
  cfg.configScope = raw;
26854
27340
  });
26855
- const label = raw === "project" ? `${color48.cyan("project")} \u2014 settings saved to <project>/.wrongstack/config.json` : `${color48.cyan("global")} \u2014 settings saved to ~/.wrongstack/profiles/${activeProfile}/config.json`;
26856
- return { message: `${color48.green("\u2713")} config scope \u2192 ${label}` };
27341
+ const label = raw === "project" ? `${color49.cyan("project")} \u2014 settings saved to <project>/.wrongstack/config.json` : `${color49.cyan("global")} \u2014 settings saved to ~/.wrongstack/profiles/${activeProfile}/config.json`;
27342
+ return { message: `${color49.green("\u2713")} config scope \u2192 ${label}` };
26857
27343
  }
26858
27344
  if (sub === "fs-access") {
26859
27345
  const raw = (rest[0] ?? "").toLowerCase();
26860
27346
  if (!["unrestricted", "project"].includes(raw)) {
26861
- return { message: `${color48.amber("Usage:")} /settings fs-access unrestricted|project` };
27347
+ return { message: `${color49.amber("Usage:")} /settings fs-access unrestricted|project` };
26862
27348
  }
26863
27349
  const restrict = raw === "project";
26864
27350
  const fsAccess = deriveFsAccessPair({ restrictFsToRoot: restrict });
@@ -26870,15 +27356,15 @@ function buildSettingsCommand(opts) {
26870
27356
  features.allowOutsideProjectRoot = fsAccess.allowOutsideProjectRoot;
26871
27357
  cfg.features = features;
26872
27358
  });
26873
- const label = restrict ? `${color48.cyan("project")} \u2014 file tools confined to the project root` : `${color48.cyan("unrestricted")} \u2014 file tools may access paths outside the project root`;
27359
+ const label = restrict ? `${color49.cyan("project")} \u2014 file tools confined to the project root` : `${color49.cyan("unrestricted")} \u2014 file tools may access paths outside the project root`;
26874
27360
  return {
26875
- message: `${color48.green("\u2713")} filesystem access \u2192 ${label} ${color48.dim("(restart or re-open the session to apply)")}`
27361
+ message: `${color49.green("\u2713")} filesystem access \u2192 ${label} ${color49.dim("(restart or re-open the session to apply)")}`
26876
27362
  };
26877
27363
  }
26878
27364
  if (sub === "refine") {
26879
27365
  const raw = (rest[0] ?? "").toLowerCase();
26880
27366
  if (!["on", "off"].includes(raw)) {
26881
- return { message: `${color48.amber("Usage:")} /settings refine on|off` };
27367
+ return { message: `${color49.amber("Usage:")} /settings refine on|off` };
26882
27368
  }
26883
27369
  const on = raw === "on";
26884
27370
  await persistAutonomySetting(persistDeps, (autonomy) => {
@@ -26888,52 +27374,52 @@ function buildSettingsCommand(opts) {
26888
27374
  opts.enhanceController.setEnabled(on);
26889
27375
  }
26890
27376
  return {
26891
- message: `${color48.green("\u2713")} refine \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim(on ? "prompts will be refined before sending" : "prompts sent verbatim")}`
27377
+ message: `${color49.green("\u2713")} refine \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim(on ? "prompts will be refined before sending" : "prompts sent verbatim")}`
26892
27378
  };
26893
27379
  }
26894
27380
  if (sub === "refine-delay") {
26895
27381
  const raw = rest[0];
26896
27382
  if (raw === void 0) {
26897
- return { message: `${color48.amber("Usage:")} /settings refine-delay <seconds>` };
27383
+ return { message: `${color49.amber("Usage:")} /settings refine-delay <seconds>` };
26898
27384
  }
26899
27385
  const seconds = Number.parseFloat(raw);
26900
27386
  if (Number.isNaN(seconds) || seconds < 0) {
26901
27387
  return {
26902
- message: `${color48.red("Invalid number")}: "${raw}". Enter seconds, e.g. /settings refine-delay 30`
27388
+ message: `${color49.red("Invalid number")}: "${raw}". Enter seconds, e.g. /settings refine-delay 30`
26903
27389
  };
26904
27390
  }
26905
27391
  const ms = Math.round(seconds * 1e3);
26906
27392
  await persistAutonomySetting(persistDeps, (autonomy) => {
26907
27393
  autonomy.enhanceDelayMs = ms;
26908
27394
  });
26909
- return { message: `${color48.green("\u2713")} refine-delay \u2192 ${formatDelay(ms)}` };
27395
+ return { message: `${color49.green("\u2713")} refine-delay \u2192 ${formatDelay(ms)}` };
26910
27396
  }
26911
27397
  if (sub === "refine-language") {
26912
27398
  const raw = (rest[0] ?? "").toLowerCase();
26913
27399
  if (!["original", "english"].includes(raw)) {
26914
27400
  return {
26915
- message: `${color48.amber("Usage:")} /settings refine-language original|english`
27401
+ message: `${color49.amber("Usage:")} /settings refine-language original|english`
26916
27402
  };
26917
27403
  }
26918
27404
  await persistAutonomySetting(persistDeps, (autonomy) => {
26919
27405
  autonomy.enhanceLanguage = raw;
26920
27406
  });
26921
- const label = raw === "original" ? `${color48.cyan("original")} \u2014 use the language you wrote in` : `${color48.cyan("english")} \u2014 translate to English`;
26922
- return { message: `${color48.green("\u2713")} refine-language \u2192 ${label}` };
27407
+ const label = raw === "original" ? `${color49.cyan("original")} \u2014 use the language you wrote in` : `${color49.cyan("english")} \u2014 translate to English`;
27408
+ return { message: `${color49.green("\u2713")} refine-language \u2192 ${label}` };
26923
27409
  }
26924
27410
  if (sub === "refiner-provider") {
26925
27411
  const raw = rest.join(" ").trim();
26926
27412
  const currentProvider = opts.configStore.get().autonomy?.refinerProvider;
26927
27413
  if (!raw) {
26928
27414
  return {
26929
- message: `${color48.amber("Usage:")} /settings refiner-provider <providerId> ${color48.dim('(e.g. "openai", "anthropic")' + (currentProvider ? ` Current: ${currentProvider}` : ""))}`
27415
+ message: `${color49.amber("Usage:")} /settings refiner-provider <providerId> ${color49.dim('(e.g. "openai", "anthropic")' + (currentProvider ? ` Current: ${currentProvider}` : ""))}`
26930
27416
  };
26931
27417
  }
26932
27418
  await persistAutonomySetting(persistDeps, (autonomy) => {
26933
27419
  autonomy.refinerProvider = raw;
26934
27420
  });
26935
27421
  return {
26936
- message: `${color48.green("\u2713")} refiner-provider \u2192 ${color48.cyan(raw)} ${color48.dim("goal refinement will use this provider when refiner-model is also set")}`
27422
+ message: `${color49.green("\u2713")} refiner-provider \u2192 ${color49.cyan(raw)} ${color49.dim("goal refinement will use this provider when refiner-model is also set")}`
26937
27423
  };
26938
27424
  }
26939
27425
  if (sub === "refiner-model") {
@@ -26941,14 +27427,14 @@ function buildSettingsCommand(opts) {
26941
27427
  const currentModel = opts.configStore.get().autonomy?.refinerModel;
26942
27428
  if (!raw) {
26943
27429
  return {
26944
- message: `${color48.amber("Usage:")} /settings refiner-model <modelId> ${color48.dim('(must be a favorite or the active model; e.g. "gpt-4o-mini")' + (currentModel ? ` Current: ${currentModel}` : ""))}`
27430
+ message: `${color49.amber("Usage:")} /settings refiner-model <modelId> ${color49.dim('(must be a favorite or the active model; e.g. "gpt-4o-mini")' + (currentModel ? ` Current: ${currentModel}` : ""))}`
26945
27431
  };
26946
27432
  }
26947
27433
  await persistAutonomySetting(persistDeps, (autonomy) => {
26948
27434
  autonomy.refinerModel = raw;
26949
27435
  });
26950
27436
  return {
26951
- message: `${color48.green("\u2713")} refiner-model \u2192 ${color48.cyan(raw)} ${color48.dim("goal refinement will use this model when it passes favorites/active validation")}`
27437
+ message: `${color49.green("\u2713")} refiner-model \u2192 ${color49.cyan(raw)} ${color49.dim("goal refinement will use this model when it passes favorites/active validation")}`
26952
27438
  };
26953
27439
  }
26954
27440
  if (sub === "refiner-fallback-profile") {
@@ -26956,14 +27442,14 @@ function buildSettingsCommand(opts) {
26956
27442
  const currentProfile = opts.configStore.get().autonomy?.refinerFallbackProfile;
26957
27443
  if (!raw) {
26958
27444
  return {
26959
- message: `${color48.amber("Usage:")} /settings refiner-fallback-profile <name> ${color48.dim('(e.g. "default")' + (currentProfile ? ` Current: ${currentProfile}` : ""))}`
27445
+ message: `${color49.amber("Usage:")} /settings refiner-fallback-profile <name> ${color49.dim('(e.g. "default")' + (currentProfile ? ` Current: ${currentProfile}` : ""))}`
26960
27446
  };
26961
27447
  }
26962
27448
  await persistAutonomySetting(persistDeps, (autonomy) => {
26963
27449
  autonomy.refinerFallbackProfile = raw;
26964
27450
  });
26965
27451
  return {
26966
- message: `${color48.green("\u2713")} refiner-fallback-profile \u2192 ${color48.cyan(raw)} ${color48.dim("goal refinement will use the first valid entry from this profile chain")}`
27452
+ message: `${color49.green("\u2713")} refiner-fallback-profile \u2192 ${color49.cyan(raw)} ${color49.dim("goal refinement will use the first valid entry from this profile chain")}`
26967
27453
  };
26968
27454
  }
26969
27455
  if (sub === "refiner-clear") {
@@ -26973,7 +27459,7 @@ function buildSettingsCommand(opts) {
26973
27459
  autonomy.refinerFallbackProfile = void 0;
26974
27460
  });
26975
27461
  return {
26976
- message: `${color48.green("\u2713")} Refiner config cleared ${color48.dim("goal refinement will use the session provider+model")}`
27462
+ message: `${color49.green("\u2713")} Refiner config cleared ${color49.dim("goal refinement will use the session provider+model")}`
26977
27463
  };
26978
27464
  }
26979
27465
  if (sub === "semver-part") {
@@ -26981,7 +27467,7 @@ function buildSettingsCommand(opts) {
26981
27467
  const parts = ["patch", "minor", "major", "auto"];
26982
27468
  if (!parts.includes(raw)) {
26983
27469
  return {
26984
- message: `${color48.amber("Usage:")} /settings semver-part patch|minor|major|auto`
27470
+ message: `${color49.amber("Usage:")} /settings semver-part patch|minor|major|auto`
26985
27471
  };
26986
27472
  }
26987
27473
  await persistConfigSetting({ ...persistDeps, inProjectConfigPath: void 0 }, (cfg) => {
@@ -26990,13 +27476,13 @@ function buildSettingsCommand(opts) {
26990
27476
  cfg.extensions = ext;
26991
27477
  });
26992
27478
  return {
26993
- message: `${color48.green("\u2713")} semver default part \u2192 ${color48.bold(raw)} ${color48.dim("saved to active profile config; used when /semver or semver_bump gets no explicit part")}`
27479
+ message: `${color49.green("\u2713")} semver default part \u2192 ${color49.bold(raw)} ${color49.dim("saved to active profile config; used when /semver or semver_bump gets no explicit part")}`
26994
27480
  };
26995
27481
  }
26996
27482
  if (sub === "breaker") {
26997
27483
  const raw = (rest[0] ?? "").toLowerCase();
26998
27484
  if (!["on", "off"].includes(raw)) {
26999
- return { message: `${color48.amber("Usage:")} /settings breaker on|off` };
27485
+ return { message: `${color49.amber("Usage:")} /settings breaker on|off` };
27000
27486
  }
27001
27487
  const on = raw === "on";
27002
27488
  await persistConfigSetting(persistDeps, (cfg) => {
@@ -27005,20 +27491,20 @@ function buildSettingsCommand(opts) {
27005
27491
  });
27006
27492
  getProcessRegistry().setBreakerConfig({ enabled: on });
27007
27493
  return {
27008
- message: `${color48.green("\u2713")} circuit breaker \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim(on ? "bash/exec gated on repeated failures; trips arm the kill/reset countdown" : "bash/exec always proceed")}`
27494
+ message: `${color49.green("\u2713")} circuit breaker \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim(on ? "bash/exec gated on repeated failures; trips arm the kill/reset countdown" : "bash/exec always proceed")}`
27009
27495
  };
27010
27496
  }
27011
27497
  if (sub === "breaker-timeout") {
27012
27498
  const raw = rest[0];
27013
27499
  if (raw === void 0) {
27014
27500
  return {
27015
- message: `${color48.amber("Usage:")} /settings breaker-timeout <seconds> ${color48.dim("(0 = manual recovery only)")}`
27501
+ message: `${color49.amber("Usage:")} /settings breaker-timeout <seconds> ${color49.dim("(0 = manual recovery only)")}`
27016
27502
  };
27017
27503
  }
27018
27504
  const seconds = Number.parseFloat(raw);
27019
27505
  if (Number.isNaN(seconds) || seconds < 0) {
27020
27506
  return {
27021
- message: `${color48.red("Invalid number")}: "${raw}". Enter seconds, e.g. /settings breaker-timeout 60`
27507
+ message: `${color49.red("Invalid number")}: "${raw}". Enter seconds, e.g. /settings breaker-timeout 60`
27022
27508
  };
27023
27509
  }
27024
27510
  const ms = Math.round(seconds * 1e3);
@@ -27031,7 +27517,7 @@ function buildSettingsCommand(opts) {
27031
27517
  });
27032
27518
  getProcessRegistry().setBreakerConfig({ autoKillResetMs: ms });
27033
27519
  return {
27034
- message: `${color48.green("\u2713")} breaker kill/reset timeout \u2192 ${ms > 0 ? formatDelay(ms) : color48.dim("manual")} ${color48.dim(ms > 0 ? "statusline shows a countdown when the breaker trips" : "breaker trips require /kill reset")}`
27520
+ message: `${color49.green("\u2713")} breaker kill/reset timeout \u2192 ${ms > 0 ? formatDelay(ms) : color49.dim("manual")} ${color49.dim(ms > 0 ? "statusline shows a countdown when the breaker trips" : "breaker trips require /kill reset")}`
27035
27521
  };
27036
27522
  }
27037
27523
  if (sub === "context-mode") {
@@ -27039,7 +27525,7 @@ function buildSettingsCommand(opts) {
27039
27525
  const modes = ["balanced", "frugal", "deep"];
27040
27526
  if (!modes.includes(raw)) {
27041
27527
  return {
27042
- message: `${color48.amber("Usage:")} /settings context-mode balanced|frugal|deep`
27528
+ message: `${color49.amber("Usage:")} /settings context-mode balanced|frugal|deep`
27043
27529
  };
27044
27530
  }
27045
27531
  await persistConfigSetting(persistDeps, (cfg) => {
@@ -27048,7 +27534,7 @@ function buildSettingsCommand(opts) {
27048
27534
  cfg.context = ctx;
27049
27535
  });
27050
27536
  return {
27051
- message: `${color48.green("\u2713")} context mode \u2192 ${color48.cyan(raw)} ${color48.dim("context window policy")}`
27537
+ message: `${color49.green("\u2713")} context mode \u2192 ${color49.cyan(raw)} ${color49.dim("context window policy")}`
27052
27538
  };
27053
27539
  }
27054
27540
  if (sub === "context-strategy") {
@@ -27056,7 +27542,7 @@ function buildSettingsCommand(opts) {
27056
27542
  const strategies = ["hybrid", "intelligent", "selective"];
27057
27543
  if (!strategies.includes(raw)) {
27058
27544
  return {
27059
- message: `${color48.amber("Usage:")} /settings context-strategy hybrid|intelligent|selective`
27545
+ message: `${color49.amber("Usage:")} /settings context-strategy hybrid|intelligent|selective`
27060
27546
  };
27061
27547
  }
27062
27548
  await persistConfigSetting(persistDeps, (cfg) => {
@@ -27065,13 +27551,13 @@ function buildSettingsCommand(opts) {
27065
27551
  cfg.context = ctx;
27066
27552
  });
27067
27553
  return {
27068
- message: `${color48.green("\u2713")} context strategy \u2192 ${color48.cyan(raw)} ${color48.dim("compactor strategy")}`
27554
+ message: `${color49.green("\u2713")} context strategy \u2192 ${color49.cyan(raw)} ${color49.dim("compactor strategy")}`
27069
27555
  };
27070
27556
  }
27071
27557
  if (sub === "context-auto-compact") {
27072
27558
  const raw = (rest[0] ?? "").toLowerCase();
27073
27559
  if (!["on", "off"].includes(raw)) {
27074
- return { message: `${color48.amber("Usage:")} /settings context-auto-compact on|off` };
27560
+ return { message: `${color49.amber("Usage:")} /settings context-auto-compact on|off` };
27075
27561
  }
27076
27562
  const on = raw === "on";
27077
27563
  await persistConfigSetting(persistDeps, (cfg) => {
@@ -27080,13 +27566,13 @@ function buildSettingsCommand(opts) {
27080
27566
  cfg.context = ctx;
27081
27567
  });
27082
27568
  return {
27083
- message: `${color48.green("\u2713")} context auto-compact \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim("auto-compact context when thresholds crossed")}`
27569
+ message: `${color49.green("\u2713")} context auto-compact \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("auto-compact context when thresholds crossed")}`
27084
27570
  };
27085
27571
  }
27086
27572
  if (sub === "nextsteps-tool") {
27087
27573
  const raw = (rest[0] ?? "").toLowerCase();
27088
27574
  if (!["on", "off"].includes(raw)) {
27089
- return { message: `${color48.amber("Usage:")} /settings nextsteps-tool on|off` };
27575
+ return { message: `${color49.amber("Usage:")} /settings nextsteps-tool on|off` };
27090
27576
  }
27091
27577
  const on = raw === "on";
27092
27578
  await persistConfigSetting(persistDeps, (cfg) => {
@@ -27095,7 +27581,7 @@ function buildSettingsCommand(opts) {
27095
27581
  cfg.tools = tools;
27096
27582
  });
27097
27583
  return {
27098
- message: `${color48.green("\u2713")} nextsteps tool \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim("takes effect in the next session")}`
27584
+ message: `${color49.green("\u2713")} nextsteps tool \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("takes effect in the next session")}`
27099
27585
  };
27100
27586
  }
27101
27587
  if (sub === "token-saving") {
@@ -27103,7 +27589,7 @@ function buildSettingsCommand(opts) {
27103
27589
  const tiers = ["off", "minimal", "light", "medium", "aggressive"];
27104
27590
  if (!tiers.includes(raw)) {
27105
27591
  return {
27106
- message: `${color48.amber("Usage:")} /settings token-saving off|minimal|light|medium|aggressive`
27592
+ message: `${color49.amber("Usage:")} /settings token-saving off|minimal|light|medium|aggressive`
27107
27593
  };
27108
27594
  }
27109
27595
  await persistConfigSetting(persistDeps, (cfg) => {
@@ -27112,47 +27598,47 @@ function buildSettingsCommand(opts) {
27112
27598
  cfg.features = feat;
27113
27599
  });
27114
27600
  return {
27115
- message: `${color48.green("\u2713")} token-saving \u2192 ${color48.cyan(raw)} ${color48.dim("token-saving mode")}`
27601
+ message: `${color49.green("\u2713")} token-saving \u2192 ${color49.cyan(raw)} ${color49.dim("token-saving mode")}`
27116
27602
  };
27117
27603
  }
27118
27604
  if (sub === "max-concurrent") {
27119
27605
  const raw = rest[0];
27120
27606
  if (raw === void 0) {
27121
27607
  return {
27122
- message: `${color48.amber("Usage:")} /settings max-concurrent <n> ${color48.dim("(0 = default)")}`
27608
+ message: `${color49.amber("Usage:")} /settings max-concurrent <n> ${color49.dim("(0 = default)")}`
27123
27609
  };
27124
27610
  }
27125
27611
  const n = Number.parseInt(raw, 10);
27126
27612
  if (Number.isNaN(n) || n < 0) {
27127
27613
  return {
27128
- message: `${color48.red("Invalid number")}: "${raw}". Enter a non-negative integer (0 = default)`
27614
+ message: `${color49.red("Invalid number")}: "${raw}". Enter a non-negative integer (0 = default)`
27129
27615
  };
27130
27616
  }
27131
27617
  await persistConfigSetting(persistDeps, (cfg) => {
27132
27618
  cfg.maxConcurrent = n;
27133
27619
  });
27134
27620
  return {
27135
- message: `${color48.green("\u2713")} max-concurrent \u2192 ${color48.cyan(n === 0 ? "default" : String(n))} ${color48.dim("max concurrent subagents")}`
27621
+ message: `${color49.green("\u2713")} max-concurrent \u2192 ${color49.cyan(n === 0 ? "default" : String(n))} ${color49.dim("max concurrent subagents")}`
27136
27622
  };
27137
27623
  }
27138
27624
  if (sub === "title-animation") {
27139
27625
  const raw = (rest[0] ?? "").toLowerCase();
27140
27626
  if (!["on", "off"].includes(raw)) {
27141
- return { message: `${color48.amber("Usage:")} /settings title-animation on|off` };
27627
+ return { message: `${color49.amber("Usage:")} /settings title-animation on|off` };
27142
27628
  }
27143
27629
  const on = raw === "on";
27144
27630
  await persistAutonomySetting(persistDeps, (autonomy) => {
27145
27631
  autonomy.terminalTitleAnimation = on;
27146
27632
  });
27147
27633
  return {
27148
- message: `${color48.green("\u2713")} title animation \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim("terminal title animation")}`
27634
+ message: `${color49.green("\u2713")} title animation \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("terminal title animation")}`
27149
27635
  };
27150
27636
  }
27151
27637
  if (sub === "reasoning") {
27152
27638
  const raw = (rest[0] ?? "").toLowerCase();
27153
27639
  const modes = ["auto", "on", "off"];
27154
27640
  if (!modes.includes(raw)) {
27155
- return { message: `${color48.amber("Usage:")} /settings reasoning auto|on|off` };
27641
+ return { message: `${color49.amber("Usage:")} /settings reasoning auto|on|off` };
27156
27642
  }
27157
27643
  await persistConfigSetting(persistDeps, (cfg) => {
27158
27644
  const mr = cfg.modelRuntime;
@@ -27160,14 +27646,14 @@ function buildSettingsCommand(opts) {
27160
27646
  reasoning.mode = raw;
27161
27647
  cfg.modelRuntime = { ...mr, reasoning };
27162
27648
  });
27163
- return { message: `${color48.green("\u2713")} reasoning mode \u2192 ${color48.bold(raw)}` };
27649
+ return { message: `${color49.green("\u2713")} reasoning mode \u2192 ${color49.bold(raw)}` };
27164
27650
  }
27165
27651
  if (sub === "reasoning-effort") {
27166
27652
  const raw = (rest[0] ?? "").toLowerCase();
27167
27653
  const efforts = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
27168
27654
  if (!efforts.includes(raw)) {
27169
27655
  return {
27170
- message: `${color48.amber("Usage:")} /settings reasoning-effort none|minimal|low|medium|high|xhigh|max`
27656
+ message: `${color49.amber("Usage:")} /settings reasoning-effort none|minimal|low|medium|high|xhigh|max`
27171
27657
  };
27172
27658
  }
27173
27659
  await persistConfigSetting(persistDeps, (cfg) => {
@@ -27176,12 +27662,12 @@ function buildSettingsCommand(opts) {
27176
27662
  reasoning.effort = raw;
27177
27663
  cfg.modelRuntime = { ...mr, reasoning };
27178
27664
  });
27179
- return { message: `${color48.green("\u2713")} reasoning effort \u2192 ${color48.bold(raw)}` };
27665
+ return { message: `${color49.green("\u2713")} reasoning effort \u2192 ${color49.bold(raw)}` };
27180
27666
  }
27181
27667
  if (sub === "reasoning-preserve") {
27182
27668
  const raw = (rest[0] ?? "").toLowerCase();
27183
27669
  if (!["on", "off"].includes(raw)) {
27184
- return { message: `${color48.amber("Usage:")} /settings reasoning-preserve on|off` };
27670
+ return { message: `${color49.amber("Usage:")} /settings reasoning-preserve on|off` };
27185
27671
  }
27186
27672
  const on = raw === "on";
27187
27673
  await persistConfigSetting(persistDeps, (cfg) => {
@@ -27191,24 +27677,24 @@ function buildSettingsCommand(opts) {
27191
27677
  cfg.modelRuntime = { ...mr, reasoning };
27192
27678
  });
27193
27679
  return {
27194
- message: `${color48.green("\u2713")} reasoning preserve \u2192 ${on ? color48.cyan("on") : color48.dim("off")}`
27680
+ message: `${color49.green("\u2713")} reasoning preserve \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
27195
27681
  };
27196
27682
  }
27197
27683
  if (sub === "cache-ttl") {
27198
27684
  const raw = (rest[0] ?? "").toLowerCase();
27199
27685
  if (!["5m", "1h"].includes(raw)) {
27200
- return { message: `${color48.amber("Usage:")} /settings cache-ttl 5m|1h` };
27686
+ return { message: `${color49.amber("Usage:")} /settings cache-ttl 5m|1h` };
27201
27687
  }
27202
27688
  await persistConfigSetting(persistDeps, (cfg) => {
27203
27689
  const mr = cfg.modelRuntime;
27204
27690
  cfg.modelRuntime = { ...mr, cache: { ttl: raw } };
27205
27691
  });
27206
- return { message: `${color48.green("\u2713")} cache TTL \u2192 ${color48.bold(raw)}` };
27692
+ return { message: `${color49.green("\u2713")} cache TTL \u2192 ${color49.bold(raw)}` };
27207
27693
  }
27208
27694
  if (sub === "mcp") {
27209
27695
  const raw = (rest[0] ?? "").toLowerCase();
27210
27696
  if (!["on", "off"].includes(raw))
27211
- return { message: `${color48.amber("Usage:")} /settings mcp on|off` };
27697
+ return { message: `${color49.amber("Usage:")} /settings mcp on|off` };
27212
27698
  const on = raw === "on";
27213
27699
  await persistConfigSetting(persistDeps, (cfg) => {
27214
27700
  const feats = cfg.features ?? {};
@@ -27216,13 +27702,13 @@ function buildSettingsCommand(opts) {
27216
27702
  cfg.features = feats;
27217
27703
  });
27218
27704
  return {
27219
- message: `${color48.green("\u2713")} MCP features \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim("restart to apply")}`
27705
+ message: `${color49.green("\u2713")} MCP features \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("restart to apply")}`
27220
27706
  };
27221
27707
  }
27222
27708
  if (sub === "plugins") {
27223
27709
  const raw = (rest[0] ?? "").toLowerCase();
27224
27710
  if (!["on", "off"].includes(raw))
27225
- return { message: `${color48.amber("Usage:")} /settings plugins on|off` };
27711
+ return { message: `${color49.amber("Usage:")} /settings plugins on|off` };
27226
27712
  const on = raw === "on";
27227
27713
  await persistConfigSetting(persistDeps, (cfg) => {
27228
27714
  const feats = cfg.features ?? {};
@@ -27230,13 +27716,13 @@ function buildSettingsCommand(opts) {
27230
27716
  cfg.features = feats;
27231
27717
  });
27232
27718
  return {
27233
- message: `${color48.green("\u2713")} Plugin features \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim("restart to apply")}`
27719
+ message: `${color49.green("\u2713")} Plugin features \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("restart to apply")}`
27234
27720
  };
27235
27721
  }
27236
27722
  if (sub === "memory") {
27237
27723
  const raw = (rest[0] ?? "").toLowerCase();
27238
27724
  if (!["on", "off"].includes(raw))
27239
- return { message: `${color48.amber("Usage:")} /settings memory on|off` };
27725
+ return { message: `${color49.amber("Usage:")} /settings memory on|off` };
27240
27726
  const on = raw === "on";
27241
27727
  await persistConfigSetting(persistDeps, (cfg) => {
27242
27728
  const feats = cfg.features ?? {};
@@ -27244,13 +27730,13 @@ function buildSettingsCommand(opts) {
27244
27730
  cfg.features = feats;
27245
27731
  });
27246
27732
  return {
27247
- message: `${color48.green("\u2713")} Memory features \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim("restart to apply")}`
27733
+ message: `${color49.green("\u2713")} Memory features \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("restart to apply")}`
27248
27734
  };
27249
27735
  }
27250
27736
  if (sub === "skills") {
27251
27737
  const raw = (rest[0] ?? "").toLowerCase();
27252
27738
  if (!["on", "off"].includes(raw))
27253
- return { message: `${color48.amber("Usage:")} /settings skills on|off` };
27739
+ return { message: `${color49.amber("Usage:")} /settings skills on|off` };
27254
27740
  const on = raw === "on";
27255
27741
  await persistConfigSetting(persistDeps, (cfg) => {
27256
27742
  const feats = cfg.features ?? {};
@@ -27258,13 +27744,13 @@ function buildSettingsCommand(opts) {
27258
27744
  cfg.features = feats;
27259
27745
  });
27260
27746
  return {
27261
- message: `${color48.green("\u2713")} Skills features \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim("restart to apply")}`
27747
+ message: `${color49.green("\u2713")} Skills features \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("restart to apply")}`
27262
27748
  };
27263
27749
  }
27264
27750
  if (sub === "models-registry") {
27265
27751
  const raw = (rest[0] ?? "").toLowerCase();
27266
27752
  if (!["on", "off"].includes(raw))
27267
- return { message: `${color48.amber("Usage:")} /settings models-registry on|off` };
27753
+ return { message: `${color49.amber("Usage:")} /settings models-registry on|off` };
27268
27754
  const on = raw === "on";
27269
27755
  await persistConfigSetting(persistDeps, (cfg) => {
27270
27756
  const feats = cfg.features ?? {};
@@ -27272,7 +27758,7 @@ function buildSettingsCommand(opts) {
27272
27758
  cfg.features = feats;
27273
27759
  });
27274
27760
  return {
27275
- message: `${color48.green("\u2713")} Models registry \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim("restart to apply")}`
27761
+ message: `${color49.green("\u2713")} Models registry \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("restart to apply")}`
27276
27762
  };
27277
27763
  }
27278
27764
  if (sub === "stream-fleet") {
@@ -27280,7 +27766,7 @@ function buildSettingsCommand(opts) {
27280
27766
  const mode = raw === "on" ? "full" : raw === "off" || raw === "full" ? raw : void 0;
27281
27767
  if (!mode)
27282
27768
  return {
27283
- message: `${color48.amber("Usage:")} /settings stream-fleet off|full (on = full)`
27769
+ message: `${color49.amber("Usage:")} /settings stream-fleet off|full (on = full)`
27284
27770
  };
27285
27771
  await persistAutonomySetting(persistDeps, (autonomy) => {
27286
27772
  autonomy.fleetChatVerbosity = mode;
@@ -27288,43 +27774,43 @@ function buildSettingsCommand(opts) {
27288
27774
  opts.fleetStreamController?.setMode(mode);
27289
27775
  const desc = mode === "full" ? "every subagent tool call and message in chat" : "subagent chat lines hidden (F2/F3 stay live)";
27290
27776
  return {
27291
- message: `${color48.green("\u2713")} fleet chat \u2192 ${color48.cyan(mode)} ${color48.dim(desc)}`
27777
+ message: `${color49.green("\u2713")} fleet chat \u2192 ${color49.cyan(mode)} ${color49.dim(desc)}`
27292
27778
  };
27293
27779
  }
27294
27780
  if (sub === "chime") {
27295
27781
  const raw = (rest[0] ?? "").toLowerCase();
27296
27782
  if (!["on", "off"].includes(raw))
27297
- return { message: `${color48.amber("Usage:")} /settings chime on|off` };
27783
+ return { message: `${color49.amber("Usage:")} /settings chime on|off` };
27298
27784
  const on = raw === "on";
27299
27785
  await persistAutonomySetting(persistDeps, (autonomy) => {
27300
27786
  autonomy.chime = on;
27301
27787
  });
27302
27788
  return {
27303
- message: `${color48.green("\u2713")} completion chime \u2192 ${on ? color48.cyan("on") : color48.dim("off")}`
27789
+ message: `${color49.green("\u2713")} completion chime \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
27304
27790
  };
27305
27791
  }
27306
27792
  if (sub === "confirm-exit") {
27307
27793
  const raw = (rest[0] ?? "").toLowerCase();
27308
27794
  if (!["on", "off"].includes(raw))
27309
- return { message: `${color48.amber("Usage:")} /settings confirm-exit on|off` };
27795
+ return { message: `${color49.amber("Usage:")} /settings confirm-exit on|off` };
27310
27796
  const on = raw === "on";
27311
27797
  await persistAutonomySetting(persistDeps, (autonomy) => {
27312
27798
  autonomy.confirmExit = on;
27313
27799
  });
27314
27800
  return {
27315
- message: `${color48.green("\u2713")} confirm before exit \u2192 ${on ? color48.cyan("on") : color48.dim("off")}`
27801
+ message: `${color49.green("\u2713")} confirm before exit \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
27316
27802
  };
27317
27803
  }
27318
27804
  if (sub === "max-iterations") {
27319
27805
  const raw = rest[0];
27320
27806
  if (raw === void 0)
27321
27807
  return {
27322
- message: `${color48.amber("Usage:")} /settings max-iterations <n> ${color48.dim("(0 = default)")}`
27808
+ message: `${color49.amber("Usage:")} /settings max-iterations <n> ${color49.dim("(0 = default)")}`
27323
27809
  };
27324
27810
  const n = Number.parseInt(raw, 10);
27325
27811
  if (Number.isNaN(n) || n < 0)
27326
27812
  return {
27327
- message: `${color48.red("Invalid number")}: "${raw}". Enter a non-negative integer.`
27813
+ message: `${color49.red("Invalid number")}: "${raw}". Enter a non-negative integer.`
27328
27814
  };
27329
27815
  await persistConfigSetting(persistDeps, (cfg) => {
27330
27816
  const tools = cfg.tools ?? {};
@@ -27332,31 +27818,31 @@ function buildSettingsCommand(opts) {
27332
27818
  cfg.tools = tools;
27333
27819
  });
27334
27820
  return {
27335
- message: `${color48.green("\u2713")} max iterations \u2192 ${color48.cyan(n === 0 ? "default" : String(n))} ${color48.dim("agent pauses after this many iterations")}`
27821
+ message: `${color49.green("\u2713")} max iterations \u2192 ${color49.cyan(n === 0 ? "default" : String(n))} ${color49.dim("agent pauses after this many iterations")}`
27336
27822
  };
27337
27823
  }
27338
27824
  if (sub === "auto-proceed-max-iterations") {
27339
27825
  const raw = rest[0];
27340
27826
  if (raw === void 0)
27341
27827
  return {
27342
- message: `${color48.amber("Usage:")} /settings auto-proceed-max-iterations <n> ${color48.dim("(0 = unlimited)")}`
27828
+ message: `${color49.amber("Usage:")} /settings auto-proceed-max-iterations <n> ${color49.dim("(0 = unlimited)")}`
27343
27829
  };
27344
27830
  const n = Number.parseInt(raw, 10);
27345
27831
  if (Number.isNaN(n) || n < 0)
27346
27832
  return {
27347
- message: `${color48.red("Invalid number")}: "${raw}". Enter a non-negative integer.`
27833
+ message: `${color49.red("Invalid number")}: "${raw}". Enter a non-negative integer.`
27348
27834
  };
27349
27835
  await persistAutonomySetting(persistDeps, (autonomy) => {
27350
27836
  autonomy.autoProceedMaxIterations = n;
27351
27837
  });
27352
27838
  return {
27353
- message: `${color48.green("\u2713")} auto-proceed max iterations \u2192 ${color48.cyan(n === 0 ? "unlimited" : String(n))}`
27839
+ message: `${color49.green("\u2713")} auto-proceed max iterations \u2192 ${color49.cyan(n === 0 ? "unlimited" : String(n))}`
27354
27840
  };
27355
27841
  }
27356
27842
  if (sub === "index-on-start") {
27357
27843
  const raw = (rest[0] ?? "").toLowerCase();
27358
27844
  if (!["on", "off"].includes(raw))
27359
- return { message: `${color48.amber("Usage:")} /settings index-on-start on|off` };
27845
+ return { message: `${color49.amber("Usage:")} /settings index-on-start on|off` };
27360
27846
  const on = raw === "on";
27361
27847
  await persistConfigSetting(persistDeps, (cfg) => {
27362
27848
  const idx = cfg.indexing ?? {};
@@ -27364,7 +27850,7 @@ function buildSettingsCommand(opts) {
27364
27850
  cfg.indexing = idx;
27365
27851
  });
27366
27852
  return {
27367
- message: `${color48.green("\u2713")} index on session start \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim("effective next session")}`
27853
+ message: `${color49.green("\u2713")} index on session start \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("effective next session")}`
27368
27854
  };
27369
27855
  }
27370
27856
  if (sub === "log-level") {
@@ -27372,21 +27858,21 @@ function buildSettingsCommand(opts) {
27372
27858
  const levels = ["error", "warn", "info", "debug", "trace"];
27373
27859
  if (!levels.includes(raw))
27374
27860
  return {
27375
- message: `${color48.amber("Usage:")} /settings log-level error|warn|info|debug|trace`
27861
+ message: `${color49.amber("Usage:")} /settings log-level error|warn|info|debug|trace`
27376
27862
  };
27377
27863
  await persistConfigSetting(persistDeps, (cfg) => {
27378
27864
  const log = cfg.log ?? {};
27379
27865
  log.level = raw;
27380
27866
  cfg.log = log;
27381
27867
  });
27382
- return { message: `${color48.green("\u2713")} log level \u2192 ${color48.cyan(raw)}` };
27868
+ return { message: `${color49.green("\u2713")} log level \u2192 ${color49.cyan(raw)}` };
27383
27869
  }
27384
27870
  if (sub === "audit-level") {
27385
27871
  const raw = (rest[0] ?? "").toLowerCase();
27386
27872
  const levels = ["minimal", "standard", "full"];
27387
27873
  if (!levels.includes(raw))
27388
27874
  return {
27389
- message: `${color48.amber("Usage:")} /settings audit-level minimal|standard|full`
27875
+ message: `${color49.amber("Usage:")} /settings audit-level minimal|standard|full`
27390
27876
  };
27391
27877
  await persistConfigSetting(persistDeps, (cfg) => {
27392
27878
  const sess = cfg.session ?? {};
@@ -27394,38 +27880,38 @@ function buildSettingsCommand(opts) {
27394
27880
  cfg.session = sess;
27395
27881
  });
27396
27882
  return {
27397
- message: `${color48.green("\u2713")} audit level \u2192 ${color48.cyan(raw)} ${color48.dim("restart to apply")}`
27883
+ message: `${color49.green("\u2713")} audit level \u2192 ${color49.cyan(raw)} ${color49.dim("restart to apply")}`
27398
27884
  };
27399
27885
  }
27400
27886
  if (sub === "thinking-word") {
27401
27887
  const raw = rest.join(" ").trim();
27402
27888
  if (!raw)
27403
27889
  return {
27404
- message: `${color48.amber("Usage:")} /settings thinking-word <word> ${color48.dim('single short word, e.g. "thinking", "vibing", "cooking"')}`
27890
+ message: `${color49.amber("Usage:")} /settings thinking-word <word> ${color49.dim('single short word, e.g. "thinking", "vibing", "cooking"')}`
27405
27891
  };
27406
27892
  if (raw.length > 16)
27407
- return { message: `${color48.red("Word too long")}: max 16 characters.` };
27893
+ return { message: `${color49.red("Word too long")}: max 16 characters.` };
27408
27894
  await persistAutonomySetting(persistDeps, (autonomy) => {
27409
27895
  autonomy.thinkingWord = raw;
27410
27896
  });
27411
- return { message: `${color48.green("\u2713")} thinking word \u2192 ${color48.cyan(raw)}` };
27897
+ return { message: `${color49.green("\u2713")} thinking word \u2192 ${color49.cyan(raw)}` };
27412
27898
  }
27413
27899
  if (sub === "statusline") {
27414
27900
  const raw = (rest[0] ?? "").toLowerCase();
27415
27901
  const modes = ["minimum", "detailed", "no-color"];
27416
27902
  if (!modes.includes(raw))
27417
27903
  return {
27418
- message: `${color48.amber("Usage:")} /settings statusline minimum|detailed|no-color`
27904
+ message: `${color49.amber("Usage:")} /settings statusline minimum|detailed|no-color`
27419
27905
  };
27420
27906
  await persistAutonomySetting(persistDeps, (autonomy) => {
27421
27907
  autonomy.statuslineMode = raw;
27422
27908
  });
27423
- return { message: `${color48.green("\u2713")} statusline mode \u2192 ${color48.cyan(raw)}` };
27909
+ return { message: `${color49.green("\u2713")} statusline mode \u2192 ${color49.cyan(raw)}` };
27424
27910
  }
27425
27911
  if (sub === "read-symbols") {
27426
27912
  const raw = (rest[0] ?? "").toLowerCase();
27427
27913
  if (!["on", "off"].includes(raw)) {
27428
- return { message: `${color48.amber("Usage:")} /settings read-symbols on|off` };
27914
+ return { message: `${color49.amber("Usage:")} /settings read-symbols on|off` };
27429
27915
  }
27430
27916
  const on = raw === "on";
27431
27917
  await persistAutonomySetting(persistDeps, (autonomy) => {
@@ -27435,7 +27921,7 @@ function buildSettingsCommand(opts) {
27435
27921
  opts.context.meta["tools.read.advancedMode"] = on;
27436
27922
  }
27437
27923
  return {
27438
- message: `${color48.green("\u2713")} read symbols \u2192 ${on ? color48.cyan("on") : color48.dim("off")} ${color48.dim(on ? "codebase-index symbols will be included in read tool results" : "read tool returns file content only")}`
27924
+ message: `${color49.green("\u2713")} read symbols \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim(on ? "codebase-index symbols will be included in read tool results" : "read tool returns file content only")}`
27439
27925
  };
27440
27926
  }
27441
27927
  if (sub === "animation") {
@@ -27443,19 +27929,19 @@ function buildSettingsCommand(opts) {
27443
27929
  const styles = ["rainbow", "wave", "pulse", "dots", "breathe", "cycle"];
27444
27930
  if (!styles.includes(raw))
27445
27931
  return {
27446
- message: `${color48.amber("Usage:")} /settings animation rainbow|wave|pulse|dots|breathe|cycle`
27932
+ message: `${color49.amber("Usage:")} /settings animation rainbow|wave|pulse|dots|breathe|cycle`
27447
27933
  };
27448
27934
  await persistAutonomySetting(persistDeps, (autonomy) => {
27449
27935
  autonomy.animationStyle = raw;
27450
27936
  });
27451
- return { message: `${color48.green("\u2713")} animation style \u2192 ${color48.cyan(raw)}` };
27937
+ return { message: `${color49.green("\u2713")} animation style \u2192 ${color49.cyan(raw)}` };
27452
27938
  }
27453
27939
  return {
27454
- message: `${color48.red("Unknown setting")} "${sub}". ${unknownSubcommand(sub, ["delay", "mode", "hints", "debug-stream", "config-scope", "fs-access", "refine", "refine-delay", "refine-language", "refiner-provider", "refiner-model", "refiner-fallback-profile", "refiner-clear", "semver-part", "breaker", "breaker-timeout", "context-mode", "context-strategy", "context-auto-compact", "token-saving", "max-concurrent", "title-animation", "reasoning", "reasoning-effort", "reasoning-preserve", "cache-ttl", "stream-fleet", "chime", "confirm-exit", "mcp", "plugins", "memory", "skills", "models-registry", "max-iterations", "auto-proceed-max-iterations", "index-on-start", "log-level", "audit-level", "thinking-word", "statusline", "animation", "read-symbols", "defaults"], "settings")}`
27940
+ message: `${color49.red("Unknown setting")} "${sub}". ${unknownSubcommand(sub, ["delay", "mode", "hints", "debug-stream", "config-scope", "fs-access", "refine", "refine-delay", "refine-language", "refiner-provider", "refiner-model", "refiner-fallback-profile", "refiner-clear", "semver-part", "breaker", "breaker-timeout", "context-mode", "context-strategy", "context-auto-compact", "token-saving", "max-concurrent", "title-animation", "reasoning", "reasoning-effort", "reasoning-preserve", "cache-ttl", "stream-fleet", "chime", "confirm-exit", "mcp", "plugins", "memory", "skills", "models-registry", "max-iterations", "auto-proceed-max-iterations", "index-on-start", "log-level", "audit-level", "thinking-word", "statusline", "animation", "read-symbols", "defaults"], "settings")}`
27455
27941
  };
27456
27942
  } catch (err) {
27457
27943
  return {
27458
- message: `${color48.red("Settings error")}: ${toErrorMessage24(err)}`
27944
+ message: `${color49.red("Settings error")}: ${toErrorMessage24(err)}`
27459
27945
  };
27460
27946
  }
27461
27947
  }
@@ -27464,7 +27950,7 @@ function buildSettingsCommand(opts) {
27464
27950
 
27465
27951
  // src/slash-commands/shadow.ts
27466
27952
  import { ToolValidationError as ToolValidationError5 } from "@wrongstack/core/types";
27467
- import { color as color49 } from "@wrongstack/core/utils";
27953
+ import { color as color50 } from "@wrongstack/core/utils";
27468
27954
  var DEFAULT_SHADOW_INTERVAL_MS = 3e4;
27469
27955
  var MIN_SHADOW_INTERVAL_MS = 5e3;
27470
27956
  function buildShadowCommand(opts) {
@@ -27534,7 +28020,7 @@ function buildShadowCommand(opts) {
27534
28020
  if (opts.shadowController?.activeId != null) {
27535
28021
  return {
27536
28022
  message: [
27537
- `${color49.yellow("\u26A0")} A Shadow Agent is already running (${opts.shadowController.activeId.slice(0, 8)}).`,
28023
+ `${color50.yellow("\u26A0")} A Shadow Agent is already running (${opts.shadowController.activeId.slice(0, 8)}).`,
27538
28024
  "",
27539
28025
  "Only one Shadow Agent instance is allowed per session.",
27540
28026
  "Use /shadow status to view the current instance."
@@ -27573,16 +28059,16 @@ function buildShadowCommand(opts) {
27573
28059
  shadowIntervalMs: intervalMs
27574
28060
  });
27575
28061
  return {
27576
- message: `${color49.green("\u2713")} Shadow Agent queued: ${spawnId}
27577
- ${color49.dim("Mode:")} one-shot quiet check
27578
- ${color49.dim("Model:")} ${modelRef.label}`
28062
+ message: `${color50.green("\u2713")} Shadow Agent queued: ${spawnId}
28063
+ ${color50.dim("Mode:")} one-shot quiet check
28064
+ ${color50.dim("Model:")} ${modelRef.label}`
27579
28065
  };
27580
28066
  }
27581
28067
  case "stop": {
27582
28068
  const activeId = opts.shadowController?.activeId;
27583
28069
  if (!activeId) {
27584
28070
  return {
27585
- message: `${color49.yellow("\u26A0")} No active Shadow Agent is registered for this session.`
28071
+ message: `${color50.yellow("\u26A0")} No active Shadow Agent is registered for this session.`
27586
28072
  };
27587
28073
  }
27588
28074
  if (!opts.onFleetTerminate) {
@@ -27591,12 +28077,12 @@ ${color49.dim("Model:")} ${modelRef.label}`
27591
28077
  const ok = await opts.onFleetTerminate(activeId);
27592
28078
  if (ok) {
27593
28079
  opts.shadowController?.clear();
27594
- return { message: `${color49.green("\u2713")} Shadow Agent stopped: ${activeId}` };
28080
+ return { message: `${color50.green("\u2713")} Shadow Agent stopped: ${activeId}` };
27595
28081
  }
27596
28082
  return {
27597
28083
  message: [
27598
- `${color49.red("\u2717")} Failed to stop Shadow Agent ${color49.bold(activeId)}.`,
27599
- `It may already be stopped. Use ${color49.bold("/shadow status")} to inspect active agents.`
28084
+ `${color50.red("\u2717")} Failed to stop Shadow Agent ${color50.bold(activeId)}.`,
28085
+ `It may already be stopped. Use ${color50.bold("/shadow status")} to inspect active agents.`
27600
28086
  ].join("\n")
27601
28087
  };
27602
28088
  }
@@ -27624,10 +28110,10 @@ ${color49.dim("Model:")} ${modelRef.label}`
27624
28110
  opts.shadowController?.clear();
27625
28111
  return {
27626
28112
  message: [
27627
- `${color49.red("\u26A0")} HOOP: Stopped ${killed} running agent(s)`,
28113
+ `${color50.red("\u26A0")} HOOP: Stopped ${killed} running agent(s)`,
27628
28114
  "",
27629
- `Target: ${color49.bold("all")}`,
27630
- `Reason: ${color49.yellow(reason)}`
28115
+ `Target: ${color50.bold("all")}`,
28116
+ `Reason: ${color50.yellow(reason)}`
27631
28117
  ].join("\n")
27632
28118
  };
27633
28119
  }
@@ -27641,10 +28127,10 @@ ${color49.dim("Model:")} ${modelRef.label}`
27641
28127
  }
27642
28128
  return {
27643
28129
  message: [
27644
- ok ? `${color49.red("\u26A0")} HOOP: Stopped agent` : `${color49.red("\u2717")} HOOP: Failed to stop agent`,
28130
+ ok ? `${color50.red("\u26A0")} HOOP: Stopped agent` : `${color50.red("\u2717")} HOOP: Failed to stop agent`,
27645
28131
  "",
27646
- `Target: ${color49.bold(targetId)}`,
27647
- `Reason: ${color49.yellow(reason)}`,
28132
+ `Target: ${color50.bold(targetId)}`,
28133
+ `Reason: ${color50.yellow(reason)}`,
27648
28134
  agentInfo ? `
27649
28135
  Agent info:
27650
28136
  ${agentInfo}` : ""
@@ -27669,7 +28155,7 @@ Current default: ${defaultModelRef.label}`
27669
28155
  opts.shadowController?.setDefaults?.({ provider: parsed.provider, model: parsed.model });
27670
28156
  return {
27671
28157
  message: `/shadow model ${parsed.label}
27672
- ${color49.dim("Model will be applied on next /shadow start")}`
28158
+ ${color50.dim("Model will be applied on next /shadow start")}`
27673
28159
  };
27674
28160
  }
27675
28161
  case "interval": {
@@ -27689,7 +28175,7 @@ Current default: ${DEFAULT_SHADOW_INTERVAL_MS}ms (30 seconds)`
27689
28175
  opts.shadowController?.setDefaults?.({ intervalMs: ms });
27690
28176
  return {
27691
28177
  message: `/shadow interval ${ms}ms
27692
- ${color49.dim("Interval will be applied on next /shadow start")}`
28178
+ ${color50.dim("Interval will be applied on next /shadow start")}`
27693
28179
  };
27694
28180
  }
27695
28181
  default: {
@@ -27962,7 +28448,7 @@ function buildStatuslineCommand(deps) {
27962
28448
  }
27963
28449
 
27964
28450
  // src/slash-commands/supervisor.ts
27965
- import { color as color50 } from "@wrongstack/core/utils";
28451
+ import { color as color51 } from "@wrongstack/core/utils";
27966
28452
  function fmtAge4(at) {
27967
28453
  const s = Math.max(0, Math.round((Date.now() - at) / 1e3));
27968
28454
  if (s < 60) return `${s}s ago`;
@@ -27998,13 +28484,13 @@ function buildSupervisorCommand(opts) {
27998
28484
  }
27999
28485
  if (sub === "on") {
28000
28486
  supervisor.start();
28001
- const msg2 = `Fleet supervisor ${color50.green("armed")} \u2014 evaluating every ${Math.round(supervisor.configSnapshot().intervalMs / 1e3)}s.`;
28487
+ const msg2 = `Fleet supervisor ${color51.green("armed")} \u2014 evaluating every ${Math.round(supervisor.configSnapshot().intervalMs / 1e3)}s.`;
28002
28488
  opts.renderer.write(msg2);
28003
28489
  return { message: msg2 };
28004
28490
  }
28005
28491
  if (sub === "off") {
28006
28492
  supervisor.stop();
28007
- const msg2 = `Fleet supervisor ${color50.yellow("disarmed")} \u2014 no further automatic interventions this session.`;
28493
+ const msg2 = `Fleet supervisor ${color51.yellow("disarmed")} \u2014 no further automatic interventions this session.`;
28008
28494
  opts.renderer.write(msg2);
28009
28495
  return { message: msg2 };
28010
28496
  }
@@ -28017,10 +28503,10 @@ function buildSupervisorCommand(opts) {
28017
28503
  return { message: msg3 };
28018
28504
  }
28019
28505
  const lines2 = entries.map((e) => {
28020
- const who = e.subagentId ? ` ${color50.cyan(e.subagentId)}` : "";
28506
+ const who = e.subagentId ? ` ${color51.cyan(e.subagentId)}` : "";
28021
28507
  const task = e.taskId ? ` task=${e.taskId.slice(0, 8)}` : "";
28022
- const outcome = e.outcome === "approved" ? color50.green(e.outcome) : e.outcome === "denied" || e.outcome === "error" ? color50.red(e.outcome) : color50.yellow(e.outcome);
28023
- return `${color50.dim(fmtAge4(e.at))} ${e.kind}${who}${task} \u2192 ${e.proposedAction} [${outcome}] ${color50.dim(e.detail)}`;
28508
+ const outcome = e.outcome === "approved" ? color51.green(e.outcome) : e.outcome === "denied" || e.outcome === "error" ? color51.red(e.outcome) : color51.yellow(e.outcome);
28509
+ return `${color51.dim(fmtAge4(e.at))} ${e.kind}${who}${task} \u2192 ${e.proposedAction} [${outcome}] ${color51.dim(e.detail)}`;
28024
28510
  });
28025
28511
  const msg2 = lines2.join("\n");
28026
28512
  opts.renderer.write(msg2);
@@ -28030,12 +28516,12 @@ function buildSupervisorCommand(opts) {
28030
28516
  const history = supervisor.history();
28031
28517
  const last = history[history.length - 1];
28032
28518
  const lines = [
28033
- `Fleet supervisor: ${supervisor.isRunning() ? color50.green("armed") : color50.yellow("disarmed")}`,
28519
+ `Fleet supervisor: ${supervisor.isRunning() ? color51.green("armed") : color51.yellow("disarmed")}`,
28034
28520
  ` interval ${Math.round(cfg.intervalMs / 1e3)}s \xB7 cooldown ${Math.round(cfg.cooldownMs / 1e3)}s \xB7 max ${cfg.maxInterventionsPerSubagent} interventions/agent`,
28035
28521
  ` signals: starvation>${Math.round(cfg.pinnedWaitMs / 1e3)}s \xB7 overload\u2265${cfg.overloadPinnedThreshold} pinned \xB7 backlog>${cfg.backlogFactor}\xD7workers \xB7 stuck>${Math.round(cfg.stuckMs / 1e3)}s \xB7 failstreak\u2265${cfg.failureStreak}`,
28036
28522
  ` actions: retarget \u2713 \xB7 spawn ${cfg.allowSpawn ? "\u2713" : "\u2717"} \xB7 steer \u2713 \xB7 terminate ${cfg.allowTerminate ? "\u2713" : "\u2717 (config fleet.supervisor.allowTerminate)"}`,
28037
28523
  ` activity: ${history.length} engagement(s)${last ? ` \u2014 last: ${last.kind} \u2192 ${last.proposedAction} [${last.outcome}] ${fmtAge4(last.at)}` : ""}`,
28038
- color50.dim(" decisions are gated by the Brain \u2014 see /brain (risk ceiling applies)")
28524
+ color51.dim(" decisions are gated by the Brain \u2014 see /brain (risk ceiling applies)")
28039
28525
  ];
28040
28526
  const msg = lines.join("\n");
28041
28527
  opts.renderer.write(msg);
@@ -28061,6 +28547,7 @@ import {
28061
28547
  formatTaskProgress,
28062
28548
  recordCompletedWorkEvidence
28063
28549
  } from "@wrongstack/core/utils";
28550
+ import { todoTool as todoTool2 } from "@wrongstack/tools";
28064
28551
  function findTask(tasks, query) {
28065
28552
  const asIndex = Number.parseInt(query, 10);
28066
28553
  if (!Number.isNaN(asIndex)) {
@@ -28179,6 +28666,7 @@ ${formatPlan2(updated)}`;
28179
28666
  }
28180
28667
  let outputMessage = "";
28181
28668
  let completedTask = null;
28669
+ let todosToReplace = null;
28182
28670
  await mutateTasks(taskPath, sessionId, async (file) => {
28183
28671
  switch (cmd) {
28184
28672
  case "add": {
@@ -28241,7 +28729,17 @@ ${formatTaskProgress(file.tasks)}`;
28241
28729
  done: "Completed",
28242
28730
  fail: "Failed"
28243
28731
  };
28244
- found.item.status = statusMap[cmd] ?? "pending";
28732
+ const nextStatus = statusMap[cmd] ?? "pending";
28733
+ if (nextStatus === "in_progress" || nextStatus === "completed") {
28734
+ const unmet = (found.item.dependsOn ?? []).filter(
28735
+ (dependencyId) => file.tasks.find((task) => task.id === dependencyId)?.status !== "completed"
28736
+ );
28737
+ if (unmet.length > 0) {
28738
+ outputMessage = `Cannot mark "${found.item.title}" ${nextStatus}; dependencies are unfinished: ${unmet.join(", ")}.`;
28739
+ return file;
28740
+ }
28741
+ }
28742
+ found.item.status = nextStatus;
28245
28743
  found.item.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
28246
28744
  if (cmd === "done") {
28247
28745
  completedTask = { id: found.item.id, title: found.item.title };
@@ -28267,6 +28765,15 @@ ${formatTaskProgress(file.tasks)}`;
28267
28765
  outputMessage = `No task matched "${targetId}".`;
28268
28766
  return file;
28269
28767
  }
28768
+ if (newStatus === "in_progress" || newStatus === "review" || newStatus === "completed") {
28769
+ const unmet = (found.item.dependsOn ?? []).filter(
28770
+ (dependencyId) => file.tasks.find((task) => task.id === dependencyId)?.status !== "completed"
28771
+ );
28772
+ if (unmet.length > 0) {
28773
+ outputMessage = `Cannot mark "${found.item.title}" ${newStatus}; dependencies are unfinished: ${unmet.join(", ")}.`;
28774
+ return file;
28775
+ }
28776
+ }
28270
28777
  found.item.status = newStatus;
28271
28778
  found.item.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
28272
28779
  if (newStatus === "completed") {
@@ -28290,6 +28797,21 @@ ${formatTaskProgress(file.tasks)}`;
28290
28797
  outputMessage = `No task matched "${targetId}".`;
28291
28798
  return file;
28292
28799
  }
28800
+ const knownIds = new Set(file.tasks.map((task) => task.id));
28801
+ const missing = depIds.filter((dependencyId) => !knownIds.has(dependencyId));
28802
+ if (missing.length > 0) {
28803
+ outputMessage = `Unknown dependency task IDs: ${missing.join(", ")}.`;
28804
+ return file;
28805
+ }
28806
+ if (found.item.status === "in_progress" || found.item.status === "review" || found.item.status === "completed") {
28807
+ const unmet = depIds.filter(
28808
+ (dependencyId) => file.tasks.find((task) => task.id === dependencyId)?.status !== "completed"
28809
+ );
28810
+ if (unmet.length > 0) {
28811
+ outputMessage = `Cannot add unfinished dependencies to active task "${found.item.title}": ${unmet.join(", ")}.`;
28812
+ return file;
28813
+ }
28814
+ }
28293
28815
  found.item.dependsOn = depIds;
28294
28816
  found.item.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
28295
28817
  outputMessage = `Dependencies set for "${found.item.title}": ${depIds.join(", ")}`;
@@ -28322,6 +28844,13 @@ ${formatTaskProgress(file.tasks)}`;
28322
28844
  outputMessage = `No task matched "${restJoined}".`;
28323
28845
  return file;
28324
28846
  }
28847
+ const unmet = (found.item.dependsOn ?? []).filter(
28848
+ (dependencyId) => file.tasks.find((task) => task.id === dependencyId)?.status !== "completed"
28849
+ );
28850
+ if (unmet.length > 0) {
28851
+ outputMessage = `Cannot promote "${found.item.title}"; dependencies are unfinished: ${unmet.join(", ")}.`;
28852
+ return file;
28853
+ }
28325
28854
  found.item.status = "in_progress";
28326
28855
  found.item.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
28327
28856
  const todos = [
@@ -28344,7 +28873,7 @@ ${formatTaskProgress(file.tasks)}`;
28344
28873
  const existing = ctx.state.todos.filter(
28345
28874
  (t) => t.promotedFromTask !== found.item.id
28346
28875
  );
28347
- ctx.state.replaceTodos([...existing, ...todos]);
28876
+ todosToReplace = [...existing, ...todos];
28348
28877
  outputMessage = `Promoted to ${todos.length} todo(s): "${found.item.title}"
28349
28878
 
28350
28879
  ${formatTaskProgress(file.tasks)}`;
@@ -28356,6 +28885,11 @@ ${formatTaskProgress(file.tasks)}`;
28356
28885
  outputMessage = "Tasks were already empty.";
28357
28886
  return file;
28358
28887
  }
28888
+ const unfinished = file.tasks.filter((task) => task.status !== "completed");
28889
+ if (unfinished.length > 0) {
28890
+ outputMessage = `Cannot clear unfinished tasks: ${unfinished.map((task) => task.id).join(", ")}. Complete them first.`;
28891
+ return file;
28892
+ }
28359
28893
  file.tasks = [];
28360
28894
  outputMessage = `Cleared ${n} task${n === 1 ? "" : "s"}.`;
28361
28895
  break;
@@ -28382,6 +28916,11 @@ ${formatTaskProgress(file.tasks)}`;
28382
28916
  }
28383
28917
  return file;
28384
28918
  });
28919
+ if (todosToReplace) {
28920
+ await todoTool2.execute({ todos: todosToReplace }, ctx, {
28921
+ signal: AbortSignal.timeout(3e4)
28922
+ });
28923
+ }
28385
28924
  if (completedTask) {
28386
28925
  const done = completedTask;
28387
28926
  try {
@@ -28411,7 +28950,7 @@ ${formatTaskProgress(file.tasks)}`;
28411
28950
  // src/slash-commands/techstack.ts
28412
28951
  import * as fs18 from "node:fs/promises";
28413
28952
  import * as path28 from "node:path";
28414
- import { color as color51, toErrorMessage as toErrorMessage26 } from "@wrongstack/core/utils";
28953
+ import { color as color52, toErrorMessage as toErrorMessage26 } from "@wrongstack/core/utils";
28415
28954
  async function discoverPackageFiles(projectRoot) {
28416
28955
  const files = [];
28417
28956
  const rootPkg = path28.join(projectRoot, "package.json");
@@ -28561,10 +29100,10 @@ function buildTechStackCommand(opts) {
28561
29100
  " 1. Reads every package.json in the project",
28562
29101
  " 2. Looks up latest versions on the npm registry",
28563
29102
  " 3. Flags outdated, dead, or obsolete packages",
28564
- ` 4. Writes a ${color51.cyan("techstack.md")} (or .json) report to the project root`,
29103
+ ` 4. Writes a ${color52.cyan("techstack.md")} (or .json) report to the project root`,
28565
29104
  "",
28566
29105
  "Uses the `tech-stack` skill for version verification rules.",
28567
- `Hooked into ${color51.cyan("/init")} \u2014 runs automatically on first project setup.`
29106
+ `Hooked into ${color52.cyan("/init")} \u2014 runs automatically on first project setup.`
28568
29107
  ].join("\n"),
28569
29108
  async run(args, _ctx) {
28570
29109
  const trimmed = args.trim().toLowerCase();
@@ -28648,12 +29187,12 @@ function buildTechStackCommand(opts) {
28648
29187
  try {
28649
29188
  packageFiles = await discoverPackageFiles(opts.projectRoot);
28650
29189
  if (packageFiles.length === 0) {
28651
- discoveryNote = color51.amber(
29190
+ discoveryNote = color52.amber(
28652
29191
  "\u26A0 No package.json files found. This does not look like a Node.js project."
28653
29192
  );
28654
29193
  }
28655
29194
  } catch (err) {
28656
- discoveryNote = color51.red(`Could not scan for package files: ${toErrorMessage26(err)}`);
29195
+ discoveryNote = color52.red(`Could not scan for package files: ${toErrorMessage26(err)}`);
28657
29196
  }
28658
29197
  const task = buildTechStackTask({
28659
29198
  projectRoot: opts.projectRoot,
@@ -28676,11 +29215,11 @@ function buildTechStackCommand(opts) {
28676
29215
  };
28677
29216
  }
28678
29217
  const header = isInit ? "Tech Stack Init Audit" : "Tech Stack Audit";
28679
- const label = `${color51.cyan("\u{1F50D}")} ${color51.bold(header)} ${color51.dim(`(${packageFiles.length} package files)`)}`;
29218
+ const label = `${color52.cyan("\u{1F50D}")} ${color52.bold(header)} ${color52.dim(`(${packageFiles.length} package files)`)}`;
28680
29219
  opts.renderer.write(label);
28681
29220
  if (discoveryNote) opts.renderer.write(discoveryNote);
28682
29221
  opts.renderer.write(
28683
- color51.dim(
29222
+ color52.dim(
28684
29223
  `Spawning tech-stack subagent \u2192 writes ${outputFormat === "json" ? "techstack.json" : "techstack.md"} when done.`
28685
29224
  )
28686
29225
  );
@@ -28702,10 +29241,10 @@ function buildTechStackCommand(opts) {
28702
29241
  }
28703
29242
 
28704
29243
  // src/slash-commands/telegram-settings.ts
28705
- import { color as color53, toErrorMessage as toErrorMessage27 } from "@wrongstack/core/utils";
29244
+ import { color as color54, toErrorMessage as toErrorMessage27 } from "@wrongstack/core/utils";
28706
29245
 
28707
29246
  // src/slash-commands/telegram-setup.ts
28708
- import { color as color52 } from "@wrongstack/core/utils";
29247
+ import { color as color53 } from "@wrongstack/core/utils";
28709
29248
 
28710
29249
  // src/slash-commands/telegram-pairing.ts
28711
29250
  var DISCOVERY_LIMIT = 25;
@@ -28821,31 +29360,31 @@ function buildTelegramSetupCommand(opts) {
28821
29360
  if (BOT_TOKEN_RE.test(first)) {
28822
29361
  return {
28823
29362
  message: [
28824
- `${color52.red("\u2717")} Bot tokens are no longer accepted as slash-command arguments.`,
28825
- `Run ${color52.cyan("/telegram-setup [chatId]")} and enter it at the masked prompt.`
29363
+ `${color53.red("\u2717")} Bot tokens are no longer accepted as slash-command arguments.`,
29364
+ `Run ${color53.cyan("/telegram-setup [chatId]")} and enter it at the masked prompt.`
28826
29365
  ].join("\n")
28827
29366
  };
28828
29367
  }
28829
29368
  if (parts.length > 1) {
28830
- return { message: `${color52.amber("Usage:")} /telegram-setup [chatId]` };
29369
+ return { message: `${color53.amber("Usage:")} /telegram-setup [chatId]` };
28831
29370
  }
28832
29371
  if (!opts.readSecret || !opts.vault || !opts.paths?.globalConfig) {
28833
29372
  return {
28834
- message: `${color52.red("\u2717")} Secure Telegram setup is unavailable in this session.`
29373
+ message: `${color53.red("\u2717")} Secure Telegram setup is unavailable in this session.`
28835
29374
  };
28836
29375
  }
28837
29376
  let botToken;
28838
29377
  try {
28839
- botToken = (await opts.readSecret(`Telegram bot token ${color52.dim("(hidden, paste OK)")}: `)).trim();
29378
+ botToken = (await opts.readSecret(`Telegram bot token ${color53.dim("(hidden, paste OK)")}: `)).trim();
28840
29379
  } catch {
28841
- return { message: color52.dim("Telegram setup cancelled.") };
29380
+ return { message: color53.dim("Telegram setup cancelled.") };
28842
29381
  }
28843
- if (!botToken) return { message: color52.dim("Telegram setup cancelled.") };
29382
+ if (!botToken) return { message: color53.dim("Telegram setup cancelled.") };
28844
29383
  if (!BOT_TOKEN_RE.test(botToken)) {
28845
29384
  return {
28846
29385
  message: [
28847
- `${color52.red("\u2717")} Invalid token format.`,
28848
- `Expected: ${color52.dim("123456789:ABCdefGHIjkl...")}`,
29386
+ `${color53.red("\u2717")} Invalid token format.`,
29387
+ `Expected: ${color53.dim("123456789:ABCdefGHIjkl...")}`,
28849
29388
  "",
28850
29389
  "Get a valid token from @BotFather on Telegram."
28851
29390
  ].join("\n")
@@ -28860,7 +29399,7 @@ function buildTelegramSetupCommand(opts) {
28860
29399
  } catch {
28861
29400
  return {
28862
29401
  message: [
28863
- `${color52.red("\u2717")} Could not reach Telegram API.`,
29402
+ `${color53.red("\u2717")} Could not reach Telegram API.`,
28864
29403
  "",
28865
29404
  "Check your network connection and try again."
28866
29405
  ].join("\n")
@@ -28869,7 +29408,7 @@ function buildTelegramSetupCommand(opts) {
28869
29408
  if (!botInfo.ok || !botInfo.result) {
28870
29409
  return {
28871
29410
  message: [
28872
- `${color52.red("\u2717")} Invalid bot token.`,
29411
+ `${color53.red("\u2717")} Invalid bot token.`,
28873
29412
  "",
28874
29413
  "Get a valid token from @BotFather on Telegram."
28875
29414
  ].join("\n")
@@ -28879,13 +29418,13 @@ function buildTelegramSetupCommand(opts) {
28879
29418
  const classifiedChatId = chatId ? classifyTelegramChatId(chatId) : void 0;
28880
29419
  if (classifiedChatId?.kind === "invalid") {
28881
29420
  return {
28882
- message: `${color52.red("\u2717")} Invalid Telegram chat ID. Expected a positive private chat ID.`
29421
+ message: `${color53.red("\u2717")} Invalid Telegram chat ID. Expected a positive private chat ID.`
28883
29422
  };
28884
29423
  }
28885
29424
  if (classifiedChatId?.kind === "group") {
28886
29425
  return {
28887
29426
  message: [
28888
- `${color52.amber("\u26A0")} Shared group, supergroup, and channel IDs cannot be paired by manual ID.`,
29427
+ `${color53.amber("\u26A0")} Shared group, supergroup, and channel IDs cannot be paired by manual ID.`,
28889
29428
  "Run /telegram-setup without a chat ID and select a discovered private identity.",
28890
29429
  "No configuration was changed."
28891
29430
  ].join("\n")
@@ -28899,7 +29438,7 @@ function buildTelegramSetupCommand(opts) {
28899
29438
  } catch {
28900
29439
  return {
28901
29440
  message: [
28902
- `${color52.red("\u2717")} Could not discover recent Telegram chats.`,
29441
+ `${color53.red("\u2717")} Could not discover recent Telegram chats.`,
28903
29442
  "Message the bot once, then run /telegram-setup again.",
28904
29443
  "No configuration was changed."
28905
29444
  ].join("\n")
@@ -28908,7 +29447,7 @@ function buildTelegramSetupCommand(opts) {
28908
29447
  if (candidates.length === 0) {
28909
29448
  return {
28910
29449
  message: [
28911
- `${color52.amber("No recent chats found.")}`,
29450
+ `${color53.amber("No recent chats found.")}`,
28912
29451
  "Message the bot from the private account you want to pair, then run setup again.",
28913
29452
  "No configuration was changed."
28914
29453
  ].join("\n")
@@ -28916,31 +29455,31 @@ function buildTelegramSetupCommand(opts) {
28916
29455
  }
28917
29456
  opts.renderer.write(
28918
29457
  [
28919
- color52.bold("Recent Telegram identities"),
29458
+ color53.bold("Recent Telegram identities"),
28920
29459
  formatTelegramPairingCandidates(candidates),
28921
29460
  "",
28922
- color52.dim("Choose a private candidate number, or press Enter to cancel.")
29461
+ color53.dim("Choose a private candidate number, or press Enter to cancel.")
28923
29462
  ].join("\n")
28924
29463
  );
28925
29464
  let choiceInput;
28926
29465
  try {
28927
29466
  choiceInput = opts.readText ? await opts.readText("Pair candidate \u203A ") : await opts.reader.readLine("Pair candidate \u203A ");
28928
29467
  } catch {
28929
- return { message: color52.dim("Telegram setup cancelled. No configuration was changed.") };
29468
+ return { message: color53.dim("Telegram setup cancelled. No configuration was changed.") };
28930
29469
  }
28931
29470
  const choice = parseTelegramPairingChoice(choiceInput, candidates);
28932
29471
  if (choice.kind === "cancel") {
28933
- return { message: color52.dim("Telegram setup cancelled. No configuration was changed.") };
29472
+ return { message: color53.dim("Telegram setup cancelled. No configuration was changed.") };
28934
29473
  }
28935
29474
  if (choice.kind === "invalid") {
28936
29475
  return {
28937
- message: `${color52.red("\u2717")} Invalid pairing choice. No configuration was changed.`
29476
+ message: `${color53.red("\u2717")} Invalid pairing choice. No configuration was changed.`
28938
29477
  };
28939
29478
  }
28940
29479
  if (!choice.candidate.eligible) {
28941
29480
  return {
28942
29481
  message: [
28943
- `${color52.amber("\u26A0")} Shared, group, or ambiguous identities are not paired automatically.`,
29482
+ `${color53.amber("\u26A0")} Shared, group, or ambiguous identities are not paired automatically.`,
28944
29483
  "Use a private chat where chat_id and user_id identify the same account.",
28945
29484
  "No configuration was changed."
28946
29485
  ].join("\n")
@@ -28982,7 +29521,7 @@ function buildTelegramSetupCommand(opts) {
28982
29521
  } catch {
28983
29522
  return {
28984
29523
  message: [
28985
- `${color52.red("\u2717")} Failed to save Telegram configuration.`,
29524
+ `${color53.red("\u2717")} Failed to save Telegram configuration.`,
28986
29525
  "The token was not printed. Check the config path and vault, then try again."
28987
29526
  ].join("\n")
28988
29527
  };
@@ -28990,15 +29529,15 @@ function buildTelegramSetupCommand(opts) {
28990
29529
  const bot = botInfo.result;
28991
29530
  return {
28992
29531
  message: [
28993
- `${color52.green("\u2713")} Telegram configured successfully.`,
29532
+ `${color53.green("\u2713")} Telegram configured successfully.`,
28994
29533
  "",
28995
- `Bot: ${color52.bold(`@${bot.username ?? bot.first_name}`)}`,
29534
+ `Bot: ${color53.bold(`@${bot.username ?? bot.first_name}`)}`,
28996
29535
  ...pairedCandidate ? [
28997
- `Paired private chat: ${color52.green(String(pairedCandidate.chatId))}`,
28998
- `Paired user: ${color52.green(String(pairedCandidate.userId))}`
28999
- ] : chatId ? [`Default chat: ${color52.green(chatId)}`] : [],
29536
+ `Paired private chat: ${color53.green(String(pairedCandidate.chatId))}`,
29537
+ `Paired user: ${color53.green(String(pairedCandidate.userId))}`
29538
+ ] : chatId ? [`Default chat: ${color53.green(chatId)}`] : [],
29000
29539
  "",
29001
- `${color52.amber("\u26A0")} Restart WrongStack for the plugin to load the new token.`
29540
+ `${color53.amber("\u26A0")} Restart WrongStack for the plugin to load the new token.`
29002
29541
  ].join("\n")
29003
29542
  };
29004
29543
  }
@@ -29032,15 +29571,15 @@ function buildTelegramSettingsCommand(opts) {
29032
29571
  const chat = tg.notifyChatId !== void 0 && tg.notifyChatId !== null ? String(tg.notifyChatId) : "not set";
29033
29572
  const hasToken = typeof tg.botToken === "string" && tg.botToken.length > 0;
29034
29573
  return [
29035
- `${color53.bold("Telegram")} ${color53.dim("\u2014 Notification Settings")}`,
29574
+ `${color54.bold("Telegram")} ${color54.dim("\u2014 Notification Settings")}`,
29036
29575
  "",
29037
- ` session end: ${sessionEnd ? color53.cyan("on") : color53.dim("off")} ${color53.dim("change: /telegram-settings session-end on|off")}`,
29038
- ` delegate done: ${delegate ? color53.cyan("on") : color53.dim("off")} ${color53.dim("change: /telegram-settings delegate on|off")}`,
29039
- ` long tool: ${color53.cyan(longTool)} ${color53.dim("change: /telegram-settings long-tool <ms|off>")}`,
29040
- ` poll interval: ${color53.cyan(poll)} ${color53.dim("change: /telegram-settings poll <seconds>")}`,
29041
- ` notify chat: ${color53.cyan(chat)} ${color53.dim("change: /telegram-settings chat <chatId>")}`,
29576
+ ` session end: ${sessionEnd ? color54.cyan("on") : color54.dim("off")} ${color54.dim("change: /telegram-settings session-end on|off")}`,
29577
+ ` delegate done: ${delegate ? color54.cyan("on") : color54.dim("off")} ${color54.dim("change: /telegram-settings delegate on|off")}`,
29578
+ ` long tool: ${color54.cyan(longTool)} ${color54.dim("change: /telegram-settings long-tool <ms|off>")}`,
29579
+ ` poll interval: ${color54.cyan(poll)} ${color54.dim("change: /telegram-settings poll <seconds>")}`,
29580
+ ` notify chat: ${color54.cyan(chat)} ${color54.dim("change: /telegram-settings chat <chatId>")}`,
29042
29581
  "",
29043
- hasToken ? color53.dim(" Bot token configured. Changes apply immediately.") : `${color53.amber("\u26A0")} No bot token configured. Run: /telegram-setup <botToken> [chatId]`
29582
+ hasToken ? color54.dim(" Bot token configured. Changes apply immediately.") : `${color54.amber("\u26A0")} No bot token configured. Run: /telegram-setup <botToken> [chatId]`
29044
29583
  ].join("\n");
29045
29584
  }
29046
29585
  return {
@@ -29056,7 +29595,7 @@ function buildTelegramSettingsCommand(opts) {
29056
29595
  return { message: HELP2 };
29057
29596
  }
29058
29597
  if (!opts.configStore || !opts.paths?.globalConfig || !opts.vault) {
29059
- return { message: `${color53.red("Error")} secure config persistence not available.` };
29598
+ return { message: `${color54.red("Error")} secure config persistence not available.` };
29060
29599
  }
29061
29600
  if (!sub) {
29062
29601
  return { message: currentView() };
@@ -29070,7 +29609,7 @@ function buildTelegramSettingsCommand(opts) {
29070
29609
  if (sub === "all") {
29071
29610
  const raw = (rest[0] ?? "").toLowerCase();
29072
29611
  if (!["on", "off"].includes(raw)) {
29073
- return { message: `${color53.amber("Usage:")} /telegram-settings all on|off` };
29612
+ return { message: `${color54.amber("Usage:")} /telegram-settings all on|off` };
29074
29613
  }
29075
29614
  const on = raw === "on";
29076
29615
  await persistTelegramConfig(persistDeps, (tg) => {
@@ -29078,40 +29617,40 @@ function buildTelegramSettingsCommand(opts) {
29078
29617
  tg.notifyOnDelegate = on;
29079
29618
  });
29080
29619
  return {
29081
- message: `${color53.green("\u2713")} all event notifications \u2192 ${on ? color53.cyan("on") : color53.dim("off")} ${color53.dim("(session-end, delegate)")}`
29620
+ message: `${color54.green("\u2713")} all event notifications \u2192 ${on ? color54.cyan("on") : color54.dim("off")} ${color54.dim("(session-end, delegate)")}`
29082
29621
  };
29083
29622
  }
29084
29623
  if (sub === "session-end") {
29085
29624
  const raw = (rest[0] ?? "").toLowerCase();
29086
29625
  if (!["on", "off"].includes(raw)) {
29087
- return { message: `${color53.amber("Usage:")} /telegram-settings session-end on|off` };
29626
+ return { message: `${color54.amber("Usage:")} /telegram-settings session-end on|off` };
29088
29627
  }
29089
29628
  const on = raw === "on";
29090
29629
  await persistTelegramConfig(persistDeps, (tg) => {
29091
29630
  tg.notifyOnSessionEnd = on;
29092
29631
  });
29093
29632
  return {
29094
- message: `${color53.green("\u2713")} session-end \u2192 ${on ? color53.cyan("on") : color53.dim("off")}`
29633
+ message: `${color54.green("\u2713")} session-end \u2192 ${on ? color54.cyan("on") : color54.dim("off")}`
29095
29634
  };
29096
29635
  }
29097
29636
  if (sub === "delegate") {
29098
29637
  const raw = (rest[0] ?? "").toLowerCase();
29099
29638
  if (!["on", "off"].includes(raw)) {
29100
- return { message: `${color53.amber("Usage:")} /telegram-settings delegate on|off` };
29639
+ return { message: `${color54.amber("Usage:")} /telegram-settings delegate on|off` };
29101
29640
  }
29102
29641
  const on = raw === "on";
29103
29642
  await persistTelegramConfig(persistDeps, (tg) => {
29104
29643
  tg.notifyOnDelegate = on;
29105
29644
  });
29106
29645
  return {
29107
- message: `${color53.green("\u2713")} delegate \u2192 ${on ? color53.cyan("on") : color53.dim("off")}`
29646
+ message: `${color54.green("\u2713")} delegate \u2192 ${on ? color54.cyan("on") : color54.dim("off")}`
29108
29647
  };
29109
29648
  }
29110
29649
  if (sub === "long-tool") {
29111
29650
  const raw = rest[0];
29112
29651
  if (raw === void 0) {
29113
29652
  return {
29114
- message: `${color53.amber("Usage:")} /telegram-settings long-tool <ms|off> ${color53.dim("(0 or off disables)")}`
29653
+ message: `${color54.amber("Usage:")} /telegram-settings long-tool <ms|off> ${color54.dim("(0 or off disables)")}`
29115
29654
  };
29116
29655
  }
29117
29656
  if (raw === "off") {
@@ -29119,57 +29658,57 @@ function buildTelegramSettingsCommand(opts) {
29119
29658
  tg.longToolThresholdMs = 0;
29120
29659
  });
29121
29660
  return {
29122
- message: `${color53.green("\u2713")} long-tool \u2192 ${color53.dim("off")}`
29661
+ message: `${color54.green("\u2713")} long-tool \u2192 ${color54.dim("off")}`
29123
29662
  };
29124
29663
  }
29125
29664
  const ms = Number.parseInt(raw, 10);
29126
29665
  if (Number.isNaN(ms) || ms < 0) {
29127
29666
  return {
29128
- message: `${color53.red("Invalid number")}: "${raw}". Enter milliseconds, e.g. /telegram-settings long-tool 15000`
29667
+ message: `${color54.red("Invalid number")}: "${raw}". Enter milliseconds, e.g. /telegram-settings long-tool 15000`
29129
29668
  };
29130
29669
  }
29131
29670
  await persistTelegramConfig(persistDeps, (tg) => {
29132
29671
  tg.longToolThresholdMs = ms;
29133
29672
  });
29134
29673
  return {
29135
- message: `${color53.green("\u2713")} long-tool \u2192 ${color53.cyan(`${ms}ms`)}`
29674
+ message: `${color54.green("\u2713")} long-tool \u2192 ${color54.cyan(`${ms}ms`)}`
29136
29675
  };
29137
29676
  }
29138
29677
  if (sub === "poll") {
29139
29678
  const raw = rest[0];
29140
29679
  if (raw === void 0) {
29141
29680
  return {
29142
- message: `${color53.amber("Usage:")} /telegram-settings poll <seconds> ${color53.dim("(1\u201360)")}`
29681
+ message: `${color54.amber("Usage:")} /telegram-settings poll <seconds> ${color54.dim("(1\u201360)")}`
29143
29682
  };
29144
29683
  }
29145
29684
  const sec = Number.parseInt(raw, 10);
29146
29685
  if (Number.isNaN(sec) || sec < 1 || sec > 60) {
29147
29686
  return {
29148
- message: `${color53.red("Invalid value")}: "${raw}". Enter seconds between 1 and 60.`
29687
+ message: `${color54.red("Invalid value")}: "${raw}". Enter seconds between 1 and 60.`
29149
29688
  };
29150
29689
  }
29151
29690
  await persistTelegramConfig(persistDeps, (tg) => {
29152
29691
  tg.pollIntervalSec = sec;
29153
29692
  });
29154
29693
  return {
29155
- message: `${color53.green("\u2713")} poll \u2192 ${color53.cyan(`${sec}s`)}`
29694
+ message: `${color54.green("\u2713")} poll \u2192 ${color54.cyan(`${sec}s`)}`
29156
29695
  };
29157
29696
  }
29158
29697
  if (sub === "chat") {
29159
29698
  const raw = rest[0];
29160
29699
  if (!raw) {
29161
- return { message: `${color53.amber("Usage:")} /telegram-settings chat <chatId>` };
29700
+ return { message: `${color54.amber("Usage:")} /telegram-settings chat <chatId>` };
29162
29701
  }
29163
29702
  const classification = classifyTelegramChatId(raw);
29164
29703
  if (classification.kind === "invalid") {
29165
- return { message: `${color53.red("Invalid chat ID")}: expected a non-zero integer.` };
29704
+ return { message: `${color54.red("Invalid chat ID")}: expected a non-zero integer.` };
29166
29705
  }
29167
29706
  const current = opts.configStore.get();
29168
29707
  const allowGroupChats = current.extensions?.telegram?.allowGroupChats === true;
29169
29708
  if (classification.kind === "group" && !allowGroupChats) {
29170
29709
  return {
29171
29710
  message: [
29172
- `${color53.amber("\u26A0")} Group, supergroup, and channel targets require explicit allowGroupChats=true.`,
29711
+ `${color54.amber("\u26A0")} Group, supergroup, and channel targets require explicit allowGroupChats=true.`,
29173
29712
  "No configuration was changed."
29174
29713
  ].join("\n")
29175
29714
  };
@@ -29192,15 +29731,15 @@ function buildTelegramSettingsCommand(opts) {
29192
29731
  }
29193
29732
  });
29194
29733
  return {
29195
- message: `${color53.green("\u2713")} notify chat \u2192 ${color53.cyan(raw)}`
29734
+ message: `${color54.green("\u2713")} notify chat \u2192 ${color54.cyan(raw)}`
29196
29735
  };
29197
29736
  }
29198
29737
  return {
29199
- message: `${color53.red("Unknown setting")} "${sub}". ${unknownSubcommand(sub, ["session-end", "delegate", "long-tool", "poll", "chat", "all"], "telegram-settings")}`
29738
+ message: `${color54.red("Unknown setting")} "${sub}". ${unknownSubcommand(sub, ["session-end", "delegate", "long-tool", "poll", "chat", "all"], "telegram-settings")}`
29200
29739
  };
29201
29740
  } catch (err) {
29202
29741
  return {
29203
- message: `${color53.red("Settings error")}: ${toErrorMessage27(err)}`
29742
+ message: `${color54.red("Settings error")}: ${toErrorMessage27(err)}`
29204
29743
  };
29205
29744
  }
29206
29745
  }
@@ -29210,6 +29749,7 @@ function buildTelegramSettingsCommand(opts) {
29210
29749
  // src/slash-commands/todos.ts
29211
29750
  import { randomUUID as randomUUID7 } from "node:crypto";
29212
29751
  import { formatTodosList as formatTodosList2 } from "@wrongstack/core/utils";
29752
+ import { todoTool as todoTool3 } from "@wrongstack/tools";
29213
29753
  function findTodo(todos, query) {
29214
29754
  const asIndex = Number.parseInt(query, 10);
29215
29755
  if (!Number.isNaN(asIndex)) {
@@ -29240,73 +29780,105 @@ function buildTodosCommand(opts) {
29240
29780
  if (!ctx) return { message: "No active context." };
29241
29781
  const { cmd, rest } = parseSubcommand(args);
29242
29782
  const restJoined = rest.join(" ").trim();
29243
- switch (cmd) {
29244
- case "":
29245
- case "show":
29246
- case "list": {
29247
- return { message: formatTodosList2(ctx.todos) };
29248
- }
29249
- case "clear": {
29250
- const n = ctx.todos.length;
29251
- if (n === 0) return { message: "Todos were already empty." };
29252
- ctx.state.replaceTodos([]);
29253
- return { message: `Cleared ${n} todo${n === 1 ? "" : "s"}.` };
29254
- }
29255
- case "add": {
29256
- if (!restJoined) return { message: "Usage: /todos add <text>" };
29257
- const item = {
29258
- id: `todo_${Date.now()}_${randomUUID7().slice(0, 7)}`,
29259
- content: restJoined,
29260
- status: "pending"
29261
- };
29262
- ctx.state.replaceTodos([...ctx.todos, item]);
29263
- return { message: `Added: ${restJoined}` };
29264
- }
29265
- case "done-all":
29266
- case "complete-all": {
29267
- const pending = ctx.todos.filter((t) => t.status !== "completed");
29268
- if (pending.length === 0) return { message: "No pending todos to complete." };
29269
- ctx.state.replaceTodos(
29270
- ctx.todos.map(
29271
- (t) => t.status === "completed" ? t : { ...t, status: "completed" }
29272
- )
29273
- );
29274
- return {
29275
- message: `Marked ${pending.length} todo${pending.length === 1 ? "" : "s"} done.`
29276
- };
29277
- }
29278
- case "done":
29279
- case "complete": {
29280
- if (!restJoined) return { message: "Usage: /todos done <id|index>" };
29281
- const found = findTodo(ctx.todos, restJoined);
29282
- if (!found) return { message: `No todo matched "${restJoined}".` };
29283
- const doneItem = { ...found.item, status: "completed" };
29284
- const nextTodos = [
29285
- ...ctx.todos.slice(0, found.idx),
29286
- doneItem,
29287
- ...ctx.todos.slice(found.idx + 1)
29288
- ];
29289
- ctx.state.replaceTodos(nextTodos);
29290
- return { message: `Marked done: ${doneItem.content}` };
29291
- }
29292
- case "remove":
29293
- case "rm":
29294
- case "delete": {
29295
- if (!restJoined) return { message: "Usage: /todos remove <id|index>" };
29296
- const found = findTodo(ctx.todos, restJoined);
29297
- if (!found) return { message: `No todo matched "${restJoined}".` };
29298
- const nextTodos = [...ctx.todos.slice(0, found.idx), ...ctx.todos.slice(found.idx + 1)];
29299
- ctx.state.replaceTodos(nextTodos);
29300
- return { message: `Removed: ${found.item.content}` };
29783
+ const updateTodos = (todos) => todoTool3.execute({ todos }, ctx, { signal: AbortSignal.timeout(3e4) });
29784
+ const isManagedProjection = (todo) => Boolean(todo.kanbanBoardId && todo.kanbanTaskId);
29785
+ try {
29786
+ switch (cmd) {
29787
+ case "":
29788
+ case "show":
29789
+ case "list": {
29790
+ return { message: formatTodosList2(ctx.todos) };
29791
+ }
29792
+ case "clear": {
29793
+ const n = ctx.todos.length;
29794
+ if (n === 0) return { message: "Todos were already empty." };
29795
+ if (ctx.todos.some(isManagedProjection)) {
29796
+ return {
29797
+ message: "Kanban-bound todos are task projections. Change or remove the tasks from Kanban."
29798
+ };
29799
+ }
29800
+ ctx.state.replaceTodos([]);
29801
+ return { message: `Cleared ${n} todo${n === 1 ? "" : "s"}.` };
29802
+ }
29803
+ case "add": {
29804
+ if (!restJoined) return { message: "Usage: /todos add <text>" };
29805
+ if (ctx.todos.some(isManagedProjection)) {
29806
+ return { message: "This Todo list is a Kanban projection. Add the task in Kanban." };
29807
+ }
29808
+ const item = {
29809
+ id: `todo_${Date.now()}_${randomUUID7().slice(0, 7)}`,
29810
+ content: restJoined,
29811
+ status: "pending"
29812
+ };
29813
+ await updateTodos([...ctx.todos, item]);
29814
+ return { message: `Added: ${restJoined}` };
29815
+ }
29816
+ case "done-all":
29817
+ case "complete-all": {
29818
+ const pending = ctx.todos.filter((t) => t.status !== "completed");
29819
+ if (pending.length === 0) return { message: "No pending todos to complete." };
29820
+ if (ctx.todos.some(isManagedProjection)) {
29821
+ return {
29822
+ message: "Kanban-bound todos must complete through their real task lifecycle; complete the active task individually."
29823
+ };
29824
+ }
29825
+ await updateTodos(
29826
+ ctx.todos.map(
29827
+ (t) => t.status === "completed" ? t : { ...t, status: "completed" }
29828
+ )
29829
+ );
29830
+ return {
29831
+ message: `Marked ${pending.length} todo${pending.length === 1 ? "" : "s"} done.`
29832
+ };
29833
+ }
29834
+ case "done":
29835
+ case "complete": {
29836
+ if (!restJoined) return { message: "Usage: /todos done <id|index>" };
29837
+ const found = findTodo(ctx.todos, restJoined);
29838
+ if (!found) return { message: `No todo matched "${restJoined}".` };
29839
+ const doneItem = { ...found.item, status: "completed" };
29840
+ const nextTodos = [
29841
+ ...ctx.todos.slice(0, found.idx),
29842
+ doneItem,
29843
+ ...ctx.todos.slice(found.idx + 1)
29844
+ ];
29845
+ const result = await updateTodos(nextTodos);
29846
+ const projected = ctx.todos.find((todo) => todo.id === doneItem.id);
29847
+ if (isManagedProjection(doneItem) && projected?.status !== "completed") {
29848
+ return {
29849
+ message: result.kanban_warnings?.[0] ?? `Kanban kept ${doneItem.content} at ${projected?.status ?? "its current state"}.`
29850
+ };
29851
+ }
29852
+ return { message: `Marked done: ${doneItem.content}` };
29853
+ }
29854
+ case "remove":
29855
+ case "rm":
29856
+ case "delete": {
29857
+ if (!restJoined) return { message: "Usage: /todos remove <id|index>" };
29858
+ const found = findTodo(ctx.todos, restJoined);
29859
+ if (!found) return { message: `No todo matched "${restJoined}".` };
29860
+ if (isManagedProjection(found.item)) {
29861
+ return {
29862
+ message: "Kanban-bound todos are task projections. Remove the task from Kanban instead."
29863
+ };
29864
+ }
29865
+ const nextTodos = [...ctx.todos.slice(0, found.idx), ...ctx.todos.slice(found.idx + 1)];
29866
+ ctx.state.replaceTodos(nextTodos);
29867
+ return { message: `Removed: ${found.item.content}` };
29868
+ }
29869
+ default:
29870
+ return {
29871
+ message: unknownSubcommand(
29872
+ cmd,
29873
+ ["show", "clear", "add", "done", "done-all", "remove"],
29874
+ "todos"
29875
+ ) + "\n\nRelated: /plan (session-persistent roadmap) | /tasks (structured tasks with priorities)"
29876
+ };
29301
29877
  }
29302
- default:
29303
- return {
29304
- message: unknownSubcommand(
29305
- cmd,
29306
- ["show", "clear", "add", "done", "done-all", "remove"],
29307
- "todos"
29308
- ) + "\n\nRelated: /plan (session-persistent roadmap) | /tasks (structured tasks with priorities)"
29309
- };
29878
+ } catch (error) {
29879
+ return {
29880
+ message: `Todo update failed: ${error instanceof Error ? error.message : String(error)}`
29881
+ };
29310
29882
  }
29311
29883
  }
29312
29884
  };
@@ -29315,7 +29887,7 @@ function buildTodosCommand(opts) {
29315
29887
  // src/slash-commands/tool.ts
29316
29888
  import { noOpVault as noOpVault10 } from "@wrongstack/core/security";
29317
29889
  import {
29318
- color as color54,
29890
+ color as color55,
29319
29891
  getToolDescriptionMode as getToolDescriptionMode2,
29320
29892
  getToolResultRenderMode,
29321
29893
  normalizeToolDescriptionMode,
@@ -29329,11 +29901,11 @@ function fit(text, width) {
29329
29901
  }
29330
29902
  function formatDescriptionMode(mode) {
29331
29903
  const raw = `desc:${mode}`;
29332
- return mode === "simple" ? color54.amber(raw) : color54.cyan(raw);
29904
+ return mode === "simple" ? color55.amber(raw) : color55.cyan(raw);
29333
29905
  }
29334
29906
  function formatResultRenderMode(mode) {
29335
29907
  const raw = `result:${mode}`;
29336
- return mode === "simple" ? color54.amber(raw) : color54.cyan(raw);
29908
+ return mode === "simple" ? color55.amber(raw) : color55.cyan(raw);
29337
29909
  }
29338
29910
  function buildToolCommand(opts) {
29339
29911
  const help = [
@@ -29439,38 +30011,38 @@ function buildToolCommand(opts) {
29439
30011
  const resultSimple = Object.entries(configured.resultRenderMode ?? {}).filter(([, mode]) => normalizeToolResultRenderMode(mode) === "simple").map(([name]) => name).sort();
29440
30012
  const disabled = opts.toolRegistry.listDisabled();
29441
30013
  const lines = [
29442
- `${color54.bold("Tool modes")} ${color54.dim("(default: extend on both axes)")}`,
30014
+ `${color55.bold("Tool modes")} ${color55.dim("(default: extend on both axes)")}`,
29443
30015
  "",
29444
- `${formatDescriptionMode("simple")}: ${descSimple.length > 0 ? descSimple.map((n) => color54.cyan(n)).join(", ") : color54.dim("none")}`,
29445
- `${formatResultRenderMode("simple")}: ${resultSimple.length > 0 ? resultSimple.map((n) => color54.cyan(n)).join(", ") : color54.dim("none")}`,
30016
+ `${formatDescriptionMode("simple")}: ${descSimple.length > 0 ? descSimple.map((n) => color55.cyan(n)).join(", ") : color55.dim("none")}`,
30017
+ `${formatResultRenderMode("simple")}: ${resultSimple.length > 0 ? resultSimple.map((n) => color55.cyan(n)).join(", ") : color55.dim("none")}`,
29446
30018
  ""
29447
30019
  ];
29448
30020
  if (disabled.length > 0) {
29449
30021
  lines.push(
29450
- `${color54.bold("Disabled tools")}`,
30022
+ `${color55.bold("Disabled tools")}`,
29451
30023
  "",
29452
- ` ${color54.red("disabled")}: ${disabled.map(({ tool }) => color54.dim(tool.name)).join(", ")}`,
30024
+ ` ${color55.red("disabled")}: ${disabled.map(({ tool }) => color55.dim(tool.name)).join(", ")}`,
29453
30025
  ""
29454
30026
  );
29455
30027
  }
29456
30028
  lines.push(
29457
- color54.dim(
30029
+ color55.dim(
29458
30030
  " /tool <name> desc simple \xB7 /tool <name> result simple \xB7 /tool list \xB7 /tool disable|enable <name>"
29459
30031
  )
29460
30032
  );
29461
30033
  return lines.join("\n");
29462
30034
  }
29463
30035
  function formatList() {
29464
- const header = ` ${color54.dim(fit("tool", 28))} ${color54.dim(fit("owner", 28))} ${color54.dim(fit("status", 10))} ${color54.dim(fit("desc", 14))} ` + color54.dim("result");
30036
+ const header = ` ${color55.dim(fit("tool", 28))} ${color55.dim(fit("owner", 28))} ${color55.dim(fit("status", 10))} ${color55.dim(fit("desc", 14))} ` + color55.dim("result");
29465
30037
  const rows = opts.toolRegistry.listWithOwner().map(({ tool }) => {
29466
30038
  const descMode = getToolDescriptionMode2(opts.toolRegistry, tool.name);
29467
30039
  const resultMode = getToolResultRenderMode(opts.toolRegistry, tool.name);
29468
30040
  const owner = opts.toolRegistry.ownerOf(tool.name) ?? "core";
29469
- const status = opts.toolRegistry.isDisabled(tool.name) ? color54.red("disabled") : color54.green("active");
29470
- return ` ${fit(tool.name, 28)} ${color54.dim(fit(`[${owner}]`, 28))} ${fit(status, 10)} ${fit(formatDescriptionMode(descMode), 14)} ` + formatResultRenderMode(resultMode);
30041
+ const status = opts.toolRegistry.isDisabled(tool.name) ? color55.red("disabled") : color55.green("active");
30042
+ return ` ${fit(tool.name, 28)} ${color55.dim(fit(`[${owner}]`, 28))} ${fit(status, 10)} ${fit(formatDescriptionMode(descMode), 14)} ` + formatResultRenderMode(resultMode);
29471
30043
  });
29472
30044
  return [
29473
- `${color54.bold("Tool modes")} ${color54.dim("(default: extend on both axes)")}`,
30045
+ `${color55.bold("Tool modes")} ${color55.dim("(default: extend on both axes)")}`,
29474
30046
  "",
29475
30047
  header,
29476
30048
  ...rows
@@ -29481,55 +30053,55 @@ function buildToolCommand(opts) {
29481
30053
  const tool = reg.get(name);
29482
30054
  if (!tool) {
29483
30055
  if (reg.isDisabled(name)) {
29484
- return `${color54.amber(name)} is disabled. Use ${color54.dim(`/tool enable ${name}`)} to restore.`;
30056
+ return `${color55.amber(name)} is disabled. Use ${color55.dim(`/tool enable ${name}`)} to restore.`;
29485
30057
  }
29486
- return `${color54.red("Unknown tool")}: ${name}. Use ${color54.dim("/tools")} to list registered tools.`;
30058
+ return `${color55.red("Unknown tool")}: ${name}. Use ${color55.dim("/tools")} to list registered tools.`;
29487
30059
  }
29488
30060
  const descMode = getToolDescriptionMode2(reg, name);
29489
30061
  const resultMode = getToolResultRenderMode(reg, name);
29490
- const status = reg.isDisabled(name) ? color54.red("disabled") : color54.green("active");
30062
+ const status = reg.isDisabled(name) ? color55.red("disabled") : color55.green("active");
29491
30063
  return [
29492
- `${color54.bold(name)} ${status}`,
30064
+ `${color55.bold(name)} ${status}`,
29493
30065
  `description mode: ${formatDescriptionMode(descMode)}`,
29494
30066
  `result mode: ${formatResultRenderMode(resultMode)}`,
29495
30067
  "",
29496
- color54.dim(tool.description)
30068
+ color55.dim(tool.description)
29497
30069
  ].join("\n");
29498
30070
  }
29499
30071
  async function cmdEnable(name) {
29500
30072
  const reg = opts.toolRegistry;
29501
30073
  if (!reg.isDisabled(name)) {
29502
- return `${color54.amber(name)} is not disabled.`;
30074
+ return `${color55.amber(name)} is not disabled.`;
29503
30075
  }
29504
30076
  const ok = reg.enable(name);
29505
- if (!ok) return `${color54.red("Could not enable")}: ${name}.`;
30077
+ if (!ok) return `${color55.red("Could not enable")}: ${name}.`;
29506
30078
  const disabled = currentDisabledSet();
29507
30079
  disabled.delete(name);
29508
30080
  await persistDisabled(Array.from(disabled));
29509
- return `${color54.green("\u2713")} ${color54.cyan(name)} re-enabled \u2014 will appear in next provider request.`;
30081
+ return `${color55.green("\u2713")} ${color55.cyan(name)} re-enabled \u2014 will appear in next provider request.`;
29510
30082
  }
29511
30083
  async function cmdEnableAll() {
29512
30084
  const reg = opts.toolRegistry;
29513
30085
  const count = reg.enableAll();
29514
- if (count === 0) return `${color54.amber("No disabled tools to re-enable.")}`;
30086
+ if (count === 0) return `${color55.amber("No disabled tools to re-enable.")}`;
29515
30087
  await persistDisabled([]);
29516
- return `${color54.green("\u2713")} All ${count} disabled tool(s) re-enabled.`;
30088
+ return `${color55.green("\u2713")} All ${count} disabled tool(s) re-enabled.`;
29517
30089
  }
29518
30090
  async function cmdDisable(name) {
29519
30091
  const reg = opts.toolRegistry;
29520
30092
  const tool = reg.get(name);
29521
30093
  if (!tool) {
29522
30094
  if (reg.isDisabled(name)) {
29523
- return `${color54.amber(name)} is already disabled.`;
30095
+ return `${color55.amber(name)} is already disabled.`;
29524
30096
  }
29525
- return `${color54.red("Unknown tool")}: ${name}. Use ${color54.dim("/tools")} to list registered tools.`;
30097
+ return `${color55.red("Unknown tool")}: ${name}. Use ${color55.dim("/tools")} to list registered tools.`;
29526
30098
  }
29527
30099
  const ok = reg.disable(name);
29528
- if (!ok) return `${color54.red("Could not disable")}: ${name}.`;
30100
+ if (!ok) return `${color55.red("Could not disable")}: ${name}.`;
29529
30101
  const disabled = currentDisabledSet();
29530
30102
  disabled.add(name);
29531
30103
  await persistDisabled(Array.from(disabled));
29532
- return `${color54.green("\u2713")} ${color54.cyan(name)} disabled \u2014 removed from system prompt and tool registry.`;
30104
+ return `${color55.green("\u2713")} ${color55.cyan(name)} disabled \u2014 removed from system prompt and tool registry.`;
29533
30105
  }
29534
30106
  function applyDescMode(name, mode) {
29535
30107
  opts.toolRegistry.setDescriptionMode?.(name, mode);
@@ -29545,7 +30117,7 @@ function buildToolCommand(opts) {
29545
30117
  help,
29546
30118
  async run(args) {
29547
30119
  if (!opts.configStore) {
29548
- return { message: `${color54.red("Error")} config store not available.` };
30120
+ return { message: `${color55.red("Error")} config store not available.` };
29549
30121
  }
29550
30122
  const parts = args.trim().split(/\s+/).filter(Boolean);
29551
30123
  const sub = (parts[0] ?? "").toLowerCase();
@@ -29556,7 +30128,7 @@ function buildToolCommand(opts) {
29556
30128
  try {
29557
30129
  return { message: await cmdEnableAll() };
29558
30130
  } catch (err) {
29559
- return { message: `${color54.red("Error")}: ${toErrorMessage28(err)}` };
30131
+ return { message: `${color55.red("Error")}: ${toErrorMessage28(err)}` };
29560
30132
  }
29561
30133
  }
29562
30134
  const name = parts[0] ?? "";
@@ -29564,43 +30136,43 @@ function buildToolCommand(opts) {
29564
30136
  if (sub === "disable") {
29565
30137
  const targets = parts.slice(1);
29566
30138
  if (targets.length === 0)
29567
- return { message: `${color54.amber("Usage:")} /tool disable <name> [name...]` };
30139
+ return { message: `${color55.amber("Usage:")} /tool disable <name> [name...]` };
29568
30140
  try {
29569
30141
  const results = [];
29570
30142
  for (const t of targets) results.push(await cmdDisable(t));
29571
30143
  return { message: results.join("\n") };
29572
30144
  } catch (err) {
29573
- return { message: `${color54.red("Error")}: ${toErrorMessage28(err)}` };
30145
+ return { message: `${color55.red("Error")}: ${toErrorMessage28(err)}` };
29574
30146
  }
29575
30147
  }
29576
30148
  if (sub === "enable") {
29577
30149
  const targets = parts.slice(1);
29578
30150
  if (targets.length === 0)
29579
- return { message: `${color54.amber("Usage:")} /tool enable <name> [name...]` };
30151
+ return { message: `${color55.amber("Usage:")} /tool enable <name> [name...]` };
29580
30152
  try {
29581
30153
  const results = [];
29582
30154
  for (const t of targets) results.push(await cmdEnable(t));
29583
30155
  return { message: results.join("\n") };
29584
30156
  } catch (err) {
29585
- return { message: `${color54.red("Error")}: ${toErrorMessage28(err)}` };
30157
+ return { message: `${color55.red("Error")}: ${toErrorMessage28(err)}` };
29586
30158
  }
29587
30159
  }
29588
30160
  const action = parts[1]?.toLowerCase();
29589
30161
  if (action === "disable" || action === "enable") {
29590
30162
  if (parts.length > 2) {
29591
30163
  return {
29592
- message: `${color54.amber("Usage:")} /tool ${name} ${action}`
30164
+ message: `${color55.amber("Usage:")} /tool ${name} ${action}`
29593
30165
  };
29594
30166
  }
29595
30167
  try {
29596
30168
  return { message: action === "disable" ? await cmdDisable(name) : await cmdEnable(name) };
29597
30169
  } catch (err) {
29598
- return { message: `${color54.red("Error")}: ${toErrorMessage28(err)}` };
30170
+ return { message: `${color55.red("Error")}: ${toErrorMessage28(err)}` };
29599
30171
  }
29600
30172
  }
29601
30173
  if (!opts.toolRegistry.get(name) && !opts.toolRegistry.isDisabled(name)) {
29602
30174
  return {
29603
- message: `${color54.red("Unknown tool")}: ${name}. Use ${color54.dim("/tools")} to list registered tools.`
30175
+ message: `${color55.red("Unknown tool")}: ${name}. Use ${color55.dim("/tools")} to list registered tools.`
29604
30176
  };
29605
30177
  }
29606
30178
  if (parts.length === 1) return { message: formatOne(name) };
@@ -29609,53 +30181,53 @@ function buildToolCommand(opts) {
29609
30181
  const rawMode = parts[2];
29610
30182
  if (!rawMode) {
29611
30183
  return {
29612
- message: `${color54.amber("Usage:")} /tool ${name} ${axis} simple|extend`
30184
+ message: `${color55.amber("Usage:")} /tool ${name} ${axis} simple|extend`
29613
30185
  };
29614
30186
  }
29615
30187
  const mode2 = normalizeToolDescriptionMode(rawMode);
29616
30188
  if (!mode2) {
29617
30189
  return {
29618
- message: `${color54.amber("Usage:")} /tool ${name} ${axis} simple|extend`
30190
+ message: `${color55.amber("Usage:")} /tool ${name} ${axis} simple|extend`
29619
30191
  };
29620
30192
  }
29621
30193
  try {
29622
30194
  if (axis === "desc") {
29623
30195
  const persisted2 = await persistModeForAxis(name, "desc", mode2);
29624
30196
  applyDescMode(name, mode2);
29625
- const persistence2 = persisted2 ? color54.dim("saved") : color54.dim("runtime only; config paths unavailable");
30197
+ const persistence2 = persisted2 ? color55.dim("saved") : color55.dim("runtime only; config paths unavailable");
29626
30198
  return {
29627
- message: `${color54.green("\u2713")} ${color54.cyan(name)} ${formatDescriptionMode(mode2)} ${persistence2}`
30199
+ message: `${color55.green("\u2713")} ${color55.cyan(name)} ${formatDescriptionMode(mode2)} ${persistence2}`
29628
30200
  };
29629
30201
  }
29630
30202
  const persisted = await persistModeForAxis(name, "result", mode2);
29631
30203
  applyResultMode(name, mode2);
29632
- const persistence = persisted ? color54.dim("saved") : color54.dim("runtime only; config paths unavailable");
30204
+ const persistence = persisted ? color55.dim("saved") : color55.dim("runtime only; config paths unavailable");
29633
30205
  return {
29634
- message: `${color54.green("\u2713")} ${color54.cyan(name)} ${formatResultRenderMode(mode2)} ${persistence}`
30206
+ message: `${color55.green("\u2713")} ${color55.cyan(name)} ${formatResultRenderMode(mode2)} ${persistence}`
29635
30207
  };
29636
30208
  } catch (err) {
29637
30209
  return {
29638
- message: `${color54.red("Could not save tool setting")}: ${toErrorMessage28(err)}`
30210
+ message: `${color55.red("Could not save tool setting")}: ${toErrorMessage28(err)}`
29639
30211
  };
29640
30212
  }
29641
30213
  }
29642
30214
  const mode = normalizeToolDescriptionMode(axis);
29643
30215
  if (!mode) {
29644
30216
  return {
29645
- message: `${color54.amber("Usage:")} /tool ${name} [desc|result] simple|extend`
30217
+ message: `${color55.amber("Usage:")} /tool ${name} [desc|result] simple|extend`
29646
30218
  };
29647
30219
  }
29648
30220
  try {
29649
30221
  const persisted = await persistModeBoth(name, mode);
29650
30222
  applyDescMode(name, mode);
29651
30223
  applyResultMode(name, mode);
29652
- const persistence = persisted ? color54.dim("saved (both axes)") : color54.dim("runtime only; config paths unavailable");
30224
+ const persistence = persisted ? color55.dim("saved (both axes)") : color55.dim("runtime only; config paths unavailable");
29653
30225
  return {
29654
- message: `${color54.green("\u2713")} ${color54.cyan(name)} ${formatDescriptionMode(mode)} + ${formatResultRenderMode(mode)} ${persistence}`
30226
+ message: `${color55.green("\u2713")} ${color55.cyan(name)} ${formatDescriptionMode(mode)} + ${formatResultRenderMode(mode)} ${persistence}`
29655
30227
  };
29656
30228
  } catch (err) {
29657
30229
  return {
29658
- message: `${color54.red("Could not save tool setting")}: ${toErrorMessage28(err)}`
30230
+ message: `${color55.red("Could not save tool setting")}: ${toErrorMessage28(err)}`
29659
30231
  };
29660
30232
  }
29661
30233
  }
@@ -29663,14 +30235,14 @@ function buildToolCommand(opts) {
29663
30235
  }
29664
30236
 
29665
30237
  // src/slash-commands/tools.ts
29666
- import { color as color55, getToolDescriptionMode as getToolDescriptionMode3 } from "@wrongstack/core/utils";
30238
+ import { color as color56, getToolDescriptionMode as getToolDescriptionMode3 } from "@wrongstack/core/utils";
29667
30239
  function fit2(text, width) {
29668
30240
  if (text.length <= width) return text.padEnd(width);
29669
30241
  return `${text.slice(0, Math.max(0, width - 3))}...`;
29670
30242
  }
29671
30243
  function formatDescriptionMode2(mode) {
29672
30244
  const raw = `desc:${mode}`;
29673
- return mode === "simple" ? color55.amber(raw) : color55.dim(raw);
30245
+ return mode === "simple" ? color56.amber(raw) : color56.dim(raw);
29674
30246
  }
29675
30247
  function buildToolsCommand(opts) {
29676
30248
  return {
@@ -29691,21 +30263,21 @@ function buildToolsCommand(opts) {
29691
30263
  if (opened) return { message: "" };
29692
30264
  }
29693
30265
  if (filter && all.length === 0) {
29694
- const msg2 = `${color55.bold("Tools")} \u2014 no tool name or owner matched "${filter}".`;
30266
+ const msg2 = `${color56.bold("Tools")} \u2014 no tool name or owner matched "${filter}".`;
29695
30267
  opts.renderer.write(msg2);
29696
30268
  return { message: msg2 };
29697
30269
  }
29698
- const header = ` ${color55.dim(fit2("tool", 28))} ${color55.dim(fit2("owner", 28))} ${color55.dim(fit2("rw", 4))} ${color55.dim(fit2("perm", 8))} ${color55.dim(fit2("status", 10))} ` + color55.dim("description");
30270
+ const header = ` ${color56.dim(fit2("tool", 28))} ${color56.dim(fit2("owner", 28))} ${color56.dim(fit2("rw", 4))} ${color56.dim(fit2("perm", 8))} ${color56.dim(fit2("status", 10))} ` + color56.dim("description");
29699
30271
  const lines = all.map(({ tool, owner }) => {
29700
30272
  const mode = getToolDescriptionMode3(reg, tool.name);
29701
- const rw = tool.mutating ? color55.yellow(fit2("mut", 4)) : color55.cyan(fit2("ro", 4));
29702
- const status = reg.isDisabled(tool.name) ? color55.red("disabled") : color55.green("active");
29703
- return ` ${fit2(tool.name, 28)} ${color55.dim(fit2(`[${owner}]`, 28))} ${rw} ${color55.dim(fit2(tool.permission, 8))} ${fit2(status, 10)} ` + formatDescriptionMode2(mode);
30273
+ const rw = tool.mutating ? color56.yellow(fit2("mut", 4)) : color56.cyan(fit2("ro", 4));
30274
+ const status = reg.isDisabled(tool.name) ? color56.red("disabled") : color56.green("active");
30275
+ return ` ${fit2(tool.name, 28)} ${color56.dim(fit2(`[${owner}]`, 28))} ${rw} ${color56.dim(fit2(tool.permission, 8))} ${fit2(status, 10)} ` + formatDescriptionMode2(mode);
29704
30276
  });
29705
30277
  const extra = disabled.length > 0 ? `
29706
- ${color55.dim(`${disabled.length} tool(s) disabled. Use /tool enable <name> or /tool enable-all to restore.`)}` : "";
29707
- const filterNote = filter ? color55.dim(` matching "${filter}" (${all.length} of ${allTools.length})`) : "";
29708
- const msg = `${color55.bold("Tools")}${filterNote} (${all.length} shown, ${disabled.length} disabled) ${color55.dim("description detail via /tool <name> simple|extend")}:
30278
+ ${color56.dim(`${disabled.length} tool(s) disabled. Use /tool enable <name> or /tool enable-all to restore.`)}` : "";
30279
+ const filterNote = filter ? color56.dim(` matching "${filter}" (${all.length} of ${allTools.length})`) : "";
30280
+ const msg = `${color56.bold("Tools")}${filterNote} (${all.length} shown, ${disabled.length} disabled) ${color56.dim("description detail via /tool <name> simple|extend")}:
29709
30281
  ${header}
29710
30282
  ${lines.join("\n")}${extra}
29711
30283
  `;
@@ -29719,7 +30291,7 @@ ${lines.join("\n")}${extra}
29719
30291
  import * as fs19 from "node:fs/promises";
29720
30292
  import * as os3 from "node:os";
29721
30293
  import * as path29 from "node:path";
29722
- import { atomicWrite as atomicWrite10, color as color56 } from "@wrongstack/core/utils";
30294
+ import { atomicWrite as atomicWrite10, color as color57 } from "@wrongstack/core/utils";
29723
30295
 
29724
30296
  // src/tuneup.ts
29725
30297
  var DEFAULT_EAGER_MAX_CHARS = 24e3;
@@ -30197,17 +30769,17 @@ function buildTuneupCommand(opts) {
30197
30769
  const parsed = parseArgs(args);
30198
30770
  if (parsed.mode === "help") return { message: help };
30199
30771
  if (parsed.mode === "usage") {
30200
- return { message: `${color56.amber("Usage:")} /tuneup [fix [--power] [--pick] | deep]` };
30772
+ return { message: `${color57.amber("Usage:")} /tuneup [fix [--power] [--pick] | deep]` };
30201
30773
  }
30202
30774
  if (!opts.paths) {
30203
- return { message: `${color56.red("Error")} config paths not available.` };
30775
+ return { message: `${color57.red("Error")} config paths not available.` };
30204
30776
  }
30205
30777
  const input = await gatherInput(opts, parsed.power);
30206
30778
  const report = runTuneup(input);
30207
- const lines = [`${color56.bold("WrongStack")} ${color56.dim("\u2014 Tune-up")}`];
30779
+ const lines = [`${color57.bold("WrongStack")} ${color57.dim("\u2014 Tune-up")}`];
30208
30780
  renderFindings(lines, report.findings);
30209
30781
  if (parsed.mode === "deep") {
30210
- lines.push("", color56.dim(" \u2192 asking the agent for a project-specific optimization plan\u2026"));
30782
+ lines.push("", color57.dim(" \u2192 asking the agent for a project-specific optimization plan\u2026"));
30211
30783
  return { message: lines.join("\n"), runText: buildDeepPrompt(report) };
30212
30784
  }
30213
30785
  if (parsed.mode === "report") {
@@ -30229,18 +30801,18 @@ function buildTuneupCommand(opts) {
30229
30801
  const applied = await applyActions(actions, opts);
30230
30802
  lines.push("");
30231
30803
  if (applied.messages.length === 0) {
30232
- lines.push(color56.dim(" no deterministic fixes to apply"));
30804
+ lines.push(color57.dim(" no deterministic fixes to apply"));
30233
30805
  } else {
30234
- for (const m of applied.messages) lines.push(` ${color56.green("\u2713")} ${m}`);
30806
+ for (const m of applied.messages) lines.push(` ${color57.green("\u2713")} ${m}`);
30235
30807
  if (applied.changed) {
30236
30808
  lines.push(
30237
- ` ${color56.green("\u2713")} written ${color56.dim("(backup: config.json.last + timestamped .bak)")}`
30809
+ ` ${color57.green("\u2713")} written ${color57.dim("(backup: config.json.last + timestamped .bak)")}`
30238
30810
  );
30239
30811
  }
30240
30812
  }
30241
30813
  const runText = report.agentHandoff || void 0;
30242
30814
  if (runText) {
30243
- lines.push("", color56.dim(" \u2192 handing instruction-file cleanups to the agent\u2026"));
30815
+ lines.push("", color57.dim(" \u2192 handing instruction-file cleanups to the agent\u2026"));
30244
30816
  }
30245
30817
  return { message: lines.join("\n"), ...runText ? { runText } : {} };
30246
30818
  }
@@ -30401,7 +30973,7 @@ async function applyActions(actions, opts) {
30401
30973
  parsed = JSON.parse(raw);
30402
30974
  } catch {
30403
30975
  return {
30404
- messages: [`${color56.red("\u2717")} global config is not valid JSON \u2014 run /doctor fix first`],
30976
+ messages: [`${color57.red("\u2717")} global config is not valid JSON \u2014 run /doctor fix first`],
30405
30977
  changed: false
30406
30978
  };
30407
30979
  }
@@ -30510,26 +31082,26 @@ var CATEGORY_ORDER = [
30510
31082
  function severityIcon(severity) {
30511
31083
  switch (severity) {
30512
31084
  case "error":
30513
- return color56.red("\u2717");
31085
+ return color57.red("\u2717");
30514
31086
  case "warning":
30515
- return color56.amber("!");
31087
+ return color57.amber("!");
30516
31088
  case "ok":
30517
- return color56.green("\u2713");
31089
+ return color57.green("\u2713");
30518
31090
  default:
30519
- return color56.cyan("\xB7");
31091
+ return color57.cyan("\xB7");
30520
31092
  }
30521
31093
  }
30522
31094
  function renderFindings(lines, findings) {
30523
31095
  for (const category of CATEGORY_ORDER) {
30524
31096
  const group = findings.filter((f) => f.category === category);
30525
31097
  if (group.length === 0) continue;
30526
- lines.push("", color56.bold(CATEGORY_LABELS[category]));
31098
+ lines.push("", color57.bold(CATEGORY_LABELS[category]));
30527
31099
  for (const f of group) {
30528
31100
  lines.push(` ${severityIcon(f.severity)} ${f.problem}`);
30529
31101
  if (f.suggestion) {
30530
- for (const s of f.suggestion.split("\n")) lines.push(color56.dim(` ${s}`));
31102
+ for (const s of f.suggestion.split("\n")) lines.push(color57.dim(` ${s}`));
30531
31103
  }
30532
- if (f.fix) lines.push(color56.dim(` \u2192 fixable: ${f.fix}`));
31104
+ if (f.fix) lines.push(color57.dim(` \u2192 fixable: ${f.fix}`));
30533
31105
  }
30534
31106
  }
30535
31107
  }
@@ -30538,20 +31110,20 @@ function summaryLine(findings, fixable, handoffs, power) {
30538
31110
  (f) => f.severity === "warning" || f.severity === "error"
30539
31111
  ).length;
30540
31112
  if (warnings === 0 && fixable === 0 && handoffs === 0) {
30541
- return `${color56.green("\u2713")} everything looks healthy`;
31113
+ return `${color57.green("\u2713")} everything looks healthy`;
30542
31114
  }
30543
31115
  const parts = [];
30544
31116
  if (warnings > 0) parts.push(`${warnings} warning(s)`);
30545
31117
  if (fixable > 0) parts.push(`${fixable} auto-fixable`);
30546
31118
  if (handoffs > 0) parts.push(`${handoffs} for the agent`);
30547
31119
  const cmd = power ? "/tuneup fix --power" : "/tuneup fix";
30548
- return `${parts.join(", ")} ${color56.dim(`\u2014 run ${cmd}`)}`;
31120
+ return `${parts.join(", ")} ${color57.dim(`\u2014 run ${cmd}`)}`;
30549
31121
  }
30550
31122
 
30551
31123
  // src/slash-commands/working-dir.ts
30552
31124
  import * as fs20 from "node:fs/promises";
30553
31125
  import * as path30 from "node:path";
30554
- import { color as color57, toErrorMessage as toErrorMessage29 } from "@wrongstack/core/utils";
31126
+ import { color as color58, toErrorMessage as toErrorMessage29 } from "@wrongstack/core/utils";
30555
31127
  function buildWorkingDirCommand(_opts) {
30556
31128
  return {
30557
31129
  name: "working_dir",
@@ -30570,16 +31142,16 @@ function buildWorkingDirCommand(_opts) {
30570
31142
  ].join("\n"),
30571
31143
  async run(args, ctx) {
30572
31144
  if (!ctx) {
30573
- return { message: color57.yellow("No active context. Start a session first.") };
31145
+ return { message: color58.yellow("No active context. Start a session first.") };
30574
31146
  }
30575
31147
  const trimmed = args.trim();
30576
31148
  if (!trimmed) {
30577
31149
  const rel2 = path30.relative(ctx.projectRoot, ctx.workingDir) || ".";
30578
31150
  return {
30579
31151
  message: [
30580
- `Working directory: ${color57.bold(ctx.workingDir)}`,
30581
- color57.dim(` (relative to root: ${rel2})`),
30582
- color57.dim(` Project root: ${ctx.projectRoot}`)
31152
+ `Working directory: ${color58.bold(ctx.workingDir)}`,
31153
+ color58.dim(` (relative to root: ${rel2})`),
31154
+ color58.dim(` Project root: ${ctx.projectRoot}`)
30583
31155
  ].join("\n")
30584
31156
  };
30585
31157
  }
@@ -30588,7 +31160,7 @@ function buildWorkingDirCommand(_opts) {
30588
31160
  const rel = path30.relative(root, resolved);
30589
31161
  if (rel.startsWith("..") || path30.isAbsolute(rel)) {
30590
31162
  return {
30591
- message: color57.red(
31163
+ message: color58.red(
30592
31164
  `Directory "${trimmed}" is outside the project root.
30593
31165
  Resolved: ${resolved}
30594
31166
  Root: ${root}`
@@ -30598,25 +31170,25 @@ function buildWorkingDirCommand(_opts) {
30598
31170
  try {
30599
31171
  const stat5 = await fs20.stat(resolved);
30600
31172
  if (!stat5.isDirectory()) {
30601
- return { message: color57.red(`Not a directory: ${resolved}`) };
31173
+ return { message: color58.red(`Not a directory: ${resolved}`) };
30602
31174
  }
30603
31175
  } catch {
30604
- return { message: color57.red(`Directory does not exist: ${resolved}`) };
31176
+ return { message: color58.red(`Directory does not exist: ${resolved}`) };
30605
31177
  }
30606
31178
  const previous = ctx.workingDir;
30607
31179
  try {
30608
31180
  ctx.setWorkingDir(resolved);
30609
31181
  } catch (err) {
30610
31182
  return {
30611
- message: color57.red(toErrorMessage29(err))
31183
+ message: color58.red(toErrorMessage29(err))
30612
31184
  };
30613
31185
  }
30614
31186
  const prevRel = path30.relative(ctx.projectRoot, previous) || ".";
30615
31187
  const newRel = path30.relative(ctx.projectRoot, resolved) || ".";
30616
31188
  return {
30617
31189
  message: [
30618
- color57.green(` \u2713 ${prevRel} \u2192 ${color57.bold(newRel)}`),
30619
- color57.dim(` ${resolved}`)
31190
+ color58.green(` \u2713 ${prevRel} \u2192 ${color58.bold(newRel)}`),
31191
+ color58.dim(` ${resolved}`)
30620
31192
  ].join("\n")
30621
31193
  };
30622
31194
  }
@@ -30685,7 +31257,7 @@ function buildWorktreeCommand(opts) {
30685
31257
  }
30686
31258
 
30687
31259
  // src/slash-commands/yolo.ts
30688
- import { color as color58 } from "@wrongstack/core/utils";
31260
+ import { color as color59 } from "@wrongstack/core/utils";
30689
31261
  function buildYoloCommand(opts) {
30690
31262
  return {
30691
31263
  name: "yolo",
@@ -30709,7 +31281,7 @@ function buildYoloCommand(opts) {
30709
31281
  }
30710
31282
  if (!arg) {
30711
31283
  const current = opts.onYolo();
30712
- const status = current ? `${color58.yellow("ON")} ${color58.dim("(auto-approving tool calls)")}` : `${color58.green("OFF")} ${color58.dim("(permission prompts active)")}`;
31284
+ const status = current ? `${color59.yellow("ON")} ${color59.dim("(auto-approving tool calls)")}` : `${color59.green("OFF")} ${color59.dim("(permission prompts active)")}`;
30713
31285
  const msg2 = `YOLO mode: ${status}`;
30714
31286
  opts.renderer.write(msg2);
30715
31287
  return { message: msg2 };
@@ -30724,11 +31296,11 @@ function buildYoloCommand(opts) {
30724
31296
  } else if (arg === "destructive") {
30725
31297
  const currentMode = opts.onYolo();
30726
31298
  if (!currentMode) {
30727
- const msg3 = `${color58.amber("YOLO is OFF.")} Destructive-gate flags are deprecated; prompts are active because YOLO is off.`;
31299
+ const msg3 = `${color59.amber("YOLO is OFF.")} Destructive-gate flags are deprecated; prompts are active because YOLO is off.`;
30728
31300
  opts.renderer.writeWarning(msg3);
30729
31301
  return { message: msg3 };
30730
31302
  }
30731
- const msg2 = `${color58.amber("Destructive gate:")} ${color58.dim("deprecated \u2014 YOLO auto-approves all non-denied tool calls.")}`;
31303
+ const msg2 = `${color59.amber("Destructive gate:")} ${color59.dim("deprecated \u2014 YOLO auto-approves all non-denied tool calls.")}`;
30732
31304
  opts.renderer.writeWarning(msg2);
30733
31305
  return { message: msg2 };
30734
31306
  } else {
@@ -30737,7 +31309,7 @@ function buildYoloCommand(opts) {
30737
31309
  return { message: msg2 };
30738
31310
  }
30739
31311
  opts.onYolo(newState);
30740
- const label = newState ? `${color58.yellow("ENABLED")} \u2014 tool calls will be auto-approved unless explicitly denied` : `${color58.green("DISABLED")} \u2014 permission prompts are active`;
31312
+ const label = newState ? `${color59.yellow("ENABLED")} \u2014 tool calls will be auto-approved unless explicitly denied` : `${color59.green("DISABLED")} \u2014 permission prompts are active`;
30741
31313
  const msg = `YOLO mode: ${label}`;
30742
31314
  opts.renderer.write(msg);
30743
31315
  return { message: msg };
@@ -30802,6 +31374,7 @@ function buildBuiltinSlashCommands(opts) {
30802
31374
  buildBtwCommand(opts),
30803
31375
  buildNextCommand(opts),
30804
31376
  buildModeCommand(opts),
31377
+ buildThemeCommand(opts),
30805
31378
  buildDesignCommand(opts),
30806
31379
  buildMailboxDemoCommand(opts),
30807
31380
  buildMailboxCommand(opts),
@@ -30888,7 +31461,7 @@ async function runInteractive(cliCtx) {
30888
31461
  activeMode
30889
31462
  });
30890
31463
  if (modeResult.kind === "exit") {
30891
- writeErr2(`${modeResult.message}
31464
+ writeErr3(`${modeResult.message}
30892
31465
  `);
30893
31466
  await reader.close();
30894
31467
  return modeResult.code;
@@ -30909,6 +31482,15 @@ async function runInteractive(cliCtx) {
30909
31482
  container,
30910
31483
  modeStore,
30911
31484
  memoryStore,
31485
+ // Provide a narrow domain-term adapter so the prompt builder emits a
31486
+ // compact `[Project Jargon Dictionary]` block. Closes over the resolved
31487
+ // SAGE `memoryStore` (a `MemoryPort`) and uses the typed
31488
+ // `SageServiceLike` capability (NOT `@wrongstack/sage-mcp` — the
31489
+ // in-process consumer rule from `packages/sage/docs/direct-icp-usage.md`).
31490
+ // Returns only memories tagged `domain-term` via the capability's
31491
+ // `searchSage` op, which performs the tag-filter at the SQL layer rather
31492
+ // than scanning the whole corpus on every prompt build.
31493
+ domainGlossary: createDomainGlossaryAdapter(memoryStore),
30912
31494
  skillLoader,
30913
31495
  sessionRef,
30914
31496
  autonomyModeRef,
@@ -30929,6 +31511,7 @@ async function runInteractive(cliCtx) {
30929
31511
  pathJoiner: { join: (a, b) => path31.join(a, b) },
30930
31512
  systemPromptBuilderToken: TOKENS9.SystemPromptBuilder
30931
31513
  });
31514
+ await refreshDomainTermsMirror({ projectRoot, memoryStore });
30932
31515
  const toolRegistry = new ToolRegistry2();
30933
31516
  registerBuiltinTools({
30934
31517
  toolRegistry,
@@ -31024,7 +31607,26 @@ async function runInteractive(cliCtx) {
31024
31607
  }
31025
31608
  });
31026
31609
  const { context, planPath, session } = sessResult;
31610
+ context.meta["promptOnlineAgents"] = onlineAgents;
31027
31611
  sessionRef.current = session;
31612
+ const replayFlag = flags["replay"];
31613
+ const recordFlag = flags["record"];
31614
+ if (typeof replayFlag === "string" || recordFlag === true) {
31615
+ const { bindReplayToContainer } = await import("./replay-XU6ZD7GM.js");
31616
+ const mode = recordFlag === true ? "record" : "replay";
31617
+ bindReplayToContainer({
31618
+ container,
31619
+ wpaths,
31620
+ // Recording follows the live session across `/resume` and new-session
31621
+ // swaps; replaying is pinned to the id the user named.
31622
+ sessionId: typeof replayFlag === "string" ? replayFlag : () => sessionRef.current?.id ?? "",
31623
+ mode,
31624
+ logger
31625
+ });
31626
+ logger.info(
31627
+ `replay: ProviderRunner bound in '${mode}' mode for session ${typeof replayFlag === "string" ? replayFlag : session.id}`
31628
+ );
31629
+ }
31028
31630
  const governanceHandle = await setupCliGovernance({
31029
31631
  projectRoot,
31030
31632
  projectId: wpaths.projectSlug,
@@ -31559,7 +32161,7 @@ async function runInteractive(cliCtx) {
31559
32161
  onEvent: evOn
31560
32162
  });
31561
32163
  const savedProviderCfg = config.providers?.[config.provider];
31562
- const { execute } = await import("./execution-LZRYRGDD.js");
32164
+ const { execute } = await import("./execution-YMBD7YWC.js");
31563
32165
  const stopHeapWatchdog = startSharedHeapWatchdog({
31564
32166
  collectStats: () => {
31565
32167
  const hqQueue = hqPublisherRef.current?.getQueueStats();
@@ -31610,6 +32212,11 @@ async function runInteractive(cliCtx) {
31610
32212
  events,
31611
32213
  slashRegistry,
31612
32214
  tokenCounter,
32215
+ // Forward the boot sessionRef so `resumeSession` can repoint
32216
+ // it when an in-process `/resume` swaps the active writer.
32217
+ // Without this, provider-side `getSessionId` callbacks and the
32218
+ // record-mode binding keep referring to the boot session.
32219
+ sessionRef,
31613
32220
  activateSessionIdentity: activateSession,
31614
32221
  config,
31615
32222
  configStore,
@@ -31792,4 +32399,4 @@ export {
31792
32399
  CLI_VERSION,
31793
32400
  runInteractive
31794
32401
  };
31795
- //# sourceMappingURL=cli-main-PJMDZHDK.js.map
32402
+ //# sourceMappingURL=cli-main-W3T56YCY.js.map