prjct-cli 0.13.2 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +106 -0
- package/bin/prjct +10 -13
- package/core/agentic/memory-system/semantic-memories.ts +2 -1
- package/core/agentic/plan-mode/plan-mode.ts +2 -1
- package/core/agentic/prompt-builder.ts +22 -43
- package/core/agentic/services.ts +5 -5
- package/core/agentic/smart-context.ts +7 -2
- package/core/command-registry/core-commands.ts +54 -29
- package/core/command-registry/optional-commands.ts +64 -0
- package/core/command-registry/setup-commands.ts +18 -3
- package/core/commands/analysis.ts +21 -68
- package/core/commands/analytics.ts +247 -213
- package/core/commands/base.ts +1 -1
- package/core/commands/index.ts +41 -36
- package/core/commands/maintenance.ts +300 -31
- package/core/commands/planning.ts +233 -22
- package/core/commands/setup.ts +3 -8
- package/core/commands/shipping.ts +14 -18
- package/core/commands/types.ts +8 -6
- package/core/commands/workflow.ts +105 -100
- package/core/context/generator.ts +317 -0
- package/core/context-sync.ts +7 -350
- package/core/data/index.ts +13 -32
- package/core/data/md-ideas-manager.ts +155 -0
- package/core/data/md-queue-manager.ts +4 -3
- package/core/data/md-shipped-manager.ts +90 -0
- package/core/data/md-state-manager.ts +11 -7
- package/core/domain/agent-generator.ts +23 -63
- package/core/events/index.ts +143 -0
- package/core/index.ts +17 -14
- package/core/infrastructure/capability-installer.ts +13 -149
- package/core/infrastructure/migrator/project-scanner.ts +2 -1
- package/core/infrastructure/path-manager.ts +4 -6
- package/core/infrastructure/setup.ts +3 -0
- package/core/infrastructure/uuid-migration.ts +750 -0
- package/core/outcomes/recorder.ts +2 -1
- package/core/plugin/loader.ts +4 -7
- package/core/plugin/registry.ts +3 -3
- package/core/schemas/index.ts +23 -25
- package/core/schemas/state.ts +1 -0
- package/core/serializers/ideas-serializer.ts +187 -0
- package/core/serializers/index.ts +16 -0
- package/core/serializers/shipped-serializer.ts +108 -0
- package/core/session/utils.ts +3 -9
- package/core/storage/ideas-storage.ts +273 -0
- package/core/storage/index.ts +204 -0
- package/core/storage/queue-storage.ts +297 -0
- package/core/storage/shipped-storage.ts +223 -0
- package/core/storage/state-storage.ts +235 -0
- package/core/storage/storage-manager.ts +175 -0
- package/package.json +1 -1
- package/packages/web/app/api/projects/[id]/momentum/route.ts +257 -0
- package/packages/web/app/api/sessions/current/route.ts +132 -0
- package/packages/web/app/api/sessions/history/route.ts +96 -14
- package/packages/web/app/globals.css +5 -0
- package/packages/web/app/layout.tsx +2 -0
- package/packages/web/app/project/[id]/code/layout.tsx +18 -0
- package/packages/web/app/project/[id]/code/page.tsx +408 -0
- package/packages/web/app/project/[id]/page.tsx +359 -389
- package/packages/web/app/project/[id]/reports/page.tsx +59 -0
- package/packages/web/app/project/[id]/reports/print/page.tsx +58 -0
- package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -1
- package/packages/web/components/AgentsCard/AgentsCard.tsx +64 -34
- package/packages/web/components/AgentsCard/AgentsCard.types.ts +1 -0
- package/packages/web/components/AppSidebar/AppSidebar.tsx +135 -11
- package/packages/web/components/BentoCard/BentoCard.constants.ts +3 -3
- package/packages/web/components/BentoCard/BentoCard.tsx +2 -1
- package/packages/web/components/BentoGrid/BentoGrid.tsx +2 -2
- package/packages/web/components/BlockersCard/BlockersCard.tsx +65 -57
- package/packages/web/components/BlockersCard/BlockersCard.types.ts +1 -0
- package/packages/web/components/CommandBar/CommandBar.tsx +67 -0
- package/packages/web/components/CommandBar/index.ts +1 -0
- package/packages/web/components/DashboardContent/DashboardContent.tsx +35 -5
- package/packages/web/components/DateGroup/DateGroup.tsx +1 -1
- package/packages/web/components/EmptyState/EmptyState.tsx +39 -21
- package/packages/web/components/EmptyState/EmptyState.types.ts +1 -0
- package/packages/web/components/EventRow/EventRow.tsx +4 -4
- package/packages/web/components/EventRow/EventRow.utils.ts +3 -3
- package/packages/web/components/HeroSection/HeroSection.tsx +52 -15
- package/packages/web/components/HeroSection/HeroSection.types.ts +4 -4
- package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -3
- package/packages/web/components/IdeasCard/IdeasCard.tsx +94 -27
- package/packages/web/components/IdeasCard/IdeasCard.types.ts +1 -0
- package/packages/web/components/MasonryGrid/MasonryGrid.tsx +18 -0
- package/packages/web/components/MasonryGrid/index.ts +1 -0
- package/packages/web/components/MomentumWidget/MomentumWidget.tsx +119 -0
- package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +16 -0
- package/packages/web/components/MomentumWidget/index.ts +2 -0
- package/packages/web/components/NowCard/NowCard.tsx +81 -56
- package/packages/web/components/NowCard/NowCard.types.ts +1 -0
- package/packages/web/components/PageHeader/PageHeader.tsx +24 -0
- package/packages/web/components/PageHeader/index.ts +1 -0
- package/packages/web/components/ProgressRing/ProgressRing.constants.ts +2 -2
- package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +2 -2
- package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +37 -0
- package/packages/web/components/ProjectColorDot/index.ts +1 -0
- package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +104 -0
- package/packages/web/components/ProjectSelectorModal/index.ts +1 -0
- package/packages/web/components/Providers/Providers.tsx +4 -1
- package/packages/web/components/QueueCard/QueueCard.tsx +78 -25
- package/packages/web/components/QueueCard/QueueCard.types.ts +1 -0
- package/packages/web/components/QueueCard/QueueCard.utils.ts +3 -3
- package/packages/web/components/RecoverCard/RecoverCard.tsx +72 -0
- package/packages/web/components/RecoverCard/RecoverCard.types.ts +16 -0
- package/packages/web/components/RecoverCard/index.ts +2 -0
- package/packages/web/components/RoadmapCard/RoadmapCard.tsx +101 -33
- package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +1 -0
- package/packages/web/components/ShipsCard/ShipsCard.tsx +71 -28
- package/packages/web/components/ShipsCard/ShipsCard.types.ts +2 -0
- package/packages/web/components/SparklineChart/SparklineChart.tsx +20 -18
- package/packages/web/components/StatsMasonry/StatsMasonry.tsx +95 -0
- package/packages/web/components/StatsMasonry/index.ts +1 -0
- package/packages/web/components/StreakCard/StreakCard.tsx +37 -35
- package/packages/web/components/TasksCounter/TasksCounter.tsx +1 -1
- package/packages/web/components/TechStackBadges/TechStackBadges.tsx +12 -4
- package/packages/web/components/TerminalDock/DockToggleTab.tsx +29 -0
- package/packages/web/components/TerminalDock/TerminalDock.tsx +386 -0
- package/packages/web/components/TerminalDock/TerminalDockTab.tsx +130 -0
- package/packages/web/components/TerminalDock/TerminalTabBar.tsx +142 -0
- package/packages/web/components/TerminalDock/index.ts +2 -0
- package/packages/web/components/VelocityBadge/VelocityBadge.tsx +8 -3
- package/packages/web/components/VelocityCard/VelocityCard.tsx +49 -47
- package/packages/web/components/WeeklyReports/PrintableReport.tsx +259 -0
- package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +187 -0
- package/packages/web/components/WeeklyReports/WeekCalendar.tsx +288 -0
- package/packages/web/components/WeeklyReports/WeeklyReports.tsx +149 -0
- package/packages/web/components/WeeklyReports/index.ts +4 -0
- package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +16 -4
- package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +1 -0
- package/packages/web/components/charts/SessionsChart.tsx +6 -3
- package/packages/web/components/ui/dialog.tsx +143 -0
- package/packages/web/components/ui/drawer.tsx +135 -0
- package/packages/web/components/ui/select.tsx +187 -0
- package/packages/web/context/GlobalTerminalContext.tsx +538 -0
- package/packages/web/lib/commands.ts +81 -0
- package/packages/web/lib/generate-week-report.ts +285 -0
- package/packages/web/lib/parse-prjct-files.ts +56 -55
- package/packages/web/lib/project-colors.ts +58 -0
- package/packages/web/lib/projects.ts +58 -5
- package/packages/web/lib/services/projects.server.ts +11 -1
- package/packages/web/next-env.d.ts +1 -1
- package/packages/web/package.json +5 -1
- package/templates/commands/analyze.md +39 -3
- package/templates/commands/ask.md +58 -3
- package/templates/commands/bug.md +117 -26
- package/templates/commands/dash.md +95 -158
- package/templates/commands/done.md +130 -148
- package/templates/commands/feature.md +125 -103
- package/templates/commands/git.md +18 -3
- package/templates/commands/idea.md +121 -38
- package/templates/commands/init.md +124 -20
- package/templates/commands/migrate-all.md +63 -28
- package/templates/commands/migrate.md +140 -0
- package/templates/commands/next.md +115 -5
- package/templates/commands/now.md +146 -82
- package/templates/commands/pause.md +89 -74
- package/templates/commands/redo.md +6 -4
- package/templates/commands/resume.md +141 -59
- package/templates/commands/ship.md +103 -231
- package/templates/commands/spec.md +98 -8
- package/templates/commands/suggest.md +22 -2
- package/templates/commands/sync.md +192 -203
- package/templates/commands/undo.md +6 -4
- package/core/data/agents-manager.ts +0 -76
- package/core/data/analysis-manager.ts +0 -83
- package/core/data/base-manager.ts +0 -156
- package/core/data/ideas-manager.ts +0 -81
- package/core/data/outcomes-manager.ts +0 -96
- package/core/data/project-manager.ts +0 -75
- package/core/data/roadmap-manager.ts +0 -118
- package/core/data/shipped-manager.ts +0 -65
- package/core/data/state-manager.ts +0 -214
- package/core/state/index.ts +0 -25
- package/core/state/manager.ts +0 -376
- package/core/state/types.ts +0 -185
- package/core/utils/project-capabilities.ts +0 -156
- package/core/view-generator.ts +0 -536
- package/packages/web/app/project/[id]/stats/loading.tsx +0 -43
- package/packages/web/app/project/[id]/stats/page.tsx +0 -253
- package/templates/agent-assignment.md +0 -72
- package/templates/analysis/project-analysis.md +0 -78
- package/templates/checklists/accessibility.md +0 -33
- package/templates/commands/build.md +0 -17
- package/templates/commands/decision.md +0 -226
- package/templates/commands/fix.md +0 -79
- package/templates/commands/help.md +0 -61
- package/templates/commands/progress.md +0 -14
- package/templates/commands/recap.md +0 -14
- package/templates/commands/roadmap.md +0 -52
- package/templates/commands/status.md +0 -17
- package/templates/commands/task.md +0 -63
- package/templates/commands/work.md +0 -44
- package/templates/commands/workflow.md +0 -12
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Planning Commands: init, feature, bug,
|
|
2
|
+
* Planning Commands: init, feature, bug, idea, spec
|
|
3
|
+
* Write-Through Architecture: JSON → MD → Event
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
import path from 'path'
|
|
6
7
|
|
|
7
8
|
import type { CommandResult, AnalyzeOptions, Context } from './types'
|
|
9
|
+
import { generateUUID } from '../schemas'
|
|
10
|
+
import type { Priority, TaskType, TaskSection } from '../schemas/state'
|
|
8
11
|
import {
|
|
9
12
|
PrjctCommandsBase,
|
|
10
13
|
contextBuilder,
|
|
@@ -15,7 +18,9 @@ import {
|
|
|
15
18
|
dateHelper,
|
|
16
19
|
out
|
|
17
20
|
} from './base'
|
|
21
|
+
import { queueStorage, ideasStorage } from '../storage'
|
|
18
22
|
import authorDetector from '../infrastructure/author-detector'
|
|
23
|
+
import commandInstaller from '../infrastructure/command-installer'
|
|
19
24
|
|
|
20
25
|
export class PlanningCommands extends PrjctCommandsBase {
|
|
21
26
|
/**
|
|
@@ -98,14 +103,12 @@ export class PlanningCommands extends PrjctCommandsBase {
|
|
|
98
103
|
const sessionContent = `# Architect Session\n\n## Idea\n${idea}\n\n## Status\nInitialized - awaiting stack recommendation\n\nGenerated: ${new Date().toLocaleString()}\n`
|
|
99
104
|
await toolRegistry.get('Write')!(sessionPath, sessionContent)
|
|
100
105
|
|
|
101
|
-
const commandInstaller = require('../infrastructure/command-installer')
|
|
102
106
|
await commandInstaller.installGlobalConfig()
|
|
103
107
|
|
|
104
108
|
out.done('architect mode ready')
|
|
105
109
|
return { success: true, mode: 'architect', projectId, idea }
|
|
106
110
|
}
|
|
107
111
|
|
|
108
|
-
const commandInstaller = require('../infrastructure/command-installer')
|
|
109
112
|
await commandInstaller.installGlobalConfig()
|
|
110
113
|
|
|
111
114
|
out.done('initialized')
|
|
@@ -129,10 +132,17 @@ export class PlanningCommands extends PrjctCommandsBase {
|
|
|
129
132
|
return { success: false, error: 'Description required' }
|
|
130
133
|
}
|
|
131
134
|
|
|
135
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
136
|
+
if (!projectId) {
|
|
137
|
+
out.fail('no project ID')
|
|
138
|
+
return { success: false, error: 'No project ID found' }
|
|
139
|
+
}
|
|
140
|
+
|
|
132
141
|
out.spin(`planning ${description}...`)
|
|
133
142
|
|
|
134
143
|
const context = await contextBuilder.build(projectPath, { description }) as Context
|
|
135
144
|
const tasks = this._breakdownFeatureTasks(description)
|
|
145
|
+
const featureId = generateUUID()
|
|
136
146
|
|
|
137
147
|
const tasksWithAgents: { task: string; agent: string }[] = []
|
|
138
148
|
for (const taskDesc of tasks) {
|
|
@@ -141,17 +151,20 @@ export class PlanningCommands extends PrjctCommandsBase {
|
|
|
141
151
|
tasksWithAgents.push({ task: taskDesc, agent })
|
|
142
152
|
}
|
|
143
153
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
// Write-through: Add tasks (JSON → MD → Event)
|
|
155
|
+
await queueStorage.addTasks(projectId, tasksWithAgents.map(t => ({
|
|
156
|
+
description: t.task,
|
|
157
|
+
priority: 'medium' as Priority,
|
|
158
|
+
type: 'feature' as TaskType,
|
|
159
|
+
section: 'active' as TaskSection,
|
|
160
|
+
featureId,
|
|
161
|
+
originFeature: description,
|
|
162
|
+
agent: t.agent
|
|
163
|
+
})))
|
|
152
164
|
|
|
153
165
|
await this.logToMemory(projectPath, 'feature_planned', {
|
|
154
166
|
feature: description,
|
|
167
|
+
featureId,
|
|
155
168
|
tasks: tasksWithAgents.length,
|
|
156
169
|
assignments: tasksWithAgents.map(t => ({ task: t.task, agent: t.agent })),
|
|
157
170
|
timestamp: dateHelper.getTimestamp(),
|
|
@@ -165,7 +178,7 @@ export class PlanningCommands extends PrjctCommandsBase {
|
|
|
165
178
|
|
|
166
179
|
out.done(`${tasks.length} tasks [${agentSummary}]`)
|
|
167
180
|
|
|
168
|
-
return { success: true, feature: description, tasks: tasksWithAgents }
|
|
181
|
+
return { success: true, feature: description, featureId, tasks: tasksWithAgents }
|
|
169
182
|
} catch (error) {
|
|
170
183
|
out.fail((error as Error).message)
|
|
171
184
|
return { success: false, error: (error as Error).message }
|
|
@@ -185,6 +198,12 @@ export class PlanningCommands extends PrjctCommandsBase {
|
|
|
185
198
|
return { success: false, error: 'Description required' }
|
|
186
199
|
}
|
|
187
200
|
|
|
201
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
202
|
+
if (!projectId) {
|
|
203
|
+
out.fail('no project ID')
|
|
204
|
+
return { success: false, error: 'No project ID found' }
|
|
205
|
+
}
|
|
206
|
+
|
|
188
207
|
out.spin('tracking bug...')
|
|
189
208
|
|
|
190
209
|
const context = await contextBuilder.build(projectPath, { description }) as Context
|
|
@@ -193,20 +212,28 @@ export class PlanningCommands extends PrjctCommandsBase {
|
|
|
193
212
|
const agentResult = await this._assignAgentForTask(`fix bug: ${description}`, projectPath, context)
|
|
194
213
|
const agent = agentResult.agent?.name || 'generalist'
|
|
195
214
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
215
|
+
// Map severity to Priority type
|
|
216
|
+
const priorityMap: Record<string, Priority> = {
|
|
217
|
+
'critical': 'critical',
|
|
218
|
+
'high': 'high',
|
|
219
|
+
'medium': 'medium',
|
|
220
|
+
'low': 'low'
|
|
221
|
+
}
|
|
222
|
+
const priority = priorityMap[severity] || 'medium'
|
|
223
|
+
|
|
224
|
+
// Write-through: Add bug task (JSON → MD → Event)
|
|
225
|
+
await queueStorage.addTask(projectId, {
|
|
226
|
+
description: `🐛 ${description}`,
|
|
227
|
+
priority,
|
|
228
|
+
type: 'bug' as TaskType,
|
|
229
|
+
section: 'active' as TaskSection,
|
|
230
|
+
agent
|
|
231
|
+
})
|
|
206
232
|
|
|
207
233
|
await this.logToMemory(projectPath, 'bug_reported', {
|
|
208
234
|
bug: description,
|
|
209
235
|
severity,
|
|
236
|
+
priority,
|
|
210
237
|
agent,
|
|
211
238
|
timestamp: dateHelper.getTimestamp(),
|
|
212
239
|
})
|
|
@@ -308,4 +335,188 @@ export class PlanningCommands extends PrjctCommandsBase {
|
|
|
308
335
|
return { success: false, error: (error as Error).message }
|
|
309
336
|
}
|
|
310
337
|
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* /p:idea - Transform ideas into architectures or quick captures
|
|
341
|
+
*/
|
|
342
|
+
async idea(description: string, projectPath: string = process.cwd()): Promise<CommandResult> {
|
|
343
|
+
try {
|
|
344
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
345
|
+
if (!initResult.success) return initResult
|
|
346
|
+
|
|
347
|
+
if (!description) {
|
|
348
|
+
out.fail('idea description required')
|
|
349
|
+
return { success: false, error: 'Idea description required' }
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
353
|
+
if (!projectId) {
|
|
354
|
+
out.fail('no project ID')
|
|
355
|
+
return { success: false, error: 'No project ID found' }
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Determine if simple or complex idea
|
|
359
|
+
const wordCount = description.split(/\s+/).length
|
|
360
|
+
const isComplex = wordCount > 20 || description.includes('with') || description.includes('that')
|
|
361
|
+
|
|
362
|
+
if (isComplex) {
|
|
363
|
+
// Complex idea → Create architecture session
|
|
364
|
+
out.spin('analyzing idea...')
|
|
365
|
+
|
|
366
|
+
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
367
|
+
const sessionPath = path.join(globalPath, 'planning', 'architect-session.md')
|
|
368
|
+
const sessionContent = `# Architect Session
|
|
369
|
+
|
|
370
|
+
## Idea
|
|
371
|
+
${description}
|
|
372
|
+
|
|
373
|
+
## Status
|
|
374
|
+
Initialized - awaiting architecture design
|
|
375
|
+
|
|
376
|
+
## Next Steps
|
|
377
|
+
1. Define tech stack
|
|
378
|
+
2. Create system design
|
|
379
|
+
3. Break down into features
|
|
380
|
+
4. Generate roadmap
|
|
381
|
+
|
|
382
|
+
Generated: ${new Date().toLocaleString()}
|
|
383
|
+
`
|
|
384
|
+
await toolRegistry.get('Write')!(sessionPath, sessionContent)
|
|
385
|
+
|
|
386
|
+
await this.logToMemory(projectPath, 'idea_architecture_started', {
|
|
387
|
+
idea: description,
|
|
388
|
+
timestamp: dateHelper.getTimestamp(),
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
out.done('architecture session created')
|
|
392
|
+
console.log('\n💡 Use /p:architect execute to continue planning\n')
|
|
393
|
+
|
|
394
|
+
return { success: true, mode: 'architecture', idea: description }
|
|
395
|
+
} else {
|
|
396
|
+
// Simple idea → Quick capture (JSON → MD → Event)
|
|
397
|
+
out.spin('capturing idea...')
|
|
398
|
+
|
|
399
|
+
await ideasStorage.addIdea(projectId, description)
|
|
400
|
+
|
|
401
|
+
await this.logToMemory(projectPath, 'idea_captured', {
|
|
402
|
+
idea: description,
|
|
403
|
+
timestamp: dateHelper.getTimestamp(),
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
out.done(`idea captured: ${description.slice(0, 40)}`)
|
|
407
|
+
|
|
408
|
+
return { success: true, mode: 'capture', idea: description }
|
|
409
|
+
}
|
|
410
|
+
} catch (error) {
|
|
411
|
+
out.fail((error as Error).message)
|
|
412
|
+
return { success: false, error: (error as Error).message }
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* /p:spec - Create detailed specifications for complex features
|
|
418
|
+
*/
|
|
419
|
+
async spec(featureName: string | null = null, projectPath: string = process.cwd()): Promise<CommandResult> {
|
|
420
|
+
try {
|
|
421
|
+
const initResult = await this.ensureProjectInit(projectPath)
|
|
422
|
+
if (!initResult.success) return initResult
|
|
423
|
+
|
|
424
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
425
|
+
if (!projectId) {
|
|
426
|
+
out.fail('no project ID')
|
|
427
|
+
return { success: false, error: 'No project ID found' }
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (!featureName) {
|
|
431
|
+
// List existing specs
|
|
432
|
+
out.spin('loading specs...')
|
|
433
|
+
|
|
434
|
+
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
435
|
+
const specsPath = path.join(globalPath, 'planning', 'specs')
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
const fs = await import('fs/promises')
|
|
439
|
+
const files = await fs.readdir(specsPath)
|
|
440
|
+
const specs = files.filter(f => f.endsWith('.md') && f !== '.gitkeep')
|
|
441
|
+
|
|
442
|
+
if (specs.length === 0) {
|
|
443
|
+
out.warn('no specs yet')
|
|
444
|
+
console.log('\n💡 Create one with /p:spec "feature name"\n')
|
|
445
|
+
return { success: true, specs: [] }
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
console.log('\n📋 SPECIFICATIONS\n')
|
|
449
|
+
console.log('═'.repeat(50))
|
|
450
|
+
specs.forEach((s, i) => {
|
|
451
|
+
const name = s.replace('.md', '').replace(/-/g, ' ')
|
|
452
|
+
console.log(` ${i + 1}. ${name}`)
|
|
453
|
+
})
|
|
454
|
+
console.log('═'.repeat(50) + '\n')
|
|
455
|
+
|
|
456
|
+
return { success: true, specs }
|
|
457
|
+
} catch {
|
|
458
|
+
out.warn('no specs directory')
|
|
459
|
+
return { success: true, specs: [] }
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Create new spec
|
|
464
|
+
out.spin('creating spec...')
|
|
465
|
+
|
|
466
|
+
const globalPath = pathManager.getGlobalProjectPath(projectId)
|
|
467
|
+
const specsPath = path.join(globalPath, 'planning', 'specs')
|
|
468
|
+
await fileHelper.ensureDir(specsPath)
|
|
469
|
+
|
|
470
|
+
const slug = featureName.toLowerCase().replace(/\s+/g, '-')
|
|
471
|
+
const specFile = path.join(specsPath, `${slug}.md`)
|
|
472
|
+
|
|
473
|
+
const specContent = `# Specification: ${featureName}
|
|
474
|
+
|
|
475
|
+
## Overview
|
|
476
|
+
[Brief description of the feature]
|
|
477
|
+
|
|
478
|
+
## Requirements
|
|
479
|
+
- [ ] Requirement 1
|
|
480
|
+
- [ ] Requirement 2
|
|
481
|
+
- [ ] Requirement 3
|
|
482
|
+
|
|
483
|
+
## Design Decisions
|
|
484
|
+
| Decision | Rationale |
|
|
485
|
+
|----------|-----------|
|
|
486
|
+
| | |
|
|
487
|
+
|
|
488
|
+
## Tasks (20-30 min each)
|
|
489
|
+
1. [ ] Task 1 - [description]
|
|
490
|
+
2. [ ] Task 2 - [description]
|
|
491
|
+
3. [ ] Task 3 - [description]
|
|
492
|
+
|
|
493
|
+
## Acceptance Criteria
|
|
494
|
+
- [ ] Criterion 1
|
|
495
|
+
- [ ] Criterion 2
|
|
496
|
+
|
|
497
|
+
## Notes
|
|
498
|
+
[Additional notes and considerations]
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
Created: ${new Date().toLocaleString()}
|
|
502
|
+
Status: Draft
|
|
503
|
+
`
|
|
504
|
+
|
|
505
|
+
await toolRegistry.get('Write')!(specFile, specContent)
|
|
506
|
+
|
|
507
|
+
await this.logToMemory(projectPath, 'spec_created', {
|
|
508
|
+
feature: featureName,
|
|
509
|
+
timestamp: dateHelper.getTimestamp(),
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
out.done(`spec created: ${slug}.md`)
|
|
513
|
+
console.log(`\n📝 Edit: ~/.prjct-cli/projects/${projectId}/planning/specs/${slug}.md`)
|
|
514
|
+
console.log('💡 When ready, use /p:feature to add tasks to queue\n')
|
|
515
|
+
|
|
516
|
+
return { success: true, feature: featureName, specPath: specFile }
|
|
517
|
+
} catch (error) {
|
|
518
|
+
out.fail((error as Error).message)
|
|
519
|
+
return { success: false, error: (error as Error).message }
|
|
520
|
+
}
|
|
521
|
+
}
|
|
311
522
|
}
|
package/core/commands/setup.ts
CHANGED
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import path from 'path'
|
|
6
6
|
import fs from 'fs'
|
|
7
|
+
import { promises as fsPromises } from 'fs'
|
|
7
8
|
import os from 'os'
|
|
9
|
+
import chalk from 'chalk'
|
|
8
10
|
|
|
9
11
|
import migrator from '../infrastructure/migrator'
|
|
12
|
+
import commandInstaller from '../infrastructure/command-installer'
|
|
10
13
|
import type { CommandResult, SetupOptions, MigrateOptions, GlobalConfig, MigrationResult } from './types'
|
|
11
14
|
import {
|
|
12
15
|
PrjctCommandsBase,
|
|
@@ -20,8 +23,6 @@ export class SetupCommands extends PrjctCommandsBase {
|
|
|
20
23
|
* First-time setup - Install commands to editors
|
|
21
24
|
*/
|
|
22
25
|
async start(): Promise<CommandResult> {
|
|
23
|
-
const commandInstaller = require('../infrastructure/command-installer')
|
|
24
|
-
|
|
25
26
|
console.log('🚀 Setting up prjct for Claude...\n')
|
|
26
27
|
|
|
27
28
|
const status = await commandInstaller.checkInstallation()
|
|
@@ -69,8 +70,6 @@ export class SetupCommands extends PrjctCommandsBase {
|
|
|
69
70
|
* Reconfigure editor installations
|
|
70
71
|
*/
|
|
71
72
|
async setup(options: SetupOptions = {}): Promise<CommandResult> {
|
|
72
|
-
const commandInstaller = require('../infrastructure/command-installer')
|
|
73
|
-
|
|
74
73
|
console.log('🔧 Reconfiguring prjct...\n')
|
|
75
74
|
|
|
76
75
|
if (options.force) {
|
|
@@ -188,8 +187,6 @@ fi
|
|
|
188
187
|
* Show beautiful ASCII art with quick start
|
|
189
188
|
*/
|
|
190
189
|
showAsciiArt(): void {
|
|
191
|
-
const chalk = require('chalk')
|
|
192
|
-
|
|
193
190
|
console.log(chalk.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'))
|
|
194
191
|
console.log('')
|
|
195
192
|
console.log(chalk.bold.cyan(' ██████╗ ██████╗ ██╗ ██████╗████████╗'))
|
|
@@ -232,8 +229,6 @@ fi
|
|
|
232
229
|
* Migrate all legacy projects
|
|
233
230
|
*/
|
|
234
231
|
async migrateAll(options: MigrateOptions = {}): Promise<CommandResult> {
|
|
235
|
-
const fsPromises = require('fs').promises
|
|
236
|
-
|
|
237
232
|
console.log('🔄 Scanning for legacy prjct projects...\n')
|
|
238
233
|
|
|
239
234
|
const homeDir = os.homedir()
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shipping Commands: ship and related helpers
|
|
3
|
+
* Write-Through Architecture: JSON → MD → Event
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
import path from 'path'
|
|
6
7
|
|
|
7
8
|
import memorySystem from '../agentic/memory-system'
|
|
8
|
-
import type { CommandResult
|
|
9
|
+
import type { CommandResult } from './types'
|
|
9
10
|
import {
|
|
10
11
|
PrjctCommandsBase,
|
|
11
|
-
contextBuilder,
|
|
12
12
|
toolRegistry,
|
|
13
13
|
configManager,
|
|
14
14
|
fileHelper,
|
|
15
15
|
dateHelper,
|
|
16
16
|
out
|
|
17
17
|
} from './base'
|
|
18
|
+
import { stateStorage, shippedStorage } from '../storage'
|
|
18
19
|
|
|
19
20
|
export class ShippingCommands extends PrjctCommandsBase {
|
|
20
21
|
/**
|
|
@@ -25,16 +26,14 @@ export class ShippingCommands extends PrjctCommandsBase {
|
|
|
25
26
|
const initResult = await this.ensureProjectInit(projectPath)
|
|
26
27
|
if (!initResult.success) return initResult
|
|
27
28
|
|
|
29
|
+
const config = await configManager.readConfig(projectPath)
|
|
30
|
+
const projectId = config!.projectId
|
|
31
|
+
|
|
28
32
|
let featureName = feature
|
|
29
33
|
if (!featureName) {
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
const match = nowContent.match(/\*\*(.+?)\*\*/)
|
|
34
|
-
featureName = match ? match[1] : 'current work'
|
|
35
|
-
} else {
|
|
36
|
-
featureName = 'current work'
|
|
37
|
-
}
|
|
34
|
+
// Read from storage (JSON is source of truth)
|
|
35
|
+
const currentTask = await stateStorage.getCurrentTask(projectId)
|
|
36
|
+
featureName = currentTask?.description || 'current work'
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
out.spin(`shipping ${featureName}...`)
|
|
@@ -56,11 +55,11 @@ export class ShippingCommands extends PrjctCommandsBase {
|
|
|
56
55
|
await this._gitPush(projectPath)
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
// Write-through: Record shipped feature (JSON → MD → Event)
|
|
59
|
+
await shippedStorage.addShipped(projectId, {
|
|
60
|
+
name: featureName,
|
|
61
|
+
version: newVersion
|
|
62
|
+
})
|
|
64
63
|
|
|
65
64
|
await this.logToMemory(projectPath, 'feature_shipped', {
|
|
66
65
|
feature: featureName,
|
|
@@ -68,9 +67,6 @@ export class ShippingCommands extends PrjctCommandsBase {
|
|
|
68
67
|
timestamp: dateHelper.getTimestamp(),
|
|
69
68
|
})
|
|
70
69
|
|
|
71
|
-
const config = await configManager.readConfig(projectPath)
|
|
72
|
-
const projectId = config!.projectId
|
|
73
|
-
|
|
74
70
|
await memorySystem.learnDecision(projectId, 'commit_footer', 'prjct', 'ship')
|
|
75
71
|
|
|
76
72
|
if (testResult.success) {
|
package/core/commands/types.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface CommandResult {
|
|
|
28
28
|
feature?: string
|
|
29
29
|
/** Files that were modified */
|
|
30
30
|
filesModified?: string[]
|
|
31
|
+
/** Allow any additional properties for command-specific data */
|
|
32
|
+
[key: string]: unknown
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
// Agent types
|
|
@@ -68,8 +70,8 @@ export interface HealthResult {
|
|
|
68
70
|
* Options for the design command.
|
|
69
71
|
*/
|
|
70
72
|
export interface DesignOptions {
|
|
71
|
-
/** Type of design
|
|
72
|
-
type?: '
|
|
73
|
+
/** Type of design */
|
|
74
|
+
type?: 'architecture' | 'api' | 'component' | 'database' | 'flow'
|
|
73
75
|
/** Output format */
|
|
74
76
|
format?: 'markdown' | 'mermaid'
|
|
75
77
|
}
|
|
@@ -151,11 +153,11 @@ export interface MigrationConfig {
|
|
|
151
153
|
|
|
152
154
|
// Type-safe command method names (for dynamic invocation)
|
|
153
155
|
export type CommandMethodName =
|
|
154
|
-
| 'now' | 'done' | 'next' | '
|
|
155
|
-
| 'init' | 'feature' | 'bug' | '
|
|
156
|
+
| 'work' | 'now' | 'done' | 'next' | 'pause' | 'resume'
|
|
157
|
+
| 'init' | 'feature' | 'bug' | 'idea' | 'spec'
|
|
156
158
|
| 'ship'
|
|
157
|
-
| '
|
|
158
|
-
| 'cleanup' | 'design'
|
|
159
|
+
| 'dash' | 'help'
|
|
160
|
+
| 'cleanup' | 'design' | 'recover' | 'undo' | 'redo' | 'history'
|
|
159
161
|
| 'analyze' | 'sync'
|
|
160
162
|
| 'start' | 'setup' | 'migrateAll'
|
|
161
163
|
|