@kontextmind/kxm 0.7.177 → 0.7.179

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.
@@ -66,15 +66,12 @@ export interface SessionBrief {
66
66
  sessionToken?: string;
67
67
  }
68
68
 
69
- function hubPrefix(hub?: Partial<SessionHubStatus>): string {
70
- // A remote hub carries a bearer over a network; a loopback one does not. The JSON
71
- // said so while every text surface printed the same bytes for both, which is the
72
- // distinction an operator actually reads.
69
+ function hubStatusPart(hub?: Partial<SessionHubStatus>): string | undefined {
73
70
  const suffix = hub?.scope === "remote" ? "/remote" : "";
74
- if (hub?.state === "on" || (hub?.state === undefined && hub?.online === true)) return `kxm hub:on${suffix}`;
75
- if (hub?.state === "off" || (hub?.state === undefined && hub?.online === false)) return `kxm hub:off${suffix}`;
76
- if (hub?.state === "unknown") return `kxm hub:unknown${suffix}`;
77
- return "kxm";
71
+ if (hub?.state === "off" || (hub?.state === undefined && hub?.online === false)) return `hub:off${suffix}`;
72
+ if (hub?.state === "unknown") return `hub:unknown${suffix}`;
73
+ if (hub?.scope === "remote") return "hub:remote";
74
+ return undefined;
78
75
  }
79
76
 
80
77
  function truncate(value: string, width: number): string {
@@ -172,12 +169,12 @@ export function formatCostLine(input?: {
172
169
  model?: string;
173
170
  }): string | undefined {
174
171
  if (!input) return undefined;
175
- if (input.sessionCostUsd === undefined && input.runCostUsd === undefined) return "unknown";
172
+ if (input.sessionCostUsd === undefined && input.runCostUsd === undefined) return undefined;
176
173
  const sess = input.sessionCostUsd !== undefined ? `$${input.sessionCostUsd.toFixed(2)} sess` : undefined;
177
174
  const run = input.runCostUsd !== undefined ? `$${input.runCostUsd.toFixed(2)} run` : undefined;
178
175
  const route = input.harness && input.model ? `${input.harness}/${input.model}` : (input.model ?? undefined);
179
176
  const parts = [sess, run, route].filter(Boolean);
180
- return parts.length > 0 ? parts.join(" · ") : "unknown";
177
+ return parts.length > 0 ? parts.join(" · ") : undefined;
181
178
  }
182
179
 
183
180
  export function renderStatusLine(
@@ -186,11 +183,10 @@ export function renderStatusLine(
186
183
  hub?: Partial<SessionHubStatus>,
187
184
  ship?: SessionShipStatus,
188
185
  updateLatest?: string,
189
- cost?: string,
190
186
  ): string {
191
187
  if ("schema" in statsOrBrief && statsOrBrief.schema === "kxm.session-brief.v1") {
192
188
  const brief = statsOrBrief;
193
- if (!current && !hub && !ship && !updateLatest && !cost) {
189
+ if (!current && !hub && !ship && !updateLatest) {
194
190
  return brief.statusLine;
195
191
  }
196
192
  return formatSessionStatusLine(
@@ -199,10 +195,9 @@ export function renderStatusLine(
199
195
  hub ?? brief.hub,
200
196
  ship,
201
197
  updateLatest,
202
- cost ?? brief.cost,
203
198
  );
204
199
  }
205
- return formatSessionStatusLine(statsOrBrief as SessionWorkStats, current, hub, ship, updateLatest, cost);
200
+ return formatSessionStatusLine(statsOrBrief as SessionWorkStats, current, hub, ship, updateLatest);
206
201
  }
207
202
 
208
203
  export function formatSessionStatusLine(
@@ -211,32 +206,20 @@ export function formatSessionStatusLine(
211
206
  hub?: Partial<SessionHubStatus>,
212
207
  ship?: SessionShipStatus,
213
208
  updateLatest?: string,
214
- cost?: string,
215
209
  ): string {
216
- const head = hubPrefix(hub);
217
- if (!current && stats.activeTasks === 0 && stats.planCount === 0 && stats.inbox === 0) {
218
- const idle = hub?.online === undefined && hub?.state === undefined ? "kxm idle" : `${head} · idle`;
219
- const withShip = ship?.dirty
220
- ? `${idle} · dirty`
221
- : (ship && ship.ahead > 0 ? `${idle} · ${ship.ahead} local` : idle);
222
- const withCost = cost ? `${withShip} · ${cost}` : withShip;
223
- const finalLine = updateLatest ? `${withCost} · upd ${updateLatest}` : withCost;
224
- return finalLine.length <= 80 ? finalLine : `${finalLine.slice(0, 79)}…`;
225
- }
226
- const parts: string[] = [];
227
- if (current?.kind === "task") parts.push(`${head} ${current.detail}`);
228
- else if (current?.kind === "plan") parts.push(`${head} plan ${truncate(current.detail, 36)}`);
229
- else parts.push(head);
230
- parts.push(`${stats.activeTasks} task${stats.activeTasks === 1 ? "" : "s"}`);
210
+ const parts = ["kxm"];
211
+ const hubPart = hubStatusPart(hub);
212
+ if (hubPart) parts.push(hubPart);
213
+ if (current?.kind === "task") parts.push(current.detail);
214
+ else if (current?.kind === "plan") parts.push(`plan ${truncate(current.detail, 36)}`);
215
+ if (stats.activeTasks > 0) parts.push(`${stats.activeTasks} task${stats.activeTasks === 1 ? "" : "s"}`);
231
216
  if (stats.waitingTasks > 0) parts.push(`${stats.waitingTasks} waiting`);
232
- parts.push(`${stats.planCount} plan${stats.planCount === 1 ? "" : "s"}`);
217
+ if (stats.planCount > 0) parts.push(`${stats.planCount} plan${stats.planCount === 1 ? "" : "s"}`);
233
218
  if (stats.inbox > 0) parts.push(`inbox ${stats.inbox}`);
234
219
  if (ship?.dirty) parts.push("dirty");
235
220
  else if (ship && ship.ahead > 0) parts.push(`${ship.ahead} local`);
236
- if (cost) parts.push(cost);
237
221
  if (updateLatest) parts.push(`upd ${updateLatest}`);
238
- const line = parts.join(" · ");
239
- return line.length <= 80 ? line : `${line.slice(0, 79)}…`;
222
+ return truncate(parts.join(" · "), 80);
240
223
  }
241
224
 
242
225
  export function formatSessionWidget(
@@ -247,19 +230,22 @@ export function formatSessionWidget(
247
230
  updateLatest?: string,
248
231
  cost?: string,
249
232
  ): string[] {
250
- const hubMark = hub?.state === "on" || hub?.online === true
251
- ? "hub:on "
252
- : (hub?.state === "off" || hub?.online === false
253
- ? "hub:off "
254
- : (hub?.state === "unknown" ? "hub:unknown " : ""));
255
- const lines = [
256
- `KXM ${hubMark}${stats.activeTasks} tasks ${stats.waitingTasks} waiting ${stats.planCount} plans inbox ${stats.inbox}`,
257
- ];
233
+ const suffix = hub?.scope === "remote" ? "/remote" : "";
234
+ const hubMark = hub?.state === "on" || (hub?.state === undefined && hub?.online === true)
235
+ ? `hub:on${suffix}`
236
+ : (hub?.state === "off" || (hub?.state === undefined && hub?.online === false)
237
+ ? `hub:off${suffix}`
238
+ : (hub?.state === "unknown" ? `hub:unknown${suffix}` : undefined));
239
+ const summary = ["KXM", hubMark];
240
+ if (stats.activeTasks > 0) summary.push(`${stats.activeTasks} task${stats.activeTasks === 1 ? "" : "s"}`);
241
+ if (stats.waitingTasks > 0) summary.push(`${stats.waitingTasks} waiting`);
242
+ if (stats.planCount > 0) summary.push(`${stats.planCount} plan${stats.planCount === 1 ? "" : "s"}`);
243
+ if (stats.inbox > 0) summary.push(`inbox ${stats.inbox}`);
244
+ const lines = [summary.filter(Boolean).join(" · ")];
258
245
  if (current) lines.push(`now ${current.kind} ${truncate(current.detail, 56)}`);
259
- else if (stats.latestPlan) lines.push(`plan ${truncate(stats.latestPlan, 60)}`);
260
- else lines.push("now no selected work");
261
- lines.push(formatShipLine(ship));
262
- if (cost) lines.push(`cost ${cost}`);
246
+ else if (stats.latestPlan) lines.push(`plan ${truncate(stats.latestPlan, 60)}`);
247
+ if (ship?.dirty || (ship && ship.ahead > 0)) lines.push(formatShipLine(ship));
248
+ if (cost && cost !== "unknown") lines.push(`cost ${cost}`);
263
249
  if (updateLatest) lines.push(`update ${updateLatest} available · kxm update --kxm`);
264
250
  return lines;
265
251
  }
@@ -290,7 +276,7 @@ export function buildSessionBrief(
290
276
  const tasks = uniqueLabels(recentTasks(snapshot.runs).map(taskItem));
291
277
  const plans = uniqueLabels(snapshot.plans.slice(0, MAX_SESSION_BRIEF_PLANS).map(planItem));
292
278
  const resolvedHub: SessionHubStatus = hub ?? { state: "off", evidence: "unconfigured", online: false };
293
- const statusLine = formatSessionStatusLine(stats, current, resolvedHub, ship, updateLatest, cost);
279
+ const statusLine = formatSessionStatusLine(stats, current, resolvedHub, ship, updateLatest);
294
280
  const widgetLines = formatSessionWidget(stats, current, resolvedHub, ship, updateLatest, cost);
295
281
  return {
296
282
  schema: SESSION_BRIEF_SCHEMA,
@@ -359,7 +345,7 @@ export function estimateSessionCost(stateDir: string): string | undefined {
359
345
  latestModel = (r.effectiveModel as string | undefined) ?? (r.requestedModel as string | undefined) ?? latestModel;
360
346
  latestHarness = (r.harness as string | undefined) ?? latestHarness;
361
347
  }
362
- if (!hasMetered) return "unknown";
348
+ if (!hasMetered) return undefined;
363
349
  const route = latestHarness && latestModel ? `${latestHarness}/${latestModel}` : latestModel;
364
350
  return `$${totalCost.toFixed(2)} sess${route ? ` · ${route}` : ""}`;
365
351
  } catch {
@@ -417,7 +403,6 @@ export function loadSessionBrief(
417
403
  effectiveHub,
418
404
  options.ship,
419
405
  options.updateLatest,
420
- effectiveCost,
421
406
  );
422
407
  const widgetLines = formatSessionWidget(
423
408
  cached.stats,
@@ -499,7 +484,6 @@ export async function loadSessionBriefAsync(
499
484
  effectiveHub,
500
485
  options.ship,
501
486
  undefined,
502
- effectiveCost,
503
487
  );
504
488
  const widgetLines = formatSessionWidget(
505
489
  cached.stats,
@@ -564,7 +548,7 @@ export function sessionBriefPickerEnabled(input: { env?: NodeJS.ProcessEnv; mode
564
548
  return reason === "startup" || reason === "new" || reason === "fork";
565
549
  }
566
550
 
567
- export const KXM_SLASH_SUBCOMMANDS = ["status", "hub", "memory", "progress", "workflow", "help"] as const;
551
+ export const KXM_SLASH_SUBCOMMANDS = ["status", "hub", "memory", "progress", "workflow", "config", "help"] as const;
568
552
  export type KxmSlashCommand = "brief" | typeof KXM_SLASH_SUBCOMMANDS[number];
569
553
 
570
554
  export function parseKxmSlashArgs(args: string | undefined): KxmSlashCommand {
@@ -576,6 +560,7 @@ export function parseKxmSlashArgs(args: string | undefined): KxmSlashCommand {
576
560
  || raw === "memory"
577
561
  || raw === "progress"
578
562
  || raw === "workflow"
563
+ || raw === "config"
579
564
  || raw === "help"
580
565
  ) return raw;
581
566
  return "help";