prjct-cli 0.10.12 → 0.10.13

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.
@@ -15,6 +15,9 @@
15
15
  * Source: Claude Code, Devin, Augment Code patterns
16
16
  */
17
17
 
18
+ const fs = require('fs')
19
+ const path = require('path')
20
+ const os = require('os')
18
21
  const templateLoader = require('./template-loader')
19
22
  const contextBuilder = require('./context-builder')
20
23
  const promptBuilder = require('./prompt-builder')
@@ -32,6 +35,9 @@ const groundTruth = require('./ground-truth')
32
35
  const thinkBlocks = require('./think-blocks')
33
36
  const parallelTools = require('./parallel-tools')
34
37
  const planMode = require('./plan-mode')
38
+
39
+ // Running file for status line integration
40
+ const RUNNING_FILE = path.join(os.homedir(), '.prjct-cli', '.running')
35
41
  // P3.5, P3.6, P3.7: DELEGATED TO CLAUDE CODE
36
42
  // - semantic-search → Claude Code has Grep/Glob with semantic understanding
37
43
  // - code-intelligence → Claude Code has native LSP integration
@@ -44,16 +50,48 @@ class CommandExecutor {
44
50
  this.contextEstimator = null
45
51
  }
46
52
 
53
+ /**
54
+ * Signal that a command is running (for status line)
55
+ */
56
+ signalStart(commandName) {
57
+ try {
58
+ const dir = path.dirname(RUNNING_FILE)
59
+ if (!fs.existsSync(dir)) {
60
+ fs.mkdirSync(dir, { recursive: true })
61
+ }
62
+ fs.writeFileSync(RUNNING_FILE, `/p:${commandName}`)
63
+ } catch {
64
+ // Silently ignore - status line is optional
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Signal that command has finished (for status line)
70
+ */
71
+ signalEnd() {
72
+ try {
73
+ if (fs.existsSync(RUNNING_FILE)) {
74
+ fs.unlinkSync(RUNNING_FILE)
75
+ }
76
+ } catch {
77
+ // Silently ignore - status line is optional
78
+ }
79
+ }
80
+
47
81
  /**
48
82
  * Execute command with MANDATORY agent assignment
49
83
  */
50
84
  async execute(commandName, params, projectPath) {
85
+ // Signal start for status line
86
+ this.signalStart(commandName)
87
+
51
88
  // Context for loop detection
52
89
  const loopContext = params.task || params.description || ''
53
90
 
54
91
  // Check if we're in a loop BEFORE attempting
55
92
  if (loopDetector.shouldEscalate(commandName, loopContext)) {
56
93
  const escalation = loopDetector.getEscalationInfo(commandName, loopContext)
94
+ this.signalEnd()
57
95
  return {
58
96
  success: false,
59
97
  error: escalation.message,
@@ -73,6 +111,7 @@ class CommandExecutor {
73
111
  // 2.5. VALIDATE: Pre-flight checks with specific errors
74
112
  const validation = await validate(commandName, metadataContext)
75
113
  if (!validation.valid) {
114
+ this.signalEnd()
76
115
  return {
77
116
  success: false,
78
117
  error: formatError(validation),
@@ -267,6 +306,9 @@ class CommandExecutor {
267
306
  // Record successful attempt
268
307
  loopDetector.recordSuccess(commandName, loopContext)
269
308
 
309
+ // Signal end for status line
310
+ this.signalEnd()
311
+
270
312
  return {
271
313
  success: true,
272
314
  template,
@@ -334,6 +376,9 @@ class CommandExecutor {
334
376
  // - Native LSP for code intelligence
335
377
  }
336
378
  } catch (error) {
379
+ // Signal end for status line
380
+ this.signalEnd()
381
+
337
382
  // Record failed attempt for loop detection
338
383
  const attemptInfo = loopDetector.recordAttempt(commandName, loopContext, {
339
384
  success: false,
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
@@ -1,24 +1,39 @@
1
1
  /**
2
2
  * Minimal Output System for prjct-cli
3
3
  * Spinner while working → Single line result
4
+ * With ⚡ prjct branding
4
5
  */
5
6
 
6
7
  const chalk = require('chalk')
8
+ const branding = require('./branding')
7
9
 
8
- const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
9
- const SPEED = 80
10
+ const FRAMES = branding.spinner.frames
11
+ const SPEED = branding.spinner.speed
10
12
 
11
13
  let interval = null
12
14
  let frame = 0
13
15
 
14
16
  const truncate = (s, max = 50) => (s && s.length > max ? s.slice(0, max - 1) + '…' : s || '')
15
- const clear = () => process.stdout.write('\r' + ' '.repeat(70) + '\r')
17
+ const clear = () => process.stdout.write('\r' + ' '.repeat(80) + '\r')
16
18
 
17
19
  const out = {
20
+ // Branding: Show header at start
21
+ start() {
22
+ console.log(branding.cli.header())
23
+ return this
24
+ },
25
+
26
+ // Branding: Show footer at end
27
+ end() {
28
+ console.log(branding.cli.footer())
29
+ return this
30
+ },
31
+
32
+ // Branded spinner: ⚡ prjct ⠋ message...
18
33
  spin(msg) {
19
34
  this.stop()
20
35
  interval = setInterval(() => {
21
- process.stdout.write(`\r${chalk.cyan(FRAMES[frame++ % 10])} ${truncate(msg, 55)}`)
36
+ process.stdout.write(`\r${branding.cli.spin(frame++, truncate(msg, 45))}`)
22
37
  }, SPEED)
23
38
  return this
24
39
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prjct-cli",
3
- "version": "0.10.12",
3
+ "version": "0.10.13",
4
4
  "description": "Built for Claude - Ship fast, track progress, stay focused. Developer momentum tool for indie hackers.",
5
5
  "main": "core/index.js",
6
6
  "bin": {