prjct-cli 0.10.12 → 0.10.14

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 CHANGED
@@ -1,5 +1,44 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.14] - 2025-11-29
4
+
5
+ ### Refactored - 100% Agentic Subagent Delegation via Task Tool
6
+
7
+ Claude now delegates to specialist agents using the Task tool with efficient reference passing.
8
+
9
+ - **`command-executor.js`** - Eliminated all if/else agent assignment logic
10
+ - Removed: `MandatoryAgentRouter`, `ContextFilter`, `ContextEstimator`
11
+ - Removed: `isTaskCommand()`, `shouldUseAgent()` methods
12
+ - JS only loads templates and context, Claude decides everything
13
+ - Added: `agentsPath` and `agentRoutingPath` to context for Claude
14
+
15
+ - **Templates updated with Agent Delegation section**:
16
+ - `feature.md` - Added Task + Glob tools, agent delegation instructions
17
+ - `bug.md` - Added Task + Glob tools, agent delegation instructions
18
+ - `task.md` - Added Task + Glob tools, agent delegation instructions
19
+
20
+ - **`agent-routing.md`** - Added efficient Task tool invocation
21
+ - Pass file PATH (~200 bytes), not content (3-5KB)
22
+ - Subagent reads agent file itself
23
+ - Reduced context bloat by 95%
24
+
25
+ ### Architecture
26
+
27
+ ```
28
+ Usuario: "p. feature mejorar UX"
29
+
30
+ Claude lee agent-routing.md → decide: "ux-ui"
31
+
32
+ Claude: Task(prompt='Read: path/agents/ux-ui.md + Task: mejorar UX')
33
+
34
+ Subagente: Lee archivo → aplica expertise → ejecuta
35
+ ```
36
+
37
+ **Benefits:**
38
+ - 95% reduction in prompt size for delegation
39
+ - Lower hallucination risk
40
+ - True 100% agentic - JS is pure orchestration
41
+
3
42
  ## [0.10.12] - 2025-11-29
4
43
 
5
44
  ### Refactored - Mandatory Agent Assignment (100% Agentic)
package/CLAUDE.md CHANGED
@@ -55,8 +55,14 @@ p. ship → Lint/test/commit/push
55
55
 
56
56
  When message starts with `p.`:
57
57
  1. Check `.prjct/prjct.config.json` exists
58
- 2. If exists → Detect intent Execute `/p:*` command
59
- 3. If not exists "No prjct project. Run /p:init first."
58
+ 2. Detect intent from message
59
+ 3. **USE SlashCommand tool** to execute the command
60
+
61
+ ⚠️ **CRITICAL** - Always use SlashCommand, never work directly:
62
+ - ✅ `SlashCommand("/p:feature add dark mode")`
63
+ - ❌ Directly creating files without the command
64
+
65
+ If no project: "No prjct project. Run /p:init first."
60
66
 
61
67
  ### Intent Map
62
68
 
@@ -201,3 +207,42 @@ Use: `generator.generateDynamicAgent(name, config)`
201
207
  4. **Confirm before executing** - Always show plan first
202
208
  5. **Log actions** - Append to memory/context.jsonl
203
209
  6. **Suggest next actions** - Maintain user momentum
