@wrongstack/cli 0.296.4 → 0.297.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/boot/dispatch-webui.d.ts +7 -0
  2. package/dist/boot/dispatch-webui.d.ts.map +1 -1
  3. package/dist/boot/launch-banner.d.ts +22 -0
  4. package/dist/boot/launch-banner.d.ts.map +1 -0
  5. package/dist/boot/launch-menu.d.ts.map +1 -1
  6. package/dist/boot/tui-settings-adapter.d.ts.map +1 -1
  7. package/dist/{chunk-OI7GWJDS.js → chunk-7JABKXET.js} +372 -10
  8. package/dist/chunk-7JABKXET.js.map +7 -0
  9. package/dist/{chunk-WY7KKOUK.js → chunk-JW75HY4F.js} +31 -10
  10. package/dist/chunk-JW75HY4F.js.map +7 -0
  11. package/dist/{chunk-Z7YGASQP.js → chunk-QZKWRNIP.js} +19 -18
  12. package/dist/chunk-QZKWRNIP.js.map +7 -0
  13. package/dist/{chunk-BQCAJ7FI.js → chunk-XTIXVJXP.js} +16 -3
  14. package/dist/chunk-XTIXVJXP.js.map +7 -0
  15. package/dist/{cli-main-TOAWVJDY.js → cli-main-GOY5YGVL.js} +25199 -25107
  16. package/dist/cli-main-GOY5YGVL.js.map +7 -0
  17. package/dist/cli-main.d.ts.map +1 -1
  18. package/dist/{execution-O7I5DYE6.js → execution-A4PGK4XV.js} +39 -9
  19. package/dist/execution-A4PGK4XV.js.map +7 -0
  20. package/dist/execution.d.ts.map +1 -1
  21. package/dist/fleet-statusline.d.ts.map +1 -1
  22. package/dist/goal-host.d.ts +1 -1
  23. package/dist/goal-host.d.ts.map +1 -1
  24. package/dist/{hq-JUYKUNJX.js → hq-GBPGXIR5.js} +2 -2
  25. package/dist/hq-publisher.d.ts +3 -2
  26. package/dist/hq-publisher.d.ts.map +1 -1
  27. package/dist/hq-server/types.d.ts +8 -0
  28. package/dist/hq-server/types.d.ts.map +1 -1
  29. package/dist/hq-server/ws.d.ts +61 -2
  30. package/dist/hq-server/ws.d.ts.map +1 -1
  31. package/dist/{hq-server-6AQ3BXZA.js → hq-server-YQUUMTPU.js} +2 -2
  32. package/dist/index.js +293 -26
  33. package/dist/index.js.map +4 -4
  34. package/dist/kanban-hq-sync.d.ts +13 -1
  35. package/dist/kanban-hq-sync.d.ts.map +1 -1
  36. package/dist/live-settings-input.d.ts +9 -0
  37. package/dist/live-settings-input.d.ts.map +1 -1
  38. package/dist/{mailbox-serve-2UWHEN3M.js → mailbox-serve-DBOOOOWZ.js} +50 -8
  39. package/dist/mailbox-serve-DBOOOOWZ.js.map +7 -0
  40. package/dist/{sdd-runtime-JG4EEKL6.js → sdd-runtime-AIJCJGWC.js} +2 -2
  41. package/dist/services/sdd/state.d.ts +3 -3
  42. package/dist/services/sdd/state.d.ts.map +1 -1
  43. package/dist/services/sdd/task-manager.d.ts.map +1 -1
  44. package/dist/session-stats.d.ts.map +1 -1
  45. package/dist/settings-menu.d.ts.map +1 -1
  46. package/dist/slash-commands/command-context.d.ts +2 -0
  47. package/dist/slash-commands/command-context.d.ts.map +1 -1
  48. package/dist/slash-commands/goal.d.ts +1 -1
  49. package/dist/slash-commands/goal.d.ts.map +1 -1
  50. package/dist/slash-commands/sdd.d.ts.map +1 -1
  51. package/dist/webui-server/kanban-host-adapter.d.ts +1 -1
  52. package/dist/webui-server/kanban-host-adapter.d.ts.map +1 -1
  53. package/dist/webui-server/session-start-payload.d.ts +4 -0
  54. package/dist/webui-server/session-start-payload.d.ts.map +1 -1
  55. package/dist/{webui-server-ZF5KVGC5.js → webui-server-XKFUOKMJ.js} +155 -132
  56. package/dist/webui-server-XKFUOKMJ.js.map +7 -0
  57. package/dist/webui-server-options.d.ts +7 -0
  58. package/dist/webui-server-options.d.ts.map +1 -1
  59. package/dist/webui-server.d.ts.map +1 -1
  60. package/dist/wiring/command-host-state.d.ts.map +1 -1
  61. package/dist/wiring/hq-telemetry.d.ts +4 -3
  62. package/dist/wiring/hq-telemetry.d.ts.map +1 -1
  63. package/dist/wiring/lifecycle-plugins.d.ts +5 -8
  64. package/dist/wiring/lifecycle-plugins.d.ts.map +1 -1
  65. package/dist/wiring/metrics.d.ts +1 -1
  66. package/dist/wiring/metrics.d.ts.map +1 -1
  67. package/dist/wiring/runtime-lifecycle-deps.d.ts.map +1 -1
  68. package/dist/wiring/sage.d.ts.map +1 -1
  69. package/dist/wiring/sdd-handlers.d.ts.map +1 -1
  70. package/package.json +22 -22
  71. package/dist/chunk-BQCAJ7FI.js.map +0 -7
  72. package/dist/chunk-OI7GWJDS.js.map +0 -7
  73. package/dist/chunk-WY7KKOUK.js.map +0 -7
  74. package/dist/chunk-Z7YGASQP.js.map +0 -7
  75. package/dist/cli-main-TOAWVJDY.js.map +0 -7
  76. package/dist/execution-O7I5DYE6.js.map +0 -7
  77. package/dist/mailbox-serve-2UWHEN3M.js.map +0 -7
  78. package/dist/webui-server-ZF5KVGC5.js.map +0 -7
  79. /package/dist/{hq-JUYKUNJX.js.map → hq-GBPGXIR5.js.map} +0 -0
  80. /package/dist/{hq-server-6AQ3BXZA.js.map → hq-server-YQUUMTPU.js.map} +0 -0
  81. /package/dist/{sdd-runtime-JG4EEKL6.js.map → sdd-runtime-AIJCJGWC.js.map} +0 -0
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/hq-publisher.ts", "../src/kanban-hq-sync.ts"],
4
- "sourcesContent": ["import { createHqPublisherFromEnv, resolveHqConfig } from '@wrongstack/core/hq';\nimport type { CreateHqPublisherOptions } from '@wrongstack/core/hq';\nimport type { HqPublisher, HqSocketLike } from '@wrongstack/core/hq';\nimport { WebSocket } from 'ws';\nimport { createKanbanHqSync } from './kanban-hq-sync.js';\n\ntype CliHqPublisherOptions = Omit<CreateHqPublisherOptions, 'socketFactory'> & {\n socketFactory?: CreateHqPublisherOptions['socketFactory'];\n};\n\nexport interface CliHqConnection {\n getPublisher(): HqPublisher | undefined;\n stop(): void;\n}\n\nexport interface CliHqConnectionOptions extends CliHqPublisherOptions {\n onConnect?: ((publisher: HqPublisher) => void) | undefined;\n retryIntervalMs?: number | undefined;\n}\n\nfunction nodeWsSocketFactory(url: string): HqSocketLike {\n return new WebSocket(url) as unknown as HqSocketLike;\n}\n\nexport function createCliHqPublisher(options: CliHqPublisherOptions): HqPublisher | undefined {\n return createHqPublisherFromEnv({\n ...options,\n socketFactory: options.socketFactory ?? nodeWsSocketFactory,\n });\n}\n\nfunction hqExplicitlyDisabled(options: CliHqPublisherOptions): boolean {\n const envEnabled = process.env['WRONGSTACK_HQ_ENABLED']?.trim();\n if (envEnabled !== undefined && envEnabled.length > 0) return envEnabled === '0';\n if (options.config?.enabled !== undefined) return options.config.enabled === false;\n return options.appConfig?.hq?.enabled === false;\n}\n\nfunction resolvedHqConnectionKey(options: CliHqPublisherOptions): string | undefined {\n if (options.config !== undefined) {\n return options.config.enabled === false\n ? undefined\n : `${options.config.url}\\n${options.config.token ?? ''}`;\n }\n const config = resolveHqConfig({ config: options.appConfig?.hq });\n return config === undefined ? undefined : `${config.url}\\n${config.token ?? ''}`;\n}\n\nexport function startCliHqConnection(options: CliHqConnectionOptions): CliHqConnection {\n const shouldOwnKanbanSync = options.onKanbanSnapshot === undefined;\n let kanbanSync: ReturnType<typeof createKanbanHqSync> | undefined;\n const publisherOptions: CliHqConnectionOptions = shouldOwnKanbanSync\n ? {\n ...options,\n onKanbanSnapshot: (snapshot) => kanbanSync?.handleRemote(snapshot),\n }\n : options;\n let publisher: HqPublisher | undefined;\n let publisherKey: string | undefined;\n let timer: ReturnType<typeof setInterval> | undefined;\n\n const tryConnect = (): void => {\n const nextKey = resolvedHqConnectionKey(options);\n if (nextKey === undefined) return;\n if (publisher !== undefined && publisherKey === nextKey) return;\n\n publisher?.close();\n publisher = undefined;\n publisherKey = undefined;\n\n const next = createCliHqPublisher(publisherOptions);\n if (next === undefined) return;\n publisher = next;\n publisherKey = nextKey;\n if (shouldOwnKanbanSync) {\n kanbanSync?.stop();\n kanbanSync = createKanbanHqSync(options.projectRoot, next.project.projectId);\n }\n next.connect();\n if (kanbanSync !== undefined) {\n kanbanSync.attachPublisher(next).catch((error: unknown) => {\n process.emitWarning(\n `WrongStack kanban HQ sync attach failed (best-effort, retried on reconnect): ${\n error instanceof Error ? error.message : String(error)\n }`,\n { code: 'WRONGSTACK_HQ_KANBAN_SYNC_FAILED' },\n );\n });\n }\n options.onConnect?.(next);\n };\n\n tryConnect();\n if (!hqExplicitlyDisabled(options)) {\n timer = setInterval(tryConnect, options.retryIntervalMs ?? 2_500);\n timer.unref?.();\n }\n\n return {\n getPublisher: () => publisher,\n stop: () => {\n if (timer !== undefined) {\n clearInterval(timer);\n timer = undefined;\n }\n kanbanSync?.stop();\n publisher?.close();\n publisher = undefined;\n publisherKey = undefined;\n },\n };\n}\n", "import type {\n HqKanbanBoardRecord,\n HqKanbanSnapshotPayload,\n HqKanbanTombstone,\n HqPublisher,\n} from '@wrongstack/core/hq';\nimport { deriveHqProjectId } from '@wrongstack/core/hq';\nimport {\n bridgeKanbanSupervisor,\n deleteBoard,\n type KanbanBoard,\n listBoardIds,\n readBoard,\n readKanbanMetadata,\n writeBoard,\n writeKanbanMetadata,\n} from '@wrongstack/kanban';\n\ninterface LocalSyncState {\n boards: Record<string, { revision: number; updatedAt: string }>;\n tombstones: Record<string, HqKanbanTombstone>;\n}\n\nexport interface KanbanHqSync {\n attachPublisher(publisher: HqPublisher): Promise<void>;\n handleRemote(snapshot: HqKanbanSnapshotPayload): Promise<void>;\n /** Explicit refresh seam for hosts/tests; daemon events call the same path. */\n refresh(boardId?: string): void;\n stop(): void;\n}\n\nconst SYNC_STATE_KEY = 'hq-sync-state-v1';\n// The HQ WebSocket server deliberately caps inbound frames at 1 MiB. Keep\n// project-state payloads comfortably below that limit so the event envelope,\n// UTF-8 expansion, and future protocol metadata still have headroom.\nconst MAX_KANBAN_SNAPSHOT_PAYLOAD_BYTES = 512 * 1024;\nconst KANBAN_TOMBSTONE_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;\n\n// Cap the number of boards read per publish cycle so a large project doesn't\n// load hundreds of board payloads in a single burst. Remaining boards spill to\n// the next cycle.\nconst MAX_BOARDS_PER_PUBLISH = 50;\n\n// Kanban\u2194HQ sync is best-effort telemetry: every detached promise in this\n// module must land here instead of surfacing as an unhandled rejection (which\n// kills the host process on Node \u226515). Failed publishes self-heal \u2014 the state\n// fingerprint stays unwritten, so the next watcher event or reconnect\n// full-snapshot re-detects the same diff.\nfunction warnSyncFailure(error: unknown): void {\n process.emitWarning(\n `WrongStack kanban HQ sync failed (best-effort, will retry on next change): ${\n error instanceof Error ? error.message : String(error)\n }`,\n { code: 'WRONGSTACK_HQ_KANBAN_SYNC_FAILED' },\n );\n}\n\nexport function createKanbanHqSync(\n projectRoot: string,\n projectId = deriveHqProjectId(projectRoot),\n): KanbanHqSync {\n let publisher: HqPublisher | undefined;\n let unsubscribeDaemon: (() => void) | undefined;\n let daemonConnectionCount = 0;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const pendingBoardIds = new Set<string>();\n let fullRescanPending = false;\n let applyingRemote = false;\n let stopped = false;\n let operationChain: Promise<void> = Promise.resolve();\n // Remote snapshots waiting to be applied, coalesced per boardId (latest\n // revision wins). Applying a snapshot does one IPC read/write per board;\n // when snapshots arrive faster than the owner can commit them, queueing each one\n // individually on `operationChain` retains every payload in memory \u2014 an\n // unbounded queue that has driven long fleet sessions into OOM. Coalescing\n // bounds pending memory to one record per distinct board.\n let pendingRemote: HqKanbanSnapshotPayload | null = null;\n let remoteApplyQueued = false;\n\n const runExclusive = <T>(operation: () => Promise<T>): Promise<T> => {\n let resolveResult!: (value: T | PromiseLike<T>) => void;\n let rejectResult!: (reason?: unknown) => void;\n const result = new Promise<T>((resolve, reject) => {\n resolveResult = resolve;\n rejectResult = reject;\n });\n operationChain = operationChain.then(async () => {\n try {\n resolveResult(await operation());\n } catch (error) {\n rejectResult(error);\n }\n });\n return result;\n };\n\n // Schedule the next publish cycle with a fixed debounce. The caller\n // (schedulePublish) already collapses rapid events into one batch, and\n // runExclusive serializes execution \u2014 no Date.now()-based interval is\n // needed because the 100ms debounce plus serialization chain already\n // prevents back-to-back publish bursts.\n const scheduleNext = (): void => {\n if (stopped || timer !== undefined || applyingRemote) return;\n timer = setTimeout(() => {\n timer = undefined;\n const rescan = fullRescanPending;\n const dirtyBoardIds = [...pendingBoardIds];\n fullRescanPending = false;\n pendingBoardIds.clear();\n runExclusive(() => publishLocal(false, rescan ? undefined : dirtyBoardIds)).catch(\n warnSyncFailure,\n );\n }, 100);\n timer.unref?.();\n };\n\n const schedulePublish = (boardId: string | null): void => {\n if (stopped || applyingRemote || publisher === undefined) return;\n if (boardId === null) fullRescanPending = true;\n else pendingBoardIds.add(boardId);\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n scheduleNext();\n };\n\n const publishLocal = async (\n fullSnapshot: boolean,\n dirtyBoardIds?: readonly string[],\n ): Promise<void> => {\n const target = publisher;\n if (target === undefined || stopped) return;\n const state = await readState(projectRoot);\n const prunedTombstones = pruneExpiredTombstones(state, Date.now());\n let stateChanged = fullSnapshot || prunedTombstones;\n const boards: HqKanbanBoardRecord[] = [];\n const tombstones: HqKanbanTombstone[] = [];\n const fullScan = fullSnapshot || dirtyBoardIds === undefined;\n const allBoardIds: readonly string[] = fullScan\n ? await listBoardIds(projectRoot)\n : dirtyBoardIds;\n let boardIds = allBoardIds;\n\n // Batch limit: only cap true delta publishes, never a full scan (initial\n // snapshot OR full rescan). When fullScan is true, ALL boards must be\n // loaded into `present` so the deletion-detection loop below does NOT\n // false-tombstone the boards this cycle didn't reach.\n if (!fullScan && boardIds.length > MAX_BOARDS_PER_PUBLISH) {\n for (const id of boardIds.slice(MAX_BOARDS_PER_PUBLISH)) {\n pendingBoardIds.add(id);\n }\n scheduleNext();\n boardIds = boardIds.slice(0, MAX_BOARDS_PER_PUBLISH);\n }\n\n const present = new Set<string>();\n const now = new Date().toISOString();\n for (const boardId of boardIds) {\n const board = await readBoard(projectRoot, boardId);\n if (board === null) {\n const known = state.boards[boardId];\n if (known !== undefined && state.tombstones[boardId] === undefined) {\n const tombstone = {\n boardId,\n revision: known.revision + 1,\n deletedAt: now,\n };\n state.tombstones[boardId] = tombstone;\n tombstones.push(tombstone);\n stateChanged = true;\n }\n continue;\n }\n present.add(boardId);\n const record = boardRecord(board);\n const known = state.boards[boardId];\n const hadTombstone = state.tombstones[boardId] !== undefined;\n if (\n fullSnapshot ||\n hadTombstone ||\n known === undefined ||\n compareVersions(known.revision, known.updatedAt, record.revision, record.updatedAt) !== 0\n ) {\n boards.push(record);\n }\n if (\n known === undefined ||\n known.revision !== record.revision ||\n known.updatedAt !== record.updatedAt ||\n hadTombstone\n ) {\n stateChanged = true;\n }\n state.boards[boardId] = { revision: record.revision, updatedAt: record.updatedAt };\n delete state.tombstones[boardId];\n }\n if (fullScan) {\n for (const [boardId, known] of Object.entries(state.boards)) {\n if (\n present.has(boardId) ||\n state.tombstones[boardId] !== undefined ||\n isExpiredVersion(known.updatedAt, Date.now())\n ) {\n continue;\n }\n const tombstone = {\n boardId,\n revision: known.revision + 1,\n deletedAt: now,\n };\n state.tombstones[boardId] = tombstone;\n tombstones.push(tombstone);\n stateChanged = true;\n }\n }\n if (stateChanged) await writeState(projectRoot, state);\n\n // A filesystem event caused by applying an HQ snapshot often arrives after\n // `applyingRemote` has been cleared. The persisted state fingerprint above\n // lets that event collapse to an empty delta instead of echoing the entire\n // project back to HQ and starting an N-client broadcast storm.\n if (!fullSnapshot && boards.length === 0 && tombstones.length === 0) return;\n\n for (const payload of chunkSnapshotPayload(\n projectId,\n now,\n boards,\n fullSnapshot ? Object.values(state.tombstones) : tombstones,\n )) {\n target.publishEvent({\n type: 'kanban.snapshot',\n payload,\n maxSummaryLength: 100_000,\n });\n }\n };\n\n const attachPublisher = (next: HqPublisher): Promise<void> => {\n publisher = next;\n return runExclusive(async () => {\n if (\n unsubscribeDaemon === undefined &&\n (process.env['NODE_ENV'] !== 'test' || process.env['WRONGSTACK_KANBAN_FORCE_IPC'] === '1')\n ) {\n unsubscribeDaemon = bridgeKanbanSupervisor(\n projectRoot,\n (event) => {\n const data = event.data as { boardId?: string } | undefined;\n // Non-board-scoped events conservatively request a full rescan.\n schedulePublish(data?.boardId ?? null);\n },\n {\n autoReconnect: true,\n reconnectDelayMs: 1_000,\n // Events emitted while the daemon was down cannot be replayed.\n // Reconcile the authoritative snapshot after every connection.\n onConnected: () => {\n daemonConnectionCount++;\n if (daemonConnectionCount > 1) schedulePublish(null);\n },\n },\n );\n }\n await publishLocal(true);\n });\n };\n\n const applyRemote = async (snapshot: HqKanbanSnapshotPayload): Promise<void> => {\n if (snapshot.projectId !== projectId || stopped) return;\n applyingRemote = true;\n try {\n const state = await readState(projectRoot);\n for (const remote of snapshot.boards) {\n const local = await readBoard(projectRoot, remote.boardId);\n if (\n local !== null &&\n compareVersions(\n local.revision ?? 0,\n local.updatedAt,\n remote.revision,\n remote.updatedAt,\n ) >= 0\n ) {\n continue;\n }\n await writeBoard(projectRoot, remote.board as unknown as KanbanBoard);\n state.boards[remote.boardId] = {\n revision: remote.revision,\n updatedAt: remote.updatedAt,\n };\n delete state.tombstones[remote.boardId];\n }\n for (const tombstone of snapshot.tombstones) {\n const local = await readBoard(projectRoot, tombstone.boardId);\n if (\n local !== null &&\n compareVersions(\n local.revision ?? 0,\n local.updatedAt,\n tombstone.revision,\n tombstone.deletedAt,\n ) >= 0\n ) {\n continue;\n }\n if (local !== null) await deleteBoard(projectRoot, tombstone.boardId);\n state.boards[tombstone.boardId] = {\n revision: Math.max(0, tombstone.revision - 1),\n updatedAt: tombstone.deletedAt,\n };\n state.tombstones[tombstone.boardId] = tombstone;\n }\n await writeState(projectRoot, state);\n } finally {\n applyingRemote = false;\n }\n };\n\n const handleRemote = (snapshot: HqKanbanSnapshotPayload): Promise<void> => {\n if (snapshot.projectId !== projectId || stopped) return Promise.resolve();\n pendingRemote =\n pendingRemote === null ? snapshot : coalesceRemoteSnapshots(pendingRemote, snapshot);\n // At most one apply pass is ever queued: it drains whatever has been\n // coalesced by the time it runs, so bursts collapse instead of chaining.\n if (remoteApplyQueued) return Promise.resolve();\n remoteApplyQueued = true;\n return runExclusive(async () => {\n remoteApplyQueued = false;\n const batch = pendingRemote;\n pendingRemote = null;\n if (batch !== null) await applyRemote(batch);\n }).catch(warnSyncFailure);\n };\n\n return {\n attachPublisher,\n handleRemote,\n refresh: (boardId?: string) => schedulePublish(boardId ?? null),\n stop: () => {\n stopped = true;\n if (timer !== undefined) clearTimeout(timer);\n pendingBoardIds.clear();\n fullRescanPending = false;\n pendingRemote = null;\n unsubscribeDaemon?.();\n unsubscribeDaemon = undefined;\n publisher = undefined;\n },\n };\n}\n\nfunction boardRecord(board: KanbanBoard): HqKanbanBoardRecord {\n return {\n boardId: board.id,\n revision: board.revision ?? 0,\n updatedAt: board.updatedAt,\n board: structuredClone(board) as unknown as Record<string, unknown>,\n };\n}\n\nfunction chunkSnapshotPayload(\n projectId: string,\n generatedAt: string,\n boards: HqKanbanBoardRecord[],\n tombstones: HqKanbanTombstone[],\n): HqKanbanSnapshotPayload[] {\n const chunks: HqKanbanSnapshotPayload[] = [];\n const emptyPayload = (): HqKanbanSnapshotPayload => ({\n projectId,\n generatedAt,\n boards: [],\n tombstones: [],\n });\n const emptyPayloadBytes = Buffer.byteLength(JSON.stringify(emptyPayload()), 'utf8');\n let currentBoards: HqKanbanBoardRecord[] = [];\n let currentTombstones: HqKanbanTombstone[] = [];\n let currentBytes = emptyPayloadBytes;\n\n const pushCurrent = (): void => {\n if (currentBoards.length === 0 && currentTombstones.length === 0) return;\n chunks.push({\n projectId,\n generatedAt,\n boards: currentBoards,\n tombstones: currentTombstones,\n });\n currentBoards = [];\n currentTombstones = [];\n currentBytes = emptyPayloadBytes;\n };\n\n const append = (\n kind: 'boards' | 'tombstones',\n item: HqKanbanBoardRecord | HqKanbanTombstone,\n ): void => {\n const itemBytes = Buffer.byteLength(JSON.stringify(item), 'utf8');\n const itemCount = kind === 'boards' ? currentBoards.length : currentTombstones.length;\n const additionalBytes = itemBytes + (itemCount > 0 ? 1 : 0);\n if (emptyPayloadBytes + itemBytes > MAX_KANBAN_SNAPSHOT_PAYLOAD_BYTES) {\n process.emitWarning(\n `Kanban ${kind === 'boards' ? 'board' : 'tombstone'} ${item.boardId} exceeds the ${MAX_KANBAN_SNAPSHOT_PAYLOAD_BYTES}-byte snapshot chunk target`,\n { code: 'WRONGSTACK_HQ_KANBAN_OVERSIZED_RECORD' },\n );\n }\n if (\n (currentBoards.length > 0 || currentTombstones.length > 0) &&\n currentBytes + additionalBytes > MAX_KANBAN_SNAPSHOT_PAYLOAD_BYTES\n ) {\n pushCurrent();\n }\n if (kind === 'boards') {\n currentBoards.push(item as HqKanbanBoardRecord);\n } else {\n currentTombstones.push(item as HqKanbanTombstone);\n }\n currentBytes +=\n itemBytes +\n (kind === 'boards'\n ? currentBoards.length > 1\n ? 1\n : 0\n : currentTombstones.length > 1\n ? 1\n : 0);\n };\n\n for (const board of boards) append('boards', board);\n for (const tombstone of tombstones) append('tombstones', tombstone);\n pushCurrent();\n\n // An empty snapshot is meaningful on first attach: it registers the project\n // state channel even when the project has no boards yet.\n return chunks.length > 0 ? chunks : [emptyPayload()];\n}\n\n/** Merge two pending remote snapshots into one, keeping \u2014 per boardId \u2014 the\n * record with the highest (revision, timestamp), whether board or tombstone.\n * Equivalent to applying both snapshots in arrival order when revisions are\n * monotonically increasing (which HQ guarantees in practice): the only\n * divergence is a lower-revision board arriving after a higher-revision\n * tombstone for the same boardId, where coalescing correctly keeps the\n * tombstone whereas naive sequential apply would resurrect the stale board. */\nfunction coalesceRemoteSnapshots(\n older: HqKanbanSnapshotPayload,\n newer: HqKanbanSnapshotPayload,\n): HqKanbanSnapshotPayload {\n type RemoteRecord =\n | { kind: 'board'; revision: number; time: string; record: HqKanbanBoardRecord }\n | { kind: 'tombstone'; revision: number; time: string; record: HqKanbanTombstone };\n const byBoardId = new Map<string, RemoteRecord>();\n const put = (candidate: RemoteRecord, boardId: string): void => {\n const existing = byBoardId.get(boardId);\n // On a full (revision, time) tie, prefer the newer-arriving record so a\n // same-timestamp content update in the second snapshot is not silently\n // discarded in favor of the older-arriving one.\n if (\n existing === undefined ||\n compareVersions(existing.revision, existing.time, candidate.revision, candidate.time) <= 0\n ) {\n byBoardId.set(boardId, candidate);\n }\n };\n for (const snapshot of [older, newer]) {\n for (const record of snapshot.boards) {\n put(\n { kind: 'board', revision: record.revision, time: record.updatedAt, record },\n record.boardId,\n );\n }\n for (const record of snapshot.tombstones) {\n put(\n { kind: 'tombstone', revision: record.revision, time: record.deletedAt, record },\n record.boardId,\n );\n }\n }\n const merged: HqKanbanSnapshotPayload = {\n projectId: newer.projectId,\n generatedAt: newer.generatedAt,\n boards: [],\n tombstones: [],\n };\n for (const entry of byBoardId.values()) {\n if (entry.kind === 'board') merged.boards.push(entry.record);\n else merged.tombstones.push(entry.record);\n }\n return merged;\n}\n\nfunction compareVersions(\n aRevision: number,\n aTime: string,\n bRevision: number,\n bTime: string,\n): number {\n if (aRevision !== bRevision) return aRevision - bRevision;\n return aTime.localeCompare(bTime);\n}\n\nfunction isExpiredVersion(timestamp: string, now: number): boolean {\n const parsed = Date.parse(timestamp);\n return !Number.isNaN(parsed) && parsed <= now - KANBAN_TOMBSTONE_RETENTION_MS;\n}\n\nfunction pruneExpiredTombstones(state: LocalSyncState, now: number): boolean {\n let changed = false;\n for (const [boardId, tombstone] of Object.entries(state.tombstones)) {\n if (!isExpiredVersion(tombstone.deletedAt, now)) continue;\n delete state.tombstones[boardId];\n delete state.boards[boardId];\n changed = true;\n }\n return changed;\n}\n\nasync function readState(projectRoot: string): Promise<LocalSyncState> {\n try {\n const raw = await readKanbanMetadata(projectRoot, SYNC_STATE_KEY);\n if (raw === null) return { boards: {}, tombstones: {} };\n const parsed = JSON.parse(raw) as Partial<LocalSyncState>;\n return { boards: parsed.boards ?? {}, tombstones: parsed.tombstones ?? {} };\n } catch {\n return { boards: {}, tombstones: {} };\n }\n}\n\nasync function writeState(projectRoot: string, state: LocalSyncState): Promise<void> {\n await writeKanbanMetadata(projectRoot, SYNC_STATE_KEY, JSON.stringify(state));\n}\n"],
5
- "mappings": "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",
6
- "names": ["known"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/tui-thinking-word.ts", "../src/settings-menu.ts", "../src/services/suggestion-store.ts"],
4
- "sourcesContent": ["export const DEFAULT_TUI_THINKING_WORD = 'thinking';\nexport const MAX_TUI_THINKING_WORD_LENGTH = 16;\n\n/** Normalize autonomy.thinkingWord to a single short word for the TUI statusline. */\nexport function normalizeTuiThinkingWord(value: unknown): string {\n if (typeof value !== 'string') return DEFAULT_TUI_THINKING_WORD;\n const word = value.trim();\n if (word.length === 0 || word.length > MAX_TUI_THINKING_WORD_LENGTH) {\n return DEFAULT_TUI_THINKING_WORD;\n }\n if (!/^[\\p{L}\\p{N}_-]+$/u.test(word)) return DEFAULT_TUI_THINKING_WORD;\n return word;\n}\n", "import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { atomicWrite, deepMerge } from '@wrongstack/core/utils';\nimport { ConfigError, ERROR_CODES, FsError, type SecretVault } from '@wrongstack/core/types';\nimport type { ConfigStore } from '@wrongstack/core/types';\nimport { decryptConfigSecrets, encryptConfigSecrets } from '@wrongstack/core/security';\n\n/** Configuration and storage dependencies needed to persist a setting.\n * This is safe to call from non-interactive surfaces such as the TUI,\n * headless runs, and the arg-driven `/settings` slash command. */\nexport interface PersistSettingDeps {\n configStore: ConfigStore;\n /** Path to the active profile config (~/.wrongstack/profiles/<name>/config.json). */\n profileConfigPath: string;\n /** Per-project config path (<project>/.wrongstack/config.json).\n * Used when configScope === 'project'. Lives inside the project\n * root so it can be gitignored or team-shared. */\n inProjectConfigPath?: string | undefined;\n vault: SecretVault;\n /** Force writes to the active profile config even when configScope is project. */\n forceGlobal?: boolean | undefined;\n /**\n * Optional resolver that recomputes the profile config path for a given\n * profile name. When set, {@link resolveActualTarget} uses this to\n * re-resolve the target path when `decrypted.activeProfile` differs from\n * the one used to compute `profileConfigPath`. This prevents the write\n * from landing in the wrong profile when the mutator changes activeProfile.\n */\n resolveProfilePath?: ((profileName: string) => string) | undefined;\n}\n\nexport function resolvePersistPath(deps: PersistSettingDeps): string {\n if (deps.forceGlobal) return deps.profileConfigPath;\n const scope = (deps.configStore.get() as { configScope?: string | undefined }).configScope;\n if (scope === 'project' && deps.inProjectConfigPath) {\n return deps.inProjectConfigPath;\n }\n // Default user scope always means the active profile, never the root bootstrap.\n return deps.profileConfigPath;\n}\n\n/**\n * Re-resolve the target path after a mutator may have changed configScope.\n * Handles the two settings targets: project config or active profile config.\n */\nexport function resolveActualTarget(\n deps: PersistSettingDeps,\n decrypted: Record<string, unknown>,\n fallbackPath: string,\n): string {\n if (deps.forceGlobal) return deps.profileConfigPath;\n const newScope = decrypted.configScope as string | undefined;\n // Recompute profile path when activeProfile changed in the mutation block.\n const newProfileName = decrypted.activeProfile as string | undefined;\n const effectiveProfilePath =\n newProfileName && deps.resolveProfilePath\n ? deps.resolveProfilePath(newProfileName)\n : deps.profileConfigPath;\n if (newScope === 'project' && deps.inProjectConfigPath) {\n return deps.inProjectConfigPath;\n }\n if (newScope === 'global') {\n return effectiveProfilePath;\n }\n return fallbackPath;\n}\n\n/**\n * Returns true when the target path is the trusted active-profile config, so\n * secrets are NOT stripped before writing.\n * Returns false for project-scoped paths where credentials must be filtered.\n */\nfunction isProfileOrGlobalTarget(actualTarget: string, deps: PersistSettingDeps): boolean {\n return actualTarget === deps.profileConfigPath;\n}\n\nasync function ensureProjectDir(filePath: string): Promise<void> {\n const dir = path.dirname(filePath);\n try {\n await fs.mkdir(dir, { recursive: true });\n } catch {\n // Directory already exists or is inaccessible \u2014 the write will surface the real error.\n }\n}\n\n/**\n * When configScope changed mid-save, the write target differs from the file\n * that was read. Read the *destination* file, decrypt it, and deep-merge the\n * mutated `source` on top (prefer-patch so the user's explicit changes win).\n * This prevents a sparse source (e.g. project config without credentials)\n * from clobbering credential-bearing fields in the destination (e.g. profile\n * config with apiKey / providers / mcpServers).\n *\n * Returns `source` unchanged when destination does not exist yet.\n */\nasync function mergeWithDestinationIfExists(\n destPath: string,\n source: Record<string, unknown>,\n vault: SecretVault,\n): Promise<Record<string, unknown>> {\n let destRaw: string;\n try {\n destRaw = await fs.readFile(destPath, 'utf8');\n } catch (err) {\n // Only swallow ENOENT (destination doesn't exist yet).\n // All other errors (EACCES, EIO, etc.) propagate as-is.\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return source;\n }\n throw err;\n }\n let destParsed: Record<string, unknown>;\n try {\n destParsed = JSON.parse(destRaw) as Record<string, unknown>;\n } catch (err) {\n // Corrupt destination \u2014 don't merge, just write our content.\n process.stderr.write(JSON.stringify({\n level: 'warn',\n event: 'settings_menu_merge_skipped',\n message: `Skipping merge into corrupt destination file: ${destPath}`,\n timestamp: new Date().toISOString(),\n }) + '\\n');\n void err;\n return source;\n }\n const destDecrypted = decryptConfigSecrets(destParsed, vault) as Record<string, unknown>;\n return deepMerge(destDecrypted, source) as Record<string, unknown>;\n}\n\n/**\n * Fields that are safe to persist in a per-project `.wrongstack/config.json`.\n * Credential-bearing fields (apiKey, providers, sync) MUST NOT appear here \u2014\n * they stay in the active profile config only. The active profile always gets the\n * full unfiltered object.\n *\n * When adding a field here, ask: \"Would I commit this to a shared repo?\"\n * If the answer is no, it doesn't belong on this list.\n */\nconst PROJECT_SAFE_FIELDS = new Set([\n 'provider',\n 'model',\n 'fallbackModels',\n // Kept in sync with core's IN_PROJECT_ALLOWED_KEYS (config-loader.ts): these\n // model-routing prefs are explicitly safe to READ from a per-project config,\n // so the writer must not strip them \u2014 otherwise a scope=project /fallback\n // profile/fav write (or a TUI full-config write under project scope) would be\n // silently dropped while the loader would happily accept them on next boot.\n 'fallbackProfiles',\n 'favoriteModels',\n 'favoriteModelsOnly',\n 'modelAvailabilitySchedule',\n 'fallbackAuto',\n 'models',\n 'modelMatrix',\n 'maxConcurrent',\n 'autonomy',\n 'hints',\n 'nextPrediction',\n 'debugStream',\n 'configScope',\n 'yolo',\n 'features',\n 'context',\n 'log',\n 'session',\n 'indexing',\n 'tools',\n 'launch',\n 'circuitBreaker',\n 'adaptiveConcurrency',\n 'modelRuntime',\n]);\n\n/**\n * Strip credential-bearing and machine-specific fields from a config object\n * so it is safe to write into a per-project `.wrongstack/config.json` file.\n * Returns a new object \u2014 the original is not mutated.\n */\nexport function filterSafeForProject(cfg: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(cfg)) {\n if (PROJECT_SAFE_FIELDS.has(key)) {\n out[key] = value;\n }\n }\n return out;\n}\n\n/**\n * Read the config file, apply `mutator` to its `autonomy` block, and write it\n * back atomically (then mirror into the in-memory config store). Pure I/O \u2014 no\n * terminal interaction \u2014 so it works identically under the plain REPL and the\n * Ink TUI.\n */\nexport async function persistAutonomySetting(\n deps: PersistSettingDeps,\n mutator: (autonomy: {\n autoProceedDelayMs?: number | undefined;\n defaultMode?: string | undefined;\n enhance?: boolean | undefined;\n enhanceDelayMs?: number | undefined;\n enhanceLanguage?: string | undefined;\n }) => void,\n): Promise<void> {\n const targetPath = resolvePersistPath(deps);\n await ensureProjectDir(targetPath);\n\n let raw: string;\n let fileExists = true;\n try {\n raw = await fs.readFile(targetPath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw new FsError({\n message: `Could not read ${targetPath}`,\n code: ERROR_CODES.FS_READ_FAILED,\n path: targetPath,\n cause: err,\n });\n }\n fileExists = false;\n raw = '{}';\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n if (fileExists) {\n throw new ConfigError({\n message: `Config at ${targetPath} is not valid JSON`,\n code: ERROR_CODES.CONFIG_PARSE_FAILED,\n context: { path: targetPath },\n cause: err,\n });\n }\n parsed = {};\n }\n\n const decrypted = decryptConfigSecrets(parsed, deps.vault) as Record<string, unknown>;\n const autonomy = (decrypted.autonomy as Record<string, unknown>) ?? {};\n mutator(\n autonomy as { autoProceedDelayMs?: number | undefined; defaultMode?: string | undefined },\n );\n decrypted.autonomy = autonomy;\n\n // Re-resolve path \u2014 the mutator might have changed configScope.\n const actualTarget = resolveActualTarget(deps, decrypted as Record<string, unknown>, targetPath);\n if (actualTarget !== targetPath) {\n await ensureProjectDir(actualTarget);\n }\n\n // When configScope changed mid-save, merge into the destination file's\n // existing content so credential-bearing fields are preserved.\n const effectiveConfig =\n actualTarget !== targetPath\n ? await mergeWithDestinationIfExists(actualTarget, decrypted, deps.vault)\n : decrypted;\n\n // When writing to the project-local config, strip credentials so\n // apiKey / providers / sync never leak into a per-project file.\n const toWrite =\n isProfileOrGlobalTarget(actualTarget, deps) ? effectiveConfig : filterSafeForProject(effectiveConfig);\n\n const encrypted = encryptConfigSecrets(toWrite, deps.vault);\n await atomicWrite(actualTarget, JSON.stringify(encrypted, null, 2), { mode: 0o600 });\n\n // Also update the in-memory config store so changes are immediately visible\n deps.configStore.update({\n autonomy: decrypted.autonomy as Parameters<typeof deps.configStore.update>[0]['autonomy'],\n });\n}\n\n/**\n * Read the config file, apply `mutator` to the full decrypted object, and\n * write it back atomically. This is used for small top-level settings that do\n * not warrant a dedicated helper.\n */\nexport async function persistConfigSetting(\n deps: PersistSettingDeps,\n mutator: (config: Record<string, unknown>) => void,\n): Promise<void> {\n const targetPath = resolvePersistPath(deps);\n await ensureProjectDir(targetPath);\n\n let raw: string;\n let fileExists = true;\n try {\n raw = await fs.readFile(targetPath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw new FsError({\n message: `Could not read ${targetPath}`,\n code: ERROR_CODES.FS_READ_FAILED,\n path: targetPath,\n cause: err,\n });\n }\n fileExists = false;\n raw = '{}';\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n if (fileExists) {\n throw new ConfigError({\n message: `Config at ${targetPath} is not valid JSON`,\n code: ERROR_CODES.CONFIG_PARSE_FAILED,\n context: { path: targetPath },\n cause: err,\n });\n }\n parsed = {};\n }\n\n const decrypted = decryptConfigSecrets(parsed, deps.vault) as Record<string, unknown>;\n mutator(decrypted);\n\n // If the mutator changed configScope, re-resolve the target path.\n // Without this, a scope change from 'project' \u2192 'global' would write\n // to the old project path instead of the new global one.\n const actualTarget = resolveActualTarget(deps, decrypted, targetPath);\n\n // Ensure the directory exists if we're writing to a new path\n if (actualTarget !== targetPath) {\n await ensureProjectDir(actualTarget);\n }\n\n // When configScope changed mid-save, merge into the destination file's\n // existing content so credential-bearing fields are preserved.\n const effectiveConfig =\n actualTarget !== targetPath\n ? await mergeWithDestinationIfExists(actualTarget, decrypted, deps.vault)\n : decrypted;\n\n // When writing to the project-local config, strip credentials so\n // apiKey / providers / sync never leak into a per-project file.\n const toWrite =\n isProfileOrGlobalTarget(actualTarget, deps) ? effectiveConfig : filterSafeForProject(effectiveConfig);\n\n const encrypted = encryptConfigSecrets(toWrite, deps.vault);\n await atomicWrite(actualTarget, JSON.stringify(encrypted, null, 2), { mode: 0o600 });\n deps.configStore.update(decrypted as Parameters<typeof deps.configStore.update>[0]);\n}\n\n/**\n * Persist Telegram plugin config to `extensions.telegram` in the active profile config\n * file. Mirrors `persistAutonomySetting` \u2014 reads the config, applies the\n * mutator, encrypts secrets, writes atomically, then updates ConfigStore.\n */\nexport async function persistTelegramConfig(\n deps: PersistSettingDeps,\n mutator: (telegram: Record<string, unknown>) => void,\n): Promise<void> {\n const targetPath = deps.profileConfigPath;\n let raw: string;\n let fileExists = true;\n try {\n raw = await fs.readFile(targetPath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw new FsError({\n message: `Could not read ${targetPath}: ${(err as Error).message}`,\n code: 'FS_READ_FAILED',\n path: targetPath,\n context: { operation: 'readGlobalConfig', phase: 'read' },\n cause: err,\n });\n }\n fileExists = false;\n raw = '{}';\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n if (fileExists) {\n throw new ConfigError({\n message: `Config at ${targetPath} is not valid JSON: ${(err as Error).message}`,\n code: 'CONFIG_PARSE_FAILED',\n context: { filePath: targetPath, operation: 'readProfileConfig' },\n cause: err,\n });\n }\n parsed = {};\n }\n\n const decrypted = decryptConfigSecrets(parsed, deps.vault) as Record<string, unknown>;\n const extensions = (decrypted.extensions as Record<string, Record<string, unknown>>) ?? {};\n const telegram = extensions.telegram ?? {};\n mutator(telegram);\n extensions.telegram = telegram;\n decrypted.extensions = extensions;\n\n const encrypted = encryptConfigSecrets(decrypted, deps.vault);\n await atomicWrite(targetPath, JSON.stringify(encrypted, null, 2), { mode: 0o600 });\n\n // Also update the in-memory config store so changes are immediately visible\n deps.configStore.update({\n extensions: extensions as NonNullable<\n Parameters<typeof deps.configStore.update>[0]['extensions']\n >,\n });\n}\n\n/**\n * Derive the filesystem-access pair (`features.allowOutsideProjectRoot` and\n * `tools.restrictToProjectRoot`) from a save input. The two are inverses\n * of each other and the codebase intentionally keeps both in sync for\n * backward compatibility with older readers that only know about\n * `tools.restrictToProjectRoot`.\n *\n * Single source of truth: `allowOutsideProjectRoot`. If the caller only\n * sets `restrictFsToRoot`, it is converted to its inverse. If both are\n * set (which the picker should not do, but defensive code paths may),\n * `allowOutsideProjectRoot` wins. Returns `undefined` if neither is set,\n * so the caller can skip the write.\n *\n * The input is duck-typed (only the two relevant fields are read) so any\n * caller can pass a partial shape: the TUI's full `LiveSettingsInput`,\n * a slash-command's `{ restrictFsToRoot: boolean }`, or any future\n * save-handler that only carries these two knobs.\n */\nexport function deriveFsAccessPair(input: {\n allowOutsideProjectRoot?: boolean | undefined;\n restrictFsToRoot?: boolean | undefined;\n}): { allowOutsideProjectRoot: boolean; restrictToProjectRoot: boolean } | undefined {\n if (input.allowOutsideProjectRoot !== undefined) {\n const allow = input.allowOutsideProjectRoot;\n return { allowOutsideProjectRoot: allow, restrictToProjectRoot: !allow };\n }\n if (input.restrictFsToRoot !== undefined) {\n const restrict = input.restrictFsToRoot;\n return { allowOutsideProjectRoot: !restrict, restrictToProjectRoot: restrict };\n }\n return undefined;\n}\n", "/**\n * Shared suggestion state used by command and runtime surfaces.\n *\n * Previously, suggestions were stored in a closure variable (`currentSuggestions`)\n * inside cli-main.ts, accessed via the `onSuggestions` callback on\n * `SlashCommandContext`. This closure-based approach works correctly in the REPL\n * but can fail in the TUI when the callback goes through multiple indirections\n * (SlashCommandRegistry \u2192 SlashCommand.run \u2192 closed-over opts).\n *\n * By using a module-level store, both /suggest (writer) and /next (reader)\n * access the same array directly, bypassing the callback indirection entirely.\n * The CLI's `onSuggestions` callback now reads/writes this shared store to\n * maintain backward compatibility with the REPL autonomy loop's\n * `getSuggestions`/`onSuggestionsParsed` flow.\n *\n * Auto suggestions (items with auto=\"true\" attribute) are stored separately\n * for YOLO+auto autonomy mode.\n */\n\nlet sharedSuggestions: string[] = [];\nlet sharedAutoSuggestions: string[] = [];\n\n/** Store suggestions (called by /suggest and onSuggestionsParsed). */\nexport function setSuggestions(suggestions: string[]): void {\n sharedSuggestions = suggestions;\n}\n\n/** Store auto suggestions (items with auto=\"true\" attribute). Called by onAutoSuggestionsParsed. */\nexport function setAutoSuggestions(suggestions: string[]): void {\n sharedAutoSuggestions = suggestions;\n}\n\n/** Retrieve current suggestions (called by /next selection and getSuggestions). */\nexport function getSuggestions(): string[] {\n return sharedSuggestions;\n}\n\n/** Retrieve current auto suggestions (called by YOLO+auto autonomy). */\nexport function getAutoSuggestions(): string[] {\n return sharedAutoSuggestions;\n}\n\n/**\n * Remove suggestions at the given 0-based indices.\n * Called by /next selection so selected prompts cannot be re-selected.\n * Also clears auto-suggestions since an explicit user selection should\n * override any auto-submit queue.\n */\nexport function removeSuggestions(indices: number[]): void {\n if (indices.length === 0) return;\n const set = new Set(indices);\n sharedSuggestions = sharedSuggestions.filter((_, i) => !set.has(i));\n sharedAutoSuggestions = [];\n}\n\n/** Clear suggestions (called by /next clear). */\nexport function clearSuggestions(): void {\n sharedSuggestions = [];\n sharedAutoSuggestions = [];\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }