prjct-cli 1.6.8 → 1.6.9

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.
@@ -2,151 +2,17 @@
2
2
  * Error Messages Catalog
3
3
  *
4
4
  * Centralized error messages with context and recovery hints.
5
- * Provides consistent, helpful error output across the CLI.
5
+ * Types and catalog live in core/types/errors.ts.
6
6
  *
7
7
  * @see PRJ-131
8
8
  * @module utils/error-messages
9
9
  */
10
10
 
11
- export interface ErrorWithHint {
12
- message: string
13
- hint?: string
14
- file?: string
15
- docs?: string
16
- code?: string
17
- }
18
-
19
- /**
20
- * Common error messages with recovery hints
21
- */
22
- export const ERRORS = {
23
- // Project errors
24
- NO_PROJECT: {
25
- message: 'No prjct project found in this directory',
26
- hint: "Run 'prjct init' to set up a new project",
27
- file: '.prjct/prjct.config.json',
28
- },
29
-
30
- NO_PROJECT_ID: {
31
- message: 'Project ID not found',
32
- hint: "Run 'prjct init' or check .prjct/prjct.config.json",
33
- file: '.prjct/prjct.config.json',
34
- },
35
-
36
- CONFIG_NOT_FOUND: {
37
- message: 'Configuration file not found',
38
- hint: "Run 'prjct init' to create project configuration",
39
- file: '.prjct/prjct.config.json',
40
- },
41
-
42
- CONFIG_INVALID: {
43
- message: 'Invalid configuration file',
44
- hint: 'Check JSON syntax or delete .prjct/ and run init again',
45
- file: '.prjct/prjct.config.json',
46
- },
47
-
48
- // Git errors
49
- GIT_NOT_FOUND: {
50
- message: 'Git repository not detected',
51
- hint: "Run 'git init' first, then 'prjct init'",
52
- },
53
-
54
- GIT_NO_COMMITS: {
55
- message: 'No commits in repository',
56
- hint: 'Make an initial commit before using prjct',
57
- },
58
-
59
- GIT_DIRTY: {
60
- message: 'Working directory has uncommitted changes',
61
- hint: "Commit or stash changes, or use '--force' to override",
62
- },
63
-
64
- GIT_ON_MAIN: {
65
- message: 'Cannot ship from main/master branch',
66
- hint: 'Create a feature branch first: git checkout -b feature/your-feature',
67
- },
68
-
69
- GIT_OPERATION_FAILED: {
70
- message: 'Git operation failed',
71
- hint: 'Check git status and resolve any conflicts',
72
- },
73
-
74
- // Auth errors
75
- GH_NOT_AUTHENTICATED: {
76
- message: 'GitHub CLI not authenticated',
77
- hint: "Run 'gh auth login' to authenticate",
78
- docs: 'https://cli.github.com/manual/gh_auth_login',
79
- },
80
-
81
- LINEAR_NOT_CONFIGURED: {
82
- message: 'Linear integration not configured',
83
- hint: "Run 'p. linear setup' to configure Linear",
84
- },
85
-
86
- LINEAR_API_ERROR: {
87
- message: 'Linear API error',
88
- hint: 'Check your API key or network connection',
89
- },
90
-
91
- // Task errors
92
- NO_ACTIVE_TASK: {
93
- message: 'No active task',
94
- hint: 'Start a task with \'p. task "description"\'',
95
- },
96
-
97
- TASK_ALREADY_ACTIVE: {
98
- message: 'A task is already in progress',
99
- hint: "Complete it with 'p. done' or pause with 'p. pause'",
100
- },
101
-
102
- // Sync errors
103
- SYNC_FAILED: {
104
- message: 'Project sync failed',
105
- hint: 'Check file permissions and try again',
106
- },
107
-
108
- // Ship errors
109
- NOTHING_TO_SHIP: {
110
- message: 'Nothing to ship',
111
- hint: 'Make some changes first, then run ship',
112
- },
113
-
114
- PR_CREATE_FAILED: {
115
- message: 'Failed to create pull request',
116
- hint: 'Check GitHub auth and remote configuration',
117
- },
118
-
119
- // Provider errors
120
- NO_AI_PROVIDER: {
121
- message: 'No AI provider detected',
122
- hint: "Install Claude Code or Gemini CLI, then run 'prjct start'",
123
- docs: 'https://prjct.app/docs',
124
- },
125
-
126
- PROVIDER_NOT_CONFIGURED: {
127
- message: 'AI provider not configured for prjct',
128
- hint: "Run 'prjct start' to configure your provider",
129
- },
130
-
131
- // Command errors
132
- UNKNOWN_COMMAND: {
133
- message: 'Unknown command',
134
- hint: "Run 'prjct --help' to see available commands",
135
- },
136
-
137
- MISSING_PARAM: {
138
- message: 'Missing required parameter',
139
- hint: 'Check command usage below',
140
- },
141
-
142
- // Generic
143
- UNKNOWN: {
144
- message: 'An unexpected error occurred',
145
- hint: 'Check the error details and try again',
146
- },
147
- } as const
11
+ import type { ErrorCode, ErrorWithHint } from '../types/errors'
12
+ import { ERRORS } from '../types/errors'
148
13
 
