prjct-cli 0.19.0 → 0.20.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 (230) hide show
  1. package/CHANGELOG.md +66 -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/outcomes/analyzer.ts +7 -41
  58. package/core/outcomes/index.ts +1 -1
  59. package/core/outcomes/recorder.ts +1 -1
  60. package/core/{plugins → plugin/builtin}/webhook.ts +6 -22
  61. package/core/plugin/loader.ts +5 -5
  62. package/core/plugin/registry.ts +2 -2
  63. package/core/schemas/ideas.ts +85 -54
  64. package/core/schemas/index.ts +14 -33
  65. package/core/schemas/permissions.ts +177 -0
  66. package/core/schemas/project.ts +39 -12
  67. package/core/schemas/roadmap.ts +94 -59
  68. package/core/schemas/schemas.ts +39 -0
  69. package/core/schemas/shipped.ts +87 -60
  70. package/core/schemas/state.ts +110 -70
  71. package/core/server/index.ts +21 -0
  72. package/core/server/routes.ts +165 -0
  73. package/core/server/server.ts +136 -0
  74. package/core/server/sse.ts +135 -0
  75. package/core/services/agent-service.ts +170 -0
  76. package/core/services/breakdown-service.ts +126 -0
  77. package/core/services/index.ts +21 -0
  78. package/core/services/memory-service.ts +108 -0
  79. package/core/services/project-service.ts +146 -0
  80. package/core/services/skill-service.ts +253 -0
  81. package/core/session/compaction.ts +257 -0
  82. package/core/session/index.ts +20 -8
  83. package/core/{infrastructure/session-manager/migration.ts → session/log-migration.ts} +9 -9
  84. package/core/{infrastructure/session-manager/session-manager.ts → session/session-log-manager.ts} +27 -26
  85. package/core/session/{session-manager.ts → task-session-manager.ts} +7 -4
  86. package/core/session/utils.ts +1 -1
  87. package/core/storage/ideas-storage.ts +10 -26
  88. package/core/storage/index.ts +14 -162
  89. package/core/storage/queue-storage.ts +13 -11
  90. package/core/storage/shipped-storage.ts +4 -17
  91. package/core/storage/state-storage.ts +35 -43
  92. package/core/storage/storage-manager.ts +42 -52
  93. package/core/storage/storage.ts +160 -0
  94. package/core/sync/auth-config.ts +1 -8
  95. package/core/sync/index.ts +17 -10
  96. package/core/sync/oauth-handler.ts +1 -6
  97. package/core/sync/sync-client.ts +6 -34
  98. package/core/sync/sync-manager.ts +11 -40
  99. package/core/types/agentic.ts +577 -0
  100. package/core/types/agents.ts +145 -0
  101. package/core/types/bus.ts +82 -0
  102. package/core/types/commands.ts +366 -0
  103. package/core/types/config.ts +66 -0
  104. package/core/types/core.ts +96 -0
  105. package/core/types/domain.ts +71 -0
  106. package/core/types/events.ts +42 -0
  107. package/core/types/fs.ts +56 -0
  108. package/core/types/index.ts +387 -500
  109. package/core/types/infrastructure.ts +196 -0
  110. package/core/{agentic/memory-system/types.ts → types/memory.ts} +33 -8
  111. package/core/{outcomes/types.ts → types/outcomes.ts} +53 -8
  112. package/core/types/plugin.ts +25 -0
  113. package/core/types/server.ts +54 -0
  114. package/core/types/services.ts +65 -0
  115. package/core/types/session.ts +135 -0
  116. package/core/types/storage.ts +148 -0
  117. package/core/types/sync.ts +121 -0
  118. package/core/types/task.ts +72 -0
  119. package/core/types/template.ts +24 -0
  120. package/core/types/utils.ts +90 -0
  121. package/core/utils/cache.ts +195 -0
  122. package/core/utils/collection-filters.ts +245 -0
  123. package/core/utils/date-helper.ts +1 -5
  124. package/core/utils/file-helper.ts +20 -10
  125. package/core/utils/jsonl-helper.ts +5 -8
  126. package/core/utils/markdown-builder.ts +277 -0
  127. package/core/utils/project-commands.ts +132 -0
  128. package/core/utils/runtime.ts +119 -0
  129. package/dist/bin/prjct.mjs +12568 -0
  130. package/package.json +13 -8
  131. package/scripts/build.js +106 -0
  132. package/scripts/postinstall.js +50 -8
  133. package/templates/agentic/agents/uxui.md +210 -0
  134. package/templates/agentic/subagent-generation.md +1 -1
  135. package/templates/commands/bug.md +219 -41
  136. package/templates/commands/feature.md +368 -80
  137. package/templates/commands/serve.md +118 -0
  138. package/templates/commands/ship.md +152 -14
  139. package/templates/commands/skill.md +110 -0
  140. package/templates/commands/sync.md +63 -4
  141. package/templates/commands/test.md +40 -188
  142. package/templates/mcp-config.json +0 -36
  143. package/templates/permissions/default.jsonc +60 -0
  144. package/templates/permissions/permissive.jsonc +49 -0
  145. package/templates/permissions/strict.jsonc +62 -0
  146. package/templates/skills/code-review.md +47 -0
  147. package/templates/skills/debug.md +61 -0
  148. package/templates/skills/refactor.md +47 -0
  149. package/templates/subagents/domain/devops.md +1 -1
  150. package/templates/subagents/domain/testing.md +6 -10
  151. package/templates/subagents/workflow/prjct-shipper.md +16 -7
  152. package/templates/tools/bash.txt +22 -0
  153. package/templates/tools/edit.txt +18 -0
  154. package/templates/tools/glob.txt +19 -0
  155. package/templates/tools/grep.txt +21 -0
  156. package/templates/tools/read.txt +14 -0
  157. package/templates/tools/task.txt +20 -0
  158. package/templates/tools/webfetch.txt +16 -0
  159. package/templates/tools/websearch.txt +18 -0
  160. package/templates/tools/write.txt +17 -0
  161. package/core/agentic/command-executor/command-executor.ts +0 -312
  162. package/core/agentic/command-executor/index.ts +0 -16
  163. package/core/agentic/command-executor/status-signal.ts +0 -38
  164. package/core/agentic/command-executor/types.ts +0 -79
  165. package/core/agentic/ground-truth/index.ts +0 -76
  166. package/core/agentic/ground-truth/types.ts +0 -33
  167. package/core/agentic/ground-truth/utils.ts +0 -48
  168. package/core/agentic/ground-truth/verifiers/analyze.ts +0 -54
  169. package/core/agentic/ground-truth/verifiers/done.ts +0 -75
  170. package/core/agentic/ground-truth/verifiers/feature.ts +0 -70
  171. package/core/agentic/ground-truth/verifiers/index.ts +0 -37
  172. package/core/agentic/ground-truth/verifiers/init.ts +0 -52
  173. package/core/agentic/ground-truth/verifiers/now.ts +0 -57
  174. package/core/agentic/ground-truth/verifiers/ship.ts +0 -85
  175. package/core/agentic/ground-truth/verifiers/spec.ts +0 -45
  176. package/core/agentic/ground-truth/verifiers/sync.ts +0 -47
  177. package/core/agentic/ground-truth/verifiers.ts +0 -6
  178. package/core/agentic/loop-detector/error-analysis.ts +0 -97
  179. package/core/agentic/loop-detector/hallucination.ts +0 -71
  180. package/core/agentic/loop-detector/index.ts +0 -41
  181. package/core/agentic/loop-detector/loop-detector.ts +0 -222
  182. package/core/agentic/loop-detector/types.ts +0 -66
  183. package/core/agentic/memory-system/history.ts +0 -53
  184. package/core/agentic/memory-system/index.ts +0 -192
  185. package/core/agentic/memory-system/patterns.ts +0 -156
  186. package/core/agentic/memory-system/semantic-memories.ts +0 -278
  187. package/core/agentic/memory-system/session.ts +0 -21
  188. package/core/agentic/plan-mode/approval.ts +0 -57
  189. package/core/agentic/plan-mode/constants.ts +0 -44
  190. package/core/agentic/plan-mode/index.ts +0 -28
  191. package/core/agentic/plan-mode/plan-mode.ts +0 -407
  192. package/core/agentic/plan-mode/types.ts +0 -193
  193. package/core/agents/types.ts +0 -126
  194. package/core/command-registry/categories.ts +0 -23
  195. package/core/command-registry/commands.ts +0 -15
  196. package/core/command-registry/core-commands.ts +0 -344
  197. package/core/command-registry/index.ts +0 -158
  198. package/core/command-registry/optional-commands.ts +0 -163
  199. package/core/command-registry/setup-commands.ts +0 -83
  200. package/core/command-registry/types.ts +0 -59
  201. package/core/command-registry.ts +0 -9
  202. package/core/commands/types.ts +0 -185
  203. package/core/commands.ts +0 -11
  204. package/core/constants/formats.ts +0 -187
  205. package/core/context-sync.ts +0 -18
  206. package/core/data/index.ts +0 -27
  207. package/core/data/md-base-manager.ts +0 -203
  208. package/core/data/md-ideas-manager.ts +0 -155
  209. package/core/data/md-queue-manager.ts +0 -180
  210. package/core/data/md-shipped-manager.ts +0 -90
  211. package/core/data/md-state-manager.ts +0 -137
  212. package/core/domain/task-stack/index.ts +0 -19
  213. package/core/domain/task-stack/parser.ts +0 -86
  214. package/core/domain/task-stack/storage.ts +0 -123
  215. package/core/domain/task-stack/task-stack.ts +0 -340
  216. package/core/domain/task-stack/types.ts +0 -51
  217. package/core/infrastructure/command-installer/command-installer.ts +0 -327
  218. package/core/infrastructure/command-installer/global-config.ts +0 -136
  219. package/core/infrastructure/command-installer/index.ts +0 -25
  220. package/core/infrastructure/command-installer/types.ts +0 -41
  221. package/core/infrastructure/session-manager/index.ts +0 -23
  222. package/core/infrastructure/session-manager/types.ts +0 -45
  223. package/core/infrastructure/session-manager.ts +0 -8
  224. package/core/serializers/ideas-serializer.ts +0 -187
  225. package/core/serializers/index.ts +0 -36
  226. package/core/serializers/queue-serializer.ts +0 -210
  227. package/core/serializers/shipped-serializer.ts +0 -108
  228. package/core/serializers/state-serializer.ts +0 -136
  229. package/core/session/types.ts +0 -29
  230. /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
- }