prjct-cli 0.20.0 → 0.21.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.
Files changed (236) hide show
  1. package/CHANGELOG.md +24 -6
  2. package/CLAUDE.md +56 -15
  3. package/README.md +5 -6
  4. package/bin/prjct +59 -42
  5. package/bin/prjct.ts +60 -0
  6. package/core/__tests__/agentic/memory-system.test.ts +18 -3
  7. package/core/__tests__/agentic/plan-mode.test.ts +55 -26
  8. package/core/__tests__/agentic/prompt-builder.test.ts +6 -6
  9. package/core/__tests__/utils/project-commands.test.ts +72 -0
  10. package/core/agentic/agent-router.ts +3 -12
  11. package/core/agentic/command-executor.ts +372 -3
  12. package/core/agentic/context-builder.ts +7 -27
  13. package/core/agentic/ground-truth.ts +604 -5
  14. package/core/agentic/index.ts +180 -0
  15. package/core/agentic/loop-detector.ts +418 -4
  16. package/core/agentic/memory-system.ts +857 -3
  17. package/core/agentic/plan-mode.ts +491 -4
  18. package/core/agentic/prompt-builder.ts +44 -65
  19. package/core/agentic/services.ts +13 -5
  20. package/core/agentic/skill-loader.ts +112 -0
  21. package/core/agentic/smart-context.ts +37 -122
  22. package/core/agentic/template-loader.ts +79 -122
  23. package/core/agentic/tool-registry.ts +5 -11
  24. package/core/agents/index.ts +1 -1
  25. package/core/agents/performance.ts +4 -2
  26. package/core/bus/bus.ts +262 -0
  27. package/core/bus/index.ts +3 -313
  28. package/core/commands/analysis.ts +5 -5
  29. package/core/commands/analytics.ts +11 -11
  30. package/core/commands/base.ts +33 -209
  31. package/core/commands/cleanup.ts +148 -0
  32. package/core/commands/command-data.ts +346 -0
  33. package/core/commands/commands.ts +216 -0
  34. package/core/commands/design.ts +83 -0
  35. package/core/commands/index.ts +13 -207
  36. package/core/commands/maintenance.ts +52 -473
  37. package/core/commands/planning.ts +3 -3
  38. package/core/commands/register.ts +104 -0
  39. package/core/commands/registry.ts +441 -0
  40. package/core/commands/setup.ts +25 -9
  41. package/core/commands/shipping.ts +48 -11
  42. package/core/commands/snapshots.ts +299 -0
  43. package/core/commands/workflow.ts +2 -2
  44. package/core/constants/index.ts +254 -4
  45. package/core/domain/agent-loader.ts +5 -6
  46. package/core/domain/task-stack.ts +555 -4
  47. package/core/errors.ts +127 -1
  48. package/core/events/events.ts +87 -0
  49. package/core/events/index.ts +4 -138
  50. package/core/index.ts +15 -23
  51. package/core/infrastructure/agent-detector.ts +126 -201
  52. package/core/infrastructure/author-detector.ts +99 -171
  53. package/core/infrastructure/command-installer.ts +476 -4
  54. package/core/infrastructure/config-manager.ts +41 -37
  55. package/core/infrastructure/path-manager.ts +59 -9
  56. package/core/infrastructure/permission-manager.ts +286 -0
  57. package/core/integrations/notion/client.ts +323 -0
  58. package/core/integrations/notion/index.ts +43 -0
  59. package/core/integrations/notion/setup.ts +230 -0
  60. package/core/integrations/notion/sync.ts +311 -0
  61. package/core/integrations/notion/templates.ts +234 -0
  62. package/core/outcomes/analyzer.ts +7 -41
  63. package/core/outcomes/index.ts +1 -1
  64. package/core/outcomes/recorder.ts +1 -1
  65. package/core/plugin/builtin/notion.ts +178 -0
  66. package/core/{plugins → plugin/builtin}/webhook.ts +6 -22
  67. package/core/plugin/loader.ts +5 -5
  68. package/core/plugin/registry.ts +2 -2
  69. package/core/schemas/ideas.ts +85 -54
  70. package/core/schemas/index.ts +14 -33
  71. package/core/schemas/permissions.ts +177 -0
  72. package/core/schemas/project.ts +39 -12
  73. package/core/schemas/roadmap.ts +94 -59
  74. package/core/schemas/schemas.ts +39 -0
  75. package/core/schemas/shipped.ts +87 -60
  76. package/core/schemas/state.ts +110 -70
  77. package/core/server/index.ts +21 -0
  78. package/core/server/routes.ts +165 -0
  79. package/core/server/server.ts +136 -0
  80. package/core/server/sse.ts +135 -0
  81. package/core/services/agent-service.ts +170 -0
  82. package/core/services/breakdown-service.ts +126 -0
  83. package/core/services/index.ts +21 -0
  84. package/core/services/memory-service.ts +108 -0
  85. package/core/services/project-service.ts +146 -0
  86. package/core/services/skill-service.ts +253 -0
  87. package/core/session/compaction.ts +257 -0
  88. package/core/session/index.ts +20 -8
  89. package/core/{infrastructure/session-manager/migration.ts → session/log-migration.ts} +9 -9
  90. package/core/{infrastructure/session-manager/session-manager.ts → session/session-log-manager.ts} +27 -26
  91. package/core/session/{session-manager.ts → task-session-manager.ts} +7 -4
  92. package/core/session/utils.ts +1 -1
  93. package/core/storage/ideas-storage.ts +10 -26
  94. package/core/storage/index.ts +14 -162
  95. package/core/storage/queue-storage.ts +13 -11
  96. package/core/storage/shipped-storage.ts +4 -17
  97. package/core/storage/state-storage.ts +35 -43
  98. package/core/storage/storage-manager.ts +42 -52
  99. package/core/storage/storage.ts +160 -0
  100. package/core/sync/auth-config.ts +1 -8
  101. package/core/sync/index.ts +17 -10
  102. package/core/sync/oauth-handler.ts +1 -6
  103. package/core/sync/sync-client.ts +6 -34
  104. package/core/sync/sync-manager.ts +11 -40
  105. package/core/types/agentic.ts +577 -0
  106. package/core/types/agents.ts +145 -0
  107. package/core/types/bus.ts +82 -0
  108. package/core/types/commands.ts +366 -0
  109. package/core/types/config.ts +70 -0
  110. package/core/types/core.ts +96 -0
  111. package/core/types/domain.ts +71 -0
  112. package/core/types/events.ts +42 -0
  113. package/core/types/fs.ts +56 -0
  114. package/core/types/index.ts +396 -500
  115. package/core/types/infrastructure.ts +196 -0
  116. package/core/types/integrations.ts +57 -0
  117. package/core/{agentic/memory-system/types.ts → types/memory.ts} +33 -8
  118. package/core/{outcomes/types.ts → types/outcomes.ts} +53 -8
  119. package/core/types/plugin.ts +25 -0
  120. package/core/types/server.ts +54 -0
  121. package/core/types/services.ts +65 -0
  122. package/core/types/session.ts +135 -0
  123. package/core/types/storage.ts +148 -0
  124. package/core/types/sync.ts +121 -0
  125. package/core/types/task.ts +72 -0
  126. package/core/types/template.ts +24 -0
  127. package/core/types/utils.ts +90 -0
  128. package/core/utils/cache.ts +195 -0
  129. package/core/utils/collection-filters.ts +245 -0
  130. package/core/utils/date-helper.ts +1 -5
  131. package/core/utils/file-helper.ts +20 -10
  132. package/core/utils/jsonl-helper.ts +5 -8
  133. package/core/utils/markdown-builder.ts +277 -0
  134. package/core/utils/project-commands.ts +132 -0
  135. package/core/utils/runtime.ts +119 -0
  136. package/dist/bin/prjct.mjs +12568 -0
  137. package/package.json +13 -8
  138. package/scripts/build.js +106 -0
  139. package/scripts/postinstall.js +50 -8
  140. package/templates/agentic/subagent-generation.md +1 -1
  141. package/templates/commands/init.md +43 -0
  142. package/templates/commands/notion-setup.md +191 -0
  143. package/templates/commands/serve.md +118 -0
  144. package/templates/commands/ship.md +13 -2
  145. package/templates/commands/skill.md +110 -0
  146. package/templates/commands/sync.md +1 -1
  147. package/templates/commands/test.md +23 -4
  148. package/templates/mcp-config.json +28 -0
  149. package/templates/permissions/default.jsonc +60 -0
  150. package/templates/permissions/permissive.jsonc +49 -0
  151. package/templates/permissions/strict.jsonc +62 -0
  152. package/templates/skills/code-review.md +47 -0
  153. package/templates/skills/debug.md +61 -0
  154. package/templates/skills/refactor.md +47 -0
  155. package/templates/subagents/domain/devops.md +1 -1
  156. package/templates/subagents/domain/testing.md +6 -10
  157. package/templates/subagents/workflow/prjct-shipper.md +16 -7
  158. package/templates/tools/bash.txt +22 -0
  159. package/templates/tools/edit.txt +18 -0
  160. package/templates/tools/glob.txt +19 -0
  161. package/templates/tools/grep.txt +21 -0
  162. package/templates/tools/read.txt +14 -0
  163. package/templates/tools/task.txt +20 -0
  164. package/templates/tools/webfetch.txt +16 -0
  165. package/templates/tools/websearch.txt +18 -0
  166. package/templates/tools/write.txt +17 -0
  167. package/core/agentic/command-executor/command-executor.ts +0 -312
  168. package/core/agentic/command-executor/index.ts +0 -16
  169. package/core/agentic/command-executor/status-signal.ts +0 -38
  170. package/core/agentic/command-executor/types.ts +0 -79
  171. package/core/agentic/ground-truth/index.ts +0 -76
  172. package/core/agentic/ground-truth/types.ts +0 -33
  173. package/core/agentic/ground-truth/utils.ts +0 -48
  174. package/core/agentic/ground-truth/verifiers/analyze.ts +0 -54
  175. package/core/agentic/ground-truth/verifiers/done.ts +0 -75
  176. package/core/agentic/ground-truth/verifiers/feature.ts +0 -70
  177. package/core/agentic/ground-truth/verifiers/index.ts +0 -37
  178. package/core/agentic/ground-truth/verifiers/init.ts +0 -52
  179. package/core/agentic/ground-truth/verifiers/now.ts +0 -57
  180. package/core/agentic/ground-truth/verifiers/ship.ts +0 -85
  181. package/core/agentic/ground-truth/verifiers/spec.ts +0 -45
  182. package/core/agentic/ground-truth/verifiers/sync.ts +0 -47
  183. package/core/agentic/ground-truth/verifiers.ts +0 -6
  184. package/core/agentic/loop-detector/error-analysis.ts +0 -97
  185. package/core/agentic/loop-detector/hallucination.ts +0 -71
  186. package/core/agentic/loop-detector/index.ts +0 -41
  187. package/core/agentic/loop-detector/loop-detector.ts +0 -222
  188. package/core/agentic/loop-detector/types.ts +0 -66
  189. package/core/agentic/memory-system/history.ts +0 -53
  190. package/core/agentic/memory-system/index.ts +0 -192
  191. package/core/agentic/memory-system/patterns.ts +0 -156
  192. package/core/agentic/memory-system/semantic-memories.ts +0 -278
  193. package/core/agentic/memory-system/session.ts +0 -21
  194. package/core/agentic/plan-mode/approval.ts +0 -57
  195. package/core/agentic/plan-mode/constants.ts +0 -44
  196. package/core/agentic/plan-mode/index.ts +0 -28
  197. package/core/agentic/plan-mode/plan-mode.ts +0 -407
  198. package/core/agentic/plan-mode/types.ts +0 -193
  199. package/core/agents/types.ts +0 -126
  200. package/core/command-registry/categories.ts +0 -23
  201. package/core/command-registry/commands.ts +0 -15
  202. package/core/command-registry/core-commands.ts +0 -344
  203. package/core/command-registry/index.ts +0 -158
  204. package/core/command-registry/optional-commands.ts +0 -163
  205. package/core/command-registry/setup-commands.ts +0 -83
  206. package/core/command-registry/types.ts +0 -59
  207. package/core/command-registry.ts +0 -9
  208. package/core/commands/types.ts +0 -185
  209. package/core/commands.ts +0 -11
  210. package/core/constants/formats.ts +0 -187
  211. package/core/context-sync.ts +0 -18
  212. package/core/data/index.ts +0 -27
  213. package/core/data/md-base-manager.ts +0 -203
  214. package/core/data/md-ideas-manager.ts +0 -155
  215. package/core/data/md-queue-manager.ts +0 -180
  216. package/core/data/md-shipped-manager.ts +0 -90
  217. package/core/data/md-state-manager.ts +0 -137
  218. package/core/domain/task-stack/index.ts +0 -19
  219. package/core/domain/task-stack/parser.ts +0 -86
  220. package/core/domain/task-stack/storage.ts +0 -123
  221. package/core/domain/task-stack/task-stack.ts +0 -340
  222. package/core/domain/task-stack/types.ts +0 -51
  223. package/core/infrastructure/command-installer/command-installer.ts +0 -327
  224. package/core/infrastructure/command-installer/global-config.ts +0 -136
  225. package/core/infrastructure/command-installer/index.ts +0 -25
  226. package/core/infrastructure/command-installer/types.ts +0 -41
  227. package/core/infrastructure/session-manager/index.ts +0 -23
  228. package/core/infrastructure/session-manager/types.ts +0 -45
  229. package/core/infrastructure/session-manager.ts +0 -8
  230. package/core/serializers/ideas-serializer.ts +0 -187
  231. package/core/serializers/index.ts +0 -36
  232. package/core/serializers/queue-serializer.ts +0 -210
  233. package/core/serializers/shipped-serializer.ts +0 -108
  234. package/core/serializers/state-serializer.ts +0 -136
  235. package/core/session/types.ts +0 -29
  236. /package/core/infrastructure/{agents/claude-agent.ts → claude-agent.ts} +0 -0
