prjct-cli 0.44.1 → 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 +114 -0
  2. package/bin/prjct.ts +131 -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 +287 -29
  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 +49 -8
  43. package/core/commands/commands.ts +60 -23
  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 +14 -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 +583 -0
  59. package/core/context-tools/imports-tool.ts +403 -0
  60. package/core/context-tools/index.ts +433 -0
  61. package/core/context-tools/recent-tool.ts +307 -0
  62. package/core/context-tools/signatures-tool.ts +501 -0
  63. package/core/context-tools/summary-tool.ts +307 -0
  64. package/core/context-tools/token-counter.ts +284 -0
  65. package/core/context-tools/types.ts +253 -0
  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 -12
  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 +143 -0
  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 +170 -329
  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 -13
  157. package/core/storage/metrics-storage.ts +320 -0
  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 -302
  170. package/core/types/integrations.ts +3 -3
  171. package/core/types/storage.ts +49 -0
  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 +18907 -13189
  194. package/dist/core/infrastructure/command-installer.js +96 -111
  195. package/dist/core/infrastructure/editors-config.js +6 -6
  196. package/dist/core/infrastructure/setup.js +256 -257
  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
@@ -3,25 +3,25 @@
3
3
  * Write-Through Architecture: JSON → MD → Event
4
4
  */
5
5
 
6
- import path from 'path'
7
-
8
- import type { CommandResult, ProjectContext } from '../types'
6
+ import path from 'node:path'
7
+ import authorDetector from '../infrastructure/author-detector'
8
+ import commandInstaller from '../infrastructure/command-installer'
9
9
  import { generateUUID } from '../schemas'
10
- import type { Priority, TaskType, TaskSection } from '../schemas/state'
10
+ import type { Priority, TaskSection, TaskType } from '../schemas/state'
11
+ import { ideasStorage, queueStorage } from '../storage'
12
+ import type { CommandResult, ProjectContext } from '../types'
13
+ import { showNextSteps } from '../utils/next-steps'
14
+ import { OnboardingWizard } from '../wizard'
11
15
  import {
12
- PrjctCommandsBase,
13
- contextBuilder,
14
- toolRegistry,
15
- pathManager,
16
16
  configManager,
17
- fileHelper,
17
+ contextBuilder,
18
18
  dateHelper,
19
- out
19
+ fileHelper,
20
+ out,
21
+ PrjctCommandsBase,
22
+ pathManager,
23
+ toolRegistry,
20
24
  } from './base'
21
- import { queueStorage, ideasStorage } from '../storage'
22
- import authorDetector from '../infrastructure/author-detector'
23
- import commandInstaller from '../infrastructure/command-installer'
24
- import { showNextSteps } from '../utils/next-steps'
25
25
 
26
26
  // Lazy-loaded to avoid circular dependencies
27
27
  let _analysisCommands: import('./analysis').AnalysisCommands | null = null
@@ -33,12 +33,32 @@ async function getAnalysisCommands(): Promise<import('./analysis').AnalysisComma
33
33
  return _analysisCommands
34
34
  }
35
35
 
