prjct-cli 0.28.1 → 0.28.3

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 (45) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/core/agentic/index.ts +11 -1
  3. package/core/agentic/prompt-builder.ts +2 -2
  4. package/core/agentic/token-estimator.ts +264 -0
  5. package/core/commands/command-data.ts +0 -33
  6. package/core/commands/commands.ts +4 -12
  7. package/core/commands/registry.ts +0 -37
  8. package/core/index.ts +0 -2
  9. package/core/infrastructure/setup.ts +28 -0
  10. package/core/infrastructure/slash-command-registry.ts +176 -0
  11. package/core/types/config.ts +1 -1
  12. package/core/types/index.ts +0 -2
  13. package/core/types/integrations.ts +22 -40
  14. package/core/types/storage.ts +0 -8
  15. package/core/types/task.ts +0 -4
  16. package/dist/bin/prjct.mjs +3 -82
  17. package/package.json +1 -1
  18. package/templates/agentic/subagent-generation.md +241 -81
  19. package/templates/commands/init.md +1 -44
  20. package/templates/commands/ship.md +106 -81
  21. package/templates/commands/sync.md +154 -402
  22. package/templates/commands/task.md +14 -31
  23. package/templates/global/CLAUDE.md +166 -35
  24. package/templates/guides/agent-generation.md +164 -0
  25. package/templates/guides/integrations.md +149 -0
  26. package/templates/mcp-config.json +23 -46
  27. package/templates/shared/validation.md +75 -0
  28. package/CLAUDE.md +0 -170
  29. package/core/integrations/notion/client.ts +0 -413
  30. package/core/integrations/notion/index.ts +0 -46
  31. package/core/integrations/notion/setup.ts +0 -235
  32. package/core/integrations/notion/sync.ts +0 -818
  33. package/core/integrations/notion/templates.ts +0 -246
  34. package/core/plugin/builtin/notion.ts +0 -178
  35. package/templates/agentic/agents/uxui.md +0 -218
  36. package/templates/commands/feature.md +0 -46
  37. package/templates/commands/now.md +0 -53
  38. package/templates/skills/notion-push.md +0 -116
  39. package/templates/skills/notion-setup.md +0 -199
  40. package/templates/skills/notion-sync.md +0 -290
  41. package/templates/subagents/domain/backend.md +0 -106
  42. package/templates/subagents/domain/database.md +0 -118
  43. package/templates/subagents/domain/devops.md +0 -149
  44. package/templates/subagents/domain/frontend.md +0 -100
  45. package/templates/subagents/domain/testing.md +0 -166