210
+
211
+ ## Output Philosophy
212
+
213
+ **Task completion responses MUST be concise (< 4 lines):**
214
+
215
+ Format:
216
+ ```
217
+ ✅ [What was done]
218
+
219
+ Files: [count] | Modified: [key file]
220
+ Next: [action]
221
+ ```
222
+
223
+ **NEVER include in task summaries:**
224
+ - Tables listing files
225
+ - "Created Files" / "Modified Files" sections
226
+ - "How It Works" explanations
227
+ - Code snippets or implementation details
228
+ - Detailed breakdowns of what was done
229
+
230
+ **Example (GOOD):**
231
+ ```
232
+ ✅ Agentic checklists integrated
233
+
234
+ Files: 11 created | Modified: prompt-builder.js
235
+ Next: /p:ship or test with /p:now
236
+ ```
237
+
238
+ **Example (BAD):**
239
+ ```
240
+ Created Files:
241
+ | File | Purpose |
242
+ |------|---------|
243
+ | x.md | Does X |
244
+ ...
245
+
246
+ How It Works:
247
+ Claude reads → decides → applies...
248
+ ```
@@ -1,27 +1,32 @@
1
1
  /**
2
2
  * Command Executor
3
- * WITH MANDATORY AGENT ASSIGNMENT
4
- * Every task MUST use a specialized agent
3
+ * 100% AGENTIC - Claude decides agent assignment via Task tool
5
4
  *
6
- * OPTIMIZATION (P0.2): Explicit Validation
7
- * - Pre-flight checks before execution
8
- * - Specific error messages, never generic failures
9
- * - Actionable suggestions in every error
5
+ * NO if/else logic for agent selection here.
6
+ * Claude reads templates/agentic/agent-routing.md and delegates via Task tool.
10
7
  *
11
- * P3.4: Plan Mode + Approval Flow
12
- * - Separates planning from execution
13
- * - Requires approval for destructive commands
8
+ * JS only:
9
+ * - Loads templates
10
+ * - Builds context
11
+ * - Returns prompt for Claude
12
+ *
13
+ * Claude:
14
+ * - Reads agent-routing.md
15
+ * - Decides best agent for task
16
+ * - Delegates via Task(subagent_type='general-purpose', prompt='Read: path/to/agent.md...')
14
17
  *
15
18
  * Source: Claude Code, Devin, Augment Code patterns
16
19
  */
17
20
 
21
+ const fs = require('fs')
22
+ const path = require('path')
23
+ const os = require('os')
18
24
  const templateLoader = require('./template-loader')
19
25
  const contextBuilder = require('./context-builder')
20
26
  const promptBuilder = require('./prompt-builder')
21
27
  const toolRegistry = require('./tool-registry')
22
- const MandatoryAgentRouter = require('./agent-router')
23
- const ContextFilter = require('./context-filter')
24
- const ContextEstimator = require('../domain/context-estimator')
28
+ // REMOVED: MandatoryAgentRouter, ContextFilter, ContextEstimator
29
+ // Agent assignment is 100% agentic - Claude decides via templates
25
30
  const { validate, formatError } = require('./validation-rules')
26
31
  const loopDetector = require('./loop-detector')
27
32
  const chainOfThought = require('./chain-of-thought')
@@ -32,6 +37,9 @@ const groundTruth = require('./ground-truth')
32
37
  const thinkBlocks = require('./think-blocks')
33
38
  const parallelTools = require('./parallel-tools')
34
39
  const planMode = require('./plan-mode')
40
+
41
+ // Running file for status line integration
42
+ const RUNNING_FILE = path.join(os.homedir(), '.prjct-cli', '.running')
35
43
  // P3.5, P3.6, P3.7: DELEGATED TO CLAUDE CODE
36
44
  // - semantic-search → Claude Code has Grep/Glob with semantic understanding
37
45
  // - code-intelligence → Claude Code has native LSP integration
@@ -39,21 +47,52 @@ const planMode = require('./plan-mode')
39
47
 
