prjct-cli 0.45.0 → 0.45.3

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 (207) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/bin/prjct.ts +117 -10
  3. package/core/__tests__/agentic/memory-system.test.ts +39 -26
  4. package/core/__tests__/agentic/plan-mode.test.ts +64 -46
  5. package/core/__tests__/agentic/prompt-builder.test.ts +14 -14
  6. package/core/__tests__/services/project-index.test.ts +353 -0
  7. package/core/__tests__/types/fs.test.ts +3 -3
  8. package/core/__tests__/utils/date-helper.test.ts +10 -10
  9. package/core/__tests__/utils/output.test.ts +9 -6
  10. package/core/__tests__/utils/project-commands.test.ts +5 -6
  11. package/core/agentic/agent-router.ts +9 -10
  12. package/core/agentic/chain-of-thought.ts +16 -4
  13. package/core/agentic/command-executor.ts +66 -40
  14. package/core/agentic/context-builder.ts +8 -5
  15. package/core/agentic/ground-truth.ts +15 -9
  16. package/core/agentic/index.ts +145 -152
  17. package/core/agentic/loop-detector.ts +40 -11
  18. package/core/agentic/memory-system.ts +98 -35
  19. package/core/agentic/orchestrator-executor.ts +135 -71
  20. package/core/agentic/plan-mode.ts +46 -16
  21. package/core/agentic/prompt-builder.ts +108 -42
  22. package/core/agentic/services.ts +10 -9
  23. package/core/agentic/skill-loader.ts +9 -15
  24. package/core/agentic/smart-context.ts +129 -79
  25. package/core/agentic/template-executor.ts +13 -12
  26. package/core/agentic/template-loader.ts +7 -4
  27. package/core/agentic/tool-registry.ts +16 -13
  28. package/core/agents/index.ts +1 -1
  29. package/core/agents/performance.ts +10 -27
  30. package/core/ai-tools/formatters.ts +8 -6
  31. package/core/ai-tools/generator.ts +4 -4
  32. package/core/ai-tools/index.ts +1 -1
  33. package/core/ai-tools/registry.ts +21 -11
  34. package/core/bus/bus.ts +23 -16
  35. package/core/bus/index.ts +2 -2
  36. package/core/cli/linear.ts +3 -5
  37. package/core/cli/start.ts +28 -25
  38. package/core/commands/analysis.ts +58 -39
  39. package/core/commands/analytics.ts +52 -44
  40. package/core/commands/base.ts +15 -13
  41. package/core/commands/cleanup.ts +6 -13
  42. package/core/commands/command-data.ts +28 -4
  43. package/core/commands/commands.ts +57 -24
  44. package/core/commands/context.ts +4 -4
  45. package/core/commands/design.ts +3 -10
  46. package/core/commands/index.ts +5 -8
  47. package/core/commands/maintenance.ts +7 -4
  48. package/core/commands/planning.ts +179 -56
  49. package/core/commands/register.ts +13 -9
  50. package/core/commands/registry.ts +15 -14
  51. package/core/commands/setup.ts +26 -14
  52. package/core/commands/shipping.ts +11 -16
  53. package/core/commands/snapshots.ts +16 -32
  54. package/core/commands/uninstall.ts +541 -0
  55. package/core/commands/workflow.ts +24 -28
  56. package/core/constants/index.ts +10 -22
  57. package/core/context/generator.ts +82 -33
  58. package/core/context-tools/files-tool.ts +18 -19
  59. package/core/context-tools/imports-tool.ts +13 -33
  60. package/core/context-tools/index.ts +29 -54
  61. package/core/context-tools/recent-tool.ts +16 -22
  62. package/core/context-tools/signatures-tool.ts +17 -26
  63. package/core/context-tools/summary-tool.ts +20 -22
  64. package/core/context-tools/token-counter.ts +25 -20
  65. package/core/context-tools/types.ts +5 -5
  66. package/core/domain/agent-generator.ts +7 -5
  67. package/core/domain/agent-loader.ts +2 -2
  68. package/core/domain/analyzer.ts +19 -16
  69. package/core/domain/architecture-generator.ts +6 -3
  70. package/core/domain/context-estimator.ts +3 -4
  71. package/core/domain/snapshot-manager.ts +25 -22
  72. package/core/domain/task-stack.ts +24 -14
  73. package/core/errors.ts +1 -1
  74. package/core/events/events.ts +2 -4
  75. package/core/events/index.ts +1 -2
  76. package/core/index.ts +28 -16
  77. package/core/infrastructure/agent-detector.ts +3 -3
  78. package/core/infrastructure/ai-provider.ts +23 -20
  79. package/core/infrastructure/author-detector.ts +16 -10
  80. package/core/infrastructure/capability-installer.ts +2 -2
  81. package/core/infrastructure/claude-agent.ts +6 -6
  82. package/core/infrastructure/command-installer.ts +22 -17
  83. package/core/infrastructure/config-manager.ts +18 -14
  84. package/core/infrastructure/editors-config.ts +8 -4
  85. package/core/infrastructure/path-manager.ts +8 -6
  86. package/core/infrastructure/permission-manager.ts +20 -17
  87. package/core/infrastructure/setup.ts +42 -38
  88. package/core/infrastructure/update-checker.ts +5 -5
  89. package/core/integrations/issue-tracker/enricher.ts +8 -19
  90. package/core/integrations/issue-tracker/index.ts +2 -2
  91. package/core/integrations/issue-tracker/manager.ts +15 -15
  92. package/core/integrations/issue-tracker/types.ts +5 -22
  93. package/core/integrations/jira/client.ts +67 -59
  94. package/core/integrations/jira/index.ts +11 -14
  95. package/core/integrations/jira/mcp-adapter.ts +5 -10
  96. package/core/integrations/jira/service.ts +10 -10
  97. package/core/integrations/linear/client.ts +27 -18
  98. package/core/integrations/linear/index.ts +9 -12
  99. package/core/integrations/linear/service.ts +11 -11
  100. package/core/integrations/linear/sync.ts +8 -8
  101. package/core/outcomes/analyzer.ts +5 -18
  102. package/core/outcomes/index.ts +2 -2
  103. package/core/outcomes/recorder.ts +3 -3
  104. package/core/plugin/builtin/webhook.ts +19 -15
  105. package/core/plugin/hooks.ts +29 -21
  106. package/core/plugin/index.ts +7 -7
  107. package/core/plugin/loader.ts +19 -19
  108. package/core/plugin/registry.ts +12 -23
  109. package/core/schemas/agents.ts +1 -1
  110. package/core/schemas/analysis.ts +1 -1
  111. package/core/schemas/enriched-task.ts +62 -49
  112. package/core/schemas/ideas.ts +13 -13
  113. package/core/schemas/index.ts +17 -27
  114. package/core/schemas/issues.ts +40 -25
  115. package/core/schemas/metrics.ts +25 -25
  116. package/core/schemas/outcomes.ts +70 -62
  117. package/core/schemas/permissions.ts +15 -12
  118. package/core/schemas/prd.ts +27 -14
  119. package/core/schemas/project.ts +3 -3
  120. package/core/schemas/roadmap.ts +47 -34
  121. package/core/schemas/schemas.ts +3 -4
  122. package/core/schemas/shipped.ts +3 -3
  123. package/core/schemas/state.ts +43 -29
  124. package/core/server/index.ts +5 -6
  125. package/core/server/routes-extended.ts +68 -72
  126. package/core/server/routes.ts +3 -3
  127. package/core/server/server.ts +31 -26
  128. package/core/services/agent-generator.ts +237 -0
  129. package/core/services/agent-service.ts +2 -2
  130. package/core/services/breakdown-service.ts +2 -4
  131. package/core/services/context-generator.ts +299 -0
  132. package/core/services/context-selector.ts +420 -0
  133. package/core/services/doctor-service.ts +426 -0
  134. package/core/services/file-categorizer.ts +448 -0
  135. package/core/services/file-scorer.ts +270 -0
  136. package/core/services/git-analyzer.ts +267 -0
  137. package/core/services/index.ts +27 -10
  138. package/core/services/memory-service.ts +3 -4
  139. package/core/services/project-index.ts +911 -0
  140. package/core/services/project-service.ts +4 -4
  141. package/core/services/skill-installer.ts +14 -17
  142. package/core/services/skill-lock.ts +3 -3
  143. package/core/services/skill-service.ts +12 -6
  144. package/core/services/stack-detector.ts +245 -0
  145. package/core/services/sync-service.ts +87 -345
  146. package/core/services/watch-service.ts +294 -0
  147. package/core/session/compaction.ts +23 -31
  148. package/core/session/index.ts +11 -5
  149. package/core/session/log-migration.ts +3 -3
  150. package/core/session/metrics.ts +19 -14
  151. package/core/session/session-log-manager.ts +12 -17
  152. package/core/session/task-session-manager.ts +25 -25
  153. package/core/session/utils.ts +1 -1
  154. package/core/storage/ideas-storage.ts +41 -57
  155. package/core/storage/index-storage.ts +514 -0
  156. package/core/storage/index.ts +41 -17
  157. package/core/storage/metrics-storage.ts +39 -34
  158. package/core/storage/queue-storage.ts +35 -45
  159. package/core/storage/shipped-storage.ts +17 -20
  160. package/core/storage/state-storage.ts +50 -30
  161. package/core/storage/storage-manager.ts +6 -6
  162. package/core/storage/storage.ts +18 -15
  163. package/core/sync/auth-config.ts +3 -3
  164. package/core/sync/index.ts +13 -19
  165. package/core/sync/oauth-handler.ts +3 -3
  166. package/core/sync/sync-client.ts +4 -9
  167. package/core/sync/sync-manager.ts +12 -14
  168. package/core/types/commands.ts +42 -7
  169. package/core/types/index.ts +284 -305
  170. package/core/types/integrations.ts +3 -3
  171. package/core/types/storage.ts +14 -14
  172. package/core/types/utils.ts +3 -3
  173. package/core/utils/agent-stream.ts +3 -1
  174. package/core/utils/animations.ts +14 -11
  175. package/core/utils/branding.ts +7 -7
  176. package/core/utils/cache.ts +1 -3
  177. package/core/utils/collection-filters.ts +3 -15
  178. package/core/utils/date-helper.ts +2 -7
  179. package/core/utils/file-helper.ts +13 -8
  180. package/core/utils/jsonl-helper.ts +13 -10
  181. package/core/utils/keychain.ts +4 -8
  182. package/core/utils/logger.ts +1 -1
  183. package/core/utils/next-steps.ts +3 -3
  184. package/core/utils/output.ts +58 -11
  185. package/core/utils/project-commands.ts +6 -6
  186. package/core/utils/project-credentials.ts +5 -12
  187. package/core/utils/runtime.ts +2 -2
  188. package/core/utils/session-helper.ts +3 -4
  189. package/core/utils/version.ts +3 -3
  190. package/core/wizard/index.ts +13 -0
  191. package/core/wizard/onboarding.ts +633 -0
  192. package/core/workflow/state-machine.ts +7 -7
  193. package/dist/bin/prjct.mjs +18755 -15574
  194. package/dist/core/infrastructure/command-installer.js +86 -79
  195. package/dist/core/infrastructure/editors-config.js +6 -6
  196. package/dist/core/infrastructure/setup.js +246 -225
  197. package/dist/core/utils/version.js +9 -9
  198. package/package.json +11 -12
  199. package/scripts/build.js +3 -3
  200. package/scripts/postinstall.js +2 -2
  201. package/templates/mcp-config.json +6 -1
  202. package/templates/permissions/permissive.jsonc +1 -1
  203. package/templates/permissions/strict.jsonc +5 -9
  204. package/templates/global/docs/agents.md +0 -88
  205. package/templates/global/docs/architecture.md +0 -103
  206. package/templates/global/docs/commands.md +0 -96
  207. package/templates/global/docs/validation.md +0 -95
