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/core/utils/help.ts
CHANGED
|
@@ -7,17 +7,10 @@
|
|
|
7
7
|
* @module utils/help
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import chalk from 'chalk'
|
|
10
11
|
import { CATEGORIES, COMMANDS } from '../commands/command-data'
|
|
11
12
|
import { VERSION } from './version'
|
|
12
13
|
|
|
13
|
-
// ANSI colors
|
|
14
|
-
const CYAN = '\x1b[36m'
|
|
15
|
-
const DIM = '\x1b[2m'
|
|
16
|
-
const BOLD = '\x1b[1m'
|
|
17
|
-
const RESET = '\x1b[0m'
|
|
18
|
-
const GREEN = '\x1b[32m'
|
|
19
|
-
const YELLOW = '\x1b[33m'
|
|
20
|
-
|
|
21
14
|
/**
|
|
22
15
|
* Terminal commands that run directly in the shell
|
|
23
16
|
*/
|
|
@@ -43,6 +36,12 @@ const TERMINAL_COMMANDS = [
|
|
|
43
36
|
example: 'prjct watch',
|
|
44
37
|
options: ['--verbose', '--debounce=<ms>', '--interval=<sec>'],
|
|
45
38
|
},
|
|
39
|
+
{
|
|
40
|
+
name: 'hooks',
|
|
41
|
+
description: 'Manage git hooks for auto-sync',
|
|
42
|
+
example: 'prjct hooks install',
|
|
43
|
+
subcommands: ['install', 'uninstall', 'status'],
|
|
44
|
+
},
|
|
46
45
|
{
|
|
47
46
|
name: 'doctor',
|
|
48
47
|
description: 'Check system health and dependencies',
|
|
@@ -90,22 +89,24 @@ export function formatMainHelp(): string {
|
|
|
90
89
|
|
|
91
90
|
// Header
|
|
92
91
|
lines.push('')
|
|
93
|
-
lines.push(`${
|
|
94
|
-
lines.push(
|
|
92
|
+
lines.push(`${chalk.cyan.bold('prjct')} v${VERSION} - Context layer for AI coding agents`)
|
|
93
|
+
lines.push(chalk.dim('Works with Claude Code, Gemini CLI, Cursor, Windsurf, and more.'))
|
|
95
94
|
lines.push('')
|
|
96
95
|
|
|
97
96
|
// Quick Start
|
|
98
|
-
lines.push(
|
|
99
|
-
lines.push(
|
|
100
|
-
lines.push(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
lines.push(` ${
|
|
97
|
+
lines.push(chalk.bold('QUICK START'))
|
|
98
|
+
lines.push(chalk.dim('─'.repeat(60)))
|
|
99
|
+
lines.push(
|
|
100
|
+
` ${chalk.green('1.')} prjct start ${chalk.dim('# Configure AI providers')}`
|
|
101
|
+
)
|
|
102
|
+
lines.push(` ${chalk.green('2.')} cd my-project && prjct init`)
|
|
103
|
+
lines.push(` ${chalk.green('3.')} Open in Claude Code / Gemini CLI / Cursor`)
|
|
104
|
+
lines.push(` ${chalk.green('4.')} p. sync ${chalk.dim('# Analyze project')}`)
|
|
104
105
|
lines.push('')
|
|
105
106
|
|
|
106
107
|
// Terminal Commands
|
|
107
|
-
lines.push(
|
|
108
|
-
lines.push(
|
|
108
|
+
lines.push(chalk.bold('TERMINAL COMMANDS'))
|
|
109
|
+
lines.push(chalk.dim('─'.repeat(60)))
|
|
109
110
|
for (const cmd of TERMINAL_COMMANDS) {
|
|
110
111
|
const name = `prjct ${cmd.name}`.padEnd(22)
|
|
111
112
|
lines.push(` ${name} ${cmd.description}`)
|
|
@@ -113,10 +114,10 @@ export function formatMainHelp(): string {
|
|
|
113
114
|
lines.push('')
|
|
114
115
|
|
|
115
116
|
// AI Agent Commands
|
|
116
|
-
lines.push(`${
|
|
117
|
-
lines.push(
|
|
117
|
+
lines.push(`${chalk.bold('AI AGENT COMMANDS')} ${chalk.dim('(inside Claude/Gemini/Cursor)')}`)
|
|
118
|
+
lines.push(chalk.dim('─'.repeat(60)))
|
|
118
119
|
lines.push(` ${'Command'.padEnd(22)} Description`)
|
|
119
|
-
lines.push(` ${
|
|
120
|
+
lines.push(` ${chalk.dim('─'.repeat(56))}`)
|
|
120
121
|
|
|
121
122
|
// Core commands
|
|
122
123
|
const coreCommands = COMMANDS.filter((c) => c.group === 'core' && c.usage?.claude)
|
|
@@ -124,22 +125,24 @@ export function formatMainHelp(): string {
|
|
|
124
125
|
const usage = `p. ${cmd.name}`.padEnd(22)
|
|
125
126
|
lines.push(` ${usage} ${cmd.description}`)
|
|
126
127
|
}
|
|
127
|
-
lines.push(
|
|
128
|
+
lines.push(
|
|
129
|
+
` ${chalk.dim(`... and ${coreCommands.length - 10} more (run 'prjct help commands')`)}`
|
|
130
|
+
)
|
|
128
131
|
lines.push('')
|
|
129
132
|
|
|
130
133
|
// Global Flags
|
|
131
|
-
lines.push(
|
|
132
|
-
lines.push(
|
|
134
|
+
lines.push(chalk.bold('FLAGS'))
|
|
135
|
+
lines.push(chalk.dim('─'.repeat(60)))
|
|
133
136
|
for (const flag of GLOBAL_FLAGS) {
|
|
134
137
|
lines.push(` ${flag.flag.padEnd(22)} ${flag.description}`)
|
|
135
138
|
}
|
|
136
139
|
lines.push('')
|
|
137
140
|
|
|
138
141
|
// More Info
|
|
139
|
-
lines.push(
|
|
140
|
-
lines.push(
|
|
141
|
-
lines.push(` Documentation: ${
|
|
142
|
-
lines.push(` GitHub: ${
|
|
142
|
+
lines.push(chalk.bold('MORE INFO'))
|
|
143
|
+
lines.push(chalk.dim('─'.repeat(60)))
|
|
144
|
+
lines.push(` Documentation: ${chalk.cyan('https://prjct.app')}`)
|
|
145
|
+
lines.push(` GitHub: ${chalk.cyan('https://github.com/jlopezlira/prjct-cli')}`)
|
|
143
146
|
lines.push(` Per-command: prjct help <command>`)
|
|
144
147
|
lines.push('')
|
|
145
148
|
|
|
@@ -156,15 +159,15 @@ export function formatTerminalCommandHelp(commandName: string): string | null {
|
|
|
156
159
|
const lines: string[] = []
|
|
157
160
|
|
|
158
161
|
lines.push('')
|
|
159
|
-
lines.push(`${
|
|
162
|
+
lines.push(`${chalk.cyan.bold(`prjct ${cmd.name}`)} - ${cmd.description}`)
|
|
160
163
|
lines.push('')
|
|
161
164
|
|
|
162
|
-
lines.push(
|
|
165
|
+
lines.push(chalk.bold('USAGE'))
|
|
163
166
|
lines.push(` ${cmd.example}`)
|
|
164
167
|
lines.push('')
|
|
165
168
|
|
|
166
169
|
if (cmd.options) {
|
|
167
|
-
lines.push(
|
|
170
|
+
lines.push(chalk.bold('OPTIONS'))
|
|
168
171
|
for (const opt of cmd.options) {
|
|
169
172
|
lines.push(` ${opt}`)
|
|
170
173
|
}
|
|
@@ -172,7 +175,7 @@ export function formatTerminalCommandHelp(commandName: string): string | null {
|
|
|
172
175
|
}
|
|
173
176
|
|
|
174
177
|
if (cmd.subcommands) {
|
|
175
|
-
lines.push(
|
|
178
|
+
lines.push(chalk.bold('SUBCOMMANDS'))
|
|
176
179
|
for (const sub of cmd.subcommands) {
|
|
177
180
|
lines.push(` ${sub}`)
|
|
178
181
|
}
|
|
@@ -192,10 +195,10 @@ export function formatAgentCommandHelp(commandName: string): string | null {
|
|
|
192
195
|
const lines: string[] = []
|
|
193
196
|
|
|
194
197
|
lines.push('')
|
|
195
|
-
lines.push(`${
|
|
198
|
+
lines.push(`${chalk.cyan.bold(`p. ${cmd.name}`)} - ${cmd.description}`)
|
|
196
199
|
lines.push('')
|
|
197
200
|
|
|
198
|
-
lines.push(
|
|
201
|
+
lines.push(chalk.bold('USAGE'))
|
|
199
202
|
if (cmd.usage?.claude) {
|
|
200
203
|
lines.push(` Claude/Gemini: ${cmd.usage.claude.replace('/p:', 'p. ')}`)
|
|
201
204
|
}
|
|
@@ -205,13 +208,13 @@ export function formatAgentCommandHelp(commandName: string): string | null {
|
|
|
205
208
|
lines.push('')
|
|
206
209
|
|
|
207
210
|
if (cmd.params) {
|
|
208
|
-
lines.push(
|
|
211
|
+
lines.push(chalk.bold('PARAMETERS'))
|
|
209
212
|
lines.push(` ${cmd.params}`)
|
|
210
213
|
lines.push('')
|
|
211
214
|
}
|
|
212
215
|
|
|
213
216
|
if (cmd.features && cmd.features.length > 0) {
|
|
214
|
-
lines.push(
|
|
217
|
+
lines.push(chalk.bold('FEATURES'))
|
|
215
218
|
for (const feature of cmd.features) {
|
|
216
219
|
lines.push(` • ${feature}`)
|
|
217
220
|
}
|
|
@@ -219,17 +222,17 @@ export function formatAgentCommandHelp(commandName: string): string | null {
|
|
|
219
222
|
}
|
|
220
223
|
|
|
221
224
|
if (cmd.blockingRules) {
|
|
222
|
-
lines.push(
|
|
223
|
-
lines.push(` ${
|
|
225
|
+
lines.push(chalk.bold('REQUIREMENTS'))
|
|
226
|
+
lines.push(` ${chalk.yellow('⚠')} ${cmd.blockingRules.check}`)
|
|
224
227
|
lines.push('')
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
// Category info
|
|
228
231
|
const category = CATEGORIES[cmd.group]
|
|
229
232
|
if (category) {
|
|
230
|
-
lines.push(
|
|
233
|
+
lines.push(chalk.dim(`Category: ${category.title}`))
|
|
231
234
|
if (cmd.isOptional) {
|
|
232
|
-
lines.push(
|
|
235
|
+
lines.push(chalk.dim('This is an optional command.'))
|
|
233
236
|
}
|
|
234
237
|
lines.push('')
|
|
235
238
|
}
|
|
@@ -251,7 +254,7 @@ export function formatCommandHelp(commandName: string): string {
|
|
|
251
254
|
|
|
252
255
|
// Command not found
|
|
253
256
|
return `
|
|
254
|
-
${
|
|
257
|
+
${chalk.yellow(`Command '${commandName}' not found.`)}
|
|
255
258
|
|
|
256
259
|
Run 'prjct help' to see all available commands.
|
|
257
260
|
`
|
|
@@ -264,7 +267,7 @@ export function formatCommandList(): string {
|
|
|
264
267
|
const lines: string[] = []
|
|
265
268
|
|
|
266
269
|
lines.push('')
|
|
267
|
-
lines.push(
|
|
270
|
+
lines.push(chalk.cyan.bold('All Commands'))
|
|
268
271
|
lines.push('')
|
|
269
272
|
|
|
270
273
|
// Group by category
|
|
@@ -275,9 +278,9 @@ export function formatCommandList(): string {
|
|
|
275
278
|
if (categoryCommands.length === 0) continue
|
|
276
279
|
|
|
277
280
|
lines.push(
|
|
278
|
-
`${
|
|
281
|
+
`${chalk.bold(category.title)} ${chalk.dim(`(${categoryCommands.length} commands)`)}`
|
|
279
282
|
)
|
|
280
|
-
lines.push(
|
|
283
|
+
lines.push(chalk.dim(category.description))
|
|
281
284
|
lines.push('')
|
|
282
285
|
|
|
283
286
|
for (const cmd of categoryCommands) {
|
|
@@ -289,7 +292,7 @@ export function formatCommandList(): string {
|
|
|
289
292
|
lines.push('')
|
|
290
293
|
}
|
|
291
294
|
|
|
292
|
-
lines.push(
|
|
295
|
+
lines.push(chalk.dim("Run 'prjct help <command>' for detailed help on a specific command."))
|
|
293
296
|
lines.push('')
|
|
294
297
|
|
|
295
298
|
return lines.join('\n')
|
|
@@ -8,32 +8,25 @@
|
|
|
8
8
|
* @module utils/subtask-table
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
const RESET = '\x1b[0m'
|
|
13
|
-
const BOLD = '\x1b[1m'
|
|
14
|
-
const DIM = '\x1b[2m'
|
|
15
|
-
const GREEN = '\x1b[32m'
|
|
16
|
-
const YELLOW = '\x1b[33m'
|
|
17
|
-
const WHITE = '\x1b[37m'
|
|
18
|
-
const GRAY = '\x1b[90m'
|
|
11
|
+
import chalk from 'chalk'
|
|
19
12
|
|
|
20
13
|
// Color palette for domains (cycle through these)
|
|
21
14
|
const DOMAIN_COLOR_PALETTE = [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
15
|
+
chalk.cyan,
|
|
16
|
+
chalk.magenta,
|
|
17
|
+
chalk.yellow,
|
|
18
|
+
chalk.blue,
|
|
19
|
+
chalk.green,
|
|
20
|
+
chalk.redBright,
|
|
21
|
+
chalk.magentaBright,
|
|
22
|
+
chalk.cyanBright,
|
|
30
23
|
]
|
|
31
24
|
|
|
32
25
|
/**
|
|
33
26
|
* Get consistent color for a domain name using hash
|
|
34
27
|
* Same domain name always returns same color
|
|
35
28
|
*/
|
|
36
|
-
function getDomainColor(domain: string): string {
|
|
29
|
+
function getDomainColor(domain: string): (text: string) => string {
|
|
37
30
|
let hash = 0
|
|
38
31
|
for (const char of domain) {
|
|
39
32
|
hash = (hash << 5) - hash + char.charCodeAt(0)
|
|
@@ -43,7 +36,7 @@ function getDomainColor(domain: string): string {
|
|
|
43
36
|
return DOMAIN_COLOR_PALETTE[index]
|
|
44
37
|
}
|
|
45
38
|
|
|
46
|
-
//
|
|
39
|
+
// Terminal control sequences (not colors - chalk doesn't handle these)
|
|
47
40
|
const HIDE_CURSOR = '\x1b[?25l'
|
|
48
41
|
const SHOW_CURSOR = '\x1b[?25h'
|
|
49
42
|
|
|
@@ -67,9 +60,9 @@ function formatSubtaskLine(
|
|
|
67
60
|
subtask: SubtaskDisplay,
|
|
68
61
|
spinnerFrame: string = '▶'
|
|
69
62
|
): string {
|
|
70
|
-
const num =
|
|
71
|
-
const
|
|
72
|
-
const domain =
|
|
63
|
+
const num = chalk.dim(String(index + 1).padStart(2))
|
|
64
|
+
const domainColorFn = getDomainColor(subtask.domain)
|
|
65
|
+
const domain = domainColorFn(subtask.domain.padEnd(10))
|
|
73
66
|
const desc =
|
|
74
67
|
subtask.description.length > 32
|
|
75
68
|
? `${subtask.description.slice(0, 29)}...`
|
|
@@ -78,22 +71,22 @@ function formatSubtaskLine(
|
|
|
78
71
|
let status: string
|
|
79
72
|
switch (subtask.status) {
|
|
80
73
|
case 'completed':
|
|
81
|
-
status =
|
|
74
|
+
status = chalk.green('✓ Complete')
|
|
82
75
|
break
|
|
83
76
|
case 'in_progress':
|
|
84
|
-
status = `${
|
|
77
|
+
status = chalk.yellow(`${spinnerFrame} Working...`)
|
|
85
78
|
break
|
|
86
79
|
case 'pending':
|
|
87
|
-
status =
|
|
80
|
+
status = chalk.gray('○ Pending')
|
|
88
81
|
break
|
|
89
82
|
case 'failed':
|
|
90
|
-
status =
|
|
83
|
+
status = chalk.red('✗ Failed')
|
|
91
84
|
break
|
|
92
85
|
case 'blocked':
|
|
93
|
-
status =
|
|
86
|
+
status = chalk.gray('⊘ Blocked')
|
|
94
87
|
break
|
|
95
88
|
default:
|
|
96
|
-
status =
|
|
89
|
+
status = chalk.gray(`○ ${subtask.status}`)
|
|
97
90
|
}
|
|
98
91
|
|
|
99
92
|
return ` ${num} ${domain} ${desc} ${status}`
|
|
@@ -108,8 +101,8 @@ export function renderSubtaskProgress(subtasks: SubtaskDisplay[]): string {
|
|
|
108
101
|
const lines: string[] = []
|
|
109
102
|
|
|
110
103
|
lines.push('')
|
|
111
|
-
lines.push(` ${
|
|
112
|
-
lines.push(` ${
|
|
104
|
+
lines.push(` ${chalk.bold.white('SUBTASK PROGRESS')}`)
|
|
105
|
+
lines.push(` ${chalk.dim('─'.repeat(58))}`)
|
|
113
106
|
|
|
114
107
|
for (let i = 0; i < subtasks.length; i++) {
|
|
115
108
|
lines.push(formatSubtaskLine(i, subtasks[i]))
|
|
@@ -141,8 +134,8 @@ export function createSubtaskAnimation(subtasks: SubtaskDisplay[]) {
|
|
|
141
134
|
const lines: string[] = []
|
|
142
135
|
|
|
143
136
|
lines.push('')
|
|
144
|
-
lines.push(` ${
|
|
145
|
-
lines.push(` ${
|
|
137
|
+
lines.push(` ${chalk.bold.white('SUBTASK PROGRESS')}`)
|
|
138
|
+
lines.push(` ${chalk.dim('─'.repeat(58))}`)
|
|
146
139
|
|
|
147
140
|
for (let i = 0; i < subtasks.length; i++) {
|
|
148
141
|
lines.push(formatSubtaskLine(i, subtasks[i], spinnerFrame))
|
|
@@ -204,8 +197,8 @@ export function createSubtaskAnimation(subtasks: SubtaskDisplay[]) {
|
|
|
204
197
|
// Print final state with static icons
|
|
205
198
|
const finalLines: string[] = []
|
|
206
199
|
finalLines.push('')
|
|
207
|
-
finalLines.push(` ${
|
|
208
|
-
finalLines.push(` ${
|
|
200
|
+
finalLines.push(` ${chalk.bold.white('SUBTASK PROGRESS')}`)
|
|
201
|
+
finalLines.push(` ${chalk.dim('─'.repeat(58))}`)
|
|
209
202
|
for (let i = 0; i < subtasks.length; i++) {
|
|
210
203
|
finalLines.push(formatSubtaskLine(i, subtasks[i], '▶'))
|
|
211
204
|
}
|
|
@@ -226,7 +219,7 @@ export function createSubtaskAnimation(subtasks: SubtaskDisplay[]) {
|
|
|
226
219
|
* Output: "Progress: 2/4 subtasks complete"
|
|
227
220
|
*/
|
|
228
221
|
export function renderProgressLine(completed: number, total: number): string {
|
|
229
|
-
return ` ${
|
|
222
|
+
return ` ${chalk.dim('Progress:')} ${completed}/${total} subtasks complete`
|
|
230
223
|
}
|
|
231
224
|
|
|
232
225
|
// Legacy exports for backwards compatibility
|
|
@@ -15,17 +15,11 @@
|
|
|
15
15
|
|
|
16
16
|
import { exec } from 'node:child_process'
|
|
17
17
|
import { promisify } from 'node:util'
|
|
18
|
+
import chalk from 'chalk'
|
|
18
19
|
import memorySystem from '../agentic/memory-system'
|
|
19
20
|
|
|
20
21
|
const execAsync = promisify(exec)
|
|
21
22
|
|
|
22
|
-
// ANSI colors
|
|
23
|
-
const DIM = '\x1b[2m'
|
|
24
|
-
const GREEN = '\x1b[32m'
|
|
25
|
-
const RED = '\x1b[31m'
|
|
26
|
-
const YELLOW = '\x1b[33m'
|
|
27
|
-
const RESET = '\x1b[0m'
|
|
28
|
-
|
|
29
23
|
export type PreferenceScope = 'permanent' | 'session' | 'once'
|
|
30
24
|
export type HookPhase = 'before' | 'after' | 'skip'
|
|
31
25
|
export type HookCommand = 'task' | 'done' | 'ship' | 'sync'
|
|
@@ -169,7 +163,7 @@ export async function runWorkflowHooks(
|
|
|
169
163
|
oncePreferences.delete(key)
|
|
170
164
|
}
|
|
171
165
|
|
|
172
|
-
console.log(`\n${
|
|
166
|
+
console.log(`\n${chalk.dim(`Running ${phase}-${command}: ${action}`)}`)
|
|
173
167
|
|
|
174
168
|
try {
|
|
175
169
|
const startTime = Date.now()
|
|
@@ -180,12 +174,12 @@ export async function runWorkflowHooks(
|
|
|
180
174
|
})
|
|
181
175
|
const elapsed = Date.now() - startTime
|
|
182
176
|
const timeStr = elapsed > 1000 ? `${(elapsed / 1000).toFixed(1)}s` : `${elapsed}ms`
|
|
183
|
-
console.log(`${
|
|
177
|
+
console.log(`${chalk.green('✓')} ${chalk.dim(`(${timeStr})`)}`)
|
|
184
178
|
return { success: true }
|
|
185
179
|
} catch (error) {
|
|
186
|
-
console.log(
|
|
180
|
+
console.log(chalk.red('✗ failed'))
|
|
187
181
|
const errorMessage = (error as Error).message || 'Unknown error'
|
|
188
|
-
console.log(
|
|
182
|
+
console.log(chalk.dim(errorMessage.split('\n')[0]))
|
|
189
183
|
return { success: false, failed: action, output: errorMessage }
|
|
190
184
|
}
|
|
191
185
|
}
|
|
@@ -270,7 +264,7 @@ export function formatWorkflowPreferences(
|
|
|
270
264
|
}>
|
|
271
265
|
): string {
|
|
272
266
|
if (preferences.length === 0) {
|
|
273
|
-
return `${
|
|
267
|
+
return `${chalk.dim('No workflow preferences configured.')}\n\nSet one: "p. workflow antes de ship corre los tests"`
|
|
274
268
|
}
|
|
275
269
|
|
|
276
270
|
const lines: string[] = ['', 'WORKFLOW PREFERENCES', '────────────────────────────']
|
|
@@ -278,17 +272,17 @@ export function formatWorkflowPreferences(
|
|
|
278
272
|
for (const pref of preferences) {
|
|
279
273
|
const scopeBadge =
|
|
280
274
|
pref.scope === 'permanent'
|
|
281
|
-
?
|
|
275
|
+
? chalk.green('permanent')
|
|
282
276
|
: pref.scope === 'session'
|
|
283
|
-
?
|
|
284
|
-
:
|
|
277
|
+
? chalk.yellow('session')
|
|
278
|
+
: chalk.dim('once')
|
|
285
279
|
|
|
286
280
|
lines.push(` [${scopeBadge}] ${pref.key.padEnd(15)} → ${pref.action}`)
|
|
287
281
|
}
|
|
288
282
|
|
|
289
283
|
lines.push('')
|
|
290
|
-
lines.push(
|
|
291
|
-
lines.push(
|
|
284
|
+
lines.push(chalk.dim('Modify: "p. workflow antes de ship corre npm test"'))
|
|
285
|
+
lines.push(chalk.dim('Remove: "p. workflow quita el hook de ship"'))
|
|
292
286
|
|
|
293
287
|
return lines.join('\n')
|
|
294
288
|
}
|