@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.
- package/dist/{acp-XCE4JJF3.js → acp-5ZLGFHWP.js} +5 -2
- package/dist/boot/system-prompt-builder.d.ts +24 -1
- package/dist/boot/tui-project-switch.d.ts +1 -1
- package/dist/boot/tui-runtime-state.d.ts +14 -0
- package/dist/boot/tui-theme-adapter.d.ts +33 -0
- package/dist/{chunk-BXQG3H2Y.js → chunk-53STH6CX.js} +87 -13
- package/dist/{chunk-JW75HY4F.js → chunk-FKWHSFX4.js} +33 -4
- package/dist/{chunk-WOXUE2CN.js → chunk-RHLQT3EV.js} +12 -2
- package/dist/{cli-main-PJMDZHDK.js → cli-main-W3T56YCY.js} +1498 -891
- package/dist/execute-deps.d.ts +16 -0
- package/dist/{execution-LZRYRGDD.js → execution-YMBD7YWC.js} +149 -37
- package/dist/fleet/host-context.d.ts +11 -0
- package/dist/fleet/host-learning-tracker.d.ts +2 -2
- package/dist/fleet/host-learning.d.ts +12 -1
- package/dist/fleet/host-session-writer.d.ts +33 -0
- package/dist/fleet/host-subagent-factory.d.ts +1 -1
- package/dist/fleet/host.d.ts +32 -0
- package/dist/{hq-AHPAE7EN.js → hq-5G6D5YIU.js} +2 -2
- package/dist/{hq-server-KU2Z54QU.js → hq-server-IVZLVCFV.js} +2 -2
- package/dist/index.js +15 -58
- package/dist/{mailbox-serve-DBOOOOWZ.js → mailbox-serve-V3QIJBDH.js} +2 -2
- package/dist/{mcp-E5EBGOHF.js → mcp-MSARYOPN.js} +5 -2
- package/dist/replay-XU6ZD7GM.js +34 -0
- package/dist/slash-commands/agent-improve.d.ts +4 -1
- package/dist/slash-commands/memory-triage.d.ts +4 -17
- package/dist/slash-commands/theme.d.ts +6 -0
- package/dist/subcommands/handlers/update.d.ts +2 -0
- package/dist/{update-LTDJGAFU.js → update-VZSOZGYC.js} +2 -2
- package/dist/{webui-server-E3OPEKU5.js → webui-server-FTG7JAZU.js} +5 -5
- package/dist/wiring/domain-glossary.d.ts +58 -0
- package/dist/wiring/domain-terms-mirror.d.ts +52 -0
- package/dist/wiring/pipeline.d.ts +2 -0
- package/dist/wiring/replay.d.ts +11 -1
- package/dist/wiring/sage.d.ts +4 -16
- 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-
|
|
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
|
|
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 {
|
|
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 {
|
|
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,
|
|
3534
|
-
|
|
3535
|
-
|
|
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 (!
|
|
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(
|
|
3618
|
+
const capture = captureLearnedFromAgentOutputDetailed(
|
|
3619
|
+
finalText,
|
|
3620
|
+
subject.role,
|
|
3621
|
+
deps.projectRoot,
|
|
3622
|
+
false
|
|
3623
|
+
);
|
|
3540
3624
|
if (capture.captured > 0) {
|
|
3541
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
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
|
-
|
|
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)
|
|
3817
|
-
|
|
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)
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
3830
|
-
|
|
3831
|
-
)
|
|
3832
|
-
|
|
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
|
|
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 (
|
|
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:
|
|
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 =
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
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
|
-
|
|
10416
|
-
captureLearnedFromAgentOutput,
|
|
10662
|
+
captureLearnedFromAgentOutputDetailed as captureLearnedFromAgentOutputDetailed2,
|
|
10417
10663
|
getProjectAgentLearnStats,
|
|
10418
|
-
|
|
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: "
|
|
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]
|
|
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
|
-
"
|
|
10458
|
-
" Agents
|
|
10459
|
-
"
|
|
10460
|
-
|
|
10461
|
-
|
|
10462
|
-
|
|
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
|
-
|
|
10539
|
-
|
|
10540
|
-
|
|
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
|
|
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
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
const
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
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,
|
|
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,
|
|
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 = `${
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
25543
|
-
if (res.ok) return
|
|
25544
|
-
if (res.status === 401) return
|
|
25545
|
-
return
|
|
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
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: ${
|
|
26100
|
+
token: ${color47.dim(maskToken(token))}` : "";
|
|
25615
26101
|
return {
|
|
25616
|
-
message: `${
|
|
26102
|
+
message: `${color47.green("\u2713")} HQ set \u2192 ${color47.cyan(url)}${tokLine}
|
|
25617
26103
|
status: ${reach}
|
|
25618
|
-
${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
25649
|
-
${
|
|
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: `${
|
|
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: `${
|
|
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 = [`${
|
|
26159
|
+
const lines = [`${color47.bold("\u{1F4CB} WrongStack HQ \u2014 connection")}`, ""];
|
|
25674
26160
|
if (!resolved) {
|
|
25675
26161
|
lines.push(
|
|
25676
|
-
` ${
|
|
26162
|
+
` ${color47.dim("Not configured.")} Use ${color47.cyan("/hq set <url> [token]")} to connect,`
|
|
25677
26163
|
);
|
|
25678
|
-
lines.push(` or run ${
|
|
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
|
-
` ${
|
|
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: ${
|
|
26176
|
+
` mode: ${color47.cyan("auto-discovery")} ${color47.dim("(no local HQ running yet)")}`
|
|
25691
26177
|
);
|
|
25692
26178
|
lines.push(
|
|
25693
|
-
` ${
|
|
26179
|
+
` ${color47.dim("This session will attach automatically when `wstack --hq` starts")}`
|
|
25694
26180
|
);
|
|
25695
|
-
lines.push(` ${
|
|
25696
|
-
lines.push(` ${
|
|
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: ${
|
|
26186
|
+
lines.push(` url: ${color47.cyan(resolved.url)}`);
|
|
25701
26187
|
lines.push(
|
|
25702
|
-
` enabled: ${resolved.enabled === false ?
|
|
26188
|
+
` enabled: ${resolved.enabled === false ? color47.dim("false") : color47.green("true")}`
|
|
25703
26189
|
);
|
|
25704
|
-
if (resolved.discover) lines.push(` mode: ${
|
|
25705
|
-
lines.push(` 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 ?
|
|
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 ?
|
|
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: ${
|
|
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: `${
|
|
25718
|
-
${
|
|
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
|
|
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: `${
|
|
25899
|
-
${
|
|
25900
|
-
} : { message: `No committed project identity. Run ${
|
|
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 ? `${
|
|
25907
|
-
${
|
|
25908
|
-
${
|
|
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 ${
|
|
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 ?
|
|
25931
|
-
`${
|
|
25932
|
-
|
|
25933
|
-
|
|
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:
|
|
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 ?
|
|
25955
|
-
const name = isCurrent ?
|
|
25956
|
-
const slug =
|
|
25957
|
-
const last =
|
|
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(` ${
|
|
26447
|
+
lines.push(` ${color48.green("\u2190 active session")}`);
|
|
25962
26448
|
}
|
|
25963
26449
|
lines.push("");
|
|
25964
26450
|
}
|
|
25965
26451
|
lines.push(
|
|
25966
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
26000
|
-
|
|
26001
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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(
|
|
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
|
-
|
|
26104
|
-
|
|
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 = [
|
|
26604
|
+
const parts = [color48.yellow(`\u26A0 Switching projects will stop all running agents.`), ""];
|
|
26119
26605
|
if (fleetRunning > 0) {
|
|
26120
|
-
parts.push(
|
|
26606
|
+
parts.push(color48.dim(` \u2022 ${fleetRunning} subagent(s) currently running`));
|
|
26121
26607
|
}
|
|
26122
26608
|
if (eternalActive) {
|
|
26123
|
-
parts.push(
|
|
26609
|
+
parts.push(color48.dim(" \u2022 Eternal engine is active"));
|
|
26124
26610
|
}
|
|
26125
26611
|
if (parallelActive) {
|
|
26126
|
-
parts.push(
|
|
26612
|
+
parts.push(color48.dim(" \u2022 Parallel engine is active"));
|
|
26127
26613
|
}
|
|
26128
26614
|
parts.push("");
|
|
26129
|
-
parts.push(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
26638
|
+
opts.renderer.write(color48.dim(" Stopped eternal engine.\n"));
|
|
26153
26639
|
}
|
|
26154
26640
|
if (parallelActive) {
|
|
26155
26641
|
parallelEngine?.stop();
|
|
26156
|
-
opts.renderer.write(
|
|
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:
|
|
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:
|
|
26660
|
+
return { message: color48.red(`Project not found: ${result.key}`) };
|
|
26175
26661
|
}
|
|
26176
26662
|
if (project.root === currentRoot) {
|
|
26177
|
-
return { message:
|
|
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:
|
|
26676
|
+
return { message: color48.dim("Cancelled.") };
|
|
26191
26677
|
}
|
|
26192
26678
|
}
|
|
26193
26679
|
default:
|
|
26194
|
-
return { message:
|
|
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:
|
|
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(
|
|
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
|
-
|
|
26246
|
-
|
|
26247
|
-
|
|
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:
|
|
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(
|
|
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
|
-
|
|
26288
|
-
|
|
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:
|
|
26785
|
+
return { message: color48.dim("No saved sessions.") };
|
|
26300
26786
|
}
|
|
26301
26787
|
const currentId = opts.context?.session?.id;
|
|
26302
|
-
const lines = [
|
|
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 ?
|
|
26306
|
-
const date =
|
|
26791
|
+
const marker = isCurrent ? color48.cyan("\u25CF") : " ";
|
|
26792
|
+
const date = color48.dim(s.startedAt.slice(0, 16).replace("T", " "));
|
|
26307
26793
|
const stats = [
|
|
26308
|
-
|
|
26309
|
-
s.toolCallCount ?
|
|
26310
|
-
s.iterationCount ?
|
|
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" ?
|
|
26313
|
-
lines.push(` ${marker} ${
|
|
26314
|
-
lines.push(` ${stats} ${outcome} ${
|
|
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(
|
|
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
|
|
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
|
-
`${
|
|
27119
|
+
`${color49.bold("WrongStack")} ${color49.dim("\u2014 Settings")}`,
|
|
26634
27120
|
"",
|
|
26635
|
-
` auto-proceed delay: ${
|
|
26636
|
-
` default autonomy mode: ${
|
|
26637
|
-
` fleet chat: ${
|
|
26638
|
-
` completion chime: ${au?.chime === true ?
|
|
26639
|
-
` confirm before exit: ${au?.confirmExit !== false ?
|
|
26640
|
-
` launch hints: ${hints ?
|
|
26641
|
-
` debug stream: ${debugStream ?
|
|
26642
|
-
` config scope: ${
|
|
26643
|
-
` filesystem access: ${
|
|
26644
|
-
` refine: ${enhanceEnabled ?
|
|
26645
|
-
` refine-delay: ${
|
|
26646
|
-
` refine-language: ${
|
|
26647
|
-
` refiner-provider: ${
|
|
26648
|
-
` refiner-model: ${
|
|
26649
|
-
` refiner-fallback-profile: ${
|
|
26650
|
-
` semver default part: ${
|
|
26651
|
-
` circuit breaker: ${breakerEnabled ?
|
|
26652
|
-
` context mode: ${
|
|
26653
|
-
` context strategy: ${
|
|
26654
|
-
` context auto-compact: ${contextAutoCompact ?
|
|
26655
|
-
` token-saving: ${
|
|
26656
|
-
` nextsteps tool: ${nextStepsToolEnabled ?
|
|
26657
|
-
` MCP features: ${feats?.mcp !== false ?
|
|
26658
|
-
` plugin features: ${feats?.plugins !== false ?
|
|
26659
|
-
` memory features: ${feats?.memory !== false ?
|
|
26660
|
-
` skills features: ${feats?.skills !== false ?
|
|
26661
|
-
` models registry: ${feats?.modelsRegistry !== false ?
|
|
26662
|
-
` max concurrent: ${
|
|
26663
|
-
` max iterations: ${
|
|
26664
|
-
` auto-proceed max iters: ${
|
|
26665
|
-
` title animation: ${titleAnimation ?
|
|
26666
|
-
` thinking word: ${
|
|
26667
|
-
` statusline mode: ${
|
|
26668
|
-
` animation style: ${
|
|
26669
|
-
` read symbols: ${au?.readAdvancedMode === true ?
|
|
26670
|
-
` reasoning mode: ${
|
|
26671
|
-
` reasoning effort: ${
|
|
26672
|
-
` reasoning preserve: ${reasoningPreserve ?
|
|
26673
|
-
` cache TTL: ${
|
|
26674
|
-
` index on start: ${idx?.onSessionStart !== false ?
|
|
26675
|
-
` log level: ${
|
|
26676
|
-
` audit level: ${
|
|
26677
|
-
` HQ publishing: ${hqEnabled ?
|
|
26678
|
-
` HQ URL: ${
|
|
26679
|
-
` HQ token: ${
|
|
26680
|
-
` HQ raw content: ${hq?.rawContent === true ?
|
|
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
|
-
|
|
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: `${
|
|
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
|
-
`${
|
|
27191
|
+
`${color49.bold("Default Values")}`,
|
|
26706
27192
|
"",
|
|
26707
|
-
` auto-proceed delay: ${
|
|
26708
|
-
` default autonomy mode: ${
|
|
26709
|
-
` launch hints: ${
|
|
26710
|
-
` iteration timeout: ${
|
|
26711
|
-
` session timeout: ${
|
|
26712
|
-
` max iterations: ${
|
|
26713
|
-
` max concurrent: ${
|
|
26714
|
-
` semver default part: ${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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" ? `${
|
|
26856
|
-
return { message: `${
|
|
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: `${
|
|
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 ? `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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" ? `${
|
|
26922
|
-
return { message: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
27937
|
+
return { message: `${color49.green("\u2713")} animation style \u2192 ${color49.cyan(raw)}` };
|
|
27452
27938
|
}
|
|
27453
27939
|
return {
|
|
27454
|
-
message: `${
|
|
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: `${
|
|
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
|
|
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
|
-
`${
|
|
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: `${
|
|
27577
|
-
${
|
|
27578
|
-
${
|
|
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: `${
|
|
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: `${
|
|
28080
|
+
return { message: `${color50.green("\u2713")} Shadow Agent stopped: ${activeId}` };
|
|
27595
28081
|
}
|
|
27596
28082
|
return {
|
|
27597
28083
|
message: [
|
|
27598
|
-
`${
|
|
27599
|
-
`It may already be stopped. Use ${
|
|
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
|
-
`${
|
|
28113
|
+
`${color50.red("\u26A0")} HOOP: Stopped ${killed} running agent(s)`,
|
|
27628
28114
|
"",
|
|
27629
|
-
`Target: ${
|
|
27630
|
-
`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 ? `${
|
|
28130
|
+
ok ? `${color50.red("\u26A0")} HOOP: Stopped agent` : `${color50.red("\u2717")} HOOP: Failed to stop agent`,
|
|
27645
28131
|
"",
|
|
27646
|
-
`Target: ${
|
|
27647
|
-
`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
|
-
${
|
|
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
|
-
${
|
|
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
|
|
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 ${
|
|
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 ${
|
|
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 ? ` ${
|
|
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" ?
|
|
28023
|
-
return `${
|
|
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() ?
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 ${
|
|
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 ${
|
|
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 =
|
|
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 =
|
|
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 = `${
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
`${
|
|
28825
|
-
`Run ${
|
|
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: `${
|
|
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: `${
|
|
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 ${
|
|
29378
|
+
botToken = (await opts.readSecret(`Telegram bot token ${color53.dim("(hidden, paste OK)")}: `)).trim();
|
|
28840
29379
|
} catch {
|
|
28841
|
-
return { message:
|
|
29380
|
+
return { message: color53.dim("Telegram setup cancelled.") };
|
|
28842
29381
|
}
|
|
28843
|
-
if (!botToken) return { message:
|
|
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
|
-
`${
|
|
28848
|
-
`Expected: ${
|
|
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
|
-
`${
|
|
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
|
-
`${
|
|
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: `${
|
|
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
|
-
`${
|
|
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
|
-
`${
|
|
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
|
-
`${
|
|
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
|
-
|
|
29458
|
+
color53.bold("Recent Telegram identities"),
|
|
28920
29459
|
formatTelegramPairingCandidates(candidates),
|
|
28921
29460
|
"",
|
|
28922
|
-
|
|
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:
|
|
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:
|
|
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: `${
|
|
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
|
-
`${
|
|
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
|
-
`${
|
|
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
|
-
`${
|
|
29532
|
+
`${color53.green("\u2713")} Telegram configured successfully.`,
|
|
28994
29533
|
"",
|
|
28995
|
-
`Bot: ${
|
|
29534
|
+
`Bot: ${color53.bold(`@${bot.username ?? bot.first_name}`)}`,
|
|
28996
29535
|
...pairedCandidate ? [
|
|
28997
|
-
`Paired private chat: ${
|
|
28998
|
-
`Paired user: ${
|
|
28999
|
-
] : chatId ? [`Default chat: ${
|
|
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
|
-
`${
|
|
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
|
-
`${
|
|
29574
|
+
`${color54.bold("Telegram")} ${color54.dim("\u2014 Notification Settings")}`,
|
|
29036
29575
|
"",
|
|
29037
|
-
` session end: ${sessionEnd ?
|
|
29038
|
-
` delegate done: ${delegate ?
|
|
29039
|
-
` long tool: ${
|
|
29040
|
-
` poll interval: ${
|
|
29041
|
-
` notify chat: ${
|
|
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 ?
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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
|
-
`${
|
|
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: `${
|
|
29734
|
+
message: `${color54.green("\u2713")} notify chat \u2192 ${color54.cyan(raw)}`
|
|
29196
29735
|
};
|
|
29197
29736
|
}
|
|
29198
29737
|
return {
|
|
29199
|
-
message: `${
|
|
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: `${
|
|
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
|
-
|
|
29244
|
-
|
|
29245
|
-
|
|
29246
|
-
|
|
29247
|
-
|
|
29248
|
-
|
|
29249
|
-
|
|
29250
|
-
|
|
29251
|
-
|
|
29252
|
-
|
|
29253
|
-
|
|
29254
|
-
|
|
29255
|
-
|
|
29256
|
-
|
|
29257
|
-
|
|
29258
|
-
|
|
29259
|
-
|
|
29260
|
-
|
|
29261
|
-
|
|
29262
|
-
|
|
29263
|
-
|
|
29264
|
-
|
|
29265
|
-
|
|
29266
|
-
|
|
29267
|
-
|
|
29268
|
-
|
|
29269
|
-
|
|
29270
|
-
|
|
29271
|
-
|
|
29272
|
-
|
|
29273
|
-
|
|
29274
|
-
|
|
29275
|
-
|
|
29276
|
-
|
|
29277
|
-
|
|
29278
|
-
|
|
29279
|
-
|
|
29280
|
-
|
|
29281
|
-
|
|
29282
|
-
|
|
29283
|
-
|
|
29284
|
-
|
|
29285
|
-
|
|
29286
|
-
|
|
29287
|
-
|
|
29288
|
-
|
|
29289
|
-
|
|
29290
|
-
|
|
29291
|
-
|
|
29292
|
-
|
|
29293
|
-
|
|
29294
|
-
|
|
29295
|
-
|
|
29296
|
-
|
|
29297
|
-
|
|
29298
|
-
|
|
29299
|
-
|
|
29300
|
-
|
|
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
|
-
|
|
29303
|
-
|
|
29304
|
-
|
|
29305
|
-
|
|
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
|
|
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" ?
|
|
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" ?
|
|
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
|
-
`${
|
|
30014
|
+
`${color55.bold("Tool modes")} ${color55.dim("(default: extend on both axes)")}`,
|
|
29443
30015
|
"",
|
|
29444
|
-
`${formatDescriptionMode("simple")}: ${descSimple.length > 0 ? descSimple.map((n) =>
|
|
29445
|
-
`${formatResultRenderMode("simple")}: ${resultSimple.length > 0 ? resultSimple.map((n) =>
|
|
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
|
-
`${
|
|
30022
|
+
`${color55.bold("Disabled tools")}`,
|
|
29451
30023
|
"",
|
|
29452
|
-
` ${
|
|
30024
|
+
` ${color55.red("disabled")}: ${disabled.map(({ tool }) => color55.dim(tool.name)).join(", ")}`,
|
|
29453
30025
|
""
|
|
29454
30026
|
);
|
|
29455
30027
|
}
|
|
29456
30028
|
lines.push(
|
|
29457
|
-
|
|
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 = ` ${
|
|
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) ?
|
|
29470
|
-
return ` ${fit(tool.name, 28)} ${
|
|
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
|
-
`${
|
|
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 `${
|
|
30056
|
+
return `${color55.amber(name)} is disabled. Use ${color55.dim(`/tool enable ${name}`)} to restore.`;
|
|
29485
30057
|
}
|
|
29486
|
-
return `${
|
|
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) ?
|
|
30062
|
+
const status = reg.isDisabled(name) ? color55.red("disabled") : color55.green("active");
|
|
29491
30063
|
return [
|
|
29492
|
-
`${
|
|
30064
|
+
`${color55.bold(name)} ${status}`,
|
|
29493
30065
|
`description mode: ${formatDescriptionMode(descMode)}`,
|
|
29494
30066
|
`result mode: ${formatResultRenderMode(resultMode)}`,
|
|
29495
30067
|
"",
|
|
29496
|
-
|
|
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 `${
|
|
30074
|
+
return `${color55.amber(name)} is not disabled.`;
|
|
29503
30075
|
}
|
|
29504
30076
|
const ok = reg.enable(name);
|
|
29505
|
-
if (!ok) return `${
|
|
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 `${
|
|
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 `${
|
|
30086
|
+
if (count === 0) return `${color55.amber("No disabled tools to re-enable.")}`;
|
|
29515
30087
|
await persistDisabled([]);
|
|
29516
|
-
return `${
|
|
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 `${
|
|
30095
|
+
return `${color55.amber(name)} is already disabled.`;
|
|
29524
30096
|
}
|
|
29525
|
-
return `${
|
|
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 `${
|
|
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 `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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: `${
|
|
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 ?
|
|
30197
|
+
const persistence2 = persisted2 ? color55.dim("saved") : color55.dim("runtime only; config paths unavailable");
|
|
29626
30198
|
return {
|
|
29627
|
-
message: `${
|
|
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 ?
|
|
30204
|
+
const persistence = persisted ? color55.dim("saved") : color55.dim("runtime only; config paths unavailable");
|
|
29633
30205
|
return {
|
|
29634
|
-
message: `${
|
|
30206
|
+
message: `${color55.green("\u2713")} ${color55.cyan(name)} ${formatResultRenderMode(mode2)} ${persistence}`
|
|
29635
30207
|
};
|
|
29636
30208
|
} catch (err) {
|
|
29637
30209
|
return {
|
|
29638
|
-
message: `${
|
|
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: `${
|
|
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 ?
|
|
30224
|
+
const persistence = persisted ? color55.dim("saved (both axes)") : color55.dim("runtime only; config paths unavailable");
|
|
29653
30225
|
return {
|
|
29654
|
-
message: `${
|
|
30226
|
+
message: `${color55.green("\u2713")} ${color55.cyan(name)} ${formatDescriptionMode(mode)} + ${formatResultRenderMode(mode)} ${persistence}`
|
|
29655
30227
|
};
|
|
29656
30228
|
} catch (err) {
|
|
29657
30229
|
return {
|
|
29658
|
-
message: `${
|
|
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
|
|
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" ?
|
|
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 = `${
|
|
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 = ` ${
|
|
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 ?
|
|
29702
|
-
const status = reg.isDisabled(tool.name) ?
|
|
29703
|
-
return ` ${fit2(tool.name, 28)} ${
|
|
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
|
-
${
|
|
29707
|
-
const filterNote = filter ?
|
|
29708
|
-
const msg = `${
|
|
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
|
|
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: `${
|
|
30772
|
+
return { message: `${color57.amber("Usage:")} /tuneup [fix [--power] [--pick] | deep]` };
|
|
30201
30773
|
}
|
|
30202
30774
|
if (!opts.paths) {
|
|
30203
|
-
return { message: `${
|
|
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 = [`${
|
|
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("",
|
|
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(
|
|
30804
|
+
lines.push(color57.dim(" no deterministic fixes to apply"));
|
|
30233
30805
|
} else {
|
|
30234
|
-
for (const m of applied.messages) lines.push(` ${
|
|
30806
|
+
for (const m of applied.messages) lines.push(` ${color57.green("\u2713")} ${m}`);
|
|
30235
30807
|
if (applied.changed) {
|
|
30236
30808
|
lines.push(
|
|
30237
|
-
` ${
|
|
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("",
|
|
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: [`${
|
|
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
|
|
31085
|
+
return color57.red("\u2717");
|
|
30514
31086
|
case "warning":
|
|
30515
|
-
return
|
|
31087
|
+
return color57.amber("!");
|
|
30516
31088
|
case "ok":
|
|
30517
|
-
return
|
|
31089
|
+
return color57.green("\u2713");
|
|
30518
31090
|
default:
|
|
30519
|
-
return
|
|
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("",
|
|
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(
|
|
31102
|
+
for (const s of f.suggestion.split("\n")) lines.push(color57.dim(` ${s}`));
|
|
30531
31103
|
}
|
|
30532
|
-
if (f.fix) lines.push(
|
|
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 `${
|
|
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(", ")} ${
|
|
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
|
|
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:
|
|
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: ${
|
|
30581
|
-
|
|
30582
|
-
|
|
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:
|
|
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:
|
|
31173
|
+
return { message: color58.red(`Not a directory: ${resolved}`) };
|
|
30602
31174
|
}
|
|
30603
31175
|
} catch {
|
|
30604
|
-
return { message:
|
|
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:
|
|
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
|
-
|
|
30619
|
-
|
|
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
|
|
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 ? `${
|
|
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 = `${
|
|
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 = `${
|
|
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 ? `${
|
|
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
|
-
|
|
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-
|
|
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-
|
|
32402
|
+
//# sourceMappingURL=cli-main-W3T56YCY.js.map
|