prjct-cli 0.28.2 → 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.
- package/CHANGELOG.md +62 -0
- package/core/agentic/index.ts +11 -1
- package/core/agentic/token-estimator.ts +264 -0
- package/core/infrastructure/setup.ts +28 -0
- package/core/infrastructure/slash-command-registry.ts +176 -0
- package/core/types/integrations.ts +28 -1
- package/package.json +1 -1
- package/templates/agentic/subagent-generation.md +237 -90
- package/templates/commands/ship.md +19 -69
- package/templates/commands/sync.md +153 -416
- package/templates/commands/task.md +14 -31
- package/templates/global/CLAUDE.md +166 -35
- package/templates/guides/agent-generation.md +164 -0
- package/templates/guides/integrations.md +149 -0
- package/templates/mcp-config.json +23 -18
- package/templates/shared/validation.md +75 -0
- package/CLAUDE.md +0 -204
- package/templates/agentic/agents/uxui.md +0 -218
- package/templates/subagents/domain/backend.md +0 -106
- package/templates/subagents/domain/database.md +0 -118
- package/templates/subagents/domain/devops.md +0 -149
- package/templates/subagents/domain/frontend.md +0 -100
- package/templates/subagents/domain/testing.md +0 -166
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,67 @@
|
|
|
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
|
+
|
|
3
65
|
## [0.28.2] - 2026-01-10
|
|
4
66
|
|
|
5
67
|
### Feature: Agent Mentions and Major Cleanup
|
package/core/agentic/index.ts
CHANGED
|
@@ -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)
|
|
@@ -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
|
+
}
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash Command Registry
|
|
3
|
+
* Generates Claude Code native slash command configuration.
|
|
4
|
+
*
|
|
5
|
+
* This enables:
|
|
6
|
+
* - Native /p:* command autocomplete in Claude Code
|
|
7
|
+
* - Command validation before execution
|
|
8
|
+
* - Command discoverability in help systems
|
|
9
|
+
*
|
|
10
|
+
* @version 1.0.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'fs/promises'
|
|
14
|
+
import path from 'path'
|
|
15
|
+
import os from 'os'
|
|
16
|
+
|
|
17
|
+
export interface SlashCommand {
|
|
18
|
+
name: string
|
|
19
|
+
description: string
|
|
20
|
+
args?: string
|
|
21
|
+
category: 'workflow' | 'planning' | 'shipping' | 'analytics' | 'maintenance'
|
|
22
|
+
requiresProject: boolean
|
|
23
|
+
deprecated?: boolean
|
|
24
|
+
replacedBy?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface SlashCommandRegistry {
|
|
28
|
+
version: string
|
|
29
|
+
generatedAt: string
|
|
30
|
+
commands: Record<string, SlashCommand>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Core prjct commands with metadata
|
|
35
|
+
*/
|
|
36
|
+
const PRJCT_COMMANDS: SlashCommand[] = [
|
|
37
|
+
// Workflow
|
|
38
|
+
{ name: 'task', description: 'Start any task with intelligent classification', args: '<description>', category: 'workflow', requiresProject: true },
|
|
39
|
+
{ name: 'done', description: 'Complete current subtask', category: 'workflow', requiresProject: true },
|
|
40
|
+
{ name: 'pause', description: 'Pause current task', category: 'workflow', requiresProject: true },
|
|
41
|
+
{ name: 'resume', description: 'Resume paused task', category: 'workflow', requiresProject: true },
|
|
42
|
+
{ name: 'next', description: 'Show next tasks in queue', category: 'workflow', requiresProject: true },
|
|
43
|
+
|
|
44
|
+
// Planning
|
|
45
|
+
{ name: 'init', description: 'Initialize prjct in current directory', args: '[description]', category: 'planning', requiresProject: false },
|
|
46
|
+
{ name: 'sync', description: 'Deep sync - analyze project, generate agents', category: 'planning', requiresProject: true },
|
|
47
|
+
{ name: 'idea', description: 'Capture an idea for later', args: '<idea>', category: 'planning', requiresProject: true },
|
|
48
|
+
{ name: 'spec', description: 'Generate feature specification', args: '<feature>', category: 'planning', requiresProject: true },
|
|
49
|
+
{ name: 'bug', description: 'Report a bug with auto-priority', args: '<description>', category: 'planning', requiresProject: true },
|
|
50
|
+
|
|
51
|
+
// Shipping
|
|
52
|
+
{ name: 'ship', description: 'Ship feature with PR workflow', args: '[feature]', category: 'shipping', requiresProject: true },
|
|
53
|
+
{ name: 'review', description: 'Run code review on changes', category: 'shipping', requiresProject: true },
|
|
54
|
+
{ name: 'test', description: 'Run tests for current changes', category: 'shipping', requiresProject: true },
|
|
55
|
+
{ name: 'verify', description: 'Verify deployment', category: 'shipping', requiresProject: true },
|
|
56
|
+
|
|
57
|
+
// Analytics
|
|
58
|
+
{ name: 'dash', description: 'Show project dashboard', category: 'analytics', requiresProject: true },
|
|
59
|
+
{ name: 'history', description: 'Show task history', category: 'analytics', requiresProject: true },
|
|
60
|
+
{ name: 'analyze', description: 'Analyze codebase', category: 'analytics', requiresProject: true },
|
|
61
|
+
|
|
62
|
+
// Maintenance
|
|
63
|
+
{ name: 'cleanup', description: 'Clean up project files', category: 'maintenance', requiresProject: true },
|
|
64
|
+
{ name: 'undo', description: 'Undo last action', category: 'maintenance', requiresProject: true },
|
|
65
|
+
{ name: 'redo', description: 'Redo undone action', category: 'maintenance', requiresProject: true },
|
|
66
|
+
|
|
67
|
+
// Deprecated
|
|
68
|
+
{ name: 'now', description: 'Start task (deprecated)', args: '<task>', category: 'workflow', requiresProject: true, deprecated: true, replacedBy: 'task' },
|
|
69
|
+
{ name: 'feature', description: 'Plan feature (deprecated)', args: '<feature>', category: 'planning', requiresProject: true, deprecated: true, replacedBy: 'task' },
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generate slash command registry for Claude Code
|
|
74
|
+
*/
|
|
75
|
+
export async function generateRegistry(): Promise<SlashCommandRegistry> {
|
|
76
|
+
const commands: Record<string, SlashCommand> = {}
|
|
77
|
+
|
|
78
|
+
for (const cmd of PRJCT_COMMANDS) {
|
|
79
|
+
commands[`p:${cmd.name}`] = cmd
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
version: '1.0.0',
|
|
84
|
+
generatedAt: new Date().toISOString(),
|
|
85
|
+
commands,
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Write registry to project's global path
|
|
91
|
+
*/
|
|
92
|
+
export async function writeRegistry(projectId: string): Promise<{ success: boolean; path?: string; error?: string }> {
|
|
93
|
+
try {
|
|
94
|
+
const globalPath = path.join(os.homedir(), '.prjct-cli', 'projects', projectId, 'config')
|
|
95
|
+
await fs.mkdir(globalPath, { recursive: true })
|
|
96
|
+
|
|
97
|
+
const registry = await generateRegistry()
|
|
98
|
+
const registryPath = path.join(globalPath, 'slash-commands.json')
|
|
99
|
+
|
|
100
|
+
await fs.writeFile(registryPath, JSON.stringify(registry, null, 2), 'utf-8')
|
|
101
|
+
|
|
102
|
+
return { success: true, path: registryPath }
|
|
103
|
+
} catch (error) {
|
|
104
|
+
return { success: false, error: (error as Error).message }
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get command by name
|
|
110
|
+
*/
|
|
111
|
+
export function getCommand(name: string): SlashCommand | undefined {
|
|
112
|
+
return PRJCT_COMMANDS.find(cmd => cmd.name === name)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Get all commands by category
|
|
117
|
+
*/
|
|
118
|
+
export function getCommandsByCategory(category: SlashCommand['category']): SlashCommand[] {
|
|
119
|
+
return PRJCT_COMMANDS.filter(cmd => cmd.category === category && !cmd.deprecated)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Validate command exists
|
|
124
|
+
*/
|
|
125
|
+
export function validateCommand(name: string): { valid: boolean; command?: SlashCommand; error?: string } {
|
|
126
|
+
const cmd = getCommand(name)
|
|
127
|
+
|
|
128
|
+
if (!cmd) {
|
|
129
|
+
return { valid: false, error: `Unknown command: ${name}` }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (cmd.deprecated) {
|
|
133
|
+
return {
|
|
134
|
+
valid: true,
|
|
135
|
+
command: cmd,
|
|
136
|
+
error: `Command '${name}' is deprecated. Use '${cmd.replacedBy}' instead.`,
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return { valid: true, command: cmd }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Format commands for help display
|
|
145
|
+
*/
|
|
146
|
+
export function formatHelpText(): string {
|
|
147
|
+
const categories = ['workflow', 'planning', 'shipping', 'analytics', 'maintenance'] as const
|
|
148
|
+
const lines: string[] = ['# prjct Commands', '']
|
|
149
|
+
|
|
150
|
+
for (const category of categories) {
|
|
151
|
+
const cmds = getCommandsByCategory(category)
|
|
152
|
+
if (cmds.length === 0) continue
|
|
153
|
+
|
|
154
|
+
lines.push(`## ${category.charAt(0).toUpperCase() + category.slice(1)}`)
|
|
155
|
+
lines.push('')
|
|
156
|
+
|
|
157
|
+
for (const cmd of cmds) {
|
|
158
|
+
const args = cmd.args ? ` ${cmd.args}` : ''
|
|
159
|
+
lines.push(`- \`/p:${cmd.name}${args}\` - ${cmd.description}`)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
lines.push('')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return lines.join('\n')
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export default {
|
|
169
|
+
generateRegistry,
|
|
170
|
+
writeRegistry,
|
|
171
|
+
getCommand,
|
|
172
|
+
getCommandsByCategory,
|
|
173
|
+
validateCommand,
|
|
174
|
+
formatHelpText,
|
|
175
|
+
PRJCT_COMMANDS,
|
|
176
|
+
}
|
|
@@ -3,10 +3,37 @@
|
|
|
3
3
|
* Types for external service integrations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* MCP Server Configuration
|
|
8
|
+
*/
|
|
9
|
+
export interface McpServerConfig {
|
|
10
|
+
name: string
|
|
11
|
+
description?: string
|
|
12
|
+
command: string
|
|
13
|
+
args: string[]
|
|
14
|
+
enabled: boolean
|
|
15
|
+
linkedAgents?: string[]
|
|
16
|
+
autoLoad?: boolean
|
|
17
|
+
setupAt?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* MCP Servers Configuration for a project
|
|
22
|
+
*/
|
|
23
|
+
export interface McpServersConfig {
|
|
24
|
+
projectId: string
|
|
25
|
+
version: string
|
|
26
|
+
servers: Record<string, McpServerConfig>
|
|
27
|
+
agentMcpMap: Record<string, string[]>
|
|
28
|
+
}
|
|
29
|
+
|
|
6
30
|
/**
|
|
7
31
|
* Integrations Config
|
|
8
32
|
* Container for all external integrations
|
|
9
33
|
*/
|
|
10
34
|
export interface IntegrationsConfig {
|
|
11
|
-
|
|
35
|
+
mcp?: {
|
|
36
|
+
enabled: boolean
|
|
37
|
+
configPath: string
|
|
38
|
+
}
|
|
12
39
|
}
|