prjct-cli 0.13.3 → 0.15.1

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 (195) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/bin/prjct +10 -13
  3. package/core/agentic/memory-system/semantic-memories.ts +2 -1
  4. package/core/agentic/plan-mode/plan-mode.ts +2 -1
  5. package/core/agentic/prompt-builder.ts +22 -43
  6. package/core/agentic/services.ts +5 -5
  7. package/core/agentic/smart-context.ts +7 -2
  8. package/core/command-registry/core-commands.ts +54 -29
  9. package/core/command-registry/optional-commands.ts +64 -0
  10. package/core/command-registry/setup-commands.ts +18 -3
  11. package/core/commands/analysis.ts +21 -68
  12. package/core/commands/analytics.ts +247 -213
  13. package/core/commands/base.ts +1 -1
  14. package/core/commands/index.ts +41 -36
  15. package/core/commands/maintenance.ts +300 -31
  16. package/core/commands/planning.ts +233 -22
  17. package/core/commands/setup.ts +3 -8
  18. package/core/commands/shipping.ts +14 -18
  19. package/core/commands/types.ts +8 -6
  20. package/core/commands/workflow.ts +105 -100
  21. package/core/context/generator.ts +317 -0
  22. package/core/context-sync.ts +7 -350
  23. package/core/data/index.ts +13 -32
  24. package/core/data/md-ideas-manager.ts +155 -0
  25. package/core/data/md-queue-manager.ts +4 -3
  26. package/core/data/md-shipped-manager.ts +90 -0
  27. package/core/data/md-state-manager.ts +11 -7
  28. package/core/domain/agent-generator.ts +23 -63
  29. package/core/events/index.ts +143 -0
  30. package/core/index.ts +17 -14
  31. package/core/infrastructure/capability-installer.ts +13 -149
  32. package/core/infrastructure/migrator/project-scanner.ts +2 -1
  33. package/core/infrastructure/path-manager.ts +4 -6
  34. package/core/infrastructure/setup.ts +3 -0
  35. package/core/infrastructure/uuid-migration.ts +750 -0
  36. package/core/outcomes/recorder.ts +2 -1
  37. package/core/plugin/loader.ts +4 -7
  38. package/core/plugin/registry.ts +3 -3
  39. package/core/schemas/index.ts +23 -25
  40. package/core/schemas/state.ts +1 -0
  41. package/core/serializers/ideas-serializer.ts +187 -0
  42. package/core/serializers/index.ts +16 -0
  43. package/core/serializers/shipped-serializer.ts +108 -0
  44. package/core/session/utils.ts +3 -9
  45. package/core/storage/ideas-storage.ts +273 -0
  46. package/core/storage/index.ts +204 -0
  47. package/core/storage/queue-storage.ts +297 -0
  48. package/core/storage/shipped-storage.ts +223 -0
  49. package/core/storage/state-storage.ts +235 -0
  50. package/core/storage/storage-manager.ts +175 -0
  51. package/package.json +1 -1
  52. package/packages/web/app/api/projects/[id]/momentum/route.ts +257 -0
  53. package/packages/web/app/api/sessions/current/route.ts +132 -0
  54. package/packages/web/app/api/sessions/history/route.ts +96 -14
  55. package/packages/web/app/globals.css +5 -0
  56. package/packages/web/app/layout.tsx +2 -0
  57. package/packages/web/app/project/[id]/code/layout.tsx +18 -0
  58. package/packages/web/app/project/[id]/code/page.tsx +408 -0
  59. package/packages/web/app/project/[id]/page.tsx +359 -389
  60. package/packages/web/app/project/[id]/reports/page.tsx +59 -0
  61. package/packages/web/app/project/[id]/reports/print/page.tsx +58 -0
  62. package/packages/web/components/ActivityTimeline/ActivityTimeline.tsx +0 -1
  63. package/packages/web/components/AgentsCard/AgentsCard.tsx +64 -34
  64. package/packages/web/components/AgentsCard/AgentsCard.types.ts +1 -0
  65. package/packages/web/components/AppSidebar/AppSidebar.tsx +135 -11
  66. package/packages/web/components/BentoCard/BentoCard.constants.ts +3 -3
  67. package/packages/web/components/BentoCard/BentoCard.tsx +2 -1
  68. package/packages/web/components/BentoGrid/BentoGrid.tsx +2 -2
  69. package/packages/web/components/BlockersCard/BlockersCard.tsx +65 -57
  70. package/packages/web/components/BlockersCard/BlockersCard.types.ts +1 -0
  71. package/packages/web/components/CommandBar/CommandBar.tsx +67 -0
  72. package/packages/web/components/CommandBar/index.ts +1 -0
  73. package/packages/web/components/DashboardContent/DashboardContent.tsx +35 -5
  74. package/packages/web/components/DateGroup/DateGroup.tsx +1 -1
  75. package/packages/web/components/EmptyState/EmptyState.tsx +39 -21
  76. package/packages/web/components/EmptyState/EmptyState.types.ts +1 -0
  77. package/packages/web/components/EventRow/EventRow.tsx +4 -4
  78. package/packages/web/components/EventRow/EventRow.utils.ts +3 -3
  79. package/packages/web/components/HeroSection/HeroSection.tsx +52 -15
  80. package/packages/web/components/HeroSection/HeroSection.types.ts +4 -4
  81. package/packages/web/components/HeroSection/HeroSection.utils.ts +7 -3
  82. package/packages/web/components/IdeasCard/IdeasCard.tsx +94 -27
  83. package/packages/web/components/IdeasCard/IdeasCard.types.ts +1 -0
  84. package/packages/web/components/MasonryGrid/MasonryGrid.tsx +18 -0
  85. package/packages/web/components/MasonryGrid/index.ts +1 -0
  86. package/packages/web/components/MomentumWidget/MomentumWidget.tsx +119 -0
  87. package/packages/web/components/MomentumWidget/MomentumWidget.types.ts +16 -0
  88. package/packages/web/components/MomentumWidget/index.ts +2 -0
  89. package/packages/web/components/NowCard/NowCard.tsx +81 -56
  90. package/packages/web/components/NowCard/NowCard.types.ts +1 -0
  91. package/packages/web/components/PageHeader/PageHeader.tsx +24 -0
  92. package/packages/web/components/PageHeader/index.ts +1 -0
  93. package/packages/web/components/ProgressRing/ProgressRing.constants.ts +2 -2
  94. package/packages/web/components/ProjectAvatar/ProjectAvatar.tsx +2 -2
  95. package/packages/web/components/ProjectColorDot/ProjectColorDot.tsx +37 -0
  96. package/packages/web/components/ProjectColorDot/index.ts +1 -0
  97. package/packages/web/components/ProjectSelectorModal/ProjectSelectorModal.tsx +104 -0
  98. package/packages/web/components/ProjectSelectorModal/index.ts +1 -0
  99. package/packages/web/components/Providers/Providers.tsx +4 -1
  100. package/packages/web/components/QueueCard/QueueCard.tsx +78 -25
  101. package/packages/web/components/QueueCard/QueueCard.types.ts +1 -0
  102. package/packages/web/components/QueueCard/QueueCard.utils.ts +3 -3
  103. package/packages/web/components/RecoverCard/RecoverCard.tsx +72 -0
  104. package/packages/web/components/RecoverCard/RecoverCard.types.ts +16 -0
  105. package/packages/web/components/RecoverCard/index.ts +2 -0
  106. package/packages/web/components/RoadmapCard/RoadmapCard.tsx +101 -33
  107. package/packages/web/components/RoadmapCard/RoadmapCard.types.ts +1 -0
  108. package/packages/web/components/ShipsCard/ShipsCard.tsx +71 -28
  109. package/packages/web/components/ShipsCard/ShipsCard.types.ts +2 -0
  110. package/packages/web/components/SparklineChart/SparklineChart.tsx +20 -18
  111. package/packages/web/components/StatsMasonry/StatsMasonry.tsx +95 -0
  112. package/packages/web/components/StatsMasonry/index.ts +1 -0
  113. package/packages/web/components/StreakCard/StreakCard.tsx +37 -35
  114. package/packages/web/components/TasksCounter/TasksCounter.tsx +1 -1
  115. package/packages/web/components/TechStackBadges/TechStackBadges.tsx +12 -4
  116. package/packages/web/components/TerminalDock/DockToggleTab.tsx +29 -0
  117. package/packages/web/components/TerminalDock/TerminalDock.tsx +386 -0
  118. package/packages/web/components/TerminalDock/TerminalDockTab.tsx +130 -0
  119. package/packages/web/components/TerminalDock/TerminalTabBar.tsx +142 -0
  120. package/packages/web/components/TerminalDock/index.ts +2 -0
  121. package/packages/web/components/VelocityBadge/VelocityBadge.tsx +8 -3
  122. package/packages/web/components/VelocityCard/VelocityCard.tsx +49 -47
  123. package/packages/web/components/WeeklyReports/PrintableReport.tsx +259 -0
  124. package/packages/web/components/WeeklyReports/ReportPreviewCard.tsx +187 -0
  125. package/packages/web/components/WeeklyReports/WeekCalendar.tsx +288 -0
  126. package/packages/web/components/WeeklyReports/WeeklyReports.tsx +149 -0
  127. package/packages/web/components/WeeklyReports/index.ts +4 -0
  128. package/packages/web/components/WeeklySparkline/WeeklySparkline.tsx +16 -4
  129. package/packages/web/components/WeeklySparkline/WeeklySparkline.types.ts +1 -0
  130. package/packages/web/components/charts/SessionsChart.tsx +6 -3
  131. package/packages/web/components/ui/dialog.tsx +143 -0
  132. package/packages/web/components/ui/drawer.tsx +135 -0
  133. package/packages/web/components/ui/select.tsx +187 -0
  134. package/packages/web/context/GlobalTerminalContext.tsx +538 -0
  135. package/packages/web/lib/commands.ts +81 -0
  136. package/packages/web/lib/generate-week-report.ts +285 -0
  137. package/packages/web/lib/parse-prjct-files.ts +56 -55
  138. package/packages/web/lib/project-colors.ts +58 -0
  139. package/packages/web/lib/projects.ts +58 -5
  140. package/packages/web/lib/services/projects.server.ts +11 -1
  141. package/packages/web/next-env.d.ts +1 -1
  142. package/packages/web/package.json +5 -1
  143. package/templates/commands/analyze.md +39 -3
  144. package/templates/commands/ask.md +58 -3
  145. package/templates/commands/bug.md +117 -26
  146. package/templates/commands/dash.md +95 -158
  147. package/templates/commands/done.md +130 -148
  148. package/templates/commands/feature.md +125 -103
  149. package/templates/commands/git.md +18 -3
  150. package/templates/commands/idea.md +121 -38
  151. package/templates/commands/init.md +124 -20
  152. package/templates/commands/migrate-all.md +63 -28
  153. package/templates/commands/migrate.md +140 -0
  154. package/templates/commands/next.md +115 -5
  155. package/templates/commands/now.md +146 -82
  156. package/templates/commands/pause.md +89 -74
  157. package/templates/commands/redo.md +6 -4
  158. package/templates/commands/resume.md +141 -59
  159. package/templates/commands/setup.md +18 -3
  160. package/templates/commands/ship.md +103 -231
  161. package/templates/commands/spec.md +98 -8
  162. package/templates/commands/suggest.md +22 -2
  163. package/templates/commands/sync.md +192 -203
  164. package/templates/commands/undo.md +6 -4
  165. package/templates/mcp-config.json +20 -1
  166. package/core/data/agents-manager.ts +0 -76
  167. package/core/data/analysis-manager.ts +0 -83
  168. package/core/data/base-manager.ts +0 -156
  169. package/core/data/ideas-manager.ts +0 -81
  170. package/core/data/outcomes-manager.ts +0 -96
  171. package/core/data/project-manager.ts +0 -75
  172. package/core/data/roadmap-manager.ts +0 -118
  173. package/core/data/shipped-manager.ts +0 -65
  174. package/core/data/state-manager.ts +0 -214
  175. package/core/state/index.ts +0 -25
  176. package/core/state/manager.ts +0 -376
  177. package/core/state/types.ts +0 -185
  178. package/core/utils/project-capabilities.ts +0 -156
  179. package/core/view-generator.ts +0 -536
  180. package/packages/web/app/project/[id]/stats/loading.tsx +0 -43
  181. package/packages/web/app/project/[id]/stats/page.tsx +0 -253
  182. package/templates/agent-assignment.md +0 -72
  183. package/templates/analysis/project-analysis.md +0 -78
  184. package/templates/checklists/accessibility.md +0 -33
  185. package/templates/commands/build.md +0 -17
  186. package/templates/commands/decision.md +0 -226
  187. package/templates/commands/fix.md +0 -79
  188. package/templates/commands/help.md +0 -61
  189. package/templates/commands/progress.md +0 -14
  190. package/templates/commands/recap.md +0 -14
  191. package/templates/commands/roadmap.md +0 -52
  192. package/templates/commands/status.md +0 -17
  193. package/templates/commands/task.md +0 -63
  194. package/templates/commands/work.md +0 -44
  195. package/templates/commands/workflow.md +0 -12
