@wrongstack/cli 0.302.2 → 0.303.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-AQMGKW6O.js} +3 -2
- package/dist/boot/tui-theme-adapter.d.ts +33 -0
- package/dist/{chunk-BXQG3H2Y.js → chunk-53STH6CX.js} +87 -13
- package/dist/{chunk-WOXUE2CN.js → chunk-LUAFQIXT.js} +2 -2
- package/dist/{cli-main-PJMDZHDK.js → cli-main-37XO26GK.js} +1190 -674
- package/dist/{execution-LZRYRGDD.js → execution-MVOH6PAQ.js} +82 -20
- package/dist/fleet/host-context.d.ts +9 -0
- package/dist/fleet/host-learning-tracker.d.ts +2 -2
- package/dist/fleet/host-learning.d.ts +12 -1
- package/dist/fleet/host-subagent-factory.d.ts +1 -1
- package/dist/fleet/host.d.ts +22 -0
- package/dist/{hq-AHPAE7EN.js → hq-CD77GVSS.js} +2 -2
- package/dist/{hq-server-KU2Z54QU.js → hq-server-DME6EWIO.js} +2 -2
- package/dist/index.js +8 -8
- package/dist/{mcp-E5EBGOHF.js → mcp-AX6ZAFJR.js} +3 -2
- package/dist/slash-commands/agent-improve.d.ts +4 -1
- 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/package.json +23 -23
|
@@ -2880,7 +2880,12 @@ function subscribeBrainDecisionLog(events) {
|
|
|
2880
2880
|
|
|
2881
2881
|
// src/fleet/host.ts
|
|
2882
2882
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
2883
|
-
import {
|
|
2883
|
+
import {
|
|
2884
|
+
createProjectAgentRoster,
|
|
2885
|
+
LearningOptimizationScheduler,
|
|
2886
|
+
listProjectAgentRoles,
|
|
2887
|
+
resolveAutoOptimizePolicy
|
|
2888
|
+
} from "@wrongstack/core/agent-catalog";
|
|
2884
2889
|
import {
|
|
2885
2890
|
AdaptiveConcurrencyController,
|
|
2886
2891
|
DEFAULT_MAX_FLEET_SPAWNS as DEFAULT_MAX_FLEET_SPAWNS2,
|
|
@@ -2889,7 +2894,8 @@ import {
|
|
|
2889
2894
|
HARD_MAX_SPAWN_DEPTH as HARD_MAX_SPAWN_DEPTH2,
|
|
2890
2895
|
makeDirectorSessionFactory,
|
|
2891
2896
|
makeFleetEmitTool,
|
|
2892
|
-
resolveProjectDir
|
|
2897
|
+
resolveProjectDir,
|
|
2898
|
+
resolveSubagentModelTarget as resolveSubagentModelTarget2
|
|
2893
2899
|
} from "@wrongstack/core/coordination";
|
|
2894
2900
|
import { TOKENS as TOKENS5 } from "@wrongstack/core/kernel";
|
|
2895
2901
|
import { ToolRegistry } from "@wrongstack/core/registry";
|
|
@@ -3528,24 +3534,53 @@ function createHostStatusBroadcaster(input) {
|
|
|
3528
3534
|
}
|
|
3529
3535
|
|
|
3530
3536
|
// src/fleet/host-learning.ts
|
|
3531
|
-
import {
|
|
3537
|
+
import {
|
|
3538
|
+
captureLearnedFromAgentOutputDetailed,
|
|
3539
|
+
recordSkillOutcome
|
|
3540
|
+
} from "@wrongstack/core/agent-catalog";
|
|
3532
3541
|
import { TOKENS as TOKENS2 } from "@wrongstack/core/kernel";
|
|
3533
|
-
function captureCompletedTaskLearningForHost(result, deps,
|
|
3534
|
-
|
|
3535
|
-
|
|
3542
|
+
function captureCompletedTaskLearningForHost(result, deps, subjects, onCaptured) {
|
|
3543
|
+
const subject = subjects.get(result.subagentId);
|
|
3544
|
+
if (!subject) return;
|
|
3545
|
+
const logger = deps.container.safeResolve(TOKENS2.Logger);
|
|
3546
|
+
if (subject.skills.length > 0) {
|
|
3547
|
+
try {
|
|
3548
|
+
recordSkillOutcome(
|
|
3549
|
+
subject.role,
|
|
3550
|
+
subject.skills,
|
|
3551
|
+
result.status === "success",
|
|
3552
|
+
deps.projectRoot
|
|
3553
|
+
);
|
|
3554
|
+
} catch (error) {
|
|
3555
|
+
logger?.debug?.(`skill affinity update failed for role "${subject.role}": ${describe(error)}`);
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3536
3558
|
const finalText = typeof result.result === "string" ? result.result : result.partial?.text;
|
|
3537
|
-
if (!
|
|
3559
|
+
if (!finalText) return;
|
|
3538
3560
|
try {
|
|
3539
|
-
const capture = captureLearnedFromAgentOutputDetailed(
|
|
3561
|
+
const capture = captureLearnedFromAgentOutputDetailed(
|
|
3562
|
+
finalText,
|
|
3563
|
+
subject.role,
|
|
3564
|
+
deps.projectRoot,
|
|
3565
|
+
false
|
|
3566
|
+
);
|
|
3540
3567
|
if (capture.captured > 0) {
|
|
3541
|
-
|
|
3568
|
+
const routed = capture.skills?.length ? ` (skills: ${capture.skills.join(", ")})` : "";
|
|
3569
|
+
logger?.debug(
|
|
3570
|
+
`agent learning captured ${capture.captured} item(s) for role "${subject.role}"${routed}`
|
|
3571
|
+
);
|
|
3572
|
+
try {
|
|
3573
|
+
onCaptured?.(subject.role);
|
|
3574
|
+
} catch {
|
|
3575
|
+
}
|
|
3542
3576
|
}
|
|
3543
3577
|
} catch (error) {
|
|
3544
|
-
|
|
3545
|
-
`agent learning capture failed for role "${role}": ${error instanceof Error ? error.message : String(error)}`
|
|
3546
|
-
);
|
|
3578
|
+
logger?.warn(`agent learning capture failed for role "${subject.role}": ${describe(error)}`);
|
|
3547
3579
|
}
|
|
3548
3580
|
}
|
|
3581
|
+
function describe(error) {
|
|
3582
|
+
return error instanceof Error ? error.message : String(error);
|
|
3583
|
+
}
|
|
3549
3584
|
|
|
3550
3585
|
// src/fleet/host-learning-tracker.ts
|
|
3551
3586
|
var HostLearningRoleTracker = class {
|
|
@@ -3555,11 +3590,17 @@ var HostLearningRoleTracker = class {
|
|
|
3555
3590
|
maxEntries;
|
|
3556
3591
|
roles = /* @__PURE__ */ new Map();
|
|
3557
3592
|
accessOrder = [];
|
|
3558
|
-
record(subagentId, role) {
|
|
3559
|
-
setBoundedLruEntry(
|
|
3593
|
+
record(subagentId, role, skills = []) {
|
|
3594
|
+
setBoundedLruEntry(
|
|
3595
|
+
this.roles,
|
|
3596
|
+
this.accessOrder,
|
|
3597
|
+
subagentId,
|
|
3598
|
+
{ role, skills: [...skills] },
|
|
3599
|
+
this.maxEntries
|
|
3600
|
+
);
|
|
3560
3601
|
}
|
|
3561
|
-
capture(result, deps) {
|
|
3562
|
-
captureCompletedTaskLearningForHost(result, deps, this.roles);
|
|
3602
|
+
capture(result, deps, onCaptured) {
|
|
3603
|
+
captureCompletedTaskLearningForHost(result, deps, this.roles, onCaptured);
|
|
3563
3604
|
}
|
|
3564
3605
|
};
|
|
3565
3606
|
|
|
@@ -3760,6 +3801,84 @@ function aggregateFleetUsage(completed) {
|
|
|
3760
3801
|
return { rows, totals };
|
|
3761
3802
|
}
|
|
3762
3803
|
|
|
3804
|
+
// src/fleet/host-provider.ts
|
|
3805
|
+
import { makeProviderFromConfig, withCatalogCapabilities } from "@wrongstack/providers";
|
|
3806
|
+
async function buildHostSubagentProvider(deps, config, overrideId, model) {
|
|
3807
|
+
const requestedProviderId = overrideId ?? config.provider;
|
|
3808
|
+
const providerId = requestedProviderId === config.provider || config.providers?.[requestedProviderId] !== void 0 || deps.providerRegistry.has(requestedProviderId) ? requestedProviderId : config.provider;
|
|
3809
|
+
const newCfg = config.providers?.[providerId] ?? {
|
|
3810
|
+
type: providerId,
|
|
3811
|
+
apiKey: config.apiKey,
|
|
3812
|
+
baseUrl: config.baseUrl
|
|
3813
|
+
};
|
|
3814
|
+
const cfgWithType = {
|
|
3815
|
+
...newCfg,
|
|
3816
|
+
type: providerId,
|
|
3817
|
+
...model ? { model } : {}
|
|
3818
|
+
};
|
|
3819
|
+
let provider = deps.providerRegistry.has(providerId) ? deps.providerRegistry.create(cfgWithType) : makeProviderFromConfig(providerId, cfgWithType);
|
|
3820
|
+
if (deps.modelsRegistry) {
|
|
3821
|
+
const resolvedModel = model ?? config.model;
|
|
3822
|
+
provider = await withCatalogCapabilities(deps.modelsRegistry, providerId, provider, {
|
|
3823
|
+
...cfgWithType,
|
|
3824
|
+
model: resolvedModel
|
|
3825
|
+
});
|
|
3826
|
+
await refreshRuntimeModelCatalog({
|
|
3827
|
+
modelsRegistry: deps.modelsRegistry,
|
|
3828
|
+
reason: `${providerId}/${resolvedModel}`
|
|
3829
|
+
});
|
|
3830
|
+
const mc = await resolveRuntimeMaxContext({
|
|
3831
|
+
modelsRegistry: deps.modelsRegistry,
|
|
3832
|
+
config,
|
|
3833
|
+
provider,
|
|
3834
|
+
runtimeProviderConfig: cfgWithType,
|
|
3835
|
+
providerId,
|
|
3836
|
+
modelId: resolvedModel
|
|
3837
|
+
});
|
|
3838
|
+
if (mc && mc > 0) provider.capabilities.maxContext = mc;
|
|
3839
|
+
}
|
|
3840
|
+
return provider;
|
|
3841
|
+
}
|
|
3842
|
+
async function resolveHostSubagentReasoningConfig(deps, providerId, modelId) {
|
|
3843
|
+
if (!deps.modelsRegistry) return void 0;
|
|
3844
|
+
try {
|
|
3845
|
+
return (await deps.modelsRegistry.getModel(providerId, modelId))?.capabilities.reasoningConfig;
|
|
3846
|
+
} catch {
|
|
3847
|
+
return void 0;
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
function resolveHostSubagentModelSelection(liveConfig, effectiveCfg, matrixTarget) {
|
|
3851
|
+
let effProvider = effectiveCfg.provider ?? matrixTarget?.provider ?? liveConfig.provider;
|
|
3852
|
+
let effModel = effectiveCfg.model ?? matrixTarget?.model ?? liveConfig.model;
|
|
3853
|
+
const modelPolicy = effectiveCfg.modelPolicy;
|
|
3854
|
+
const closedModelPolicy = modelPolicy?.strict === true;
|
|
3855
|
+
const allowedModels = modelPolicy?.allowed ?? [];
|
|
3856
|
+
if (modelPolicy && !allowedModels.some((target) => target.provider === effProvider && target.model === effModel)) {
|
|
3857
|
+
const firstAllowed = allowedModels[0];
|
|
3858
|
+
if (!firstAllowed) throw new Error(`Agent "${effectiveCfg.role}" has no allowed models.`);
|
|
3859
|
+
effProvider = firstAllowed.provider;
|
|
3860
|
+
effModel = firstAllowed.model;
|
|
3861
|
+
}
|
|
3862
|
+
const fallbackProfile = modelPolicy ? void 0 : effectiveCfg.fallbackProfile ?? matrixTarget?.fallbackProfile;
|
|
3863
|
+
const runtimeOverride = effectiveCfg.modelRuntime ?? matrixTarget?.modelRuntime;
|
|
3864
|
+
const startupTargets = modelPolicy ? [
|
|
3865
|
+
{ provider: effProvider, model: effModel },
|
|
3866
|
+
...modelPolicy.fallbacks ?? [],
|
|
3867
|
+
...closedModelPolicy || effProvider === liveConfig.provider && effModel === liveConfig.model ? [] : [{ provider: liveConfig.provider, model: liveConfig.model }]
|
|
3868
|
+
] : [
|
|
3869
|
+
{ provider: effProvider, model: effModel },
|
|
3870
|
+
...effProvider === liveConfig.provider && effModel === liveConfig.model ? [] : [{ provider: liveConfig.provider, model: liveConfig.model }]
|
|
3871
|
+
];
|
|
3872
|
+
return {
|
|
3873
|
+
effProvider,
|
|
3874
|
+
effModel,
|
|
3875
|
+
fallbackProfile,
|
|
3876
|
+
runtimeOverride,
|
|
3877
|
+
closedModelPolicy,
|
|
3878
|
+
startupTargets
|
|
3879
|
+
};
|
|
3880
|
+
}
|
|
3881
|
+
|
|
3763
3882
|
// src/fleet/host-subagent-factory.ts
|
|
3764
3883
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
3765
3884
|
import { existsSync, statSync } from "node:fs";
|
|
@@ -3795,44 +3914,91 @@ import { AutoApprovePermissionPolicy } from "@wrongstack/core/security";
|
|
|
3795
3914
|
|
|
3796
3915
|
// src/fleet/host-context.ts
|
|
3797
3916
|
import {
|
|
3917
|
+
loadProjectSkillAugmentation,
|
|
3798
3918
|
missingRequiredRuntimeTools,
|
|
3799
3919
|
missingRuntimeCapabilities,
|
|
3920
|
+
rankRoleSkills,
|
|
3921
|
+
recordSkillLoad,
|
|
3922
|
+
resolveRoleSkillCandidates,
|
|
3800
3923
|
runtimeToolReferencesFromText
|
|
3801
3924
|
} from "@wrongstack/core/agent-catalog";
|
|
3802
3925
|
import { TOKENS as TOKENS3 } from "@wrongstack/core/kernel";
|
|
3803
3926
|
import { getSageRetrieval } from "@wrongstack/sage";
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
const
|
|
3927
|
+
var EAGER_SKILL_LIMIT = 3;
|
|
3928
|
+
async function resolveHostSubagentSkillResolution(deps, roster, subCfg, availableToolNames = []) {
|
|
3929
|
+
const role = subCfg.role;
|
|
3930
|
+
const rosterEntry = role ? roster[role] : void 0;
|
|
3807
3931
|
const directContent = subCfg.skillContent?.trim();
|
|
3808
|
-
|
|
3932
|
+
const dropped = {};
|
|
3933
|
+
const pool = [
|
|
3934
|
+
...new Set(
|
|
3935
|
+
subCfg.skillNames ?? (role ? [
|
|
3936
|
+
...rosterEntry?.skillPool ?? rosterEntry?.skillNames ?? [],
|
|
3937
|
+
...resolveRoleSkillCandidates(role, deps.projectRoot)
|
|
3938
|
+
] : [])
|
|
3939
|
+
)
|
|
3940
|
+
];
|
|
3941
|
+
const skillNames = role ? rankRoleSkills(role, pool, deps.projectRoot, EAGER_SKILL_LIMIT) : pool.slice(0, EAGER_SKILL_LIMIT);
|
|
3942
|
+
if (skillNames.length === 0 || !deps.skillLoader) {
|
|
3943
|
+
return { content: directContent ?? "", selected: [], dropped };
|
|
3944
|
+
}
|
|
3809
3945
|
const resolved = [];
|
|
3946
|
+
const selected = [];
|
|
3810
3947
|
let usedChars = 0;
|
|
3811
3948
|
const maxChars = 16e3;
|
|
3812
3949
|
const maxCharsPerSkill = 4e3;
|
|
3813
3950
|
for (const skillName of skillNames) {
|
|
3814
3951
|
try {
|
|
3815
3952
|
const manifest = await deps.skillLoader.find(skillName);
|
|
3816
|
-
if (!manifest)
|
|
3817
|
-
|
|
3953
|
+
if (!manifest) {
|
|
3954
|
+
dropped[skillName] = "not-found";
|
|
3955
|
+
continue;
|
|
3956
|
+
}
|
|
3957
|
+
if (missingRuntimeCapabilities(manifest.requiredCapabilities, availableToolNames).length > 0) {
|
|
3958
|
+
dropped[skillName] = "missing-capability";
|
|
3959
|
+
continue;
|
|
3960
|
+
}
|
|
3961
|
+
if (missingRequiredRuntimeTools(manifest.requiredTools, availableToolNames).length > 0) {
|
|
3962
|
+
dropped[skillName] = "missing-tool";
|
|
3818
3963
|
continue;
|
|
3819
3964
|
}
|
|
3820
3965
|
const body = (await deps.skillLoader.readSaveBody(skillName)).trim();
|
|
3821
|
-
if (!body)
|
|
3966
|
+
if (!body) {
|
|
3967
|
+
dropped[skillName] = "empty";
|
|
3968
|
+
continue;
|
|
3969
|
+
}
|
|
3822
3970
|
if (missingRequiredRuntimeTools(runtimeToolReferencesFromText(body), availableToolNames).length > 0) {
|
|
3971
|
+
dropped[skillName] = "missing-tool";
|
|
3823
3972
|
continue;
|
|
3824
3973
|
}
|
|
3825
|
-
const
|
|
3826
|
-
|
|
3827
|
-
${
|
|
3974
|
+
const augmentation = role ? loadProjectSkillAugmentation(role, skillName, deps.projectRoot) : "";
|
|
3975
|
+
const entry = [
|
|
3976
|
+
`## Skill: ${skillName}`,
|
|
3977
|
+
"",
|
|
3978
|
+
body.slice(0, maxCharsPerSkill),
|
|
3979
|
+
...augmentation ? [
|
|
3980
|
+
"",
|
|
3981
|
+
`### Project practice for \`${skillName}\``,
|
|
3982
|
+
"",
|
|
3983
|
+
"Learned in this project. Where this differs from the general method above, follow this.",
|
|
3984
|
+
"",
|
|
3985
|
+
augmentation
|
|
3986
|
+
] : []
|
|
3987
|
+
].join("\n");
|
|
3828
3988
|
if (usedChars + entry.length > maxChars) {
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
);
|
|
3832
|
-
break;
|
|
3989
|
+
dropped[skillName] = "budget";
|
|
3990
|
+
continue;
|
|
3833
3991
|
}
|
|
3834
3992
|
resolved.push(entry);
|
|
3993
|
+
selected.push(skillName);
|
|
3835
3994
|
usedChars += entry.length;
|
|
3995
|
+
} catch {
|
|
3996
|
+
dropped[skillName] = "not-found";
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
if (role && selected.length > 0) {
|
|
4000
|
+
try {
|
|
4001
|
+
recordSkillLoad(role, selected, deps.projectRoot);
|
|
3836
4002
|
} catch {
|
|
3837
4003
|
}
|
|
3838
4004
|
}
|
|
@@ -3844,7 +4010,7 @@ Apply these skills first for this assignment.
|
|
|
3844
4010
|
|
|
3845
4011
|
${resolved.join("\n\n---\n\n")}` : void 0
|
|
3846
4012
|
].filter((section) => Boolean(section));
|
|
3847
|
-
return sections.join("\n\n");
|
|
4013
|
+
return { content: sections.join("\n\n"), selected, dropped };
|
|
3848
4014
|
}
|
|
3849
4015
|
async function retrieveHostSubagentMemory(deps, getLeaderMode, subCfg, taskContext) {
|
|
3850
4016
|
const memoryPort = deps.container.safeResolve(TOKENS3.MemoryStore);
|
|
@@ -4043,84 +4209,6 @@ function installSubagentEventBridge(opts) {
|
|
|
4043
4209
|
};
|
|
4044
4210
|
}
|
|
4045
4211
|
|
|
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
4212
|
// src/fleet/host-session-writer.ts
|
|
4125
4213
|
function createParentSubagentSessionWriter(parentSession) {
|
|
4126
4214
|
return {
|
|
@@ -4269,17 +4357,26 @@ ${message} Falling back to the assigned checkout.` : `${message} Falling back to
|
|
|
4269
4357
|
${audienceMemory.map((text) => `- ${text}`).join("\n")}`
|
|
4270
4358
|
});
|
|
4271
4359
|
}
|
|
4272
|
-
const
|
|
4360
|
+
const skillResolution = await resolveHostSubagentSkillResolution(
|
|
4273
4361
|
host.deps,
|
|
4274
4362
|
host.roster,
|
|
4275
4363
|
effectiveCfg,
|
|
4276
4364
|
subagentTools.map((tool) => tool.name)
|
|
4277
4365
|
);
|
|
4278
|
-
if (
|
|
4366
|
+
if (skillResolution.content) {
|
|
4279
4367
|
for (let index = baseSystem.length - 1; index >= 0; index--) {
|
|
4280
4368
|
if (baseSystem[index]?.text.includes("# Active Skills")) baseSystem.splice(index, 1);
|
|
4281
4369
|
}
|
|
4282
|
-
baseSystem.push({ type: "text", text:
|
|
4370
|
+
baseSystem.push({ type: "text", text: skillResolution.content });
|
|
4371
|
+
}
|
|
4372
|
+
const droppedSkills = Object.entries(skillResolution.dropped);
|
|
4373
|
+
if (droppedSkills.length > 0) {
|
|
4374
|
+
host.deps.events.emit("subagent.skills.dropped", {
|
|
4375
|
+
sessionId: host.deps.session.id,
|
|
4376
|
+
role: effectiveCfg.role,
|
|
4377
|
+
selected: skillResolution.selected,
|
|
4378
|
+
dropped: Object.fromEntries(droppedSkills)
|
|
4379
|
+
});
|
|
4283
4380
|
}
|
|
4284
4381
|
const rawRolePrompt = effectiveCfg.systemPromptOverride ?? effectiveCfg.prompt ?? (effectiveCfg.role ? host.roster[effectiveCfg.role]?.prompt : void 0);
|
|
4285
4382
|
const rolePrompt = rawRolePrompt && effectiveCfg.role ? buildProjectContextualizedPrompt(rawRolePrompt, effectiveCfg.role, projectRoot, {
|
|
@@ -4290,7 +4387,7 @@ ${audienceMemory.map((text) => `- ${text}`).join("\n")}`
|
|
|
4290
4387
|
}
|
|
4291
4388
|
const subagentName = effectiveCfg.id ?? effectiveCfg.name ?? `sub_${randomUUID2().slice(0, 8)}`;
|
|
4292
4389
|
if (effectiveCfg.role) {
|
|
4293
|
-
host.recordLearningRole(subagentName, effectiveCfg.role);
|
|
4390
|
+
host.recordLearningRole(subagentName, effectiveCfg.role, skillResolution.selected);
|
|
4294
4391
|
}
|
|
4295
4392
|
let subSession;
|
|
4296
4393
|
if (host.sessionFactory) {
|
|
@@ -4347,7 +4444,8 @@ ${audienceMemory.map((text) => `- ${text}`).join("\n")}`
|
|
|
4347
4444
|
iterationTimeoutMs: config.tools?.iterationTimeoutMs ?? 12e4,
|
|
4348
4445
|
maxToolTimeoutMs: config.tools?.maxToolTimeoutMs ?? 3e5,
|
|
4349
4446
|
perIterationOutputCapBytes: config.tools?.perIterationOutputCapBytes ?? 1e5,
|
|
4350
|
-
tracer: void 0
|
|
4447
|
+
tracer: void 0,
|
|
4448
|
+
requireKanbanGovernance: true
|
|
4351
4449
|
});
|
|
4352
4450
|
const subagentConfigStore = host.deps.configStore;
|
|
4353
4451
|
const pipelines = createDefaultPipelines();
|
|
@@ -4623,6 +4721,9 @@ var MultiAgentHost = class {
|
|
|
4623
4721
|
* Bounded to 20 entries with LRU eviction to prevent unbounded memory growth. */
|
|
4624
4722
|
acpRunnerCache = new HostAcpRunnerCache();
|
|
4625
4723
|
learningRoles = new HostLearningRoleTracker();
|
|
4724
|
+
/** Background distillation of captured learning into per-skill addenda. */
|
|
4725
|
+
learningOptimizer = null;
|
|
4726
|
+
learningSwept = false;
|
|
4626
4727
|
/** Adaptive concurrency controller — created in buildDirector() when config has
|
|
4627
4728
|
* adaptiveConcurrency.enabled = true. Monitors FleetBus for 429 errors and
|
|
4628
4729
|
* automatically adjusts maxConcurrent to prevent rate limiting. */
|
|
@@ -4688,6 +4789,7 @@ var MultiAgentHost = class {
|
|
|
4688
4789
|
async buildDirector() {
|
|
4689
4790
|
if (this.director) return;
|
|
4690
4791
|
const config = this.deps.configStore.get();
|
|
4792
|
+
this.sweepLearningOptimization();
|
|
4691
4793
|
const fleetManager = createHostFleetManager(this.opts);
|
|
4692
4794
|
this.fleetManager = fleetManager;
|
|
4693
4795
|
if (this.opts.sessionsRoot && !this.sessionFactory) {
|
|
@@ -4959,7 +5061,7 @@ var MultiAgentHost = class {
|
|
|
4959
5061
|
sessionFactory: this.sessionFactory,
|
|
4960
5062
|
filterTools: (allow) => this.filterTools(allow),
|
|
4961
5063
|
mailboxProjectDir: () => this.mailboxProjectDir(),
|
|
4962
|
-
recordLearningRole: (subagentId, role) => this.recordLearningRole(subagentId, role),
|
|
5064
|
+
recordLearningRole: (subagentId, role, skills) => this.recordLearningRole(subagentId, role, skills),
|
|
4963
5065
|
subagentToolRegistry: (allow) => this.subagentToolRegistry(allow)
|
|
4964
5066
|
});
|
|
4965
5067
|
}
|
|
@@ -4982,6 +5084,7 @@ var MultiAgentHost = class {
|
|
|
4982
5084
|
await this.ensureCoordinator(config);
|
|
4983
5085
|
const coordinator = this.getCoordinator();
|
|
4984
5086
|
const acpRunner = await this.buildACPRunner(subagentId);
|
|
5087
|
+
this.recordLearningRole(subagentId, subagentId);
|
|
4985
5088
|
coordinator.setRunner(acpRunner);
|
|
4986
5089
|
this.directorRunnerSet = true;
|
|
4987
5090
|
await coordinator.spawn({
|
|
@@ -5184,11 +5287,75 @@ var MultiAgentHost = class {
|
|
|
5184
5287
|
emitLifecycleCompleted(taskId, result) {
|
|
5185
5288
|
emitHostLifecycleCompleted(this.deps.events, this.deps.session.id, taskId, result);
|
|
5186
5289
|
}
|
|
5187
|
-
recordLearningRole(subagentId, role) {
|
|
5188
|
-
this.learningRoles.record(subagentId, role);
|
|
5290
|
+
recordLearningRole(subagentId, role, skills = []) {
|
|
5291
|
+
this.learningRoles.record(subagentId, role, skills);
|
|
5189
5292
|
}
|
|
5190
5293
|
captureCompletedTaskLearning(result) {
|
|
5191
|
-
this.learningRoles.capture(
|
|
5294
|
+
this.learningRoles.capture(
|
|
5295
|
+
result,
|
|
5296
|
+
this.deps,
|
|
5297
|
+
(role) => this.getLearningOptimizer()?.notifyCaptured(role)
|
|
5298
|
+
);
|
|
5299
|
+
}
|
|
5300
|
+
/**
|
|
5301
|
+
* Lazily built so a session that never spawns a subagent pays nothing, and
|
|
5302
|
+
* so the policy is read from the live config rather than frozen at boot.
|
|
5303
|
+
* Returns null when auto-optimization is switched off.
|
|
5304
|
+
*/
|
|
5305
|
+
getLearningOptimizer() {
|
|
5306
|
+
const settings = this.deps.configStore.get().fleet?.learning?.autoOptimize;
|
|
5307
|
+
if (settings?.enabled === false) return null;
|
|
5308
|
+
if (!this.learningOptimizer) {
|
|
5309
|
+
this.learningOptimizer = new LearningOptimizationScheduler({
|
|
5310
|
+
projectRoot: this.deps.projectRoot,
|
|
5311
|
+
getPolicy: () => resolveAutoOptimizePolicy(this.deps.configStore.get().fleet?.learning?.autoOptimize),
|
|
5312
|
+
getLlm: () => this.resolveOptimizerLlm(),
|
|
5313
|
+
onEvent: (event) => {
|
|
5314
|
+
this.deps.events.emit("agent.learning.optimized", {
|
|
5315
|
+
sessionId: this.deps.session.id,
|
|
5316
|
+
role: event.role,
|
|
5317
|
+
trigger: event.trigger,
|
|
5318
|
+
status: event.result?.status ?? "failed",
|
|
5319
|
+
skills: event.result?.skills ?? [],
|
|
5320
|
+
...event.error ? { error: event.error } : {}
|
|
5321
|
+
});
|
|
5322
|
+
}
|
|
5323
|
+
});
|
|
5324
|
+
}
|
|
5325
|
+
return this.learningOptimizer;
|
|
5326
|
+
}
|
|
5327
|
+
/**
|
|
5328
|
+
* Resolve a model for the distillation pass. Uses the `memory-curator` slot
|
|
5329
|
+
* of the model matrix when one is configured — curating learned knowledge is
|
|
5330
|
+
* exactly that role's job — and the session default otherwise. A failure
|
|
5331
|
+
* here is not fatal: the pass still writes the deterministic skill addenda.
|
|
5332
|
+
*/
|
|
5333
|
+
async resolveOptimizerLlm() {
|
|
5334
|
+
try {
|
|
5335
|
+
const config = this.deps.configStore.get();
|
|
5336
|
+
const target = resolveSubagentModelTarget2(config, "memory-curator");
|
|
5337
|
+
const providerId = target?.provider ?? config.provider;
|
|
5338
|
+
const model = target?.model ?? config.model;
|
|
5339
|
+
if (!providerId || !model) return void 0;
|
|
5340
|
+
const provider = await buildHostSubagentProvider(this.deps, config, providerId, model);
|
|
5341
|
+
return { provider, model };
|
|
5342
|
+
} catch {
|
|
5343
|
+
return void 0;
|
|
5344
|
+
}
|
|
5345
|
+
}
|
|
5346
|
+
/**
|
|
5347
|
+
* Evaluate every role with learning data once per session. Without it, a
|
|
5348
|
+
* role that became eligible before this session would wait for its next
|
|
5349
|
+
* capture — which for a rarely-used role can be never.
|
|
5350
|
+
*/
|
|
5351
|
+
sweepLearningOptimization() {
|
|
5352
|
+
if (this.learningSwept) return;
|
|
5353
|
+
this.learningSwept = true;
|
|
5354
|
+
if (this.deps.configStore.get().fleet?.learning?.autoOptimize?.sweepOnStart === false) return;
|
|
5355
|
+
try {
|
|
5356
|
+
this.getLearningOptimizer()?.sweep(listProjectAgentRoles(this.deps.projectRoot));
|
|
5357
|
+
} catch {
|
|
5358
|
+
}
|
|
5192
5359
|
}
|
|
5193
5360
|
status() {
|
|
5194
5361
|
return buildFleetHostStatus({
|
|
@@ -5285,6 +5452,8 @@ var MultiAgentHost = class {
|
|
|
5285
5452
|
* Safe to call multiple times — subsequent calls are no-ops.
|
|
5286
5453
|
*/
|
|
5287
5454
|
async dispose() {
|
|
5455
|
+
this.learningOptimizer?.dispose();
|
|
5456
|
+
this.learningOptimizer = null;
|
|
5288
5457
|
this.clearShadowAgent();
|
|
5289
5458
|
for (const off of this.shadowActivityOffHandles) {
|
|
5290
5459
|
off();
|
|
@@ -7580,7 +7749,8 @@ function createAgent(params) {
|
|
|
7580
7749
|
perIterationOutputCapBytes: params.config.tools.perIterationOutputCapBytes,
|
|
7581
7750
|
tracer: params.tracer,
|
|
7582
7751
|
logger,
|
|
7583
|
-
hookRunner: params.hookRunner
|
|
7752
|
+
hookRunner: params.hookRunner,
|
|
7753
|
+
requireKanbanGovernance: true
|
|
7584
7754
|
};
|
|
7585
7755
|
const toolExecutor = new ToolExecutor2(params.tools, toolExecutorOptions);
|
|
7586
7756
|
void bootstrapMailboxBridgeAtStartup({
|
|
@@ -10412,17 +10582,21 @@ function stripQuotes(s) {
|
|
|
10412
10582
|
|
|
10413
10583
|
// src/slash-commands/agent-improve.ts
|
|
10414
10584
|
import {
|
|
10415
|
-
|
|
10416
|
-
captureLearnedFromAgentOutput,
|
|
10585
|
+
captureLearnedFromAgentOutputDetailed as captureLearnedFromAgentOutputDetailed2,
|
|
10417
10586
|
getProjectAgentLearnStats,
|
|
10418
|
-
|
|
10587
|
+
listProjectSkillAugmentations,
|
|
10419
10588
|
loadConsolidationMetadata,
|
|
10420
10589
|
loadProjectAgentConfig as loadProjectAgentConfig2,
|
|
10421
10590
|
loadProjectAgentIdentity,
|
|
10422
10591
|
loadProjectAgentLearned,
|
|
10592
|
+
loadProjectSkillAugmentation as loadProjectSkillAugmentation2,
|
|
10423
10593
|
loadRoleKnowledgeManifest,
|
|
10594
|
+
loadSkillAffinity,
|
|
10595
|
+
optimizeProjectAgentLearning,
|
|
10424
10596
|
refreshProjectAgentIdentity,
|
|
10425
10597
|
resetProjectAgentIdentity,
|
|
10598
|
+
resolveRoleSkillCandidates as resolveRoleSkillCandidates2,
|
|
10599
|
+
setSkillPinned,
|
|
10426
10600
|
updateProjectAgentIdentity
|
|
10427
10601
|
} from "@wrongstack/core/agent-catalog";
|
|
10428
10602
|
import { color as color11 } from "@wrongstack/core/utils";
|
|
@@ -10440,7 +10614,7 @@ function buildAgentImproveCommand(opts) {
|
|
|
10440
10614
|
return {
|
|
10441
10615
|
name: "agent-improve",
|
|
10442
10616
|
category: "Config",
|
|
10443
|
-
description: "
|
|
10617
|
+
description: "Inspect and develop a roster agent for this project: learned directives, per-skill project addenda and the optimization pass.",
|
|
10444
10618
|
help: [
|
|
10445
10619
|
"Usage:",
|
|
10446
10620
|
" /agent-improve [role] Show customization for a role (or all roles)",
|
|
@@ -10448,20 +10622,26 @@ function buildAgentImproveCommand(opts) {
|
|
|
10448
10622
|
" /agent-improve [role] update Update identity + learned content",
|
|
10449
10623
|
" /agent-improve [role] refresh Reset identity + learned to empty templates",
|
|
10450
10624
|
" /agent-improve [role] capture Scan last output for ## LEARNED blocks and persist them",
|
|
10451
|
-
" /agent-improve [role]
|
|
10625
|
+
" /agent-improve [role] optimize Distil learning into skill addenda + a consolidated doc",
|
|
10626
|
+
" /agent-improve [role] skills Show the role's skills, affinity and project addenda",
|
|
10627
|
+
" /agent-improve [role] skills <skill> Show one skill's project addendum",
|
|
10628
|
+
" /agent-improve [role] skills <skill> pin|unpin Always/never keep this skill loaded",
|
|
10452
10629
|
" /agent-improve [role] reset Delete ALL custom files for this role",
|
|
10453
10630
|
" /agent-improve * reset Delete ALL custom files for every role",
|
|
10454
10631
|
"",
|
|
10455
10632
|
"Use without arguments to see which roles have project-customizations.",
|
|
10456
10633
|
"",
|
|
10457
|
-
"
|
|
10458
|
-
" Agents
|
|
10459
|
-
"
|
|
10460
|
-
|
|
10461
|
-
|
|
10462
|
-
|
|
10634
|
+
"Learning loop (runs on its own):",
|
|
10635
|
+
" Agents end a run with a ## LEARNED block, optionally tagged with the",
|
|
10636
|
+
" skill it refines: `## LEARNED [skill: testing]`. The runtime captures it,",
|
|
10637
|
+
" routes it to that skill, and a background pass distils it into that skill\u2019s",
|
|
10638
|
+
" project addendum \u2014 injected beneath the bundled skill body on the next spawn.",
|
|
10639
|
+
' Capture also runs on failed tasks. Use "capture" to re-scan the last turn',
|
|
10640
|
+
' by hand and "optimize" to force a distillation early.',
|
|
10463
10641
|
"",
|
|
10464
|
-
"Files live under: .wrongstack/agents/<role>/"
|
|
10642
|
+
"Files live under: .wrongstack/agents/<role>/ (committed; skills/affinity.json",
|
|
10643
|
+
"and archive/ stay local). Disable the automatic pass with",
|
|
10644
|
+
"fleet.learning.autoOptimize.enabled = false."
|
|
10465
10645
|
].join("\n"),
|
|
10466
10646
|
async run(args) {
|
|
10467
10647
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
@@ -10500,7 +10680,11 @@ ${lines.join("\n")}`;
|
|
|
10500
10680
|
const idText = loadProjectAgentIdentity(role, projectRoot);
|
|
10501
10681
|
const learned = loadProjectAgentLearned(role, projectRoot);
|
|
10502
10682
|
const kn = loadRoleKnowledgeManifest(role, projectRoot);
|
|
10683
|
+
const developedSkills = listProjectSkillAugmentations(role, projectRoot);
|
|
10503
10684
|
const lines = [`${color11.bold(role)} project identity:`];
|
|
10685
|
+
if (developedSkills.length > 0) {
|
|
10686
|
+
lines.push(` skills developed: ${developedSkills.join(", ")}`);
|
|
10687
|
+
}
|
|
10504
10688
|
if (cfg) lines.push(` config: ${JSON.stringify(cfg)}`);
|
|
10505
10689
|
if (kn)
|
|
10506
10690
|
lines.push(
|
|
@@ -10535,33 +10719,108 @@ Or edit files directly under .wrongstack/agents/${role}/`
|
|
|
10535
10719
|
case "capture": {
|
|
10536
10720
|
const storedOutput = opts.context?.meta["lastAgentOutput"];
|
|
10537
10721
|
const recentOutput = typeof storedOutput === "string" ? storedOutput : "";
|
|
10538
|
-
|
|
10539
|
-
|
|
10540
|
-
|
|
10722
|
+
if (!recentOutput) {
|
|
10723
|
+
return {
|
|
10724
|
+
message: "No agent output recorded yet in this session. Run a turn first, then re-run capture."
|
|
10725
|
+
};
|
|
10726
|
+
}
|
|
10727
|
+
const result = captureLearnedFromAgentOutputDetailed2(
|
|
10728
|
+
recentOutput,
|
|
10729
|
+
role,
|
|
10730
|
+
projectRoot,
|
|
10731
|
+
true
|
|
10732
|
+
);
|
|
10733
|
+
if (result.captured > 0) {
|
|
10734
|
+
const routed = result.skills?.length ? ` Routed to skill(s): ${result.skills.join(", ")}.` : "";
|
|
10735
|
+
const msg = `Captured ${result.captured} learned item(s) for role "${role}".${routed} Use /agent-improve ${role} to see them.`;
|
|
10541
10736
|
opts.renderer.write(msg);
|
|
10542
10737
|
return { message: msg };
|
|
10543
10738
|
}
|
|
10544
10739
|
return {
|
|
10545
|
-
message: `No ## LEARNED blocks found in recent output for role "${role}".`
|
|
10740
|
+
message: result.reason ?? `No ## LEARNED blocks found in recent output for role "${role}" (status: ${result.status}).`
|
|
10546
10741
|
};
|
|
10547
10742
|
}
|
|
10743
|
+
case "optimize":
|
|
10548
10744
|
case "consolidate": {
|
|
10549
10745
|
const stats = getProjectAgentLearnStats(role, projectRoot);
|
|
10550
10746
|
if (stats.entryCount === 0) {
|
|
10551
|
-
const msg = `No raw learned entries for role "${role}" to
|
|
10747
|
+
const msg = `No raw learned entries for role "${role}" to optimize.`;
|
|
10552
10748
|
opts.renderer.write(msg);
|
|
10553
10749
|
return { message: msg };
|
|
10554
10750
|
}
|
|
10555
|
-
const
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
const
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
};
|
|
10751
|
+
const before = loadConsolidationMetadata(role, projectRoot);
|
|
10752
|
+
opts.renderer.write(
|
|
10753
|
+
`${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"
|
|
10754
|
+
);
|
|
10755
|
+
const llm = opts.llmProvider && opts.llmModel ? { provider: opts.llmProvider, model: opts.llmModel } : void 0;
|
|
10756
|
+
const result = await optimizeProjectAgentLearning(role, projectRoot, {
|
|
10757
|
+
...llm ? { llm } : {},
|
|
10758
|
+
trigger: "manual"
|
|
10759
|
+
});
|
|
10760
|
+
const skillNote = result.skills.length ? ` Skill addenda refreshed: ${result.skills.join(", ")}.` : "";
|
|
10761
|
+
switch (result.status) {
|
|
10762
|
+
case "optimized": {
|
|
10763
|
+
const after = loadConsolidationMetadata(role, projectRoot);
|
|
10764
|
+
const msg = `Optimized "${role}": ${result.rawEntryCount} directive(s) \u2192 ${after?.consolidatedBytes ?? 0}B consolidated.` + skillNote + (result.pruned ? " Raw buffer archived and reset." : "");
|
|
10765
|
+
opts.renderer.write(msg);
|
|
10766
|
+
return { message: msg };
|
|
10767
|
+
}
|
|
10768
|
+
case "empty-synthesis":
|
|
10769
|
+
return {
|
|
10770
|
+
message: `Model "${result.model}" returned an empty document; the existing knowledge for "${role}" was left untouched.${skillNote}`
|
|
10771
|
+
};
|
|
10772
|
+
case "failed":
|
|
10773
|
+
return { message: `Optimization failed for "${role}": ${result.error}${skillNote}` };
|
|
10774
|
+
case "no-llm":
|
|
10775
|
+
return {
|
|
10776
|
+
message: `No active model; routing the "${role}" consolidation through the agent.${skillNote}`,
|
|
10777
|
+
runText: `${result.instruction}
|
|
10778
|
+
|
|
10779
|
+
---
|
|
10780
|
+
|
|
10781
|
+
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.`
|
|
10782
|
+
};
|
|
10783
|
+
default:
|
|
10784
|
+
return { message: `Nothing to optimize for "${role}".` };
|
|
10785
|
+
}
|
|
10786
|
+
}
|
|
10787
|
+
case "skills": {
|
|
10788
|
+
const candidates = resolveRoleSkillCandidates2(role, projectRoot);
|
|
10789
|
+
const developed = listProjectSkillAugmentations(role, projectRoot);
|
|
10790
|
+
const affinity = loadSkillAffinity(role, projectRoot);
|
|
10791
|
+
const target = parts[2];
|
|
10792
|
+
if (target && parts[3]) {
|
|
10793
|
+
const skillAction = parts[3];
|
|
10794
|
+
if (skillAction !== "pin" && skillAction !== "unpin") {
|
|
10795
|
+
return { message: `Unknown skill action "${skillAction}". Use pin or unpin.` };
|
|
10796
|
+
}
|
|
10797
|
+
setSkillPinned(role, target, skillAction === "pin", projectRoot);
|
|
10798
|
+
const msg2 = `${skillAction === "pin" ? "Pinned" : "Unpinned"} skill "${target}" for role "${role}".`;
|
|
10799
|
+
opts.renderer.write(msg2);
|
|
10800
|
+
return { message: msg2 };
|
|
10801
|
+
}
|
|
10802
|
+
if (target) {
|
|
10803
|
+
const body = loadProjectSkillAugmentation2(role, target, projectRoot);
|
|
10804
|
+
const msg2 = body ? `${color11.bold(`${role} \xB7 ${target}`)}
|
|
10805
|
+
|
|
10806
|
+
${body}` : `No project addendum for skill "${target}" on role "${role}" yet.`;
|
|
10807
|
+
opts.renderer.write(msg2);
|
|
10808
|
+
return { message: msg2 };
|
|
10809
|
+
}
|
|
10810
|
+
const skillLines = candidates.map((skill) => {
|
|
10811
|
+
const entry = affinity.entries[skill];
|
|
10812
|
+
const marks = [
|
|
10813
|
+
developed.includes(skill) ? "developed" : "",
|
|
10814
|
+
entry?.pinned ? "pinned" : "",
|
|
10815
|
+
entry?.learned ? `${entry.learned} learned` : "",
|
|
10816
|
+
entry ? `${entry.succeeded}\u2713/${entry.failed}\u2717 of ${entry.loaded} loads` : "unused"
|
|
10817
|
+
].filter(Boolean);
|
|
10818
|
+
return ` ${color11.cyan(skill.padEnd(22))} ${marks.join(" \xB7 ")}`;
|
|
10819
|
+
});
|
|
10820
|
+
const msg = candidates.length ? `Skills for ${color11.bold(role)}:
|
|
10821
|
+
${skillLines.join("\n")}` : `Role "${role}" has no curated skills.`;
|
|
10822
|
+
opts.renderer.write(msg);
|
|
10823
|
+
return { message: msg };
|
|
10565
10824
|
}
|
|
10566
10825
|
case "reset": {
|
|
10567
10826
|
const removed = resetProjectAgentIdentity(role === "*" ? void 0 : role, projectRoot);
|
|
@@ -10577,7 +10836,7 @@ Or edit files directly under .wrongstack/agents/${role}/`
|
|
|
10577
10836
|
}
|
|
10578
10837
|
default:
|
|
10579
10838
|
return {
|
|
10580
|
-
message: `Unknown action "${action}". Use: show, update, refresh, capture,
|
|
10839
|
+
message: `Unknown action "${action}". Use: show, update, refresh, capture, optimize, skills, reset, or reset-all.`
|
|
10581
10840
|
};
|
|
10582
10841
|
}
|
|
10583
10842
|
}
|
|
@@ -11122,7 +11381,7 @@ function buildBrainCommand(opts) {
|
|
|
11122
11381
|
const trimmed = args.trim();
|
|
11123
11382
|
const [sub, ...rest] = trimmed.split(/\s+/);
|
|
11124
11383
|
const subcommand = (sub ?? "").toLowerCase();
|
|
11125
|
-
const applyPatch = async (patch,
|
|
11384
|
+
const applyPatch = async (patch, describe2) => {
|
|
11126
11385
|
if (!opts.brainRuntime) {
|
|
11127
11386
|
const msg2 = "The Brain runtime is not available in this session.";
|
|
11128
11387
|
opts.renderer.writeWarning(msg2);
|
|
@@ -11132,7 +11391,7 @@ function buildBrainCommand(opts) {
|
|
|
11132
11391
|
const { snapshot, persisted } = opts.brainRuntime.apply(patch);
|
|
11133
11392
|
const result = await persisted;
|
|
11134
11393
|
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 = `${
|
|
11394
|
+
const msg2 = `${describe2(snapshot)}${note}`;
|
|
11136
11395
|
if (result.ok) opts.renderer.write(msg2);
|
|
11137
11396
|
else opts.renderer.writeWarning(msg2);
|
|
11138
11397
|
return { message: msg2 };
|
|
@@ -11941,6 +12200,7 @@ function buildBtwCommand(opts) {
|
|
|
11941
12200
|
}
|
|
11942
12201
|
|
|
11943
12202
|
// src/slash-commands/clear.ts
|
|
12203
|
+
import { resetCaptureWindows } from "@wrongstack/core/agent-catalog";
|
|
11944
12204
|
import { createContextEvidenceState } from "@wrongstack/core/utils";
|
|
11945
12205
|
|
|
11946
12206
|
// src/slash-commands/interrupt.ts
|
|
@@ -12040,6 +12300,7 @@ function buildClearCommand(opts) {
|
|
|
12040
12300
|
if (opts.sessionStore) {
|
|
12041
12301
|
await opts.sessionStore.clearHistory(ctx?.session.id ?? "");
|
|
12042
12302
|
}
|
|
12303
|
+
resetCaptureWindows();
|
|
12043
12304
|
opts.onClear?.();
|
|
12044
12305
|
await opts.onNewSession?.();
|
|
12045
12306
|
opts.renderer.clear();
|
|
@@ -17196,6 +17457,8 @@ function stripQuotes2(value) {
|
|
|
17196
17457
|
import { color as color31 } from "@wrongstack/core/utils";
|
|
17197
17458
|
import {
|
|
17198
17459
|
areDependenciesMet,
|
|
17460
|
+
evaluateContractGraph,
|
|
17461
|
+
evaluateContractGraphReadiness,
|
|
17199
17462
|
findBlockedTasks
|
|
17200
17463
|
} from "@wrongstack/kanban";
|
|
17201
17464
|
var HEADING = (s) => color31.bold(s);
|
|
@@ -17351,6 +17614,19 @@ function formatTaskDetail(board, task) {
|
|
|
17351
17614
|
if (metric.notes) lines.push(` ${DIM(metric.notes)}`);
|
|
17352
17615
|
}
|
|
17353
17616
|
}
|
|
17617
|
+
const readiness = evaluateContractGraphReadiness(board, task.id);
|
|
17618
|
+
const completion = evaluateContractGraph(board, task.id);
|
|
17619
|
+
lines.push("");
|
|
17620
|
+
lines.push(HEADING(" Contract Map:"));
|
|
17621
|
+
lines.push(
|
|
17622
|
+
` ${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`)}`
|
|
17623
|
+
);
|
|
17624
|
+
for (const issue of readiness.issues.slice(0, 5)) {
|
|
17625
|
+
lines.push(` ${color31.yellow("!")} ${issue.message}`);
|
|
17626
|
+
}
|
|
17627
|
+
if (readiness.issues.length > 5) {
|
|
17628
|
+
lines.push(` ${DIM(`+${readiness.issues.length - 5} more setup gaps`)}`);
|
|
17629
|
+
}
|
|
17354
17630
|
if (task.estimatedHours || task.actualHours) {
|
|
17355
17631
|
lines.push("");
|
|
17356
17632
|
lines.push(
|
|
@@ -21940,6 +22216,7 @@ import {
|
|
|
21940
22216
|
setPlanItemStatus
|
|
21941
22217
|
} from "@wrongstack/core/storage";
|
|
21942
22218
|
import { formatTaskList, formatTodosList } from "@wrongstack/core/utils";
|
|
22219
|
+
import { todoTool } from "@wrongstack/tools";
|
|
21943
22220
|
function findPlanItemIndex(plan, query) {
|
|
21944
22221
|
const asNum = Number.parseInt(query, 10);
|
|
21945
22222
|
if (!Number.isNaN(asNum) && asNum >= 1 && asNum <= plan.items.length) return asNum - 1;
|
|
@@ -22043,6 +22320,7 @@ ${formatTaskList(taskFile.tasks)}`,
|
|
|
22043
22320
|
let outputMessage = "";
|
|
22044
22321
|
let outputError;
|
|
22045
22322
|
let outputExtra = {};
|
|
22323
|
+
let todosToReplace = null;
|
|
22046
22324
|
const finalPlan = await mutatePlan(planPath, sessionId, async (plan) => {
|
|
22047
22325
|
switch (verb) {
|
|
22048
22326
|
case "add": {
|
|
@@ -22087,6 +22365,18 @@ ${formatPlan(updated)}`;
|
|
|
22087
22365
|
outputError = { code: "usage", message: outputMessage };
|
|
22088
22366
|
return plan;
|
|
22089
22367
|
}
|
|
22368
|
+
const itemIdx = findPlanItemIndex(plan, restJoined);
|
|
22369
|
+
const item = itemIdx >= 0 ? plan.items[itemIdx] : void 0;
|
|
22370
|
+
if (!item) {
|
|
22371
|
+
outputMessage = `No plan item matched "${restJoined}".`;
|
|
22372
|
+
outputError = { code: "item_not_found", message: outputMessage };
|
|
22373
|
+
return plan;
|
|
22374
|
+
}
|
|
22375
|
+
if (item.status !== "done") {
|
|
22376
|
+
outputMessage = `Plan item "${item.title}" is unfinished and cannot be removed. Complete it first.`;
|
|
22377
|
+
outputError = { code: "unfinished_item", message: outputMessage };
|
|
22378
|
+
return plan;
|
|
22379
|
+
}
|
|
22090
22380
|
const updated = removePlanItem(plan, restJoined);
|
|
22091
22381
|
outputMessage = formatPlan(updated);
|
|
22092
22382
|
return updated;
|
|
@@ -22114,7 +22404,7 @@ ${formatPlan(updated)}`;
|
|
|
22114
22404
|
outputError = { code: "item_not_found", message: outputMessage };
|
|
22115
22405
|
return plan;
|
|
22116
22406
|
}
|
|
22117
|
-
|
|
22407
|
+
todosToReplace = derived.todos;
|
|
22118
22408
|
const label = verb === "derive" ? "Derived" : "Promoted to";
|
|
22119
22409
|
outputMessage = `${label} ${derived.todos.length} todo(s):
|
|
22120
22410
|
${formatTodosList(derived.todos)}
|
|
@@ -22152,6 +22442,12 @@ ${formatPlan(updated)}`;
|
|
|
22152
22442
|
return plan;
|
|
22153
22443
|
}
|
|
22154
22444
|
case "clear": {
|
|
22445
|
+
const unfinished = plan.items.filter((item) => item.status !== "done");
|
|
22446
|
+
if (unfinished.length > 0) {
|
|
22447
|
+
outputMessage = `Plan contains unfinished items and cannot be cleared: ${unfinished.map((item) => item.id).join(", ")}.`;
|
|
22448
|
+
outputError = { code: "unfinished_items", message: outputMessage };
|
|
22449
|
+
return plan;
|
|
22450
|
+
}
|
|
22155
22451
|
const updated = clearPlan(plan);
|
|
22156
22452
|
outputMessage = "Plan cleared.";
|
|
22157
22453
|
return updated;
|
|
@@ -22162,6 +22458,11 @@ ${formatPlan(updated)}`;
|
|
|
22162
22458
|
return plan;
|
|
22163
22459
|
}
|
|
22164
22460
|
});
|
|
22461
|
+
if (todosToReplace && opts.context) {
|
|
22462
|
+
await todoTool.execute({ todos: todosToReplace }, opts.context, {
|
|
22463
|
+
signal: AbortSignal.timeout(3e4)
|
|
22464
|
+
});
|
|
22465
|
+
}
|
|
22165
22466
|
const payload = outputError ? { ok: false, plan: finalPlan, error: outputError } : { ok: true, plan: finalPlan, ...outputExtra };
|
|
22166
22467
|
return result(outputMessage, payload, json);
|
|
22167
22468
|
}
|
|
@@ -25287,6 +25588,134 @@ function buildWebuiCommand() {
|
|
|
25287
25588
|
};
|
|
25288
25589
|
}
|
|
25289
25590
|
|
|
25591
|
+
// src/slash-commands/theme.ts
|
|
25592
|
+
import { color as color46, writeOut as writeOut3 } from "@wrongstack/core/utils";
|
|
25593
|
+
var THEME_OPTIONS = [
|
|
25594
|
+
{ id: "catppuccin", name: "Catppuccin Mocha", desc: "Soft pastel dark theme (Default)" },
|
|
25595
|
+
{ id: "tokyo-night", name: "Tokyo Night", desc: "Deep violet, neon orange & cyan" },
|
|
25596
|
+
{ id: "nord", name: "Nord", desc: "Cool arctic blue & slate pastels" },
|
|
25597
|
+
{ id: "cyberpunk", name: "Cyberpunk Neon", desc: "High-contrast magenta, cyan & yellow" },
|
|
25598
|
+
{ id: "dracula", name: "Dracula", desc: "Vibrant purple, pink & green" },
|
|
25599
|
+
{ id: "gruvbox-dark", name: "Gruvbox Dark", desc: "Warm earthy retro \u2014 orange, olive & aqua" },
|
|
25600
|
+
{ id: "solarized-dark", name: "Solarized Dark", desc: "Base16 classic \u2014 teal & ochre precision" },
|
|
25601
|
+
{ id: "one-dark", name: "One Dark", desc: "Atom's iconic palette \u2014 blue-led, warm accents" },
|
|
25602
|
+
{ id: "monokai", name: "Monokai", desc: "Sublime classic \u2014 vivid magenta, cyan & lime" },
|
|
25603
|
+
{ id: "rose-pine", name: "Ros\xE9 Pine", desc: "Aesthetic pine & foam \u2014 soft evening pastels" },
|
|
25604
|
+
{ id: "kanagawa", name: "Kanagawa", desc: "Hokusai-inspired waves \u2014 sumi ink on washi" },
|
|
25605
|
+
{ id: "ayu-dark", name: "Ayu Dark", desc: "Simple pleasant dark \u2014 warm orange + cool blue" },
|
|
25606
|
+
{ id: "everforest", name: "Everforest", desc: "Green-based comfort \u2014 forest greens & warm tans" },
|
|
25607
|
+
{ id: "night-owl", name: "Night Owl", desc: "Sarah Drasner's night \u2014 deep navy + bold accents" },
|
|
25608
|
+
{ id: "synthwave", name: "Synthwave '84", desc: "Hot pink + neon cyan on deep purple" }
|
|
25609
|
+
];
|
|
25610
|
+
async function runThemePicker(reader, activeId) {
|
|
25611
|
+
let cursor = THEME_OPTIONS.findIndex((p) => p.id === activeId);
|
|
25612
|
+
if (cursor < 0) cursor = 0;
|
|
25613
|
+
const render = (currentCursor) => {
|
|
25614
|
+
const lines = [];
|
|
25615
|
+
lines.push(`
|
|
25616
|
+
${color46.bold(color46.amber("WrongStack") + color46.dim(" \u2014 TUI Theme Selection"))}
|
|
25617
|
+
`);
|
|
25618
|
+
lines.push(color46.dim(" \u2191\u2193 navigate Enter select q quit\n"));
|
|
25619
|
+
lines.push("");
|
|
25620
|
+
for (const [i, p] of THEME_OPTIONS.entries()) {
|
|
25621
|
+
const mark = p.id === activeId ? color46.green(" [active]") : "";
|
|
25622
|
+
const prefix = i === currentCursor ? color46.bold("\u276F ") : " ";
|
|
25623
|
+
const name = i === currentCursor ? color46.bold(p.name) : p.name;
|
|
25624
|
+
lines.push(` ${prefix}${name.padEnd(18)} ${color46.dim(p.desc)}${mark}`);
|
|
25625
|
+
}
|
|
25626
|
+
lines.push("");
|
|
25627
|
+
return lines.join("\n");
|
|
25628
|
+
};
|
|
25629
|
+
writeOut3(render(cursor));
|
|
25630
|
+
const options = [
|
|
25631
|
+
{ key: "\x1B[A", label: "\u2191", value: "up" },
|
|
25632
|
+
{ key: "\x1B[B", label: "\u2193", value: "down" },
|
|
25633
|
+
{ key: "\r", label: "Enter", value: "enter" },
|
|
25634
|
+
{ key: "q", label: "q", value: "quit" }
|
|
25635
|
+
];
|
|
25636
|
+
while (true) {
|
|
25637
|
+
const answer = await reader.readKey("", options);
|
|
25638
|
+
if (answer === "quit") return void 0;
|
|
25639
|
+
if (answer === "enter") return THEME_OPTIONS[cursor]?.id ?? THEME_OPTIONS[0].id;
|
|
25640
|
+
if (answer === "up") {
|
|
25641
|
+
cursor = cursor > 0 ? cursor - 1 : THEME_OPTIONS.length - 1;
|
|
25642
|
+
} else if (answer === "down") {
|
|
25643
|
+
cursor = cursor < THEME_OPTIONS.length - 1 ? cursor + 1 : 0;
|
|
25644
|
+
}
|
|
25645
|
+
writeOut3("\x1B[J");
|
|
25646
|
+
writeOut3(render(cursor));
|
|
25647
|
+
}
|
|
25648
|
+
}
|
|
25649
|
+
function buildThemeCommand(opts) {
|
|
25650
|
+
return {
|
|
25651
|
+
name: "theme",
|
|
25652
|
+
category: "Config",
|
|
25653
|
+
description: "Switch or select the TUI color theme preset interactively",
|
|
25654
|
+
argsHint: "[catppuccin | tokyo-night | nord | cyberpunk | dracula | gruvbox-dark | solarized-dark | one-dark | monokai | rose-pine | kanagawa | ayu-dark | everforest | night-owl | synthwave]",
|
|
25655
|
+
help: [
|
|
25656
|
+
"Usage:",
|
|
25657
|
+
" /theme Interactive menu selection or view available theme presets",
|
|
25658
|
+
" /theme <preset> Switch directly to a theme preset",
|
|
25659
|
+
"",
|
|
25660
|
+
"Available presets:",
|
|
25661
|
+
" catppuccin, tokyo-night, nord, cyberpunk, dracula,",
|
|
25662
|
+
" gruvbox-dark, solarized-dark, one-dark, monokai, rose-pine,",
|
|
25663
|
+
" kanagawa, ayu-dark, everforest, night-owl, synthwave"
|
|
25664
|
+
].join("\n"),
|
|
25665
|
+
async run(args) {
|
|
25666
|
+
const validPresets = THEME_OPTIONS.map((t) => t.id);
|
|
25667
|
+
const currentConfig = opts.configStore?.get();
|
|
25668
|
+
const activePreset = currentConfig?.themePreset ?? "catppuccin";
|
|
25669
|
+
if (!args.trim()) {
|
|
25670
|
+
if (opts.inputReader) {
|
|
25671
|
+
const selected = await runThemePicker(opts.inputReader, activePreset);
|
|
25672
|
+
if (!selected) {
|
|
25673
|
+
return { message: "Theme selection cancelled." };
|
|
25674
|
+
}
|
|
25675
|
+
if (opts.configStore) {
|
|
25676
|
+
try {
|
|
25677
|
+
opts.configStore.update({ themePreset: selected });
|
|
25678
|
+
} catch {
|
|
25679
|
+
}
|
|
25680
|
+
}
|
|
25681
|
+
return {
|
|
25682
|
+
message: color46.green(`Switched TUI theme preset to "${selected}" (saved to config).`),
|
|
25683
|
+
metadata: { themePreset: selected }
|
|
25684
|
+
};
|
|
25685
|
+
}
|
|
25686
|
+
const lines = [
|
|
25687
|
+
color46.bold(`Current TUI theme: ${activePreset}`),
|
|
25688
|
+
"",
|
|
25689
|
+
color46.bold("Available Theme Presets:")
|
|
25690
|
+
];
|
|
25691
|
+
for (const t of THEME_OPTIONS) {
|
|
25692
|
+
const mark = t.id === activePreset ? color46.green(" [active]") : "";
|
|
25693
|
+
lines.push(` \u2022 ${color46.bold(t.id.padEnd(14))} ${t.desc}${mark}`);
|
|
25694
|
+
}
|
|
25695
|
+
lines.push("");
|
|
25696
|
+
lines.push(color46.dim("Usage: /theme <preset>"));
|
|
25697
|
+
return { message: lines.join("\n") };
|
|
25698
|
+
}
|
|
25699
|
+
const preset = args.trim().toLowerCase();
|
|
25700
|
+
if (!validPresets.includes(preset)) {
|
|
25701
|
+
return {
|
|
25702
|
+
message: `Unknown theme preset "${preset}". Available options: ${validPresets.join(", ")}`
|
|
25703
|
+
};
|
|
25704
|
+
}
|
|
25705
|
+
if (opts.configStore) {
|
|
25706
|
+
try {
|
|
25707
|
+
opts.configStore.update({ themePreset: preset });
|
|
25708
|
+
} catch {
|
|
25709
|
+
}
|
|
25710
|
+
}
|
|
25711
|
+
return {
|
|
25712
|
+
message: color46.green(`Switched TUI theme preset to "${preset}" (saved to config).`),
|
|
25713
|
+
metadata: { themePreset: preset }
|
|
25714
|
+
};
|
|
25715
|
+
}
|
|
25716
|
+
};
|
|
25717
|
+
}
|
|
25718
|
+
|
|
25290
25719
|
// src/slash-commands/agents.ts
|
|
25291
25720
|
import { noOpVault as noOpVault7 } from "@wrongstack/core/security";
|
|
25292
25721
|
function formatAgentLine(a) {
|
|
@@ -25528,7 +25957,7 @@ ${lines.join("\n")}` };
|
|
|
25528
25957
|
// src/slash-commands/hq.ts
|
|
25529
25958
|
import { readHqRuntimeFileSync, resolveHqConfig, resolveHqDataDir } from "@wrongstack/core/hq";
|
|
25530
25959
|
import { noOpVault as noOpVault8 } from "@wrongstack/core/security";
|
|
25531
|
-
import { color as
|
|
25960
|
+
import { color as color47 } from "@wrongstack/core/utils";
|
|
25532
25961
|
function maskToken(t) {
|
|
25533
25962
|
if (t.length <= 10) return `${t.slice(0, 2)}\u2026(${t.length})`;
|
|
25534
25963
|
return `${t.slice(0, 6)}\u2026${t.slice(-4)} (${t.length} chars)`;
|
|
@@ -25539,12 +25968,12 @@ async function probeHq(url) {
|
|
|
25539
25968
|
const timer = setTimeout(() => ctrl.abort(), 2500);
|
|
25540
25969
|
const res = await fetch(url, { signal: ctrl.signal, redirect: "manual" }).catch(() => null);
|
|
25541
25970
|
clearTimeout(timer);
|
|
25542
|
-
if (!res) return
|
|
25543
|
-
if (res.ok) return
|
|
25544
|
-
if (res.status === 401) return
|
|
25545
|
-
return
|
|
25971
|
+
if (!res) return color47.red("unreachable");
|
|
25972
|
+
if (res.ok) return color47.green("reachable");
|
|
25973
|
+
if (res.status === 401) return color47.green("reachable") + color47.dim(" (token required)");
|
|
25974
|
+
return color47.amber(`reachable (HTTP ${res.status})`);
|
|
25546
25975
|
} catch {
|
|
25547
|
-
return
|
|
25976
|
+
return color47.red("unreachable");
|
|
25548
25977
|
}
|
|
25549
25978
|
}
|
|
25550
25979
|
function buildHqCommand(opts) {
|
|
@@ -25578,7 +26007,7 @@ function buildHqCommand(opts) {
|
|
|
25578
26007
|
const { cmd, rest } = parseSubcommand(args);
|
|
25579
26008
|
const sub = cmd;
|
|
25580
26009
|
if (!opts.configStore || !opts.paths) {
|
|
25581
|
-
return { message: `${
|
|
26010
|
+
return { message: `${color47.red("\u2717")} HQ config is unavailable in this surface.` };
|
|
25582
26011
|
}
|
|
25583
26012
|
const persistDeps = {
|
|
25584
26013
|
configStore: opts.configStore,
|
|
@@ -25592,14 +26021,14 @@ function buildHqCommand(opts) {
|
|
|
25592
26021
|
const url = (rest[0] ?? "").trim();
|
|
25593
26022
|
const token = rest.slice(1).join(" ").trim();
|
|
25594
26023
|
if (!url) {
|
|
25595
|
-
return { message: `${
|
|
26024
|
+
return { message: `${color47.amber("Usage:")} /hq set <http://host:3499> [client-token]` };
|
|
25596
26025
|
}
|
|
25597
26026
|
try {
|
|
25598
26027
|
const parsed = new URL(url);
|
|
25599
26028
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error("proto");
|
|
25600
26029
|
} catch {
|
|
25601
26030
|
return {
|
|
25602
|
-
message: `${
|
|
26031
|
+
message: `${color47.red("Invalid URL:")} ${url} ${color47.dim("(expected http://host:3499)")}`
|
|
25603
26032
|
};
|
|
25604
26033
|
}
|
|
25605
26034
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
@@ -25611,16 +26040,16 @@ function buildHqCommand(opts) {
|
|
|
25611
26040
|
});
|
|
25612
26041
|
const reach = await probeHq(url);
|
|
25613
26042
|
const tokLine = token ? `
|
|
25614
|
-
token: ${
|
|
26043
|
+
token: ${color47.dim(maskToken(token))}` : "";
|
|
25615
26044
|
return {
|
|
25616
|
-
message: `${
|
|
26045
|
+
message: `${color47.green("\u2713")} HQ set \u2192 ${color47.cyan(url)}${tokLine}
|
|
25617
26046
|
status: ${reach}
|
|
25618
|
-
${
|
|
26047
|
+
${color47.dim("Connects on the next session start.")}`
|
|
25619
26048
|
};
|
|
25620
26049
|
}
|
|
25621
26050
|
if (sub === "token") {
|
|
25622
26051
|
const token = rest.join(" ").trim();
|
|
25623
|
-
if (!token) return { message: `${
|
|
26052
|
+
if (!token) return { message: `${color47.amber("Usage:")} /hq token <client-token>` };
|
|
25624
26053
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
25625
26054
|
const hq = cfg.hq ?? {};
|
|
25626
26055
|
hq.token = token;
|
|
@@ -25628,14 +26057,14 @@ function buildHqCommand(opts) {
|
|
|
25628
26057
|
cfg.hq = hq;
|
|
25629
26058
|
});
|
|
25630
26059
|
return {
|
|
25631
|
-
message: `${
|
|
26060
|
+
message: `${color47.green("\u2713")} HQ client token saved ${color47.dim(maskToken(token))}`
|
|
25632
26061
|
};
|
|
25633
26062
|
}
|
|
25634
26063
|
if (sub === "raw") {
|
|
25635
26064
|
const mode = (rest[0] ?? "").trim().toLowerCase();
|
|
25636
26065
|
if (mode !== "on" && mode !== "off") {
|
|
25637
26066
|
return {
|
|
25638
|
-
message: `${
|
|
26067
|
+
message: `${color47.amber("Usage:")} /hq raw on|off ${color47.dim("(publish raw chat/tool content to HQ)")}`
|
|
25639
26068
|
};
|
|
25640
26069
|
}
|
|
25641
26070
|
const on = mode === "on";
|
|
@@ -25645,8 +26074,8 @@ function buildHqCommand(opts) {
|
|
|
25645
26074
|
cfg.hq = hq;
|
|
25646
26075
|
});
|
|
25647
26076
|
return {
|
|
25648
|
-
message: `${
|
|
25649
|
-
${
|
|
26077
|
+
message: `${color47.green("\u2713")} HQ raw content \u2192 ${on ? color47.amber("on (unredacted)") : color47.dim("off (redacted)")}
|
|
26078
|
+
${color47.dim("Applies to sessions started after this change. Only enable for HQ servers you trust.")}`
|
|
25650
26079
|
};
|
|
25651
26080
|
}
|
|
25652
26081
|
if (sub === "on" || sub === "off") {
|
|
@@ -25657,29 +26086,29 @@ function buildHqCommand(opts) {
|
|
|
25657
26086
|
cfg.hq = hq;
|
|
25658
26087
|
});
|
|
25659
26088
|
return {
|
|
25660
|
-
message: `${
|
|
26089
|
+
message: `${color47.green("\u2713")} HQ publishing \u2192 ${on ? color47.cyan("on") : color47.dim("off")}`
|
|
25661
26090
|
};
|
|
25662
26091
|
}
|
|
25663
26092
|
if (sub === "clear") {
|
|
25664
26093
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
25665
26094
|
delete cfg.hq;
|
|
25666
26095
|
});
|
|
25667
|
-
return { message: `${
|
|
26096
|
+
return { message: `${color47.green("\u2713")} HQ configuration cleared.` };
|
|
25668
26097
|
}
|
|
25669
26098
|
if (sub === "" || sub === "status") {
|
|
25670
26099
|
const dataDir = resolveHqDataDir(currentHq?.dataDir);
|
|
25671
26100
|
const runtime = readHqRuntimeFileSync(dataDir);
|
|
25672
26101
|
const resolved = resolveHqConfig({ config: currentHq });
|
|
25673
|
-
const lines = [`${
|
|
26102
|
+
const lines = [`${color47.bold("\u{1F4CB} WrongStack HQ \u2014 connection")}`, ""];
|
|
25674
26103
|
if (!resolved) {
|
|
25675
26104
|
lines.push(
|
|
25676
|
-
` ${
|
|
26105
|
+
` ${color47.dim("Not configured.")} Use ${color47.cyan("/hq set <url> [token]")} to connect,`
|
|
25677
26106
|
);
|
|
25678
|
-
lines.push(` or run ${
|
|
26107
|
+
lines.push(` or run ${color47.cyan("wstack --hq")} locally (auto-discovered).`);
|
|
25679
26108
|
if (runtime) {
|
|
25680
26109
|
lines.push("");
|
|
25681
26110
|
lines.push(
|
|
25682
|
-
` ${
|
|
26111
|
+
` ${color47.green("A local HQ is running")} at ${color47.cyan(runtime.url)} ${color47.dim("(start a new session to attach)")}`
|
|
25683
26112
|
);
|
|
25684
26113
|
}
|
|
25685
26114
|
const message = lines.join("\n");
|
|
@@ -25687,35 +26116,35 @@ function buildHqCommand(opts) {
|
|
|
25687
26116
|
}
|
|
25688
26117
|
if (resolved.discover && !runtime) {
|
|
25689
26118
|
lines.push(
|
|
25690
|
-
` mode: ${
|
|
26119
|
+
` mode: ${color47.cyan("auto-discovery")} ${color47.dim("(no local HQ running yet)")}`
|
|
25691
26120
|
);
|
|
25692
26121
|
lines.push(
|
|
25693
|
-
` ${
|
|
26122
|
+
` ${color47.dim("This session will attach automatically when `wstack --hq` starts")}`
|
|
25694
26123
|
);
|
|
25695
|
-
lines.push(` ${
|
|
25696
|
-
lines.push(` ${
|
|
26124
|
+
lines.push(` ${color47.dim(`on this machine (watching ${dataDir}).`)}`);
|
|
26125
|
+
lines.push(` ${color47.dim("Disable with WRONGSTACK_HQ_ENABLED=0 or /hq off.")}`);
|
|
25697
26126
|
return { message: lines.join("\n") };
|
|
25698
26127
|
}
|
|
25699
26128
|
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: ${
|
|
26129
|
+
lines.push(` url: ${color47.cyan(resolved.url)}`);
|
|
25701
26130
|
lines.push(
|
|
25702
|
-
` enabled: ${resolved.enabled === false ?
|
|
26131
|
+
` enabled: ${resolved.enabled === false ? color47.dim("false") : color47.green("true")}`
|
|
25703
26132
|
);
|
|
25704
|
-
if (resolved.discover) lines.push(` mode: ${
|
|
25705
|
-
lines.push(` source: ${
|
|
26133
|
+
if (resolved.discover) lines.push(` mode: ${color47.cyan("auto-discovery")}`);
|
|
26134
|
+
lines.push(` source: ${color47.dim(source)}`);
|
|
25706
26135
|
lines.push(
|
|
25707
|
-
` token: ${resolved.token ?
|
|
26136
|
+
` token: ${resolved.token ? color47.dim(maskToken(resolved.token)) : color47.dim("none (open mode)")}`
|
|
25708
26137
|
);
|
|
25709
26138
|
lines.push(
|
|
25710
|
-
` content: ${resolved.rawContent === true ?
|
|
26139
|
+
` content: ${resolved.rawContent === true ? color47.amber("raw (unredacted)") : color47.dim("redacted \u2014 explicitly disabled; enable with /hq raw on")}`
|
|
25711
26140
|
);
|
|
25712
|
-
if (resolved.projectAlias) lines.push(` alias: ${
|
|
26141
|
+
if (resolved.projectAlias) lines.push(` alias: ${color47.cyan(resolved.projectAlias)}`);
|
|
25713
26142
|
lines.push(` status: ${await probeHq(resolved.url)}`);
|
|
25714
26143
|
return { message: lines.join("\n") };
|
|
25715
26144
|
}
|
|
25716
26145
|
return {
|
|
25717
|
-
message: `${
|
|
25718
|
-
${
|
|
26146
|
+
message: `${color47.red("Unknown subcommand:")} ${sub}
|
|
26147
|
+
${color47.dim("Try /hq, /hq set <url> [token], /hq raw on|off, /hq on|off, /hq clear, or /help hq")}`
|
|
25719
26148
|
};
|
|
25720
26149
|
}
|
|
25721
26150
|
};
|
|
@@ -25762,7 +26191,7 @@ import * as fs17 from "node:fs/promises";
|
|
|
25762
26191
|
import { createRequire } from "node:module";
|
|
25763
26192
|
import * as path26 from "node:path";
|
|
25764
26193
|
import {
|
|
25765
|
-
color as
|
|
26194
|
+
color as color48,
|
|
25766
26195
|
ensureProjectGitignore,
|
|
25767
26196
|
ensureProjectIdentity as ensureProjectIdentity2,
|
|
25768
26197
|
projectIdentityPath,
|
|
@@ -25895,17 +26324,17 @@ async function projectIdentityCommand(opts, ctx, action, confirmed) {
|
|
|
25895
26324
|
if (action === "show") {
|
|
25896
26325
|
const identity = await readProjectIdentity(root);
|
|
25897
26326
|
return identity ? {
|
|
25898
|
-
message: `${
|
|
25899
|
-
${
|
|
25900
|
-
} : { message: `No committed project identity. Run ${
|
|
26327
|
+
message: `${color48.bold("Project ID:")} ${color48.cyan(identity.projectId)}
|
|
26328
|
+
${color48.dim(filePath)}`
|
|
26329
|
+
} : { message: `No committed project identity. Run ${color48.cyan("/project init")}.` };
|
|
25901
26330
|
}
|
|
25902
26331
|
if (action === "init") {
|
|
25903
26332
|
const result2 = await ensureProjectIdentity2(root);
|
|
25904
26333
|
await ensureProjectGitignore(root);
|
|
25905
26334
|
return {
|
|
25906
|
-
message: result2.created ? `${
|
|
25907
|
-
${
|
|
25908
|
-
${
|
|
26335
|
+
message: result2.created ? `${color48.green("Created")} ${filePath}
|
|
26336
|
+
${color48.cyan(result2.identity.projectId)}
|
|
26337
|
+
${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
26338
|
};
|
|
25910
26339
|
}
|
|
25911
26340
|
if (!confirmed && opts.confirm) {
|
|
@@ -25920,17 +26349,17 @@ ${color47.dim("Commit this file so every clone and machine shares the same HQ pr
|
|
|
25920
26349
|
}
|
|
25921
26350
|
if (!confirmed) {
|
|
25922
26351
|
return {
|
|
25923
|
-
message: `Rekey requires confirmation. Pass ${
|
|
26352
|
+
message: `Rekey requires confirmation. Pass ${color48.cyan("--yes")} or ${color48.cyan("-y")}.`
|
|
25924
26353
|
};
|
|
25925
26354
|
}
|
|
25926
26355
|
const result = await rekeyProjectIdentity(root);
|
|
25927
26356
|
await ensureProjectGitignore(root);
|
|
25928
26357
|
return {
|
|
25929
26358
|
message: [
|
|
25930
|
-
result.previous ?
|
|
25931
|
-
`${
|
|
25932
|
-
|
|
25933
|
-
|
|
26359
|
+
result.previous ? color48.dim(`Previous: ${result.previous.projectId}`) : color48.dim("Previous: none"),
|
|
26360
|
+
`${color48.green("New project ID:")} ${color48.cyan(result.identity.projectId)}`,
|
|
26361
|
+
color48.dim(`Commit ${filePath} to make this fork independent on every machine.`),
|
|
26362
|
+
color48.dim("Restart WrongStack before publishing HQ or Kanban updates under the new ID.")
|
|
25934
26363
|
].join("\n")
|
|
25935
26364
|
};
|
|
25936
26365
|
}
|
|
@@ -25939,7 +26368,7 @@ async function listProjectsCommand(opts, ctx) {
|
|
|
25939
26368
|
const currentRoot = ctx?.projectRoot;
|
|
25940
26369
|
if (manifest.projects.length === 0) {
|
|
25941
26370
|
return {
|
|
25942
|
-
message:
|
|
26371
|
+
message: color48.dim("No projects registered. Add one: /project add <path> [name]")
|
|
25943
26372
|
};
|
|
25944
26373
|
}
|
|
25945
26374
|
const sorted = [...manifest.projects].sort((a, b) => {
|
|
@@ -25951,19 +26380,19 @@ async function listProjectsCommand(opts, ctx) {
|
|
|
25951
26380
|
const lines = [`Projects (${sorted.length}) registered in projects.json:`, ""];
|
|
25952
26381
|
for (const p of sorted) {
|
|
25953
26382
|
const isCurrent = p.root === currentRoot;
|
|
25954
|
-
const marker = isCurrent ?
|
|
25955
|
-
const name = isCurrent ?
|
|
25956
|
-
const slug =
|
|
25957
|
-
const last =
|
|
26383
|
+
const marker = isCurrent ? color48.green("\u25CF") : color48.dim("\u25CB");
|
|
26384
|
+
const name = isCurrent ? color48.bold(p.name) : p.name;
|
|
26385
|
+
const slug = color48.dim(`[${p.slug}]`);
|
|
26386
|
+
const last = color48.dim(fmtLastSeen(p.lastSeen));
|
|
25958
26387
|
lines.push(` ${marker} ${name} ${slug} ${last}`);
|
|
25959
26388
|
lines.push(` ${p.root}`);
|
|
25960
26389
|
if (isCurrent) {
|
|
25961
|
-
lines.push(` ${
|
|
26390
|
+
lines.push(` ${color48.green("\u2190 active session")}`);
|
|
25962
26391
|
}
|
|
25963
26392
|
lines.push("");
|
|
25964
26393
|
}
|
|
25965
26394
|
lines.push(
|
|
25966
|
-
|
|
26395
|
+
color48.dim(
|
|
25967
26396
|
"Commands: add <path> [name] | rename <slug> <name> | remove <slug> | switch [dir] (no args = picker)"
|
|
25968
26397
|
)
|
|
25969
26398
|
);
|
|
@@ -25974,17 +26403,17 @@ async function addProjectCommand(opts, ctx, targetPath, displayName) {
|
|
|
25974
26403
|
try {
|
|
25975
26404
|
await fs17.access(resolved);
|
|
25976
26405
|
} catch {
|
|
25977
|
-
return { message:
|
|
26406
|
+
return { message: color48.red(`Directory not found: ${resolved}`) };
|
|
25978
26407
|
}
|
|
25979
26408
|
const stat5 = await fs17.stat(resolved);
|
|
25980
26409
|
if (!stat5.isDirectory()) {
|
|
25981
|
-
return { message:
|
|
26410
|
+
return { message: color48.red(`Not a directory: ${resolved}`) };
|
|
25982
26411
|
}
|
|
25983
26412
|
const manifest = await loadManifest(opts.paths?.globalConfig);
|
|
25984
26413
|
const existing = manifest.projects.find((p) => p.root === resolved);
|
|
25985
26414
|
if (existing) {
|
|
25986
26415
|
return {
|
|
25987
|
-
message:
|
|
26416
|
+
message: color48.yellow(`Project already registered: "${existing.name}" (${existing.slug})`)
|
|
25988
26417
|
};
|
|
25989
26418
|
}
|
|
25990
26419
|
const name = displayName?.trim() || path26.basename(resolved);
|
|
@@ -25996,9 +26425,9 @@ async function addProjectCommand(opts, ctx, targetPath, displayName) {
|
|
|
25996
26425
|
return {
|
|
25997
26426
|
message: [
|
|
25998
26427
|
"",
|
|
25999
|
-
|
|
26000
|
-
|
|
26001
|
-
|
|
26428
|
+
color48.green(` Added project: ${name}`),
|
|
26429
|
+
color48.dim(` Root: ${resolved}`),
|
|
26430
|
+
color48.dim(` Slug: ${slug}`),
|
|
26002
26431
|
""
|
|
26003
26432
|
].join("\n")
|
|
26004
26433
|
};
|
|
@@ -26008,7 +26437,7 @@ async function renameProjectCommand(opts, _ctx, slugOrName, newName) {
|
|
|
26008
26437
|
const project = findProject(manifest, slugOrName);
|
|
26009
26438
|
if (!project) {
|
|
26010
26439
|
return {
|
|
26011
|
-
message:
|
|
26440
|
+
message: color48.red(
|
|
26012
26441
|
`Project not found: "${slugOrName}". Use /project list to see available projects.`
|
|
26013
26442
|
)
|
|
26014
26443
|
};
|
|
@@ -26016,7 +26445,7 @@ async function renameProjectCommand(opts, _ctx, slugOrName, newName) {
|
|
|
26016
26445
|
const oldName = project.name;
|
|
26017
26446
|
project.name = newName;
|
|
26018
26447
|
await saveManifest(manifest, opts.paths?.globalConfig);
|
|
26019
|
-
return { message:
|
|
26448
|
+
return { message: color48.green(`Renamed: "${oldName}" \u2192 "${newName}" (${project.slug})`) };
|
|
26020
26449
|
}
|
|
26021
26450
|
async function removeProjectCommand(opts, _ctx, slugOrName) {
|
|
26022
26451
|
const manifest = await loadManifest(opts.paths?.globalConfig);
|
|
@@ -26025,7 +26454,7 @@ async function removeProjectCommand(opts, _ctx, slugOrName) {
|
|
|
26025
26454
|
);
|
|
26026
26455
|
if (idx === -1) {
|
|
26027
26456
|
return {
|
|
26028
|
-
message:
|
|
26457
|
+
message: color48.red(
|
|
26029
26458
|
`Project not found: "${slugOrName}". Use /project list to see available projects.`
|
|
26030
26459
|
)
|
|
26031
26460
|
};
|
|
@@ -26034,7 +26463,7 @@ async function removeProjectCommand(opts, _ctx, slugOrName) {
|
|
|
26034
26463
|
manifest.projects.splice(idx, 1);
|
|
26035
26464
|
await saveManifest(manifest, opts.paths?.globalConfig);
|
|
26036
26465
|
return {
|
|
26037
|
-
message:
|
|
26466
|
+
message: color48.dim(
|
|
26038
26467
|
`Removed: "${removed.name}" (${removed.root}) \u2014 data directory kept at ~/.wrongstack/projects/${removed.slug}/`
|
|
26039
26468
|
)
|
|
26040
26469
|
};
|
|
@@ -26044,11 +26473,11 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
|
|
|
26044
26473
|
try {
|
|
26045
26474
|
await fs17.access(resolved);
|
|
26046
26475
|
} catch {
|
|
26047
|
-
return { message:
|
|
26476
|
+
return { message: color48.red(`Directory not found: ${resolved}`) };
|
|
26048
26477
|
}
|
|
26049
26478
|
const stat5 = await fs17.stat(resolved);
|
|
26050
26479
|
if (!stat5.isDirectory()) {
|
|
26051
|
-
return { message:
|
|
26480
|
+
return { message: color48.red(`Not a directory: ${resolved}`) };
|
|
26052
26481
|
}
|
|
26053
26482
|
let cliPath;
|
|
26054
26483
|
try {
|
|
@@ -26061,7 +26490,7 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
|
|
|
26061
26490
|
cliPath = process.argv[1] ?? "";
|
|
26062
26491
|
if (!cliPath) {
|
|
26063
26492
|
return {
|
|
26064
|
-
message:
|
|
26493
|
+
message: color48.red(
|
|
26065
26494
|
"Could not locate the CLI entry point. Run `wstack` manually in the target directory."
|
|
26066
26495
|
)
|
|
26067
26496
|
};
|
|
@@ -26094,14 +26523,14 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
|
|
|
26094
26523
|
// and boot/tui-project-spawn.ts documents this exact bug being removed.
|
|
26095
26524
|
});
|
|
26096
26525
|
child.on("error", (err) => {
|
|
26097
|
-
console.error(
|
|
26526
|
+
console.error(color48.red(`Failed to spawn wstack: ${err.message}`));
|
|
26098
26527
|
});
|
|
26099
26528
|
child.unref();
|
|
26100
26529
|
return {
|
|
26101
26530
|
message: [
|
|
26102
26531
|
"",
|
|
26103
|
-
|
|
26104
|
-
|
|
26532
|
+
color48.green(` Spawning wstack in ${resolved} ...`),
|
|
26533
|
+
color48.dim(" (current session stays open \u2014 Ctrl+C to return)"),
|
|
26105
26534
|
""
|
|
26106
26535
|
].join("\n")
|
|
26107
26536
|
};
|
|
@@ -26115,45 +26544,45 @@ async function confirmProjectSwitch(opts, targetName) {
|
|
|
26115
26544
|
const parallelActive = parallelEngine?.currentState === "running";
|
|
26116
26545
|
const hasActiveAgents = fleetRunning > 0 || eternalActive || parallelActive;
|
|
26117
26546
|
if (!hasActiveAgents) return true;
|
|
26118
|
-
const parts = [
|
|
26547
|
+
const parts = [color48.yellow(`\u26A0 Switching projects will stop all running agents.`), ""];
|
|
26119
26548
|
if (fleetRunning > 0) {
|
|
26120
|
-
parts.push(
|
|
26549
|
+
parts.push(color48.dim(` \u2022 ${fleetRunning} subagent(s) currently running`));
|
|
26121
26550
|
}
|
|
26122
26551
|
if (eternalActive) {
|
|
26123
|
-
parts.push(
|
|
26552
|
+
parts.push(color48.dim(" \u2022 Eternal engine is active"));
|
|
26124
26553
|
}
|
|
26125
26554
|
if (parallelActive) {
|
|
26126
|
-
parts.push(
|
|
26555
|
+
parts.push(color48.dim(" \u2022 Parallel engine is active"));
|
|
26127
26556
|
}
|
|
26128
26557
|
parts.push("");
|
|
26129
|
-
parts.push(
|
|
26558
|
+
parts.push(color48.dim(` Target: ${targetName}`));
|
|
26130
26559
|
opts.renderer.write(`
|
|
26131
26560
|
${parts.join("\n")}
|
|
26132
26561
|
`);
|
|
26133
26562
|
if (!opts.confirm) return true;
|
|
26134
26563
|
const confirmed = await opts.confirm(
|
|
26135
|
-
|
|
26564
|
+
color48.yellow(`Stop all agents and switch to "${targetName}"?`),
|
|
26136
26565
|
false
|
|
26137
26566
|
// default to No for safety
|
|
26138
26567
|
);
|
|
26139
26568
|
if (!confirmed) {
|
|
26140
|
-
opts.renderer.write(
|
|
26569
|
+
opts.renderer.write(color48.dim(" Switch cancelled.\n"));
|
|
26141
26570
|
return false;
|
|
26142
26571
|
}
|
|
26143
26572
|
if (fleetRunning > 0) {
|
|
26144
26573
|
const killed = opts.onFleetKill ? await opts.onFleetKill() : 0;
|
|
26145
26574
|
if (killed > 0) {
|
|
26146
|
-
opts.renderer.write(
|
|
26575
|
+
opts.renderer.write(color48.dim(` Stopped ${killed} subagent(s).
|
|
26147
26576
|
`));
|
|
26148
26577
|
}
|
|
26149
26578
|
}
|
|
26150
26579
|
if (eternalActive) {
|
|
26151
26580
|
eternalEngine?.stop();
|
|
26152
|
-
opts.renderer.write(
|
|
26581
|
+
opts.renderer.write(color48.dim(" Stopped eternal engine.\n"));
|
|
26153
26582
|
}
|
|
26154
26583
|
if (parallelActive) {
|
|
26155
26584
|
parallelEngine?.stop();
|
|
26156
|
-
opts.renderer.write(
|
|
26585
|
+
opts.renderer.write(color48.dim(" Stopped parallel engine.\n"));
|
|
26157
26586
|
}
|
|
26158
26587
|
return true;
|
|
26159
26588
|
}
|
|
@@ -26165,16 +26594,16 @@ async function switchInteractiveCommand(opts, ctx) {
|
|
|
26165
26594
|
currentProjectRoot: currentRoot
|
|
26166
26595
|
});
|
|
26167
26596
|
if (!result) {
|
|
26168
|
-
return { message:
|
|
26597
|
+
return { message: color48.dim("Cancelled.") };
|
|
26169
26598
|
}
|
|
26170
26599
|
switch (result.kind) {
|
|
26171
26600
|
case "project": {
|
|
26172
26601
|
const project = manifest.projects.find((p) => p.slug === result.key);
|
|
26173
26602
|
if (!project) {
|
|
26174
|
-
return { message:
|
|
26603
|
+
return { message: color48.red(`Project not found: ${result.key}`) };
|
|
26175
26604
|
}
|
|
26176
26605
|
if (project.root === currentRoot) {
|
|
26177
|
-
return { message:
|
|
26606
|
+
return { message: color48.dim(`Already in ${project.name} (${project.root})`) };
|
|
26178
26607
|
}
|
|
26179
26608
|
const canSwitch = await confirmProjectSwitch(opts, project.name);
|
|
26180
26609
|
if (!canSwitch) return { message: "" };
|
|
@@ -26187,11 +26616,11 @@ async function switchInteractiveCommand(opts, ctx) {
|
|
|
26187
26616
|
case "prev-sessions":
|
|
26188
26617
|
return handlePrevSessions(opts, ctx);
|
|
26189
26618
|
default:
|
|
26190
|
-
return { message:
|
|
26619
|
+
return { message: color48.dim("Cancelled.") };
|
|
26191
26620
|
}
|
|
26192
26621
|
}
|
|
26193
26622
|
default:
|
|
26194
|
-
return { message:
|
|
26623
|
+
return { message: color48.dim("Cancelled.") };
|
|
26195
26624
|
}
|
|
26196
26625
|
}
|
|
26197
26626
|
async function spawnInProject(opts, _ctx, root, projectName) {
|
|
@@ -26206,7 +26635,7 @@ async function spawnInProject(opts, _ctx, root, projectName) {
|
|
|
26206
26635
|
cliPath = process.argv[1] ?? "";
|
|
26207
26636
|
if (!cliPath) {
|
|
26208
26637
|
return {
|
|
26209
|
-
message:
|
|
26638
|
+
message: color48.red(
|
|
26210
26639
|
"Could not locate the CLI entry point. Run `wstack` manually in the target directory."
|
|
26211
26640
|
)
|
|
26212
26641
|
};
|
|
@@ -26236,15 +26665,15 @@ async function spawnInProject(opts, _ctx, root, projectName) {
|
|
|
26236
26665
|
// and boot/tui-project-spawn.ts documents this exact bug being removed.
|
|
26237
26666
|
});
|
|
26238
26667
|
child.on("error", (err) => {
|
|
26239
|
-
console.error(
|
|
26668
|
+
console.error(color48.red(`Failed to spawn wstack: ${err.message}`));
|
|
26240
26669
|
});
|
|
26241
26670
|
child.unref();
|
|
26242
26671
|
return {
|
|
26243
26672
|
message: [
|
|
26244
26673
|
"",
|
|
26245
|
-
|
|
26246
|
-
|
|
26247
|
-
|
|
26674
|
+
color48.green(` Switched to ${projectName}`),
|
|
26675
|
+
color48.dim(` Root: ${root}`),
|
|
26676
|
+
color48.dim(" (current session stays open \u2014 Ctrl+C to return)"),
|
|
26248
26677
|
""
|
|
26249
26678
|
].join("\n")
|
|
26250
26679
|
};
|
|
@@ -26261,7 +26690,7 @@ async function handleNewSession(_opts, _ctx) {
|
|
|
26261
26690
|
cliPath = process.argv[1] ?? "";
|
|
26262
26691
|
if (!cliPath) {
|
|
26263
26692
|
return {
|
|
26264
|
-
message:
|
|
26693
|
+
message: color48.red("Could not locate the CLI entry point. Run `wstack` manually.")
|
|
26265
26694
|
};
|
|
26266
26695
|
}
|
|
26267
26696
|
}
|
|
@@ -26278,14 +26707,14 @@ async function handleNewSession(_opts, _ctx) {
|
|
|
26278
26707
|
// and boot/tui-project-spawn.ts documents this exact bug being removed.
|
|
26279
26708
|
});
|
|
26280
26709
|
child.on("error", (err) => {
|
|
26281
|
-
console.error(
|
|
26710
|
+
console.error(color48.red(`Failed to spawn wstack: ${err.message}`));
|
|
26282
26711
|
});
|
|
26283
26712
|
child.unref();
|
|
26284
26713
|
return {
|
|
26285
26714
|
message: [
|
|
26286
26715
|
"",
|
|
26287
|
-
|
|
26288
|
-
|
|
26716
|
+
color48.green(" Starting new session ..."),
|
|
26717
|
+
color48.dim(" (current session stays open \u2014 Ctrl+C to return)"),
|
|
26289
26718
|
""
|
|
26290
26719
|
].join("\n")
|
|
26291
26720
|
};
|
|
@@ -26296,25 +26725,25 @@ async function handlePrevSessions(opts, _ctx) {
|
|
|
26296
26725
|
}
|
|
26297
26726
|
const list = await opts.sessionStore.list(15);
|
|
26298
26727
|
if (list.length === 0) {
|
|
26299
|
-
return { message:
|
|
26728
|
+
return { message: color48.dim("No saved sessions.") };
|
|
26300
26729
|
}
|
|
26301
26730
|
const currentId = opts.context?.session?.id;
|
|
26302
|
-
const lines = [
|
|
26731
|
+
const lines = [color48.bold(`Recent sessions (${list.length}):`), ""];
|
|
26303
26732
|
for (const s of list) {
|
|
26304
26733
|
const isCurrent = s.id === currentId;
|
|
26305
|
-
const marker = isCurrent ?
|
|
26306
|
-
const date =
|
|
26734
|
+
const marker = isCurrent ? color48.cyan("\u25CF") : " ";
|
|
26735
|
+
const date = color48.dim(s.startedAt.slice(0, 16).replace("T", " "));
|
|
26307
26736
|
const stats = [
|
|
26308
|
-
|
|
26309
|
-
s.toolCallCount ?
|
|
26310
|
-
s.iterationCount ?
|
|
26737
|
+
color48.dim(`${s.tokenTotal.toLocaleString()} tok`),
|
|
26738
|
+
s.toolCallCount ? color48.cyan(`${s.toolCallCount} calls`) : "",
|
|
26739
|
+
s.iterationCount ? color48.dim(`${s.iterationCount} iter`) : ""
|
|
26311
26740
|
].filter(Boolean).join(" ");
|
|
26312
|
-
const outcome = s.outcome === "completed" ?
|
|
26313
|
-
lines.push(` ${marker} ${
|
|
26314
|
-
lines.push(` ${stats} ${outcome} ${
|
|
26741
|
+
const outcome = s.outcome === "completed" ? color48.green("\u2713") : s.outcome === "aborted" ? color48.yellow("\u26A0") : s.outcome === "error" ? color48.red("\u2717") : color48.dim("?");
|
|
26742
|
+
lines.push(` ${marker} ${color48.bold(s.id)} ${date}`);
|
|
26743
|
+
lines.push(` ${stats} ${outcome} ${color48.dim(s.title)}`);
|
|
26315
26744
|
lines.push("");
|
|
26316
26745
|
}
|
|
26317
|
-
lines.push(
|
|
26746
|
+
lines.push(color48.dim("Resume: /sessions or wstack resume <id>"));
|
|
26318
26747
|
return { message: lines.join("\n") };
|
|
26319
26748
|
}
|
|
26320
26749
|
|
|
@@ -26522,7 +26951,7 @@ function buildSecurityCommand(opts) {
|
|
|
26522
26951
|
// src/slash-commands/settings.ts
|
|
26523
26952
|
import { noOpVault as noOpVault9 } from "@wrongstack/core/security";
|
|
26524
26953
|
import { resolveFleetChatVerbosity as resolveFleetChatVerbosity2 } from "@wrongstack/core/types";
|
|
26525
|
-
import { color as
|
|
26954
|
+
import { color as color49, toErrorMessage as toErrorMessage24 } from "@wrongstack/core/utils";
|
|
26526
26955
|
import { getProcessRegistry } from "@wrongstack/tools";
|
|
26527
26956
|
|
|
26528
26957
|
// src/utils/delay-format.ts
|
|
@@ -26630,56 +27059,56 @@ function buildSettingsCommand(opts) {
|
|
|
26630
27059
|
const idx = opts.configStore.get().indexing;
|
|
26631
27060
|
const sess = opts.configStore.get().session;
|
|
26632
27061
|
return [
|
|
26633
|
-
`${
|
|
27062
|
+
`${color49.bold("WrongStack")} ${color49.dim("\u2014 Settings")}`,
|
|
26634
27063
|
"",
|
|
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 ?
|
|
27064
|
+
` auto-proceed delay: ${color49.cyan(formatDelay(delay))} ${color49.dim("change: /settings delay <seconds>")}`,
|
|
27065
|
+
` default autonomy mode: ${color49.cyan(mode)} ${color49.dim("change: /settings mode off|suggest|auto")}`,
|
|
27066
|
+
` fleet chat: ${color49.cyan(resolveFleetChatVerbosity2(au))} ${color49.dim("change: /settings stream-fleet off|full")}`,
|
|
27067
|
+
` completion chime: ${au?.chime === true ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings chime on|off")}`,
|
|
27068
|
+
` confirm before exit: ${au?.confirmExit !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings confirm-exit on|off")}`,
|
|
27069
|
+
` launch hints: ${hints ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings hints on|off")}`,
|
|
27070
|
+
` debug stream: ${debugStream ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings debug-stream on|off")}`,
|
|
27071
|
+
` config scope: ${color49.cyan(configScope)} ${color49.dim("change: /settings config-scope global|project")}`,
|
|
27072
|
+
` filesystem access: ${color49.cyan(fsAccess)} ${color49.dim("change: /settings fs-access unrestricted|project")}`,
|
|
27073
|
+
` refine: ${enhanceEnabled ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings refine on|off")}`,
|
|
27074
|
+
` refine-delay: ${color49.cyan(formatDelay(enhanceDelay))} ${color49.dim("change: /settings refine-delay <seconds>")}`,
|
|
27075
|
+
` refine-language: ${color49.cyan(enhanceLanguage)} ${color49.dim("change: /settings refine-language original|english")}`,
|
|
27076
|
+
` refiner-provider: ${color49.cyan(au?.refinerProvider ?? color49.dim("(unset)"))} ${color49.dim("change: /settings refiner-provider <id>")}`,
|
|
27077
|
+
` refiner-model: ${color49.cyan(au?.refinerModel ?? color49.dim("(unset)"))} ${color49.dim("change: /settings refiner-model <model>")}`,
|
|
27078
|
+
` refiner-fallback-profile: ${color49.cyan(au?.refinerFallbackProfile ?? color49.dim("(unset)"))} ${color49.dim("change: /settings refiner-fallback-profile <name>")}`,
|
|
27079
|
+
` semver default part: ${color49.cyan(semverPart)} ${color49.dim("change: /settings semver-part patch|minor|major|auto")}`,
|
|
27080
|
+
` circuit breaker: ${breakerEnabled ? color49.cyan("on") : color49.dim("off")} (${breakerTimeout > 0 ? formatDelay(breakerTimeout) : color49.dim("manual")}) ${color49.dim("change: /settings breaker on|off")}`,
|
|
27081
|
+
` context mode: ${color49.cyan(contextMode)} ${color49.dim("change: /settings context-mode balanced|frugal|deep")}`,
|
|
27082
|
+
` context strategy: ${color49.cyan(contextStrategy)} ${color49.dim("change: /settings context-strategy hybrid|intelligent|selective")}`,
|
|
27083
|
+
` context auto-compact: ${contextAutoCompact ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings context-auto-compact on|off")}`,
|
|
27084
|
+
` token-saving: ${color49.cyan(tokenSavingTier)} ${color49.dim("change: /settings token-saving off|minimal|light|medium|aggressive")}`,
|
|
27085
|
+
` nextsteps tool: ${nextStepsToolEnabled ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings nextsteps-tool on|off")}`,
|
|
27086
|
+
` MCP features: ${feats?.mcp !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings mcp on|off")}`,
|
|
27087
|
+
` plugin features: ${feats?.plugins !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings plugins on|off")}`,
|
|
27088
|
+
` memory features: ${feats?.memory !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings memory on|off")}`,
|
|
27089
|
+
` skills features: ${feats?.skills !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings skills on|off")}`,
|
|
27090
|
+
` models registry: ${feats?.modelsRegistry !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings models-registry on|off")}`,
|
|
27091
|
+
` max concurrent: ${color49.cyan(maxConcurrent === 0 ? "default" : String(maxConcurrent))} ${color49.dim("change: /settings max-concurrent <n>")}`,
|
|
27092
|
+
` max iterations: ${color49.cyan(String(tools?.maxIterations ?? "default"))} ${color49.dim("change: /settings max-iterations <n>")}`,
|
|
27093
|
+
` auto-proceed max iters: ${color49.cyan(String(au?.autoProceedMaxIterations ?? "unlimited"))} ${color49.dim("change: /settings auto-proceed-max-iterations <n>")}`,
|
|
27094
|
+
` title animation: ${titleAnimation ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings title-animation on|off")}`,
|
|
27095
|
+
` thinking word: ${color49.cyan(au?.thinkingWord ?? "thinking")} ${color49.dim("change: /settings thinking-word <word>")}`,
|
|
27096
|
+
` statusline mode: ${color49.cyan(au?.statuslineMode ?? "minimum")} ${color49.dim("change: /settings statusline minimum|detailed|no-color")}`,
|
|
27097
|
+
` animation style: ${color49.cyan(au?.animationStyle ?? "rainbow")} ${color49.dim("change: /settings animation rainbow|wave|pulse|dots|breathe|cycle")}`,
|
|
27098
|
+
` read symbols: ${au?.readAdvancedMode === true ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings read-symbols on|off")}`,
|
|
27099
|
+
` reasoning mode: ${color49.cyan(reasoningMode)} ${color49.dim("change: /settings reasoning auto|on|off")}`,
|
|
27100
|
+
` reasoning effort: ${color49.cyan(reasoningEffort)} ${color49.dim("change: /settings reasoning-effort <level>")}`,
|
|
27101
|
+
` reasoning preserve: ${reasoningPreserve ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings reasoning-preserve on|off")}`,
|
|
27102
|
+
` cache TTL: ${color49.cyan(cacheTtl)} ${color49.dim("change: /settings cache-ttl 5m|1h")}`,
|
|
27103
|
+
` index on start: ${idx?.onSessionStart !== false ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings index-on-start on|off")}`,
|
|
27104
|
+
` log level: ${color49.cyan(log?.level ?? "info")} ${color49.dim("change: /settings log-level error|warn|info|debug|trace")}`,
|
|
27105
|
+
` audit level: ${color49.cyan(sess?.auditLevel ?? "standard")} ${color49.dim("change: /settings audit-level minimal|standard|full")}`,
|
|
27106
|
+
` HQ publishing: ${hqEnabled ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings hq on|off")}`,
|
|
27107
|
+
` HQ URL: ${color49.cyan(hqUrl)} ${color49.dim("change: /settings hq-url <url>")}`,
|
|
27108
|
+
` HQ token: ${color49.cyan(hqToken)} ${color49.dim("change: /settings hq-token <token>")}`,
|
|
27109
|
+
` HQ raw content: ${hq?.rawContent === true ? color49.cyan("on") : color49.dim("off")} ${color49.dim("change: /settings hq-raw on|off")}`,
|
|
26681
27110
|
"",
|
|
26682
|
-
|
|
27111
|
+
color49.dim(` Persisted to ${persistedTo} \xB7 /settings help for more`)
|
|
26683
27112
|
].join("\n");
|
|
26684
27113
|
}
|
|
26685
27114
|
return {
|
|
@@ -26694,7 +27123,7 @@ function buildSettingsCommand(opts) {
|
|
|
26694
27123
|
return { message: this.help ?? "" };
|
|
26695
27124
|
}
|
|
26696
27125
|
if (!opts.configStore || !opts.paths) {
|
|
26697
|
-
return { message: `${
|
|
27126
|
+
return { message: `${color49.red("Error")} config store not available.` };
|
|
26698
27127
|
}
|
|
26699
27128
|
if (!sub) {
|
|
26700
27129
|
return { message: currentView() };
|
|
@@ -26702,16 +27131,16 @@ function buildSettingsCommand(opts) {
|
|
|
26702
27131
|
if (sub === "defaults") {
|
|
26703
27132
|
return {
|
|
26704
27133
|
message: [
|
|
26705
|
-
`${
|
|
27134
|
+
`${color49.bold("Default Values")}`,
|
|
26706
27135
|
"",
|
|
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: ${
|
|
27136
|
+
` auto-proceed delay: ${color49.cyan("45s")} ${color49.dim("(WRONGSTACK_AUTO_PROCEED_DELAY_MS env)")}`,
|
|
27137
|
+
` default autonomy mode: ${color49.cyan("off")}`,
|
|
27138
|
+
` launch hints: ${color49.cyan("on")}`,
|
|
27139
|
+
` iteration timeout: ${color49.cyan("5 min")}`,
|
|
27140
|
+
` session timeout: ${color49.cyan("30 min")}`,
|
|
27141
|
+
` max iterations: ${color49.cyan("100")}`,
|
|
27142
|
+
` max concurrent: ${color49.cyan("4")}`,
|
|
27143
|
+
` semver default part: ${color49.cyan("patch")}`
|
|
26715
27144
|
].join("\n")
|
|
26716
27145
|
};
|
|
26717
27146
|
}
|
|
@@ -26726,7 +27155,7 @@ function buildSettingsCommand(opts) {
|
|
|
26726
27155
|
if (sub === "hq") {
|
|
26727
27156
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
26728
27157
|
if (!["on", "off"].includes(raw)) {
|
|
26729
|
-
return { message: `${
|
|
27158
|
+
return { message: `${color49.amber("Usage:")} /settings hq on|off` };
|
|
26730
27159
|
}
|
|
26731
27160
|
const on = raw === "on";
|
|
26732
27161
|
await persistConfigSetting({ ...persistDeps, forceGlobal: true }, (cfg) => {
|
|
@@ -26735,19 +27164,19 @@ function buildSettingsCommand(opts) {
|
|
|
26735
27164
|
cfg.hq = hq;
|
|
26736
27165
|
});
|
|
26737
27166
|
return {
|
|
26738
|
-
message: `${
|
|
27167
|
+
message: `${color49.green("\u2713")} HQ publishing \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
|
|
26739
27168
|
};
|
|
26740
27169
|
}
|
|
26741
27170
|
if (sub === "hq-url") {
|
|
26742
27171
|
const raw = rest.join(" ").trim();
|
|
26743
27172
|
if (!raw)
|
|
26744
|
-
return { message: `${
|
|
27173
|
+
return { message: `${color49.amber("Usage:")} /settings hq-url <http://host:3499>` };
|
|
26745
27174
|
try {
|
|
26746
27175
|
const url = new URL(raw);
|
|
26747
27176
|
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
26748
27177
|
throw new Error("bad protocol");
|
|
26749
27178
|
} catch {
|
|
26750
|
-
return { message: `${
|
|
27179
|
+
return { message: `${color49.red("Invalid URL")}: ${raw}` };
|
|
26751
27180
|
}
|
|
26752
27181
|
await persistConfigSetting({ ...persistDeps, forceGlobal: true }, (cfg) => {
|
|
26753
27182
|
const hq = cfg.hq ?? {};
|
|
@@ -26755,12 +27184,12 @@ function buildSettingsCommand(opts) {
|
|
|
26755
27184
|
hq.enabled = true;
|
|
26756
27185
|
cfg.hq = hq;
|
|
26757
27186
|
});
|
|
26758
|
-
return { message: `${
|
|
27187
|
+
return { message: `${color49.green("\u2713")} HQ URL \u2192 ${color49.cyan(raw)}` };
|
|
26759
27188
|
}
|
|
26760
27189
|
if (sub === "hq-token") {
|
|
26761
27190
|
const token = rest.join(" ").trim();
|
|
26762
27191
|
if (!token)
|
|
26763
|
-
return { message: `${
|
|
27192
|
+
return { message: `${color49.amber("Usage:")} /settings hq-token <client-token>` };
|
|
26764
27193
|
await persistConfigSetting({ ...persistDeps, forceGlobal: true }, (cfg) => {
|
|
26765
27194
|
const hq = cfg.hq ?? {};
|
|
26766
27195
|
hq.token = token;
|
|
@@ -26768,13 +27197,13 @@ function buildSettingsCommand(opts) {
|
|
|
26768
27197
|
cfg.hq = hq;
|
|
26769
27198
|
});
|
|
26770
27199
|
return {
|
|
26771
|
-
message: `${
|
|
27200
|
+
message: `${color49.green("\u2713")} HQ token saved ${color49.dim("(active profile config)")}`
|
|
26772
27201
|
};
|
|
26773
27202
|
}
|
|
26774
27203
|
if (sub === "hq-raw") {
|
|
26775
27204
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
26776
27205
|
if (!["on", "off"].includes(raw)) {
|
|
26777
|
-
return { message: `${
|
|
27206
|
+
return { message: `${color49.amber("Usage:")} /settings hq-raw on|off` };
|
|
26778
27207
|
}
|
|
26779
27208
|
const on = raw === "on";
|
|
26780
27209
|
await persistConfigSetting({ ...persistDeps, forceGlobal: true }, (cfg) => {
|
|
@@ -26783,56 +27212,56 @@ function buildSettingsCommand(opts) {
|
|
|
26783
27212
|
cfg.hq = hq;
|
|
26784
27213
|
});
|
|
26785
27214
|
return {
|
|
26786
|
-
message: `${
|
|
27215
|
+
message: `${color49.green("\u2713")} HQ raw content \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
|
|
26787
27216
|
};
|
|
26788
27217
|
}
|
|
26789
27218
|
if (sub === "delay") {
|
|
26790
27219
|
const raw = rest[0];
|
|
26791
27220
|
if (raw === void 0) {
|
|
26792
27221
|
return {
|
|
26793
|
-
message: `${
|
|
27222
|
+
message: `${color49.amber("Usage:")} /settings delay <seconds> ${color49.dim("(0 disables)")}`
|
|
26794
27223
|
};
|
|
26795
27224
|
}
|
|
26796
27225
|
const seconds = Number.parseFloat(raw);
|
|
26797
27226
|
if (Number.isNaN(seconds) || seconds < 0) {
|
|
26798
27227
|
return {
|
|
26799
|
-
message: `${
|
|
27228
|
+
message: `${color49.red("Invalid number")}: "${raw}". Enter seconds, e.g. /settings delay 30`
|
|
26800
27229
|
};
|
|
26801
27230
|
}
|
|
26802
27231
|
const ms = Math.round(seconds * 1e3);
|
|
26803
27232
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
26804
27233
|
autonomy.autoProceedDelayMs = ms;
|
|
26805
27234
|
});
|
|
26806
|
-
return { message: `${
|
|
27235
|
+
return { message: `${color49.green("\u2713")} auto-proceed delay \u2192 ${formatDelay(ms)}` };
|
|
26807
27236
|
}
|
|
26808
27237
|
if (sub === "mode") {
|
|
26809
27238
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
26810
27239
|
const modes = ["off", "suggest", "auto"];
|
|
26811
27240
|
if (!modes.includes(raw)) {
|
|
26812
|
-
return { message: `${
|
|
27241
|
+
return { message: `${color49.amber("Usage:")} /settings mode off|suggest|auto` };
|
|
26813
27242
|
}
|
|
26814
27243
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
26815
27244
|
autonomy.defaultMode = raw;
|
|
26816
27245
|
});
|
|
26817
|
-
return { message: `${
|
|
27246
|
+
return { message: `${color49.green("\u2713")} default autonomy \u2192 ${color49.bold(raw)}` };
|
|
26818
27247
|
}
|
|
26819
27248
|
if (sub === "hints") {
|
|
26820
27249
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
26821
27250
|
if (!["on", "off"].includes(raw)) {
|
|
26822
|
-
return { message: `${
|
|
27251
|
+
return { message: `${color49.amber("Usage:")} /settings hints on|off` };
|
|
26823
27252
|
}
|
|
26824
27253
|
const on = raw === "on";
|
|
26825
27254
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
26826
27255
|
cfg.hints = on;
|
|
26827
27256
|
});
|
|
26828
27257
|
return {
|
|
26829
|
-
message: `${
|
|
27258
|
+
message: `${color49.green("\u2713")} launch hints \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
|
|
26830
27259
|
};
|
|
26831
27260
|
}
|
|
26832
27261
|
if (sub === "debug-stream") {
|
|
26833
27262
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
26834
27263
|
if (!["on", "off"].includes(raw)) {
|
|
26835
|
-
return { message: `${
|
|
27264
|
+
return { message: `${color49.amber("Usage:")} /settings debug-stream on|off` };
|
|
26836
27265
|
}
|
|
26837
27266
|
const on = raw === "on";
|
|
26838
27267
|
const { setDebugStreamEnabled } = await import("@wrongstack/providers");
|
|
@@ -26841,24 +27270,24 @@ function buildSettingsCommand(opts) {
|
|
|
26841
27270
|
cfg.debugStream = on;
|
|
26842
27271
|
});
|
|
26843
27272
|
return {
|
|
26844
|
-
message: `${
|
|
27273
|
+
message: `${color49.green("\u2713")} debug stream \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("raw SSE hex-dump to stderr")}`
|
|
26845
27274
|
};
|
|
26846
27275
|
}
|
|
26847
27276
|
if (sub === "config-scope") {
|
|
26848
27277
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
26849
27278
|
if (!["global", "project"].includes(raw)) {
|
|
26850
|
-
return { message: `${
|
|
27279
|
+
return { message: `${color49.amber("Usage:")} /settings config-scope global|project` };
|
|
26851
27280
|
}
|
|
26852
27281
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
26853
27282
|
cfg.configScope = raw;
|
|
26854
27283
|
});
|
|
26855
|
-
const label = raw === "project" ? `${
|
|
26856
|
-
return { message: `${
|
|
27284
|
+
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`;
|
|
27285
|
+
return { message: `${color49.green("\u2713")} config scope \u2192 ${label}` };
|
|
26857
27286
|
}
|
|
26858
27287
|
if (sub === "fs-access") {
|
|
26859
27288
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
26860
27289
|
if (!["unrestricted", "project"].includes(raw)) {
|
|
26861
|
-
return { message: `${
|
|
27290
|
+
return { message: `${color49.amber("Usage:")} /settings fs-access unrestricted|project` };
|
|
26862
27291
|
}
|
|
26863
27292
|
const restrict = raw === "project";
|
|
26864
27293
|
const fsAccess = deriveFsAccessPair({ restrictFsToRoot: restrict });
|
|
@@ -26870,15 +27299,15 @@ function buildSettingsCommand(opts) {
|
|
|
26870
27299
|
features.allowOutsideProjectRoot = fsAccess.allowOutsideProjectRoot;
|
|
26871
27300
|
cfg.features = features;
|
|
26872
27301
|
});
|
|
26873
|
-
const label = restrict ? `${
|
|
27302
|
+
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
27303
|
return {
|
|
26875
|
-
message: `${
|
|
27304
|
+
message: `${color49.green("\u2713")} filesystem access \u2192 ${label} ${color49.dim("(restart or re-open the session to apply)")}`
|
|
26876
27305
|
};
|
|
26877
27306
|
}
|
|
26878
27307
|
if (sub === "refine") {
|
|
26879
27308
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
26880
27309
|
if (!["on", "off"].includes(raw)) {
|
|
26881
|
-
return { message: `${
|
|
27310
|
+
return { message: `${color49.amber("Usage:")} /settings refine on|off` };
|
|
26882
27311
|
}
|
|
26883
27312
|
const on = raw === "on";
|
|
26884
27313
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
@@ -26888,52 +27317,52 @@ function buildSettingsCommand(opts) {
|
|
|
26888
27317
|
opts.enhanceController.setEnabled(on);
|
|
26889
27318
|
}
|
|
26890
27319
|
return {
|
|
26891
|
-
message: `${
|
|
27320
|
+
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
27321
|
};
|
|
26893
27322
|
}
|
|
26894
27323
|
if (sub === "refine-delay") {
|
|
26895
27324
|
const raw = rest[0];
|
|
26896
27325
|
if (raw === void 0) {
|
|
26897
|
-
return { message: `${
|
|
27326
|
+
return { message: `${color49.amber("Usage:")} /settings refine-delay <seconds>` };
|
|
26898
27327
|
}
|
|
26899
27328
|
const seconds = Number.parseFloat(raw);
|
|
26900
27329
|
if (Number.isNaN(seconds) || seconds < 0) {
|
|
26901
27330
|
return {
|
|
26902
|
-
message: `${
|
|
27331
|
+
message: `${color49.red("Invalid number")}: "${raw}". Enter seconds, e.g. /settings refine-delay 30`
|
|
26903
27332
|
};
|
|
26904
27333
|
}
|
|
26905
27334
|
const ms = Math.round(seconds * 1e3);
|
|
26906
27335
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
26907
27336
|
autonomy.enhanceDelayMs = ms;
|
|
26908
27337
|
});
|
|
26909
|
-
return { message: `${
|
|
27338
|
+
return { message: `${color49.green("\u2713")} refine-delay \u2192 ${formatDelay(ms)}` };
|
|
26910
27339
|
}
|
|
26911
27340
|
if (sub === "refine-language") {
|
|
26912
27341
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
26913
27342
|
if (!["original", "english"].includes(raw)) {
|
|
26914
27343
|
return {
|
|
26915
|
-
message: `${
|
|
27344
|
+
message: `${color49.amber("Usage:")} /settings refine-language original|english`
|
|
26916
27345
|
};
|
|
26917
27346
|
}
|
|
26918
27347
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
26919
27348
|
autonomy.enhanceLanguage = raw;
|
|
26920
27349
|
});
|
|
26921
|
-
const label = raw === "original" ? `${
|
|
26922
|
-
return { message: `${
|
|
27350
|
+
const label = raw === "original" ? `${color49.cyan("original")} \u2014 use the language you wrote in` : `${color49.cyan("english")} \u2014 translate to English`;
|
|
27351
|
+
return { message: `${color49.green("\u2713")} refine-language \u2192 ${label}` };
|
|
26923
27352
|
}
|
|
26924
27353
|
if (sub === "refiner-provider") {
|
|
26925
27354
|
const raw = rest.join(" ").trim();
|
|
26926
27355
|
const currentProvider = opts.configStore.get().autonomy?.refinerProvider;
|
|
26927
27356
|
if (!raw) {
|
|
26928
27357
|
return {
|
|
26929
|
-
message: `${
|
|
27358
|
+
message: `${color49.amber("Usage:")} /settings refiner-provider <providerId> ${color49.dim('(e.g. "openai", "anthropic")' + (currentProvider ? ` Current: ${currentProvider}` : ""))}`
|
|
26930
27359
|
};
|
|
26931
27360
|
}
|
|
26932
27361
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
26933
27362
|
autonomy.refinerProvider = raw;
|
|
26934
27363
|
});
|
|
26935
27364
|
return {
|
|
26936
|
-
message: `${
|
|
27365
|
+
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
27366
|
};
|
|
26938
27367
|
}
|
|
26939
27368
|
if (sub === "refiner-model") {
|
|
@@ -26941,14 +27370,14 @@ function buildSettingsCommand(opts) {
|
|
|
26941
27370
|
const currentModel = opts.configStore.get().autonomy?.refinerModel;
|
|
26942
27371
|
if (!raw) {
|
|
26943
27372
|
return {
|
|
26944
|
-
message: `${
|
|
27373
|
+
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
27374
|
};
|
|
26946
27375
|
}
|
|
26947
27376
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
26948
27377
|
autonomy.refinerModel = raw;
|
|
26949
27378
|
});
|
|
26950
27379
|
return {
|
|
26951
|
-
message: `${
|
|
27380
|
+
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
27381
|
};
|
|
26953
27382
|
}
|
|
26954
27383
|
if (sub === "refiner-fallback-profile") {
|
|
@@ -26956,14 +27385,14 @@ function buildSettingsCommand(opts) {
|
|
|
26956
27385
|
const currentProfile = opts.configStore.get().autonomy?.refinerFallbackProfile;
|
|
26957
27386
|
if (!raw) {
|
|
26958
27387
|
return {
|
|
26959
|
-
message: `${
|
|
27388
|
+
message: `${color49.amber("Usage:")} /settings refiner-fallback-profile <name> ${color49.dim('(e.g. "default")' + (currentProfile ? ` Current: ${currentProfile}` : ""))}`
|
|
26960
27389
|
};
|
|
26961
27390
|
}
|
|
26962
27391
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
26963
27392
|
autonomy.refinerFallbackProfile = raw;
|
|
26964
27393
|
});
|
|
26965
27394
|
return {
|
|
26966
|
-
message: `${
|
|
27395
|
+
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
27396
|
};
|
|
26968
27397
|
}
|
|
26969
27398
|
if (sub === "refiner-clear") {
|
|
@@ -26973,7 +27402,7 @@ function buildSettingsCommand(opts) {
|
|
|
26973
27402
|
autonomy.refinerFallbackProfile = void 0;
|
|
26974
27403
|
});
|
|
26975
27404
|
return {
|
|
26976
|
-
message: `${
|
|
27405
|
+
message: `${color49.green("\u2713")} Refiner config cleared ${color49.dim("goal refinement will use the session provider+model")}`
|
|
26977
27406
|
};
|
|
26978
27407
|
}
|
|
26979
27408
|
if (sub === "semver-part") {
|
|
@@ -26981,7 +27410,7 @@ function buildSettingsCommand(opts) {
|
|
|
26981
27410
|
const parts = ["patch", "minor", "major", "auto"];
|
|
26982
27411
|
if (!parts.includes(raw)) {
|
|
26983
27412
|
return {
|
|
26984
|
-
message: `${
|
|
27413
|
+
message: `${color49.amber("Usage:")} /settings semver-part patch|minor|major|auto`
|
|
26985
27414
|
};
|
|
26986
27415
|
}
|
|
26987
27416
|
await persistConfigSetting({ ...persistDeps, inProjectConfigPath: void 0 }, (cfg) => {
|
|
@@ -26990,13 +27419,13 @@ function buildSettingsCommand(opts) {
|
|
|
26990
27419
|
cfg.extensions = ext;
|
|
26991
27420
|
});
|
|
26992
27421
|
return {
|
|
26993
|
-
message: `${
|
|
27422
|
+
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
27423
|
};
|
|
26995
27424
|
}
|
|
26996
27425
|
if (sub === "breaker") {
|
|
26997
27426
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
26998
27427
|
if (!["on", "off"].includes(raw)) {
|
|
26999
|
-
return { message: `${
|
|
27428
|
+
return { message: `${color49.amber("Usage:")} /settings breaker on|off` };
|
|
27000
27429
|
}
|
|
27001
27430
|
const on = raw === "on";
|
|
27002
27431
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
@@ -27005,20 +27434,20 @@ function buildSettingsCommand(opts) {
|
|
|
27005
27434
|
});
|
|
27006
27435
|
getProcessRegistry().setBreakerConfig({ enabled: on });
|
|
27007
27436
|
return {
|
|
27008
|
-
message: `${
|
|
27437
|
+
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
27438
|
};
|
|
27010
27439
|
}
|
|
27011
27440
|
if (sub === "breaker-timeout") {
|
|
27012
27441
|
const raw = rest[0];
|
|
27013
27442
|
if (raw === void 0) {
|
|
27014
27443
|
return {
|
|
27015
|
-
message: `${
|
|
27444
|
+
message: `${color49.amber("Usage:")} /settings breaker-timeout <seconds> ${color49.dim("(0 = manual recovery only)")}`
|
|
27016
27445
|
};
|
|
27017
27446
|
}
|
|
27018
27447
|
const seconds = Number.parseFloat(raw);
|
|
27019
27448
|
if (Number.isNaN(seconds) || seconds < 0) {
|
|
27020
27449
|
return {
|
|
27021
|
-
message: `${
|
|
27450
|
+
message: `${color49.red("Invalid number")}: "${raw}". Enter seconds, e.g. /settings breaker-timeout 60`
|
|
27022
27451
|
};
|
|
27023
27452
|
}
|
|
27024
27453
|
const ms = Math.round(seconds * 1e3);
|
|
@@ -27031,7 +27460,7 @@ function buildSettingsCommand(opts) {
|
|
|
27031
27460
|
});
|
|
27032
27461
|
getProcessRegistry().setBreakerConfig({ autoKillResetMs: ms });
|
|
27033
27462
|
return {
|
|
27034
|
-
message: `${
|
|
27463
|
+
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
27464
|
};
|
|
27036
27465
|
}
|
|
27037
27466
|
if (sub === "context-mode") {
|
|
@@ -27039,7 +27468,7 @@ function buildSettingsCommand(opts) {
|
|
|
27039
27468
|
const modes = ["balanced", "frugal", "deep"];
|
|
27040
27469
|
if (!modes.includes(raw)) {
|
|
27041
27470
|
return {
|
|
27042
|
-
message: `${
|
|
27471
|
+
message: `${color49.amber("Usage:")} /settings context-mode balanced|frugal|deep`
|
|
27043
27472
|
};
|
|
27044
27473
|
}
|
|
27045
27474
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
@@ -27048,7 +27477,7 @@ function buildSettingsCommand(opts) {
|
|
|
27048
27477
|
cfg.context = ctx;
|
|
27049
27478
|
});
|
|
27050
27479
|
return {
|
|
27051
|
-
message: `${
|
|
27480
|
+
message: `${color49.green("\u2713")} context mode \u2192 ${color49.cyan(raw)} ${color49.dim("context window policy")}`
|
|
27052
27481
|
};
|
|
27053
27482
|
}
|
|
27054
27483
|
if (sub === "context-strategy") {
|
|
@@ -27056,7 +27485,7 @@ function buildSettingsCommand(opts) {
|
|
|
27056
27485
|
const strategies = ["hybrid", "intelligent", "selective"];
|
|
27057
27486
|
if (!strategies.includes(raw)) {
|
|
27058
27487
|
return {
|
|
27059
|
-
message: `${
|
|
27488
|
+
message: `${color49.amber("Usage:")} /settings context-strategy hybrid|intelligent|selective`
|
|
27060
27489
|
};
|
|
27061
27490
|
}
|
|
27062
27491
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
@@ -27065,13 +27494,13 @@ function buildSettingsCommand(opts) {
|
|
|
27065
27494
|
cfg.context = ctx;
|
|
27066
27495
|
});
|
|
27067
27496
|
return {
|
|
27068
|
-
message: `${
|
|
27497
|
+
message: `${color49.green("\u2713")} context strategy \u2192 ${color49.cyan(raw)} ${color49.dim("compactor strategy")}`
|
|
27069
27498
|
};
|
|
27070
27499
|
}
|
|
27071
27500
|
if (sub === "context-auto-compact") {
|
|
27072
27501
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27073
27502
|
if (!["on", "off"].includes(raw)) {
|
|
27074
|
-
return { message: `${
|
|
27503
|
+
return { message: `${color49.amber("Usage:")} /settings context-auto-compact on|off` };
|
|
27075
27504
|
}
|
|
27076
27505
|
const on = raw === "on";
|
|
27077
27506
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
@@ -27080,13 +27509,13 @@ function buildSettingsCommand(opts) {
|
|
|
27080
27509
|
cfg.context = ctx;
|
|
27081
27510
|
});
|
|
27082
27511
|
return {
|
|
27083
|
-
message: `${
|
|
27512
|
+
message: `${color49.green("\u2713")} context auto-compact \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("auto-compact context when thresholds crossed")}`
|
|
27084
27513
|
};
|
|
27085
27514
|
}
|
|
27086
27515
|
if (sub === "nextsteps-tool") {
|
|
27087
27516
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27088
27517
|
if (!["on", "off"].includes(raw)) {
|
|
27089
|
-
return { message: `${
|
|
27518
|
+
return { message: `${color49.amber("Usage:")} /settings nextsteps-tool on|off` };
|
|
27090
27519
|
}
|
|
27091
27520
|
const on = raw === "on";
|
|
27092
27521
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
@@ -27095,7 +27524,7 @@ function buildSettingsCommand(opts) {
|
|
|
27095
27524
|
cfg.tools = tools;
|
|
27096
27525
|
});
|
|
27097
27526
|
return {
|
|
27098
|
-
message: `${
|
|
27527
|
+
message: `${color49.green("\u2713")} nextsteps tool \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("takes effect in the next session")}`
|
|
27099
27528
|
};
|
|
27100
27529
|
}
|
|
27101
27530
|
if (sub === "token-saving") {
|
|
@@ -27103,7 +27532,7 @@ function buildSettingsCommand(opts) {
|
|
|
27103
27532
|
const tiers = ["off", "minimal", "light", "medium", "aggressive"];
|
|
27104
27533
|
if (!tiers.includes(raw)) {
|
|
27105
27534
|
return {
|
|
27106
|
-
message: `${
|
|
27535
|
+
message: `${color49.amber("Usage:")} /settings token-saving off|minimal|light|medium|aggressive`
|
|
27107
27536
|
};
|
|
27108
27537
|
}
|
|
27109
27538
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
@@ -27112,47 +27541,47 @@ function buildSettingsCommand(opts) {
|
|
|
27112
27541
|
cfg.features = feat;
|
|
27113
27542
|
});
|
|
27114
27543
|
return {
|
|
27115
|
-
message: `${
|
|
27544
|
+
message: `${color49.green("\u2713")} token-saving \u2192 ${color49.cyan(raw)} ${color49.dim("token-saving mode")}`
|
|
27116
27545
|
};
|
|
27117
27546
|
}
|
|
27118
27547
|
if (sub === "max-concurrent") {
|
|
27119
27548
|
const raw = rest[0];
|
|
27120
27549
|
if (raw === void 0) {
|
|
27121
27550
|
return {
|
|
27122
|
-
message: `${
|
|
27551
|
+
message: `${color49.amber("Usage:")} /settings max-concurrent <n> ${color49.dim("(0 = default)")}`
|
|
27123
27552
|
};
|
|
27124
27553
|
}
|
|
27125
27554
|
const n = Number.parseInt(raw, 10);
|
|
27126
27555
|
if (Number.isNaN(n) || n < 0) {
|
|
27127
27556
|
return {
|
|
27128
|
-
message: `${
|
|
27557
|
+
message: `${color49.red("Invalid number")}: "${raw}". Enter a non-negative integer (0 = default)`
|
|
27129
27558
|
};
|
|
27130
27559
|
}
|
|
27131
27560
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27132
27561
|
cfg.maxConcurrent = n;
|
|
27133
27562
|
});
|
|
27134
27563
|
return {
|
|
27135
|
-
message: `${
|
|
27564
|
+
message: `${color49.green("\u2713")} max-concurrent \u2192 ${color49.cyan(n === 0 ? "default" : String(n))} ${color49.dim("max concurrent subagents")}`
|
|
27136
27565
|
};
|
|
27137
27566
|
}
|
|
27138
27567
|
if (sub === "title-animation") {
|
|
27139
27568
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27140
27569
|
if (!["on", "off"].includes(raw)) {
|
|
27141
|
-
return { message: `${
|
|
27570
|
+
return { message: `${color49.amber("Usage:")} /settings title-animation on|off` };
|
|
27142
27571
|
}
|
|
27143
27572
|
const on = raw === "on";
|
|
27144
27573
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
27145
27574
|
autonomy.terminalTitleAnimation = on;
|
|
27146
27575
|
});
|
|
27147
27576
|
return {
|
|
27148
|
-
message: `${
|
|
27577
|
+
message: `${color49.green("\u2713")} title animation \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("terminal title animation")}`
|
|
27149
27578
|
};
|
|
27150
27579
|
}
|
|
27151
27580
|
if (sub === "reasoning") {
|
|
27152
27581
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27153
27582
|
const modes = ["auto", "on", "off"];
|
|
27154
27583
|
if (!modes.includes(raw)) {
|
|
27155
|
-
return { message: `${
|
|
27584
|
+
return { message: `${color49.amber("Usage:")} /settings reasoning auto|on|off` };
|
|
27156
27585
|
}
|
|
27157
27586
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27158
27587
|
const mr = cfg.modelRuntime;
|
|
@@ -27160,14 +27589,14 @@ function buildSettingsCommand(opts) {
|
|
|
27160
27589
|
reasoning.mode = raw;
|
|
27161
27590
|
cfg.modelRuntime = { ...mr, reasoning };
|
|
27162
27591
|
});
|
|
27163
|
-
return { message: `${
|
|
27592
|
+
return { message: `${color49.green("\u2713")} reasoning mode \u2192 ${color49.bold(raw)}` };
|
|
27164
27593
|
}
|
|
27165
27594
|
if (sub === "reasoning-effort") {
|
|
27166
27595
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27167
27596
|
const efforts = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
27168
27597
|
if (!efforts.includes(raw)) {
|
|
27169
27598
|
return {
|
|
27170
|
-
message: `${
|
|
27599
|
+
message: `${color49.amber("Usage:")} /settings reasoning-effort none|minimal|low|medium|high|xhigh|max`
|
|
27171
27600
|
};
|
|
27172
27601
|
}
|
|
27173
27602
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
@@ -27176,12 +27605,12 @@ function buildSettingsCommand(opts) {
|
|
|
27176
27605
|
reasoning.effort = raw;
|
|
27177
27606
|
cfg.modelRuntime = { ...mr, reasoning };
|
|
27178
27607
|
});
|
|
27179
|
-
return { message: `${
|
|
27608
|
+
return { message: `${color49.green("\u2713")} reasoning effort \u2192 ${color49.bold(raw)}` };
|
|
27180
27609
|
}
|
|
27181
27610
|
if (sub === "reasoning-preserve") {
|
|
27182
27611
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27183
27612
|
if (!["on", "off"].includes(raw)) {
|
|
27184
|
-
return { message: `${
|
|
27613
|
+
return { message: `${color49.amber("Usage:")} /settings reasoning-preserve on|off` };
|
|
27185
27614
|
}
|
|
27186
27615
|
const on = raw === "on";
|
|
27187
27616
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
@@ -27191,24 +27620,24 @@ function buildSettingsCommand(opts) {
|
|
|
27191
27620
|
cfg.modelRuntime = { ...mr, reasoning };
|
|
27192
27621
|
});
|
|
27193
27622
|
return {
|
|
27194
|
-
message: `${
|
|
27623
|
+
message: `${color49.green("\u2713")} reasoning preserve \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
|
|
27195
27624
|
};
|
|
27196
27625
|
}
|
|
27197
27626
|
if (sub === "cache-ttl") {
|
|
27198
27627
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27199
27628
|
if (!["5m", "1h"].includes(raw)) {
|
|
27200
|
-
return { message: `${
|
|
27629
|
+
return { message: `${color49.amber("Usage:")} /settings cache-ttl 5m|1h` };
|
|
27201
27630
|
}
|
|
27202
27631
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27203
27632
|
const mr = cfg.modelRuntime;
|
|
27204
27633
|
cfg.modelRuntime = { ...mr, cache: { ttl: raw } };
|
|
27205
27634
|
});
|
|
27206
|
-
return { message: `${
|
|
27635
|
+
return { message: `${color49.green("\u2713")} cache TTL \u2192 ${color49.bold(raw)}` };
|
|
27207
27636
|
}
|
|
27208
27637
|
if (sub === "mcp") {
|
|
27209
27638
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27210
27639
|
if (!["on", "off"].includes(raw))
|
|
27211
|
-
return { message: `${
|
|
27640
|
+
return { message: `${color49.amber("Usage:")} /settings mcp on|off` };
|
|
27212
27641
|
const on = raw === "on";
|
|
27213
27642
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27214
27643
|
const feats = cfg.features ?? {};
|
|
@@ -27216,13 +27645,13 @@ function buildSettingsCommand(opts) {
|
|
|
27216
27645
|
cfg.features = feats;
|
|
27217
27646
|
});
|
|
27218
27647
|
return {
|
|
27219
|
-
message: `${
|
|
27648
|
+
message: `${color49.green("\u2713")} MCP features \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("restart to apply")}`
|
|
27220
27649
|
};
|
|
27221
27650
|
}
|
|
27222
27651
|
if (sub === "plugins") {
|
|
27223
27652
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27224
27653
|
if (!["on", "off"].includes(raw))
|
|
27225
|
-
return { message: `${
|
|
27654
|
+
return { message: `${color49.amber("Usage:")} /settings plugins on|off` };
|
|
27226
27655
|
const on = raw === "on";
|
|
27227
27656
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27228
27657
|
const feats = cfg.features ?? {};
|
|
@@ -27230,13 +27659,13 @@ function buildSettingsCommand(opts) {
|
|
|
27230
27659
|
cfg.features = feats;
|
|
27231
27660
|
});
|
|
27232
27661
|
return {
|
|
27233
|
-
message: `${
|
|
27662
|
+
message: `${color49.green("\u2713")} Plugin features \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("restart to apply")}`
|
|
27234
27663
|
};
|
|
27235
27664
|
}
|
|
27236
27665
|
if (sub === "memory") {
|
|
27237
27666
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27238
27667
|
if (!["on", "off"].includes(raw))
|
|
27239
|
-
return { message: `${
|
|
27668
|
+
return { message: `${color49.amber("Usage:")} /settings memory on|off` };
|
|
27240
27669
|
const on = raw === "on";
|
|
27241
27670
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27242
27671
|
const feats = cfg.features ?? {};
|
|
@@ -27244,13 +27673,13 @@ function buildSettingsCommand(opts) {
|
|
|
27244
27673
|
cfg.features = feats;
|
|
27245
27674
|
});
|
|
27246
27675
|
return {
|
|
27247
|
-
message: `${
|
|
27676
|
+
message: `${color49.green("\u2713")} Memory features \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("restart to apply")}`
|
|
27248
27677
|
};
|
|
27249
27678
|
}
|
|
27250
27679
|
if (sub === "skills") {
|
|
27251
27680
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27252
27681
|
if (!["on", "off"].includes(raw))
|
|
27253
|
-
return { message: `${
|
|
27682
|
+
return { message: `${color49.amber("Usage:")} /settings skills on|off` };
|
|
27254
27683
|
const on = raw === "on";
|
|
27255
27684
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27256
27685
|
const feats = cfg.features ?? {};
|
|
@@ -27258,13 +27687,13 @@ function buildSettingsCommand(opts) {
|
|
|
27258
27687
|
cfg.features = feats;
|
|
27259
27688
|
});
|
|
27260
27689
|
return {
|
|
27261
|
-
message: `${
|
|
27690
|
+
message: `${color49.green("\u2713")} Skills features \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("restart to apply")}`
|
|
27262
27691
|
};
|
|
27263
27692
|
}
|
|
27264
27693
|
if (sub === "models-registry") {
|
|
27265
27694
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27266
27695
|
if (!["on", "off"].includes(raw))
|
|
27267
|
-
return { message: `${
|
|
27696
|
+
return { message: `${color49.amber("Usage:")} /settings models-registry on|off` };
|
|
27268
27697
|
const on = raw === "on";
|
|
27269
27698
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27270
27699
|
const feats = cfg.features ?? {};
|
|
@@ -27272,7 +27701,7 @@ function buildSettingsCommand(opts) {
|
|
|
27272
27701
|
cfg.features = feats;
|
|
27273
27702
|
});
|
|
27274
27703
|
return {
|
|
27275
|
-
message: `${
|
|
27704
|
+
message: `${color49.green("\u2713")} Models registry \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("restart to apply")}`
|
|
27276
27705
|
};
|
|
27277
27706
|
}
|
|
27278
27707
|
if (sub === "stream-fleet") {
|
|
@@ -27280,7 +27709,7 @@ function buildSettingsCommand(opts) {
|
|
|
27280
27709
|
const mode = raw === "on" ? "full" : raw === "off" || raw === "full" ? raw : void 0;
|
|
27281
27710
|
if (!mode)
|
|
27282
27711
|
return {
|
|
27283
|
-
message: `${
|
|
27712
|
+
message: `${color49.amber("Usage:")} /settings stream-fleet off|full (on = full)`
|
|
27284
27713
|
};
|
|
27285
27714
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
27286
27715
|
autonomy.fleetChatVerbosity = mode;
|
|
@@ -27288,43 +27717,43 @@ function buildSettingsCommand(opts) {
|
|
|
27288
27717
|
opts.fleetStreamController?.setMode(mode);
|
|
27289
27718
|
const desc = mode === "full" ? "every subagent tool call and message in chat" : "subagent chat lines hidden (F2/F3 stay live)";
|
|
27290
27719
|
return {
|
|
27291
|
-
message: `${
|
|
27720
|
+
message: `${color49.green("\u2713")} fleet chat \u2192 ${color49.cyan(mode)} ${color49.dim(desc)}`
|
|
27292
27721
|
};
|
|
27293
27722
|
}
|
|
27294
27723
|
if (sub === "chime") {
|
|
27295
27724
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27296
27725
|
if (!["on", "off"].includes(raw))
|
|
27297
|
-
return { message: `${
|
|
27726
|
+
return { message: `${color49.amber("Usage:")} /settings chime on|off` };
|
|
27298
27727
|
const on = raw === "on";
|
|
27299
27728
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
27300
27729
|
autonomy.chime = on;
|
|
27301
27730
|
});
|
|
27302
27731
|
return {
|
|
27303
|
-
message: `${
|
|
27732
|
+
message: `${color49.green("\u2713")} completion chime \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
|
|
27304
27733
|
};
|
|
27305
27734
|
}
|
|
27306
27735
|
if (sub === "confirm-exit") {
|
|
27307
27736
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27308
27737
|
if (!["on", "off"].includes(raw))
|
|
27309
|
-
return { message: `${
|
|
27738
|
+
return { message: `${color49.amber("Usage:")} /settings confirm-exit on|off` };
|
|
27310
27739
|
const on = raw === "on";
|
|
27311
27740
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
27312
27741
|
autonomy.confirmExit = on;
|
|
27313
27742
|
});
|
|
27314
27743
|
return {
|
|
27315
|
-
message: `${
|
|
27744
|
+
message: `${color49.green("\u2713")} confirm before exit \u2192 ${on ? color49.cyan("on") : color49.dim("off")}`
|
|
27316
27745
|
};
|
|
27317
27746
|
}
|
|
27318
27747
|
if (sub === "max-iterations") {
|
|
27319
27748
|
const raw = rest[0];
|
|
27320
27749
|
if (raw === void 0)
|
|
27321
27750
|
return {
|
|
27322
|
-
message: `${
|
|
27751
|
+
message: `${color49.amber("Usage:")} /settings max-iterations <n> ${color49.dim("(0 = default)")}`
|
|
27323
27752
|
};
|
|
27324
27753
|
const n = Number.parseInt(raw, 10);
|
|
27325
27754
|
if (Number.isNaN(n) || n < 0)
|
|
27326
27755
|
return {
|
|
27327
|
-
message: `${
|
|
27756
|
+
message: `${color49.red("Invalid number")}: "${raw}". Enter a non-negative integer.`
|
|
27328
27757
|
};
|
|
27329
27758
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27330
27759
|
const tools = cfg.tools ?? {};
|
|
@@ -27332,31 +27761,31 @@ function buildSettingsCommand(opts) {
|
|
|
27332
27761
|
cfg.tools = tools;
|
|
27333
27762
|
});
|
|
27334
27763
|
return {
|
|
27335
|
-
message: `${
|
|
27764
|
+
message: `${color49.green("\u2713")} max iterations \u2192 ${color49.cyan(n === 0 ? "default" : String(n))} ${color49.dim("agent pauses after this many iterations")}`
|
|
27336
27765
|
};
|
|
27337
27766
|
}
|
|
27338
27767
|
if (sub === "auto-proceed-max-iterations") {
|
|
27339
27768
|
const raw = rest[0];
|
|
27340
27769
|
if (raw === void 0)
|
|
27341
27770
|
return {
|
|
27342
|
-
message: `${
|
|
27771
|
+
message: `${color49.amber("Usage:")} /settings auto-proceed-max-iterations <n> ${color49.dim("(0 = unlimited)")}`
|
|
27343
27772
|
};
|
|
27344
27773
|
const n = Number.parseInt(raw, 10);
|
|
27345
27774
|
if (Number.isNaN(n) || n < 0)
|
|
27346
27775
|
return {
|
|
27347
|
-
message: `${
|
|
27776
|
+
message: `${color49.red("Invalid number")}: "${raw}". Enter a non-negative integer.`
|
|
27348
27777
|
};
|
|
27349
27778
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
27350
27779
|
autonomy.autoProceedMaxIterations = n;
|
|
27351
27780
|
});
|
|
27352
27781
|
return {
|
|
27353
|
-
message: `${
|
|
27782
|
+
message: `${color49.green("\u2713")} auto-proceed max iterations \u2192 ${color49.cyan(n === 0 ? "unlimited" : String(n))}`
|
|
27354
27783
|
};
|
|
27355
27784
|
}
|
|
27356
27785
|
if (sub === "index-on-start") {
|
|
27357
27786
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27358
27787
|
if (!["on", "off"].includes(raw))
|
|
27359
|
-
return { message: `${
|
|
27788
|
+
return { message: `${color49.amber("Usage:")} /settings index-on-start on|off` };
|
|
27360
27789
|
const on = raw === "on";
|
|
27361
27790
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27362
27791
|
const idx = cfg.indexing ?? {};
|
|
@@ -27364,7 +27793,7 @@ function buildSettingsCommand(opts) {
|
|
|
27364
27793
|
cfg.indexing = idx;
|
|
27365
27794
|
});
|
|
27366
27795
|
return {
|
|
27367
|
-
message: `${
|
|
27796
|
+
message: `${color49.green("\u2713")} index on session start \u2192 ${on ? color49.cyan("on") : color49.dim("off")} ${color49.dim("effective next session")}`
|
|
27368
27797
|
};
|
|
27369
27798
|
}
|
|
27370
27799
|
if (sub === "log-level") {
|
|
@@ -27372,21 +27801,21 @@ function buildSettingsCommand(opts) {
|
|
|
27372
27801
|
const levels = ["error", "warn", "info", "debug", "trace"];
|
|
27373
27802
|
if (!levels.includes(raw))
|
|
27374
27803
|
return {
|
|
27375
|
-
message: `${
|
|
27804
|
+
message: `${color49.amber("Usage:")} /settings log-level error|warn|info|debug|trace`
|
|
27376
27805
|
};
|
|
27377
27806
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27378
27807
|
const log = cfg.log ?? {};
|
|
27379
27808
|
log.level = raw;
|
|
27380
27809
|
cfg.log = log;
|
|
27381
27810
|
});
|
|
27382
|
-
return { message: `${
|
|
27811
|
+
return { message: `${color49.green("\u2713")} log level \u2192 ${color49.cyan(raw)}` };
|
|
27383
27812
|
}
|
|
27384
27813
|
if (sub === "audit-level") {
|
|
27385
27814
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27386
27815
|
const levels = ["minimal", "standard", "full"];
|
|
27387
27816
|
if (!levels.includes(raw))
|
|
27388
27817
|
return {
|
|
27389
|
-
message: `${
|
|
27818
|
+
message: `${color49.amber("Usage:")} /settings audit-level minimal|standard|full`
|
|
27390
27819
|
};
|
|
27391
27820
|
await persistConfigSetting(persistDeps, (cfg) => {
|
|
27392
27821
|
const sess = cfg.session ?? {};
|
|
@@ -27394,38 +27823,38 @@ function buildSettingsCommand(opts) {
|
|
|
27394
27823
|
cfg.session = sess;
|
|
27395
27824
|
});
|
|
27396
27825
|
return {
|
|
27397
|
-
message: `${
|
|
27826
|
+
message: `${color49.green("\u2713")} audit level \u2192 ${color49.cyan(raw)} ${color49.dim("restart to apply")}`
|
|
27398
27827
|
};
|
|
27399
27828
|
}
|
|
27400
27829
|
if (sub === "thinking-word") {
|
|
27401
27830
|
const raw = rest.join(" ").trim();
|
|
27402
27831
|
if (!raw)
|
|
27403
27832
|
return {
|
|
27404
|
-
message: `${
|
|
27833
|
+
message: `${color49.amber("Usage:")} /settings thinking-word <word> ${color49.dim('single short word, e.g. "thinking", "vibing", "cooking"')}`
|
|
27405
27834
|
};
|
|
27406
27835
|
if (raw.length > 16)
|
|
27407
|
-
return { message: `${
|
|
27836
|
+
return { message: `${color49.red("Word too long")}: max 16 characters.` };
|
|
27408
27837
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
27409
27838
|
autonomy.thinkingWord = raw;
|
|
27410
27839
|
});
|
|
27411
|
-
return { message: `${
|
|
27840
|
+
return { message: `${color49.green("\u2713")} thinking word \u2192 ${color49.cyan(raw)}` };
|
|
27412
27841
|
}
|
|
27413
27842
|
if (sub === "statusline") {
|
|
27414
27843
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27415
27844
|
const modes = ["minimum", "detailed", "no-color"];
|
|
27416
27845
|
if (!modes.includes(raw))
|
|
27417
27846
|
return {
|
|
27418
|
-
message: `${
|
|
27847
|
+
message: `${color49.amber("Usage:")} /settings statusline minimum|detailed|no-color`
|
|
27419
27848
|
};
|
|
27420
27849
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
27421
27850
|
autonomy.statuslineMode = raw;
|
|
27422
27851
|
});
|
|
27423
|
-
return { message: `${
|
|
27852
|
+
return { message: `${color49.green("\u2713")} statusline mode \u2192 ${color49.cyan(raw)}` };
|
|
27424
27853
|
}
|
|
27425
27854
|
if (sub === "read-symbols") {
|
|
27426
27855
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
27427
27856
|
if (!["on", "off"].includes(raw)) {
|
|
27428
|
-
return { message: `${
|
|
27857
|
+
return { message: `${color49.amber("Usage:")} /settings read-symbols on|off` };
|
|
27429
27858
|
}
|
|
27430
27859
|
const on = raw === "on";
|
|
27431
27860
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
@@ -27435,7 +27864,7 @@ function buildSettingsCommand(opts) {
|
|
|
27435
27864
|
opts.context.meta["tools.read.advancedMode"] = on;
|
|
27436
27865
|
}
|
|
27437
27866
|
return {
|
|
27438
|
-
message: `${
|
|
27867
|
+
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
27868
|
};
|
|
27440
27869
|
}
|
|
27441
27870
|
if (sub === "animation") {
|
|
@@ -27443,19 +27872,19 @@ function buildSettingsCommand(opts) {
|
|
|
27443
27872
|
const styles = ["rainbow", "wave", "pulse", "dots", "breathe", "cycle"];
|
|
27444
27873
|
if (!styles.includes(raw))
|
|
27445
27874
|
return {
|
|
27446
|
-
message: `${
|
|
27875
|
+
message: `${color49.amber("Usage:")} /settings animation rainbow|wave|pulse|dots|breathe|cycle`
|
|
27447
27876
|
};
|
|
27448
27877
|
await persistAutonomySetting(persistDeps, (autonomy) => {
|
|
27449
27878
|
autonomy.animationStyle = raw;
|
|
27450
27879
|
});
|
|
27451
|
-
return { message: `${
|
|
27880
|
+
return { message: `${color49.green("\u2713")} animation style \u2192 ${color49.cyan(raw)}` };
|
|
27452
27881
|
}
|
|
27453
27882
|
return {
|
|
27454
|
-
message: `${
|
|
27883
|
+
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
27884
|
};
|
|
27456
27885
|
} catch (err) {
|
|
27457
27886
|
return {
|
|
27458
|
-
message: `${
|
|
27887
|
+
message: `${color49.red("Settings error")}: ${toErrorMessage24(err)}`
|
|
27459
27888
|
};
|
|
27460
27889
|
}
|
|
27461
27890
|
}
|
|
@@ -27464,7 +27893,7 @@ function buildSettingsCommand(opts) {
|
|
|
27464
27893
|
|
|
27465
27894
|
// src/slash-commands/shadow.ts
|
|
27466
27895
|
import { ToolValidationError as ToolValidationError5 } from "@wrongstack/core/types";
|
|
27467
|
-
import { color as
|
|
27896
|
+
import { color as color50 } from "@wrongstack/core/utils";
|
|
27468
27897
|
var DEFAULT_SHADOW_INTERVAL_MS = 3e4;
|
|
27469
27898
|
var MIN_SHADOW_INTERVAL_MS = 5e3;
|
|
27470
27899
|
function buildShadowCommand(opts) {
|
|
@@ -27534,7 +27963,7 @@ function buildShadowCommand(opts) {
|
|
|
27534
27963
|
if (opts.shadowController?.activeId != null) {
|
|
27535
27964
|
return {
|
|
27536
27965
|
message: [
|
|
27537
|
-
`${
|
|
27966
|
+
`${color50.yellow("\u26A0")} A Shadow Agent is already running (${opts.shadowController.activeId.slice(0, 8)}).`,
|
|
27538
27967
|
"",
|
|
27539
27968
|
"Only one Shadow Agent instance is allowed per session.",
|
|
27540
27969
|
"Use /shadow status to view the current instance."
|
|
@@ -27573,16 +28002,16 @@ function buildShadowCommand(opts) {
|
|
|
27573
28002
|
shadowIntervalMs: intervalMs
|
|
27574
28003
|
});
|
|
27575
28004
|
return {
|
|
27576
|
-
message: `${
|
|
27577
|
-
${
|
|
27578
|
-
${
|
|
28005
|
+
message: `${color50.green("\u2713")} Shadow Agent queued: ${spawnId}
|
|
28006
|
+
${color50.dim("Mode:")} one-shot quiet check
|
|
28007
|
+
${color50.dim("Model:")} ${modelRef.label}`
|
|
27579
28008
|
};
|
|
27580
28009
|
}
|
|
27581
28010
|
case "stop": {
|
|
27582
28011
|
const activeId = opts.shadowController?.activeId;
|
|
27583
28012
|
if (!activeId) {
|
|
27584
28013
|
return {
|
|
27585
|
-
message: `${
|
|
28014
|
+
message: `${color50.yellow("\u26A0")} No active Shadow Agent is registered for this session.`
|
|
27586
28015
|
};
|
|
27587
28016
|
}
|
|
27588
28017
|
if (!opts.onFleetTerminate) {
|
|
@@ -27591,12 +28020,12 @@ ${color49.dim("Model:")} ${modelRef.label}`
|
|
|
27591
28020
|
const ok = await opts.onFleetTerminate(activeId);
|
|
27592
28021
|
if (ok) {
|
|
27593
28022
|
opts.shadowController?.clear();
|
|
27594
|
-
return { message: `${
|
|
28023
|
+
return { message: `${color50.green("\u2713")} Shadow Agent stopped: ${activeId}` };
|
|
27595
28024
|
}
|
|
27596
28025
|
return {
|
|
27597
28026
|
message: [
|
|
27598
|
-
`${
|
|
27599
|
-
`It may already be stopped. Use ${
|
|
28027
|
+
`${color50.red("\u2717")} Failed to stop Shadow Agent ${color50.bold(activeId)}.`,
|
|
28028
|
+
`It may already be stopped. Use ${color50.bold("/shadow status")} to inspect active agents.`
|
|
27600
28029
|
].join("\n")
|
|
27601
28030
|
};
|
|
27602
28031
|
}
|
|
@@ -27624,10 +28053,10 @@ ${color49.dim("Model:")} ${modelRef.label}`
|
|
|
27624
28053
|
opts.shadowController?.clear();
|
|
27625
28054
|
return {
|
|
27626
28055
|
message: [
|
|
27627
|
-
`${
|
|
28056
|
+
`${color50.red("\u26A0")} HOOP: Stopped ${killed} running agent(s)`,
|
|
27628
28057
|
"",
|
|
27629
|
-
`Target: ${
|
|
27630
|
-
`Reason: ${
|
|
28058
|
+
`Target: ${color50.bold("all")}`,
|
|
28059
|
+
`Reason: ${color50.yellow(reason)}`
|
|
27631
28060
|
].join("\n")
|
|
27632
28061
|
};
|
|
27633
28062
|
}
|
|
@@ -27641,10 +28070,10 @@ ${color49.dim("Model:")} ${modelRef.label}`
|
|
|
27641
28070
|
}
|
|
27642
28071
|
return {
|
|
27643
28072
|
message: [
|
|
27644
|
-
ok ? `${
|
|
28073
|
+
ok ? `${color50.red("\u26A0")} HOOP: Stopped agent` : `${color50.red("\u2717")} HOOP: Failed to stop agent`,
|
|
27645
28074
|
"",
|
|
27646
|
-
`Target: ${
|
|
27647
|
-
`Reason: ${
|
|
28075
|
+
`Target: ${color50.bold(targetId)}`,
|
|
28076
|
+
`Reason: ${color50.yellow(reason)}`,
|
|
27648
28077
|
agentInfo ? `
|
|
27649
28078
|
Agent info:
|
|
27650
28079
|
${agentInfo}` : ""
|
|
@@ -27669,7 +28098,7 @@ Current default: ${defaultModelRef.label}`
|
|
|
27669
28098
|
opts.shadowController?.setDefaults?.({ provider: parsed.provider, model: parsed.model });
|
|
27670
28099
|
return {
|
|
27671
28100
|
message: `/shadow model ${parsed.label}
|
|
27672
|
-
${
|
|
28101
|
+
${color50.dim("Model will be applied on next /shadow start")}`
|
|
27673
28102
|
};
|
|
27674
28103
|
}
|
|
27675
28104
|
case "interval": {
|
|
@@ -27689,7 +28118,7 @@ Current default: ${DEFAULT_SHADOW_INTERVAL_MS}ms (30 seconds)`
|
|
|
27689
28118
|
opts.shadowController?.setDefaults?.({ intervalMs: ms });
|
|
27690
28119
|
return {
|
|
27691
28120
|
message: `/shadow interval ${ms}ms
|
|
27692
|
-
${
|
|
28121
|
+
${color50.dim("Interval will be applied on next /shadow start")}`
|
|
27693
28122
|
};
|
|
27694
28123
|
}
|
|
27695
28124
|
default: {
|
|
@@ -27962,7 +28391,7 @@ function buildStatuslineCommand(deps) {
|
|
|
27962
28391
|
}
|
|
27963
28392
|
|
|
27964
28393
|
// src/slash-commands/supervisor.ts
|
|
27965
|
-
import { color as
|
|
28394
|
+
import { color as color51 } from "@wrongstack/core/utils";
|
|
27966
28395
|
function fmtAge4(at) {
|
|
27967
28396
|
const s = Math.max(0, Math.round((Date.now() - at) / 1e3));
|
|
27968
28397
|
if (s < 60) return `${s}s ago`;
|
|
@@ -27998,13 +28427,13 @@ function buildSupervisorCommand(opts) {
|
|
|
27998
28427
|
}
|
|
27999
28428
|
if (sub === "on") {
|
|
28000
28429
|
supervisor.start();
|
|
28001
|
-
const msg2 = `Fleet supervisor ${
|
|
28430
|
+
const msg2 = `Fleet supervisor ${color51.green("armed")} \u2014 evaluating every ${Math.round(supervisor.configSnapshot().intervalMs / 1e3)}s.`;
|
|
28002
28431
|
opts.renderer.write(msg2);
|
|
28003
28432
|
return { message: msg2 };
|
|
28004
28433
|
}
|
|
28005
28434
|
if (sub === "off") {
|
|
28006
28435
|
supervisor.stop();
|
|
28007
|
-
const msg2 = `Fleet supervisor ${
|
|
28436
|
+
const msg2 = `Fleet supervisor ${color51.yellow("disarmed")} \u2014 no further automatic interventions this session.`;
|
|
28008
28437
|
opts.renderer.write(msg2);
|
|
28009
28438
|
return { message: msg2 };
|
|
28010
28439
|
}
|
|
@@ -28017,10 +28446,10 @@ function buildSupervisorCommand(opts) {
|
|
|
28017
28446
|
return { message: msg3 };
|
|
28018
28447
|
}
|
|
28019
28448
|
const lines2 = entries.map((e) => {
|
|
28020
|
-
const who = e.subagentId ? ` ${
|
|
28449
|
+
const who = e.subagentId ? ` ${color51.cyan(e.subagentId)}` : "";
|
|
28021
28450
|
const task = e.taskId ? ` task=${e.taskId.slice(0, 8)}` : "";
|
|
28022
|
-
const outcome = e.outcome === "approved" ?
|
|
28023
|
-
return `${
|
|
28451
|
+
const outcome = e.outcome === "approved" ? color51.green(e.outcome) : e.outcome === "denied" || e.outcome === "error" ? color51.red(e.outcome) : color51.yellow(e.outcome);
|
|
28452
|
+
return `${color51.dim(fmtAge4(e.at))} ${e.kind}${who}${task} \u2192 ${e.proposedAction} [${outcome}] ${color51.dim(e.detail)}`;
|
|
28024
28453
|
});
|
|
28025
28454
|
const msg2 = lines2.join("\n");
|
|
28026
28455
|
opts.renderer.write(msg2);
|
|
@@ -28030,12 +28459,12 @@ function buildSupervisorCommand(opts) {
|
|
|
28030
28459
|
const history = supervisor.history();
|
|
28031
28460
|
const last = history[history.length - 1];
|
|
28032
28461
|
const lines = [
|
|
28033
|
-
`Fleet supervisor: ${supervisor.isRunning() ?
|
|
28462
|
+
`Fleet supervisor: ${supervisor.isRunning() ? color51.green("armed") : color51.yellow("disarmed")}`,
|
|
28034
28463
|
` interval ${Math.round(cfg.intervalMs / 1e3)}s \xB7 cooldown ${Math.round(cfg.cooldownMs / 1e3)}s \xB7 max ${cfg.maxInterventionsPerSubagent} interventions/agent`,
|
|
28035
28464
|
` 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
28465
|
` actions: retarget \u2713 \xB7 spawn ${cfg.allowSpawn ? "\u2713" : "\u2717"} \xB7 steer \u2713 \xB7 terminate ${cfg.allowTerminate ? "\u2713" : "\u2717 (config fleet.supervisor.allowTerminate)"}`,
|
|
28037
28466
|
` activity: ${history.length} engagement(s)${last ? ` \u2014 last: ${last.kind} \u2192 ${last.proposedAction} [${last.outcome}] ${fmtAge4(last.at)}` : ""}`,
|
|
28038
|
-
|
|
28467
|
+
color51.dim(" decisions are gated by the Brain \u2014 see /brain (risk ceiling applies)")
|
|
28039
28468
|
];
|
|
28040
28469
|
const msg = lines.join("\n");
|
|
28041
28470
|
opts.renderer.write(msg);
|
|
@@ -28061,6 +28490,7 @@ import {
|
|
|
28061
28490
|
formatTaskProgress,
|
|
28062
28491
|
recordCompletedWorkEvidence
|
|
28063
28492
|
} from "@wrongstack/core/utils";
|
|
28493
|
+
import { todoTool as todoTool2 } from "@wrongstack/tools";
|
|
28064
28494
|
function findTask(tasks, query) {
|
|
28065
28495
|
const asIndex = Number.parseInt(query, 10);
|
|
28066
28496
|
if (!Number.isNaN(asIndex)) {
|
|
@@ -28179,6 +28609,7 @@ ${formatPlan2(updated)}`;
|
|
|
28179
28609
|
}
|
|
28180
28610
|
let outputMessage = "";
|
|
28181
28611
|
let completedTask = null;
|
|
28612
|
+
let todosToReplace = null;
|
|
28182
28613
|
await mutateTasks(taskPath, sessionId, async (file) => {
|
|
28183
28614
|
switch (cmd) {
|
|
28184
28615
|
case "add": {
|
|
@@ -28241,7 +28672,17 @@ ${formatTaskProgress(file.tasks)}`;
|
|
|
28241
28672
|
done: "Completed",
|
|
28242
28673
|
fail: "Failed"
|
|
28243
28674
|
};
|
|
28244
|
-
|
|
28675
|
+
const nextStatus = statusMap[cmd] ?? "pending";
|
|
28676
|
+
if (nextStatus === "in_progress" || nextStatus === "completed") {
|
|
28677
|
+
const unmet = (found.item.dependsOn ?? []).filter(
|
|
28678
|
+
(dependencyId) => file.tasks.find((task) => task.id === dependencyId)?.status !== "completed"
|
|
28679
|
+
);
|
|
28680
|
+
if (unmet.length > 0) {
|
|
28681
|
+
outputMessage = `Cannot mark "${found.item.title}" ${nextStatus}; dependencies are unfinished: ${unmet.join(", ")}.`;
|
|
28682
|
+
return file;
|
|
28683
|
+
}
|
|
28684
|
+
}
|
|
28685
|
+
found.item.status = nextStatus;
|
|
28245
28686
|
found.item.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
28246
28687
|
if (cmd === "done") {
|
|
28247
28688
|
completedTask = { id: found.item.id, title: found.item.title };
|
|
@@ -28267,6 +28708,15 @@ ${formatTaskProgress(file.tasks)}`;
|
|
|
28267
28708
|
outputMessage = `No task matched "${targetId}".`;
|
|
28268
28709
|
return file;
|
|
28269
28710
|
}
|
|
28711
|
+
if (newStatus === "in_progress" || newStatus === "review" || newStatus === "completed") {
|
|
28712
|
+
const unmet = (found.item.dependsOn ?? []).filter(
|
|
28713
|
+
(dependencyId) => file.tasks.find((task) => task.id === dependencyId)?.status !== "completed"
|
|
28714
|
+
);
|
|
28715
|
+
if (unmet.length > 0) {
|
|
28716
|
+
outputMessage = `Cannot mark "${found.item.title}" ${newStatus}; dependencies are unfinished: ${unmet.join(", ")}.`;
|
|
28717
|
+
return file;
|
|
28718
|
+
}
|
|
28719
|
+
}
|
|
28270
28720
|
found.item.status = newStatus;
|
|
28271
28721
|
found.item.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
28272
28722
|
if (newStatus === "completed") {
|
|
@@ -28290,6 +28740,21 @@ ${formatTaskProgress(file.tasks)}`;
|
|
|
28290
28740
|
outputMessage = `No task matched "${targetId}".`;
|
|
28291
28741
|
return file;
|
|
28292
28742
|
}
|
|
28743
|
+
const knownIds = new Set(file.tasks.map((task) => task.id));
|
|
28744
|
+
const missing = depIds.filter((dependencyId) => !knownIds.has(dependencyId));
|
|
28745
|
+
if (missing.length > 0) {
|
|
28746
|
+
outputMessage = `Unknown dependency task IDs: ${missing.join(", ")}.`;
|
|
28747
|
+
return file;
|
|
28748
|
+
}
|
|
28749
|
+
if (found.item.status === "in_progress" || found.item.status === "review" || found.item.status === "completed") {
|
|
28750
|
+
const unmet = depIds.filter(
|
|
28751
|
+
(dependencyId) => file.tasks.find((task) => task.id === dependencyId)?.status !== "completed"
|
|
28752
|
+
);
|
|
28753
|
+
if (unmet.length > 0) {
|
|
28754
|
+
outputMessage = `Cannot add unfinished dependencies to active task "${found.item.title}": ${unmet.join(", ")}.`;
|
|
28755
|
+
return file;
|
|
28756
|
+
}
|
|
28757
|
+
}
|
|
28293
28758
|
found.item.dependsOn = depIds;
|
|
28294
28759
|
found.item.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
28295
28760
|
outputMessage = `Dependencies set for "${found.item.title}": ${depIds.join(", ")}`;
|
|
@@ -28322,6 +28787,13 @@ ${formatTaskProgress(file.tasks)}`;
|
|
|
28322
28787
|
outputMessage = `No task matched "${restJoined}".`;
|
|
28323
28788
|
return file;
|
|
28324
28789
|
}
|
|
28790
|
+
const unmet = (found.item.dependsOn ?? []).filter(
|
|
28791
|
+
(dependencyId) => file.tasks.find((task) => task.id === dependencyId)?.status !== "completed"
|
|
28792
|
+
);
|
|
28793
|
+
if (unmet.length > 0) {
|
|
28794
|
+
outputMessage = `Cannot promote "${found.item.title}"; dependencies are unfinished: ${unmet.join(", ")}.`;
|
|
28795
|
+
return file;
|
|
28796
|
+
}
|
|
28325
28797
|
found.item.status = "in_progress";
|
|
28326
28798
|
found.item.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
28327
28799
|
const todos = [
|
|
@@ -28344,7 +28816,7 @@ ${formatTaskProgress(file.tasks)}`;
|
|
|
28344
28816
|
const existing = ctx.state.todos.filter(
|
|
28345
28817
|
(t) => t.promotedFromTask !== found.item.id
|
|
28346
28818
|
);
|
|
28347
|
-
|
|
28819
|
+
todosToReplace = [...existing, ...todos];
|
|
28348
28820
|
outputMessage = `Promoted to ${todos.length} todo(s): "${found.item.title}"
|
|
28349
28821
|
|
|
28350
28822
|
${formatTaskProgress(file.tasks)}`;
|
|
@@ -28356,6 +28828,11 @@ ${formatTaskProgress(file.tasks)}`;
|
|
|
28356
28828
|
outputMessage = "Tasks were already empty.";
|
|
28357
28829
|
return file;
|
|
28358
28830
|
}
|
|
28831
|
+
const unfinished = file.tasks.filter((task) => task.status !== "completed");
|
|
28832
|
+
if (unfinished.length > 0) {
|
|
28833
|
+
outputMessage = `Cannot clear unfinished tasks: ${unfinished.map((task) => task.id).join(", ")}. Complete them first.`;
|
|
28834
|
+
return file;
|
|
28835
|
+
}
|
|
28359
28836
|
file.tasks = [];
|
|
28360
28837
|
outputMessage = `Cleared ${n} task${n === 1 ? "" : "s"}.`;
|
|
28361
28838
|
break;
|
|
@@ -28382,6 +28859,11 @@ ${formatTaskProgress(file.tasks)}`;
|
|
|
28382
28859
|
}
|
|
28383
28860
|
return file;
|
|
28384
28861
|
});
|
|
28862
|
+
if (todosToReplace) {
|
|
28863
|
+
await todoTool2.execute({ todos: todosToReplace }, ctx, {
|
|
28864
|
+
signal: AbortSignal.timeout(3e4)
|
|
28865
|
+
});
|
|
28866
|
+
}
|
|
28385
28867
|
if (completedTask) {
|
|
28386
28868
|
const done = completedTask;
|
|
28387
28869
|
try {
|
|
@@ -28411,7 +28893,7 @@ ${formatTaskProgress(file.tasks)}`;
|
|
|
28411
28893
|
// src/slash-commands/techstack.ts
|
|
28412
28894
|
import * as fs18 from "node:fs/promises";
|
|
28413
28895
|
import * as path28 from "node:path";
|
|
28414
|
-
import { color as
|
|
28896
|
+
import { color as color52, toErrorMessage as toErrorMessage26 } from "@wrongstack/core/utils";
|
|
28415
28897
|
async function discoverPackageFiles(projectRoot) {
|
|
28416
28898
|
const files = [];
|
|
28417
28899
|
const rootPkg = path28.join(projectRoot, "package.json");
|
|
@@ -28561,10 +29043,10 @@ function buildTechStackCommand(opts) {
|
|
|
28561
29043
|
" 1. Reads every package.json in the project",
|
|
28562
29044
|
" 2. Looks up latest versions on the npm registry",
|
|
28563
29045
|
" 3. Flags outdated, dead, or obsolete packages",
|
|
28564
|
-
` 4. Writes a ${
|
|
29046
|
+
` 4. Writes a ${color52.cyan("techstack.md")} (or .json) report to the project root`,
|
|
28565
29047
|
"",
|
|
28566
29048
|
"Uses the `tech-stack` skill for version verification rules.",
|
|
28567
|
-
`Hooked into ${
|
|
29049
|
+
`Hooked into ${color52.cyan("/init")} \u2014 runs automatically on first project setup.`
|
|
28568
29050
|
].join("\n"),
|
|
28569
29051
|
async run(args, _ctx) {
|
|
28570
29052
|
const trimmed = args.trim().toLowerCase();
|
|
@@ -28648,12 +29130,12 @@ function buildTechStackCommand(opts) {
|
|
|
28648
29130
|
try {
|
|
28649
29131
|
packageFiles = await discoverPackageFiles(opts.projectRoot);
|
|
28650
29132
|
if (packageFiles.length === 0) {
|
|
28651
|
-
discoveryNote =
|
|
29133
|
+
discoveryNote = color52.amber(
|
|
28652
29134
|
"\u26A0 No package.json files found. This does not look like a Node.js project."
|
|
28653
29135
|
);
|
|
28654
29136
|
}
|
|
28655
29137
|
} catch (err) {
|
|
28656
|
-
discoveryNote =
|
|
29138
|
+
discoveryNote = color52.red(`Could not scan for package files: ${toErrorMessage26(err)}`);
|
|
28657
29139
|
}
|
|
28658
29140
|
const task = buildTechStackTask({
|
|
28659
29141
|
projectRoot: opts.projectRoot,
|
|
@@ -28676,11 +29158,11 @@ function buildTechStackCommand(opts) {
|
|
|
28676
29158
|
};
|
|
28677
29159
|
}
|
|
28678
29160
|
const header = isInit ? "Tech Stack Init Audit" : "Tech Stack Audit";
|
|
28679
|
-
const label = `${
|
|
29161
|
+
const label = `${color52.cyan("\u{1F50D}")} ${color52.bold(header)} ${color52.dim(`(${packageFiles.length} package files)`)}`;
|
|
28680
29162
|
opts.renderer.write(label);
|
|
28681
29163
|
if (discoveryNote) opts.renderer.write(discoveryNote);
|
|
28682
29164
|
opts.renderer.write(
|
|
28683
|
-
|
|
29165
|
+
color52.dim(
|
|
28684
29166
|
`Spawning tech-stack subagent \u2192 writes ${outputFormat === "json" ? "techstack.json" : "techstack.md"} when done.`
|
|
28685
29167
|
)
|
|
28686
29168
|
);
|
|
@@ -28702,10 +29184,10 @@ function buildTechStackCommand(opts) {
|
|
|
28702
29184
|
}
|
|
28703
29185
|
|
|
28704
29186
|
// src/slash-commands/telegram-settings.ts
|
|
28705
|
-
import { color as
|
|
29187
|
+
import { color as color54, toErrorMessage as toErrorMessage27 } from "@wrongstack/core/utils";
|
|
28706
29188
|
|
|
28707
29189
|
// src/slash-commands/telegram-setup.ts
|
|
28708
|
-
import { color as
|
|
29190
|
+
import { color as color53 } from "@wrongstack/core/utils";
|
|
28709
29191
|
|
|
28710
29192
|
// src/slash-commands/telegram-pairing.ts
|
|
28711
29193
|
var DISCOVERY_LIMIT = 25;
|
|
@@ -28821,31 +29303,31 @@ function buildTelegramSetupCommand(opts) {
|
|
|
28821
29303
|
if (BOT_TOKEN_RE.test(first)) {
|
|
28822
29304
|
return {
|
|
28823
29305
|
message: [
|
|
28824
|
-
`${
|
|
28825
|
-
`Run ${
|
|
29306
|
+
`${color53.red("\u2717")} Bot tokens are no longer accepted as slash-command arguments.`,
|
|
29307
|
+
`Run ${color53.cyan("/telegram-setup [chatId]")} and enter it at the masked prompt.`
|
|
28826
29308
|
].join("\n")
|
|
28827
29309
|
};
|
|
28828
29310
|
}
|
|
28829
29311
|
if (parts.length > 1) {
|
|
28830
|
-
return { message: `${
|
|
29312
|
+
return { message: `${color53.amber("Usage:")} /telegram-setup [chatId]` };
|
|
28831
29313
|
}
|
|
28832
29314
|
if (!opts.readSecret || !opts.vault || !opts.paths?.globalConfig) {
|
|
28833
29315
|
return {
|
|
28834
|
-
message: `${
|
|
29316
|
+
message: `${color53.red("\u2717")} Secure Telegram setup is unavailable in this session.`
|
|
28835
29317
|
};
|
|
28836
29318
|
}
|
|
28837
29319
|
let botToken;
|
|
28838
29320
|
try {
|
|
28839
|
-
botToken = (await opts.readSecret(`Telegram bot token ${
|
|
29321
|
+
botToken = (await opts.readSecret(`Telegram bot token ${color53.dim("(hidden, paste OK)")}: `)).trim();
|
|
28840
29322
|
} catch {
|
|
28841
|
-
return { message:
|
|
29323
|
+
return { message: color53.dim("Telegram setup cancelled.") };
|
|
28842
29324
|
}
|
|
28843
|
-
if (!botToken) return { message:
|
|
29325
|
+
if (!botToken) return { message: color53.dim("Telegram setup cancelled.") };
|
|
28844
29326
|
if (!BOT_TOKEN_RE.test(botToken)) {
|
|
28845
29327
|
return {
|
|
28846
29328
|
message: [
|
|
28847
|
-
`${
|
|
28848
|
-
`Expected: ${
|
|
29329
|
+
`${color53.red("\u2717")} Invalid token format.`,
|
|
29330
|
+
`Expected: ${color53.dim("123456789:ABCdefGHIjkl...")}`,
|
|
28849
29331
|
"",
|
|
28850
29332
|
"Get a valid token from @BotFather on Telegram."
|
|
28851
29333
|
].join("\n")
|
|
@@ -28860,7 +29342,7 @@ function buildTelegramSetupCommand(opts) {
|
|
|
28860
29342
|
} catch {
|
|
28861
29343
|
return {
|
|
28862
29344
|
message: [
|
|
28863
|
-
`${
|
|
29345
|
+
`${color53.red("\u2717")} Could not reach Telegram API.`,
|
|
28864
29346
|
"",
|
|
28865
29347
|
"Check your network connection and try again."
|
|
28866
29348
|
].join("\n")
|
|
@@ -28869,7 +29351,7 @@ function buildTelegramSetupCommand(opts) {
|
|
|
28869
29351
|
if (!botInfo.ok || !botInfo.result) {
|
|
28870
29352
|
return {
|
|
28871
29353
|
message: [
|
|
28872
|
-
`${
|
|
29354
|
+
`${color53.red("\u2717")} Invalid bot token.`,
|
|
28873
29355
|
"",
|
|
28874
29356
|
"Get a valid token from @BotFather on Telegram."
|
|
28875
29357
|
].join("\n")
|
|
@@ -28879,13 +29361,13 @@ function buildTelegramSetupCommand(opts) {
|
|
|
28879
29361
|
const classifiedChatId = chatId ? classifyTelegramChatId(chatId) : void 0;
|
|
28880
29362
|
if (classifiedChatId?.kind === "invalid") {
|
|
28881
29363
|
return {
|
|
28882
|
-
message: `${
|
|
29364
|
+
message: `${color53.red("\u2717")} Invalid Telegram chat ID. Expected a positive private chat ID.`
|
|
28883
29365
|
};
|
|
28884
29366
|
}
|
|
28885
29367
|
if (classifiedChatId?.kind === "group") {
|
|
28886
29368
|
return {
|
|
28887
29369
|
message: [
|
|
28888
|
-
`${
|
|
29370
|
+
`${color53.amber("\u26A0")} Shared group, supergroup, and channel IDs cannot be paired by manual ID.`,
|
|
28889
29371
|
"Run /telegram-setup without a chat ID and select a discovered private identity.",
|
|
28890
29372
|
"No configuration was changed."
|
|
28891
29373
|
].join("\n")
|
|
@@ -28899,7 +29381,7 @@ function buildTelegramSetupCommand(opts) {
|
|
|
28899
29381
|
} catch {
|
|
28900
29382
|
return {
|
|
28901
29383
|
message: [
|
|
28902
|
-
`${
|
|
29384
|
+
`${color53.red("\u2717")} Could not discover recent Telegram chats.`,
|
|
28903
29385
|
"Message the bot once, then run /telegram-setup again.",
|
|
28904
29386
|
"No configuration was changed."
|
|
28905
29387
|
].join("\n")
|
|
@@ -28908,7 +29390,7 @@ function buildTelegramSetupCommand(opts) {
|
|
|
28908
29390
|
if (candidates.length === 0) {
|
|
28909
29391
|
return {
|
|
28910
29392
|
message: [
|
|
28911
|
-
`${
|
|
29393
|
+
`${color53.amber("No recent chats found.")}`,
|
|
28912
29394
|
"Message the bot from the private account you want to pair, then run setup again.",
|
|
28913
29395
|
"No configuration was changed."
|
|
28914
29396
|
].join("\n")
|
|
@@ -28916,31 +29398,31 @@ function buildTelegramSetupCommand(opts) {
|
|
|
28916
29398
|
}
|
|
28917
29399
|
opts.renderer.write(
|
|
28918
29400
|
[
|
|
28919
|
-
|
|
29401
|
+
color53.bold("Recent Telegram identities"),
|
|
28920
29402
|
formatTelegramPairingCandidates(candidates),
|
|
28921
29403
|
"",
|
|
28922
|
-
|
|
29404
|
+
color53.dim("Choose a private candidate number, or press Enter to cancel.")
|
|
28923
29405
|
].join("\n")
|
|
28924
29406
|
);
|
|
28925
29407
|
let choiceInput;
|
|
28926
29408
|
try {
|
|
28927
29409
|
choiceInput = opts.readText ? await opts.readText("Pair candidate \u203A ") : await opts.reader.readLine("Pair candidate \u203A ");
|
|
28928
29410
|
} catch {
|
|
28929
|
-
return { message:
|
|
29411
|
+
return { message: color53.dim("Telegram setup cancelled. No configuration was changed.") };
|
|
28930
29412
|
}
|
|
28931
29413
|
const choice = parseTelegramPairingChoice(choiceInput, candidates);
|
|
28932
29414
|
if (choice.kind === "cancel") {
|
|
28933
|
-
return { message:
|
|
29415
|
+
return { message: color53.dim("Telegram setup cancelled. No configuration was changed.") };
|
|
28934
29416
|
}
|
|
28935
29417
|
if (choice.kind === "invalid") {
|
|
28936
29418
|
return {
|
|
28937
|
-
message: `${
|
|
29419
|
+
message: `${color53.red("\u2717")} Invalid pairing choice. No configuration was changed.`
|
|
28938
29420
|
};
|
|
28939
29421
|
}
|
|
28940
29422
|
if (!choice.candidate.eligible) {
|
|
28941
29423
|
return {
|
|
28942
29424
|
message: [
|
|
28943
|
-
`${
|
|
29425
|
+
`${color53.amber("\u26A0")} Shared, group, or ambiguous identities are not paired automatically.`,
|
|
28944
29426
|
"Use a private chat where chat_id and user_id identify the same account.",
|
|
28945
29427
|
"No configuration was changed."
|
|
28946
29428
|
].join("\n")
|
|
@@ -28982,7 +29464,7 @@ function buildTelegramSetupCommand(opts) {
|
|
|
28982
29464
|
} catch {
|
|
28983
29465
|
return {
|
|
28984
29466
|
message: [
|
|
28985
|
-
`${
|
|
29467
|
+
`${color53.red("\u2717")} Failed to save Telegram configuration.`,
|
|
28986
29468
|
"The token was not printed. Check the config path and vault, then try again."
|
|
28987
29469
|
].join("\n")
|
|
28988
29470
|
};
|
|
@@ -28990,15 +29472,15 @@ function buildTelegramSetupCommand(opts) {
|
|
|
28990
29472
|
const bot = botInfo.result;
|
|
28991
29473
|
return {
|
|
28992
29474
|
message: [
|
|
28993
|
-
`${
|
|
29475
|
+
`${color53.green("\u2713")} Telegram configured successfully.`,
|
|
28994
29476
|
"",
|
|
28995
|
-
`Bot: ${
|
|
29477
|
+
`Bot: ${color53.bold(`@${bot.username ?? bot.first_name}`)}`,
|
|
28996
29478
|
...pairedCandidate ? [
|
|
28997
|
-
`Paired private chat: ${
|
|
28998
|
-
`Paired user: ${
|
|
28999
|
-
] : chatId ? [`Default chat: ${
|
|
29479
|
+
`Paired private chat: ${color53.green(String(pairedCandidate.chatId))}`,
|
|
29480
|
+
`Paired user: ${color53.green(String(pairedCandidate.userId))}`
|
|
29481
|
+
] : chatId ? [`Default chat: ${color53.green(chatId)}`] : [],
|
|
29000
29482
|
"",
|
|
29001
|
-
`${
|
|
29483
|
+
`${color53.amber("\u26A0")} Restart WrongStack for the plugin to load the new token.`
|
|
29002
29484
|
].join("\n")
|
|
29003
29485
|
};
|
|
29004
29486
|
}
|
|
@@ -29032,15 +29514,15 @@ function buildTelegramSettingsCommand(opts) {
|
|
|
29032
29514
|
const chat = tg.notifyChatId !== void 0 && tg.notifyChatId !== null ? String(tg.notifyChatId) : "not set";
|
|
29033
29515
|
const hasToken = typeof tg.botToken === "string" && tg.botToken.length > 0;
|
|
29034
29516
|
return [
|
|
29035
|
-
`${
|
|
29517
|
+
`${color54.bold("Telegram")} ${color54.dim("\u2014 Notification Settings")}`,
|
|
29036
29518
|
"",
|
|
29037
|
-
` session end: ${sessionEnd ?
|
|
29038
|
-
` delegate done: ${delegate ?
|
|
29039
|
-
` long tool: ${
|
|
29040
|
-
` poll interval: ${
|
|
29041
|
-
` notify chat: ${
|
|
29519
|
+
` session end: ${sessionEnd ? color54.cyan("on") : color54.dim("off")} ${color54.dim("change: /telegram-settings session-end on|off")}`,
|
|
29520
|
+
` delegate done: ${delegate ? color54.cyan("on") : color54.dim("off")} ${color54.dim("change: /telegram-settings delegate on|off")}`,
|
|
29521
|
+
` long tool: ${color54.cyan(longTool)} ${color54.dim("change: /telegram-settings long-tool <ms|off>")}`,
|
|
29522
|
+
` poll interval: ${color54.cyan(poll)} ${color54.dim("change: /telegram-settings poll <seconds>")}`,
|
|
29523
|
+
` notify chat: ${color54.cyan(chat)} ${color54.dim("change: /telegram-settings chat <chatId>")}`,
|
|
29042
29524
|
"",
|
|
29043
|
-
hasToken ?
|
|
29525
|
+
hasToken ? color54.dim(" Bot token configured. Changes apply immediately.") : `${color54.amber("\u26A0")} No bot token configured. Run: /telegram-setup <botToken> [chatId]`
|
|
29044
29526
|
].join("\n");
|
|
29045
29527
|
}
|
|
29046
29528
|
return {
|
|
@@ -29056,7 +29538,7 @@ function buildTelegramSettingsCommand(opts) {
|
|
|
29056
29538
|
return { message: HELP2 };
|
|
29057
29539
|
}
|
|
29058
29540
|
if (!opts.configStore || !opts.paths?.globalConfig || !opts.vault) {
|
|
29059
|
-
return { message: `${
|
|
29541
|
+
return { message: `${color54.red("Error")} secure config persistence not available.` };
|
|
29060
29542
|
}
|
|
29061
29543
|
if (!sub) {
|
|
29062
29544
|
return { message: currentView() };
|
|
@@ -29070,7 +29552,7 @@ function buildTelegramSettingsCommand(opts) {
|
|
|
29070
29552
|
if (sub === "all") {
|
|
29071
29553
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
29072
29554
|
if (!["on", "off"].includes(raw)) {
|
|
29073
|
-
return { message: `${
|
|
29555
|
+
return { message: `${color54.amber("Usage:")} /telegram-settings all on|off` };
|
|
29074
29556
|
}
|
|
29075
29557
|
const on = raw === "on";
|
|
29076
29558
|
await persistTelegramConfig(persistDeps, (tg) => {
|
|
@@ -29078,40 +29560,40 @@ function buildTelegramSettingsCommand(opts) {
|
|
|
29078
29560
|
tg.notifyOnDelegate = on;
|
|
29079
29561
|
});
|
|
29080
29562
|
return {
|
|
29081
|
-
message: `${
|
|
29563
|
+
message: `${color54.green("\u2713")} all event notifications \u2192 ${on ? color54.cyan("on") : color54.dim("off")} ${color54.dim("(session-end, delegate)")}`
|
|
29082
29564
|
};
|
|
29083
29565
|
}
|
|
29084
29566
|
if (sub === "session-end") {
|
|
29085
29567
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
29086
29568
|
if (!["on", "off"].includes(raw)) {
|
|
29087
|
-
return { message: `${
|
|
29569
|
+
return { message: `${color54.amber("Usage:")} /telegram-settings session-end on|off` };
|
|
29088
29570
|
}
|
|
29089
29571
|
const on = raw === "on";
|
|
29090
29572
|
await persistTelegramConfig(persistDeps, (tg) => {
|
|
29091
29573
|
tg.notifyOnSessionEnd = on;
|
|
29092
29574
|
});
|
|
29093
29575
|
return {
|
|
29094
|
-
message: `${
|
|
29576
|
+
message: `${color54.green("\u2713")} session-end \u2192 ${on ? color54.cyan("on") : color54.dim("off")}`
|
|
29095
29577
|
};
|
|
29096
29578
|
}
|
|
29097
29579
|
if (sub === "delegate") {
|
|
29098
29580
|
const raw = (rest[0] ?? "").toLowerCase();
|
|
29099
29581
|
if (!["on", "off"].includes(raw)) {
|
|
29100
|
-
return { message: `${
|
|
29582
|
+
return { message: `${color54.amber("Usage:")} /telegram-settings delegate on|off` };
|
|
29101
29583
|
}
|
|
29102
29584
|
const on = raw === "on";
|
|
29103
29585
|
await persistTelegramConfig(persistDeps, (tg) => {
|
|
29104
29586
|
tg.notifyOnDelegate = on;
|
|
29105
29587
|
});
|
|
29106
29588
|
return {
|
|
29107
|
-
message: `${
|
|
29589
|
+
message: `${color54.green("\u2713")} delegate \u2192 ${on ? color54.cyan("on") : color54.dim("off")}`
|
|
29108
29590
|
};
|
|
29109
29591
|
}
|
|
29110
29592
|
if (sub === "long-tool") {
|
|
29111
29593
|
const raw = rest[0];
|
|
29112
29594
|
if (raw === void 0) {
|
|
29113
29595
|
return {
|
|
29114
|
-
message: `${
|
|
29596
|
+
message: `${color54.amber("Usage:")} /telegram-settings long-tool <ms|off> ${color54.dim("(0 or off disables)")}`
|
|
29115
29597
|
};
|
|
29116
29598
|
}
|
|
29117
29599
|
if (raw === "off") {
|
|
@@ -29119,57 +29601,57 @@ function buildTelegramSettingsCommand(opts) {
|
|
|
29119
29601
|
tg.longToolThresholdMs = 0;
|
|
29120
29602
|
});
|
|
29121
29603
|
return {
|
|
29122
|
-
message: `${
|
|
29604
|
+
message: `${color54.green("\u2713")} long-tool \u2192 ${color54.dim("off")}`
|
|
29123
29605
|
};
|
|
29124
29606
|
}
|
|
29125
29607
|
const ms = Number.parseInt(raw, 10);
|
|
29126
29608
|
if (Number.isNaN(ms) || ms < 0) {
|
|
29127
29609
|
return {
|
|
29128
|
-
message: `${
|
|
29610
|
+
message: `${color54.red("Invalid number")}: "${raw}". Enter milliseconds, e.g. /telegram-settings long-tool 15000`
|
|
29129
29611
|
};
|
|
29130
29612
|
}
|
|
29131
29613
|
await persistTelegramConfig(persistDeps, (tg) => {
|
|
29132
29614
|
tg.longToolThresholdMs = ms;
|
|
29133
29615
|
});
|
|
29134
29616
|
return {
|
|
29135
|
-
message: `${
|
|
29617
|
+
message: `${color54.green("\u2713")} long-tool \u2192 ${color54.cyan(`${ms}ms`)}`
|
|
29136
29618
|
};
|
|
29137
29619
|
}
|
|
29138
29620
|
if (sub === "poll") {
|
|
29139
29621
|
const raw = rest[0];
|
|
29140
29622
|
if (raw === void 0) {
|
|
29141
29623
|
return {
|
|
29142
|
-
message: `${
|
|
29624
|
+
message: `${color54.amber("Usage:")} /telegram-settings poll <seconds> ${color54.dim("(1\u201360)")}`
|
|
29143
29625
|
};
|
|
29144
29626
|
}
|
|
29145
29627
|
const sec = Number.parseInt(raw, 10);
|
|
29146
29628
|
if (Number.isNaN(sec) || sec < 1 || sec > 60) {
|
|
29147
29629
|
return {
|
|
29148
|
-
message: `${
|
|
29630
|
+
message: `${color54.red("Invalid value")}: "${raw}". Enter seconds between 1 and 60.`
|
|
29149
29631
|
};
|
|
29150
29632
|
}
|
|
29151
29633
|
await persistTelegramConfig(persistDeps, (tg) => {
|
|
29152
29634
|
tg.pollIntervalSec = sec;
|
|
29153
29635
|
});
|
|
29154
29636
|
return {
|
|
29155
|
-
message: `${
|
|
29637
|
+
message: `${color54.green("\u2713")} poll \u2192 ${color54.cyan(`${sec}s`)}`
|
|
29156
29638
|
};
|
|
29157
29639
|
}
|
|
29158
29640
|
if (sub === "chat") {
|
|
29159
29641
|
const raw = rest[0];
|
|
29160
29642
|
if (!raw) {
|
|
29161
|
-
return { message: `${
|
|
29643
|
+
return { message: `${color54.amber("Usage:")} /telegram-settings chat <chatId>` };
|
|
29162
29644
|
}
|
|
29163
29645
|
const classification = classifyTelegramChatId(raw);
|
|
29164
29646
|
if (classification.kind === "invalid") {
|
|
29165
|
-
return { message: `${
|
|
29647
|
+
return { message: `${color54.red("Invalid chat ID")}: expected a non-zero integer.` };
|
|
29166
29648
|
}
|
|
29167
29649
|
const current = opts.configStore.get();
|
|
29168
29650
|
const allowGroupChats = current.extensions?.telegram?.allowGroupChats === true;
|
|
29169
29651
|
if (classification.kind === "group" && !allowGroupChats) {
|
|
29170
29652
|
return {
|
|
29171
29653
|
message: [
|
|
29172
|
-
`${
|
|
29654
|
+
`${color54.amber("\u26A0")} Group, supergroup, and channel targets require explicit allowGroupChats=true.`,
|
|
29173
29655
|
"No configuration was changed."
|
|
29174
29656
|
].join("\n")
|
|
29175
29657
|
};
|
|
@@ -29192,15 +29674,15 @@ function buildTelegramSettingsCommand(opts) {
|
|
|
29192
29674
|
}
|
|
29193
29675
|
});
|
|
29194
29676
|
return {
|
|
29195
|
-
message: `${
|
|
29677
|
+
message: `${color54.green("\u2713")} notify chat \u2192 ${color54.cyan(raw)}`
|
|
29196
29678
|
};
|
|
29197
29679
|
}
|
|
29198
29680
|
return {
|
|
29199
|
-
message: `${
|
|
29681
|
+
message: `${color54.red("Unknown setting")} "${sub}". ${unknownSubcommand(sub, ["session-end", "delegate", "long-tool", "poll", "chat", "all"], "telegram-settings")}`
|
|
29200
29682
|
};
|
|
29201
29683
|
} catch (err) {
|
|
29202
29684
|
return {
|
|
29203
|
-
message: `${
|
|
29685
|
+
message: `${color54.red("Settings error")}: ${toErrorMessage27(err)}`
|
|
29204
29686
|
};
|
|
29205
29687
|
}
|
|
29206
29688
|
}
|
|
@@ -29210,6 +29692,7 @@ function buildTelegramSettingsCommand(opts) {
|
|
|
29210
29692
|
// src/slash-commands/todos.ts
|
|
29211
29693
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
29212
29694
|
import { formatTodosList as formatTodosList2 } from "@wrongstack/core/utils";
|
|
29695
|
+
import { todoTool as todoTool3 } from "@wrongstack/tools";
|
|
29213
29696
|
function findTodo(todos, query) {
|
|
29214
29697
|
const asIndex = Number.parseInt(query, 10);
|
|
29215
29698
|
if (!Number.isNaN(asIndex)) {
|
|
@@ -29240,73 +29723,105 @@ function buildTodosCommand(opts) {
|
|
|
29240
29723
|
if (!ctx) return { message: "No active context." };
|
|
29241
29724
|
const { cmd, rest } = parseSubcommand(args);
|
|
29242
29725
|
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
|
-
|
|
29726
|
+
const updateTodos = (todos) => todoTool3.execute({ todos }, ctx, { signal: AbortSignal.timeout(3e4) });
|
|
29727
|
+
const isManagedProjection = (todo) => Boolean(todo.kanbanBoardId && todo.kanbanTaskId);
|
|
29728
|
+
try {
|
|
29729
|
+
switch (cmd) {
|
|
29730
|
+
case "":
|
|
29731
|
+
case "show":
|
|
29732
|
+
case "list": {
|
|
29733
|
+
return { message: formatTodosList2(ctx.todos) };
|
|
29734
|
+
}
|
|
29735
|
+
case "clear": {
|
|
29736
|
+
const n = ctx.todos.length;
|
|
29737
|
+
if (n === 0) return { message: "Todos were already empty." };
|
|
29738
|
+
if (ctx.todos.some(isManagedProjection)) {
|
|
29739
|
+
return {
|
|
29740
|
+
message: "Kanban-bound todos are task projections. Change or remove the tasks from Kanban."
|
|
29741
|
+
};
|
|
29742
|
+
}
|
|
29743
|
+
ctx.state.replaceTodos([]);
|
|
29744
|
+
return { message: `Cleared ${n} todo${n === 1 ? "" : "s"}.` };
|
|
29745
|
+
}
|
|
29746
|
+
case "add": {
|
|
29747
|
+
if (!restJoined) return { message: "Usage: /todos add <text>" };
|
|
29748
|
+
if (ctx.todos.some(isManagedProjection)) {
|
|
29749
|
+
return { message: "This Todo list is a Kanban projection. Add the task in Kanban." };
|
|
29750
|
+
}
|
|
29751
|
+
const item = {
|
|
29752
|
+
id: `todo_${Date.now()}_${randomUUID7().slice(0, 7)}`,
|
|
29753
|
+
content: restJoined,
|
|
29754
|
+
status: "pending"
|
|
29755
|
+
};
|
|
29756
|
+
await updateTodos([...ctx.todos, item]);
|
|
29757
|
+
return { message: `Added: ${restJoined}` };
|
|
29758
|
+
}
|
|
29759
|
+
case "done-all":
|
|
29760
|
+
case "complete-all": {
|
|
29761
|
+
const pending = ctx.todos.filter((t) => t.status !== "completed");
|
|
29762
|
+
if (pending.length === 0) return { message: "No pending todos to complete." };
|
|
29763
|
+
if (ctx.todos.some(isManagedProjection)) {
|
|
29764
|
+
return {
|
|
29765
|
+
message: "Kanban-bound todos must complete through their real task lifecycle; complete the active task individually."
|
|
29766
|
+
};
|
|
29767
|
+
}
|
|
29768
|
+
await updateTodos(
|
|
29769
|
+
ctx.todos.map(
|
|
29770
|
+
(t) => t.status === "completed" ? t : { ...t, status: "completed" }
|
|
29771
|
+
)
|
|
29772
|
+
);
|
|
29773
|
+
return {
|
|
29774
|
+
message: `Marked ${pending.length} todo${pending.length === 1 ? "" : "s"} done.`
|
|
29775
|
+
};
|
|
29776
|
+
}
|
|
29777
|
+
case "done":
|
|
29778
|
+
case "complete": {
|
|
29779
|
+
if (!restJoined) return { message: "Usage: /todos done <id|index>" };
|
|
29780
|
+
const found = findTodo(ctx.todos, restJoined);
|
|
29781
|
+
if (!found) return { message: `No todo matched "${restJoined}".` };
|
|
29782
|
+
const doneItem = { ...found.item, status: "completed" };
|
|
29783
|
+
const nextTodos = [
|
|
29784
|
+
...ctx.todos.slice(0, found.idx),
|
|
29785
|
+
doneItem,
|
|
29786
|
+
...ctx.todos.slice(found.idx + 1)
|
|
29787
|
+
];
|
|
29788
|
+
const result = await updateTodos(nextTodos);
|
|
29789
|
+
const projected = ctx.todos.find((todo) => todo.id === doneItem.id);
|
|
29790
|
+
if (isManagedProjection(doneItem) && projected?.status !== "completed") {
|
|
29791
|
+
return {
|
|
29792
|
+
message: result.kanban_warnings?.[0] ?? `Kanban kept ${doneItem.content} at ${projected?.status ?? "its current state"}.`
|
|
29793
|
+
};
|
|
29794
|
+
}
|
|
29795
|
+
return { message: `Marked done: ${doneItem.content}` };
|
|
29796
|
+
}
|
|
29797
|
+
case "remove":
|
|
29798
|
+
case "rm":
|
|
29799
|
+
case "delete": {
|
|
29800
|
+
if (!restJoined) return { message: "Usage: /todos remove <id|index>" };
|
|
29801
|
+
const found = findTodo(ctx.todos, restJoined);
|
|
29802
|
+
if (!found) return { message: `No todo matched "${restJoined}".` };
|
|
29803
|
+
if (isManagedProjection(found.item)) {
|
|
29804
|
+
return {
|
|
29805
|
+
message: "Kanban-bound todos are task projections. Remove the task from Kanban instead."
|
|
29806
|
+
};
|
|
29807
|
+
}
|
|
29808
|
+
const nextTodos = [...ctx.todos.slice(0, found.idx), ...ctx.todos.slice(found.idx + 1)];
|
|
29809
|
+
ctx.state.replaceTodos(nextTodos);
|
|
29810
|
+
return { message: `Removed: ${found.item.content}` };
|
|
29811
|
+
}
|
|
29812
|
+
default:
|
|
29813
|
+
return {
|
|
29814
|
+
message: unknownSubcommand(
|
|
29815
|
+
cmd,
|
|
29816
|
+
["show", "clear", "add", "done", "done-all", "remove"],
|
|
29817
|
+
"todos"
|
|
29818
|
+
) + "\n\nRelated: /plan (session-persistent roadmap) | /tasks (structured tasks with priorities)"
|
|
29819
|
+
};
|
|
29301
29820
|
}
|
|
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
|
-
};
|
|
29821
|
+
} catch (error) {
|
|
29822
|
+
return {
|
|
29823
|
+
message: `Todo update failed: ${error instanceof Error ? error.message : String(error)}`
|
|
29824
|
+
};
|
|
29310
29825
|
}
|
|
29311
29826
|
}
|
|
29312
29827
|
};
|
|
@@ -29315,7 +29830,7 @@ function buildTodosCommand(opts) {
|
|
|
29315
29830
|
// src/slash-commands/tool.ts
|
|
29316
29831
|
import { noOpVault as noOpVault10 } from "@wrongstack/core/security";
|
|
29317
29832
|
import {
|
|
29318
|
-
color as
|
|
29833
|
+
color as color55,
|
|
29319
29834
|
getToolDescriptionMode as getToolDescriptionMode2,
|
|
29320
29835
|
getToolResultRenderMode,
|
|
29321
29836
|
normalizeToolDescriptionMode,
|
|
@@ -29329,11 +29844,11 @@ function fit(text, width) {
|
|
|
29329
29844
|
}
|
|
29330
29845
|
function formatDescriptionMode(mode) {
|
|
29331
29846
|
const raw = `desc:${mode}`;
|
|
29332
|
-
return mode === "simple" ?
|
|
29847
|
+
return mode === "simple" ? color55.amber(raw) : color55.cyan(raw);
|
|
29333
29848
|
}
|
|
29334
29849
|
function formatResultRenderMode(mode) {
|
|
29335
29850
|
const raw = `result:${mode}`;
|
|
29336
|
-
return mode === "simple" ?
|
|
29851
|
+
return mode === "simple" ? color55.amber(raw) : color55.cyan(raw);
|
|
29337
29852
|
}
|
|
29338
29853
|
function buildToolCommand(opts) {
|
|
29339
29854
|
const help = [
|
|
@@ -29439,38 +29954,38 @@ function buildToolCommand(opts) {
|
|
|
29439
29954
|
const resultSimple = Object.entries(configured.resultRenderMode ?? {}).filter(([, mode]) => normalizeToolResultRenderMode(mode) === "simple").map(([name]) => name).sort();
|
|
29440
29955
|
const disabled = opts.toolRegistry.listDisabled();
|
|
29441
29956
|
const lines = [
|
|
29442
|
-
`${
|
|
29957
|
+
`${color55.bold("Tool modes")} ${color55.dim("(default: extend on both axes)")}`,
|
|
29443
29958
|
"",
|
|
29444
|
-
`${formatDescriptionMode("simple")}: ${descSimple.length > 0 ? descSimple.map((n) =>
|
|
29445
|
-
`${formatResultRenderMode("simple")}: ${resultSimple.length > 0 ? resultSimple.map((n) =>
|
|
29959
|
+
`${formatDescriptionMode("simple")}: ${descSimple.length > 0 ? descSimple.map((n) => color55.cyan(n)).join(", ") : color55.dim("none")}`,
|
|
29960
|
+
`${formatResultRenderMode("simple")}: ${resultSimple.length > 0 ? resultSimple.map((n) => color55.cyan(n)).join(", ") : color55.dim("none")}`,
|
|
29446
29961
|
""
|
|
29447
29962
|
];
|
|
29448
29963
|
if (disabled.length > 0) {
|
|
29449
29964
|
lines.push(
|
|
29450
|
-
`${
|
|
29965
|
+
`${color55.bold("Disabled tools")}`,
|
|
29451
29966
|
"",
|
|
29452
|
-
` ${
|
|
29967
|
+
` ${color55.red("disabled")}: ${disabled.map(({ tool }) => color55.dim(tool.name)).join(", ")}`,
|
|
29453
29968
|
""
|
|
29454
29969
|
);
|
|
29455
29970
|
}
|
|
29456
29971
|
lines.push(
|
|
29457
|
-
|
|
29972
|
+
color55.dim(
|
|
29458
29973
|
" /tool <name> desc simple \xB7 /tool <name> result simple \xB7 /tool list \xB7 /tool disable|enable <name>"
|
|
29459
29974
|
)
|
|
29460
29975
|
);
|
|
29461
29976
|
return lines.join("\n");
|
|
29462
29977
|
}
|
|
29463
29978
|
function formatList() {
|
|
29464
|
-
const header = ` ${
|
|
29979
|
+
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
29980
|
const rows = opts.toolRegistry.listWithOwner().map(({ tool }) => {
|
|
29466
29981
|
const descMode = getToolDescriptionMode2(opts.toolRegistry, tool.name);
|
|
29467
29982
|
const resultMode = getToolResultRenderMode(opts.toolRegistry, tool.name);
|
|
29468
29983
|
const owner = opts.toolRegistry.ownerOf(tool.name) ?? "core";
|
|
29469
|
-
const status = opts.toolRegistry.isDisabled(tool.name) ?
|
|
29470
|
-
return ` ${fit(tool.name, 28)} ${
|
|
29984
|
+
const status = opts.toolRegistry.isDisabled(tool.name) ? color55.red("disabled") : color55.green("active");
|
|
29985
|
+
return ` ${fit(tool.name, 28)} ${color55.dim(fit(`[${owner}]`, 28))} ${fit(status, 10)} ${fit(formatDescriptionMode(descMode), 14)} ` + formatResultRenderMode(resultMode);
|
|
29471
29986
|
});
|
|
29472
29987
|
return [
|
|
29473
|
-
`${
|
|
29988
|
+
`${color55.bold("Tool modes")} ${color55.dim("(default: extend on both axes)")}`,
|
|
29474
29989
|
"",
|
|
29475
29990
|
header,
|
|
29476
29991
|
...rows
|
|
@@ -29481,55 +29996,55 @@ function buildToolCommand(opts) {
|
|
|
29481
29996
|
const tool = reg.get(name);
|
|
29482
29997
|
if (!tool) {
|
|
29483
29998
|
if (reg.isDisabled(name)) {
|
|
29484
|
-
return `${
|
|
29999
|
+
return `${color55.amber(name)} is disabled. Use ${color55.dim(`/tool enable ${name}`)} to restore.`;
|
|
29485
30000
|
}
|
|
29486
|
-
return `${
|
|
30001
|
+
return `${color55.red("Unknown tool")}: ${name}. Use ${color55.dim("/tools")} to list registered tools.`;
|
|
29487
30002
|
}
|
|
29488
30003
|
const descMode = getToolDescriptionMode2(reg, name);
|
|
29489
30004
|
const resultMode = getToolResultRenderMode(reg, name);
|
|
29490
|
-
const status = reg.isDisabled(name) ?
|
|
30005
|
+
const status = reg.isDisabled(name) ? color55.red("disabled") : color55.green("active");
|
|
29491
30006
|
return [
|
|
29492
|
-
`${
|
|
30007
|
+
`${color55.bold(name)} ${status}`,
|
|
29493
30008
|
`description mode: ${formatDescriptionMode(descMode)}`,
|
|
29494
30009
|
`result mode: ${formatResultRenderMode(resultMode)}`,
|
|
29495
30010
|
"",
|
|
29496
|
-
|
|
30011
|
+
color55.dim(tool.description)
|
|
29497
30012
|
].join("\n");
|
|
29498
30013
|
}
|
|
29499
30014
|
async function cmdEnable(name) {
|
|
29500
30015
|
const reg = opts.toolRegistry;
|
|
29501
30016
|
if (!reg.isDisabled(name)) {
|
|
29502
|
-
return `${
|
|
30017
|
+
return `${color55.amber(name)} is not disabled.`;
|
|
29503
30018
|
}
|
|
29504
30019
|
const ok = reg.enable(name);
|
|
29505
|
-
if (!ok) return `${
|
|
30020
|
+
if (!ok) return `${color55.red("Could not enable")}: ${name}.`;
|
|
29506
30021
|
const disabled = currentDisabledSet();
|
|
29507
30022
|
disabled.delete(name);
|
|
29508
30023
|
await persistDisabled(Array.from(disabled));
|
|
29509
|
-
return `${
|
|
30024
|
+
return `${color55.green("\u2713")} ${color55.cyan(name)} re-enabled \u2014 will appear in next provider request.`;
|
|
29510
30025
|
}
|
|
29511
30026
|
async function cmdEnableAll() {
|
|
29512
30027
|
const reg = opts.toolRegistry;
|
|
29513
30028
|
const count = reg.enableAll();
|
|
29514
|
-
if (count === 0) return `${
|
|
30029
|
+
if (count === 0) return `${color55.amber("No disabled tools to re-enable.")}`;
|
|
29515
30030
|
await persistDisabled([]);
|
|
29516
|
-
return `${
|
|
30031
|
+
return `${color55.green("\u2713")} All ${count} disabled tool(s) re-enabled.`;
|
|
29517
30032
|
}
|
|
29518
30033
|
async function cmdDisable(name) {
|
|
29519
30034
|
const reg = opts.toolRegistry;
|
|
29520
30035
|
const tool = reg.get(name);
|
|
29521
30036
|
if (!tool) {
|
|
29522
30037
|
if (reg.isDisabled(name)) {
|
|
29523
|
-
return `${
|
|
30038
|
+
return `${color55.amber(name)} is already disabled.`;
|
|
29524
30039
|
}
|
|
29525
|
-
return `${
|
|
30040
|
+
return `${color55.red("Unknown tool")}: ${name}. Use ${color55.dim("/tools")} to list registered tools.`;
|
|
29526
30041
|
}
|
|
29527
30042
|
const ok = reg.disable(name);
|
|
29528
|
-
if (!ok) return `${
|
|
30043
|
+
if (!ok) return `${color55.red("Could not disable")}: ${name}.`;
|
|
29529
30044
|
const disabled = currentDisabledSet();
|
|
29530
30045
|
disabled.add(name);
|
|
29531
30046
|
await persistDisabled(Array.from(disabled));
|
|
29532
|
-
return `${
|
|
30047
|
+
return `${color55.green("\u2713")} ${color55.cyan(name)} disabled \u2014 removed from system prompt and tool registry.`;
|
|
29533
30048
|
}
|
|
29534
30049
|
function applyDescMode(name, mode) {
|
|
29535
30050
|
opts.toolRegistry.setDescriptionMode?.(name, mode);
|
|
@@ -29545,7 +30060,7 @@ function buildToolCommand(opts) {
|
|
|
29545
30060
|
help,
|
|
29546
30061
|
async run(args) {
|
|
29547
30062
|
if (!opts.configStore) {
|
|
29548
|
-
return { message: `${
|
|
30063
|
+
return { message: `${color55.red("Error")} config store not available.` };
|
|
29549
30064
|
}
|
|
29550
30065
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|
|
29551
30066
|
const sub = (parts[0] ?? "").toLowerCase();
|
|
@@ -29556,7 +30071,7 @@ function buildToolCommand(opts) {
|
|
|
29556
30071
|
try {
|
|
29557
30072
|
return { message: await cmdEnableAll() };
|
|
29558
30073
|
} catch (err) {
|
|
29559
|
-
return { message: `${
|
|
30074
|
+
return { message: `${color55.red("Error")}: ${toErrorMessage28(err)}` };
|
|
29560
30075
|
}
|
|
29561
30076
|
}
|
|
29562
30077
|
const name = parts[0] ?? "";
|
|
@@ -29564,43 +30079,43 @@ function buildToolCommand(opts) {
|
|
|
29564
30079
|
if (sub === "disable") {
|
|
29565
30080
|
const targets = parts.slice(1);
|
|
29566
30081
|
if (targets.length === 0)
|
|
29567
|
-
return { message: `${
|
|
30082
|
+
return { message: `${color55.amber("Usage:")} /tool disable <name> [name...]` };
|
|
29568
30083
|
try {
|
|
29569
30084
|
const results = [];
|
|
29570
30085
|
for (const t of targets) results.push(await cmdDisable(t));
|
|
29571
30086
|
return { message: results.join("\n") };
|
|
29572
30087
|
} catch (err) {
|
|
29573
|
-
return { message: `${
|
|
30088
|
+
return { message: `${color55.red("Error")}: ${toErrorMessage28(err)}` };
|
|
29574
30089
|
}
|
|
29575
30090
|
}
|
|
29576
30091
|
if (sub === "enable") {
|
|
29577
30092
|
const targets = parts.slice(1);
|
|
29578
30093
|
if (targets.length === 0)
|
|
29579
|
-
return { message: `${
|
|
30094
|
+
return { message: `${color55.amber("Usage:")} /tool enable <name> [name...]` };
|
|
29580
30095
|
try {
|
|
29581
30096
|
const results = [];
|
|
29582
30097
|
for (const t of targets) results.push(await cmdEnable(t));
|
|
29583
30098
|
return { message: results.join("\n") };
|
|
29584
30099
|
} catch (err) {
|
|
29585
|
-
return { message: `${
|
|
30100
|
+
return { message: `${color55.red("Error")}: ${toErrorMessage28(err)}` };
|
|
29586
30101
|
}
|
|
29587
30102
|
}
|
|
29588
30103
|
const action = parts[1]?.toLowerCase();
|
|
29589
30104
|
if (action === "disable" || action === "enable") {
|
|
29590
30105
|
if (parts.length > 2) {
|
|
29591
30106
|
return {
|
|
29592
|
-
message: `${
|
|
30107
|
+
message: `${color55.amber("Usage:")} /tool ${name} ${action}`
|
|
29593
30108
|
};
|
|
29594
30109
|
}
|
|
29595
30110
|
try {
|
|
29596
30111
|
return { message: action === "disable" ? await cmdDisable(name) : await cmdEnable(name) };
|
|
29597
30112
|
} catch (err) {
|
|
29598
|
-
return { message: `${
|
|
30113
|
+
return { message: `${color55.red("Error")}: ${toErrorMessage28(err)}` };
|
|
29599
30114
|
}
|
|
29600
30115
|
}
|
|
29601
30116
|
if (!opts.toolRegistry.get(name) && !opts.toolRegistry.isDisabled(name)) {
|
|
29602
30117
|
return {
|
|
29603
|
-
message: `${
|
|
30118
|
+
message: `${color55.red("Unknown tool")}: ${name}. Use ${color55.dim("/tools")} to list registered tools.`
|
|
29604
30119
|
};
|
|
29605
30120
|
}
|
|
29606
30121
|
if (parts.length === 1) return { message: formatOne(name) };
|
|
@@ -29609,53 +30124,53 @@ function buildToolCommand(opts) {
|
|
|
29609
30124
|
const rawMode = parts[2];
|
|
29610
30125
|
if (!rawMode) {
|
|
29611
30126
|
return {
|
|
29612
|
-
message: `${
|
|
30127
|
+
message: `${color55.amber("Usage:")} /tool ${name} ${axis} simple|extend`
|
|
29613
30128
|
};
|
|
29614
30129
|
}
|
|
29615
30130
|
const mode2 = normalizeToolDescriptionMode(rawMode);
|
|
29616
30131
|
if (!mode2) {
|
|
29617
30132
|
return {
|
|
29618
|
-
message: `${
|
|
30133
|
+
message: `${color55.amber("Usage:")} /tool ${name} ${axis} simple|extend`
|
|
29619
30134
|
};
|
|
29620
30135
|
}
|
|
29621
30136
|
try {
|
|
29622
30137
|
if (axis === "desc") {
|
|
29623
30138
|
const persisted2 = await persistModeForAxis(name, "desc", mode2);
|
|
29624
30139
|
applyDescMode(name, mode2);
|
|
29625
|
-
const persistence2 = persisted2 ?
|
|
30140
|
+
const persistence2 = persisted2 ? color55.dim("saved") : color55.dim("runtime only; config paths unavailable");
|
|
29626
30141
|
return {
|
|
29627
|
-
message: `${
|
|
30142
|
+
message: `${color55.green("\u2713")} ${color55.cyan(name)} ${formatDescriptionMode(mode2)} ${persistence2}`
|
|
29628
30143
|
};
|
|
29629
30144
|
}
|
|
29630
30145
|
const persisted = await persistModeForAxis(name, "result", mode2);
|
|
29631
30146
|
applyResultMode(name, mode2);
|
|
29632
|
-
const persistence = persisted ?
|
|
30147
|
+
const persistence = persisted ? color55.dim("saved") : color55.dim("runtime only; config paths unavailable");
|
|
29633
30148
|
return {
|
|
29634
|
-
message: `${
|
|
30149
|
+
message: `${color55.green("\u2713")} ${color55.cyan(name)} ${formatResultRenderMode(mode2)} ${persistence}`
|
|
29635
30150
|
};
|
|
29636
30151
|
} catch (err) {
|
|
29637
30152
|
return {
|
|
29638
|
-
message: `${
|
|
30153
|
+
message: `${color55.red("Could not save tool setting")}: ${toErrorMessage28(err)}`
|
|
29639
30154
|
};
|
|
29640
30155
|
}
|
|
29641
30156
|
}
|
|
29642
30157
|
const mode = normalizeToolDescriptionMode(axis);
|
|
29643
30158
|
if (!mode) {
|
|
29644
30159
|
return {
|
|
29645
|
-
message: `${
|
|
30160
|
+
message: `${color55.amber("Usage:")} /tool ${name} [desc|result] simple|extend`
|
|
29646
30161
|
};
|
|
29647
30162
|
}
|
|
29648
30163
|
try {
|
|
29649
30164
|
const persisted = await persistModeBoth(name, mode);
|
|
29650
30165
|
applyDescMode(name, mode);
|
|
29651
30166
|
applyResultMode(name, mode);
|
|
29652
|
-
const persistence = persisted ?
|
|
30167
|
+
const persistence = persisted ? color55.dim("saved (both axes)") : color55.dim("runtime only; config paths unavailable");
|
|
29653
30168
|
return {
|
|
29654
|
-
message: `${
|
|
30169
|
+
message: `${color55.green("\u2713")} ${color55.cyan(name)} ${formatDescriptionMode(mode)} + ${formatResultRenderMode(mode)} ${persistence}`
|
|
29655
30170
|
};
|
|
29656
30171
|
} catch (err) {
|
|
29657
30172
|
return {
|
|
29658
|
-
message: `${
|
|
30173
|
+
message: `${color55.red("Could not save tool setting")}: ${toErrorMessage28(err)}`
|
|
29659
30174
|
};
|
|
29660
30175
|
}
|
|
29661
30176
|
}
|
|
@@ -29663,14 +30178,14 @@ function buildToolCommand(opts) {
|
|
|
29663
30178
|
}
|
|
29664
30179
|
|
|
29665
30180
|
// src/slash-commands/tools.ts
|
|
29666
|
-
import { color as
|
|
30181
|
+
import { color as color56, getToolDescriptionMode as getToolDescriptionMode3 } from "@wrongstack/core/utils";
|
|
29667
30182
|
function fit2(text, width) {
|
|
29668
30183
|
if (text.length <= width) return text.padEnd(width);
|
|
29669
30184
|
return `${text.slice(0, Math.max(0, width - 3))}...`;
|
|
29670
30185
|
}
|
|
29671
30186
|
function formatDescriptionMode2(mode) {
|
|
29672
30187
|
const raw = `desc:${mode}`;
|
|
29673
|
-
return mode === "simple" ?
|
|
30188
|
+
return mode === "simple" ? color56.amber(raw) : color56.dim(raw);
|
|
29674
30189
|
}
|
|
29675
30190
|
function buildToolsCommand(opts) {
|
|
29676
30191
|
return {
|
|
@@ -29691,21 +30206,21 @@ function buildToolsCommand(opts) {
|
|
|
29691
30206
|
if (opened) return { message: "" };
|
|
29692
30207
|
}
|
|
29693
30208
|
if (filter && all.length === 0) {
|
|
29694
|
-
const msg2 = `${
|
|
30209
|
+
const msg2 = `${color56.bold("Tools")} \u2014 no tool name or owner matched "${filter}".`;
|
|
29695
30210
|
opts.renderer.write(msg2);
|
|
29696
30211
|
return { message: msg2 };
|
|
29697
30212
|
}
|
|
29698
|
-
const header = ` ${
|
|
30213
|
+
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
30214
|
const lines = all.map(({ tool, owner }) => {
|
|
29700
30215
|
const mode = getToolDescriptionMode3(reg, tool.name);
|
|
29701
|
-
const rw = tool.mutating ?
|
|
29702
|
-
const status = reg.isDisabled(tool.name) ?
|
|
29703
|
-
return ` ${fit2(tool.name, 28)} ${
|
|
30216
|
+
const rw = tool.mutating ? color56.yellow(fit2("mut", 4)) : color56.cyan(fit2("ro", 4));
|
|
30217
|
+
const status = reg.isDisabled(tool.name) ? color56.red("disabled") : color56.green("active");
|
|
30218
|
+
return ` ${fit2(tool.name, 28)} ${color56.dim(fit2(`[${owner}]`, 28))} ${rw} ${color56.dim(fit2(tool.permission, 8))} ${fit2(status, 10)} ` + formatDescriptionMode2(mode);
|
|
29704
30219
|
});
|
|
29705
30220
|
const extra = disabled.length > 0 ? `
|
|
29706
|
-
${
|
|
29707
|
-
const filterNote = filter ?
|
|
29708
|
-
const msg = `${
|
|
30221
|
+
${color56.dim(`${disabled.length} tool(s) disabled. Use /tool enable <name> or /tool enable-all to restore.`)}` : "";
|
|
30222
|
+
const filterNote = filter ? color56.dim(` matching "${filter}" (${all.length} of ${allTools.length})`) : "";
|
|
30223
|
+
const msg = `${color56.bold("Tools")}${filterNote} (${all.length} shown, ${disabled.length} disabled) ${color56.dim("description detail via /tool <name> simple|extend")}:
|
|
29709
30224
|
${header}
|
|
29710
30225
|
${lines.join("\n")}${extra}
|
|
29711
30226
|
`;
|
|
@@ -29719,7 +30234,7 @@ ${lines.join("\n")}${extra}
|
|
|
29719
30234
|
import * as fs19 from "node:fs/promises";
|
|
29720
30235
|
import * as os3 from "node:os";
|
|
29721
30236
|
import * as path29 from "node:path";
|
|
29722
|
-
import { atomicWrite as atomicWrite10, color as
|
|
30237
|
+
import { atomicWrite as atomicWrite10, color as color57 } from "@wrongstack/core/utils";
|
|
29723
30238
|
|
|
29724
30239
|
// src/tuneup.ts
|
|
29725
30240
|
var DEFAULT_EAGER_MAX_CHARS = 24e3;
|
|
@@ -30197,17 +30712,17 @@ function buildTuneupCommand(opts) {
|
|
|
30197
30712
|
const parsed = parseArgs(args);
|
|
30198
30713
|
if (parsed.mode === "help") return { message: help };
|
|
30199
30714
|
if (parsed.mode === "usage") {
|
|
30200
|
-
return { message: `${
|
|
30715
|
+
return { message: `${color57.amber("Usage:")} /tuneup [fix [--power] [--pick] | deep]` };
|
|
30201
30716
|
}
|
|
30202
30717
|
if (!opts.paths) {
|
|
30203
|
-
return { message: `${
|
|
30718
|
+
return { message: `${color57.red("Error")} config paths not available.` };
|
|
30204
30719
|
}
|
|
30205
30720
|
const input = await gatherInput(opts, parsed.power);
|
|
30206
30721
|
const report = runTuneup(input);
|
|
30207
|
-
const lines = [`${
|
|
30722
|
+
const lines = [`${color57.bold("WrongStack")} ${color57.dim("\u2014 Tune-up")}`];
|
|
30208
30723
|
renderFindings(lines, report.findings);
|
|
30209
30724
|
if (parsed.mode === "deep") {
|
|
30210
|
-
lines.push("",
|
|
30725
|
+
lines.push("", color57.dim(" \u2192 asking the agent for a project-specific optimization plan\u2026"));
|
|
30211
30726
|
return { message: lines.join("\n"), runText: buildDeepPrompt(report) };
|
|
30212
30727
|
}
|
|
30213
30728
|
if (parsed.mode === "report") {
|
|
@@ -30229,18 +30744,18 @@ function buildTuneupCommand(opts) {
|
|
|
30229
30744
|
const applied = await applyActions(actions, opts);
|
|
30230
30745
|
lines.push("");
|
|
30231
30746
|
if (applied.messages.length === 0) {
|
|
30232
|
-
lines.push(
|
|
30747
|
+
lines.push(color57.dim(" no deterministic fixes to apply"));
|
|
30233
30748
|
} else {
|
|
30234
|
-
for (const m of applied.messages) lines.push(` ${
|
|
30749
|
+
for (const m of applied.messages) lines.push(` ${color57.green("\u2713")} ${m}`);
|
|
30235
30750
|
if (applied.changed) {
|
|
30236
30751
|
lines.push(
|
|
30237
|
-
` ${
|
|
30752
|
+
` ${color57.green("\u2713")} written ${color57.dim("(backup: config.json.last + timestamped .bak)")}`
|
|
30238
30753
|
);
|
|
30239
30754
|
}
|
|
30240
30755
|
}
|
|
30241
30756
|
const runText = report.agentHandoff || void 0;
|
|
30242
30757
|
if (runText) {
|
|
30243
|
-
lines.push("",
|
|
30758
|
+
lines.push("", color57.dim(" \u2192 handing instruction-file cleanups to the agent\u2026"));
|
|
30244
30759
|
}
|
|
30245
30760
|
return { message: lines.join("\n"), ...runText ? { runText } : {} };
|
|
30246
30761
|
}
|
|
@@ -30401,7 +30916,7 @@ async function applyActions(actions, opts) {
|
|
|
30401
30916
|
parsed = JSON.parse(raw);
|
|
30402
30917
|
} catch {
|
|
30403
30918
|
return {
|
|
30404
|
-
messages: [`${
|
|
30919
|
+
messages: [`${color57.red("\u2717")} global config is not valid JSON \u2014 run /doctor fix first`],
|
|
30405
30920
|
changed: false
|
|
30406
30921
|
};
|
|
30407
30922
|
}
|
|
@@ -30510,26 +31025,26 @@ var CATEGORY_ORDER = [
|
|
|
30510
31025
|
function severityIcon(severity) {
|
|
30511
31026
|
switch (severity) {
|
|
30512
31027
|
case "error":
|
|
30513
|
-
return
|
|
31028
|
+
return color57.red("\u2717");
|
|
30514
31029
|
case "warning":
|
|
30515
|
-
return
|
|
31030
|
+
return color57.amber("!");
|
|
30516
31031
|
case "ok":
|
|
30517
|
-
return
|
|
31032
|
+
return color57.green("\u2713");
|
|
30518
31033
|
default:
|
|
30519
|
-
return
|
|
31034
|
+
return color57.cyan("\xB7");
|
|
30520
31035
|
}
|
|
30521
31036
|
}
|
|
30522
31037
|
function renderFindings(lines, findings) {
|
|
30523
31038
|
for (const category of CATEGORY_ORDER) {
|
|
30524
31039
|
const group = findings.filter((f) => f.category === category);
|
|
30525
31040
|
if (group.length === 0) continue;
|
|
30526
|
-
lines.push("",
|
|
31041
|
+
lines.push("", color57.bold(CATEGORY_LABELS[category]));
|
|
30527
31042
|
for (const f of group) {
|
|
30528
31043
|
lines.push(` ${severityIcon(f.severity)} ${f.problem}`);
|
|
30529
31044
|
if (f.suggestion) {
|
|
30530
|
-
for (const s of f.suggestion.split("\n")) lines.push(
|
|
31045
|
+
for (const s of f.suggestion.split("\n")) lines.push(color57.dim(` ${s}`));
|
|
30531
31046
|
}
|
|
30532
|
-
if (f.fix) lines.push(
|
|
31047
|
+
if (f.fix) lines.push(color57.dim(` \u2192 fixable: ${f.fix}`));
|
|
30533
31048
|
}
|
|
30534
31049
|
}
|
|
30535
31050
|
}
|
|
@@ -30538,20 +31053,20 @@ function summaryLine(findings, fixable, handoffs, power) {
|
|
|
30538
31053
|
(f) => f.severity === "warning" || f.severity === "error"
|
|
30539
31054
|
).length;
|
|
30540
31055
|
if (warnings === 0 && fixable === 0 && handoffs === 0) {
|
|
30541
|
-
return `${
|
|
31056
|
+
return `${color57.green("\u2713")} everything looks healthy`;
|
|
30542
31057
|
}
|
|
30543
31058
|
const parts = [];
|
|
30544
31059
|
if (warnings > 0) parts.push(`${warnings} warning(s)`);
|
|
30545
31060
|
if (fixable > 0) parts.push(`${fixable} auto-fixable`);
|
|
30546
31061
|
if (handoffs > 0) parts.push(`${handoffs} for the agent`);
|
|
30547
31062
|
const cmd = power ? "/tuneup fix --power" : "/tuneup fix";
|
|
30548
|
-
return `${parts.join(", ")} ${
|
|
31063
|
+
return `${parts.join(", ")} ${color57.dim(`\u2014 run ${cmd}`)}`;
|
|
30549
31064
|
}
|
|
30550
31065
|
|
|
30551
31066
|
// src/slash-commands/working-dir.ts
|
|
30552
31067
|
import * as fs20 from "node:fs/promises";
|
|
30553
31068
|
import * as path30 from "node:path";
|
|
30554
|
-
import { color as
|
|
31069
|
+
import { color as color58, toErrorMessage as toErrorMessage29 } from "@wrongstack/core/utils";
|
|
30555
31070
|
function buildWorkingDirCommand(_opts) {
|
|
30556
31071
|
return {
|
|
30557
31072
|
name: "working_dir",
|
|
@@ -30570,16 +31085,16 @@ function buildWorkingDirCommand(_opts) {
|
|
|
30570
31085
|
].join("\n"),
|
|
30571
31086
|
async run(args, ctx) {
|
|
30572
31087
|
if (!ctx) {
|
|
30573
|
-
return { message:
|
|
31088
|
+
return { message: color58.yellow("No active context. Start a session first.") };
|
|
30574
31089
|
}
|
|
30575
31090
|
const trimmed = args.trim();
|
|
30576
31091
|
if (!trimmed) {
|
|
30577
31092
|
const rel2 = path30.relative(ctx.projectRoot, ctx.workingDir) || ".";
|
|
30578
31093
|
return {
|
|
30579
31094
|
message: [
|
|
30580
|
-
`Working directory: ${
|
|
30581
|
-
|
|
30582
|
-
|
|
31095
|
+
`Working directory: ${color58.bold(ctx.workingDir)}`,
|
|
31096
|
+
color58.dim(` (relative to root: ${rel2})`),
|
|
31097
|
+
color58.dim(` Project root: ${ctx.projectRoot}`)
|
|
30583
31098
|
].join("\n")
|
|
30584
31099
|
};
|
|
30585
31100
|
}
|
|
@@ -30588,7 +31103,7 @@ function buildWorkingDirCommand(_opts) {
|
|
|
30588
31103
|
const rel = path30.relative(root, resolved);
|
|
30589
31104
|
if (rel.startsWith("..") || path30.isAbsolute(rel)) {
|
|
30590
31105
|
return {
|
|
30591
|
-
message:
|
|
31106
|
+
message: color58.red(
|
|
30592
31107
|
`Directory "${trimmed}" is outside the project root.
|
|
30593
31108
|
Resolved: ${resolved}
|
|
30594
31109
|
Root: ${root}`
|
|
@@ -30598,25 +31113,25 @@ function buildWorkingDirCommand(_opts) {
|
|
|
30598
31113
|
try {
|
|
30599
31114
|
const stat5 = await fs20.stat(resolved);
|
|
30600
31115
|
if (!stat5.isDirectory()) {
|
|
30601
|
-
return { message:
|
|
31116
|
+
return { message: color58.red(`Not a directory: ${resolved}`) };
|
|
30602
31117
|
}
|
|
30603
31118
|
} catch {
|
|
30604
|
-
return { message:
|
|
31119
|
+
return { message: color58.red(`Directory does not exist: ${resolved}`) };
|
|
30605
31120
|
}
|
|
30606
31121
|
const previous = ctx.workingDir;
|
|
30607
31122
|
try {
|
|
30608
31123
|
ctx.setWorkingDir(resolved);
|
|
30609
31124
|
} catch (err) {
|
|
30610
31125
|
return {
|
|
30611
|
-
message:
|
|
31126
|
+
message: color58.red(toErrorMessage29(err))
|
|
30612
31127
|
};
|
|
30613
31128
|
}
|
|
30614
31129
|
const prevRel = path30.relative(ctx.projectRoot, previous) || ".";
|
|
30615
31130
|
const newRel = path30.relative(ctx.projectRoot, resolved) || ".";
|
|
30616
31131
|
return {
|
|
30617
31132
|
message: [
|
|
30618
|
-
|
|
30619
|
-
|
|
31133
|
+
color58.green(` \u2713 ${prevRel} \u2192 ${color58.bold(newRel)}`),
|
|
31134
|
+
color58.dim(` ${resolved}`)
|
|
30620
31135
|
].join("\n")
|
|
30621
31136
|
};
|
|
30622
31137
|
}
|
|
@@ -30685,7 +31200,7 @@ function buildWorktreeCommand(opts) {
|
|
|
30685
31200
|
}
|
|
30686
31201
|
|
|
30687
31202
|
// src/slash-commands/yolo.ts
|
|
30688
|
-
import { color as
|
|
31203
|
+
import { color as color59 } from "@wrongstack/core/utils";
|
|
30689
31204
|
function buildYoloCommand(opts) {
|
|
30690
31205
|
return {
|
|
30691
31206
|
name: "yolo",
|
|
@@ -30709,7 +31224,7 @@ function buildYoloCommand(opts) {
|
|
|
30709
31224
|
}
|
|
30710
31225
|
if (!arg) {
|
|
30711
31226
|
const current = opts.onYolo();
|
|
30712
|
-
const status = current ? `${
|
|
31227
|
+
const status = current ? `${color59.yellow("ON")} ${color59.dim("(auto-approving tool calls)")}` : `${color59.green("OFF")} ${color59.dim("(permission prompts active)")}`;
|
|
30713
31228
|
const msg2 = `YOLO mode: ${status}`;
|
|
30714
31229
|
opts.renderer.write(msg2);
|
|
30715
31230
|
return { message: msg2 };
|
|
@@ -30724,11 +31239,11 @@ function buildYoloCommand(opts) {
|
|
|
30724
31239
|
} else if (arg === "destructive") {
|
|
30725
31240
|
const currentMode = opts.onYolo();
|
|
30726
31241
|
if (!currentMode) {
|
|
30727
|
-
const msg3 = `${
|
|
31242
|
+
const msg3 = `${color59.amber("YOLO is OFF.")} Destructive-gate flags are deprecated; prompts are active because YOLO is off.`;
|
|
30728
31243
|
opts.renderer.writeWarning(msg3);
|
|
30729
31244
|
return { message: msg3 };
|
|
30730
31245
|
}
|
|
30731
|
-
const msg2 = `${
|
|
31246
|
+
const msg2 = `${color59.amber("Destructive gate:")} ${color59.dim("deprecated \u2014 YOLO auto-approves all non-denied tool calls.")}`;
|
|
30732
31247
|
opts.renderer.writeWarning(msg2);
|
|
30733
31248
|
return { message: msg2 };
|
|
30734
31249
|
} else {
|
|
@@ -30737,7 +31252,7 @@ function buildYoloCommand(opts) {
|
|
|
30737
31252
|
return { message: msg2 };
|
|
30738
31253
|
}
|
|
30739
31254
|
opts.onYolo(newState);
|
|
30740
|
-
const label = newState ? `${
|
|
31255
|
+
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
31256
|
const msg = `YOLO mode: ${label}`;
|
|
30742
31257
|
opts.renderer.write(msg);
|
|
30743
31258
|
return { message: msg };
|
|
@@ -30802,6 +31317,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
30802
31317
|
buildBtwCommand(opts),
|
|
30803
31318
|
buildNextCommand(opts),
|
|
30804
31319
|
buildModeCommand(opts),
|
|
31320
|
+
buildThemeCommand(opts),
|
|
30805
31321
|
buildDesignCommand(opts),
|
|
30806
31322
|
buildMailboxDemoCommand(opts),
|
|
30807
31323
|
buildMailboxCommand(opts),
|
|
@@ -31559,7 +32075,7 @@ async function runInteractive(cliCtx) {
|
|
|
31559
32075
|
onEvent: evOn
|
|
31560
32076
|
});
|
|
31561
32077
|
const savedProviderCfg = config.providers?.[config.provider];
|
|
31562
|
-
const { execute } = await import("./execution-
|
|
32078
|
+
const { execute } = await import("./execution-MVOH6PAQ.js");
|
|
31563
32079
|
const stopHeapWatchdog = startSharedHeapWatchdog({
|
|
31564
32080
|
collectStats: () => {
|
|
31565
32081
|
const hqQueue = hqPublisherRef.current?.getQueueStats();
|
|
@@ -31792,4 +32308,4 @@ export {
|
|
|
31792
32308
|
CLI_VERSION,
|
|
31793
32309
|
runInteractive
|
|
31794
32310
|
};
|
|
31795
|
-
//# sourceMappingURL=cli-main-
|
|
32311
|
+
//# sourceMappingURL=cli-main-37XO26GK.js.map
|