package/CHANGELOG.md CHANGED
@@ -1,5 +1,98 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.28.3] - 2026-01-11
4
+
5
+ ### Feature: Claude Code Synergy Optimization
6
+
7
+ Major improvements to maximize prjct + Claude Code integration efficiency.
8
+
9
+ **Skill Auto-Invocation (Phase 4 of /p:task):**
10
+ - Skills are now automatically invoked when loading agents
11
+ - Agent frontmatter `skills: [skill-name]` triggers `Skill("skill-name")`
12
+ - Example: Loading `frontend.md` auto-activates `/frontend-design`
13
+
14
+ **MCP Auto-Usage:**
15
+ - Agents with `mcp: [context7]` auto-query documentation during tasks
16
+ - Per-project MCP config at `{globalPath}/config/mcp-servers.json`
17
+ - Seamless library documentation lookup during implementation
18
+
19
+ **Think Blocks for Destructive Commands:**
20
+ - `/p:ship` now includes mandatory `<think>` block
21
+ - Pre-ship verification checklist (completeness, quality, git state)
22
+ - Prevents shipping incomplete or broken code
23
+
24
+ **Agent Auto-Refresh in /p:sync:**
25
+ - Detects when dependencies change (package.json, etc.)
26
+ - Regenerates stale agents (>7 days old)
27
+ - Versions previous agents as `.backup` files
28
+ - Logs refresh events to memory
29
+
30
+ **Slash Command Registration:**
31
+ - New `slash-commands.json` config for Claude Code integration
32
+ - Enables command discovery and validation
33
+ - Path: `{globalPath}/config/slash-commands.json`
34
+
35
+ **Token Budget Analysis:**
36
+ - Estimates context token usage during sync
37
+ - Warns if approaching 80% of budget (160k tokens)
38
+ - Auto-summarizes large agents if needed
39
+
40
+ **Client Migration on Update:**
41
+ - Setup now creates missing config files for existing projects
42
+ - `slash-commands.json` auto-created for existing installations
43
+ - Seamless upgrade experience for npm update users
44
+
45
+ **New TypeScript Modules:**
46
+ - `core/infrastructure/slash-command-registry.ts` - Command validation
47
+ - `core/agentic/token-estimator.ts` - Token budget estimation
48
+
49
+ **Template Context Optimization:**
50
+ - Reduced main templates from 3,197 to 2,214 lines (-31%)
51
+ - `sync.md`: 1,602 → 835 lines (-48%)
52
+ - `global/CLAUDE.md`: 429 → 363 lines (-15%)
53
+ - `task.md`: 397 → 336 lines (-15%)
54
+ - `ship.md`: 769 → 680 lines (-12%)
55
+ - New `templates/guides/` for on-demand documentation
56
+ - New `templates/shared/validation.md` for reusable validation
57
+
58
+ **Files Removed (Agentic Generation):**
59
+ - Deleted hardcoded domain agents from `templates/subagents/domain/`
60
+ - Deleted `.mcp.json` (now generated per-project)
61
+ - Deleted `CLAUDE.md` from root (moved to templates/global/)
62
+
63
+ ---
64
+
65
+ ## [0.28.2] - 2026-01-10
66
+
67
+ ### Feature: Agent Mentions and Major Cleanup
68
+
69
+ **Agent Mentions (p.agent.{name}):**
70
+ - New `agentId` field in agent frontmatter for identification
71
+ - Users can invoke agents in prompts: `p.agent.backend help me...`
72
+ - Format: `p.agent.workflow`, `p.agent.planner`, `p.agent.frontend`, etc.
73
+
74
+ **Deprecated Commands Removed:**
75
+ - `/p:now` - Use `/p:task` instead
76
+ - `/p:feature` - Use `/p:task` instead
77
+ - `/p:work` - Use `/p:task` instead
78
+
79
+ **Notion Integration Removed:**
80
+ - Removed entire `core/integrations/notion/` directory
81
+ - Removed Notion skills and MCP config
82
+ - Cleaned up type references
83
+
84
+ **Code Cleanup:**
85
+ - Removed `executeWithoutProject()` deprecated method
86
+ - Cleaned dead code in prompt-builder arrays
87
+ - Removed ARCHITECTURE.md (outdated)
88
+
89
+ **Ship Template Enhancement:**
90
+ - Version bump now REQUIRED before PR creation
91
+ - CHANGELOG.md update now REQUIRED before PR creation
92
+ - Better categorization of changes (Added/Fixed/Changed)
93
+
94
+ ---
95
+
3
96
  ## [0.28.1] - 2026-01-10
4
97
 
5
98
  ### Feature: @ Agent Mentions
@@ -90,9 +90,19 @@ export { default as toolRegistry } from './tool-registry'
90
90
  export { default as templateLoader } from './template-loader'
91
91
 
92
92
  // ============ Utilities ============
93
- // Chain of thought, services
93
+ // Chain of thought, services, token estimation
94
94
  export { default as chainOfThought } from './chain-of-thought'
95
95
  export { default as services } from './services'
96
+ export {
97
+ default as tokenEstimator,
98
+ estimateTokens,
99
+ getTokenBudget,
100
+ estimateContext,
101
+ filterContext,
102
+ summarizeForTokens,
103
+ createContextSections,
104
+ formatEstimate,
105
+ } from './token-estimator'
96
106
 
97
107
  // ============ Types ============
98
108
  // All types re-exported from ../types (canonical source)
