prjct-cli 0.45.0 → 0.45.4

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 +82 -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
@@ -0,0 +1,633 @@
1
+ /**
2
+ * OnboardingWizard - Interactive first-run setup experience
3
+ *
4
+ * Guides new users through project setup in ~60 seconds:
5
+ * 1. Project type detection + confirmation
6
+ * 2. AI agent selection (multi-select)
7
+ * 3. Stack confirmation
8
+ * 4. Preferences collection
9
+ * 5. Generation + summary
10
+ */
11
+
12
+ import chalk from 'chalk'
13
+ import prompts from 'prompts'
14
+ import out from '../utils/output'
15
+
16
+ // ============================================================================
17
+ // Types
18
+ // ============================================================================
19
+
20
+ export type ProjectType =
21
+ | 'web-app'
22
+ | 'api-backend'
23
+ | 'fullstack'
24
+ | 'cli-tool'
25
+ | 'library'
26
+ | 'monorepo'
27
+ | 'unknown'
28
+
29
+ export type AIAgent = 'claude' | 'cursor' | 'windsurf' | 'copilot' | 'gemini'
30
+
31
+ export interface DetectedStack {
32
+ language: string
33
+ framework?: string
34
+ runtime?: string
35
+ packageManager?: string
36
+ technologies: string[]
37
+ }
38
+
39
+ export interface WizardPreferences {
40
+ verbosity: 'minimal' | 'normal' | 'verbose'
41
+ autoSync: boolean
42
+ telemetry: boolean
43
+ }
44
+
45
+ export interface WizardResult {
46
+ projectType: ProjectType
47
+ agents: AIAgent[]
48
+ stack: DetectedStack
49
+ preferences: WizardPreferences
50
+ skipped: boolean
51
+ }
52
+
53
+ export interface WizardStep {
54
+ id: string
55
+ title: string
56
+ run: () => Promise<boolean> // Returns true if should continue, false to abort
57
+ }
58
+
59
+ // ============================================================================
60
+ // Constants
61
+ // ============================================================================
62
+
63
+ const PROJECT_TYPES: { value: ProjectType; title: string; description: string }[] = [
64
+ { value: 'web-app', title: 'Web Application', description: 'React, Vue, Angular, Next.js, etc.' },
65
+ {
66
+ value: 'api-backend',
67
+ title: 'API / Backend Service',
68
+ description: 'Express, Hono, FastAPI, etc.',
69
+ },
70
+ {
71
+ value: 'fullstack',
72
+ title: 'Full-Stack (Monorepo)',
73
+ description: 'Frontend + Backend in one repo',
74
+ },
75
+ { value: 'cli-tool', title: 'CLI Tool', description: 'Command-line application' },
76
+ { value: 'library', title: 'Library / Package', description: 'Reusable npm/pip/cargo package' },
77
+ {
78
+ value: 'monorepo',
79
+ title: 'Monorepo (Multiple Projects)',
80
+ description: 'Turborepo, Nx, Lerna, etc.',
81
+ },
82
+ ]
83
+
84
+ const AI_AGENTS: { value: AIAgent; title: string; description: string }[] = [
85
+ { value: 'claude', title: 'Claude Code', description: "Anthropic's Claude in VS Code/CLI" },
86
+ { value: 'cursor', title: 'Cursor', description: 'AI-first code editor' },
87
+ { value: 'windsurf', title: 'Windsurf', description: "Codeium's AI IDE" },
88
+ { value: 'copilot', title: 'GitHub Copilot', description: "GitHub's AI pair programmer" },
89
+ { value: 'gemini', title: 'Gemini CLI', description: "Google's Gemini in terminal" },
90
+ ]
91
+
92
+ // ============================================================================
93
+ // OnboardingWizard Class
94
+ // ============================================================================
95
+
96
+ export class OnboardingWizard {
97
+ private projectPath: string
98
+ private currentStep: number = 0
99
+ private totalSteps: number = 5
100
+ private aborted: boolean = false
101
+
102
+ // Collected data
103
+ private detectedType: ProjectType = 'unknown'
104
+ private confirmedType: ProjectType = 'unknown'
105
+ private selectedAgents: AIAgent[] = []
106
+ private detectedStack: DetectedStack = { language: 'Unknown', technologies: [] }
107
+ private confirmedStack: DetectedStack = { language: 'Unknown', technologies: [] }
108
+ private preferences: WizardPreferences = {
109
+ verbosity: 'normal',
110
+ autoSync: true,
111
+ telemetry: false,
112
+ }
113
+
114
+ constructor(projectPath: string = process.cwd()) {
115
+ this.projectPath = projectPath
116
+
117
+ // Handle Ctrl+C gracefully
118
+ prompts.override({})
119
+ }
120
+
121
+ // ==========================================================================
122
+ // Public API
123
+ // ==========================================================================
124
+
125
+ /**
126
+ * Run the full wizard flow
127
+ */
128
+ async run(): Promise<WizardResult> {
129
+ this.printWelcome()
130
+
131
+ const steps: WizardStep[] = [
132
+ { id: 'project-type', title: 'Project Type', run: () => this.stepProjectType() },
133
+ { id: 'ai-agents', title: 'AI Agents', run: () => this.stepAIAgents() },
134
+ { id: 'stack', title: 'Stack Confirmation', run: () => this.stepStack() },
135
+ { id: 'preferences', title: 'Preferences', run: () => this.stepPreferences() },
136
+ { id: 'summary', title: 'Summary', run: () => this.stepSummary() },
137
+ ]
138
+
139
+ for (const step of steps) {
140
+ this.currentStep++
141
+ this.printStepHeader(step.title)
142
+
143
+ const shouldContinue = await step.run()
144
+
145
+ if (!shouldContinue || this.aborted) {
146
+ return this.buildResult(true)
147
+ }
148
+ }
149
+
150
+ return this.buildResult(false)
151
+ }
152
+
153
+ /**
154
+ * Run in non-interactive mode (--yes flag)
155
+ * Uses all auto-detected values without prompting
156
+ */
157
+ async runNonInteractive(): Promise<WizardResult> {
158
+ out.spin('Auto-detecting project configuration...')
159
+
160
+ // Auto-detect everything
161
+ this.detectedType = await this.detectProjectType()
162
+ this.confirmedType = this.detectedType
163
+ this.selectedAgents = ['claude'] // Default to Claude
164
+ this.detectedStack = await this.detectStack()
165
+ this.confirmedStack = this.detectedStack
166
+ // Use default preferences
167
+
168
+ out.done('Configuration detected')
169
+
170
+ return this.buildResult(false)
171
+ }
172
+
173
+ // ==========================================================================
174
+ // Step Implementations
175
+ // ==========================================================================
176
+
177
+ /**
178
+ * Step 1: Project Type Detection + Confirmation
179
+ */
180
+ private async stepProjectType(): Promise<boolean> {
181
+ this.detectedType = await this.detectProjectType()
182
+
183
+ const response = await prompts(
184
+ {
185
+ type: 'select',
186
+ name: 'projectType',
187
+ message:
188
+ this.detectedType !== 'unknown'
189
+ ? `Detected: ${this.getProjectTypeLabel(this.detectedType)}. Is this correct?`
190
+ : 'What type of project is this?',
191
+ choices: PROJECT_TYPES.map((pt) => ({
192
+ title: pt.title,
193
+ description: pt.description,
194
+ value: pt.value,
195
+ selected: pt.value === this.detectedType,
196
+ })),
197
+ initial: PROJECT_TYPES.findIndex((pt) => pt.value === this.detectedType),
198
+ },
199
+ { onCancel: () => this.handleCancel() }
200
+ )
201
+
202
+ if (this.aborted) return false
203
+
204
+ this.confirmedType = response.projectType || this.detectedType
205
+ return true
206
+ }
207
+
208
+ /**
209
+ * Step 2: AI Agent Selection (multi-select)
210
+ */
211
+ private async stepAIAgents(): Promise<boolean> {
212
+ const detectedAgents = await this.detectInstalledAgents()
213
+
214
+ const response = await prompts(
215
+ {
216
+ type: 'multiselect',
217
+ name: 'agents',
218
+ message: 'Which AI agents do you use?',
219
+ choices: AI_AGENTS.map((agent) => ({
220
+ title: agent.title,
221
+ description: agent.description,
222
+ value: agent.value,
223
+ selected: detectedAgents.includes(agent.value),
224
+ })),
225
+ hint: '- Space to select, Enter to confirm',
226
+ instructions: false,
227
+ min: 1,
228
+ },
229
+ { onCancel: () => this.handleCancel() }
230
+ )
231
+
232
+ if (this.aborted) return false
233
+
234
+ this.selectedAgents = response.agents || ['claude']
235
+ return true
236
+ }
237
+
238
+ /**
239
+ * Step 3: Stack Detection + Confirmation
240
+ */
241
+ private async stepStack(): Promise<boolean> {
242
+ this.detectedStack = await this.detectStack()
243
+
244
+ const stackDisplay = this.formatStackDisplay(this.detectedStack)
245
+ console.log(chalk.dim(`\n Detected: ${stackDisplay}\n`))
246
+
247
+ const response = await prompts(
248
+ {
249
+ type: 'confirm',
250
+ name: 'confirmed',
251
+ message: 'Is this stack correct?',
252
+ initial: true,
253
+ },
254
+ { onCancel: () => this.handleCancel() }
255
+ )
256
+
257
+ if (this.aborted) return false
258
+
259
+ if (response.confirmed) {
260
+ this.confirmedStack = this.detectedStack
261
+ } else {
262
+ // Allow manual override
263
+ const manualResponse = await prompts(
264
+ [
265
+ {
266
+ type: 'text',
267
+ name: 'language',
268
+ message: 'Primary language:',
269
+ initial: this.detectedStack.language,
270
+ },
271
+ {
272
+ type: 'text',
273
+ name: 'framework',
274
+ message: 'Framework (optional):',
275
+ initial: this.detectedStack.framework || '',
276
+ },
277
+ ],
278
+ { onCancel: () => this.handleCancel() }
279
+ )
280
+
281
+ if (this.aborted) return false
282
+
283
+ this.confirmedStack = {
284
+ ...this.detectedStack,
285
+ language: manualResponse.language || this.detectedStack.language,
286
+ framework: manualResponse.framework || undefined,
287
+ }
288
+ }
289
+
290
+ return true
291
+ }
292
+
293
+ /**
294
+ * Step 4: Preferences Collection
295
+ */
296
+ private async stepPreferences(): Promise<boolean> {
297
+ const response = await prompts(
298
+ [
299
+ {
300
+ type: 'select',
301
+ name: 'verbosity',
302
+ message: 'Output verbosity:',
303
+ choices: [
304
+ { title: 'Minimal', description: 'Essential output only', value: 'minimal' },
305
+ { title: 'Normal (Recommended)', description: 'Balanced information', value: 'normal' },
306
+ { title: 'Verbose', description: 'Detailed logging', value: 'verbose' },
307
+ ],
308
+ initial: 1,
309
+ },
310
+ {
311
+ type: 'confirm',
312
+ name: 'autoSync',
313
+ message: 'Auto-sync context on file changes?',
314
+ initial: true,
315
+ },
316
+ ],
317
+ { onCancel: () => this.handleCancel() }
318
+ )
319
+
320
+ if (this.aborted) return false
321
+
322
+ this.preferences = {
323
+ verbosity: response.verbosity || 'normal',
324
+ autoSync: response.autoSync ?? true,
325
+ telemetry: false, // Default off, can be enabled later
326
+ }
327
+
328
+ return true
329
+ }
330
+
331
+ /**
332
+ * Step 5: Summary + Generation
333
+ */
334
+ private async stepSummary(): Promise<boolean> {
335
+ console.log('')
336
+ console.log(chalk.bold(' Configuration Summary'))
337
+ console.log(chalk.dim(` ${'─'.repeat(40)}`))
338
+ console.log(` ${chalk.cyan('Project Type:')} ${this.getProjectTypeLabel(this.confirmedType)}`)
339
+ console.log(
340
+ ` ${chalk.cyan('AI Agents:')} ${this.selectedAgents.map((a) => this.getAgentLabel(a)).join(', ')}`
341
+ )
342
+ console.log(` ${chalk.cyan('Stack:')} ${this.formatStackDisplay(this.confirmedStack)}`)
343
+ console.log(` ${chalk.cyan('Verbosity:')} ${this.preferences.verbosity}`)
344
+ console.log(` ${chalk.cyan('Auto-sync:')} ${this.preferences.autoSync ? 'Yes' : 'No'}`)
345
+ console.log('')
346
+
347
+ const response = await prompts(
348
+ {
349
+ type: 'confirm',
350
+ name: 'proceed',
351
+ message: 'Generate configuration with these settings?',
352
+ initial: true,
353
+ },
354
+ { onCancel: () => this.handleCancel() }
355
+ )
356
+
357
+ if (this.aborted || !response.proceed) {
358
+ return false
359
+ }
360
+
361
+ return true
362
+ }
363
+
364
+ // ==========================================================================
365
+ // Detection Methods
366
+ // ==========================================================================
367
+
368
+ /**
369
+ * Detect project type from file system
370
+ */
371
+ async detectProjectType(): Promise<ProjectType> {
372
+ const fs = await import('node:fs/promises')
373
+ const path = await import('node:path')
374
+
375
+ try {
376
+ const files = await fs.readdir(this.projectPath)
377
+
378
+ // Check for monorepo indicators
379
+ if (
380
+ files.includes('turbo.json') ||
381
+ files.includes('lerna.json') ||
382
+ files.includes('nx.json')
383
+ ) {
384
+ return 'monorepo'
385
+ }
386
+
387
+ // Check for package.json
388
+ if (files.includes('package.json')) {
389
+ const pkgPath = path.join(this.projectPath, 'package.json')
390
+ const pkgContent = await fs.readFile(pkgPath, 'utf-8')
391
+ const pkg = JSON.parse(pkgContent)
392
+
393
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies }
394
+
395
+ // CLI tool indicators
396
+ if (pkg.bin) return 'cli-tool'
397
+
398
+ // Library indicators
399
+ if (pkg.main && !deps.react && !deps.vue && !deps.angular && !deps.express && !deps.hono) {
400
+ return 'library'
401
+ }
402
+
403
+ // Full-stack indicators
404
+ if ((deps.react || deps.vue) && (deps.express || deps.hono || deps.fastify)) {
405
+ return 'fullstack'
406
+ }
407
+
408
+ // Frontend indicators
409
+ if (deps.react || deps.vue || deps['@angular/core'] || deps.next || deps.nuxt) {
410
+ return 'web-app'
411
+ }
412
+
413
+ // Backend indicators
414
+ if (deps.express || deps.hono || deps.fastify || deps.koa || deps.nestjs) {
415
+ return 'api-backend'
416
+ }
417
+ }
418
+
419
+ // Python project detection
420
+ if (files.includes('pyproject.toml') || files.includes('setup.py')) {
421
+ const hasServer = files.some((f) => ['main.py', 'app.py', 'server.py'].includes(f))
422
+ return hasServer ? 'api-backend' : 'library'
423
+ }
424
+
425
+ // Go project detection
426
+ if (files.includes('go.mod')) {
427
+ return files.includes('main.go') ? 'cli-tool' : 'library'
428
+ }
429
+
430
+ // Rust project detection
431
+ if (files.includes('Cargo.toml')) {
432
+ return 'cli-tool' // Most Rust CLIs
433
+ }
434
+
435
+ return 'unknown'
436
+ } catch {
437
+ return 'unknown'
438
+ }
439
+ }
440
+
441
+ /**
442
+ * Detect installed AI agents from config files
443
+ */
444
+ async detectInstalledAgents(): Promise<AIAgent[]> {
445
+ const fs = await import('node:fs/promises')
446
+ const path = await import('node:path')
447
+ const os = await import('node:os')
448
+
449
+ const agents: AIAgent[] = []
450
+
451
+ // Claude Code: Check ~/.claude directory
452
+ try {
453
+ await fs.access(path.join(os.homedir(), '.claude'))
454
+ agents.push('claude')
455
+ } catch {
456
+ /* not installed */
457
+ }
458
+
459
+ // Cursor: Check for .cursorrules in project
460
+ try {
461
+ await fs.access(path.join(this.projectPath, '.cursorrules'))
462
+ agents.push('cursor')
463
+ } catch {
464
+ /* not installed */
465
+ }
466
+
467
+ // Windsurf: Check for .windsurfrules
468
+ try {
469
+ await fs.access(path.join(this.projectPath, '.windsurfrules'))
470
+ agents.push('windsurf')
471
+ } catch {
472
+ /* not installed */
473
+ }
474
+
475
+ // Copilot: Check for .github/copilot-instructions.md
476
+ try {
477
+ await fs.access(path.join(this.projectPath, '.github', 'copilot-instructions.md'))
478
+ agents.push('copilot')
479
+ } catch {
480
+ /* not installed */
481
+ }
482
+
483
+ // Gemini: Check ~/.gemini
484
+ try {
485
+ await fs.access(path.join(os.homedir(), '.gemini'))
486
+ agents.push('gemini')
487
+ } catch {
488
+ /* not installed */
489
+ }
490
+
491
+ // Default to Claude if nothing detected
492
+ return agents.length > 0 ? agents : ['claude']
493
+ }
494
+
495
+ /**
496
+ * Detect tech stack from project files
497
+ */
498
+ async detectStack(): Promise<DetectedStack> {
499
+ const fs = await import('node:fs/promises')
500
+ const path = await import('node:path')
501
+
502
+ const stack: DetectedStack = {
503
+ language: 'Unknown',
504
+ technologies: [],
505
+ }
506
+
507
+ try {
508
+ const files = await fs.readdir(this.projectPath)
509
+
510
+ // Language detection
511
+ if (files.includes('package.json')) {
512
+ const pkgPath = path.join(this.projectPath, 'package.json')
513
+ const pkgContent = await fs.readFile(pkgPath, 'utf-8')
514
+ const pkg = JSON.parse(pkgContent)
515
+
516
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies }
517
+
518
+ // TypeScript check
519
+ stack.language = deps.typescript ? 'TypeScript' : 'JavaScript'
520
+
521
+ // Framework detection
522
+ if (deps.next) stack.framework = 'Next.js'
523
+ else if (deps.nuxt) stack.framework = 'Nuxt'
524
+ else if (deps.react) stack.framework = 'React'
525
+ else if (deps.vue) stack.framework = 'Vue'
526
+ else if (deps['@angular/core']) stack.framework = 'Angular'
527
+ else if (deps.express) stack.framework = 'Express'
528
+ else if (deps.hono) stack.framework = 'Hono'
529
+ else if (deps.fastify) stack.framework = 'Fastify'
530
+ else if (deps.nestjs || deps['@nestjs/core']) stack.framework = 'NestJS'
531
+
532
+ // Runtime detection
533
+ if (deps.bun || deps['@types/bun']) stack.runtime = 'Bun'
534
+ else if (pkg.engines?.bun) stack.runtime = 'Bun'
535
+ else stack.runtime = 'Node.js'
536
+
537
+ // Package manager detection
538
+ if (files.includes('bun.lockb')) stack.packageManager = 'Bun'
539
+ else if (files.includes('pnpm-lock.yaml')) stack.packageManager = 'pnpm'
540
+ else if (files.includes('yarn.lock')) stack.packageManager = 'Yarn'
541
+ else if (files.includes('package-lock.json')) stack.packageManager = 'npm'
542
+
543
+ // Additional technologies
544
+ if (deps.prisma || deps['@prisma/client']) stack.technologies.push('Prisma')
545
+ if (deps.drizzle || deps['drizzle-orm']) stack.technologies.push('Drizzle')
546
+ if (deps.tailwindcss) stack.technologies.push('Tailwind CSS')
547
+ if (deps.zod) stack.technologies.push('Zod')
548
+ if (deps.trpc || deps['@trpc/server']) stack.technologies.push('tRPC')
549
+ } else if (files.includes('pyproject.toml') || files.includes('requirements.txt')) {
550
+ stack.language = 'Python'
551
+ // Could parse pyproject.toml for framework detection
552
+ } else if (files.includes('go.mod')) {
553
+ stack.language = 'Go'
554
+ } else if (files.includes('Cargo.toml')) {
555
+ stack.language = 'Rust'
556
+ } else if (files.includes('pom.xml') || files.includes('build.gradle')) {
557
+ stack.language = 'Java'
558
+ }
559
+
560
+ return stack
561
+ } catch {
562
+ return stack
563
+ }
564
+ }
565
+
566
+ // ==========================================================================
567
+ // Helper Methods
568
+ // ==========================================================================
569
+
570
+ private printWelcome(): void {
571
+ console.log('')
572
+ console.log(chalk.bold.cyan(' Welcome to prjct-cli!'))
573
+ console.log(chalk.dim(" Let's set up your project in 60 seconds."))
574
+ console.log('')
575
+ }
576
+
577
+ private printStepHeader(title: string): void {
578
+ console.log('')
579
+ console.log(chalk.dim(` Step ${this.currentStep}/${this.totalSteps}: ${title}`))
580
+ console.log('')
581
+ }
582
+
583
+ private handleCancel(): void {
584
+ this.aborted = true
585
+ console.log(chalk.yellow('\n Setup cancelled. Run again anytime.\n'))
586
+ }
587
+
588
+ private getProjectTypeLabel(type: ProjectType): string {
589
+ return PROJECT_TYPES.find((pt) => pt.value === type)?.title || 'Unknown'
590
+ }
591
+
592
+ private getAgentLabel(agent: AIAgent): string {
593
+ return AI_AGENTS.find((a) => a.value === agent)?.title || agent
594
+ }
595
+
596
+ private formatStackDisplay(stack: DetectedStack): string {
597
+ const parts = [stack.language]
598
+ if (stack.framework) parts.push(stack.framework)
599
+ if (stack.runtime && stack.runtime !== 'Node.js') parts.push(stack.runtime)
600
+ if (stack.technologies.length > 0) {
601
+ parts.push(`+ ${stack.technologies.slice(0, 3).join(', ')}`)
602
+ }
603
+ return parts.join(' / ')
604
+ }
605
+
606
+ private buildResult(skipped: boolean): WizardResult {
607
+ return {
608
+ projectType: this.confirmedType,
609
+ agents: this.selectedAgents,
610
+ stack: this.confirmedStack,
611
+ preferences: this.preferences,
612
+ skipped,
613
+ }
614
+ }
615
+
616
+ // ==========================================================================
617
+ // Getters for external access
618
+ // ==========================================================================
619
+
620
+ getSelectedAgents(): AIAgent[] {
621
+ return this.selectedAgents
622
+ }
623
+
624
+ getConfirmedStack(): DetectedStack {
625
+ return this.confirmedStack
626
+ }
627
+
628
+ getPreferences(): WizardPreferences {
629
+ return this.preferences
630
+ }
631
+ }
632
+
633
+ export default OnboardingWizard
@@ -34,27 +34,27 @@ interface TransitionResult {
34
34
  const WORKFLOW_STATES: Record<WorkflowState, StateDefinition> = {
35
35
  idle: {
36
36
  transitions: ['task', 'next'],
37
- prompt: "p. task <description> Start working",
37
+ prompt: 'p. task <description> Start working',
38
38
  description: 'No active task',
39
39
  },
40
40
  working: {
41
41
  transitions: ['done', 'pause'],
42
- prompt: "p. done Complete task | p. pause Switch context",
42
+ prompt: 'p. done Complete task | p. pause Switch context',
43
43
  description: 'Task in progress',
44
44
  },
45
45
  paused: {
46
46
  transitions: ['resume', 'task'],
47
- prompt: "p. resume Continue | p. task <new> Start different",
47
+ prompt: 'p. resume Continue | p. task <new> Start different',
48
48
  description: 'Task paused',
49
49
  },
50
50
  completed: {
51
51
  transitions: ['ship', 'task', 'next'],
52
- prompt: "p. ship Ship it | p. task <next> Start next",
52
+ prompt: 'p. ship Ship it | p. task <next> Start next',
53
53
  description: 'Task completed',
54
54
  },
55
55
  shipped: {
56
56
  transitions: ['task', 'next'],
57
- prompt: "p. task <description> Start new task",
57
+ prompt: 'p. task <description> Start new task',
58
58
  description: 'Feature shipped',
59
59
  },
60
60
  }
@@ -103,7 +103,7 @@ export class WorkflowStateMachine {
103
103
  }
104
104
 
105
105
  // Build helpful error message
106
- const validCommands = stateConfig.transitions.map(c => `p. ${c}`).join(', ')
106
+ const validCommands = stateConfig.transitions.map((c) => `p. ${c}`).join(', ')
107
107
 
108
108
  return {
109
109
  valid: false,
@@ -160,7 +160,7 @@ export class WorkflowStateMachine {
160
160
  */
161
161
  formatNextSteps(state: WorkflowState): string[] {
162
162
  const stateConfig = WORKFLOW_STATES[state]
163
- return stateConfig.transitions.map(cmd => {
163
+ return stateConfig.transitions.map((cmd) => {
164
164
  switch (cmd) {
165
165
  case 'task':
166
166
  return 'p. task <desc> Start new task'