@@ -17,17 +17,31 @@ import { z } from 'zod'
17
17
  export const PrioritySchema = z.enum(['low', 'medium', 'high', 'critical'])
18
18
  export const TaskTypeSchema = z.enum(['feature', 'bug', 'improvement', 'chore'])
19
19
  export const TaskSectionSchema = z.enum(['active', 'backlog', 'previously_active'])
20
- export const TaskStatusSchema = z.enum(['pending', 'in_progress', 'completed', 'blocked', 'paused', 'failed'])
21
- export const ActivityTypeSchema = z.enum(['task_completed', 'feature_shipped', 'idea_captured', 'session_started'])
20
+ export const TaskStatusSchema = z.enum([
21
+ 'pending',
22
+ 'in_progress',
23
+ 'completed',
24
+ 'blocked',
25
+ 'paused',
26
+ 'failed',
27
+ ])
28
+ export const ActivityTypeSchema = z.enum([
29
+ 'task_completed',
30
+ 'feature_shipped',
31
+ 'idea_captured',
32
+ 'session_started',
33
+ ])
22
34
 
23
35
  // Subtask summary for context handoff between agents
24
36
  export const SubtaskSummarySchema = z.object({
25
37
  title: z.string(),
26
38
  description: z.string(),
27
- filesChanged: z.array(z.object({
28
- path: z.string(),
29
- action: z.enum(['created', 'modified', 'deleted']),
30
- })),
39
+ filesChanged: z.array(
40
+ z.object({
41
+ path: z.string(),
42
+ action: z.enum(['created', 'modified', 'deleted']),
43
+ })
44
+ ),
31
45
  whatWasDone: z.array(z.string()),
32
46
  outputForNextAgent: z.string().optional(),
33
47
  notes: z.string().optional(),
@@ -35,15 +49,15 @@ export const SubtaskSummarySchema = z.object({
35
49
 
36
50
  // Subtask schema for task fragmentation
37
51
  export const SubtaskSchema = z.object({
38
- id: z.string(), // subtask-xxx
52
+ id: z.string(), // subtask-xxx
39
53
  description: z.string(),
40
- domain: z.string(), // frontend, backend, database, testing, etc.
41
- agent: z.string(), // agent file name (e.g., "frontend.md")
54
+ domain: z.string(), // frontend, backend, database, testing, etc.
55
+ agent: z.string(), // agent file name (e.g., "frontend.md")
42
56
  status: TaskStatusSchema,
43
- dependsOn: z.array(z.string()), // IDs of dependent subtasks
44
- startedAt: z.string().optional(), // ISO8601
57
+ dependsOn: z.array(z.string()), // IDs of dependent subtasks
58
+ startedAt: z.string().optional(), // ISO8601
45
59
  completedAt: z.string().optional(), // ISO8601
46
- output: z.string().optional(), // Brief output description
60
+ output: z.string().optional(), // Brief output description
47
61
  summary: SubtaskSummarySchema.optional(), // Full summary for context handoff
48
62
  })
49
63
 
@@ -55,26 +69,26 @@ export const SubtaskProgressSchema = z.object({
55
69
  })
56
70
 
57
71
  export const CurrentTaskSchema = z.object({
58
- id: z.string(), // task_xxxxxxxx
72
+ id: z.string(), // task_xxxxxxxx
59
73
  description: z.string(),
60
- startedAt: z.string(), // ISO8601
61
- sessionId: z.string(), // sess_xxxxxxxx
74
+ startedAt: z.string(), // ISO8601
75
+ sessionId: z.string(), // sess_xxxxxxxx
62
76
  featureId: z.string().optional(), // feat_xxxxxxxx
63
77
  // Subtask tracking for fragmented tasks
64
78
  subtasks: z.array(SubtaskSchema).optional(),
65
79
  currentSubtaskIndex: z.number().optional(),
66
80
  subtaskProgress: SubtaskProgressSchema.optional(),
67
81
  // Linear integration - bidirectional sync
68
- linearId: z.string().optional(), // "PRJ-123" - Linear identifier
69
- linearUuid: z.string().optional(), // Linear internal UUID for API calls
82
+ linearId: z.string().optional(), // "PRJ-123" - Linear identifier
83
+ linearUuid: z.string().optional(), // Linear internal UUID for API calls
70
84
  })
71
85
 
72
86
  export const PreviousTaskSchema = z.object({
73
87
  id: z.string(),
74
88
  description: z.string(),
75
89
  status: z.literal('paused'),
76
- startedAt: z.string(), // ISO8601
77
- pausedAt: z.string(), // ISO8601
90
+ startedAt: z.string(), // ISO8601
91
+ pausedAt: z.string(), // ISO8601
78
92
  pauseReason: z.string().optional(),
79
93
  })
80
94
 
@@ -85,20 +99,20 @@ export const StateJsonSchema = z.object({
85
99
  })
86
100
 
87
101
  export const QueueTaskSchema = z.object({
88
- id: z.string(), // task_xxxxxxxx
102
+ id: z.string(), // task_xxxxxxxx
89
103
  description: z.string(),
90
104
  priority: PrioritySchema,
91
- type: TaskTypeSchema, // detect from emoji 🐛=bug
105
+ type: TaskTypeSchema, // detect from emoji 🐛=bug
92
106
  featureId: z.string().optional(),
93
107
  originFeature: z.string().optional(),
94
108
  completed: z.boolean(),
95
109
  completedAt: z.string().optional(),
96
- createdAt: z.string(), // ISO8601
110
+ createdAt: z.string(), // ISO8601
97
111
  section: TaskSectionSchema,
98
112
  // Additional fields for ZERO DATA LOSS
99
- agent: z.string().optional(), // "fe", "be", "fe + be"
113
+ agent: z.string().optional(), // "fe", "be", "fe + be"
100
114
  groupName: z.string().optional(), // "Sales Reports", "Stock Audits"
101
- groupId: z.string().optional(), // For grouping related tasks
115
+ groupId: z.string().optional(), // For grouping related tasks
102
116
  })
103
117
 
104
118
  export const QueueJsonSchema = z.object({
@@ -117,7 +131,7 @@ export const StatsSchema = z.object({
117
131
  export const RecentActivitySchema = z.object({
118
132
  type: ActivityTypeSchema,
119
133
  description: z.string(),
120
- timestamp: z.string(), // ISO8601
134
+ timestamp: z.string(), // ISO8601
121
135
  duration: z.string().optional(),
122
136
  })
123
137
 
@@ -127,7 +141,7 @@ export const StateSchemaFull = z.object({
127
141
  queue: z.array(QueueTaskSchema),
128
142
  stats: StatsSchema,
129
143
  recentActivity: z.array(RecentActivitySchema),
130
- lastSync: z.string(), // ISO8601
144
+ lastSync: z.string(), // ISO8601
131
145
  })
132
146
 
133
147
  // =============================================================================
@@ -176,12 +190,12 @@ export const safeParseQueue = (data: unknown) => QueueJsonSchema.safeParse(data)
176
190
 
177
191
  export const DEFAULT_STATE: StateJson = {
178
192
  currentTask: null,
179
- lastUpdated: ''
193
+ lastUpdated: '',
180
194
  }
181
195
 
182
196
  export const DEFAULT_QUEUE: QueueJson = {
183
197
  tasks: [],
184
- lastUpdated: ''
198
+ lastUpdated: '',
185
199
  }
186
200
 
187
201
  export const DEFAULT_STATS: Stats = {
@@ -189,5 +203,5 @@ export const DEFAULT_STATS: Stats = {
189
203
  tasksThisWeek: 0,
190
204
  streak: 0,
191
205
  velocity: '0/day',
192
- avgDuration: '0m'
206
+ avgDuration: '0m',
193
207
  }
@@ -7,16 +7,15 @@
7
7
  * @version 1.0.0
8
8
  */
9
9
 
10
- export { createServer, startServer, DEFAULT_PORT } from './server'
11
- export { createRoutes } from './routes'
12
- export { createExtendedRoutes } from './routes-extended'
13
- export { createSSEManager, SSE_EVENTS } from './sse'
14
-
15
10
  // Re-export types from canonical location
16
11
  export type {
17
12
  ServerConfig,
18
13
  ServerInstance,
19
14
  SSEClient,
20
- SSEManager,
21
15
  SSEEventType,
16
+ SSEManager,
22
17
  } from '../types'
18
+ export { createRoutes } from './routes'
19
+ export { createExtendedRoutes } from './routes-extended'
20
+ export { createServer, DEFAULT_PORT, startServer } from './server'
21
+ export { createSSEManager, SSE_EVENTS } from './sse'
@@ -7,9 +7,9 @@
7
7
  * @version 2.0.0
8
8
  */
9
9
 
10
+ import fs from 'node:fs/promises'
11
+ import path from 'node:path'
10
12
  import { Hono } from 'hono'
11
- import fs from 'fs/promises'
12
- import path from 'path'
13
13
  import * as jsonc from 'jsonc-parser'
14
14
  import pathManager from '../infrastructure/path-manager'
15
15
  import { isNotFoundError } from '../types/fs'
@@ -38,7 +38,7 @@ async function readJsonFile<T>(filePath: string): Promise<T | null> {
38
38
  async function writeJsonFile(filePath: string, data: unknown): Promise<boolean> {
39
39
  try {
40
40
  await fs.mkdir(path.dirname(filePath), { recursive: true })
41
- await fs.writeFile(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8')
41
+ await fs.writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf-8')
42
42
  return true
43
43
  } catch (error) {
44
44
  if (isNotFoundError(error)) {
@@ -59,14 +59,14 @@ async function getProjectConfig(projectId: string): Promise<any> {
59
59
 
60
60
  async function calculateDuration(startedAt: string | undefined): Promise<string> {
61
61
  if (!startedAt) return ''
62
-
62
+
63
63
  const start = new Date(startedAt)
64
64
  const now = new Date()
65
65
  const elapsed = now.getTime() - start.getTime()
66
-
66
+
67
67
  const hours = Math.floor(elapsed / (1000 * 60 * 60))
68
68
  const minutes = Math.floor((elapsed % (1000 * 60 * 60)) / (1000 * 60))
69
-
69
+
70
70
  if (hours > 0) {
71
71
  return `${hours}h ${minutes}m`
72
72
  }
@@ -87,34 +87,26 @@ export function createExtendedRoutes(): Hono {
87
87
  try {
88
88
  await fs.mkdir(PROJECTS_DIR, { recursive: true })
89
89
  const entries = await fs.readdir(PROJECTS_DIR, { withFileTypes: true })
90
- const projectIds = entries.filter(e => e.isDirectory()).map(e => e.name)
90
+ const projectIds = entries.filter((e) => e.isDirectory()).map((e) => e.name)
91
91
 
92
92
  const projects = await Promise.all(
93
93
  projectIds.map(async (id) => {
94
94
  const projectPath = getProjectPath(id)
95
-
95
+
96
96
  // Read config
97
97
  const config = await getProjectConfig(id)
98
-
98
+
99
99
  // Read state
100
- const state = await readJsonFile<any>(
101
- path.join(projectPath, 'storage/state.json')
102
- )
103
-
100
+ const state = await readJsonFile<any>(path.join(projectPath, 'storage/state.json'))
101
+
104
102
  // Read queue for count
105
- const queue = await readJsonFile<any>(
106
- path.join(projectPath, 'storage/queue.json')
107
- )
108
-
103
+ const queue = await readJsonFile<any>(path.join(projectPath, 'storage/queue.json'))
104
+
109
105
  // Read ideas for count
110
- const ideas = await readJsonFile<any>(
111
- path.join(projectPath, 'storage/ideas.json')
112
- )
113
-
106
+ const ideas = await readJsonFile<any>(path.join(projectPath, 'storage/ideas.json'))
107
+
114
108
  // Read shipped for count
115
- const shipped = await readJsonFile<any>(
116
- path.join(projectPath, 'storage/shipped.json')
117
- )
109
+ const shipped = await readJsonFile<any>(path.join(projectPath, 'storage/shipped.json'))
118
110
 
119
111
  const currentTask = state?.currentTask
120
112
  const duration = await calculateDuration(currentTask?.startedAt)
@@ -123,16 +115,18 @@ export function createExtendedRoutes(): Hono {
123
115
  id,
124
116
  name: config?.name || id.slice(0, 8),
125
117
  path: config?.path || null,
126
- currentTask: currentTask ? {
127
- ...currentTask,
128
- duration
129
- } : null,
118
+ currentTask: currentTask
119
+ ? {
120
+ ...currentTask,
121
+ duration,
122
+ }
123
+ : null,
130
124
  pausedTask: state?.previousTask || null,
131
125
  stats: {
132
126
  queueCount: queue?.tasks?.filter((t: any) => !t.completed)?.length || 0,
133
127
  ideasCount: ideas?.ideas?.filter((i: any) => i.status === 'pending')?.length || 0,
134
128
  shippedCount: shipped?.shipped?.length || 0,
135
- }
129
+ },
136
130
  }
137
131
  })
138
132
  )
@@ -178,15 +172,17 @@ export function createExtendedRoutes(): Hono {
178
172
  const weekStart = new Date(todayStart)
179
173
  weekStart.setDate(weekStart.getDate() - weekStart.getDay())
180
174
 
181
- const completedToday = queue?.tasks?.filter((t: any) => {
182
- if (!t.completed || !t.completedAt) return false
183
- return new Date(t.completedAt) >= todayStart
184
- })?.length || 0
175
+ const completedToday =
176
+ queue?.tasks?.filter((t: any) => {
177
+ if (!t.completed || !t.completedAt) return false
178
+ return new Date(t.completedAt) >= todayStart
179
+ })?.length || 0
185
180
 
186
- const completedThisWeek = queue?.tasks?.filter((t: any) => {
187
- if (!t.completed || !t.completedAt) return false
188
- return new Date(t.completedAt) >= weekStart
189
- })?.length || 0
181
+ const completedThisWeek =
182
+ queue?.tasks?.filter((t: any) => {
183
+ if (!t.completed || !t.completedAt) return false
184
+ return new Date(t.completedAt) >= weekStart
185
+ })?.length || 0
190
186
 
191
187
  return c.json({
192
188
  id: projectId,
@@ -204,7 +200,7 @@ export function createExtendedRoutes(): Hono {
204
200
  ideasCount: ideas?.ideas?.filter((i: any) => i.status === 'pending')?.length || 0,
205
201
  shippedCount: shipped?.shipped?.length || 0,
206
202
  },
207
- timestamp: new Date().toISOString()
203
+ timestamp: new Date().toISOString(),
208
204
  })
209
205
  } catch (error) {
210
206
  return c.json({ error: String(error) }, 500)
@@ -221,7 +217,7 @@ export function createExtendedRoutes(): Hono {
221
217
 
222
218
  try {
223
219
  const state = await readJsonFile<any>(statePath)
224
-
220
+
225
221
  if (!state?.currentTask) {
226
222
  return c.json({ success: false, error: 'No active task' }, 400)
227
223
  }
@@ -232,15 +228,15 @@ export function createExtendedRoutes(): Hono {
232
228
  const newState = {
233
229
  currentTask: null,
234
230
  previousTask: null,
235
- lastUpdated: new Date().toISOString()
231
+ lastUpdated: new Date().toISOString(),
236
232
  }
237
233
 
238
234
  await writeJsonFile(statePath, newState)
239
235
 
240
- return c.json({
241
- success: true,
236
+ return c.json({
237
+ success: true,
242
238
  completedTask,
243
- message: `Completed: ${completedTask.description}`
239
+ message: `Completed: ${completedTask.description}`,
244
240
  })
245
241
  } catch (error) {
246
242
  return c.json({ success: false, error: String(error) }, 500)
@@ -260,7 +256,7 @@ export function createExtendedRoutes(): Hono {
260
256
  const reason = body.reason
261
257
 
262
258
  const state = await readJsonFile<any>(statePath)
263
-
259
+
264
260
  if (!state?.currentTask) {
265
261
  return c.json({ success: false, error: 'No active task' }, 400)
266
262
  }
@@ -271,21 +267,21 @@ export function createExtendedRoutes(): Hono {
271
267
  status: 'paused',
272
268
  startedAt: state.currentTask.startedAt,
273
269
  pausedAt: new Date().toISOString(),
274
- pauseReason: reason
270
+ pauseReason: reason,
275
271
  }
276
272
 
277
273
  const newState = {
278
274
  currentTask: null,
279
275
  previousTask: pausedTask,
280
- lastUpdated: new Date().toISOString()
276
+ lastUpdated: new Date().toISOString(),
281
277
  }
282
278
 
283
279
  await writeJsonFile(statePath, newState)
284
280
 
285
- return c.json({
286
- success: true,
281
+ return c.json({
282
+ success: true,
287
283
  pausedTask,
288
- message: `Paused: ${pausedTask.description}`
284
+ message: `Paused: ${pausedTask.description}`,
289
285
  })
290
286
  } catch (error) {
291
287
  return c.json({ success: false, error: String(error) }, 500)
@@ -302,7 +298,7 @@ export function createExtendedRoutes(): Hono {
302
298
 
303
299
  try {
304
300
  const state = await readJsonFile<any>(statePath)
305
-
301
+
306
302
  if (!state?.previousTask) {
307
303
  return c.json({ success: false, error: 'No paused task' }, 400)
308
304
  }
@@ -311,21 +307,21 @@ export function createExtendedRoutes(): Hono {
311
307
  id: state.previousTask.id,
312
308
  description: state.previousTask.description,
313
309
  startedAt: new Date().toISOString(),
314
- sessionId: `sess_${Date.now().toString(36)}`
310
+ sessionId: `sess_${Date.now().toString(36)}`,
315
311
  }
316
312
 
317
313
  const newState = {
318
314
  currentTask: resumedTask,
319
315
  previousTask: null,
320
- lastUpdated: new Date().toISOString()
316
+ lastUpdated: new Date().toISOString(),
321
317
  }
322
318
 
323
319
  await writeJsonFile(statePath, newState)
324
320
 
325
- return c.json({
326
- success: true,
321
+ return c.json({
322
+ success: true,
327
323
  resumedTask,
328
- message: `Resumed: ${resumedTask.description}`
324
+ message: `Resumed: ${resumedTask.description}`,
329
325
  })
330
326
  } catch (error) {
331
327
  return c.json({ success: false, error: String(error) }, 500)
@@ -369,22 +365,22 @@ export function createExtendedRoutes(): Hono {
369
365
  description: task.description,
370
366
  startedAt: new Date().toISOString(),
371
367
  sessionId: `sess_${Date.now().toString(36)}`,
372
- featureId: task.featureId
368
+ featureId: task.featureId,
373
369
  }
374
370
 
375
371
  // Update state
376
372
  const newState = {
377
373
  currentTask: newTask,
378
374
  previousTask: null,
379
- lastUpdated: new Date().toISOString()
375
+ lastUpdated: new Date().toISOString(),
380
376
  }
381
377
 
382
378
  await writeJsonFile(statePath, newState)
383
379
 
384
- return c.json({
385
- success: true,
380
+ return c.json({
381
+ success: true,
386
382
  task: newTask,
387
- message: `Started: ${newTask.description}`
383
+ message: `Started: ${newTask.description}`,
388
384
  })
389
385
  } catch (error) {
390
386
  return c.json({ success: false, error: String(error) }, 500)
@@ -407,7 +403,7 @@ export function createExtendedRoutes(): Hono {
407
403
  return c.json({ success: false, error: 'text required' }, 400)
408
404
  }
409
405
 
410
- const ideas = await readJsonFile<any>(ideasPath) || { ideas: [], lastUpdated: '' }
406
+ const ideas = (await readJsonFile<any>(ideasPath)) || { ideas: [], lastUpdated: '' }
411
407
 
412
408
  const newIdea = {
413
409
  id: `idea_${Date.now().toString(36)}`,
@@ -415,7 +411,7 @@ export function createExtendedRoutes(): Hono {
415
411
  status: 'pending',
416
412
  priority,
417
413
  tags,
418
- addedAt: new Date().toISOString()
414
+ addedAt: new Date().toISOString(),
419
415
  }
420
416
 
421
417
  ideas.ideas.unshift(newIdea) // Prepend
@@ -423,10 +419,10 @@ export function createExtendedRoutes(): Hono {
423
419
 
424
420
  await writeJsonFile(ideasPath, ideas)
425
421
 
426
- return c.json({
427
- success: true,
422
+ return c.json({
423
+ success: true,
428
424
  idea: newIdea,
429
- message: `Captured: ${text.slice(0, 50)}...`
425
+ message: `Captured: ${text.slice(0, 50)}...`,
430
426
  })
431
427
  } catch (error) {
432
428
  return c.json({ success: false, error: String(error) }, 500)
@@ -440,7 +436,7 @@ export function createExtendedRoutes(): Hono {
440
436
  try {
441
437
  await fs.mkdir(PROJECTS_DIR, { recursive: true })
442
438
  const entries = await fs.readdir(PROJECTS_DIR, { withFileTypes: true })
443
- const projectIds = entries.filter(e => e.isDirectory()).map(e => e.name)
439
+ const projectIds = entries.filter((e) => e.isDirectory()).map((e) => e.name)
444
440
 
445
441
  let totalTasks = 0
446
442
  let totalIdeas = 0
@@ -449,14 +445,14 @@ export function createExtendedRoutes(): Hono {
449
445
 
450
446
  for (const id of projectIds) {
451
447
  const projectPath = getProjectPath(id)
452
-
448
+
453
449
  const state = await readJsonFile<any>(path.join(projectPath, 'storage/state.json'))
454
450
  const queue = await readJsonFile<any>(path.join(projectPath, 'storage/queue.json'))
455
451
  const ideas = await readJsonFile<any>(path.join(projectPath, 'storage/ideas.json'))
456
452
  const shipped = await readJsonFile<any>(path.join(projectPath, 'storage/shipped.json'))
457
453
 
458
454
  if (state?.currentTask) activeProjects++
459
-
455
+
460
456
  totalTasks += queue?.tasks?.filter((t: any) => !t.completed)?.length || 0
461
457
  totalIdeas += ideas?.ideas?.filter((i: any) => i.status === 'pending')?.length || 0
462
458
  totalShipped += shipped?.shipped?.length || 0
@@ -468,7 +464,7 @@ export function createExtendedRoutes(): Hono {
468
464
  totalTasks,
469
465
  totalIdeas,
470
466
  totalShipped,
471
- timestamp: new Date().toISOString()
467
+ timestamp: new Date().toISOString(),
472
468
  })
473
469
  } catch (error) {
474
470
  return c.json({ error: String(error) }, 500)
@@ -485,7 +481,7 @@ export function createExtendedRoutes(): Hono {
485
481
 
486
482
  await fs.mkdir(PROJECTS_DIR, { recursive: true })
487
483
  const entries = await fs.readdir(PROJECTS_DIR, { withFileTypes: true })
488
- const projectIds = entries.filter(e => e.isDirectory()).map(e => e.name)
484
+ const projectIds = entries.filter((e) => e.isDirectory()).map((e) => e.name)
489
485
 
490
486
  // If cwd provided, find matching project by path
491
487
  let targetProjectId: string | null = null
@@ -519,7 +515,7 @@ export function createExtendedRoutes(): Hono {
519
515
  activeProject = { id, name: config?.name || id, path: config?.repoPath || config?.path }
520
516
  activeTask = {
521
517
  ...state.currentTask,
522
- duration: await calculateDuration(state.currentTask.startedAt)
518
+ duration: await calculateDuration(state.currentTask.startedAt),
523
519
  }
524
520
  break
525
521
  }
@@ -540,7 +536,7 @@ export function createExtendedRoutes(): Hono {
540
536
  // Include whether we filtered by cwd
541
537
  filtered: !!targetProjectId,
542
538
  cwd: cwd || null,
543
- timestamp: new Date().toISOString()
539
+ timestamp: new Date().toISOString(),
544
540
  })
545
541
  } catch (error) {
546
542
  return c.json({ error: String(error) }, 500)
@@ -6,9 +6,9 @@
6
6
  * @version 1.0.0
7
7
  */
8
8
 
9
+ import fs from 'node:fs/promises'
10
+ import path from 'node:path'
9
11
  import { Hono } from 'hono'
10
- import fs from 'fs/promises'
11
- import path from 'path'
12
12
  import * as jsonc from 'jsonc-parser'
13
13
  import pathManager from '../infrastructure/path-manager'
14
14
  import { isNotFoundError } from '../types/fs'
@@ -46,7 +46,7 @@ async function readJsonFile<T>(filePath: string): Promise<T | null> {
46
46
  async function writeJsonFile(filePath: string, data: unknown): Promise<boolean> {
47
47
  try {
48
48
  await fs.mkdir(path.dirname(filePath), { recursive: true })
49
- await fs.writeFile(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8')
49
+ await fs.writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf-8')
50
50
  return true
51
51
  } catch (error) {
52
52
  console.error(`JSON write error: ${(error as Error).message}`)
@@ -11,11 +11,11 @@
11
11
  import { Hono } from 'hono'
12
12
  import { cors } from 'hono/cors'
13
13
  import { logger } from 'hono/logger'
14
+ import type { ServerConfig, ServerInstance } from '../types'
15
+ import { isBun } from '../utils/runtime'
14
16
  import { createRoutes } from './routes'
15
17
  import { createExtendedRoutes } from './routes-extended'
16
18
  import { createSSEManager } from './sse'
17
- import { isBun } from '../utils/runtime'
18
- import type { ServerConfig, ServerInstance } from '../types'
19
19
 
20
20
  // Server handle type that works for both runtimes
21
21
  type ServerHandle = { stop: () => void } | null
@@ -29,11 +29,14 @@ export function createServer(config: ServerConfig): ServerInstance {
29
29
 
30
30
  // Middleware
31
31
  if (config.enableCors !== false) {
32
- app.use('*', cors({
33
- origin: '*',
34
- allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
35
- allowHeaders: ['Content-Type', 'Authorization'],
36
- }))
32
+ app.use(
33
+ '*',
34
+ cors({
35
+ origin: '*',
36
+ allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
37
+ allowHeaders: ['Content-Type', 'Authorization'],
38
+ })
39
+ )
37
40
  }
38
41
 
39
42
  if (config.enableLogging !== false) {
@@ -44,25 +47,27 @@ export function createServer(config: ServerConfig): ServerInstance {
44
47
  app.get('/health', (c) => c.json({ status: 'ok', timestamp: new Date().toISOString() }))
45
48
 
46
49
  // API info
47
- app.get('/', (c) => c.json({
48
- name: 'prjct-cli',
49
- version: '0.20.0',
50
- projectId: config.projectId,
51
- endpoints: {
52
- health: '/health',
53
- state: '/api/state',
54
- queue: '/api/queue',
55
- ideas: '/api/ideas',
56
- roadmap: '/api/roadmap',
57
- shipped: '/api/shipped',
58
- events: '/api/events',
59
- // Extended endpoints for status-bar
60
- projects: '/api/projects',
61
- projectFull: '/api/projects/:id/full',
62
- statusBarCompact: '/api/status-bar/compact',
63
- globalStats: '/api/stats/global',
64
- }
65
- }))
50
+ app.get('/', (c) =>
51
+ c.json({
52
+ name: 'prjct-cli',
53
+ version: '0.20.0',
54
+ projectId: config.projectId,
55
+ endpoints: {
56
+ health: '/health',
57
+ state: '/api/state',
58
+ queue: '/api/queue',
59
+ ideas: '/api/ideas',
60
+ roadmap: '/api/roadmap',
61
+ shipped: '/api/shipped',
62
+ events: '/api/events',
63
+ // Extended endpoints for status-bar
64
+ projects: '/api/projects',
65
+ projectFull: '/api/projects/:id/full',
66
+ statusBarCompact: '/api/status-bar/compact',
67
+ globalStats: '/api/stats/global',
68
+ },
69
+ })
70
+ )
66
71
 
67
72
  // Mount API routes
68
73
  const routes = createRoutes(config.projectId, config.projectPath)