@@ -1,187 +0,0 @@
1
- /**
2
- * Ideas Serializer
3
- *
4
- * Parses and serializes ideas.md for idea backlog.
5
- *
6
- * MD Format (ideas.md):
7
- * ```
8
- * # IDEAS 💡
9
- *
10
- * ## Brain Dump
11
- *
12
- * - Add dark mode _(2025-12-10)_ #ui #enhancement
13
- * - Improve caching _(2025-12-09)_ #performance
14
- *
15
- * ## Converted
16
- *
17
- * - ✓ Add user auth → feat_123 _(2025-12-08)_
18
- * ```
19
- */
20
-
21
- export type IdeaStatus = 'pending' | 'converted' | 'archived'
22
- export type IdeaPriority = 'low' | 'medium' | 'high'
23
-
24
- export interface Idea {
25
- id: string
26
- text: string
27
- status: IdeaStatus
28
- priority: IdeaPriority
29
- tags: string[]
30
- addedAt: string // ISO8601
31
- convertedTo?: string // featureId if converted
32
- }
33
-
34
- /**
35
- * Parse ideas.md content to Idea[]
36
- */
37
- export function parseIdeas(content: string): Idea[] {
38
- if (!content || !content.trim()) {
39
- return []
40
- }
41
-
42
- const ideas: Idea[] = []
43
-
44
- // Split by sections
45
- const sections = content.split(/\n## /).slice(1)
46
-
47
- for (const section of sections) {
48
- const lines = section.trim().split('\n')
49
- const sectionName = lines[0]?.trim().toLowerCase() || ''
50
-
51
- // Determine status based on section
52
- let status: IdeaStatus = 'pending'
53
- if (sectionName.includes('converted')) {
54
- status = 'converted'
55
- } else if (sectionName.includes('archived')) {
56
- status = 'archived'
57
- }
58
-
59
- // Parse list items
60
- for (let i = 1; i < lines.length; i++) {
61
- const line = lines[i].trim()
62
- if (!line.startsWith('-')) continue
63
-
64
- const itemText = line.slice(1).trim()
65
- if (!itemText) continue
66
-
67
- // Extract date: _(date)_
68
- const dateMatch = itemText.match(/_\((.+?)\)_/)
69
- const addedAt = dateMatch ? dateMatch[1].trim() : new Date().toISOString()
70
-
71
- // Extract tags: #tag1 #tag2
72
- const tags = (itemText.match(/#(\w+)/g) || []).map(t => t.slice(1))
73
-
74
- // Extract converted feature id: → feat_123
75
- const convertedMatch = itemText.match(/→\s*(\w+)/)
76
- const convertedTo = convertedMatch ? convertedMatch[1] : undefined
77
-
78
- // Clean text: remove date, tags, and conversion marker
79
- let text = itemText
80
- .replace(/_\(.+?\)_/g, '')
81
- .replace(/#\w+/g, '')
82
- .replace(/→\s*\w+/g, '')
83
- .replace(/^✓\s*/, '')
84
- .trim()
85
-
86
- // Detect priority from keywords
87
- let priority: IdeaPriority = 'medium'
88
- if (text.toLowerCase().includes('urgent') || text.toLowerCase().includes('critical')) {
89
- priority = 'high'
90
- } else if (text.toLowerCase().includes('nice to have') || text.toLowerCase().includes('maybe')) {
91
- priority = 'low'
92
- }
93
-
94
- ideas.push({
95
- id: `idea_${Date.parse(addedAt) || Date.now()}_${i}`,
96
- text,
97
- status,
98
- priority,
99
- tags,
100
- addedAt,
101
- convertedTo
102
- })
103
- }
104
- }
105
-
106
- return ideas
107
- }
108
-
109
- /**
110
- * Serialize Idea[] to ideas.md format
111
- */
112
- export function serializeIdeas(ideas: Idea[]): string {
113
- const lines: string[] = ['# IDEAS 💡', '']
114
-
115
- const pending = ideas.filter(i => i.status === 'pending')
116
- const converted = ideas.filter(i => i.status === 'converted')
117
- const archived = ideas.filter(i => i.status === 'archived')
118
-
119
- // Brain Dump section (pending ideas)
120
- lines.push('## Brain Dump', '')
121
-
122
- if (pending.length === 0) {
123
- lines.push('_No pending ideas. Use `/p:idea` to capture one._', '')
124
- } else {
125
- // Sort by addedAt descending (newest first)
126
- const sorted = [...pending].sort(
127
- (a, b) => new Date(b.addedAt).getTime() - new Date(a.addedAt).getTime()
128
- )
129
-
130
- for (const idea of sorted) {
131
- const tagsStr = idea.tags.length > 0 ? ' ' + idea.tags.map(t => `#${t}`).join(' ') : ''
132
- const dateStr = formatDate(idea.addedAt)
133
- lines.push(`- ${idea.text} _(${dateStr})_${tagsStr}`)
134
- }
135
- lines.push('')
136
- }
137
-
138
- // Converted section
139
- if (converted.length > 0) {
140
- lines.push('## Converted', '')
141
- const sorted = [...converted].sort(
142
- (a, b) => new Date(b.addedAt).getTime() - new Date(a.addedAt).getTime()
143
- )
144
-
145
- for (const idea of sorted) {
146
- const convStr = idea.convertedTo ? ` → ${idea.convertedTo}` : ''
147
- const dateStr = formatDate(idea.addedAt)
148
- lines.push(`- ✓ ${idea.text}${convStr} _(${dateStr})_`)
149
- }
150
- lines.push('')
151
- }
152
-
153
- // Archived section
154
- if (archived.length > 0) {
155
- lines.push('## Archived', '')
156
- const sorted = [...archived].sort(
157
- (a, b) => new Date(b.addedAt).getTime() - new Date(a.addedAt).getTime()
158
- )
159
-
160
- for (const idea of sorted) {
161
- const dateStr = formatDate(idea.addedAt)
162
- lines.push(`- ${idea.text} _(${dateStr})_`)
163
- }
164
- lines.push('')
165
- }
166
-
167
- return lines.join('\n')
168
- }
169
-
170
- /**
171
- * Format date for display
172
- */
173
- function formatDate(isoDate: string): string {
174
- try {
175
- const date = new Date(isoDate)
176
- return date.toISOString().split('T')[0] // YYYY-MM-DD
177
- } catch {
178
- return isoDate
179
- }
180
- }
181
-
182
- /**
183
- * Create empty ideas.md
184
- */
185
- export function createEmptyIdeasMd(): string {
186
- return serializeIdeas([])
187
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * Serializers Index
3
- *
4
- * MD-First Architecture: These serializers convert between TypeScript schemas and MD format.
5
- */
6
-
7
- // State (now.md)
8
- export {
9
- parseState,
10
- serializeState,
11
- createCurrentTaskMd,
12
- createEmptyStateMd
13
- } from './state-serializer'
14
-
15
- // Queue (next.md)
16
- export {
17
- parseQueue,
18
- serializeQueue,
19
- createEmptyQueueMd
20
- } from './queue-serializer'
21
-
22
- // Shipped (shipped.md)
23
- export {
24
- parseShipped,
25
- serializeShipped,
26
- createEmptyShippedMd
27
- } from './shipped-serializer'
28
- export type { ShippedFeature } from './shipped-serializer'
29
-
30
- // Ideas (ideas.md)
31
- export {
32
- parseIdeas,
33
- serializeIdeas,
34
- createEmptyIdeasMd
35
- } from './ideas-serializer'
36
- export type { Idea, IdeaStatus, IdeaPriority } from './ideas-serializer'
@@ -1,210 +0,0 @@
1
- /**
2
- * Queue Serializer
3
- *
4
- * Parses and serializes next.md for task queue.
5
- *
6
- * MD Format (next.md):
7
- * ```
8
- * # Priority Queue
9
- *
10
- * > Tasks ready to start
11
- *
12
- * ## Active Tasks
13
- * 1. [ ] Task description @agent (from: Feature Name)
14
- * 2. [x] Completed task ✅
15
- *
16
- * ## Previously Active
17
- * - [ ] Paused task
18
- *
19
- * ## Backlog
20
- * - [ ] 🐛 [HIGH] Bug description
21
- * - [ ] Feature task
22
- * ```
23
- */
24
-
25
- import type { QueueJson, QueueTask, Priority, TaskType, TaskSection } from '../schemas/state'
26
-
27
- /**
28
- * Parse next.md content to QueueJson
29
- */
30
- export function parseQueue(content: string): QueueJson {
31
- if (!content || !content.trim()) {
32
- return { tasks: [], lastUpdated: '' }
33
- }
34
-
35
- const lines = content.split('\n')
36
- const tasks: QueueTask[] = []
37
- let currentSection: TaskSection = 'active'
38
- let taskIndex = 0
39
-
40
- for (const line of lines) {
41
- // Detect section headers
42
- if (line.match(/^##\s*Active/i)) {
43
- currentSection = 'active'
44
- continue
45
- }
46
- if (line.match(/^##\s*Previously/i)) {
47
- currentSection = 'previously_active'
48
- continue
49
- }
50
- if (line.match(/^##\s*Backlog/i)) {
51
- currentSection = 'backlog'
52
- continue
53
- }
54
-
55
- // Parse task lines: "1. [ ] Task" or "- [ ] Task" or "- [x] Task"
56
- const taskMatch = line.match(/^(?:\d+\.|[-*])\s*\[([\sx])\]\s*(.+)$/i)
57
- if (!taskMatch) continue
58
-
59
- const isCompleted = taskMatch[1].toLowerCase() === 'x'
60
- let taskText = taskMatch[2].trim()
61
-
62
- // Extract agent: @fe, @be, @fe+be
63
- const agentMatch = taskText.match(/@(\w+(?:\+\w+)?)/)
64
- const agent = agentMatch ? agentMatch[1] : undefined
65
- taskText = taskText.replace(/@\w+(?:\+\w+)?/, '').trim()
66
-
67
- // Extract origin feature: (from: Feature Name)
68
- const fromMatch = taskText.match(/\(from:\s*([^)]+)\)/)
69
- const originFeature = fromMatch ? fromMatch[1].trim() : undefined
70
- taskText = taskText.replace(/\(from:\s*[^)]+\)/, '').trim()
71
-
72
- // Detect task type from emoji/prefix
73
- let type: TaskType = 'feature'
74
- let priority: Priority = 'medium'
75
-
76
- if (taskText.includes('🐛') || taskText.toLowerCase().includes('bug')) {
77
- type = 'bug'
78
- taskText = taskText.replace('🐛', '').trim()
79
- }
80
- if (taskText.includes('🔧') || taskText.toLowerCase().includes('fix')) {
81
- type = 'improvement'
82
- }
83
- if (taskText.includes('♻️') || taskText.toLowerCase().includes('refactor')) {
84
- type = 'chore'
85
- }
86
-
87
- // Extract priority: [HIGH], [MEDIUM], [LOW], [CRITICAL]
88
- const priorityMatch = taskText.match(/\[(HIGH|MEDIUM|LOW|CRITICAL)\]/i)
89
- if (priorityMatch) {
90
- priority = priorityMatch[1].toLowerCase() as Priority
91
- taskText = taskText.replace(/\[(HIGH|MEDIUM|LOW|CRITICAL)\]/i, '').trim()
92
- }
93
-
94
- // Remove checkmarks and clean up
95
- taskText = taskText.replace(/✅/g, '').trim()
96
-
97
- // Skip empty descriptions
98
- if (!taskText) continue
99
-
100
- const task: QueueTask = {
101
- id: `task_${Date.now()}_${taskIndex++}`,
102
- description: taskText,
103
- priority,
104
- type,
105
- completed: isCompleted,
106
- createdAt: new Date().toISOString(),
107
- section: currentSection
108
- }
109
-
110
- if (agent) task.agent = agent
111
- if (originFeature) task.originFeature = originFeature
112
- if (isCompleted) task.completedAt = new Date().toISOString()
113
-
114
- tasks.push(task)
115
- }
116
-
117
- // Extract updated date
118
- const updatedMatch = content.match(/_Updated:\s*(\d{4}-\d{2}-\d{2})/)
119
- const lastUpdated = updatedMatch ? updatedMatch[1] : new Date().toISOString().split('T')[0]
120
-
121
- return { tasks, lastUpdated }
122
- }
123
-
124
- /**
125
- * Serialize QueueJson to next.md format
126
- */
127
- export function serializeQueue(data: QueueJson): string {
128
- const lines: string[] = [
129
- '# Priority Queue',
130
- '',
131
- '> Tasks ready to start (max 100)',
132
- '> Auto-updated by prjct',
133
- ''
134
- ]
135
-
136
- // Group tasks by section
137
- const active = data.tasks.filter(t => t.section === 'active')
138
- const previouslyActive = data.tasks.filter(t => t.section === 'previously_active')
139
- const backlog = data.tasks.filter(t => t.section === 'backlog')
140
-
141
- // Active Tasks
142
- if (active.length > 0) {
143
- lines.push('## Active Tasks', '')
144
- active.forEach((task, i) => {
145
- lines.push(formatTask(task, i + 1, true))
146
- })
147
- lines.push('')
148
- }
149
-
150
- // Previously Active
151
- if (previouslyActive.length > 0) {
152
- lines.push('## Previously Active', '')
153
- previouslyActive.forEach(task => {
154
- lines.push(formatTask(task, 0, false))
155
- })
156
- lines.push('')
157
- }
158
-
159
- // Backlog
160
- lines.push('---', '', '## Backlog', '')
161
- if (backlog.length > 0) {
162
- backlog.forEach(task => {
163
- lines.push(formatTask(task, 0, false))
164
- })
165
- } else {
166
- lines.push('_No backlog items_')
167
- }
168
-
169
- lines.push('')
170
- lines.push('---', '')
171
- lines.push(`_Updated: ${data.lastUpdated || new Date().toISOString().split('T')[0]}_`)
172
-
173
- return lines.join('\n')
174
- }
175
-
176
- /**
177
- * Format a single task as markdown
178
- */
179
- function formatTask(task: QueueTask, num: number, numbered: boolean): string {
180
- const checkbox = task.completed ? '[x]' : '[ ]'
181
- const prefix = numbered && num > 0 ? `${num}.` : '-'
182
-
183
- let text = task.description
184
-
185
- // Add emoji for type
186
- if (task.type === 'bug') text = `🐛 ${text}`
187
-
188
- // Add priority tag for high/critical
189
- if (task.priority === 'high' || task.priority === 'critical') {
190
- text = `[${task.priority.toUpperCase()}] ${text}`
191
- }
192
-
193
- // Add agent
194
- if (task.agent) text = `${text} @${task.agent}`
195
-
196
- // Add origin feature
197
- if (task.originFeature) text = `${text} (from: ${task.originFeature})`
198
-
199
- // Add checkmark for completed
200
- if (task.completed) text = `${text} ✅`
201
-
202
- return `${prefix} ${checkbox} ${text}`
203
- }
204
-
205
- /**
206
- * Quick helpers
207
- */
208
- export function createEmptyQueueMd(): string {
209
- return serializeQueue({ tasks: [], lastUpdated: new Date().toISOString().split('T')[0] })
210
- }
@@ -1,108 +0,0 @@
1
- /**
2
- * Shipped Serializer
3
- *
4
- * Parses and serializes shipped.md for shipped features.
5
- *
6
- * MD Format (shipped.md):
7
- * ```
8
- * # SHIPPED 🚀
9
- *
10
- * ## Feature Name
11
- *
12
- * Shipped: 2025-12-10T10:00:00.000Z
13
- * Version: 0.1.5
14
- *
15
- * ## Another Feature
16
- *
17
- * Shipped: 2025-12-09T14:30:00.000Z
18
- * Version: 0.1.4
19
- * ```
20
- */
21
-
22
- export interface ShippedFeature {
23
- id: string
24
- name: string
25
- shippedAt: string // ISO8601
26
- version: string
27
- description?: string
28
- }
29
-
30
- /**
31
- * Parse shipped.md content to ShippedFeature[]
32
- */
33
- export function parseShipped(content: string): ShippedFeature[] {
34
- if (!content || !content.trim()) {
35
- return []
36
- }
37
-
38
- const features: ShippedFeature[] = []
39
- const sections = content.split(/\n## /).slice(1) // Skip header
40
-
41
- for (const section of sections) {
42
- const lines = section.trim().split('\n')
43
- const name = lines[0]?.trim()
44
-
45
- if (!name) continue
46
-
47
- const shippedMatch = section.match(/Shipped:\s*(.+)/i)
48
- const versionMatch = section.match(/Version:\s*(.+)/i)
49
-
50
- const shippedAt = shippedMatch ? shippedMatch[1].trim() : new Date().toISOString()
51
- const version = versionMatch ? versionMatch[1].trim() : '0.0.0'
52
-
53
- // Extract description (lines between name and metadata)
54
- const descLines: string[] = []
55
- for (let i = 1; i < lines.length; i++) {
56
- const line = lines[i].trim()
57
- if (line.startsWith('Shipped:') || line.startsWith('Version:') || !line) break
58
- descLines.push(line)
59
- }
60
-
61
- features.push({
62
- id: `ship_${Date.parse(shippedAt) || Date.now()}`,
63
- name,
64
- shippedAt,
65
- version,
66
- description: descLines.join(' ') || undefined
67
- })
68
- }
69
-
70
- return features
71
- }
72
-
73
- /**
74
- * Serialize ShippedFeature[] to shipped.md format
75
- */
76
- export function serializeShipped(features: ShippedFeature[]): string {
77
- const lines: string[] = ['# SHIPPED 🚀', '']
78
-
79
- if (features.length === 0) {
80
- lines.push('_No features shipped yet._', '')
81
- lines.push('Use `/p:ship` to celebrate your first ship!')
82
- return lines.join('\n')
83
- }
84
-
85
- // Sort by shippedAt descending (newest first)
86
- const sorted = [...features].sort(
87
- (a, b) => new Date(b.shippedAt).getTime() - new Date(a.shippedAt).getTime()
88
- )
89
-
90
- for (const feature of sorted) {
91
- lines.push(`## ${feature.name}`, '')
92
- if (feature.description) {
93
- lines.push(feature.description, '')
94
- }
95
- lines.push(`Shipped: ${feature.shippedAt}`)
96
- lines.push(`Version: ${feature.version}`)
97
- lines.push('')
98
- }
99
-
100
- return lines.join('\n')
101
- }
102
-
103
- /**
104
- * Create empty shipped.md
105
- */
106
- export function createEmptyShippedMd(): string {
107
- return serializeShipped([])
108
- }
@@ -1,136 +0,0 @@
1
- /**
2
- * State Serializer
3
- *
4
- * Parses and serializes now.md for current task state.
5
- *
6
- * MD Format (now.md):
7
- * ```
8
- * # NOW
9
- *
10
- * **Task description here**
11
- *
12
- * Started: 2025-12-10T10:00:00.000Z
13
- * Session: sess_abc123
14
- * Feature: feat_xyz789
15
- * Agent: fe
16
- * ```
17
- */
18
-
19
- import type { StateJson, CurrentTask, PreviousTask } from '../schemas/state'
20
-
21
- /**
22
- * Parse now.md content to StateJson
23
- */
24
- export function parseState(content: string): StateJson {
25
- if (!content || !content.trim()) {
26
- return { currentTask: null, lastUpdated: '' }
27
- }
28
-
29
- const lines = content.split('\n')
30
- let currentTask: CurrentTask | null = null
31
- let previousTask: PreviousTask | null = null
32
-
33
- // Find task description (bold line after # NOW)
34
- let description = ''
35
- for (const line of lines) {
36
- const boldMatch = line.match(/^\*\*(.+)\*\*$/)
37
- if (boldMatch) {
38
- description = boldMatch[1].trim()
39
- break
40
- }
41
- }
42
-
43
- if (!description || description.toLowerCase().includes('no active task')) {
44
- return { currentTask: null, lastUpdated: '' }
45
- }
46
-
47
- // Extract metadata
48
- const startedMatch = content.match(/Started:\s*(.+)/i)
49
- const sessionMatch = content.match(/Session:\s*(.+)/i)
50
- const featureMatch = content.match(/Feature:\s*(.+)/i)
51
- const idMatch = content.match(/ID:\s*(.+)/i)
52
- const agentMatch = content.match(/Agent:\s*(.+)/i)
53
- const pausedMatch = content.match(/Paused:\s*(.+)/i)
54
-
55
- const id = idMatch ? idMatch[1].trim() : `task_${Date.now()}`
56
- const startedAt = startedMatch ? startedMatch[1].trim() : new Date().toISOString()
57
- const sessionId = sessionMatch ? sessionMatch[1].trim() : `sess_${Date.now()}`
58
-
59
- if (pausedMatch) {
60
- // This is a paused task
61
- previousTask = {
62
- id,
63
- description,
64
- status: 'paused',
65
- startedAt,
66
- pausedAt: pausedMatch[1].trim()
67
- }
68
- } else {
69
- // Active task
70
- currentTask = {
71
- id,
72
- description,
73
- startedAt,
74
- sessionId
75
- }
76
-
77
- if (featureMatch) {
78
- currentTask.featureId = featureMatch[1].trim()
79
- }
80
- }
81
-
82
- return {
83
- currentTask,
84
- previousTask,
85
- lastUpdated: startedAt
86
- }
87
- }
88
-
89
- /**
90
- * Serialize StateJson to now.md format
91
- */
92
- export function serializeState(data: StateJson): string {
93
- const lines: string[] = ['# NOW', '']
94
-
95
- if (!data.currentTask && !data.previousTask) {
96
- lines.push('_No active task_', '')
97
- lines.push('Use `/p:now` to start working on something.')
98
- return lines.join('\n')
99
- }
100
-
101
- const task = data.currentTask || data.previousTask
102
-
103
- if (task) {
104
- lines.push(`**${task.description}**`, '')
105
-
106
- if ('pausedAt' in task && task.pausedAt) {
107
- lines.push(`Started: ${task.startedAt}`)
108
- lines.push(`Paused: ${task.pausedAt}`)
109
- } else if (data.currentTask) {
110
- lines.push(`Started: ${data.currentTask.startedAt}`)
111
- lines.push(`Session: ${data.currentTask.sessionId}`)
112
- if (data.currentTask.featureId) {
113
- lines.push(`Feature: ${data.currentTask.featureId}`)
114
- }
115
- }
116
- }
117
-
118
- return lines.join('\n')
119
- }
120
-
121
- /**
122
- * Quick helpers for common operations
123
- */
124
- export function createCurrentTaskMd(task: CurrentTask): string {
125
- return serializeState({
126
- currentTask: task,
127
- lastUpdated: task.startedAt
128
- })
129
- }
130
-
131
- export function createEmptyStateMd(): string {
132
- return serializeState({
133
- currentTask: null,
134
- lastUpdated: ''
135
- })
136
- }
@@ -1,29 +0,0 @@
1
- /**
2
- * Session Types
3
- */
4
-
5
- export interface SessionMetrics {
6
- filesChanged: number
7
- linesAdded: number
8
- linesRemoved: number
9
- commits: number
10
- snapshots: string[]
11
- }
12
-
13
- export interface TimelineEvent {
14
- type: 'start' | 'pause' | 'resume' | 'complete'
15
- at: string
16
- }
17
-
18
- export interface Session {
19
- id: string
20
- projectId: string
21
- task: string
22
- status: 'active' | 'paused' | 'completed'
23
- startedAt: string
24
- pausedAt: string | null
25
- completedAt: string | null
26
- duration: number
27
- metrics: SessionMetrics
28
- timeline: TimelineEvent[]
29
- }