@@ -1,360 +1,17 @@
1
1
  /**
2
- * Context Sync - Generates RICH dynamic project context for Claude
2
+ * Context Sync
3
3
  *
4
- * Creates ~/.prjct-cli/projects/{id}/CLAUDE.md with:
5
- * - Full stack info (languages, frameworks, dependencies)
6
- * - Project structure and stats
7
- * - Agent expertise summaries
8
- * - Current task and priority queue
9
- * - Active features from roadmap
10
- *
11
- * GOAL: Give Claude ALL the context it needs WITHOUT extra file reads
12
- *
13
- * Called by: /p:sync, /p:analyze, /p:init
14
- */
15
-
16
- import fs from 'fs/promises'
17
- import path from 'path'
18
- import os from 'os'
19
-
20
- interface StackInfo {
21
- primary: string | null
22
- full: string | null
23
- languages: string[]
24
- frameworks: string[]
25
- dependencies: string[]
26
- }
27
-
28
- interface ProjectStructure {
29
- fileCount: number | null
30
- directories: string[]
31
- }
32
-
33
- interface GitCommit {
34
- hash: string
35
- message: string
36
- }
37
-
38
- interface GitStats {
39
- commits: number | null
40
- contributors: number | null
41
- recentCommits: GitCommit[]
42
- }
43
-
44
- interface AgentExpertise {
45
- name: string
46
- role: string
47
- expertise: string
48
- }
49
-
50
- interface ContextResult {
51
- agents: string[]
52
- stack: StackInfo
53
- currentTask: string | null
54
- }
55
-
56
- /**
57
- * Generate RICH project context file for Claude
58
- * Embeds key information so Claude doesn't need to read multiple files
59
- */
60
- async function generateLocalContext(projectPath: string, projectId: string): Promise<ContextResult> {
61
- const globalPath = path.join(os.homedir(), '.prjct-cli/projects', projectId)
62
- const projectName = path.basename(projectPath)
63
-
64
- // Helper to read files safely
65
- const readSafe = async (p: string): Promise<string | null> => {
66
- try {
67
- return await fs.readFile(p, 'utf-8')
68
- } catch {
69
- return null
70
- }
71
- }
72
-
73
- // 1. Read ALL data sources in parallel
74
- const agentsDir = path.join(globalPath, 'agents')
75
- let agentFiles: string[] = []
76
- try {
77
- agentFiles = await fs.readdir(agentsDir)
78
- agentFiles = agentFiles.filter(f => f.endsWith('.md'))
79
- } catch {
80
- // No agents directory yet
81
- }
82
-
83
- const [repoSummary, now, next, roadmap, ideas] = await Promise.all([
84
- readSafe(path.join(globalPath, 'analysis/repo-summary.md')),
85
- readSafe(path.join(globalPath, 'core/now.md')),
86
- readSafe(path.join(globalPath, 'core/next.md')),
87
- readSafe(path.join(globalPath, 'planning/roadmap.md')),
88
- readSafe(path.join(globalPath, 'planning/ideas.md'))
89
- ])
90
-
91
- // 2. Extract RICH stack info from repo-summary
92
- const stackInfo = extractFullStack(repoSummary)
93
-
94
- // 3. Extract project structure from repo-summary
95
- const projectStructure = extractProjectStructure(repoSummary)
96
-
97
- // 4. Extract git stats
98
- const gitStats = extractGitStats(repoSummary)
99
-
100
- // 5. Read agent expertise (just the key parts, not full templates)
101
- const agentExpertise = await extractAgentExpertise(agentsDir, agentFiles)
102
-
103
- // 6. Extract current task
104
- const currentTask = extractCurrentTask(now)
105
-
106
- // 7. Extract priority queue (top 5)
107
- const nextTasks = extractTopTasks(next, 5)
108
-
109
- // 8. Extract active features from roadmap
110
- const activeFeatures = extractActiveFeatures(roadmap)
111
-
112
- // 9. Extract recent ideas
113
- const recentIdeas = extractRecentIdeas(ideas)
114
-
115
- // 10. Generate RICH CLAUDE.md content
116
- const content = `# ${projectName} - Project Context
117
- <!-- Auto-generated by /p:sync - DO NOT EDIT MANUALLY -->
118
- <!-- projectId: ${projectId} -->
119
- <!-- Last sync: ${new Date().toISOString()} -->
120
-
121
- ## Quick Reference
122
-
123
- | Attribute | Value |
124
- |-----------|-------|
125
- | **Project** | ${projectName} |
126
- | **Stack** | ${stackInfo.primary || 'Not detected'} |
127
- | **Files** | ${projectStructure.fileCount || '?'} |
128
- | **Commits** | ${gitStats.commits || '?'} |
129
- | **Contributors** | ${gitStats.contributors || '?'} |
130
-
131
- ## Tech Stack
132
-
133
- ${stackInfo.full || 'Run /p:analyze to detect stack'}
134
-
135
- ## Project Structure
136
-
137
- ${projectStructure.directories?.length ? projectStructure.directories.map(d => `- \`${d}/\``).join('\n') : 'Run /p:analyze to detect structure'}
138
-
139
- ## Available Agents
140
-
141
- ${agentExpertise.length ? agentExpertise.map(a => `### ${a.name}
142
- - **Role**: ${a.role}
143
- - **Expertise**: ${a.expertise}
144
- `).join('\n') : 'None. Run /p:sync to generate agents.'}
145
-
146
- ## Current Focus
147
-
148
- ${currentTask ? `**Active Task**: ${currentTask}` : 'No active task. Use /p:now to start.'}
149
-
150
- ## Priority Queue
151
-
152
- ${nextTasks.length ? nextTasks.map((t, i) => `${i + 1}. ${t}`).join('\n') : 'Empty queue. Use /p:next to add tasks.'}
153
-
154
- ## Active Features (Roadmap)
155
-
156
- ${activeFeatures.length ? activeFeatures.map(f => `- [ ] ${f}`).join('\n') : 'No features in progress.'}
157
-
158
- ## Recent Ideas
159
-
160
- ${recentIdeas.length ? recentIdeas.map(i => `- ${i}`).join('\n') : 'No recent ideas.'}
161
-
162
- ## Git Activity
163
-
164
- ${gitStats.recentCommits?.length ? gitStats.recentCommits.map(c => `- \`${c.hash}\` ${c.message}`).join('\n') : 'No recent commits.'}
165
-
166
- ---
167
-
168
- ## Deep Dive (Read When Needed)
169
-
170
- For detailed information, read these files:
171
- - \`~/.prjct-cli/projects/${projectId}/agents/*.md\` - Full agent templates with code patterns
172
- - \`~/.prjct-cli/projects/${projectId}/analysis/repo-summary.md\` - Complete technical analysis
173
- - \`~/.prjct-cli/projects/${projectId}/planning/roadmap.md\` - Full feature roadmap
174
- - \`~/.prjct-cli/projects/${projectId}/memory/context.jsonl\` - Decision history
175
- `
176
-
177
- // 11. Write to global storage
178
- const contextPath = path.join(globalPath, 'CLAUDE.md')
179
- await fs.writeFile(contextPath, content, 'utf-8')
180
-
181
- return { agents: agentFiles.map(f => f.replace('.md', '')), stack: stackInfo, currentTask }
182
- }
183
-
184
- /**
185
- * Extract FULL stack info from repo-summary
4
+ * Wrapper that generates context MD files from JSON data.
5
+ * Uses the context generator to create CLAUDE.md, now.md, queue.md, summary.md
186
6
  */
