prjct-cli 1.1.1 → 1.2.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.
- package/CHANGELOG.md +104 -1
- package/bin/prjct.ts +31 -31
- package/core/cli/start.ts +45 -59
- package/core/commands/planning.ts +1 -0
- package/core/index.ts +21 -24
- package/core/infrastructure/setup.ts +29 -32
- package/core/schemas/ideas.ts +1 -1
- package/core/services/hooks-service.ts +676 -0
- package/core/utils/help.ts +48 -45
- package/core/utils/subtask-table.ts +27 -34
- package/core/workflow/workflow-preferences.ts +11 -17
- package/dist/bin/prjct.mjs +1492 -1036
- package/dist/core/infrastructure/setup.js +29 -30
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,115 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.1] - 2026-02-06
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
- replace raw ANSI codes with chalk library (PRJ-132) (#111)
|
|
8
|
+
- replace raw ANSI codes with chalk library (PRJ-132)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
## [1.2.0] - 2026-02-06
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
- git hooks integration for auto-sync (PRJ-128) (#112)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## [1.2.0] - 2026-02-05
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Git hooks integration (PRJ-128)**: New `prjct hooks` command for auto-syncing context on commit and branch checkout
|
|
23
|
+
|
|
24
|
+
### Implementation Details
|
|
25
|
+
|
|
26
|
+
New `prjct hooks` CLI subcommand with three operations:
|
|
27
|
+
- `prjct hooks install` — auto-detects hook manager (lefthook > husky > direct `.git/hooks/`) and installs post-commit + post-checkout hooks
|
|
28
|
+
- `prjct hooks uninstall` — cleanly removes only prjct hooks, preserving existing hooks
|
|
29
|
+
- `prjct hooks status` — shows active hooks, strategy, and available managers
|
|
30
|
+
|
|
31
|
+
Hook scripts feature:
|
|
32
|
+
- **Rate limiting** — 30-second lockfile prevents over-syncing on rapid commits
|
|
33
|
+
- **Background execution** — hooks run `prjct sync` in background, never blocking git
|
|
34
|
+
- **Branch-only checkout** — post-checkout only fires on branch switch, not file checkout
|
|
35
|
+
- **Cross-platform** — handles macOS/Linux differences in `stat` and `md5` commands
|
|
36
|
+
|
|
37
|
+
Supports three installation strategies:
|
|
38
|
+
- **Lefthook** — adds `prjct-sync-*` commands to existing `lefthook.yml`
|
|
39
|
+
- **Husky** — appends to existing `.husky/` hook scripts
|
|
40
|
+
- **Direct** — writes to `.git/hooks/` as fallback
|
|
41
|
+
|
|
42
|
+
Hook configuration saved to `project.json` for persistence across sessions.
|
|
43
|
+
|
|
44
|
+
### Learnings
|
|
45
|
+
|
|
46
|
+
- Strategy pattern works well for hook manager abstraction (detect → select → install)
|
|
47
|
+
- `stat -f%m` (macOS) vs `stat -c%Y` (Linux) for file modification time
|
|
48
|
+
- Lefthook section merging needs careful regex to avoid duplicates
|
|
49
|
+
- `$3` parameter in post-checkout distinguishes branch checkout (1) from file checkout (0)
|
|
50
|
+
|
|
51
|
+
### Test Plan
|
|
52
|
+
|
|
53
|
+
#### For QA
|
|
54
|
+
1. Run `prjct hooks status` — verify shows "Not installed" with available managers
|
|
55
|
+
2. Run `prjct hooks install` — verify detects manager and installs hooks
|
|
56
|
+
3. Run `prjct hooks status` — verify shows "Active"
|
|
57
|
+
4. Make a git commit — verify sync runs in background
|
|
58
|
+
5. Switch branches — verify post-checkout triggers sync
|
|
59
|
+
6. Run `prjct hooks uninstall` — verify clean removal
|
|
60
|
+
7. Run `bun run build && bun run typecheck` — zero errors
|
|
61
|
+
|
|
62
|
+
#### For Users
|
|
63
|
+
**What changed:** New `prjct hooks` command for automatic context syncing
|
|
64
|
+
**How to use:** Run `prjct hooks install` in any prjct project
|
|
65
|
+
**Breaking changes:** None
|
|
66
|
+
|
|
67
|
+
## [1.1.2] - 2026-02-05
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- **Replace raw ANSI codes with chalk library (PRJ-132)**: Replaced ~60 raw ANSI escape codes across 7 files with chalk library calls
|
|
72
|
+
|
|
73
|
+
### Implementation Details
|
|
74
|
+
|
|
75
|
+
Replaced all raw ANSI color/formatting constants (`\x1b[32m`, `\x1b[1m`, etc.) with chalk equivalents (`chalk.green()`, `chalk.bold()`, etc.) in:
|
|
76
|
+
- `bin/prjct.ts` — version display, provider status, welcome message
|
|
77
|
+
- `core/index.ts` — version display, provider status
|
|
78
|
+
- `core/cli/start.ts` — gradient banner using `chalk.rgb()`, setup UI
|
|
79
|
+
- `core/utils/subtask-table.ts` — color palette as chalk functions, progress display
|
|
80
|
+
- `core/utils/help.ts` — all help formatting
|
|
81
|
+
- `core/workflow/workflow-preferences.ts` — hook status display
|
|
82
|
+
- `core/infrastructure/setup.ts` — installation messages
|
|
83
|
+
|
|
84
|
+
Terminal control sequences (cursor movement, hide/show) kept as raw ANSI since chalk only handles colors/formatting.
|
|
85
|
+
|
|
86
|
+
### Learnings
|
|
87
|
+
|
|
88
|
+
- `chalk.rgb(R,G,B)` replaces `\x1b[38;2;R;G;Bm` for true-color support
|
|
89
|
+
- Chalk functions can be stored as array values and called dynamically (useful for color palettes)
|
|
90
|
+
- `chalk.bold.white()` chains work for compound styling
|
|
91
|
+
- Terminal control sequences (`\x1b[?25l`, cursor movement) must stay raw — chalk doesn't handle them
|
|
92
|
+
|
|
93
|
+
### Test Plan
|
|
94
|
+
|
|
95
|
+
#### For QA
|
|
96
|
+
1. Run `prjct --version` — verify colored output with provider status
|
|
97
|
+
2. Run `prjct help` — verify formatted help with colors
|
|
98
|
+
3. Run `prjct start --force` — verify gradient banner and colored UI
|
|
99
|
+
4. Set `NO_COLOR=1` and repeat above — verify all color is suppressed
|
|
100
|
+
5. Run `bun run build && bun run typecheck` — verify zero errors
|
|
101
|
+
|
|
102
|
+
#### For Users
|
|
103
|
+
**What changed:** Terminal colors now use the chalk library instead of raw ANSI codes
|
|
104
|
+
**How to use:** No change — colors appear the same but now respect `NO_COLOR` env variable
|
|
105
|
+
**Breaking changes:** None
|
|
106
|
+
|
|
3
107
|
## [1.1.1] - 2026-02-06
|
|
4
108
|
|
|
5
109
|
### Bug Fixes
|
|
6
110
|
|
|
7
111
|
- visual grouping with boxes and tables for structured output (PRJ-134) (#110)
|
|
8
112
|
|
|
9
|
-
|
|
10
113
|
## [1.1.1] - 2026-02-05
|
|
11
114
|
|
|
12
115
|
### Improved
|
package/bin/prjct.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import fs from 'node:fs'
|
|
12
12
|
import os from 'node:os'
|
|
13
13
|
import path from 'node:path'
|
|
14
|
+
import chalk from 'chalk'
|
|
14
15
|
import { detectAllProviders } from '../core/infrastructure/ai-provider'
|
|
15
16
|
import configManager from '../core/infrastructure/config-manager'
|
|
16
17
|
import editorsConfig from '../core/infrastructure/editors-config'
|
|
@@ -61,12 +62,7 @@ if (isQuietMode) {
|
|
|
61
62
|
setQuietMode(true)
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
// Colors for output
|
|
65
|
-
const CYAN = '\x1b[36m'
|
|
66
|
-
const YELLOW = '\x1b[33m'
|
|
67
|
-
const DIM = '\x1b[2m'
|
|
68
|
-
const BOLD = '\x1b[1m'
|
|
69
|
-
const RESET = '\x1b[0m'
|
|
65
|
+
// Colors for output (chalk respects NO_COLOR env)
|
|
70
66
|
|
|
71
67
|
if (args[0] === 'start' || args[0] === 'setup') {
|
|
72
68
|
// Interactive setup with beautiful UI
|
|
@@ -108,6 +104,12 @@ if (args[0] === 'start' || args[0] === 'setup') {
|
|
|
108
104
|
console.log(JSON.stringify(result, null, 2))
|
|
109
105
|
process.exitCode = result.tool === 'error' ? 1 : 0
|
|
110
106
|
}
|
|
107
|
+
} else if (args[0] === 'hooks') {
|
|
108
|
+
// Git hooks management
|
|
109
|
+
const { hooksService } = await import('../core/services/hooks-service')
|
|
110
|
+
const subcommand = args[1] || 'status'
|
|
111
|
+
const exitCode = await hooksService.run(process.cwd(), subcommand)
|
|
112
|
+
process.exitCode = exitCode
|
|
111
113
|
} else if (args[0] === 'doctor') {
|
|
112
114
|
// Health check command
|
|
113
115
|
const { doctorService } = await import('../core/services/doctor-service')
|
|
@@ -200,52 +202,50 @@ if (args[0] === 'start' || args[0] === 'setup') {
|
|
|
200
202
|
const windsurfDetected = fs.existsSync(path.join(cwd, '.windsurf'))
|
|
201
203
|
const windsurfConfigured = fs.existsSync(path.join(cwd, '.windsurf', 'rules', 'prjct.md'))
|
|
202
204
|
|
|
203
|
-
const GREEN = '\x1b[32m'
|
|
204
|
-
|
|
205
205
|
console.log(`
|
|
206
|
-
${
|
|
207
|
-
${
|
|
206
|
+
${chalk.cyan('p/')} prjct v${VERSION}
|
|
207
|
+
${chalk.dim('Context layer for AI coding agents')}
|
|
208
208
|
|
|
209
|
-
${
|
|
209
|
+
${chalk.dim('Providers:')}`)
|
|
210
210
|
|
|
211
211
|
// Claude status
|
|
212
212
|
if (detection.claude.installed) {
|
|
213
|
-
const status = claudeConfigured ?
|
|
213
|
+
const status = claudeConfigured ? chalk.green('✓ ready') : chalk.yellow('● installed')
|
|
214
214
|
const ver = detection.claude.version ? ` (v${detection.claude.version})` : ''
|
|
215
|
-
console.log(` Claude Code ${status}${
|
|
215
|
+
console.log(` Claude Code ${status}${chalk.dim(ver)}`)
|
|
216
216
|
} else {
|
|
217
|
-
console.log(` Claude Code ${
|
|
217
|
+
console.log(` Claude Code ${chalk.dim('○ not installed')}`)
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
// Gemini status
|
|
221
221
|
if (detection.gemini.installed) {
|
|
222
|
-
const status = geminiConfigured ?
|
|
222
|
+
const status = geminiConfigured ? chalk.green('✓ ready') : chalk.yellow('● installed')
|
|
223
223
|
const ver = detection.gemini.version ? ` (v${detection.gemini.version})` : ''
|
|
224
|
-
console.log(` Gemini CLI ${status}${
|
|
224
|
+
console.log(` Gemini CLI ${status}${chalk.dim(ver)}`)
|
|
225
225
|
} else {
|
|
226
|
-
console.log(` Gemini CLI ${
|
|
226
|
+
console.log(` Gemini CLI ${chalk.dim('○ not installed')}`)
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
// Cursor status (project-level)
|
|
230
230
|
if (cursorDetected) {
|
|
231
|
-
const status = cursorConfigured ?
|
|
232
|
-
console.log(` Cursor IDE ${status}${
|
|
231
|
+
const status = cursorConfigured ? chalk.green('✓ ready') : chalk.yellow('● detected')
|
|
232
|
+
console.log(` Cursor IDE ${status}${chalk.dim(' (project)')}`)
|
|
233
233
|
} else {
|
|
234
|
-
console.log(` Cursor IDE ${
|
|
234
|
+
console.log(` Cursor IDE ${chalk.dim('○ not detected')}`)
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
// Windsurf status (project-level)
|
|
238
238
|
if (windsurfDetected) {
|
|
239
|
-
const status = windsurfConfigured ?
|
|
240
|
-
console.log(` Windsurf IDE ${status}${
|
|
239
|
+
const status = windsurfConfigured ? chalk.green('✓ ready') : chalk.yellow('● detected')
|
|
240
|
+
console.log(` Windsurf IDE ${status}${chalk.dim(' (project)')}`)
|
|
241
241
|
} else {
|
|
242
|
-
console.log(` Windsurf IDE ${
|
|
242
|
+
console.log(` Windsurf IDE ${chalk.dim('○ not detected')}`)
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
console.log(`
|
|
246
|
-
${
|
|
247
|
-
${
|
|
248
|
-
${
|
|
246
|
+
${chalk.dim("Run 'prjct start' to configure (CLI providers)")}
|
|
247
|
+
${chalk.dim("Run 'prjct init' to configure (Cursor/Windsurf IDE)")}
|
|
248
|
+
${chalk.cyan('https://prjct.app')}
|
|
249
249
|
`)
|
|
250
250
|
} else {
|
|
251
251
|
// Check if setup has been done
|
|
@@ -255,12 +255,12 @@ ${CYAN}https://prjct.app${RESET}
|
|
|
255
255
|
if (!fs.existsSync(configPath) || !routersInstalled) {
|
|
256
256
|
// First time - prompt to run start
|
|
257
257
|
console.log(`
|
|
258
|
-
${
|
|
258
|
+
${chalk.cyan.bold(' Welcome to prjct!')}
|
|
259
259
|
|
|
260
|
-
Run ${
|
|
260
|
+
Run ${chalk.bold('prjct start')} to configure your AI providers.
|
|
261
261
|
|
|
262
|
-
${
|
|
263
|
-
Claude Code, Gemini CLI, or both
|
|
262
|
+
${chalk.dim(`This is a one-time setup that lets you choose between
|
|
263
|
+
Claude Code, Gemini CLI, or both.`)}
|
|
264
264
|
`)
|
|
265
265
|
process.exitCode = 0
|
|
266
266
|
} else {
|
|
@@ -269,7 +269,7 @@ ${CYAN}${BOLD} Welcome to prjct!${RESET}
|
|
|
269
269
|
const lastVersion = await editorsConfig.getLastVersion()
|
|
270
270
|
|
|
271
271
|
if (lastVersion && lastVersion !== VERSION) {
|
|
272
|
-
console.log(`\n${
|
|
272
|
+
console.log(`\n${chalk.yellow('ℹ')} Updating prjct v${lastVersion} → v${VERSION}...\n`)
|
|
273
273
|
|
|
274
274
|
const { default: setup } = await import('../core/infrastructure/setup')
|
|
275
275
|
await setup.run()
|
package/core/cli/start.ts
CHANGED
|
@@ -12,46 +12,35 @@ import fs from 'node:fs'
|
|
|
12
12
|
import os from 'node:os'
|
|
13
13
|
import path from 'node:path'
|
|
14
14
|
import readline from 'node:readline'
|
|
15
|
+
import chalk from 'chalk'
|
|
15
16
|
import { detectAllProviders, Providers } from '../infrastructure/ai-provider'
|
|
16
17
|
import type { AIProviderName } from '../types/provider'
|
|
17
18
|
import { VERSION } from '../utils/version'
|
|
18
19
|
|
|
19
|
-
// Colors
|
|
20
|
-
const RESET = '\x1b[0m'
|
|
21
|
-
const BOLD = '\x1b[1m'
|
|
22
|
-
const DIM = '\x1b[2m'
|
|
23
|
-
const GREEN = '\x1b[32m'
|
|
24
|
-
const YELLOW = '\x1b[33m'
|
|
25
|
-
const _BLUE = '\x1b[34m'
|
|
26
|
-
const _MAGENTA = '\x1b[35m'
|
|
27
|
-
const CYAN = '\x1b[36m'
|
|
28
|
-
const WHITE = '\x1b[37m'
|
|
29
|
-
const _BG_BLUE = '\x1b[44m'
|
|
30
|
-
|
|
31
20
|
// True color gradient (cyan -> blue -> purple -> pink)
|
|
32
|
-
const G1 =
|
|
33
|
-
const G2 =
|
|
34
|
-
const G3 =
|
|
35
|
-
const G4 =
|
|
36
|
-
const G5 =
|
|
21
|
+
const G1 = chalk.rgb(0, 255, 255)
|
|
22
|
+
const G2 = chalk.rgb(80, 180, 255)
|
|
23
|
+
const G3 = chalk.rgb(140, 120, 255)
|
|
24
|
+
const G4 = chalk.rgb(200, 80, 220)
|
|
25
|
+
const G5 = chalk.rgb(255, 80, 180)
|
|
37
26
|
|
|
38
27
|
// Large block letters - PRJCT (7 lines tall)
|
|
39
28
|
const BANNER = `
|
|
40
29
|
|
|
41
|
-
${G1
|
|
42
|
-
${G1}
|
|
43
|
-
${G1}
|
|
44
|
-
${G1
|
|
45
|
-
${G1
|
|
46
|
-
${G1
|
|
30
|
+
${G1(' ██████╗ ')}${G2(' ██████╗ ')}${G3(' ██╗')}${G4(' ██████╗')}${G5('████████╗')}
|
|
31
|
+
${G1(' ██╔══██╗')}${G2(' ██╔══██╗')}${G3(' ██║')}${G4('██╔════╝')}${G5('╚══██╔══╝')}
|
|
32
|
+
${G1(' ██████╔╝')}${G2(' ██████╔╝')}${G3(' ██║')}${G4('██║ ')}${G5(' ██║ ')}
|
|
33
|
+
${G1(' ██╔═══╝ ')}${G2(' ██╔══██╗')}${G3('██ ██║')}${G4('██║ ')}${G5(' ██║ ')}
|
|
34
|
+
${G1(' ██║ ')}${G2(' ██║ ██║')}${G3('╚█████╔╝')}${G4('╚██████╗')}${G5(' ██║ ')}
|
|
35
|
+
${G1(' ╚═╝ ')}${G2(' ╚═╝ ╚═╝')}${G3(' ╚════╝ ')}${G4(' ╚═════╝')}${G5(' ╚═╝ ')}
|
|
47
36
|
|
|
48
37
|
`
|
|
49
38
|
|
|
50
|
-
const WELCOME_BOX = ` ${
|
|
39
|
+
const WELCOME_BOX = ` ${chalk.white('Context Layer for AI Agents')} ${chalk.dim(`v${VERSION}`)}
|
|
51
40
|
|
|
52
|
-
${
|
|
53
|
-
Works with Claude Code, Gemini CLI, and more
|
|
54
|
-
${
|
|
41
|
+
${chalk.dim(`Project context layer for AI coding agents.
|
|
42
|
+
Works with Claude Code, Gemini CLI, and more.`)}
|
|
43
|
+
${chalk.cyan('https://prjct.app')}
|
|
55
44
|
`
|
|
56
45
|
|
|
57
46
|
interface ProviderOption {
|
|
@@ -85,17 +74,14 @@ function showBanner(): void {
|
|
|
85
74
|
* Show provider selection UI
|
|
86
75
|
*/
|
|
87
76
|
function showProviderSelection(options: ProviderOption[], currentIndex: number): void {
|
|
88
|
-
console.log(`\n${
|
|
89
|
-
console.log(` ${
|
|
77
|
+
console.log(`\n${chalk.bold(' Select AI providers to configure:')}\n`)
|
|
78
|
+
console.log(` ${chalk.dim('(Use arrow keys to navigate, space to toggle, enter to confirm)')}\n`)
|
|
90
79
|
|
|
91
80
|
options.forEach((option, index) => {
|
|
92
|
-
const cursor = index === currentIndex ?
|
|
93
|
-
const checkbox = option.selected ?
|
|
94
|
-
const status = option.installed
|
|
95
|
-
|
|
96
|
-
: `${YELLOW}(will install)${RESET}`
|
|
97
|
-
const name =
|
|
98
|
-
index === currentIndex ? `${BOLD}${option.displayName}${RESET}` : option.displayName
|
|
81
|
+
const cursor = index === currentIndex ? chalk.cyan('❯') : ' '
|
|
82
|
+
const checkbox = option.selected ? chalk.green('[✓]') : chalk.dim('[ ]')
|
|
83
|
+
const status = option.installed ? chalk.green('(installed)') : chalk.yellow('(will install)')
|
|
84
|
+
const name = index === currentIndex ? chalk.bold(option.displayName) : option.displayName
|
|
99
85
|
|
|
100
86
|
console.log(` ${cursor} ${checkbox} ${name} ${status}`)
|
|
101
87
|
})
|
|
@@ -131,10 +117,10 @@ async function selectProviders(): Promise<AIProviderName[]> {
|
|
|
131
117
|
|
|
132
118
|
// Non-interactive mode: auto-select detected providers
|
|
133
119
|
if (!process.stdin.isTTY) {
|
|
134
|
-
console.log(`\n${
|
|
120
|
+
console.log(`\n${chalk.bold(' Detected providers:')}\n`)
|
|
135
121
|
options.forEach((option) => {
|
|
136
122
|
if (option.installed) {
|
|
137
|
-
console.log(` ${
|
|
123
|
+
console.log(` ${chalk.green('✓')} ${option.displayName}`)
|
|
138
124
|
}
|
|
139
125
|
})
|
|
140
126
|
console.log('')
|
|
@@ -230,7 +216,7 @@ async function installRouter(provider: AIProviderName): Promise<boolean> {
|
|
|
230
216
|
return false
|
|
231
217
|
} catch (error) {
|
|
232
218
|
console.error(
|
|
233
|
-
` ${
|
|
219
|
+
` ${chalk.yellow('⚠')} Failed to install ${provider} router: ${(error as Error).message}`
|
|
234
220
|
)
|
|
235
221
|
return false
|
|
236
222
|
}
|
|
@@ -294,7 +280,7 @@ async function installGlobalConfig(provider: AIProviderName): Promise<boolean> {
|
|
|
294
280
|
return false
|
|
295
281
|
} catch (error) {
|
|
296
282
|
console.error(
|
|
297
|
-
` ${
|
|
283
|
+
` ${chalk.yellow('⚠')} Failed to install ${provider} config: ${(error as Error).message}`
|
|
298
284
|
)
|
|
299
285
|
return false
|
|
300
286
|
}
|
|
@@ -324,27 +310,27 @@ async function saveSetupConfig(providers: AIProviderName[]): Promise<void> {
|
|
|
324
310
|
* Show completion message
|
|
325
311
|
*/
|
|
326
312
|
function showCompletion(providers: AIProviderName[]): void {
|
|
327
|
-
console.log(`\n${
|
|
313
|
+
console.log(`\n${chalk.green.bold(' ✓ Setup complete!')}\n`)
|
|
328
314
|
|
|
329
|
-
console.log(` ${
|
|
315
|
+
console.log(` ${chalk.dim('Configured providers:')}`)
|
|
330
316
|
providers.forEach((p) => {
|
|
331
317
|
const config = Providers[p]
|
|
332
|
-
console.log(` ${
|
|
318
|
+
console.log(` ${chalk.green('✓')} ${config.displayName}`)
|
|
333
319
|
})
|
|
334
320
|
|
|
335
321
|
console.log(`
|
|
336
|
-
${
|
|
322
|
+
${chalk.bold('Next steps:')}
|
|
337
323
|
|
|
338
|
-
${
|
|
339
|
-
${
|
|
340
|
-
${
|
|
324
|
+
${chalk.cyan('1.')} Navigate to your project directory
|
|
325
|
+
${chalk.cyan('2.')} Run ${chalk.bold('p. init')} to initialize prjct for that project
|
|
326
|
+
${chalk.cyan('3.')} Start tracking with ${chalk.bold('p. task "your task"')}
|
|
341
327
|
|
|
342
|
-
${
|
|
343
|
-
${
|
|
344
|
-
${
|
|
345
|
-
${
|
|
328
|
+
${chalk.dim('Tips:')}
|
|
329
|
+
${chalk.dim('•')} Use ${chalk.bold('p. sync')} to analyze your codebase
|
|
330
|
+
${chalk.dim('•')} Use ${chalk.bold('p. done')} to complete tasks
|
|
331
|
+
${chalk.dim('•')} Use ${chalk.bold('p. ship')} to create PRs
|
|
346
332
|
|
|
347
|
-
${
|
|
333
|
+
${chalk.dim('Learn more: https://prjct.app/docs')}
|
|
348
334
|
`)
|
|
349
335
|
}
|
|
350
336
|
|
|
@@ -359,8 +345,8 @@ export async function runStart(): Promise<void> {
|
|
|
359
345
|
if (fs.existsSync(configPath)) {
|
|
360
346
|
const existing = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
|
361
347
|
if (existing.version === VERSION) {
|
|
362
|
-
console.log(` ${
|
|
363
|
-
console.log(` ${
|
|
348
|
+
console.log(` ${chalk.yellow('ℹ')} Already configured for v${VERSION}`)
|
|
349
|
+
console.log(` ${chalk.dim('Run with --force to reconfigure')}\n`)
|
|
364
350
|
|
|
365
351
|
if (!process.argv.includes('--force')) {
|
|
366
352
|
return
|
|
@@ -371,22 +357,22 @@ export async function runStart(): Promise<void> {
|
|
|
371
357
|
// Select providers
|
|
372
358
|
const selectedProviders = await selectProviders()
|
|
373
359
|
|
|
374
|
-
console.log(`\n ${
|
|
360
|
+
console.log(`\n ${chalk.cyan('Setting up...')}\n`)
|
|
375
361
|
|
|
376
362
|
// Install for each selected provider
|
|
377
363
|
for (const provider of selectedProviders) {
|
|
378
364
|
const config = Providers[provider]
|
|
379
|
-
process.stdout.write(` ${
|
|
365
|
+
process.stdout.write(` ${chalk.dim('•')} ${config.displayName}... `)
|
|
380
366
|
|
|
381
367
|
const routerOk = await installRouter(provider)
|
|
382
368
|
const configOk = await installGlobalConfig(provider)
|
|
383
369
|
|
|
384
370
|
if (routerOk && configOk) {
|
|
385
|
-
console.log(
|
|
371
|
+
console.log(chalk.green('✓'))
|
|
386
372
|
} else if (routerOk || configOk) {
|
|
387
|
-
console.log(
|
|
373
|
+
console.log(chalk.yellow('partial'))
|
|
388
374
|
} else {
|
|
389
|
-
console.log(
|
|
375
|
+
console.log(chalk.yellow('skipped'))
|
|
390
376
|
}
|
|
391
377
|
}
|
|
392
378
|
|
|
@@ -207,6 +207,7 @@ export class PlanningCommands extends PrjctCommandsBase {
|
|
|
207
207
|
console.log(' Quick start:')
|
|
208
208
|
console.log(' prjct sync Update context after changes')
|
|
209
209
|
console.log(' prjct task Start working on a task')
|
|
210
|
+
console.log(' prjct hooks Auto-sync on commit/checkout')
|
|
210
211
|
console.log('')
|
|
211
212
|
|
|
212
213
|
if (wizardResult) {
|
package/core/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import './commands/register' // Ensure commands are registered
|
|
|
10
10
|
import fs from 'node:fs'
|
|
11
11
|
import os from 'node:os'
|
|
12
12
|
import path from 'node:path'
|
|
13
|
+
import chalk from 'chalk'
|
|
13
14
|
import type { CommandMeta } from './commands/registry'
|
|
14
15
|
import { detectAllProviders, detectAntigravity } from './infrastructure/ai-provider'
|
|
15
16
|
import out from './utils/output'
|
|
@@ -195,12 +196,7 @@ function parseCommandArgs(_cmd: CommandMeta, rawArgs: string[]): ParsedCommandAr
|
|
|
195
196
|
return { parsedArgs, options }
|
|
196
197
|
}
|
|
197
198
|
|
|
198
|
-
// Colors
|
|
199
|
-
const CYAN = '\x1b[36m'
|
|
200
|
-
const GREEN = '\x1b[32m'
|
|
201
|
-
const YELLOW = '\x1b[33m'
|
|
202
|
-
const DIM = '\x1b[2m'
|
|
203
|
-
const RESET = '\x1b[0m'
|
|
199
|
+
// Colors via chalk (respects NO_COLOR env)
|
|
204
200
|
|
|
205
201
|
/**
|
|
206
202
|
* Display version with provider status
|
|
@@ -219,53 +215,53 @@ function displayVersion(version: string): void {
|
|
|
219
215
|
const cursorExists = fs.existsSync(path.join(process.cwd(), '.cursor'))
|
|
220
216
|
|
|
221
217
|
console.log(`
|
|
222
|
-
${
|
|
223
|
-
${
|
|
218
|
+
${chalk.cyan('p/')} prjct v${version}
|
|
219
|
+
${chalk.dim('Context layer for AI coding agents')}
|
|
224
220
|
|
|
225
|
-
${
|
|
221
|
+
${chalk.dim('Providers:')}`)
|
|
226
222
|
|
|
227
223
|
// Claude status
|
|
228
224
|
if (detection.claude.installed) {
|
|
229
|
-
const status = claudeConfigured ?
|
|
225
|
+
const status = claudeConfigured ? chalk.green('✓ ready') : chalk.yellow('● installed')
|
|
230
226
|
const ver = detection.claude.version ? ` (v${detection.claude.version})` : ''
|
|
231
|
-
console.log(` Claude Code ${status}${
|
|
227
|
+
console.log(` Claude Code ${status}${chalk.dim(ver)}`)
|
|
232
228
|
} else {
|
|
233
|
-
console.log(` Claude Code ${
|
|
229
|
+
console.log(` Claude Code ${chalk.dim('○ not installed')}`)
|
|
234
230
|
}
|
|
235
231
|
|
|
236
232
|
// Gemini status
|
|
237
233
|
if (detection.gemini.installed) {
|
|
238
|
-
const status = geminiConfigured ?
|
|
234
|
+
const status = geminiConfigured ? chalk.green('✓ ready') : chalk.yellow('● installed')
|
|
239
235
|
const ver = detection.gemini.version ? ` (v${detection.gemini.version})` : ''
|
|
240
|
-
console.log(` Gemini CLI ${status}${
|
|
236
|
+
console.log(` Gemini CLI ${status}${chalk.dim(ver)}`)
|
|
241
237
|
} else {
|
|
242
|
-
console.log(` Gemini CLI ${
|
|
238
|
+
console.log(` Gemini CLI ${chalk.dim('○ not installed')}`)
|
|
243
239
|
}
|
|
244
240
|
|
|
245
241
|
// Antigravity status (global, skills-based)
|
|
246
242
|
const antigravityDetection = detectAntigravity()
|
|
247
243
|
if (antigravityDetection.installed) {
|
|
248
244
|
const status = antigravityDetection.skillInstalled
|
|
249
|
-
?
|
|
250
|
-
:
|
|
251
|
-
const hint = antigravityDetection.skillInstalled ? '' : ` ${
|
|
245
|
+
? chalk.green('✓ ready')
|
|
246
|
+
: chalk.yellow('● detected')
|
|
247
|
+
const hint = antigravityDetection.skillInstalled ? '' : ` ${chalk.dim('(run prjct start)')}`
|
|
252
248
|
console.log(` Antigravity ${status}${hint}`)
|
|
253
249
|
} else {
|
|
254
|
-
console.log(` Antigravity ${
|
|
250
|
+
console.log(` Antigravity ${chalk.dim('○ not installed')}`)
|
|
255
251
|
}
|
|
256
252
|
|
|
257
253
|
// Cursor status (project-level, but shown in same format)
|
|
258
254
|
if (cursorConfigured) {
|
|
259
|
-
console.log(` Cursor IDE ${
|
|
255
|
+
console.log(` Cursor IDE ${chalk.green('✓ ready')} ${chalk.dim('(use /sync, /task)')}`)
|
|
260
256
|
} else if (cursorExists) {
|
|
261
|
-
console.log(` Cursor IDE ${
|
|
257
|
+
console.log(` Cursor IDE ${chalk.yellow('● detected')} ${chalk.dim('(run prjct init)')}`)
|
|
262
258
|
} else {
|
|
263
|
-
console.log(` Cursor IDE ${
|
|
259
|
+
console.log(` Cursor IDE ${chalk.dim('○ no .cursor/ folder')}`)
|
|
264
260
|
}
|
|
265
261
|
|
|
266
262
|
console.log(`
|
|
267
|
-
${
|
|
268
|
-
${
|
|
263
|
+
${chalk.dim("Run 'prjct start' for Claude/Gemini, 'prjct init' for Cursor")}
|
|
264
|
+
${chalk.cyan('https://prjct.app')}
|
|
269
265
|
`)
|
|
270
266
|
}
|
|
271
267
|
|
|
@@ -306,6 +302,7 @@ TERMINAL COMMANDS (this CLI)
|
|
|
306
302
|
prjct setup Reconfigure installations
|
|
307
303
|
prjct sync Sync project state
|
|
308
304
|
prjct watch Auto-sync on file changes (Ctrl+C to stop)
|
|
305
|
+
prjct hooks Manage git hooks for auto-sync
|
|
309
306
|
prjct doctor Check system health and dependencies
|
|
310
307
|
|
|
311
308
|
EXAMPLES
|