36
+ export interface InitOptions {
37
+ yes?: boolean // Skip interactive wizard, use defaults
38
+ idea?: string | null // Initial idea for architect mode
39
+ }
40
+
36
41
  export class PlanningCommands extends PrjctCommandsBase {
37
42
  /**
38
- * /p:init - Initialize prjct project
43
+ * /p:init - Initialize prjct project with interactive wizard
44
+ *
45
+ * @param options.yes - Skip wizard, use auto-detected values (for CI)
46
+ * @param options.idea - Initial idea for architect mode
47
+ * @param projectPath - Project directory path
39
48
  */
40
- async init(idea: string | null = null, projectPath: string = process.cwd()): Promise<CommandResult> {
49
+ async init(
50
+ options: InitOptions | string | null = {},
51
+ projectPath: string = process.cwd()
52
+ ): Promise<CommandResult> {
41
53
  try {
54
+ // Handle legacy signature: init(idea, projectPath)
55
+ let opts: InitOptions = {}
56
+ if (typeof options === 'string' || options === null) {
57
+ opts = { idea: options }
58
+ } else {
59
+ opts = options
60
+ }
61
+
42
62
  await this.initializeAgent()
43
63
 
44
64
  const isConfigured = await configManager.isConfigured(projectPath)
@@ -48,6 +68,25 @@ export class PlanningCommands extends PrjctCommandsBase {
48
68
  return { success: false, message: 'Already initialized' }
49
69
  }
50
70
 
71
+ // Determine if we should run interactive wizard
72
+ const isTTY = process.stdout.isTTY && process.stdin.isTTY
73
+ const skipWizard = opts.yes || !isTTY || process.env.CI === 'true'
74
+
75
+ // Run wizard if interactive
76
+ let wizardResult = null
77
+ if (!skipWizard) {
78
+ const wizard = new OnboardingWizard(projectPath)
79
+ wizardResult = await wizard.run()
80
+
81
+ if (wizardResult.skipped) {
82
+ return { success: false, message: 'Setup cancelled' }
83
+ }
84
+ } else if (isTTY && opts.yes) {
85
+ // Non-interactive but show progress
86
+ const wizard = new OnboardingWizard(projectPath)
87
+ wizardResult = await wizard.runNonInteractive()
88
+ }
89
+
51
90
  out.step(1, 4, 'Detecting author...')
52
91
 
53
92
  const detectedAuthor = await authorDetector.detect()
@@ -55,7 +94,7 @@ export class PlanningCommands extends PrjctCommandsBase {
55
94
  const author = {
56
95
  name: detectedAuthor.name || undefined,
57
96
  email: detectedAuthor.email || undefined,
58
- github: detectedAuthor.github || undefined
97
+ github: detectedAuthor.github || undefined,
59
98
  }
60
99
  const config = await configManager.createConfig(projectPath, author)
61
100
  const projectId = config.projectId
@@ -75,13 +114,32 @@ export class PlanningCommands extends PrjctCommandsBase {
75
114
  'planning/roadmap.md': '# ROADMAP\n\n',
76
115
  'planning/specs/.gitkeep': '# Specs directory - created by /p:spec\n',
77
116
  'memory/context.jsonl': '',
78
- 'memory/patterns.json': JSON.stringify({
79
- version: 1,
80
- decisions: {},
81
- preferences: {},
82
- workflows: {},
83
- counters: {}
84
- }, null, 2),
117
+ 'memory/patterns.json': JSON.stringify(
118
+ {
119
+ version: 1,
120
+ decisions: {},
121
+ preferences: {},
122
+ workflows: {},
123
+ counters: {},
124
+ },
125
+ null,
126
+ 2
127
+ ),
128
+ }
129
+
130
+ // Save wizard preferences if available
131
+ if (wizardResult) {
132
+ baseFiles['config/wizard.json'] = JSON.stringify(
133
+ {
134
+ projectType: wizardResult.projectType,
135
+ agents: wizardResult.agents,
136
+ stack: wizardResult.stack,
137
+ preferences: wizardResult.preferences,
138
+ createdAt: new Date().toISOString(),
139
+ },
140
+ null,
141
+ 2
142
+ )
85
143
  }
86
144
 
87
145
  for (const [filePath, content] of Object.entries(baseFiles)) {
@@ -98,16 +156,25 @@ export class PlanningCommands extends PrjctCommandsBase {
98
156
 
99
157
  if (analysisResult.success) {
100
158
  out.step(4, 4, 'Generating agents...')
101
- await analysis.sync(projectPath)
159
+
160
+ // Pass wizard agent selection to sync if available
161
+ if (wizardResult?.agents) {
162
+ await analysis.sync(projectPath, { aiTools: wizardResult.agents })
163
+ } else {
164
+ await analysis.sync(projectPath)
165
+ }
166
+
102
167
  out.done('initialized')
103
- return { success: true, mode: 'existing', projectId }
168
+ this._printNextSteps(wizardResult)
169
+ return { success: true, mode: 'existing', projectId, wizard: wizardResult }
104
170
  }
105
171
  }
106
172
 
173
+ const idea = opts.idea
107
174
  if (isEmpty && !hasCode) {
108
175
  if (!idea) {
109
176
  out.done('blank project - provide idea for architect mode')
110
- return { success: true, mode: 'blank_no_idea', projectId }
177
+ return { success: true, mode: 'blank_no_idea', projectId, wizard: wizardResult }
111
178
  }
112
179
 
113
180
  out.spin('architect mode...')
@@ -118,20 +185,60 @@ export class PlanningCommands extends PrjctCommandsBase {
118
185
  await commandInstaller.installGlobalConfig()
119
186
 
120
187
  out.done('architect mode ready')
121
- return { success: true, mode: 'architect', projectId, idea }
188
+ return { success: true, mode: 'architect', projectId, idea, wizard: wizardResult }
122
189
  }
123
190
 
124
191
  await commandInstaller.installGlobalConfig()
125
192
 
126
193
  out.done('initialized')
127
- showNextSteps('init')
128
- return { success: true, projectId }
194
+ this._printNextSteps(wizardResult)
195
+ return { success: true, projectId, wizard: wizardResult }
129
196
  } catch (error) {
130
197
  out.fail((error as Error).message)
131
198
  return { success: false, error: (error as Error).message }
132
199
  }
133
200
  }
134
201
 
202
+ /**
203
+ * Print next steps after initialization
204
+ */
205
+ private _printNextSteps(wizardResult: import('../wizard').WizardResult | null): void {
206
+ console.log('')
207
+ console.log(' Quick start:')
208
+ console.log(' prjct sync Update context after changes')
209
+ console.log(' prjct task Start working on a task')
210
+ console.log('')
211
+
212
+ if (wizardResult) {
213
+ const agentFiles = wizardResult.agents
214
+ .map((a) => {
215
+ switch (a) {
216
+ case 'claude':
217
+ return 'CLAUDE.md'
218
+ case 'cursor':
219
+ return '.cursorrules'
220
+ case 'windsurf':
221
+ return '.windsurfrules'
222
+ case 'copilot':
223
+ return '.github/copilot-instructions.md'
224
+ case 'gemini':
225
+ return 'GEMINI.md'
226
+ default:
227
+ return null
228
+ }
229
+ })
230
+ .filter(Boolean)
231
+
232
+ if (agentFiles.length > 0) {
233
+ console.log(` Generated: ${agentFiles.join(', ')}`)
234
+ console.log('')
235
+ }
236
+ }
237
+
238
+ console.log(' Docs: https://prjct.app/docs')
239
+ console.log('')
240
+ }
241
+
135
242
  /**
136
243
  * /p:feature - Add feature with value analysis, roadmap, and task breakdown
137
244
  */
@@ -153,7 +260,7 @@ export class PlanningCommands extends PrjctCommandsBase {
153
260
 
154
261
  out.spin(`planning ${description}...`)
155
262
 
156
- const context = await contextBuilder.build(projectPath, { description }) as ProjectContext
263
+ const context = (await contextBuilder.build(projectPath, { description })) as ProjectContext
157
264
  const tasks = this._breakdownFeatureTasks(description)
158
265
  const featureId = generateUUID()
159
266
 
@@ -165,21 +272,24 @@ export class PlanningCommands extends PrjctCommandsBase {
165
272
  }
166
273
 
167
274
  // Write-through: Add tasks (JSON → MD → Event)
168
- await queueStorage.addTasks(projectId, tasksWithAgents.map(t => ({
169
- description: t.task,
170
- priority: 'medium' as Priority,
171
- type: 'feature' as TaskType,
172
- section: 'active' as TaskSection,
173
- featureId,
174
- originFeature: description,
175
- agent: t.agent
176
- })))
275
+ await queueStorage.addTasks(
276
+ projectId,
277
+ tasksWithAgents.map((t) => ({
278
+ description: t.task,
279
+ priority: 'medium' as Priority,
280
+ type: 'feature' as TaskType,
281
+ section: 'active' as TaskSection,
282
+ featureId,
283
+ originFeature: description,
284
+ agent: t.agent,
285
+ }))
286
+ )
177
287
 
178
288
  await this.logToMemory(projectPath, 'feature_planned', {
179
289
  feature: description,
180
290
  featureId,
181
291
  tasks: tasksWithAgents.length,
182
- assignments: tasksWithAgents.map(t => ({ task: t.task, agent: t.agent })),
292
+ assignments: tasksWithAgents.map((t) => ({ task: t.task, agent: t.agent })),
183
293
  timestamp: dateHelper.getTimestamp(),
184
294
  })
185
295
 
@@ -187,7 +297,9 @@ export class PlanningCommands extends PrjctCommandsBase {
187
297
  acc[t.agent] = (acc[t.agent] || 0) + 1
188
298
  return acc
189
299
  }, {})
190
- const agentSummary = Object.entries(agentCounts).map(([a, c]) => `${a}:${c}`).join(' ')
300
+ const agentSummary = Object.entries(agentCounts)
301
+ .map(([a, c]) => `${a}:${c}`)
302
+ .join(' ')
191
303
 
192
304
  out.done(`${tasks.length} tasks [${agentSummary}]`)
193
305
 
@@ -219,18 +331,22 @@ export class PlanningCommands extends PrjctCommandsBase {
219
331
 
220
332
  out.spin('tracking bug...')
221
333
 
222
- const context = await contextBuilder.build(projectPath, { description }) as ProjectContext
334
+ const context = (await contextBuilder.build(projectPath, { description })) as ProjectContext
223
335
  const severity = this._detectBugSeverity(description)
224
336
 
225
- const agentResult = await this._assignAgentForTask(`fix bug: ${description}`, projectPath, context)
337
+ const agentResult = await this._assignAgentForTask(
338
+ `fix bug: ${description}`,
339
+ projectPath,
340
+ context
341
+ )
226
342
  const agent = agentResult.agent?.name || 'generalist'
227
343
 
228
344
  // Map severity to Priority type
229
345
  const priorityMap: Record<string, Priority> = {
230
- 'critical': 'critical',
231
- 'high': 'high',
232
- 'medium': 'medium',
233
- 'low': 'low'
346
+ critical: 'critical',
347
+ high: 'high',
348
+ medium: 'medium',
349
+ low: 'low',
234
350
  }
235
351
  const priority = priorityMap[severity] || 'medium'
236
352
 
@@ -240,7 +356,7 @@ export class PlanningCommands extends PrjctCommandsBase {
240
356
  priority,
241
357
  type: 'bug' as TaskType,
242
358
  section: 'active' as TaskSection,
243
- agent
359
+ agent,
244
360
  })
245
361
 
246
362
  await this.logToMemory(projectPath, 'bug_reported', {
@@ -264,7 +380,10 @@ export class PlanningCommands extends PrjctCommandsBase {
264
380
  /**
265
381
  * /p:architect - Execute architect plan and generate code
266
382
  */
267
- async architect(action: string = 'execute', projectPath: string = process.cwd()): Promise<CommandResult> {
383
+ async architect(
384
+ action: string = 'execute',
385
+ projectPath: string = process.cwd()
386
+ ): Promise<CommandResult> {
268
387
  if (action !== 'execute') {
269
388
  return {
270
389
  success: false,
@@ -319,7 +438,7 @@ export class PlanningCommands extends PrjctCommandsBase {
319
438
  console.log(`\nšŸ”§ Stack:\n${stack}`)
320
439
  console.log(`\nšŸ“‹ Implementation Steps:\n${steps}`)
321
440
 
322
- console.log('\n' + '='.repeat(60))
441
+ console.log(`\n${'='.repeat(60)}`)
323
442
  console.log('šŸ¤– READY TO GENERATE CODE')
324
443
  console.log('='.repeat(60))
325
444
 
@@ -372,7 +491,8 @@ export class PlanningCommands extends PrjctCommandsBase {
372
491
 
373
492
  // Determine if simple or complex idea
374
493
  const wordCount = description.split(/\s+/).length
375
- const isComplex = wordCount > 20 || description.includes('with') || description.includes('that')
494
+ const isComplex =
495
+ wordCount > 20 || description.includes('with') || description.includes('that')
376
496
 
377
497
  if (isComplex) {
378
498
  // Complex idea → Create architecture session
@@ -432,7 +552,10 @@ Generated: ${new Date().toLocaleString()}
432
552
  /**
433
553
  * /p:spec - Create detailed specifications for complex features
434
554
  */
435
- async spec(featureName: string | null = null, projectPath: string = process.cwd()): Promise<CommandResult> {
555
+ async spec(
556
+ featureName: string | null = null,
557
+ projectPath: string = process.cwd()
558
+ ): Promise<CommandResult> {
436
559
  try {
437
560
  const initResult = await this.ensureProjectInit(projectPath)
438
561
  if (!initResult.success) return initResult
@@ -451,9 +574,9 @@ Generated: ${new Date().toLocaleString()}
451
574
  const specsPath = path.join(globalPath, 'planning', 'specs')
452
575
 
453
576
  try {
454
- const fs = await import('fs/promises')
577
+ const fs = await import('node:fs/promises')
455
578
  const files = await fs.readdir(specsPath)
456
- const specs = files.filter(f => f.endsWith('.md') && f !== '.gitkeep')
579
+ const specs = files.filter((f) => f.endsWith('.md') && f !== '.gitkeep')
457
580
 
458
581
  if (specs.length === 0) {
459
582
  out.warn('no specs yet')
@@ -467,7 +590,7 @@ Generated: ${new Date().toLocaleString()}
467
590
  const name = s.replace('.md', '').replace(/-/g, ' ')
468
591
  console.log(` ${i + 1}. ${name}`)
469
592
  })
470
- console.log('═'.repeat(50) + '\n')
593
+ console.log(`${'═'.repeat(50)}\n`)
471
594
 
472
595
  return { success: true, specs }
473
596
  } catch (_error) {
@@ -8,16 +8,17 @@
8
8
  * - Future migration to pure handler pattern
9
9
  */
10
10
 
11
- import { commandRegistry } from './registry'
12
- import { COMMANDS, CATEGORIES } from './command-data'
13
- import { WorkflowCommands } from './workflow'
14
- import { PlanningCommands } from './planning'
15
- import { ShippingCommands } from './shipping'
11
+ import { AnalysisCommands } from './analysis'
16
12
  import { AnalyticsCommands } from './analytics'
13
+ import { CATEGORIES, COMMANDS } from './command-data'
14
+ import { ContextCommands } from './context'
17
15
  import { MaintenanceCommands } from './maintenance'
18
- import { AnalysisCommands } from './analysis'
16
+ import { PlanningCommands } from './planning'
17
+ import { commandRegistry } from './registry'
19
18
  import { SetupCommands } from './setup'
20
- import { ContextCommands } from './context'
19
+ import { ShippingCommands } from './shipping'
20
+ import { UninstallCommands } from './uninstall'
21
+ import { WorkflowCommands } from './workflow'
21
22
 
22
23
  // Singleton instances of command groups
23
24
  const workflow = new WorkflowCommands()
@@ -28,6 +29,7 @@ const maintenance = new MaintenanceCommands()
28
29
  const analysis = new AnalysisCommands()
29
30
  const setup = new SetupCommands()
30
31
  const context = new ContextCommands()
32
+ const uninstallCmd = new UninstallCommands()
31
33
 
32
34
  /**
33
35
  * Register categories
@@ -49,7 +51,7 @@ export function registerAllCommands(): void {
49
51
  registerCategories()
50
52
 
51
53
  // Helper to get metadata from COMMANDS
52
- const getMeta = (name: string) => COMMANDS.find(c => c.name === name)
54
+ const getMeta = (name: string) => COMMANDS.find((c) => c.name === name)
53
55
 
54
56
  // Workflow commands
55
57
  commandRegistry.registerMethod('done', workflow, 'done', getMeta('done'))
@@ -81,10 +83,12 @@ export function registerAllCommands(): void {
81
83
  // Analysis commands
82
84
  commandRegistry.registerMethod('analyze', analysis, 'analyze', getMeta('analyze'))
83
85
  commandRegistry.registerMethod('sync', analysis, 'sync', getMeta('sync'))
86
+ commandRegistry.registerMethod('stats', analysis, 'stats', getMeta('stats'))
84
87
 
85
88
  // Setup commands
86
89
  commandRegistry.registerMethod('start', setup, 'start', getMeta('start'))
87
90
  commandRegistry.registerMethod('setup', setup, 'setup', getMeta('setup'))
91
+ commandRegistry.registerMethod('uninstall', uninstallCmd, 'uninstall', getMeta('uninstall'))
88
92
 
89
93
  // Context command (for Claude templates)
90
94
  commandRegistry.registerMethod('context', context, 'context', getMeta('context'))
@@ -102,5 +106,6 @@ export {
102
106
  maintenance,
103
107
  analysis,
104
108
  setup,
105
- context
109
+ context,
110
+ uninstallCmd,
106
111
  }
@@ -5,30 +5,30 @@
5
5
  * Each command is registered as a handler that receives context and returns a result.
6
6
  */
7
7
 
8
- import type { CommandResult } from '../types'
9
8
  import configManager from '../infrastructure/config-manager'
10
9
  import pathManager from '../infrastructure/path-manager'
10
+ import type { CommandResult } from '../types'
11
11
  import { getTimestamp } from '../utils/date-helper'
12
12
 
13
13
  // Re-export types for convenience
14
14
  export type {
15
- ExecutionContext,
15
+ BlockingRules,
16
+ CategoryInfo,
16
17
  CommandHandler,
18
+ CommandMeta,
19
+ CommandValidationResult as ValidationResult,
20
+ ExecutionContext,
17
21
  HandlerFn,
18
22
  RegistryCommandUsage as CommandUsage,
19
- BlockingRules,
20
- CommandMeta,
21
- CategoryInfo,
22
23
  RegistryStats,
23
- CommandValidationResult as ValidationResult,
24
24
  } from '../types'
25
25
 
26
26
  import type {
27
- ExecutionContext,
27
+ CategoryInfo,
28
28
  CommandHandler,
29
- HandlerFn,
30
29
  CommandMeta,
31
- CategoryInfo,
30
+ ExecutionContext,
31
+ HandlerFn,
32
32
  RegistryStats,
33
33
  } from '../types'
34
34
 
@@ -60,7 +60,11 @@ export class CommandRegistry {
60
60
  /**
61
61
  * Register a command handler function (function-based)
62
62
  */
63
- registerFn<TParams>(name: string, handler: HandlerFn<TParams>, meta?: Partial<CommandMeta>): void {
63
+ registerFn<TParams>(
64
+ name: string,
65
+ handler: HandlerFn<TParams>,
66
+ meta?: Partial<CommandMeta>
67
+ ): void {
64
68
  this.handlerFns.set(name, handler as HandlerFn<unknown>)
65
69
  this.setMeta(name, meta)
66
70
  }
@@ -134,10 +138,7 @@ export class CommandRegistry {
134
138
  * Get list of registered commands
135
139
  */
136
140
  list(): string[] {
137
- return [
138
- ...this.handlers.keys(),
139
- ...this.handlerFns.keys(),
140
- ]
141
+ return [...this.handlers.keys(), ...this.handlerFns.keys()]
141
142
  }
142
143
 
143
144
  /**
@@ -2,15 +2,15 @@
2
2
  * Setup Commands: start, setup, installStatusLine, showAsciiArt
3
3
  */
4
4
 
5
- import path from 'path'
6
- import fs from 'fs'
5
+ import fs from 'node:fs'
6
+ import path from 'node:path'
7
7
  import chalk from 'chalk'
8
8
 
9
9
  import commandInstaller from '../infrastructure/command-installer'
10
10
  import pathManager from '../infrastructure/path-manager'
11
11
  import type { CommandResult, SetupOptions } from '../types'
12
- import { PrjctCommandsBase } from './base'
13
12
  import { VERSION } from '../utils/version'
13
+ import { PrjctCommandsBase } from './base'
14
14
 
15
15
  export class SetupCommands extends PrjctCommandsBase {
16
16
  /**
@@ -19,12 +19,13 @@ export class SetupCommands extends PrjctCommandsBase {
19
19
  async start(): Promise<CommandResult> {
20
20
  const aiProvider = require('../infrastructure/ai-provider')
21
21
  const activeProvider = aiProvider.getActiveProvider()
22
-
22
+
23
23
  console.log(`šŸš€ Setting up prjct for ${activeProvider.displayName}...\n`)
24
24
 
25
25
  const status = await commandInstaller.checkInstallation()
26
26
 
27
- if (!status.claudeDetected) { // Note: variable name is legacy, checks active provider
27
+ if (!status.claudeDetected) {
28
+ // Note: variable name is legacy, checks active provider
28
29
  return {
29
30
  success: false,
30
31
  message:
@@ -43,11 +44,15 @@ export class SetupCommands extends PrjctCommandsBase {
43
44
  }
44
45
  }
45
46
 
46
- console.log(`\nāœ… Installed ${result.installed?.length ?? 0} commands to:\n ${pathManager.getDisplayPath(result.path || '')}`)
47
+ console.log(
48
+ `\nāœ… Installed ${result.installed?.length ?? 0} commands to:\n ${pathManager.getDisplayPath(result.path || '')}`
49
+ )
47
50
 
48
51
  if ((result.errors?.length ?? 0) > 0) {
49
52
  console.log(`\nāš ļø ${result.errors?.length ?? 0} errors:`)
50
- result.errors?.forEach((e: { file: string; error: string }) => console.log(` - ${e.file}: ${e.error}`))
53
+ result.errors?.forEach((e: { file: string; error: string }) =>
54
+ console.log(` - ${e.file}: ${e.error}`)
55
+ )
51
56
  }
52
57
 
53
58
  console.log('\nšŸŽ‰ Setup complete!')
@@ -87,12 +92,16 @@ export class SetupCommands extends PrjctCommandsBase {
87
92
 
88
93
  if ((result.errors?.length ?? 0) > 0) {
89
94
  console.log(`\nāš ļø ${result.errors?.length ?? 0} errors:`)
90
- result.errors?.forEach((e: { file: string; error: string }) => console.log(` - ${e.file}: ${e.error}`))
95
+ result.errors?.forEach((e: { file: string; error: string }) =>
96
+ console.log(` - ${e.file}: ${e.error}`)
97
+ )
91
98
  }
92
99
 
93
100
  console.log('\nšŸ“ Installing global configuration...')
94
101
  const configResult = await commandInstaller.installGlobalConfig()
95
- const displayPath = configResult.path ? pathManager.getDisplayPath(configResult.path) : 'global config'
102
+ const displayPath = configResult.path
103
+ ? pathManager.getDisplayPath(configResult.path)
104
+ : 'global config'
96
105
 
97
106
  if (configResult.success) {
98
107
  if (configResult.action === 'created') {
@@ -108,7 +117,7 @@ export class SetupCommands extends PrjctCommandsBase {
108
117
 
109
118
  const aiProvider = require('../infrastructure/ai-provider')
110
119
  const activeProvider = aiProvider.getActiveProvider()
111
-
120
+
112
121
  // Status line is currently Claude-only
113
122
  if (activeProvider.name === 'claude') {
114
123
  console.log('\n⚔ Installing status line...')
@@ -211,7 +220,7 @@ echo "⚔ prjct"
211
220
 
212
221
  settings.statusLine = {
213
222
  type: 'command',
214
- command: statusLinePath
223
+ command: statusLinePath,
215
224
  }
216
225
 
217
226
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2))
@@ -235,7 +244,9 @@ echo "⚔ prjct"
235
244
  console.log(chalk.bold.cyan(' ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘'))
236
245
  console.log(chalk.bold.cyan(' ā•šā•ā• ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•'))
237
246
  console.log('')
238
- console.log(` ${chalk.bold.cyan('prjct')}${chalk.magenta('/')}${chalk.green('cli')} ${chalk.dim.white('v' + VERSION + ' installed')}`)
247
+ console.log(
248
+ ` ${chalk.bold.cyan('prjct')}${chalk.magenta('/')}${chalk.green('cli')} ${chalk.dim.white(`v${VERSION} installed`)}`
249
+ )
239
250
  console.log('')
240
251
  console.log(` ${chalk.yellow('⚔')} Ship faster with zero friction`)
241
252
  console.log(` ${chalk.green('šŸ“')} From idea to technical tasks in minutes`)
@@ -258,10 +269,11 @@ echo "⚔ prjct"
258
269
  console.log(chalk.dim('─────────────────────────────────────────────────'))
259
270
  console.log('')
260
271
  console.log(` ${chalk.dim('Documentation:')} ${chalk.cyan('https://prjct.app')}`)
261
- console.log(` ${chalk.dim('Report issues:')} ${chalk.cyan('https://github.com/jlopezlira/prjct-cli/issues')}`)
272
+ console.log(
273
+ ` ${chalk.dim('Report issues:')} ${chalk.cyan('https://github.com/jlopezlira/prjct-cli/issues')}`
274
+ )
262
275
  console.log('')
263
276
  console.log(chalk.bold.magenta('Happy shipping! šŸš€'))
264
277
  console.log('')
265
278
  }
266
-
267
279
  }