187
- function extractFullStack(summary: string | null): StackInfo {
188
- if (!summary) return { primary: null, full: null, languages: [], frameworks: [], dependencies: [] }
189
-
190
- const result: StackInfo = { primary: null, full: null, languages: [], frameworks: [], dependencies: [] }
191
-
192
- // Extract languages (e.g., "### JavaScript/TypeScript")
193
- const langMatch = summary.match(/###\s*(JavaScript|TypeScript|Python|Go|Rust|Ruby|Java|C#|PHP)/i)
194
- if (langMatch) result.primary = langMatch[1]
195
-
196
- // Extract dependencies
197
- const depsMatch = summary.match(/\*\*Dependencies\*\*:\s*([^\n]+)/i)
198
- if (depsMatch) result.dependencies = depsMatch[1].split(',').map(d => d.trim())
199
7
 
200
- // Extract full stack section (between ## Stack Detected and next ##)
201
- const stackSection = summary.match(/## Stack Detected\n([\s\S]*?)(?=\n## |$)/i)
202
- if (stackSection && stackSection[1]) {
203
- result.full = stackSection[1].trim()
204
- }
205
-
206
- return result
207
- }
8
+ import { generateContext } from './context/generator'
208
9
 
209
10
  /**
210
- * Extract project structure from repo-summary
11
+ * Generate local context files for Claude
211
12
  */
212
- function extractProjectStructure(summary: string | null): ProjectStructure {
213
- if (!summary) return { fileCount: null, directories: [] }
214
-
215
- const result: ProjectStructure = { fileCount: null, directories: [] }
216
-
217
- // Extract file count
218
- const fileMatch = summary.match(/\*\*Total Files\*\*:\s*(\d+)/i)
219
- if (fileMatch) result.fileCount = parseInt(fileMatch[1])
220
-
221
- // Extract directories
222
- const dirMatch = summary.match(/\*\*Directories\*\*:\s*([^\n]+)/i)
223
- if (dirMatch) result.directories = dirMatch[1].split(',').map(d => d.trim())
224
-
225
- return result
226
- }
227
-
228
- /**
229
- * Extract git stats from repo-summary
230
- */
231
- function extractGitStats(summary: string | null): GitStats {
232
- if (!summary) return { commits: null, contributors: null, recentCommits: [] }
233
-
234
- const result: GitStats = { commits: null, contributors: null, recentCommits: [] }
235
-
236
- // Extract commit count
237
- const commitMatch = summary.match(/\*\*Total Commits\*\*:\s*(\d+)/i)
238
- if (commitMatch) result.commits = parseInt(commitMatch[1])
239
-
240
- // Extract contributors
241
- const contribMatch = summary.match(/\*\*Contributors\*\*:\s*(\d+)/i)
242
- if (contribMatch) result.contributors = parseInt(contribMatch[1])
243
-
244
- // Extract recent commits
245
- const recentSection = summary.match(/## Recent Activity[\s\S]*?(?=##|$)/i)
246
- if (recentSection) {
247
- const commitLines = recentSection[0].match(/- `([a-f0-9]+)` (.+?) \(/g)
248
- if (commitLines) {
249
- result.recentCommits = commitLines.slice(0, 5).map(line => {
250
- const match = line.match(/- `([a-f0-9]+)` (.+?) \(/)
251
- return match ? { hash: match[1], message: match[2] } : null
252
- }).filter((c): c is GitCommit => c !== null)
253
- }
254
- }
255
-
256
- return result
257
- }
258
-
259
- /**
260
- * Extract agent expertise from agent files
261
- */
262
- async function extractAgentExpertise(agentsDir: string, agentFiles: string[]): Promise<AgentExpertise[]> {
263
- const expertise: AgentExpertise[] = []
264
-
265
- for (const file of agentFiles.slice(0, 6)) { // Max 6 agents to keep context small
266
- try {
267
- const content = await fs.readFile(path.join(agentsDir, file), 'utf-8')
268
- const name = file.replace('.md', '')
269
-
270
- // Extract role
271
- const roleMatch = content.match(/Role:\s*([^\n]+)/i)
272
- const role = roleMatch ? roleMatch[1].trim() : 'Specialist'
273
-
274
- // Extract domain or expertise
275
- const domainMatch = content.match(/domain[:\s]+([^\n]+)/i) ||
276
- content.match(/expertise[:\s]+([^\n]+)/i) ||
277
- content.match(/owner of the ([^\n.]+)/i)
278
- const expertiseText = domainMatch ? domainMatch[1].trim() : 'General'
279
-
280
- expertise.push({ name, role, expertise: expertiseText })
281
- } catch {
282
- // Skip unreadable agents
283
- }
284
- }
285
-
286
- return expertise
287
- }
288
-
289
- /**
290
- * Extract current task from now.md
291
- */
292
- function extractCurrentTask(now: string | null): string | null {
293
- if (!now) return null
294
-
295
- const lines = now.split('\n')
296
- for (const line of lines) {
297
- const trimmed = line.trim()
298
- if (trimmed && !trimmed.startsWith('#') && !trimmed.startsWith('<!--') && !trimmed.startsWith('No current')) {
299
- return trimmed
300
- }
301
- }
302
- return null
303
- }
304
-
305
- /**
306
- * Extract top N tasks from next.md
307
- */
308
- function extractTopTasks(next: string | null, count: number): string[] {
309
- if (!next) return []
310
-
311
- return next
312
- .split('\n')
313
- .filter(l => l.match(/^[-*\d.]\s/))
314
- .slice(0, count)
315
- .map(l => l.replace(/^[-*\d.]\s+/, '').trim())
316
- .filter(Boolean)
317
- }
318
-
319
- /**
320
- * Extract active features from roadmap.md
321
- */
322
- function extractActiveFeatures(roadmap: string | null): string[] {
323
- if (!roadmap) return []
324
-
325
- const features: string[] = []
326
- const lines = roadmap.split('\n')
327
- let inActive = false
328
-
329
- for (const line of lines) {
330
- if (line.includes('Active') || line.includes('In Progress') || line.includes('Current')) {
331
- inActive = true
332
- continue
333
- }
334
- if (line.startsWith('## ') && inActive) {
335
- break
336
- }
337
- if (inActive && line.match(/^[-*]\s*\[[ x]\]/i)) {
338
- const feature = line.replace(/^[-*]\s*\[[ x]\]\s*/, '').replace(/\*\*/g, '').trim()
339
- if (feature) features.push(feature)
340
- }
341
- }
342
-
343
- return features.slice(0, 5)
344
- }
345
-
346
- /**
347
- * Extract recent ideas from ideas.md
348
- */
349
- function extractRecentIdeas(ideas: string | null): string[] {
350
- if (!ideas) return []
351
-
352
- return ideas
353
- .split('\n')
354
- .filter(l => l.match(/^[-*]\s/))
355
- .slice(0, 3)
356
- .map(l => l.replace(/^[-*]\s+/, '').trim())
357
- .filter(Boolean)
13
+ async function generateLocalContext(projectPath: string, projectId: string): Promise<void> {
14
+ await generateContext(projectId, projectPath)
358
15
  }
359
16
 
360
17
  export { generateLocalContext }
@@ -2,45 +2,26 @@
2
2
  * Data Module
3
3
  *
4
4
  * MD-First Architecture: MD files are the source of truth.
5
- * JSON managers are deprecated in favor of MD managers.
6
5
  */
7
6
 
8
- // Base (legacy JSON)
9
- export { BaseManager, ArrayManager } from './base-manager'
7
+ // MD managers
8
+ import { mdStateManager } from './md-state-manager'
9
+ import { mdQueueManager } from './md-queue-manager'
10
+ import { mdShippedManager } from './md-shipped-manager'
11
+ import { mdIdeasManager } from './md-ideas-manager'
10
12
 
11
- // MD-First Base
13
+ // Base classes
12
14
  export { MdBaseManager, MdArrayManager } from './md-base-manager'
13
15
 
14
- // MD-First Managers (NEW - use these!)
15
- export { mdStateManager } from './md-state-manager'
16
- export { mdQueueManager } from './md-queue-manager'
16
+ // MD managers
17
+ export { mdStateManager, mdQueueManager, mdShippedManager, mdIdeasManager }
17
18
 
18
- // Legacy JSON Managers (deprecated - for backwards compatibility only)
19
- export { stateManager, default as stateManagerDefault } from './state-manager'
20
- export { projectManager, default as projectManagerDefault } from './project-manager'
21
- export { agentsManager, default as agentsManagerDefault } from './agents-manager'
22
- export { ideasManager, default as ideasManagerDefault } from './ideas-manager'
23
- export { roadmapManager, default as roadmapManagerDefault } from './roadmap-manager'
24
- export { shippedManager, default as shippedManagerDefault } from './shipped-manager'
25
- export { analysisManager, default as analysisManagerDefault } from './analysis-manager'
26
- export { outcomesManager, default as outcomesManagerDefault } from './outcomes-manager'
27
-
28
- // MD-First managers (preferred)
19
+ // MD managers object
29
20
  export const mdManagers = {
30
- state: require('./md-state-manager').mdStateManager,
31
- queue: require('./md-queue-manager').mdQueueManager
32
- }
33
-
34
- // Legacy JSON managers (deprecated)
35
- export const dataManagers = {
36
- state: require('./state-manager').stateManager,
37
- project: require('./project-manager').projectManager,
38
- agents: require('./agents-manager').agentsManager,
39
- ideas: require('./ideas-manager').ideasManager,
40
- roadmap: require('./roadmap-manager').roadmapManager,
41
- shipped: require('./shipped-manager').shippedManager,
42
- analysis: require('./analysis-manager').analysisManager,
43
- outcomes: require('./outcomes-manager').outcomesManager
21
+ state: mdStateManager,
22
+ queue: mdQueueManager,
23
+ shipped: mdShippedManager,
24
+ ideas: mdIdeasManager
44
25
  }
45
26
 
46
27
  export default mdManagers
@@ -0,0 +1,155 @@
1
+ /**
2
+ * MD Ideas Manager
3
+ *
4
+ * MD-First Architecture: Manages ideas via ideas.md.
5
+ * Source of truth is the markdown file, not JSON.
6
+ */
7
+
8
+ import { MdArrayManager } from './md-base-manager'
9
+ import { parseIdeas, serializeIdeas, type Idea, type IdeaStatus, type IdeaPriority } from '../serializers/ideas-serializer'
10
+ import { generateUUID } from '../schemas'
11
+
12
+ class MdIdeasManager extends MdArrayManager<Idea> {
13
+ constructor() {
14
+ super('planning/ideas.md')
15
+ }
16
+
17
+ protected parse(content: string): Idea[] {
18
+ return parseIdeas(content)
19
+ }
20
+
21
+ protected serialize(data: Idea[]): string {
22
+ return serializeIdeas(data)
23
+ }
24
+
25
+ // =========== Ideas Operations ===========
26
+
27
+ /**
28
+ * Add a new idea
29
+ */
30
+ async addIdea(
31
+ projectId: string,
32
+ text: string,
33
+ options?: { tags?: string[]; priority?: IdeaPriority }
34
+ ): Promise<Idea[]> {
35
+ const idea: Idea = {
36
+ id: generateUUID(),
37
+ text,
38
+ status: 'pending',
39
+ priority: options?.priority || 'medium',
40
+ tags: options?.tags || [],
41
+ addedAt: new Date().toISOString()
42
+ }
43
+
44
+ return this.add(projectId, idea)
45
+ }
46
+
47
+ /**
48
+ * Get all ideas
49
+ */
50
+ async getAll(projectId: string): Promise<Idea[]> {
51
+ return this.read(projectId)
52
+ }
53
+
54
+ /**
55
+ * Get pending ideas
56
+ */
57
+ async getPending(projectId: string): Promise<Idea[]> {
58
+ const all = await this.read(projectId)
59
+ return all.filter(i => i.status === 'pending')
60
+ }
61
+
62
+ /**
63
+ * Get idea by ID
64
+ */
65
+ async getById(projectId: string, id: string): Promise<Idea | undefined> {
66
+ return this.find(projectId, i => i.id === id)
67
+ }
68
+
69
+ /**
70
+ * Convert idea to feature
71
+ */
72
+ async convertToFeature(projectId: string, id: string, featureId: string): Promise<Idea[]> {
73
+ return this.updateItem(
74
+ projectId,
75
+ i => i.id === id,
76
+ i => ({ ...i, status: 'converted' as IdeaStatus, convertedTo: featureId })
77
+ )
78
+ }
79
+
80
+ /**
81
+ * Archive an idea
82
+ */
83
+ async archive(projectId: string, id: string): Promise<Idea[]> {
84
+ return this.updateItem(
85
+ projectId,
86
+ i => i.id === id,
87
+ i => ({ ...i, status: 'archived' as IdeaStatus })
88
+ )
89
+ }
90
+
91
+ /**
92
+ * Update idea priority
93
+ */
94
+ async setPriority(projectId: string, id: string, priority: IdeaPriority): Promise<Idea[]> {
95
+ return this.updateItem(
96
+ projectId,
97
+ i => i.id === id,
98
+ i => ({ ...i, priority })
99
+ )
100
+ }
101
+
102
+ /**
103
+ * Add tags to an idea
104
+ */
105
+ async addTags(projectId: string, id: string, tags: string[]): Promise<Idea[]> {
106
+ return this.updateItem(
107
+ projectId,
108
+ i => i.id === id,
109
+ i => ({ ...i, tags: [...new Set([...i.tags, ...tags])] })
110
+ )
111
+ }
112
+
113
+ /**
114
+ * Remove an idea
115
+ */
116
+ async removeIdea(projectId: string, id: string): Promise<Idea[]> {
117
+ return this.remove(projectId, i => i.id === id)
118
+ }
119
+
120
+ /**
121
+ * Get ideas count by status
122
+ */
123
+ async getCounts(projectId: string): Promise<{ pending: number; converted: number; archived: number }> {
124
+ const all = await this.read(projectId)
125
+ return {
126
+ pending: all.filter(i => i.status === 'pending').length,
127
+ converted: all.filter(i => i.status === 'converted').length,
128
+ archived: all.filter(i => i.status === 'archived').length
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Clean up empty sections (remove archived ideas older than 30 days)
134
+ */
135
+ async cleanup(projectId: string): Promise<{ removed: number }> {
136
+ const thirtyDaysAgo = new Date()
137
+ thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30)
138
+
139
+ const all = await this.read(projectId)
140
+ const toKeep = all.filter(i => {
141
+ if (i.status !== 'archived') return true
142
+ return new Date(i.addedAt) > thirtyDaysAgo
143
+ })
144
+
145
+ const removed = all.length - toKeep.length
146
+ if (removed > 0) {
147
+ await this.write(projectId, toKeep)
148
+ }
149
+
150
+ return { removed }
151
+ }
152
+ }
153
+
154
+ export const mdIdeasManager = new MdIdeasManager()
155
+ export default mdIdeasManager
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { MdBaseManager } from './md-base-manager'
9
9
  import { parseQueue, serializeQueue } from '../serializers'
10
+ import { generateUUID } from '../schemas'
10
11
  import type { QueueJson, QueueTask, Priority, TaskType, TaskSection } from '../schemas/state'
11
12
 
12
13
  class MdQueueManager extends MdBaseManager<QueueJson> {
@@ -52,7 +53,7 @@ class MdQueueManager extends MdBaseManager<QueueJson> {
52
53
  ): Promise<QueueJson> {
53
54
  const newTask: QueueTask = {
54
55
  ...task,
55
- id: `task_${Date.now()}`,
56
+ id: generateUUID(),
56
57
  createdAt: new Date().toISOString(),
57
58
  completed: false
58
59
  }
@@ -119,9 +120,9 @@ class MdQueueManager extends MdBaseManager<QueueJson> {
119
120
  projectId: string,
120
121
  tasks: Array<Omit<QueueTask, 'id' | 'createdAt' | 'completed'>>
121
122
  ): Promise<QueueJson> {
122
- const newTasks: QueueTask[] = tasks.map((task, i) => ({
123
+ const newTasks: QueueTask[] = tasks.map((task) => ({
123
124
  ...task,
124
- id: `task_${Date.now()}_${i}`,
125
+ id: generateUUID(),
125
126
  createdAt: new Date().toISOString(),
126
127
  completed: false
127
128
  }))
@@ -0,0 +1,90 @@
1
+ /**
2
+ * MD Shipped Manager
3
+ *
4
+ * MD-First Architecture: Manages shipped features via shipped.md.
5
+ * Source of truth is the markdown file, not JSON.
6
+ */
7
+
8
+ import { MdArrayManager } from './md-base-manager'
9
+ import { parseShipped, serializeShipped, type ShippedFeature } from '../serializers/shipped-serializer'
10
+ import { generateUUID } from '../schemas'
11
+
12
+ class MdShippedManager extends MdArrayManager<ShippedFeature> {
13
+ constructor() {
14
+ super('progress/shipped.md')
15
+ }
16
+
17
+ protected parse(content: string): ShippedFeature[] {
18
+ return parseShipped(content)
19
+ }
20
+
21
+ protected serialize(data: ShippedFeature[]): string {
22
+ return serializeShipped(data)
23
+ }
24
+
25
+ // =========== Shipped Features ===========
26
+
27
+ /**
28
+ * Add a shipped feature
29
+ */
30
+ async addShipped(
31
+ projectId: string,
32
+ feature: Omit<ShippedFeature, 'id' | 'shippedAt'>
33
+ ): Promise<ShippedFeature[]> {
34
+ const shippedFeature: ShippedFeature = {
35
+ ...feature,
36
+ id: generateUUID(),
37
+ shippedAt: new Date().toISOString()
38
+ }
39
+
40
+ return this.prepend(projectId, shippedFeature)
41
+ }
42
+
43
+ /**
44
+ * Get all shipped features
45
+ */
46
+ async getAll(projectId: string): Promise<ShippedFeature[]> {
47
+ return this.read(projectId)
48
+ }
49
+
50
+ /**
51
+ * Get recent shipped features (last N)
52
+ */
53
+ async getRecent(projectId: string, limit: number = 5): Promise<ShippedFeature[]> {
54
+ const all = await this.read(projectId)
55
+ return all.slice(0, limit)
56
+ }
57
+
58
+ /**
59
+ * Get shipped features by version
60
+ */
61
+ async getByVersion(projectId: string, version: string): Promise<ShippedFeature | undefined> {
62
+ return this.find(projectId, (f) => f.version === version)
63
+ }
64
+
65
+ /**
66
+ * Get shipped features count
67
+ */
68
+ async getCount(projectId: string): Promise<number> {
69
+ const all = await this.read(projectId)
70
+ return all.length
71
+ }
72
+
73
+ /**
74
+ * Get shipped features for a date range
75
+ */
76
+ async getByDateRange(
77
+ projectId: string,
78
+ startDate: Date,
79
+ endDate: Date
80
+ ): Promise<ShippedFeature[]> {
81
+ const all = await this.read(projectId)
82
+ return all.filter((f) => {
83
+ const date = new Date(f.shippedAt)
84
+ return date >= startDate && date <= endDate
85
+ })
86
+ }
87
+ }
88
+
89
+ export const mdShippedManager = new MdShippedManager()
90
+ export default mdShippedManager