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.
- package/CHANGELOG.md +122 -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/setup.md +18 -3
- 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/templates/mcp-config.json +20 -1
- 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
package/core/context-sync.ts
CHANGED
|
@@ -1,360 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Context Sync
|
|
2
|
+
* Context Sync
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
11
|
+
* Generate local context files for Claude
|
|
211
12
|
*/
|
|
212
|
-
function
|
|
213
|
-
|
|
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 }
|
package/core/data/index.ts
CHANGED
|
@@ -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
|
-
//
|
|
9
|
-
|
|
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
|
-
//
|
|
13
|
+
// Base classes
|
|
12
14
|
export { MdBaseManager, MdArrayManager } from './md-base-manager'
|
|
13
15
|
|
|
14
|
-
// MD
|
|
15
|
-
export { mdStateManager
|
|
16
|
-
export { mdQueueManager } from './md-queue-manager'
|
|
16
|
+
// MD managers
|
|
17
|
+
export { mdStateManager, mdQueueManager, mdShippedManager, mdIdeasManager }
|
|
17
18
|
|
|
18
|
-
//
|
|
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:
|
|
31
|
-
queue:
|
|
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:
|
|
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
|
|
123
|
+
const newTasks: QueueTask[] = tasks.map((task) => ({
|
|
123
124
|
...task,
|
|
124
|
-
id:
|
|
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
|