40
48
  class CommandExecutor {
41
49
  constructor() {
42
- this.agentRouter = new MandatoryAgentRouter()
43
- this.contextFilter = new ContextFilter()
44
- this.contextEstimator = null
50
+ // 100% AGENTIC: No agent router here
51
+ // Claude decides agent assignment via templates and Task tool
52
+ }
53
+
54
+ /**
55
+ * Signal that a command is running (for status line)
56
+ */
57
+ signalStart(commandName) {
58
+ try {
59
+ const dir = path.dirname(RUNNING_FILE)
60
+ if (!fs.existsSync(dir)) {
61
+ fs.mkdirSync(dir, { recursive: true })
62
+ }
63
+ fs.writeFileSync(RUNNING_FILE, `/p:${commandName}`)
64
+ } catch {
65
+ // Silently ignore - status line is optional
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Signal that command has finished (for status line)
71
+ */
72
+ signalEnd() {
73
+ try {
74
+ if (fs.existsSync(RUNNING_FILE)) {
75
+ fs.unlinkSync(RUNNING_FILE)
76
+ }
77
+ } catch {
78
+ // Silently ignore - status line is optional
79
+ }
45
80
  }
46
81
 
47
82
  /**
48
83
  * Execute command with MANDATORY agent assignment
49
84
  */
50
85
  async execute(commandName, params, projectPath) {
86
+ // Signal start for status line
87
+ this.signalStart(commandName)
88
+
51
89
  // Context for loop detection
52
90
  const loopContext = params.task || params.description || ''
53
91
 
54
92
  // Check if we're in a loop BEFORE attempting
55
93
  if (loopDetector.shouldEscalate(commandName, loopContext)) {
56
94
  const escalation = loopDetector.getEscalationInfo(commandName, loopContext)
95
+ this.signalEnd()
57
96
  return {
58
97
  success: false,
59
98
  error: escalation.message,
@@ -73,6 +112,7 @@ class CommandExecutor {
73
112
  // 2.5. VALIDATE: Pre-flight checks with specific errors
74
113
  const validation = await validate(commandName, metadataContext)
75
114
  if (!validation.valid) {
115
+ this.signalEnd()
76
116
  return {
77
117
  success: false,
78
118
  error: formatError(validation),
@@ -134,72 +174,18 @@ class CommandExecutor {
134
174
  }
135
175
  }
136
176
 
137
- // 3. CRITICAL: Force agent assignment for ALL task-related commands
138
- const requiresAgent = template.metadata?.['required-agent'] !== false &&
139
- (template.metadata?.['required-agent'] === true ||
140
- this.isTaskCommand(commandName) ||
141
- this.shouldUseAgent(commandName))
142
-
177
+ // 3. AGENTIC: Claude decides agent assignment via templates
178
+ // NO if/else logic here - templates instruct Claude to use Task tool
179
+ // See templates/agentic/agent-routing.md for routing rules
143
180
  let context = metadataContext
144
- let assignedAgent = null
145
-
146
- // MANDATORY: Assign specialized agent for task commands
147
- if (requiresAgent) {
148
- // 4. Create task object for analysis
149
- const task = {
150
- description: params.task || params.description || commandName,
151
- type: commandName
152
- }
153
-
154
- // 5. LAZY CONTEXT: Analyze task FIRST, then estimate files needed
155
- // This avoids reading all files before knowing what we need
156
- const agentAssignment = await this.agentRouter.executeTask(
157
- task,
158
- metadataContext, // Only metadata, no files yet
159
- projectPath
160
- )
161
-
162
- assignedAgent = agentAssignment.agent
163
- const taskAnalysis = agentAssignment.taskAnalysis
164
181
 
165
- // Validate agent was assigned
166
- if (!assignedAgent || !assignedAgent.name) {
167
- throw new Error(
168
- `CRITICAL: Failed to assign agent for command "${commandName}". ` +
169
- `System requires ALL task commands to use specialized agents.`
170
- )
171
- }
172
-
173
- // 6. PRE-FILTER: Estimate which files are needed BEFORE reading
174
- if (!this.contextEstimator) {
175
- this.contextEstimator = new ContextEstimator()
176
- }
177
-
178
- const estimatedFiles = await this.contextEstimator.estimateFiles(
179
- taskAnalysis,
180
- projectPath
181
- )
182
-
183
- // 7. Build context ONLY with estimated files (lazy loading)
184
- const filtered = await this.contextFilter.filterForAgent(
185
- assignedAgent,
186
- task,
187
- projectPath,
188
- {
189
- ...metadataContext,
190
- estimatedFiles, // Pre-filtered file list
191
- fileCount: estimatedFiles.length
192
- }
193
- )
194
-
195
- context = {
196
- ...filtered,
197
- agent: assignedAgent,
198
- originalSize: estimatedFiles.length, // Estimated, not actual full size
199
- filteredSize: filtered.files?.length || 0,
200
- reduction: filtered.metrics?.reductionPercent || 0,
201
- lazyLoaded: true // Flag indicating lazy loading was used
202
- }
182
+ // Provide agent info to context so Claude can delegate
183
+ context = {
184
+ ...context,
185
+ agentsPath: path.join(os.homedir(), '.prjct-cli', 'projects', metadataContext.projectId || '', 'agents'),
186
+ agentRoutingPath: path.join(__dirname, '..', '..', 'templates', 'agentic', 'agent-routing.md'),
187
+ // Flag: Claude must delegate to subagent via Task tool
188
+ agenticDelegation: true
203
189
  }
204
190
 
205
191
  // 6. Load state with filtered context
@@ -246,7 +232,7 @@ class CommandExecutor {
246
232
  )
247
233
  }
248
234
 
249
- // 9. Build prompt with agent assignment, learned patterns, think blocks, memories, AND plan mode
235
+ // 9. Build prompt - NO agent assignment here, Claude decides via templates
250
236
  const planInfo = {
251
237
  isPlanning: requiresPlanning || isInPlanningMode,
252
238
  requiresApproval: isDestructive && !params.approved,
@@ -256,25 +242,28 @@ class CommandExecutor {
256
242
  template.frontmatter['allowed-tools'] || []
257
243
  )
258
244
  }
259
- const prompt = promptBuilder.build(template, context, state, assignedAgent, learnedPatterns, thinkBlock, relevantMemories, planInfo)
245
+ // Agent is null - Claude assigns via Task tool using agent-routing.md
246
+ const prompt = promptBuilder.build(template, context, state, null, learnedPatterns, thinkBlock, relevantMemories, planInfo)
260
247
 
261
- // 8. Log agent usage
262
- if (assignedAgent) {
263
- console.log(`🤖 Task assigned to: ${assignedAgent.name}`)
264
- console.log(`📉 Context reduced by: ${context.reduction}%`)
265
- }
248
+ // Log agentic mode
249
+ console.log(`🤖 Agentic delegation enabled - Claude will assign agent via Task tool`)
266
250
 
267
251
  // Record successful attempt
268
252
  loopDetector.recordSuccess(commandName, loopContext)
269
253
 
254
+ // Signal end for status line
255
+ this.signalEnd()
256
+
270
257
  return {
271
258
  success: true,
272
259
  template,
273
260
  context,
274
261
  state,
275
262
  prompt,
276
- assignedAgent,
277
- contextReduction: context.reduction,
263
+ // AGENTIC: No pre-assigned agent - Claude delegates via Task tool
264
+ agenticDelegation: true,
265
+ agentsPath: context.agentsPath,
266
+ agentRoutingPath: context.agentRoutingPath,
278
267
  reasoning, // Chain of thought results
279
268
  thinkBlock, // Think blocks (P3.1)
280
269
  groundTruth: groundTruthResult, // Ground truth verification (P1.3)
@@ -334,6 +323,9 @@ class CommandExecutor {
334
323
  // - Native LSP for code intelligence
335
324
  }
336
325
  } catch (error) {
326
+ // Signal end for status line
327
+ this.signalEnd()
328
+
337
329
  // Record failed attempt for loop detection
338
330
  const attemptInfo = loopDetector.recordAttempt(commandName, loopContext, {
339
331
  success: false,
@@ -361,30 +353,9 @@ class CommandExecutor {
361
353
  }
362
354
  }
363
355
 
364
- /**
365
- * Check if command is task-related
366
- */
367
- isTaskCommand(commandName) {
368
- const taskCommands = [
369
- 'work', 'now', 'build', 'feature', 'bug', 'done',
370
- 'task', 'design', 'cleanup', 'fix', 'test'
371
- ]
372
- return taskCommands.includes(commandName)
373
- }
374
-
375
- /**
376
- * Determine if command should use an agent
377
- * Expanded list of commands that benefit from agent specialization
378
- */
379
- shouldUseAgent(commandName) {
380
- // Commands that should ALWAYS use agents
381
- const agentCommands = [
382
- 'work', 'now', 'build', 'feature', 'bug', 'done',
383
- 'task', 'design', 'cleanup', 'fix', 'test',
384
- 'sync', 'analyze' // These analyze/modify code, need specialization
385
- ]
386
- return agentCommands.includes(commandName)
387
- }
356
+ // REMOVED: isTaskCommand() and shouldUseAgent()
357
+ // Agent assignment is now 100% agentic - Claude decides via templates
358
+ // See templates/agentic/agent-routing.md
388
359
 
389
360
  /**
390
361
  * Execute tool with permission check
@@ -7,9 +7,65 @@
7
7
  * P3.1: Includes think blocks for anti-hallucination
8
8
  * P3.3: Includes relevant memories from semantic database
9
9
  * P3.4: Includes plan mode instructions
10
+ * P4.1: Includes quality checklists (Claude decides which to apply)
10
11
  */
11
12
 
13
+ const fs = require('fs')
14
+ const path = require('path')
15
+
12
16
  class PromptBuilder {
17
+ constructor() {
18
+ this._checklistsCache = null
19
+ this._checklistRoutingCache = null
20
+ }
21
+
22
+ /**
23
+ * Load quality checklists from templates/checklists/
24
+ * Returns checklist content - Claude decides which to apply
25
+ * NO if/else logic here - just load and provide
26
+ */
27
+ loadChecklists() {
28
+ if (this._checklistsCache) return this._checklistsCache
29
+
30
+ const checklistsDir = path.join(__dirname, '..', '..', 'templates', 'checklists')
31
+ const checklists = {}
32
+
33
+ try {
34
+ if (fs.existsSync(checklistsDir)) {
35
+ const files = fs.readdirSync(checklistsDir).filter(f => f.endsWith('.md'))
36
+ for (const file of files) {
37
+ const name = file.replace('.md', '')
38
+ const content = fs.readFileSync(path.join(checklistsDir, file), 'utf-8')
39
+ checklists[name] = content
40
+ }
41
+ }
42
+ } catch (err) {
43
+ // Silent fail - checklists are optional enhancement
44
+ }
45
+
46
+ this._checklistsCache = checklists
47
+ return checklists
48
+ }
49
+
50
+ /**
51
+ * Load checklist routing template
52
+ * Claude reads this to decide which checklists to apply
53
+ */
54
+ loadChecklistRouting() {
55
+ if (this._checklistRoutingCache) return this._checklistRoutingCache
56
+
57
+ const routingPath = path.join(__dirname, '..', '..', 'templates', 'agentic', 'checklist-routing.md')
58
+
59
+ try {
60
+ if (fs.existsSync(routingPath)) {
61
+ this._checklistRoutingCache = fs.readFileSync(routingPath, 'utf-8')
62
+ }
63
+ } catch (err) {
64
+ // Silent fail
65
+ }
66
+
67
+ return this._checklistRoutingCache || null
68
+ }
13
69
  /**
14
70
  * Build concise prompt - only essentials
15
71
  * CRITICAL: Includes full agent content if agent is provided
@@ -154,6 +210,22 @@ class PromptBuilder {
154
210
  parts.push(`\n## APPROVAL REQUIRED\nShow changes, list affected files, ask for confirmation.\n`)
155
211
  }
156
212
 
213
+ // P4.1: Quality Checklists (Claude decides which to apply)
214
+ // Only for code-modifying commands that benefit from quality gates
215
+ const checklistCommands = ['now', 'build', 'feature', 'design', 'fix', 'bug', 'cleanup', 'spec', 'work']
216
+ if (checklistCommands.includes(commandName)) {
217
+ const routing = this.loadChecklistRouting()
218
+ const checklists = this.loadChecklists()
219
+
220
+ if (routing && Object.keys(checklists).length > 0) {
221
+ parts.push('\n## QUALITY CHECKLISTS\n')
222
+ parts.push('Apply relevant checklists based on task. Read checklist-routing.md for guidance.\n')
223
+ parts.push(`Available: ${Object.keys(checklists).join(', ')}\n`)
224
+ parts.push('Path: templates/checklists/{name}.md\n')
225
+ parts.push('Use Read tool to load checklists you determine are relevant.\n')
226
+ }
227
+ }
228
+
157
229
  // Simple execution directive
158
230
  parts.push('\nEXECUTE: Follow flow. Use tools. Decide.\n')
159
231
 
package/core/commands.js CHANGED
@@ -1925,6 +1925,15 @@ Agent: ${agent} (${Math.round(confidence * 100)}% confidence)
1925
1925
  console.log(`⚠️ ${configResult.error}`)
1926
1926
  }
1927
1927
 
1928
+ // Install status line for Claude Code
1929
+ console.log('\n⚡ Installing status line...')
1930
+ const statusLineResult = await this.installStatusLine()
1931
+ if (statusLineResult.success) {
1932
+ console.log('✅ Status line configured')
1933
+ } else {
1934
+ console.log(`⚠️ ${statusLineResult.error}`)
1935
+ }
1936
+
1928
1937
  console.log('\n🎉 Setup complete!\n')
1929
1938
 
1930
1939
  // Show beautiful ASCII art
@@ -1936,6 +1945,69 @@ Agent: ${agent} (${Math.round(confidence * 100)}% confidence)
1936
1945
  }
1937
1946
  }
1938
1947
 
1948
+ /**
1949
+ * Install status line script and configure settings.json
1950
+ */
1951
+ async installStatusLine() {
1952
+ const fs = require('fs')
1953
+ const path = require('path')
1954
+ const os = require('os')
1955
+
1956
+ try {
1957
+ const claudeDir = path.join(os.homedir(), '.claude')
1958
+ const settingsPath = path.join(claudeDir, 'settings.json')
1959
+ const statusLinePath = path.join(claudeDir, 'prjct-statusline.sh')
1960
+
1961
+ // Copy status line script
1962
+ const scriptContent = `#!/bin/bash
1963
+ # prjct Status Line for Claude Code
1964
+ # Shows ⚡ prjct with animated spinner when command is running
1965
+
1966
+ # Read JSON context from stdin (provided by Claude Code)
1967
+ read -r json
1968
+
1969
+ # Spinner frames
1970
+ frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
1971
+
1972
+ # Calculate frame based on time (changes every 80ms)
1973
+ frame=$(($(date +%s%N 2>/dev/null || echo 0) / 80000000 % 10))
1974
+
1975
+ # Check if prjct command is running
1976
+ running_file="$HOME/.prjct-cli/.running"
1977
+
1978
+ if [ -f "$running_file" ]; then
1979
+ task=$(cat "$running_file" 2>/dev/null || echo "working")
1980
+ echo "⚡ prjct \${frames[$frame]} $task"
1981
+ else
1982
+ echo "⚡ prjct"
1983
+ fi
1984
+ `
1985
+ fs.writeFileSync(statusLinePath, scriptContent, { mode: 0o755 })
1986
+
1987
+ // Update settings.json
1988
+ let settings = {}
1989
+ if (fs.existsSync(settingsPath)) {
1990
+ try {
1991
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'))
1992
+ } catch {
1993
+ // Invalid JSON, start fresh
1994
+ }
1995
+ }
1996
+
1997
+ // Set status line configuration
1998
+ settings.statusLine = {
1999
+ type: 'command',
2000
+ command: statusLinePath
2001
+ }
2002
+
2003
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2))
2004
+
2005
+ return { success: true }
2006
+ } catch (error) {
2007
+ return { success: false, error: error.message }
2008
+ }
2009
+ }
2010
+
1939
2011
  /**
1940
2012
  * Show beautiful ASCII art with quick start
1941
2013
  */
package/core/index.js CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  const { PrjctCommands } = require('./commands')
8
8
  const registry = require('./command-registry')
9
+ const out = require('./utils/output')
9
10
 
10
11
  async function main() {
11
12
  const [commandName, ...rawArgs] = process.argv.slice(2)
@@ -25,6 +26,9 @@ async function main() {
25
26
 
26
27
  // === DYNAMIC COMMAND EXECUTION ===
27
28
 
29
+ // Show branding header
30
+ out.start()
31
+
28
32
  try {
29
33
  // 1. Find command in registry
30
34
  const cmd = registry.getByName(commandName)
@@ -32,6 +36,7 @@ async function main() {
32
36
  if (!cmd) {
33
37
  console.error(`Unknown command: ${commandName}`)
34
38
  console.error(`\nUse 'prjct --help' to see available commands.`)
39
+ out.end()
35
40
  process.exit(1)
36
41
  }
37
42
 
@@ -41,6 +46,7 @@ async function main() {
41
46
  if (cmd.replacedBy) {
42
47
  console.error(`Use 'prjct ${cmd.replacedBy}' instead.`)
43
48
  }
49
+ out.end()
44
50
  process.exit(1)
45
51
  }
46
52
 
@@ -49,6 +55,7 @@ async function main() {
49
55
  console.error(`Command '${commandName}' exists but is not yet implemented.`)
50
56
  console.error(`Check the roadmap or contribute: https://github.com/jlopezlira/prjct-cli`)
51
57
  console.error(`\nUse 'prjct --help' to see available commands.`)
58
+ out.end()
52
59
  process.exit(1)
53
60
  }
54
61
 
@@ -90,12 +97,16 @@ async function main() {
90
97
  console.log(result.message)
91
98
  }
92
99
 
100
+ // Show branding footer
101
+ out.end()
93
102
  process.exit(result && result.success ? 0 : 1)
94
103
  } catch (error) {
95
104
  console.error('Error:', error.message)
96
105
  if (process.env.DEBUG) {
97
106
  console.error(error.stack)
98
107
  }
108
+ // Show branding footer even on error
109
+ out.end()
99
110
  process.exit(1)
100
111
  }
101
112
  }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Branding Configuration for prjct-cli
3
+ * Single source of truth for all branding across CLI and Claude Code
4
+ */
5
+
6
+ const chalk = require('chalk')
7
+
8
+ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
9
+ const SPINNER_SPEED = 80
10
+
11
+ const branding = {
12
+ // Core identity
13
+ name: 'prjct',
14
+ icon: '⚡',
15
+ signature: '⚡ prjct',
16
+
17
+ // Spinner config
18
+ spinner: {
19
+ frames: SPINNER_FRAMES,
20
+ speed: SPINNER_SPEED
21
+ },
22
+
23
+ // CLI output (with chalk colors)
24
+ cli: {
25
+ header: () => chalk.cyan.bold('⚡') + ' ' + chalk.cyan('prjct'),
26
+ footer: () => chalk.dim('⚡ prjct'),
27
+ spin: (frame, msg) => chalk.cyan('⚡') + ' ' + chalk.cyan('prjct') + ' ' + chalk.cyan(SPINNER_FRAMES[frame % 10]) + ' ' + chalk.dim(msg || '')
28
+ },
29
+
30
+ // Template/Claude (plain text)
31
+ template: {
32
+ header: '⚡ prjct',
33
+ footer: '⚡ prjct'
34
+ },
35
+
36
+ // Git commit footer
37
+ commitFooter: `🤖 Generated with [p/](https://www.prjct.app/)
38
+ Designed for [Claude](https://www.anthropic.com/claude)`,
39
+
40
+ // URLs
41
+ urls: {
42
+ website: 'https://prjct.app',
43
+ docs: 'https://prjct.app/docs'
44
+ }
45
+ }
46
+
47
+ module.exports = branding