149
- export type ErrorCode = keyof typeof ERRORS
14
+ export type { ErrorCode, ErrorWithHint } from '../types/errors'
15
+ export { ERRORS } from '../types/errors'
150
16
 
151
17
  /**
152
18
  * Get error with optional overrides
@@ -16,17 +16,7 @@
16
16
  * log.error('Failed to load', error.message)
17
17
  */
18
18
 
19
- type LogLevel = 'error' | 'warn' | 'info' | 'debug'
20
- type LogFunction = (...args: unknown[]) => void
21
-
22
- interface Logger {
23
- error: LogFunction
24
- warn: LogFunction
25
- info: LogFunction
26
- debug: LogFunction
27
- isEnabled: () => boolean
28
- level: () => LogLevel | 'disabled'
29
- }
19
+ import type { LogFunction, Logger, LogLevel } from '../types/logger'
30
20
 
31
21
  const LEVELS: Record<LogLevel, number> = { error: 0, warn: 1, info: 2, debug: 3 }
32
22
  const TRUTHY_VALUES = new Set(['1', 'true', '*'])
@@ -36,6 +26,8 @@ const TRUTHY_VALUES = new Set(['1', 'true', '*'])
36
26
  * Returns -1 (disabled) or a level from LEVELS
37
27
  */
38
28
  function getLogLevel(): { level: number; name: LogLevel | 'disabled' } {
29
+ // PRJCT_DEBUG (primary) or DEBUG (fallback): Enable debug logging
30
+ // Values: '1', 'true', a log level name, or 'prjct' to match
39
31
  const debugEnv = process.env.PRJCT_DEBUG || process.env.DEBUG || ''
40
32
 
41
33
  // Disabled if empty
@@ -11,31 +11,22 @@
11
11
 
12
12
  import chalk from 'chalk'
13
13
  import { OUTPUT_LIMITS } from '../constants'
14
+ import type { ErrorCode, ErrorWithHint } from '../types/errors'
15
+ import type { Output, OutputMetrics, OutputTier, TierConfig } from '../types/output'
14
16
  import branding from './branding'
15
- import type { ErrorCode, ErrorWithHint } from './error-messages'
16
17
  import { getError } from './error-messages'
17
18
 
18
19
  const _FRAMES = branding.spinner.frames
19
20
  const SPEED = branding.spinner.speed
20
21
 
21
- /**
22
- * Output tier configuration
23
- * Controls verbosity of CLI output
24
- */
25
- export type OutputTier = 'silent' | 'minimal' | 'compact' | 'verbose'
26
-
27
- export interface TierConfig {
28
- maxLines: number
29
- maxCharsPerLine: number
30
- showMetrics: boolean
31
- }
22
+ export type { Output, OutputMetrics, OutputTier, TierConfig } from '../types/output'
32
23
 
33
24
  export const OUTPUT_TIERS: Record<OutputTier, TierConfig> = {
34
25
  silent: { maxLines: 0, maxCharsPerLine: 0, showMetrics: false },
35
26
  minimal: { maxLines: 1, maxCharsPerLine: 65, showMetrics: false },
36
27
  compact: { maxLines: 4, maxCharsPerLine: 80, showMetrics: true },
37
28
  verbose: { maxLines: Infinity, maxCharsPerLine: Infinity, showMetrics: true },
38
- }
29
+ } as const
39
30
 
40
31
  // Current output tier (default: compact for human-readable output)
41
32
  let currentTier: OutputTier = 'compact'
