Skip to content

Commit 4c5bde6

Browse files
committed
fix: prefer styleText for ansi
1 parent e49809f commit 4c5bde6

30 files changed

Lines changed: 204 additions & 192 deletions

src/agent/clis/cli-progress.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { StreamProgress } from './types.ts'
2+
import { styleText } from 'node:util'
23
import { TOOL_NAMES } from './types.ts'
34

45
const STATIC_REGEX_1 = /^\[(?:starting|retrying|cached)/
@@ -20,7 +21,7 @@ export function createToolProgress(log: ToolProgressLog): (progress: StreamProgr
2021
function emit(msg: string) {
2122
if (msg === lastMsg) {
2223
repeatCount++
23-
log.message(`${msg} \x1B[90m(+${repeatCount})\x1B[0m`)
24+
log.message(`${msg} ${styleText('gray', `(+${repeatCount})`)}`)
2425
}
2526
else {
2627
lastMsg = msg
@@ -37,18 +38,18 @@ export function createToolProgress(log: ToolProgressLog): (progress: StreamProgr
3738
if (now - last < TEXT_THROTTLE_MS)
3839
return
3940
lastTextEmit.set(key, now)
40-
const prefix = section ? `\x1B[90m[${section}]\x1B[0m ` : ''
41+
const prefix = section ? `${styleText('gray', `[${section}]`)} ` : ''
4142
// Count bullet items in accumulated text for meaningful progress
4243
const items = text ? (text.match(/^- (?:BREAKING|DEPRECATED|NEW|CHANGED|REMOVED|Use |Do |Set |Add |Avoid |Always |Never |Prefer |Check |Ensure )/gm)?.length ?? 0) : 0
43-
emit(items > 0 ? `${prefix}Writing... \x1B[90m(${items} items)\x1B[0m` : `${prefix}Writing...`)
44+
emit(items > 0 ? `${prefix}Writing... ${styleText('gray', `(${items} items)`)}` : `${prefix}Writing...`)
4445
return
4546
}
4647
if (type !== 'reasoning' || !chunk.startsWith('['))
4748
return
4849

4950
// Handle status messages like [starting...], [retrying...], [cached]
5051
if (STATIC_REGEX_1.test(chunk)) {
51-
const prefix = section ? `\x1B[90m[${section}]\x1B[0m ` : ''
52+
const prefix = section ? `${styleText('gray', `[${section}]`)} ` : ''
5253
emit(`${prefix}${chunk.slice(1, -1)}`)
5354
return
5455
}
@@ -65,12 +66,12 @@ export function createToolProgress(log: ToolProgressLog): (progress: StreamProgr
6566
const rawName = names[i]!
6667
const hint = hints[i] ?? hints[0] ?? ''
6768
const verb = TOOL_NAMES[rawName]?.verb ?? rawName
68-
const prefix = section ? `\x1B[90m[${section}]\x1B[0m ` : ''
69+
const prefix = section ? `${styleText('gray', `[${section}]`)} ` : ''
6970

7071
if ((rawName === 'Bash' || rawName === 'run_shell_command') && hint) {
7172
const searchMatch = hint.match(STATIC_REGEX_3)
7273
if (searchMatch) {
73-
emit(`${prefix}Searching \x1B[36m"${searchMatch[1]}"\x1B[0m`)
74+
emit(`${prefix}Searching ${styleText('cyan', `"${searchMatch[1]}"`)}`)
7475
}
7576
else if (hint.includes('skilld validate')) {
7677
emit(`${prefix}Validating...`)
@@ -82,7 +83,7 @@ export function createToolProgress(log: ToolProgressLog): (progress: StreamProgr
8283
}
8384
else {
8485
const path = shortenPath(hint || '...')
85-
emit(`${prefix}${verb} \x1B[90m${path}\x1B[0m`)
86+
emit(`${prefix}${verb} ${styleText('gray', path)}`)
8687
}
8788
}
8889
}

src/agent/skill-builder.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import type { FeaturesConfig } from '../core/config.ts'
1515
import type { CustomPrompt, OptimizeModel, OptimizeResult, SkillSection, StreamProgress } from './index.ts'
1616
import { mkdirSync, writeFileSync } from 'node:fs'
17+
import { styleText } from 'node:util'
1718
import * as p from '@clack/prompts'
1819
import { join, relative } from 'pathe'
1920
import { createReferenceCache, listReferenceFiles } from '../cache/index.ts'
@@ -176,10 +177,10 @@ export async function enhanceSkillWithLLM(ctx: SkillContext, run: EnhanceRunOpti
176177
if (debugLogsDir)
177178
p.log.info(`Debug logs: ${relative(process.cwd(), debugLogsDir)}`)
178179
if (error)
179-
p.log.warn(`\x1B[33mPartial failure: ${error}\x1B[0m`)
180+
p.log.warn(styleText('yellow', `Partial failure: ${error}`))
180181
if (warnings?.length) {
181182
for (const w of warnings)
182-
p.log.warn(`\x1B[33m${w}\x1B[0m`)
183+
p.log.warn(styleText('yellow', w))
183184
}
184185
}
185186
else {
@@ -294,7 +295,7 @@ export function writePromptFiles(ctx: SkillContext, run: PromptRunOptions): Skil
294295
const relDir = relative(process.cwd(), skillDir)
295296
const promptFiles = written.map(s => `PROMPT_${s}.md`).join(', ')
296297
const outputFileList = written.map(s => SECTION_OUTPUT_FILES[s]).join(', ')
297-
p.log.info(`Prompt files written to ${relDir}/.skilld/\n\x1B[2m\x1B[3m Read each prompt file (${promptFiles}) in ${relDir}/.skilld/, read the\n referenced files, then write your output to the matching file (${outputFileList}).\n When done, run: skilld assemble\x1B[0m`)
298+
p.log.info(`Prompt files written to ${relDir}/.skilld/\n${styleText(['dim', 'italic'], ` Read each prompt file (${promptFiles}) in ${relDir}/.skilld/, read the\n referenced files, then write your output to the matching file (${outputFileList}).\n When done, run: skilld assemble`)}`)
298299
}
299300

300301
return written

src/agent/skill-installer.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import type { SkillInfo } from '../core/lockfile.ts'
1616
import type { AgentType } from './index.ts'
1717
import { appendFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
18+
import { styleText } from 'node:util'
1819
import * as p from '@clack/prompts'
1920
import { join, relative } from 'pathe'
2021
import { isInteractive } from '../cli/env.ts'
@@ -212,10 +213,10 @@ export async function ensureGitignore(skillsDir: string, cwd: string, isGlobal:
212213

213214
const relSkillsDir = relative(cwd, skillsDir) || '.'
214215
p.log.info(
215-
`\x1B[1mGit guidance:\x1B[0m\n`
216-
+ ` \x1B[32m✓\x1B[0m Commit: \x1B[36m${relSkillsDir}/*/SKILL.md\x1B[0m\n`
217-
+ ` \x1B[32m✓\x1B[0m Commit: \x1B[36m${relSkillsDir}/skilld-lock.yaml\x1B[0m\n`
218-
+ ` \x1B[31m✗\x1B[0m Ignore: \x1B[36m${pattern}\x1B[0m \x1B[90m(recreated by \`skilld install\`)\x1B[0m`,
216+
`${styleText('bold', 'Git guidance:')}\n`
217+
+ ` ${styleText('green', '✓')} Commit: ${styleText('cyan', `${relSkillsDir}/*/SKILL.md`)}\n`
218+
+ ` ${styleText('green', '✓')} Commit: ${styleText('cyan', `${relSkillsDir}/skilld-lock.yaml`)}\n`
219+
+ ` ${styleText('red', '✗')} Ignore: ${styleText('cyan', pattern)} ${styleText('gray', '(recreated by `skilld install`)')}`,
219220
)
220221

221222
const add = await p.confirm({
@@ -285,8 +286,8 @@ export async function ensureAgentInstructions(agent: AgentType, cwd: string, isG
285286
`This tells your agent to check installed skills before making\n`
286287
+ `code changes. Without it, skills are available but may not\n`
287288
+ `activate automatically.\n`
288-
+ `\n`
289-
+ `\x1B[90m${getMdcSkillInstructions(agent)}\x1B[0m`,
289+
+ `\n${
290+
styleText('gray', getMdcSkillInstructions(agent))}`,
290291
`Create ${agentConfig.instructionFile}`,
291292
)
292293

@@ -328,8 +329,8 @@ export async function ensureAgentInstructions(agent: AgentType, cwd: string, isG
328329
`This tells your agent to check installed skills before making\n`
329330
+ `code changes. Without it, skills are available but may not\n`
330331
+ `activate automatically.\n`
331-
+ `\n`
332-
+ `\x1B[90m${getSkillInstructions(agent).replace(/\n/g, '\n')}\x1B[0m`,
332+
+ `\n${
333+
styleText('gray', getSkillInstructions(agent).replace(/\n/g, '\n'))}`,
333334
`${action} ${agentConfig.instructionFile}`,
334335
)
335336

src/cli.ts

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import type { PackageUsage } from './agent/detect-imports.ts'
33
import type { AgentType } from './agent/index.ts'
44
import { existsSync, readFileSync, realpathSync } from 'node:fs'
5+
import { styleText } from 'node:util'
56
import * as p from '@clack/prompts'
67
import { defineCommand, runMain } from 'citty'
78
import pLimit from 'p-limit'
@@ -49,7 +50,7 @@ function deprecatedForwarder(
4950
...cmd,
5051
meta: { ...cmd.meta, name: oldName },
5152
async run(ctx: any) {
52-
console.warn(`\x1B[33m⚠ \`skilld ${oldName}\` is deprecated. Use \`skilld ${newName}\` instead.\x1B[0m`)
53+
console.warn(styleText('yellow', `⚠ \`skilld ${oldName}\` is deprecated. Use \`skilld ${newName}\` instead.`))
5354
return original(ctx)
5455
},
5556
})
@@ -128,8 +129,8 @@ const main = defineCommand({
128129
}
129130
p.log.info(
130131
'No agent selected - skills export as portable PROMPT_*.md files.\n'
131-
+ ' Run \x1B[36mskilld add <pkg>\x1B[0m to generate prompts for any package.\n'
132-
+ ' Run \x1B[36mskilld config\x1B[0m to set a target agent later.',
132+
+ ` Run ${styleText('cyan', 'skilld add <pkg>')} to generate prompts for any package.\n`
133+
+ ` Run ${styleText('cyan', 'skilld config')} to set a target agent later.`,
133134
)
134135
return
135136
}
@@ -181,14 +182,14 @@ const main = defineCommand({
181182

182183
// Show self-update notification
183184
if (selfUpdate) {
184-
const released = selfUpdate.releasedAt ? `\x1B[90m · ${relativeTime(new Date(selfUpdate.releasedAt))}\x1B[0m` : ''
185+
const released = selfUpdate.releasedAt ? styleText('gray', ` · ${relativeTime(new Date(selfUpdate.releasedAt))}`) : ''
185186
const binPath = realpathSync(process.argv[1]!)
186187
const isLocal = binPath.startsWith(resolve(cwd, 'node_modules'))
187188
const flag = isLocal ? '' : ' -g'
188189
const cmd = `npx nypm add${flag} skilld@${selfUpdate.latest}`
189190
p.note(
190-
`\x1B[90m${version}\x1B[0m\x1B[1m\x1B[32m${selfUpdate.latest}\x1B[0m${released}\n\x1B[36m${cmd}\x1B[0m`,
191-
'\x1B[33mUpdate available\x1B[0m',
191+
`${styleText('gray', version)}${styleText(['bold', 'green'], selfUpdate.latest)}${released}\n${styleText('cyan', cmd)}`,
192+
styleText('yellow', 'Update available'),
192193
)
193194
}
194195

@@ -208,7 +209,7 @@ const main = defineCommand({
208209
const hasPkgJson = !!projectPkg
209210
const projectName = projectPkg?.parsed.name as string | undefined
210211
const projectLabel = projectName
211-
? `Generating skills for \x1B[36m${projectName}\x1B[0m`
212+
? `Generating skills for ${styleText('cyan', projectName)}`
212213
: 'Generating skills for current directory'
213214
p.log.step(projectLabel)
214215

@@ -219,7 +220,7 @@ const main = defineCommand({
219220
if (state.shipped.length > 0) {
220221
const totalShipped = state.shipped.reduce((sum, s) => sum + s.skills.length, 0)
221222
const names = state.shipped.map(s => s.packageName).join(', ')
222-
p.log.info(`\x1B[36m${totalShipped} ready-to-use skill${totalShipped > 1 ? 's' : ''}\x1B[0m shipped by your dependencies: ${names}`)
223+
p.log.info(`${styleText('cyan', `${totalShipped} ready-to-use skill${totalShipped > 1 ? 's' : ''}`)} shipped by your dependencies: ${names}`)
223224
}
224225

225226
p.log.info('Tip: Add skills for packages with complex APIs or frequent breaking changes - not every dependency needs one.')
@@ -228,7 +229,7 @@ const main = defineCommand({
228229
let setupComplete = false
229230
while (!setupComplete) {
230231
const shippedOption = state.shipped.length > 0
231-
? [{ label: 'Install shipped skills', value: 'shipped' as const, hint: `\x1B[36m${state.shipped.reduce((sum, s) => sum + s.skills.length, 0)} ready to use\x1B[0m` }]
232+
? [{ label: 'Install shipped skills', value: 'shipped' as const, hint: styleText('cyan', `${state.shipped.reduce((sum, s) => sum + s.skills.length, 0)} ready to use`) }]
232233
: []
233234

234235
const source = hasPkgJson
@@ -250,7 +251,7 @@ const main = defineCommand({
250251
}
251252

252253
if (source === 'skip') {
253-
p.log.info('Run \x1B[36mskilld add <pkg>\x1B[0m or \x1B[36mskilld\x1B[0m anytime to add skills.')
254+
p.log.info(`Run ${styleText('cyan', 'skilld add <pkg>')} or ${styleText('cyan', 'skilld')} anytime to add skills.`)
254255
return
255256
}
256257

@@ -344,7 +345,7 @@ const main = defineCommand({
344345
const hint = sourceMap.get(name) === 'preset' ? 'nuxt module' : undefined
345346
const pad = ' '.repeat(maxLen - name.length + 2)
346347
const meta = [ver, hint, repo].filter(Boolean).join(' ')
347-
return { label: meta ? `${name}${pad}\x1B[90m${meta}\x1B[39m` : name, value: name }
348+
return { label: meta ? `${name}${pad}${styleText('gray', meta)}` : name, value: name }
348349
}),
349350
initialValues: preselect,
350351
})
@@ -384,7 +385,7 @@ const main = defineCommand({
384385
const previewLines = previewContent.split('\n').slice(0, 20).join('\n').replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '').replace(/\x1B\].*?(?:\x07|\x1B\\)/g, '')
385386
const fileSize = (Buffer.byteLength(previewContent) / 1024).toFixed(1)
386387
p.note(
387-
`\x1B[90m${previewLines}\n...\x1B[0m`,
388+
styleText('gray', `${previewLines}\n...`),
388389
`${agents[agent].skillsDir}/${previewSkill.name}/SKILL.md (${fileSize} KB)`,
389390
)
390391
}
@@ -408,18 +409,18 @@ const main = defineCommand({
408409
}
409410
const verifyLine = agentInstalled
410411
? (verifyTips[agent] ?? '')
411-
: `Skills are ready in ${agents[agent].skillsDir}/.\n\x1B[90m${agentName} was not detected on this machine.\nInstall it to use these skills, or run \`skilld config\` to change agents.\x1B[0m`
412+
: `Skills are ready in ${agents[agent].skillsDir}/.\n${styleText('gray', `${agentName} was not detected on this machine.\nInstall it to use these skills, or run \`skilld config\` to change agents.`)}`
412413

413414
// Build a "try it" suggestion that tests skill-specific knowledge
414415
const firstPkg = previewSkill?.info?.packageName || previewSkill?.name
415416
const trySuggestion = firstPkg
416-
? `\n\n\x1B[36mTry it:\x1B[0m ask your agent "What are the gotchas or breaking changes in ${firstPkg}?"`
417+
? `\n\n${styleText('cyan', 'Try it:')} ask your agent "What are the gotchas or breaking changes in ${firstPkg}?"`
417418
: ''
418419

419420
p.note(
420421
`${verifyLine}${trySuggestion}\n\n`
421-
+ `Run \x1B[36mskilld info\x1B[0m to see installed skills.\n`
422-
+ `Run \x1B[36mskilld\x1B[0m again to add more, update, or search.`,
422+
+ `Run ${styleText('cyan', 'skilld info')} to see installed skills.\n`
423+
+ `Run ${styleText('cyan', 'skilld')} again to add more, update, or search.`,
423424
`${agentName} - next steps`,
424425
)
425426

@@ -439,13 +440,13 @@ const main = defineCommand({
439440

440441
let needsPrepareHook = !hasPrepareHook(cwd)
441442
if (needsPrepareHook) {
442-
p.log.warn(`\x1B[33mNo prepare hook.\x1B[0m Skills won't auto-restore on \x1B[36mnpm install\x1B[0m.`)
443+
p.log.warn(`${styleText('yellow', 'No prepare hook.')} Skills won't auto-restore on ${styleText('cyan', 'npm install')}.`)
443444
}
444445

445446
if (state.shipped.length > 0) {
446447
const totalSkills = state.shipped.reduce((sum, s) => sum + s.skills.length, 0)
447448
const names = state.shipped.map(s => s.packageName).join(', ')
448-
p.log.info(`\x1B[36m${totalSkills} ready-to-use skill${totalSkills > 1 ? 's' : ''}\x1B[0m shipped by your dependencies: ${names}`)
449+
p.log.info(`${styleText('cyan', `${totalSkills} ready-to-use skill${totalSkills > 1 ? 's' : ''}`)} shipped by your dependencies: ${names}`)
449450
}
450451

451452
const refreshState = async () => {
@@ -459,14 +460,14 @@ const main = defineCommand({
459460
const opts: Array<{ label: string, value: string, hint?: string }> = []
460461
if (state.shipped.length > 0) {
461462
const total = state.shipped.reduce((sum, s) => sum + s.skills.length, 0)
462-
opts.push({ label: 'Install shipped skills', value: 'shipped', hint: `\x1B[36m${total} available\x1B[0m` })
463+
opts.push({ label: 'Install shipped skills', value: 'shipped', hint: styleText('cyan', `${total} available`) })
463464
}
464465
opts.push({ label: 'Add new skills', value: 'install' })
465466
if (state.outdated.length > 0) {
466-
opts.push({ label: 'Update skills', value: 'update', hint: `\x1B[33m${state.outdated.length} outdated\x1B[0m` })
467+
opts.push({ label: 'Update skills', value: 'update', hint: styleText('yellow', `${state.outdated.length} outdated`) })
467468
}
468469
if (needsPrepareHook) {
469-
opts.push({ label: 'Setup prepare hook', value: 'prepare-hook', hint: '\x1B[33mrecommended\x1B[0m' })
470+
opts.push({ label: 'Setup prepare hook', value: 'prepare-hook', hint: styleText('yellow', 'recommended') })
470471
}
471472
opts.push(
472473
{ label: 'Remove skills', value: 'remove' },
@@ -595,7 +596,7 @@ const main = defineCommand({
595596
: undefined
596597
const pad = ' '.repeat(maxLen - name.length + 2)
597598
const meta = [ver, hint, repo].filter(Boolean).join(' ')
598-
return { label: meta ? `${name}${pad}\x1B[90m${meta}\x1B[39m` : name, value: name }
599+
return { label: meta ? `${name}${pad}${styleText('gray', meta)}` : name, value: name }
599600
}),
600601
initialValues: [],
601602
}))

src/cli/agent-prompt.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { AgentType } from '../agent/index.ts'
2+
import { styleText } from 'node:util'
23
import * as p from '@clack/prompts'
34
import { agents, detectInstalledAgents, detectProjectAgents, detectTargetAgent } from '../agent/index.ts'
45
import { readConfig, updateConfig } from '../core/config.ts'
@@ -95,7 +96,7 @@ export async function promptForAgent(): Promise<AgentType | 'none' | null> {
9596
? `Found ${installed.map(t => agents[t].displayName).join(', ')} but couldn't determine which to use`
9697
: 'No agents auto-detected'
9798
const crossNote = sharedIds.length > 1
98-
? `\n \x1B[90mTip: Picking Claude Code shares skills with ${sharedIds.filter(id => id !== 'claude-code').map(id => agents[id].displayName).join(', ')} automatically.\x1B[0m`
99+
? `\n ${styleText('gray', `Tip: Picking Claude Code shares skills with ${sharedIds.filter(id => id !== 'claude-code').map(id => agents[id].displayName).join(', ')} automatically.`)}`
99100
: ''
100101
p.log.warn(`${hint}\n Pick the agent you actively code with.${crossNote}`)
101102
}

src/cli/intro.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { AgentType } from '../agent/index.ts'
22
import type { ProjectState } from '../core/skills.ts'
3+
import { styleText } from 'node:util'
34
import { join } from 'pathe'
45
import { agents, detectInstalledAgents, getAgentVersion, getModelName } from '../agent/index.ts'
56
import { readPackageJsonSafe } from '../core/package-json.ts'
@@ -54,10 +55,10 @@ export function getLastSynced(state: ProjectState): string | null {
5455
}
5556

5657
export function introLine({ state, generators, modelId, agentId }: IntroOptions): string {
57-
const name = '\x1B[1m\x1B[35mskilld\x1B[0m'
58-
const ver = `\x1B[90mv${version}\x1B[0m`
58+
const name = styleText(['bold', 'magenta'], 'skilld')
59+
const ver = styleText('gray', `v${version}`)
5960
const lastSynced = getLastSynced(state)
60-
const synced = lastSynced ? ` · \x1B[90msynced ${lastSynced}\x1B[0m` : ''
61+
const synced = lastSynced ? ` · ${styleText('gray', `synced ${lastSynced}`)}` : ''
6162

6263
const parts: string[] = []
6364
if (modelId)
@@ -67,7 +68,7 @@ export function introLine({ state, generators, modelId, agentId }: IntroOptions)
6768
if (agentId && agents[agentId as AgentType])
6869
parts.push(agents[agentId as AgentType].displayName)
6970
const statusLine = parts.length > 0
70-
? `\n\x1B[90m${parts.join(' → ')}\x1B[0m`
71+
? `\n${styleText('gray', `${parts.join(' → ')}`)}`
7172
: ''
7273

7374
return `${name} ${ver}${synced}${statusLine}`
@@ -76,9 +77,9 @@ export function introLine({ state, generators, modelId, agentId }: IntroOptions)
7677
export function formatStatus(synced: number, outdated: number): string {
7778
const parts: string[] = []
7879
if (synced > 0)
79-
parts.push(`\x1B[32m${synced} synced\x1B[0m`)
80+
parts.push(styleText('green', `${synced} synced`))
8081
if (outdated > 0)
81-
parts.push(`\x1B[33m${outdated} outdated\x1B[0m`)
82+
parts.push(styleText('yellow', `${outdated} outdated`))
8283
return `Skills: ${parts.join(' · ')}`
8384
}
8485

0 commit comments

Comments
 (0)