prjct-cli 1.6.6 → 1.6.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.6.7] - 2026-02-07
4
+
5
+ ### Bug Fixes
6
+
7
+ - add context to silent catch blocks in sync-service.ts (PRJ-80) (#120)
8
+
9
+ ### Refactoring
10
+
11
+ - replace `any` types in routes-extended.ts and server.ts (PRJ-77) (#130)
12
+
13
+
14
+ ## [1.6.7] - 2026-02-07
15
+
16
+ ### Refactoring
17
+ - **Replace `any` types in routes-extended.ts and server.ts (PRJ-77)**: Added `ProjectJson`, `StateJson`, `StateTask`, `QueueJson`, `QueueTask`, `RoadmapJson` interfaces to `core/types/storage.ts`. Replaced all 33 `any` types in `core/server/routes-extended.ts` with proper typed generics. Fixed `handleConnection: (c: any)` in `core/types/server.ts` with Hono `Context` type. Disabled redundant task component in statusline default config.
18
+
19
+ ### Test Plan
20
+
21
+ #### For QA
22
+ 1. Build the project (`bun run build`) — should succeed
23
+ 2. Typecheck (`npx tsc -p core/tsconfig.json --noEmit`) — should pass clean
24
+ 3. Verify zero `any` types in `routes-extended.ts` and `server.ts`
25
+ 4. Status bar should no longer show task description segment
26
+
27
+ #### For Users
28
+ **What changed:** Internal type safety improvement. Cleaner status bar.
29
+ **Breaking changes:** None
30
+
31
+
3
32
  ## [1.6.6] - 2026-02-07
4
33
 
5
34
  ### Refactoring
@@ -13,7 +13,7 @@
13
13
  "position": 0
14
14
  },
15
15
  "task": {
16
- "enabled": true,
16
+ "enabled": false,
17
17
  "position": 1,
18
18
  "maxLength": 25
19
19
  },
@@ -13,6 +13,16 @@ import { Hono } from 'hono'
13
13
  import * as jsonc from 'jsonc-parser'
14
14
  import pathManager from '../infrastructure/path-manager'
15
15
  import { isNotFoundError } from '../types/fs'
16
+ import type {
17
+ IdeasJson,
18
+ ProjectJson,
19
+ QueueJson,
20
+ QueueTask,
21
+ RoadmapJson,
22
+ ShippedJson,
23
+ StateJson,
24
+ StateTask,
25
+ } from '../types/storage'
16
26
 
17
27
  // =============================================================================
18
28
  // HELPERS
@@ -52,9 +62,9 @@ function getProjectPath(projectId: string): string {
52
62
  return path.join(PROJECTS_DIR, projectId)
53
63
  }
54
64
 
55
- async function getProjectConfig(projectId: string): Promise<any> {
65
+ async function getProjectConfig(projectId: string): Promise<ProjectJson | null> {
56
66
  const configPath = path.join(getProjectPath(projectId), 'project.json')
57
- return await readJsonFile(configPath)
67
+ return await readJsonFile<ProjectJson>(configPath)
58
68
  }
59
69
 
60
70
  async function calculateDuration(startedAt: string | undefined): Promise<string> {
@@ -97,16 +107,18 @@ export function createExtendedRoutes(): Hono {
97
107
  const config = await getProjectConfig(id)
98
108
 
99
109
  // Read state
100
- const state = await readJsonFile<any>(path.join(projectPath, 'storage/state.json'))
110
+ const state = await readJsonFile<StateJson>(path.join(projectPath, 'storage/state.json'))
101
111
 
102
112
  // Read queue for count
103
- const queue = await readJsonFile<any>(path.join(projectPath, 'storage/queue.json'))
113
+ const queue = await readJsonFile<QueueJson>(path.join(projectPath, 'storage/queue.json'))
104
114
 
105
115
  // Read ideas for count
106
- const ideas = await readJsonFile<any>(path.join(projectPath, 'storage/ideas.json'))
116
+ const ideas = await readJsonFile<IdeasJson>(path.join(projectPath, 'storage/ideas.json'))
107
117
 
108
118
  // Read shipped for count
109
- const shipped = await readJsonFile<any>(path.join(projectPath, 'storage/shipped.json'))
119
+ const shipped = await readJsonFile<ShippedJson>(
120
+ path.join(projectPath, 'storage/shipped.json')
121
+ )
110
122
 
111
123
  const currentTask = state?.currentTask
112
124
  const duration = await calculateDuration(currentTask?.startedAt)
@@ -123,8 +135,8 @@ export function createExtendedRoutes(): Hono {
123
135
  : null,
124
136
  pausedTask: state?.previousTask || null,
125
137
  stats: {
126
- queueCount: queue?.tasks?.filter((t: any) => !t.completed)?.length || 0,
127
- ideasCount: ideas?.ideas?.filter((i: any) => i.status === 'pending')?.length || 0,
138
+ queueCount: queue?.tasks?.filter((t: QueueTask) => !t.completed)?.length || 0,
139
+ ideasCount: ideas?.ideas?.filter((i) => i.status === 'pending')?.length || 0,
128
140
  shippedCount: shipped?.shipped?.length || 0,
129
141
  },
130
142
  }
@@ -154,11 +166,11 @@ export function createExtendedRoutes(): Hono {
154
166
  try {
155
167
  const [config, state, queue, ideas, shipped, roadmap] = await Promise.all([
156
168
  getProjectConfig(projectId),
157
- readJsonFile<any>(path.join(projectPath, 'storage/state.json')),
158
- readJsonFile<any>(path.join(projectPath, 'storage/queue.json')),
159
- readJsonFile<any>(path.join(projectPath, 'storage/ideas.json')),
160
- readJsonFile<any>(path.join(projectPath, 'storage/shipped.json')),
161
- readJsonFile<any>(path.join(projectPath, 'planning/roadmap.json')),
169
+ readJsonFile<StateJson>(path.join(projectPath, 'storage/state.json')),
170
+ readJsonFile<QueueJson>(path.join(projectPath, 'storage/queue.json')),
171
+ readJsonFile<IdeasJson>(path.join(projectPath, 'storage/ideas.json')),
172
+ readJsonFile<ShippedJson>(path.join(projectPath, 'storage/shipped.json')),
173
+ readJsonFile<RoadmapJson>(path.join(projectPath, 'planning/roadmap.json')),
162
174
  ])
163
175
 
164
176
  // Calculate current task duration
@@ -173,13 +185,13 @@ export function createExtendedRoutes(): Hono {
173
185
  weekStart.setDate(weekStart.getDate() - weekStart.getDay())
174
186
 
175
187
  const completedToday =
176
- queue?.tasks?.filter((t: any) => {
188
+ queue?.tasks?.filter((t: QueueTask) => {
177
189
  if (!t.completed || !t.completedAt) return false
178
190
  return new Date(t.completedAt) >= todayStart
179
191
  })?.length || 0
180
192
 
181
193
  const completedThisWeek =
182
- queue?.tasks?.filter((t: any) => {
194
+ queue?.tasks?.filter((t: QueueTask) => {
183
195
  if (!t.completed || !t.completedAt) return false
184
196
  return new Date(t.completedAt) >= weekStart
185
197
  })?.length || 0
@@ -196,8 +208,8 @@ export function createExtendedRoutes(): Hono {
196
208
  stats: {
197
209
  tasksToday: completedToday,
198
210
  tasksThisWeek: completedThisWeek,
199
- queueCount: queue?.tasks?.filter((t: any) => !t.completed)?.length || 0,
200
- ideasCount: ideas?.ideas?.filter((i: any) => i.status === 'pending')?.length || 0,
211
+ queueCount: queue?.tasks?.filter((t: QueueTask) => !t.completed)?.length || 0,
212
+ ideasCount: ideas?.ideas?.filter((i) => i.status === 'pending')?.length || 0,
201
213
  shippedCount: shipped?.shipped?.length || 0,
202
214
  },
203
215
  timestamp: new Date().toISOString(),
@@ -216,7 +228,7 @@ export function createExtendedRoutes(): Hono {
216
228
  const statePath = path.join(projectPath, 'storage/state.json')
217
229
 
218
230
  try {
219
- const state = await readJsonFile<any>(statePath)
231
+ const state = await readJsonFile<StateJson>(statePath)
220
232
 
221
233
  if (!state?.currentTask) {
222
234
  return c.json({ success: false, error: 'No active task' }, 400)
@@ -225,7 +237,7 @@ export function createExtendedRoutes(): Hono {
225
237
  const completedTask = state.currentTask
226
238
 
227
239
  // Update state
228
- const newState = {
240
+ const newState: StateJson = {
229
241
  currentTask: null,
230
242
  previousTask: null,
231
243
  lastUpdated: new Date().toISOString(),
@@ -255,13 +267,13 @@ export function createExtendedRoutes(): Hono {
255
267
  const body = await c.req.json().catch(() => ({}))
256
268
  const reason = body.reason
257
269
 
258
- const state = await readJsonFile<any>(statePath)
270
+ const state = await readJsonFile<StateJson>(statePath)
259
271
 
260
272
  if (!state?.currentTask) {
261
273
  return c.json({ success: false, error: 'No active task' }, 400)
262
274
  }
263
275
 
264
- const pausedTask = {
276
+ const pausedTask: StateTask = {
265
277
  id: state.currentTask.id,
266
278
  description: state.currentTask.description,
267
279
  status: 'paused',
@@ -270,7 +282,7 @@ export function createExtendedRoutes(): Hono {
270
282
  pauseReason: reason,
271
283
  }
272
284
 
273
- const newState = {
285
+ const newState: StateJson = {
274
286
  currentTask: null,
275
287
  previousTask: pausedTask,
276
288
  lastUpdated: new Date().toISOString(),
@@ -297,20 +309,21 @@ export function createExtendedRoutes(): Hono {
297
309
  const statePath = path.join(projectPath, 'storage/state.json')
298
310
 
299
311
  try {
300
- const state = await readJsonFile<any>(statePath)
312
+ const state = await readJsonFile<StateJson>(statePath)
301
313
 
302
314
  if (!state?.previousTask) {
303
315
  return c.json({ success: false, error: 'No paused task' }, 400)
304
316
  }
305
317
 
306
- const resumedTask = {
318
+ const resumedTask: StateTask = {
307
319
  id: state.previousTask.id,
308
320
  description: state.previousTask.description,
321
+ status: 'active',
309
322
  startedAt: new Date().toISOString(),
310
323
  sessionId: `sess_${Date.now().toString(36)}`,
311
324
  }
312
325
 
313
- const newState = {
326
+ const newState: StateJson = {
314
327
  currentTask: resumedTask,
315
328
  previousTask: null,
316
329
  lastUpdated: new Date().toISOString(),
@@ -345,8 +358,8 @@ export function createExtendedRoutes(): Hono {
345
358
  return c.json({ success: false, error: 'taskId required' }, 400)
346
359
  }
347
360
 
348
- const state = await readJsonFile<any>(statePath)
349
- const queue = await readJsonFile<any>(queuePath)
361
+ const state = await readJsonFile<StateJson>(statePath)
362
+ const queue = await readJsonFile<QueueJson>(queuePath)
350
363
 
351
364
  // Check if there's already an active task
352
365
  if (state?.currentTask) {
@@ -354,22 +367,23 @@ export function createExtendedRoutes(): Hono {
354
367
  }
355
368
 
356
369
  // Find task in queue
357
- const task = queue?.tasks?.find((t: any) => t.id === taskId)
370
+ const task = queue?.tasks?.find((t: QueueTask) => t.id === taskId)
358
371
  if (!task) {
359
372
  return c.json({ success: false, error: 'Task not found in queue' }, 404)
360
373
  }
361
374
 
362
375
  // Create new current task
363
- const newTask = {
376
+ const newTask: StateTask = {
364
377
  id: task.id,
365
378
  description: task.description,
379
+ status: 'active',
366
380
  startedAt: new Date().toISOString(),
367
381
  sessionId: `sess_${Date.now().toString(36)}`,
368
382
  featureId: task.featureId,
369
383
  }
370
384
 
371
385
  // Update state
372
- const newState = {
386
+ const newState: StateJson = {
373
387
  currentTask: newTask,
374
388
  previousTask: null,
375
389
  lastUpdated: new Date().toISOString(),
@@ -403,14 +417,17 @@ export function createExtendedRoutes(): Hono {
403
417
  return c.json({ success: false, error: 'text required' }, 400)
404
418
  }
405
419
 
406
- const ideas = (await readJsonFile<any>(ideasPath)) || { ideas: [], lastUpdated: '' }
420
+ const ideas: IdeasJson = (await readJsonFile<IdeasJson>(ideasPath)) || {
421
+ ideas: [],
422
+ lastUpdated: '',
423
+ }
407
424
 
408
425
  const newIdea = {
409
426
  id: `idea_${Date.now().toString(36)}`,
410
427
  text,
411
- status: 'pending',
412
- priority,
413
- tags,
428
+ status: 'pending' as const,
429
+ priority: (priority || 'medium') as 'low' | 'medium' | 'high',
430
+ tags: tags as string[],
414
431
  addedAt: new Date().toISOString(),
415
432
  }
416
433
 
@@ -446,15 +463,17 @@ export function createExtendedRoutes(): Hono {
446
463
  for (const id of projectIds) {
447
464
  const projectPath = getProjectPath(id)
448
465
 
449
- const state = await readJsonFile<any>(path.join(projectPath, 'storage/state.json'))
450
- const queue = await readJsonFile<any>(path.join(projectPath, 'storage/queue.json'))
451
- const ideas = await readJsonFile<any>(path.join(projectPath, 'storage/ideas.json'))
452
- const shipped = await readJsonFile<any>(path.join(projectPath, 'storage/shipped.json'))
466
+ const state = await readJsonFile<StateJson>(path.join(projectPath, 'storage/state.json'))
467
+ const queue = await readJsonFile<QueueJson>(path.join(projectPath, 'storage/queue.json'))
468
+ const ideas = await readJsonFile<IdeasJson>(path.join(projectPath, 'storage/ideas.json'))
469
+ const shipped = await readJsonFile<ShippedJson>(
470
+ path.join(projectPath, 'storage/shipped.json')
471
+ )
453
472
 
454
473
  if (state?.currentTask) activeProjects++
455
474
 
456
- totalTasks += queue?.tasks?.filter((t: any) => !t.completed)?.length || 0
457
- totalIdeas += ideas?.ideas?.filter((i: any) => i.status === 'pending')?.length || 0
475
+ totalTasks += queue?.tasks?.filter((t: QueueTask) => !t.completed)?.length || 0
476
+ totalIdeas += ideas?.ideas?.filter((i) => i.status === 'pending')?.length || 0
458
477
  totalShipped += shipped?.shipped?.length || 0
459
478
  }
460
479
 
@@ -499,16 +518,16 @@ export function createExtendedRoutes(): Hono {
499
518
  }
500
519
 
501
520
  // Find active/paused task
502
- let activeProject: any = null
503
- let activeTask: any = null
504
- let pausedTask: any = null
521
+ let activeProject: { id: string; name: string; path: string | undefined } | null = null
522
+ let activeTask: (StateTask & { duration?: string }) | null = null
523
+ let pausedTask: StateTask | null = null
505
524
 
506
525
  // If we have a target project, only check that one
507
526
  const idsToCheck = targetProjectId ? [targetProjectId] : projectIds
508
527
 
509
528
  for (const id of idsToCheck) {
510
529
  const projectPath = getProjectPath(id)
511
- const state = await readJsonFile<any>(path.join(projectPath, 'storage/state.json'))
530
+ const state = await readJsonFile<StateJson>(path.join(projectPath, 'storage/state.json'))
512
531
  const config = await getProjectConfig(id)
513
532
 
514
533
  if (state?.currentTask) {
@@ -267,8 +267,8 @@ class SyncService {
267
267
  this.globalPath,
268
268
  localConfig?.verification
269
269
  )
270
- } catch {
271
- // Verification is non-critical don't fail sync
270
+ } catch (error) {
271
+ log.debug('Verification failed (non-critical)', { error: getErrorMessage(error) })
272
272
  }
273
273
 
274
274
  return {
@@ -395,8 +395,8 @@ class SyncService {
395
395
  cwd: this.projectPath,
396
396
  })
397
397
  data.weeklyCommits = parseInt(weekly.trim(), 10) || 0
398
- } catch {
399
- // Not a git repo - use defaults
398
+ } catch (error) {
399
+ log.debug('Git analysis failed (not a git repo?)', { error: getErrorMessage(error) })
400
400
  }
401
401
 
402
402
  return data
@@ -424,7 +424,8 @@ class SyncService {
424
424
  { cwd: this.projectPath }
425
425
  )
426
426
  stats.fileCount = parseInt(stdout.trim(), 10) || 0
427
- } catch {
427
+ } catch (error) {
428
+ log.debug('File count failed', { path: this.projectPath, error: getErrorMessage(error) })
428
429
  stats.fileCount = 0
429
430
  }
430
431
 
@@ -453,8 +454,8 @@ class SyncService {
453
454
  } else {
454
455
  stats.languages.push('JavaScript')
455
456
  }
456
- } catch {
457
- // No package.json
457
+ } catch (error) {
458
+ log.debug('No package.json found', { path: this.projectPath, error: getErrorMessage(error) })
458
459
  }
459
460
 
460
461
  // Check other ecosystems
@@ -620,8 +621,8 @@ class SyncService {
620
621
  await fs.unlink(path.join(agentsPath, file))
621
622
  }
622
623
  }
623
- } catch {
624
- // Directory might not exist yet
624
+ } catch (error) {
625
+ log.debug('Failed to purge old agents', { path: agentsPath, error: getErrorMessage(error) })
625
626
  }
626
627
 
627
628
  // Workflow agents (always generated) - IN PARALLEL
@@ -714,8 +715,11 @@ class SyncService {
714
715
  )
715
716
  content = await fs.readFile(templatePath, 'utf-8')
716
717
  content = await this.resolveTemplateIncludes(content)
717
- } catch {
718
- // Generate minimal agent
718
+ } catch (error) {
719
+ log.debug('Workflow agent template not found, generating minimal', {
720
+ name,
721
+ error: getErrorMessage(error),
722
+ })
719
723
  content = this.generateMinimalWorkflowAgent(name)
720
724
  }
721
725
 
@@ -749,8 +753,11 @@ class SyncService {
749
753
  content = content.replace('{projectName}', stats.name)
750
754
  content = content.replace('{frameworks}', stack.frameworks.join(', ') || 'None detected')
751
755
  content = content.replace('{ecosystem}', stats.ecosystem)
752
- } catch {
753
- // Generate minimal agent
756
+ } catch (error) {
757
+ log.debug('Domain agent template not found, generating minimal', {
758
+ name,
759
+ error: getErrorMessage(error),
760
+ })
754
761
  content = this.generateMinimalDomainAgent(name, stats, stack)
755
762
  }
756
763
 
@@ -839,8 +846,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
839
846
  path.join(this.globalPath, 'config', 'skills.json'),
840
847
  JSON.stringify(skillsConfig, null, 2),
841
848
  'utf-8'
842
- ).catch(() => {
843
- // Best effort
849
+ ).catch((error) => {
850
+ log.debug('Failed to write skills.json', { error: getErrorMessage(error) })
844
851
  })
845
852
 
846
853
  return skills
@@ -986,8 +993,11 @@ You are the ${name} expert for this project. Apply best practices for the detect
986
993
  let existing: Record<string, unknown> = {}
987
994
  try {
988
995
  existing = JSON.parse(await fs.readFile(projectJsonPath, 'utf-8'))
989
- } catch {
990
- // No existing file
996
+ } catch (error) {
997
+ log.debug('No existing project.json', {
998
+ path: projectJsonPath,
999
+ error: getErrorMessage(error),
1000
+ })
991
1001
  }
992
1002
 
993
1003
  const updated = {
@@ -1024,8 +1034,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
1024
1034
  let state: Record<string, unknown> = {}
1025
1035
  try {
1026
1036
  state = JSON.parse(await fs.readFile(statePath, 'utf-8'))
1027
- } catch {
1028
- // No existing file
1037
+ } catch (error) {
1038
+ log.debug('No existing state.json', { path: statePath, error: getErrorMessage(error) })
1029
1039
  }
1030
1040
 
1031
1041
  // Update with enterprise fields
@@ -1062,8 +1072,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
1062
1072
  this.projectPath,
1063
1073
  state as import('../schemas/state').StateJson
1064
1074
  )
1065
- } catch {
1066
- // Silently fail - local state is optional
1075
+ } catch (error) {
1076
+ log.debug('Local state generation failed (optional)', { error: getErrorMessage(error) })
1067
1077
  }
1068
1078
  }
1069
1079
 
@@ -1114,8 +1124,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
1114
1124
  const filePath = path.join(this.globalPath, file)
1115
1125
  const content = await fs.readFile(filePath, 'utf-8')
1116
1126
  filteredChars += content.length
1117
- } catch {
1118
- // File might not exist, skip
1127
+ } catch (error) {
1128
+ log.debug('Context file not found for metrics', { file, error: getErrorMessage(error) })
1119
1129
  }
1120
1130
  }
1121
1131
 
@@ -1125,8 +1135,11 @@ You are the ${name} expert for this project. Apply best practices for the detect
1125
1135
  const agentPath = path.join(this.globalPath, 'agents', `${agent.name}.md`)
1126
1136
  const content = await fs.readFile(agentPath, 'utf-8')
1127
1137
  filteredChars += content.length
1128
- } catch {
1129
- // Skip if not found
1138
+ } catch (error) {
1139
+ log.debug('Agent file not found for metrics', {
1140
+ agent: agent.name,
1141
+ error: getErrorMessage(error),
1142
+ })
1130
1143
  }
1131
1144
  }
1132
1145
 
@@ -1152,8 +1165,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
1152
1165
  agents: agents.filter((a) => a.type === 'domain').map((a) => a.name),
1153
1166
  })
1154
1167
  } catch (error) {
1155
- // Non-blocking - metrics are nice to have
1156
- console.error('Warning: Failed to record metrics:', getErrorMessage(error))
1168
+ log.debug('Failed to record sync metrics', { error: getErrorMessage(error) })
1157
1169
  }
1158
1170
 
1159
1171
  return {
@@ -1172,7 +1184,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
1172
1184
  try {
1173
1185
  await fs.access(path.join(this.projectPath, filename))
1174
1186
  return true
1175
- } catch {
1187
+ } catch (error) {
1188
+ log.debug('File not found', { filename, error: getErrorMessage(error) })
1176
1189
  return false
1177
1190
  }
1178
1191
  }
@@ -1183,7 +1196,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
1183
1196
  const pkgPath = path.join(__dirname, '..', '..', 'package.json')
1184
1197
  const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf-8'))
1185
1198
  return pkg.version || '0.0.0'
1186
- } catch {
1199
+ } catch (error) {
1200
+ log.debug('Failed to read CLI version', { error: getErrorMessage(error) })
1187
1201
  return '0.0.0'
1188
1202
  }
1189
1203
  }
@@ -3,7 +3,7 @@
3
3
  * Types for HTTP server and SSE modules.
4
4
  */
5
5
 
6
- import type { Hono } from 'hono'
6
+ import type { Context, Hono } from 'hono'
7
7
 
8
8
  // =============================================================================
9
9
  // Server Types
@@ -36,8 +36,7 @@ export interface SSEClient {
36
36
  }
37
37
 
38
38
  export interface SSEManager {
39
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
- handleConnection: (c: any) => Response
39
+ handleConnection: (c: Context) => Response
41
40
  broadcast: (event: string, data: unknown) => void
42
41
  getClientCount: () => number
43
42
  }
@@ -18,6 +18,127 @@ export interface Storage {
18
18
  exists(path: string[]): Promise<boolean>
19
19
  }
20
20
 
21
+ // =============================================================================
22
+ // Project JSON Types (project.json)
23
+ // =============================================================================
24
+
25
+ export interface ProjectJson {
26
+ projectId: string
27
+ repoPath?: string
28
+ path?: string
29
+ name?: string
30
+ version?: string
31
+ cliVersion?: string
32
+ techStack?: string[]
33
+ fileCount?: number
34
+ commitCount?: number
35
+ stack?: string
36
+ currentBranch?: string
37
+ hasUncommittedChanges?: boolean
38
+ createdAt?: string
39
+ lastSync?: string
40
+ integrations?: {
41
+ linear?: {
42
+ enabled: boolean
43
+ authMode?: string
44
+ teamId?: string
45
+ teamName?: string
46
+ teamKey?: string
47
+ setupAt?: string
48
+ }
49
+ jira?: {
50
+ enabled: boolean
51
+ }
52
+ }
53
+ lastSyncCommit?: string
54
+ lastSyncBranch?: string
55
+ hooks?: {
56
+ enabled: boolean
57
+ strategy?: string
58
+ hooks?: unknown[]
59
+ }
60
+ }
61
+
62
+ // =============================================================================
63
+ // State JSON Types (state.json)
64
+ // =============================================================================
65
+
66
+ export interface StateTask {
67
+ id: string
68
+ description: string
69
+ type?: string
70
+ status: string
71
+ startedAt: string
72
+ shippedAt?: string
73
+ prUrl?: string
74
+ subtasks?: Array<{ description: string; status: string }>
75
+ currentSubtaskIndex?: number
76
+ parentDescription?: string
77
+ branch?: string
78
+ linearId?: string | null
79
+ linearUuid?: string | null
80
+ duration?: string
81
+ sessionId?: string
82
+ featureId?: string
83
+ pausedAt?: string
84
+ pauseReason?: string
85
+ expectedValue?: {
86
+ type: string
87
+ impact: string
88
+ successCriteria: string[]
89
+ }
90
+ }
91
+
92
+ export interface StateJson {
93
+ currentTask: StateTask | null
94
+ pausedTasks?: StateTask[]
95
+ previousTask: StateTask | null
96
+ lastUpdated?: string
97
+ projectId?: string
98
+ stack?: { language: string; framework: string }
99
+ domains?: Record<string, boolean>
100
+ projectType?: string
101
+ metrics?: { totalFiles: number }
102
+ lastSync?: string
103
+ context?: {
104
+ lastSession: string
105
+ lastAction: string
106
+ nextAction: string
107
+ }
108
+ }
109
+
110
+ // =============================================================================
111
+ // Queue JSON Types (queue.json)
112
+ // =============================================================================
113
+
114
+ export interface QueueTask {
115
+ id: string
116
+ description: string
117
+ type?: string
118
+ priority?: string
119
+ section?: string
120
+ createdAt: string
121
+ completed?: boolean
122
+ completedAt?: string
123
+ featureId?: string
124
+ featureName?: string
125
+ }
126
+
127
+ export interface QueueJson {
128
+ tasks: QueueTask[]
129
+ lastUpdated?: string
130
+ }
131
+
132
+ // =============================================================================
133
+ // Roadmap JSON Types (roadmap.json)
134
+ // =============================================================================
135
+
136
+ export interface RoadmapJson {
137
+ features: unknown[]
138
+ backlog: unknown[]
139
+ lastUpdated: string
140
+ }
141
+
21
142
  // =============================================================================
22
143
  // Shipped Storage Types
23
144
  // =============================================================================
@@ -22478,7 +22478,8 @@ var init_sync_service = __esm({
22478
22478
  this.globalPath,
22479
22479
  localConfig?.verification
22480
22480
  );
22481
- } catch {
22481
+ } catch (error) {
22482
+ logger_default.debug("Verification failed (non-critical)", { error: getErrorMessage(error) });
22482
22483
  }
22483
22484
  return {
22484
22485
  success: true,
@@ -22583,7 +22584,8 @@ var init_sync_service = __esm({
22583
22584
  cwd: this.projectPath
22584
22585
  });
22585
22586
  data.weeklyCommits = parseInt(weekly.trim(), 10) || 0;
22586
- } catch {
22587
+ } catch (error) {
22588
+ logger_default.debug("Git analysis failed (not a git repo?)", { error: getErrorMessage(error) });
22587
22589
  }
22588
22590
  return data;
22589
22591
  }
@@ -22606,7 +22608,8 @@ var init_sync_service = __esm({
22606
22608
  { cwd: this.projectPath }
22607
22609
  );
22608
22610
  stats.fileCount = parseInt(stdout.trim(), 10) || 0;
22609
- } catch {
22611
+ } catch (error) {
22612
+ logger_default.debug("File count failed", { path: this.projectPath, error: getErrorMessage(error) });
22610
22613
  stats.fileCount = 0;
22611
22614
  }
22612
22615
  try {
@@ -22628,7 +22631,8 @@ var init_sync_service = __esm({
22628
22631
  } else {
22629
22632
  stats.languages.push("JavaScript");
22630
22633
  }
22631
- } catch {
22634
+ } catch (error) {
22635
+ logger_default.debug("No package.json found", { path: this.projectPath, error: getErrorMessage(error) });
22632
22636
  }
22633
22637
  if (await this.fileExists("Cargo.toml")) {
22634
22638
  stats.ecosystem = "Rust";
@@ -22763,7 +22767,8 @@ var init_sync_service = __esm({
22763
22767
  await fs45.unlink(path49.join(agentsPath, file));
22764
22768
  }
22765
22769
  }
22766
- } catch {
22770
+ } catch (error) {
22771
+ logger_default.debug("Failed to purge old agents", { path: agentsPath, error: getErrorMessage(error) });
22767
22772
  }
22768
22773
  const workflowAgents = ["prjct-workflow", "prjct-planner", "prjct-shipper"];
22769
22774
  await Promise.all(workflowAgents.map((name) => this.generateWorkflowAgent(name, agentsPath)));
@@ -22839,7 +22844,11 @@ var init_sync_service = __esm({
22839
22844
  );
22840
22845
  content = await fs45.readFile(templatePath, "utf-8");
22841
22846
  content = await this.resolveTemplateIncludes(content);
22842
- } catch {
22847
+ } catch (error) {
22848
+ logger_default.debug("Workflow agent template not found, generating minimal", {
22849
+ name,
22850
+ error: getErrorMessage(error)
22851
+ });
22843
22852
  content = this.generateMinimalWorkflowAgent(name);
22844
22853
  }
22845
22854
  await fs45.writeFile(path49.join(agentsPath, `${name}.md`), content, "utf-8");
@@ -22861,7 +22870,11 @@ var init_sync_service = __esm({
22861
22870
  content = content.replace("{projectName}", stats.name);
22862
22871
  content = content.replace("{frameworks}", stack.frameworks.join(", ") || "None detected");
22863
22872
  content = content.replace("{ecosystem}", stats.ecosystem);
22864
- } catch {
22873
+ } catch (error) {
22874
+ logger_default.debug("Domain agent template not found, generating minimal", {
22875
+ name,
22876
+ error: getErrorMessage(error)
22877
+ });
22865
22878
  content = this.generateMinimalDomainAgent(name, stats, stack);
22866
22879
  }
22867
22880
  await fs45.writeFile(path49.join(agentsPath, `${name}.md`), content, "utf-8");
@@ -22936,7 +22949,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
22936
22949
  path49.join(this.globalPath, "config", "skills.json"),
22937
22950
  JSON.stringify(skillsConfig, null, 2),
22938
22951
  "utf-8"
22939
- ).catch(() => {
22952
+ ).catch((error) => {
22953
+ logger_default.debug("Failed to write skills.json", { error: getErrorMessage(error) });
22940
22954
  });
22941
22955
  return skills;
22942
22956
  }
@@ -23044,7 +23058,11 @@ You are the ${name} expert for this project. Apply best practices for the detect
23044
23058
  let existing = {};
23045
23059
  try {
23046
23060
  existing = JSON.parse(await fs45.readFile(projectJsonPath, "utf-8"));
23047
- } catch {
23061
+ } catch (error) {
23062
+ logger_default.debug("No existing project.json", {
23063
+ path: projectJsonPath,
23064
+ error: getErrorMessage(error)
23065
+ });
23048
23066
  }
23049
23067
  const updated = {
23050
23068
  ...existing,
@@ -23075,7 +23093,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
23075
23093
  let state = {};
23076
23094
  try {
23077
23095
  state = JSON.parse(await fs45.readFile(statePath, "utf-8"));
23078
- } catch {
23096
+ } catch (error) {
23097
+ logger_default.debug("No existing state.json", { path: statePath, error: getErrorMessage(error) });
23079
23098
  }
23080
23099
  state.projectId = this.projectId;
23081
23100
  state.stack = {
@@ -23107,7 +23126,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
23107
23126
  this.projectPath,
23108
23127
  state
23109
23128
  );
23110
- } catch {
23129
+ } catch (error) {
23130
+ logger_default.debug("Local state generation failed (optional)", { error: getErrorMessage(error) });
23111
23131
  }
23112
23132
  }
23113
23133
  // ==========================================================================
@@ -23146,7 +23166,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
23146
23166
  const filePath = path49.join(this.globalPath, file);
23147
23167
  const content = await fs45.readFile(filePath, "utf-8");
23148
23168
  filteredChars += content.length;
23149
- } catch {
23169
+ } catch (error) {
23170
+ logger_default.debug("Context file not found for metrics", { file, error: getErrorMessage(error) });
23150
23171
  }
23151
23172
  }
23152
23173
  for (const agent of agents) {
@@ -23154,7 +23175,11 @@ You are the ${name} expert for this project. Apply best practices for the detect
23154
23175
  const agentPath = path49.join(this.globalPath, "agents", `${agent.name}.md`);
23155
23176
  const content = await fs45.readFile(agentPath, "utf-8");
23156
23177
  filteredChars += content.length;
23157
- } catch {
23178
+ } catch (error) {
23179
+ logger_default.debug("Agent file not found for metrics", {
23180
+ agent: agent.name,
23181
+ error: getErrorMessage(error)
23182
+ });
23158
23183
  }
23159
23184
  }
23160
23185
  const filteredSize = Math.floor(filteredChars / CHARS_PER_TOKEN3);
@@ -23170,7 +23195,7 @@ You are the ${name} expert for this project. Apply best practices for the detect
23170
23195
  agents: agents.filter((a) => a.type === "domain").map((a) => a.name)
23171
23196
  });
23172
23197
  } catch (error) {
23173
- console.error("Warning: Failed to record metrics:", getErrorMessage(error));
23198
+ logger_default.debug("Failed to record sync metrics", { error: getErrorMessage(error) });
23174
23199
  }
23175
23200
  return {
23176
23201
  duration,
@@ -23186,7 +23211,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
23186
23211
  try {
23187
23212
  await fs45.access(path49.join(this.projectPath, filename));
23188
23213
  return true;
23189
- } catch {
23214
+ } catch (error) {
23215
+ logger_default.debug("File not found", { filename, error: getErrorMessage(error) });
23190
23216
  return false;
23191
23217
  }
23192
23218
  }
@@ -23195,7 +23221,8 @@ You are the ${name} expert for this project. Apply best practices for the detect
23195
23221
  const pkgPath = path49.join(__dirname, "..", "..", "package.json");
23196
23222
  const pkg = JSON.parse(await fs45.readFile(pkgPath, "utf-8"));
23197
23223
  return pkg.version || "0.0.0";
23198
- } catch {
23224
+ } catch (error) {
23225
+ logger_default.debug("Failed to read CLI version", { error: getErrorMessage(error) });
23199
23226
  return "0.0.0";
23200
23227
  }
23201
23228
  }
@@ -28719,7 +28746,7 @@ var require_package = __commonJS({
28719
28746
  "package.json"(exports, module) {
28720
28747
  module.exports = {
28721
28748
  name: "prjct-cli",
28722
- version: "1.6.6",
28749
+ version: "1.6.7",
28723
28750
  description: "Context layer for AI agents. Project context for Claude Code, Gemini CLI, and more.",
28724
28751
  main: "core/index.ts",
28725
28752
  bin: {
@@ -29397,7 +29424,9 @@ function createExtendedRoutes() {
29397
29424
  const state = await readJsonFile2(path8.join(projectPath, "storage/state.json"));
29398
29425
  const queue = await readJsonFile2(path8.join(projectPath, "storage/queue.json"));
29399
29426
  const ideas = await readJsonFile2(path8.join(projectPath, "storage/ideas.json"));
29400
- const shipped = await readJsonFile2(path8.join(projectPath, "storage/shipped.json"));
29427
+ const shipped = await readJsonFile2(
29428
+ path8.join(projectPath, "storage/shipped.json")
29429
+ );
29401
29430
  const currentTask = state?.currentTask;
29402
29431
  const duration = await calculateDuration2(currentTask?.startedAt);
29403
29432
  return {
@@ -29547,6 +29576,7 @@ function createExtendedRoutes() {
29547
29576
  const resumedTask = {
29548
29577
  id: state.previousTask.id,
29549
29578
  description: state.previousTask.description,
29579
+ status: "active",
29550
29580
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
29551
29581
  sessionId: `sess_${Date.now().toString(36)}`
29552
29582
  };
@@ -29588,6 +29618,7 @@ function createExtendedRoutes() {
29588
29618
  const newTask = {
29589
29619
  id: task.id,
29590
29620
  description: task.description,
29621
+ status: "active",
29591
29622
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
29592
29623
  sessionId: `sess_${Date.now().toString(36)}`,
29593
29624
  featureId: task.featureId
@@ -29617,12 +29648,15 @@ function createExtendedRoutes() {
29617
29648
  if (!text) {
29618
29649
  return c.json({ success: false, error: "text required" }, 400);
29619
29650
  }
29620
- const ideas = await readJsonFile2(ideasPath) || { ideas: [], lastUpdated: "" };
29651
+ const ideas = await readJsonFile2(ideasPath) || {
29652
+ ideas: [],
29653
+ lastUpdated: ""
29654
+ };
29621
29655
  const newIdea = {
29622
29656
  id: `idea_${Date.now().toString(36)}`,
29623
29657
  text,
29624
29658
  status: "pending",
29625
- priority,
29659
+ priority: priority || "medium",
29626
29660
  tags,
29627
29661
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
29628
29662
  };
@@ -29652,7 +29686,9 @@ function createExtendedRoutes() {
29652
29686
  const state = await readJsonFile2(path8.join(projectPath, "storage/state.json"));
29653
29687
  const queue = await readJsonFile2(path8.join(projectPath, "storage/queue.json"));
29654
29688
  const ideas = await readJsonFile2(path8.join(projectPath, "storage/ideas.json"));
29655
- const shipped = await readJsonFile2(path8.join(projectPath, "storage/shipped.json"));
29689
+ const shipped = await readJsonFile2(
29690
+ path8.join(projectPath, "storage/shipped.json")
29691
+ );
29656
29692
  if (state?.currentTask) activeProjects++;
29657
29693
  totalTasks += queue?.tasks?.filter((t) => !t.completed)?.length || 0;
29658
29694
  totalIdeas += ideas?.ideas?.filter((i) => i.status === "pending")?.length || 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "1.6.6",
3
+ "version": "1.6.7",
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": {