@@ -184,36 +175,6 @@ export function formatForHuman(data: unknown): string {
184
175
  const clear = (): boolean =>
185
176
  process.stdout.isTTY ? process.stdout.write(`\r${' '.repeat(OUTPUT_LIMITS.CLEAR_WIDTH)}\r`) : true
186
177
 
187
- /**
188
- * Metrics to display after command completion
189
- * Shows value provided by prjct (compression, agent count, etc.)
190
- */
191
- interface OutputMetrics {
192
- agents?: number // Number of agents used
193
- reduction?: number // Context reduction percentage
194
- tokens?: number // Token count (in thousands)
195
- }
196
-
197
- interface Output {
198
- start(): Output
199
- end(): Output
200
- spin(msg: string): Output
201
- done(msg: string, metrics?: OutputMetrics): Output
202
- fail(msg: string): Output
203
- failWithHint(error: ErrorWithHint | ErrorCode): Output
204
- warn(msg: string): Output
205
- info(msg: string): Output
206
- debug(msg: string): Output
207
- success(msg: string, metrics?: OutputMetrics): Output
208
- list(items: string[], options?: { bullet?: string; indent?: number }): Output
209
- table(rows: Array<Record<string, string | number>>, options?: { header?: boolean }): Output
210
- box(title: string, content: string): Output
211
- section(title: string): Output
212
- stop(): Output
213
- step(current: number, total: number, msg: string): Output
214
- progress(current: number, total: number, msg?: string): Output
215
- }
216
-
217
178
  const out: Output = {
218
179
  // Branding: Show header at start
219
180
  start() {
@@ -273,7 +234,7 @@ const out: Output = {
273
234
  // Rich error with context and recovery hint
274
235
  failWithHint(error: ErrorWithHint | ErrorCode) {
275
236
  this.stop()
276
- const err = typeof error === 'string' ? getError(error) : error
237
+ const err = typeof error === 'string' ? getError(error as ErrorCode) : error
277
238
  console.error()
278
239
  console.error(`${ICONS.fail} ${err.message}`)
279
240
  if (err.file) {
@@ -305,6 +266,7 @@ const out: Output = {
305
266
  // Debug message (only if DEBUG=1 or DEBUG=true)
306
267
  debug(msg: string) {
307
268
  this.stop()
269
+ // DEBUG: Enable debug output (values: '1' or 'true')
308
270
  const debugEnabled = process.env.DEBUG === '1' || process.env.DEBUG === 'true'
309
271
  if (!quietMode && debugEnabled) {
310
272
  console.log(`${ICONS.debug} ${chalk.dim(msg)}`)
@@ -436,7 +398,6 @@ const out: Output = {
436
398
  },
437
399
  }
438
400
 
439
- export type { OutputMetrics }
440
401
  export type { ErrorCode, ErrorWithHint } from './error-messages'
441
402
  export { createError, ERRORS, getError } from './error-messages'
442
403
  export default out
@@ -18,28 +18,24 @@ import { promisify } from 'node:util'
18
18
  import chalk from 'chalk'
19
19
  import memorySystem from '../agentic/memory-system'
20
20
  import { WORKFLOW_HELP } from '../constants'
21
+ import type {
22
+ HookCommand,
23
+ HookPhase,
24
+ HookResult,
25
+ PreferenceScope,
26
+ WorkflowPreference,
27
+ } from '../types'
21
28
  import { getErrorMessage } from '../types/fs'
22
29
 
23
30
  const execAsync = promisify(exec)
24
31
 
25
- export type PreferenceScope = 'permanent' | 'session' | 'once'
26
- export type HookPhase = 'before' | 'after' | 'skip'
27
- export type HookCommand = 'task' | 'done' | 'ship' | 'sync'
28
-
29
- export interface WorkflowPreference {
30
- hook: HookPhase
31
- command: HookCommand
32
- action: string // command to run or 'true' for skip
33
- scope: PreferenceScope
34
- createdAt: string
35
- }
36
-
37
- export interface HookResult {
38
- success: boolean
39
- failed?: string
40
- skipped?: string[]
41
- output?: string
42
- }
32
+ export type {
33
+ HookCommand,
34
+ HookPhase,
35
+ HookResult,
36
+ PreferenceScope,
37
+ WorkflowPreference,
38
+ } from '../types'
43
39
 
44
40
  // Session and once preferences (in-memory)
45
41
  const sessionPreferences: Map<string, WorkflowPreference> = new Map()
@@ -2020,6 +2020,13 @@ var init_logger = __esm({
2020
2020
  }
2021
2021
  });
2022
2022
 
2023
+ // core/types/server.ts
2024
+ var init_server = __esm({
2025
+ "core/types/server.ts"() {
2026
+ "use strict";
2027
+ }
2028
+ });
2029
+
2023
2030
  // core/constants/index.ts
2024
2031
  function getTimeout(key) {
2025
2032
  const envVar = `PRJCT_TIMEOUT_${key}`;
@@ -2183,24 +2190,12 @@ var init_branding = __esm({
2183
2190
  }
2184
2191
  });
2185
2192
 
2186
- // core/utils/error-messages.ts
2187
- function getError(code, overrides) {
2188
- const base = ERRORS[code];
2189
- return { ...base, ...overrides };
2190
- }
2191
- function createError(message, hint, options) {
2192
- return {
2193
- message,
2194
- hint,
2195
- ...options
2196
- };
2197
- }
2193
+ // core/types/errors.ts
2198
2194
  var ERRORS;
2199
- var init_error_messages = __esm({
2200
- "core/utils/error-messages.ts"() {
2195
+ var init_errors2 = __esm({
2196
+ "core/types/errors.ts"() {
2201
2197
  "use strict";
2202
2198
  ERRORS = {
2203
- // Project errors
2204
2199
  NO_PROJECT: {
2205
2200
  message: "No prjct project found in this directory",
2206
2201
  hint: "Run 'prjct init' to set up a new project",
@@ -2221,7 +2216,6 @@ var init_error_messages = __esm({
2221
2216
  hint: "Check JSON syntax or delete .prjct/ and run init again",
2222
2217
  file: ".prjct/prjct.config.json"
2223
2218
  },
2224
- // Git errors
2225
2219
  GIT_NOT_FOUND: {
2226
2220
  message: "Git repository not detected",
2227
2221
  hint: "Run 'git init' first, then 'prjct init'"
@@ -2242,7 +2236,6 @@ var init_error_messages = __esm({
2242
2236
  message: "Git operation failed",
2243
2237
  hint: "Check git status and resolve any conflicts"
2244
2238
  },
2245
- // Auth errors
2246
2239
  GH_NOT_AUTHENTICATED: {
2247
2240
  message: "GitHub CLI not authenticated",
2248
2241
  hint: "Run 'gh auth login' to authenticate",
@@ -2256,7 +2249,6 @@ var init_error_messages = __esm({
2256
2249
  message: "Linear API error",
2257
2250
  hint: "Check your API key or network connection"
2258
2251
  },
2259
- // Task errors
2260
2252
  NO_ACTIVE_TASK: {
2261
2253
  message: "No active task",
2262
2254
  hint: `Start a task with 'p. task "description"'`
@@ -2265,12 +2257,10 @@ var init_error_messages = __esm({
2265
2257
  message: "A task is already in progress",
2266
2258
  hint: "Complete it with 'p. done' or pause with 'p. pause'"
2267
2259
  },
2268
- // Sync errors
2269
2260
  SYNC_FAILED: {
2270
2261
  message: "Project sync failed",
2271
2262
  hint: "Check file permissions and try again"
2272
2263
  },
2273
- // Ship errors
2274
2264
  NOTHING_TO_SHIP: {
2275
2265
  message: "Nothing to ship",
2276
2266
  hint: "Make some changes first, then run ship"
@@ -2279,7 +2269,6 @@ var init_error_messages = __esm({
2279
2269
  message: "Failed to create pull request",
2280
2270
  hint: "Check GitHub auth and remote configuration"
2281
2271
  },
2282
- // Provider errors
2283
2272
  NO_AI_PROVIDER: {
2284
2273
  message: "No AI provider detected",
2285
2274
  hint: "Install Claude Code or Gemini CLI, then run 'prjct start'",
@@ -2289,7 +2278,6 @@ var init_error_messages = __esm({
2289
2278
  message: "AI provider not configured for prjct",
2290
2279
  hint: "Run 'prjct start' to configure your provider"
2291
2280
  },
2292
- // Command errors
2293
2281
  UNKNOWN_COMMAND: {
2294
2282
  message: "Unknown command",
2295
2283
  hint: "Run 'prjct --help' to see available commands"
@@ -2298,12 +2286,31 @@ var init_error_messages = __esm({
2298
2286
  message: "Missing required parameter",
2299
2287
  hint: "Check command usage below"
2300
2288
  },
2301
- // Generic
2302
2289
  UNKNOWN: {
2303
2290
  message: "An unexpected error occurred",
2304
2291
  hint: "Check the error details and try again"
2305
2292
  }
2306
2293
  };
2294
+ }
2295
+ });
2296
+
2297
+ // core/utils/error-messages.ts
2298
+ function getError(code, overrides) {
2299
+ const base = ERRORS[code];
2300
+ return { ...base, ...overrides };
2301
+ }
2302
+ function createError(message, hint, options) {
2303
+ return {
2304
+ message,
2305
+ hint,
2306
+ ...options
2307
+ };
2308
+ }
2309
+ var init_error_messages = __esm({
2310
+ "core/utils/error-messages.ts"() {
2311
+ "use strict";
2312
+ init_errors2();
2313
+ init_errors2();
2307
2314
  __name(getError, "getError");
2308
2315
  __name(createError, "createError");
2309
2316
  }
@@ -13320,9 +13327,11 @@ var init_types3 = __esm({
13320
13327
  "use strict";
13321
13328
  init_agentic();
13322
13329
  init_bus();
13330
+ init_errors2();
13323
13331
  init_fs();
13324
13332
  init_integrations();
13325
13333
  init_memory();
13334
+ init_server();
13326
13335
  }
13327
13336
  });
13328
13337
 
@@ -22952,13 +22961,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
22952
22961
  projectPath: this.projectPath,
22953
22962
  globalPath: this.globalPath
22954
22963
  });
22955
- return generator.generate(
22956
- { branch: git.branch, commits: git.commits },
22957
- stats,
22958
- commands,
22959
- agents,
22960
- sources
22961
- );
22964
+ return generator.generate(git, stats, commands, agents, sources);
22962
22965
  }
22963
22966
  // ==========================================================================
22964
22967
  // PROJECT.JSON UPDATE
@@ -28646,7 +28649,7 @@ var require_package = __commonJS({
28646
28649
  "package.json"(exports, module) {
28647
28650
  module.exports = {
28648
28651
  name: "prjct-cli",
28649
- version: "1.6.8",
28652
+ version: "1.6.9",
28650
28653
  description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
28651
28654
  main: "core/index.ts",
28652
28655
  bin: {
@@ -29665,9 +29668,47 @@ function createExtendedRoutes() {
29665
29668
  __name(createExtendedRoutes, "createExtendedRoutes");
29666
29669
 
29667
29670
  // core/server/sse.ts
29671
+ init_server();
29668
29672
  import { streamSSE } from "hono/streaming";
29673
+ var MAX_CLIENT_TTL_MS = 60 * 60 * 1e3;
29674
+ var REAPER_INTERVAL_MS = 5 * 60 * 1e3;
29675
+ var HEARTBEAT_INTERVAL_MS = 3e4;
29669
29676
  function createSSEManager() {
29670
29677
  const clients = /* @__PURE__ */ new Map();
29678
+ let reaperInterval = null;
29679
+ function removeClient(clientId) {
29680
+ const entry = clients.get(clientId);
29681
+ if (!entry) return;
29682
+ clearInterval(entry.heartbeatInterval);
29683
+ clearTimeout(entry.ttlTimeout);
29684
+ entry.abortController.abort();
29685
+ clients.delete(clientId);
29686
+ }
29687
+ __name(removeClient, "removeClient");
29688
+ function startReaper() {
29689
+ if (reaperInterval) return;
29690
+ reaperInterval = setInterval(() => {
29691
+ const now = Date.now();
29692
+ for (const [id, entry] of clients) {
29693
+ const connectedMs = now - new Date(entry.client.connectedAt).getTime();
29694
+ if (connectedMs > MAX_CLIENT_TTL_MS) {
29695
+ removeClient(id);
29696
+ }
29697
+ }
29698
+ }, REAPER_INTERVAL_MS);
29699
+ if (reaperInterval && typeof reaperInterval === "object" && "unref" in reaperInterval) {
29700
+ reaperInterval.unref();
29701
+ }
29702
+ }
29703
+ __name(startReaper, "startReaper");
29704
+ function stopReaper() {
29705
+ if (reaperInterval) {
29706
+ clearInterval(reaperInterval);
29707
+ reaperInterval = null;
29708
+ }
29709
+ }
29710
+ __name(stopReaper, "stopReaper");
29711
+ startReaper();
29671
29712
  return {
29672
29713
  /**
29673
29714
  * Handle a new SSE connection
@@ -29675,8 +29716,11 @@ function createSSEManager() {
29675
29716
  handleConnection(c) {
29676
29717
  return streamSSE(c, async (stream) => {
29677
29718
  const clientId = crypto.randomUUID();
29719
+ const connectedAt = (/* @__PURE__ */ new Date()).toISOString();
29720
+ const abortController = new AbortController();
29678
29721
  const client = {
29679
29722
  id: clientId,
29723
+ connectedAt,
29680
29724
  send: /* @__PURE__ */ __name((event, data) => {
29681
29725
  stream.writeSSE({
29682
29726
  event,
@@ -29684,34 +29728,47 @@ function createSSEManager() {
29684
29728
  });
29685
29729
  }, "send"),
29686
29730
  close: /* @__PURE__ */ __name(() => {
29687
- clients.delete(clientId);
29731
+ removeClient(clientId);
29688
29732
  }, "close")
29689
29733
  };
29690
- clients.set(clientId, client);
29734
+ const heartbeatInterval = setInterval(async () => {
29735
+ try {
29736
+ await stream.writeSSE({
29737
+ event: "heartbeat",
29738
+ data: JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString() })
29739
+ });
29740
+ } catch {
29741
+ removeClient(clientId);
29742
+ }
29743
+ }, HEARTBEAT_INTERVAL_MS);
29744
+ const ttlTimeout = setTimeout(() => {
29745
+ removeClient(clientId);
29746
+ }, MAX_CLIENT_TTL_MS);
29747
+ if (typeof heartbeatInterval === "object" && "unref" in heartbeatInterval) {
29748
+ heartbeatInterval.unref();
29749
+ }
29750
+ if (typeof ttlTimeout === "object" && "unref" in ttlTimeout) {
29751
+ ttlTimeout.unref();
29752
+ }
29753
+ clients.set(clientId, {
29754
+ client,
29755
+ heartbeatInterval,
29756
+ ttlTimeout,
29757
+ abortController
29758
+ });
29691
29759
  await stream.writeSSE({
29692
29760
  event: "connected",
29693
29761
  data: JSON.stringify({
29694
29762
  clientId,
29695
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
29763
+ timestamp: connectedAt,
29696
29764
  message: "Connected to prjct-cli server"
29697
29765
  })
29698
29766
  });
29699
- const heartbeat = setInterval(async () => {
29700
- try {
29701
- await stream.writeSSE({
29702
- event: "heartbeat",
29703
- data: JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString() })
29704
- });
29705
- } catch (_error) {
29706
- clearInterval(heartbeat);
29707
- clients.delete(clientId);
29708
- }
29709
- }, 3e4);
29710
29767
  stream.onAbort(() => {
29711
- clearInterval(heartbeat);
29712
- clients.delete(clientId);
29768
+ removeClient(clientId);
29713
29769
  });
29714
- await new Promise(() => {
29770
+ await new Promise((resolve) => {
29771
+ abortController.signal.addEventListener("abort", () => resolve(), { once: true });
29715
29772
  });
29716
29773
  });
29717
29774
  },
@@ -29724,11 +29781,11 @@ function createSSEManager() {
29724
29781
  data,
29725
29782
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
29726
29783
  };
29727
- for (const client of clients.values()) {
29784
+ for (const [id, entry] of clients) {
29728
29785
  try {
29729
- client.send(event, message);
29730
- } catch (_error) {
29731
- clients.delete(client.id);
29786
+ entry.client.send(event, message);
29787
+ } catch {
29788
+ removeClient(id);
29732
29789
  }
29733
29790
  }
29734
29791
  },
@@ -29737,6 +29794,16 @@ function createSSEManager() {
29737
29794
  */
29738
29795
  getClientCount() {
29739
29796
  return clients.size;
29797
+ },
29798
+ /**
29799
+ * Shut down all clients and stop the reaper.
29800
+ * Called on server stop.
29801
+ */
29802
+ shutdown() {
29803
+ stopReaper();
29804
+ for (const id of [...clients.keys()]) {
29805
+ removeClient(id);
29806
+ }
29740
29807
  }
29741
29808
  };
29742
29809
  }
@@ -29818,6 +29885,7 @@ function createServer(config) {
29818
29885
  console.log(` Dashboard: http://localhost:${port}`);
29819
29886
  },
29820
29887
  stop() {
29888
+ sseManager.shutdown();
29821
29889
  if (server) {
29822
29890
  server.stop();
29823
29891
  server = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "1.6.8",
3
+ "version": "1.6.9",
4
4
  "description": "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
5
5
  "main": "core/index.ts",
6
6
  "bin": {