@@ -283,7 +283,7 @@ class PromptBuilder {
283
283
 
284
284
  // Agent assignment (CONDITIONAL - only for code-modifying commands)
285
285
  const commandName = template.frontmatter?.name?.replace('p:', '') || ''
286
- const agentCommands = ['now', 'build', 'feature', 'design', 'fix', 'bug', 'test', 'work', 'cleanup', 'spec']
286
+ const agentCommands = ['task', 'design', 'fix', 'bug', 'test', 'cleanup', 'spec']
287
287
  const needsAgent = agentCommands.includes(commandName)
288
288
 
289
289
  if (agent && needsAgent) {
@@ -332,7 +332,7 @@ class PromptBuilder {
332
332
  }
333
333
 
334
334
  // OPTIMIZED: Only include patterns for code-modifying commands
335
- const codeCommands = ['now', 'build', 'feature', 'design', 'cleanup', 'fix', 'bug', 'test', 'init', 'spec', 'work']
335
+ const codeCommands = ['task', 'design', 'cleanup', 'fix', 'bug', 'test', 'init', 'spec']
336
336
  const needsPatterns = codeCommands.includes(commandName)
337
337
 
338
338
  // Include code patterns analysis for code-modifying commands
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Token Budget Estimator
3
+ * Estimates token usage and provides context filtering for large projects.
4
+ *
5
+ * This prevents context overflow by:
6
+ * - Estimating token count before sending to Claude
7
+ * - Prioritizing critical context over nice-to-have
8
+ * - Truncating or summarizing when needed
9
+ *
10
+ * @version 1.0.0
11
+ */
12
+
13
+ export interface TokenEstimate {
14
+ total: number
15
+ breakdown: {
16
+ projectContext: number
17
+ agentContext: number
18
+ skillContext: number
19
+ userPrompt: number
20
+ systemPrompt: number
21
+ }
22
+ withinBudget: boolean
23
+ recommendations: string[]
24
+ }
25
+
26
+ export interface ContextSection {
27
+ name: string
28
+ content: string
29
+ priority: 'critical' | 'high' | 'medium' | 'low'
30
+ tokens: number
31
+ }
32
+
33
+ /**
34
+ * Approximate tokens per character ratio
35
+ * Claude uses ~4 characters per token on average for English text
36
+ * Code tends to be slightly more efficient (~3.5 chars/token)
37
+ */
38
+ const CHARS_PER_TOKEN = 3.8
39
+
40
+ /**
41
+ * Default token budgets by model
42
+ */
43
+ const TOKEN_BUDGETS = {
44
+ 'claude-3-opus': 200000,
45
+ 'claude-3-sonnet': 200000,
46
+ 'claude-3-haiku': 200000,
47
+ 'claude-3.5-sonnet': 200000,
48
+ 'claude-opus-4': 200000,
49
+ default: 100000, // Conservative default
50
+ }
51
+
52
+ /**
53
+ * Estimate tokens from string content
54
+ */
55
+ export function estimateTokens(content: string): number {
56
+ if (!content) return 0
57
+ return Math.ceil(content.length / CHARS_PER_TOKEN)
58
+ }
59
+
60
+ /**
61
+ * Get token budget for a model
62
+ */
63
+ export function getTokenBudget(model: string = 'default'): number {
64
+ return TOKEN_BUDGETS[model as keyof typeof TOKEN_BUDGETS] || TOKEN_BUDGETS.default
65
+ }
66
+
67
+ /**
68
+ * Estimate total context tokens
69
+ */
70
+ export function estimateContext(sections: ContextSection[]): TokenEstimate {
71
+ const breakdown = {
72
+ projectContext: 0,
73
+ agentContext: 0,
74
+ skillContext: 0,
75
+ userPrompt: 0,
76
+ systemPrompt: 0,
77
+ }
78
+
79
+ let total = 0
80
+
81
+ for (const section of sections) {
82
+ const tokens = estimateTokens(section.content)
83
+ section.tokens = tokens
84
+ total += tokens
85
+
86
+ // Categorize for breakdown
87
+ if (section.name.includes('agent')) {
88
+ breakdown.agentContext += tokens
89
+ } else if (section.name.includes('skill')) {
90
+ breakdown.skillContext += tokens
91
+ } else if (section.name.includes('user') || section.name.includes('prompt')) {
92
+ breakdown.userPrompt += tokens
93
+ } else if (section.name.includes('system')) {
94
+ breakdown.systemPrompt += tokens
95
+ } else {
96
+ breakdown.projectContext += tokens
97
+ }
98
+ }
99
+
100
+ const budget = getTokenBudget()
101
+ const withinBudget = total < budget * 0.8 // Leave 20% buffer for response
102
+
103
+ const recommendations: string[] = []
104
+
105
+ if (!withinBudget) {
106
+ recommendations.push(`Context exceeds safe limit (${total} tokens vs ${Math.floor(budget * 0.8)} budget)`)
107
+
108
+ // Find sections that can be reduced
109
+ const lowPriority = sections.filter(s => s.priority === 'low')
110
+ if (lowPriority.length > 0) {
111
+ const lowTokens = lowPriority.reduce((sum, s) => sum + s.tokens, 0)
112
+ recommendations.push(`Remove low-priority sections to save ~${lowTokens} tokens`)
113
+ }
114
+
115
+ const mediumPriority = sections.filter(s => s.priority === 'medium')
116
+ if (mediumPriority.length > 0) {
117
+ const medTokens = mediumPriority.reduce((sum, s) => sum + s.tokens, 0)
118
+ recommendations.push(`Consider summarizing medium-priority sections (~${medTokens} tokens)`)
119
+ }
120
+ }
121
+
122
+ return {
123
+ total,
124
+ breakdown,
125
+ withinBudget,
126
+ recommendations,
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Filter context to fit within budget
132
+ */
133
+ export function filterContext(
134
+ sections: ContextSection[],
135
+ maxTokens?: number
136
+ ): { filtered: ContextSection[]; removed: string[]; totalTokens: number } {
137
+ const budget = maxTokens || getTokenBudget() * 0.8
138
+
139
+ // Sort by priority (critical first)
140
+ const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 }
141
+ const sorted = [...sections].sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority])
142
+
143
+ const filtered: ContextSection[] = []
144
+ const removed: string[] = []
145
+ let totalTokens = 0
146
+
147
+ for (const section of sorted) {
148
+ const sectionTokens = estimateTokens(section.content)
149
+
150
+ if (totalTokens + sectionTokens <= budget) {
151
+ filtered.push({ ...section, tokens: sectionTokens })
152
+ totalTokens += sectionTokens
153
+ } else if (section.priority === 'critical') {
154
+ // Critical sections are always included, even if over budget
155
+ filtered.push({ ...section, tokens: sectionTokens })
156
+ totalTokens += sectionTokens
157
+ } else {
158
+ removed.push(section.name)
159
+ }
160
+ }
161
+
162
+ return { filtered, removed, totalTokens }
163
+ }
164
+
165
+ /**
166
+ * Summarize content to reduce tokens
167
+ */
168
+ export function summarizeForTokens(content: string, targetTokens: number): string {
169
+ const currentTokens = estimateTokens(content)
170
+
171
+ if (currentTokens <= targetTokens) {
172
+ return content
173
+ }
174
+
175
+ // Calculate target character count
176
+ const targetChars = targetTokens * CHARS_PER_TOKEN
177
+
178
+ // Split into lines and take priority lines
179
+ const lines = content.split('\n')
180
+
181
+ // Keep headers and first lines of sections
182
+ const priorityLines: string[] = []
183
+ let charCount = 0
184
+
185
+ for (const line of lines) {
186
+ const isHeader = line.startsWith('#') || line.startsWith('**')
187
+ const isImportant = line.includes('CRITICAL') || line.includes('IMPORTANT') || line.includes('TODO')
188
+
189
+ if (isHeader || isImportant) {
190
+ priorityLines.push(line)
191
+ charCount += line.length + 1
192
+ } else if (charCount < targetChars * 0.8) {
193
+ priorityLines.push(line)
194
+ charCount += line.length + 1
195
+ }
196
+
197
+ if (charCount >= targetChars) {
198
+ break
199
+ }
200
+ }
201
+
202
+ if (priorityLines.length < lines.length) {
203
+ priorityLines.push('')
204
+ priorityLines.push(`[... ${lines.length - priorityLines.length} lines truncated for context limit ...]`)
205
+ }
206
+
207
+ return priorityLines.join('\n')
208
+ }
209
+
210
+ /**
211
+ * Create context sections from project state
212
+ */
213
+ export function createContextSections(
214
+ projectContext: string,
215
+ agentContext: string,
216
+ skillContext: string,
217
+ userPrompt: string
218
+ ): ContextSection[] {
219
+ return [
220
+ { name: 'user-prompt', content: userPrompt, priority: 'critical', tokens: 0 },
221
+ { name: 'agent-context', content: agentContext, priority: 'high', tokens: 0 },
222
+ { name: 'project-context', content: projectContext, priority: 'medium', tokens: 0 },
223
+ { name: 'skill-context', content: skillContext, priority: 'medium', tokens: 0 },
224
+ ]
225
+ }
226
+
227
+ /**
228
+ * Format token estimate for display
229
+ */
230
+ export function formatEstimate(estimate: TokenEstimate): string {
231
+ const lines = [
232
+ '📊 Token Budget',
233
+ '',
234
+ `Total: ${estimate.total.toLocaleString()} tokens`,
235
+ '',
236
+ 'Breakdown:',
237
+ ` Project: ${estimate.breakdown.projectContext.toLocaleString()}`,
238
+ ` Agents: ${estimate.breakdown.agentContext.toLocaleString()}`,
239
+ ` Skills: ${estimate.breakdown.skillContext.toLocaleString()}`,
240
+ ` Prompt: ${estimate.breakdown.userPrompt.toLocaleString()}`,
241
+ '',
242
+ `Status: ${estimate.withinBudget ? '✅ Within budget' : '⚠️ Over budget'}`,
243
+ ]
244
+
245
+ if (estimate.recommendations.length > 0) {
246
+ lines.push('')
247
+ lines.push('Recommendations:')
248
+ for (const rec of estimate.recommendations) {
249
+ lines.push(` - ${rec}`)
250
+ }
251
+ }
252
+
253
+ return lines.join('\n')
254
+ }
255
+
256
+ export default {
257
+ estimateTokens,
258
+ getTokenBudget,
259
+ estimateContext,
260
+ filterContext,
261
+ summarizeForTokens,
262
+ createContextSections,
263
+ formatEstimate,
264
+ }
@@ -62,17 +62,6 @@ export const COMMANDS: CommandMeta[] = [
62
62
  requiresProject: true,
63
63
  features: ['Agentic type classification', '7-phase workflow', 'Git branch management', 'Task breakdown'],
64
64
  },
65
- {
66
- name: 'feature',
67
- group: 'core',
68
- description: 'DEPRECATED - Use /p:task instead',
69
- usage: { claude: '/p:task "<description>"', terminal: 'prjct task "<description>"' },
70
- params: '<description>',
71
- implemented: true,
72
- hasTemplate: true,
73
- requiresProject: true,
74
- deprecated: true,
75
- },
76
65
  {
77
66
  name: 'spec',
78
67
  group: 'core',
@@ -83,28 +72,6 @@ export const COMMANDS: CommandMeta[] = [
83
72
  hasTemplate: true,
84
73
  requiresProject: true,
85
74
  },
86
- {
87
- name: 'now',
88
- group: 'core',
89
- description: 'DEPRECATED - Use /p:task instead',
90
- usage: { claude: '/p:task "<description>"', terminal: 'prjct task "<description>"' },
91
- params: '[task]',
92
- implemented: true,
93
- hasTemplate: true,
94
- requiresProject: true,
95
- deprecated: true,
96
- },
97
- {
98
- name: 'work',
99
- group: 'core',
100
- description: 'DEPRECATED - Use /p:task instead',
101
- usage: { claude: '/p:task "<description>"', terminal: 'prjct task "<description>"' },
102
- params: '[task]',
103
- implemented: true,
104
- hasTemplate: true,
105
- requiresProject: true,
106
- deprecated: true,
107
- },
108
75
  {
109
76
  name: 'pause',
110
77
  group: 'core',
@@ -3,12 +3,12 @@
3
3
  *
4
4
  * MD-First Architecture - All state in Markdown files.
5
5
  *
6
- * COMMANDS (22 total):
7
- * - Workflow (5): work, done, next, pause, resume
8
- * - Planning (5): init, feature, bug, idea, spec
6
+ * COMMANDS (20 total):
7
+ * - Workflow (4): done, next, pause, resume
8
+ * - Planning (4): init, bug, idea, spec
9
9
  * - Shipping (1): ship
10
10
  * - Analytics (2): dash, help
11
- * - Maintenance (5): cleanup, design, recover, undo, redo, history
11
+ * - Maintenance (6): cleanup, design, recover, undo, redo, history
12
12
  * - Analysis (2): analyze, sync
13
13
  * - Setup (3): start, setup, migrateAll
14
14
  */
@@ -69,10 +69,6 @@ class PrjctCommands {
69
69
 
70
70
  // ========== Workflow Commands ==========
71
71
 
72
- async work(task: string | null = null, projectPath: string = process.cwd()): Promise<CommandResult> {
73
- return this.workflow.now(task, projectPath)
74
- }
75
-
76
72
  async done(projectPath: string = process.cwd()): Promise<CommandResult> {
77
73
  return this.workflow.done(projectPath)
78
74
  }
@@ -95,10 +91,6 @@ class PrjctCommands {
95
91
  return this.planning.init(idea, projectPath)
96
92
  }
97
93
 
98
- async feature(description: string, projectPath: string = process.cwd()): Promise<CommandResult> {
99
- return this.planning.feature(description, projectPath)
100
- }
101
-
102
94
  async bug(description: string, projectPath: string = process.cwd()): Promise<CommandResult> {
103
95
  return this.planning.bug(description, projectPath)
104
96
  }
@@ -388,43 +388,6 @@ export class CommandRegistry {
388
388
  }
389
389
  }
390
390
 
391
- /**
392
- * Execute without requiring project (for init, setup commands)
393
- * @deprecated Use execute() - it auto-detects based on command metadata
394
- */
395
- async executeWithoutProject<TParams = void>(
396
- name: string,
397
- params: TParams,
398
- projectPath: string = process.cwd()
399
- ): Promise<CommandResult> {
400
- const handler = this.handlers.get(name)
401
- if (handler) {
402
- const context: ExecutionContext = {
403
- projectId: '',
404
- projectPath,
405
- globalPath: '',
406
- timestamp: getTimestamp(),
407
- }
408
- return handler.execute(params, context)
409
- }
410
-
411
- const handlerFn = this.handlerFns.get(name)
412
- if (handlerFn) {
413
- const context: ExecutionContext = {
414
- projectId: '',
415
- projectPath,
416
- globalPath: '',
417
- timestamp: getTimestamp(),
418
- }
419
- return handlerFn(params, context)
420
- }
421
-
422
- return {
423
- success: false,
424
- error: `Command not found: ${name}`,
425
- }
426
- }
427
-
428
391
  /**
429
392
  * Clear all registrations (useful for testing)
430
393
  */
package/core/index.ts CHANGED
@@ -97,14 +97,12 @@ async function main(): Promise<void> {
97
97
  const param = parsedArgs.join(' ') || null
98
98
  const standardCommands: Record<string, (p: string | null) => Promise<CommandResult>> = {
99
99
  // Core workflow
100
- work: (p) => commands.work(p),
101
100
  done: () => commands.done(),
102
101
  next: () => commands.next(),
103
102
  pause: (p) => commands.pause(p || ''),
104
103
  resume: (p) => commands.resume(p),
105
104
  // Planning
106
105
  init: (p) => commands.init(p),
107
- feature: (p) => commands.feature(p || ''),
108
106
  bug: (p) => commands.bug(p || ''),
109
107
  idea: (p) => commands.idea(p || ''),
110
108
  spec: (p) => commands.spec(p),
@@ -146,9 +146,34 @@ async function migrateProjectsCliVersion(): Promise<void> {
146
146
  .map(dirent => dirent.name)
147
147
 
148
148
  let migrated = 0
149
+ let configsCreated = 0
149
150
 
150
151
  for (const projectId of projectDirs) {
151
152
  const projectJsonPath = path.join(projectsDir, projectId, 'project.json')
153
+ const configDir = path.join(projectsDir, projectId, 'config')
154
+
155
+ // Ensure config directory exists for new config files
156
+ if (!fs.existsSync(configDir)) {
157
+ fs.mkdirSync(configDir, { recursive: true })
158
+ }
159
+
160
+ // Create slash-commands.json if missing (v0.28+ feature)
161
+ const slashCommandsPath = path.join(configDir, 'slash-commands.json')
162
+ if (!fs.existsSync(slashCommandsPath)) {
163
+ const slashCommandsConfig = {
164
+ version: '1.0.0',
165
+ generatedAt: new Date().toISOString(),
166
+ note: 'Run /p:sync to regenerate with full command list',
167
+ commands: {
168
+ 'p:task': { description: 'Start any task', category: 'workflow' },
169
+ 'p:done': { description: 'Complete subtask', category: 'workflow' },
170
+ 'p:ship': { description: 'Ship feature', category: 'shipping' },
171
+ 'p:sync': { description: 'Sync project', category: 'planning' },
172
+ }
173
+ }
174
+ fs.writeFileSync(slashCommandsPath, JSON.stringify(slashCommandsConfig, null, 2))
175
+ configsCreated++
176
+ }
152
177
 
153
178
  if (!fs.existsSync(projectJsonPath)) {
154
179
  continue
@@ -172,6 +197,9 @@ async function migrateProjectsCliVersion(): Promise<void> {
172
197
  if (migrated > 0) {
173
198
  console.log(` ${GREEN}✓${NC} Updated ${migrated} project(s) to v${VERSION}`)
174
199
  }
200
+ if (configsCreated > 0) {
201
+ console.log(` ${GREEN}✓${NC} Created ${configsCreated} new config file(s)`)
202
+ }
175
203
  } catch {
176
204
  // Silently fail - migration is optional
177
205
  }