devvami 1.4.2 → 1.5.0

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.
Files changed (96) hide show
  1. package/README.md +7 -0
  2. package/oclif.manifest.json +129 -89
  3. package/package.json +2 -1
  4. package/src/commands/auth/login.js +20 -16
  5. package/src/commands/changelog.js +12 -12
  6. package/src/commands/costs/get.js +14 -24
  7. package/src/commands/costs/trend.js +13 -24
  8. package/src/commands/create/repo.js +72 -54
  9. package/src/commands/docs/list.js +29 -25
  10. package/src/commands/docs/projects.js +58 -24
  11. package/src/commands/docs/read.js +56 -39
  12. package/src/commands/docs/search.js +37 -25
  13. package/src/commands/doctor.js +37 -35
  14. package/src/commands/dotfiles/add.js +51 -39
  15. package/src/commands/dotfiles/setup.js +62 -33
  16. package/src/commands/dotfiles/status.js +18 -18
  17. package/src/commands/dotfiles/sync.js +62 -46
  18. package/src/commands/init.js +143 -132
  19. package/src/commands/logs/index.js +10 -16
  20. package/src/commands/open.js +12 -12
  21. package/src/commands/pipeline/logs.js +8 -11
  22. package/src/commands/pipeline/rerun.js +21 -16
  23. package/src/commands/pipeline/status.js +28 -24
  24. package/src/commands/pr/create.js +40 -27
  25. package/src/commands/pr/detail.js +9 -7
  26. package/src/commands/pr/review.js +18 -19
  27. package/src/commands/pr/status.js +27 -21
  28. package/src/commands/prompts/browse.js +15 -15
  29. package/src/commands/prompts/download.js +15 -16
  30. package/src/commands/prompts/install-speckit.js +11 -12
  31. package/src/commands/prompts/list.js +12 -12
  32. package/src/commands/prompts/run.js +16 -19
  33. package/src/commands/repo/list.js +57 -41
  34. package/src/commands/search.js +20 -18
  35. package/src/commands/security/setup.js +38 -34
  36. package/src/commands/sync-config-ai/index.js +143 -0
  37. package/src/commands/tasks/assigned.js +43 -33
  38. package/src/commands/tasks/list.js +43 -33
  39. package/src/commands/tasks/today.js +32 -30
  40. package/src/commands/upgrade.js +18 -17
  41. package/src/commands/vuln/detail.js +8 -8
  42. package/src/commands/vuln/scan.js +39 -20
  43. package/src/commands/vuln/search.js +23 -18
  44. package/src/commands/welcome.js +2 -2
  45. package/src/commands/whoami.js +19 -23
  46. package/src/formatters/ai-config.js +127 -0
  47. package/src/formatters/charts.js +6 -23
  48. package/src/formatters/cost.js +1 -7
  49. package/src/formatters/dotfiles.js +48 -19
  50. package/src/formatters/markdown.js +11 -6
  51. package/src/formatters/openapi.js +7 -9
  52. package/src/formatters/prompts.js +69 -78
  53. package/src/formatters/security.js +2 -2
  54. package/src/formatters/status.js +1 -1
  55. package/src/formatters/table.js +1 -3
  56. package/src/formatters/vuln.js +33 -20
  57. package/src/help.js +162 -164
  58. package/src/hooks/init.js +1 -3
  59. package/src/hooks/postrun.js +5 -7
  60. package/src/index.js +1 -1
  61. package/src/services/ai-config-store.js +318 -0
  62. package/src/services/ai-env-deployer.js +444 -0
  63. package/src/services/ai-env-scanner.js +242 -0
  64. package/src/services/audit-detector.js +2 -2
  65. package/src/services/audit-runner.js +40 -31
  66. package/src/services/auth.js +9 -9
  67. package/src/services/awesome-copilot.js +7 -4
  68. package/src/services/aws-costs.js +22 -22
  69. package/src/services/clickup.js +26 -26
  70. package/src/services/cloudwatch-logs.js +5 -9
  71. package/src/services/config.js +13 -13
  72. package/src/services/docs.js +19 -20
  73. package/src/services/dotfiles.js +149 -51
  74. package/src/services/github.js +22 -24
  75. package/src/services/nvd.js +21 -31
  76. package/src/services/platform.js +2 -2
  77. package/src/services/prompts.js +23 -35
  78. package/src/services/security.js +135 -61
  79. package/src/services/shell.js +4 -4
  80. package/src/services/skills-sh.js +3 -9
  81. package/src/services/speckit.js +4 -7
  82. package/src/services/version-check.js +10 -10
  83. package/src/types.js +85 -0
  84. package/src/utils/aws-vault.js +18 -41
  85. package/src/utils/banner.js +5 -7
  86. package/src/utils/errors.js +42 -46
  87. package/src/utils/frontmatter.js +4 -4
  88. package/src/utils/gradient.js +18 -16
  89. package/src/utils/open-browser.js +3 -3
  90. package/src/utils/tui/form.js +1006 -0
  91. package/src/utils/tui/modal.js +15 -14
  92. package/src/utils/tui/navigable-table.js +16 -16
  93. package/src/utils/tui/tab-tui.js +800 -0
  94. package/src/utils/typewriter.js +3 -3
  95. package/src/utils/welcome.js +18 -21
  96. package/src/validators/repo-name.js +2 -2
@@ -1,9 +1,9 @@
1
- import { Command, Flags } from '@oclif/core'
1
+ import {Command, Flags} from '@oclif/core'
2
2
  import chalk from 'chalk'
3
3
  import ora from 'ora'
4
- import { listWorkflowRuns } from '../../services/github.js'
5
- import { exec } from '../../services/shell.js'
6
- import { renderTable, colorStatus } from '../../formatters/table.js'
4
+ import {listWorkflowRuns} from '../../services/github.js'
5
+ import {exec} from '../../services/shell.js'
6
+ import {renderTable, colorStatus} from '../../formatters/table.js'
7
7
 
8
8
  export default class PipelineStatus extends Command {
9
9
  static description = 'Stato GitHub Actions per il repo corrente'
@@ -17,46 +17,50 @@ export default class PipelineStatus extends Command {
17
17
  static enableJsonFlag = true
18
18
 
19
19
  static flags = {
20
- branch: Flags.string({ description: 'Filtra per branch' }),
21
- limit: Flags.integer({ description: 'Numero di run da mostrare', default: 10 }),
20
+ branch: Flags.string({description: 'Filtra per branch'}),
21
+ limit: Flags.integer({description: 'Numero di run da mostrare', default: 10}),
22
22
  }
23
23
 
24
24
  async run() {
25
- const { flags } = await this.parse(PipelineStatus)
25
+ const {flags} = await this.parse(PipelineStatus)
26
26
  const isJson = flags.json
27
27
 
28
- // Detect repo from git remote
29
- const remoteResult = await exec('git', ['remote', 'get-url', 'origin'])
30
- if (remoteResult.exitCode !== 0) {
31
- this.error('Not in a Git repository. Navigate to a repo or use `dvmi repo list`')
32
- }
28
+ // Detect repo from git remote
29
+ const remoteResult = await exec('git', ['remote', 'get-url', 'origin'])
30
+ if (remoteResult.exitCode !== 0) {
31
+ this.error('Not in a Git repository. Navigate to a repo or use `dvmi repo list`')
32
+ }
33
33
  const match = remoteResult.stdout.match(/github\.com[:/]([^/]+)\/([^/.]+)/)
34
34
  if (!match) this.error('Could not detect GitHub repository.')
35
35
  const [, owner, repo] = match
36
36
 
37
- const spinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Fetching pipeline runs...') }).start()
37
+ const spinner = isJson
38
+ ? null
39
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Fetching pipeline runs...')}).start()
38
40
  const runs = await listWorkflowRuns(owner, repo, {
39
41
  branch: flags.branch,
40
42
  limit: flags.limit,
41
43
  })
42
44
  spinner?.stop()
43
45
 
44
- if (isJson) return { runs }
46
+ if (isJson) return {runs}
45
47
 
46
48
  if (runs.length === 0) {
47
49
  this.log(chalk.dim('No workflow runs found.'))
48
- return { runs: [] }
50
+ return {runs: []}
49
51
  }
50
52
 
51
53
  this.log(chalk.bold('\nGitHub Actions runs:\n'))
52
- this.log(renderTable(runs, [
53
- { header: 'Status', key: 'conclusion', width: 10, format: (v) => colorStatus(v ? String(v) : 'pending') },
54
- { header: 'Workflow', key: 'name', width: 25 },
55
- { header: 'Branch', key: 'branch', width: 20 },
56
- { header: 'Duration', key: 'duration', width: 10, format: (v) => `${v}s` },
57
- { header: 'Actor', key: 'actor', width: 15 },
58
- ]))
59
-
60
- return { runs }
54
+ this.log(
55
+ renderTable(runs, [
56
+ {header: 'Status', key: 'conclusion', width: 10, format: (v) => colorStatus(v ? String(v) : 'pending')},
57
+ {header: 'Workflow', key: 'name', width: 25},
58
+ {header: 'Branch', key: 'branch', width: 20},
59
+ {header: 'Duration', key: 'duration', width: 10, format: (v) => `${v}s`},
60
+ {header: 'Actor', key: 'actor', width: 15},
61
+ ]),
62
+ )
63
+
64
+ return {runs}
61
65
  }
62
66
  }
@@ -1,11 +1,11 @@
1
- import { Command, Flags } from '@oclif/core'
1
+ import {Command, Flags} from '@oclif/core'
2
2
  import chalk from 'chalk'
3
3
  import ora from 'ora'
4
- import { confirm, input } from '@inquirer/prompts'
5
- import { createPR } from '../../services/github.js'
6
- import { exec } from '../../services/shell.js'
7
- import { readFile } from 'node:fs/promises'
8
- import { existsSync } from 'node:fs'
4
+ import {confirm, input} from '@inquirer/prompts'
5
+ import {createPR} from '../../services/github.js'
6
+ import {exec} from '../../services/shell.js'
7
+ import {readFile} from 'node:fs/promises'
8
+ import {existsSync} from 'node:fs'
9
9
 
10
10
  /**
11
11
  * @param {string} branchName
@@ -14,7 +14,7 @@ import { existsSync } from 'node:fs'
14
14
  function titleFromBranch(branchName) {
15
15
  const [type, ...rest] = branchName.split('/')
16
16
  const desc = rest.join('/').replace(/-/g, ' ')
17
- const typeMap = { feature: 'Feature', fix: 'Fix', chore: 'Chore', hotfix: 'Hotfix' }
17
+ const typeMap = {feature: 'Feature', fix: 'Fix', chore: 'Chore', hotfix: 'Hotfix'}
18
18
  return `${typeMap[type] ?? type}: ${desc}`
19
19
  }
20
20
 
@@ -23,7 +23,7 @@ function titleFromBranch(branchName) {
23
23
  * @returns {string[]}
24
24
  */
25
25
  function labelFromType(branchType) {
26
- const map = { feature: ['feature'], fix: ['bug'], chore: ['chore'], hotfix: ['critical'] }
26
+ const map = {feature: ['feature'], fix: ['bug'], chore: ['chore'], hotfix: ['critical']}
27
27
  return map[branchType] ?? []
28
28
  }
29
29
 
@@ -39,13 +39,13 @@ export default class PRCreate extends Command {
39
39
  static enableJsonFlag = true
40
40
 
41
41
  static flags = {
42
- title: Flags.string({ description: 'Titolo PR (default: auto-generated)' }),
43
- draft: Flags.boolean({ description: 'Crea come draft', default: false }),
44
- 'dry-run': Flags.boolean({ description: 'Preview senza eseguire', default: false }),
42
+ title: Flags.string({description: 'Titolo PR (default: auto-generated)'}),
43
+ draft: Flags.boolean({description: 'Crea come draft', default: false}),
44
+ 'dry-run': Flags.boolean({description: 'Preview senza eseguire', default: false}),
45
45
  }
46
46
 
47
47
  async run() {
48
- const { flags } = await this.parse(PRCreate)
48
+ const {flags} = await this.parse(PRCreate)
49
49
  const isJson = flags.json
50
50
  const isDryRun = flags['dry-run']
51
51
  // Get current branch
@@ -53,9 +53,9 @@ export default class PRCreate extends Command {
53
53
  if (branchResult.exitCode !== 0) this.error('Not in a Git repository.')
54
54
  const branch = branchResult.stdout
55
55
 
56
- if (['main', 'master', 'develop'].includes(branch)) {
57
- this.error(`You're on the default branch "${branch}". Create a feature branch first with \`dvmi branch create\``)
58
- }
56
+ if (['main', 'master', 'develop'].includes(branch)) {
57
+ this.error(`You're on the default branch "${branch}". Create a feature branch first with \`dvmi branch create\``)
58
+ }
59
59
 
60
60
  // Check for commits
61
61
  const repoUrl = await exec('git', ['remote', 'get-url', 'origin'])
@@ -64,7 +64,9 @@ export default class PRCreate extends Command {
64
64
  const [, owner, repo] = repoMatch
65
65
 
66
66
  // Push branch if needed
67
- const pushSpinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Pushing branch...') }).start()
67
+ const pushSpinner = isJson
68
+ ? null
69
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Pushing branch...')}).start()
68
70
  await exec('git', ['push', '-u', 'origin', branch])
69
71
  pushSpinner?.stop()
70
72
 
@@ -80,32 +82,43 @@ export default class PRCreate extends Command {
80
82
 
81
83
  // Generate title
82
84
  const autoTitle = titleFromBranch(branch)
83
- const title = flags.title ?? (isJson ? autoTitle : await input({ message: 'PR title:', default: autoTitle }))
85
+ const title = flags.title ?? (isJson ? autoTitle : await input({message: 'PR title:', default: autoTitle}))
84
86
  const branchType = branch.split('/')[0]
85
87
  const labels = labelFromType(branchType)
86
88
 
87
- const preview = { branch, base: 'main', title, labels, draft: flags.draft }
89
+ const preview = {branch, base: 'main', title, labels, draft: flags.draft}
88
90
  if (isDryRun) {
89
- if (isJson) return { pr: preview }
91
+ if (isJson) return {pr: preview}
90
92
  this.log(chalk.bold('Dry run — would create PR:'))
91
93
  this.log(JSON.stringify(preview, null, 2))
92
- return { pr: preview }
94
+ return {pr: preview}
93
95
  }
94
96
 
95
97
  if (!isJson) {
96
- const ok = await confirm({ message: `Create PR "${title}"?` })
97
- if (!ok) { this.log('Aborted.'); return }
98
+ const ok = await confirm({message: `Create PR "${title}"?`})
99
+ if (!ok) {
100
+ this.log('Aborted.')
101
+ return
102
+ }
98
103
  }
99
104
 
100
- const spinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Creating PR...') }).start()
105
+ const spinner = isJson
106
+ ? null
107
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Creating PR...')}).start()
101
108
  const pr = await createPR({
102
- owner, repo, title, body,
103
- head: branch, base: 'main',
104
- draft: flags.draft, labels, reviewers: [],
109
+ owner,
110
+ repo,
111
+ title,
112
+ body,
113
+ head: branch,
114
+ base: 'main',
115
+ draft: flags.draft,
116
+ labels,
117
+ reviewers: [],
105
118
  })
106
119
  spinner?.succeed(`PR created: ${pr.htmlUrl}`)
107
120
 
108
- const result = { pr: { number: pr.number, title, url: pr.htmlUrl, labels, draft: flags.draft } }
121
+ const result = {pr: {number: pr.number, title, url: pr.htmlUrl, labels, draft: flags.draft}}
109
122
 
110
123
  if (isJson) return result
111
124
  this.log(chalk.green('✓') + ' ' + pr.htmlUrl)
@@ -1,8 +1,8 @@
1
- import { Command, Args, Flags } from '@oclif/core'
1
+ import {Command, Args, Flags} from '@oclif/core'
2
2
  import chalk from 'chalk'
3
3
  import ora from 'ora'
4
- import { getPRDetail } from '../../services/github.js'
5
- import { exec } from '../../services/shell.js'
4
+ import {getPRDetail} from '../../services/github.js'
5
+ import {exec} from '../../services/shell.js'
6
6
 
7
7
  export default class PRDetail extends Command {
8
8
  static description = 'Dettaglio PR con commenti QA e checklist degli step'
@@ -16,15 +16,15 @@ export default class PRDetail extends Command {
16
16
  static enableJsonFlag = true
17
17
 
18
18
  static args = {
19
- number: Args.integer({ description: 'Numero della PR', required: true }),
19
+ number: Args.integer({description: 'Numero della PR', required: true}),
20
20
  }
21
21
 
22
22
  static flags = {
23
- repo: Flags.string({ description: 'Repository nel formato owner/repo (default: rilevato da git remote)' }),
23
+ repo: Flags.string({description: 'Repository nel formato owner/repo (default: rilevato da git remote)'}),
24
24
  }
25
25
 
26
26
  async run() {
27
- const { args, flags } = await this.parse(PRDetail)
27
+ const {args, flags} = await this.parse(PRDetail)
28
28
  const isJson = flags.json
29
29
 
30
30
  let owner, repo
@@ -39,7 +39,9 @@ export default class PRDetail extends Command {
39
39
  ;[, owner, repo] = match
40
40
  }
41
41
 
42
- const spinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Caricamento PR...') }).start()
42
+ const spinner = isJson
43
+ ? null
44
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Caricamento PR...')}).start()
43
45
  const detail = await getPRDetail(owner, repo, args.number)
44
46
  spinner?.stop()
45
47
 
@@ -1,22 +1,19 @@
1
- import { Command } from '@oclif/core'
1
+ import {Command} from '@oclif/core'
2
2
  import chalk from 'chalk'
3
3
  import ora from 'ora'
4
- import { listMyPRs } from '../../services/github.js'
5
- import { loadConfig } from '../../services/config.js'
6
- import { renderTable, colorStatus } from '../../formatters/table.js'
4
+ import {listMyPRs} from '../../services/github.js'
5
+ import {loadConfig} from '../../services/config.js'
6
+ import {renderTable, colorStatus} from '../../formatters/table.js'
7
7
 
8
8
  export default class PRReview extends Command {
9
9
  static description = 'Lista PR assegnate a te per la code review'
10
10
 
11
- static examples = [
12
- '<%= config.bin %> pr review',
13
- '<%= config.bin %> pr review --json',
14
- ]
11
+ static examples = ['<%= config.bin %> pr review', '<%= config.bin %> pr review --json']
15
12
 
16
13
  static enableJsonFlag = true
17
14
 
18
15
  async run() {
19
- const { flags } = await this.parse(PRReview)
16
+ const {flags} = await this.parse(PRReview)
20
17
  const isJson = flags.json
21
18
  const config = await loadConfig()
22
19
 
@@ -24,28 +21,30 @@ export default class PRReview extends Command {
24
21
  this.error("GitHub org non configurata. Esegui `dvmi init` per configurare l'ambiente.")
25
22
  }
26
23
 
27
- const spinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Caricamento PR in review...') }).start()
28
- const { reviewing } = await listMyPRs(config.org)
24
+ const spinner = isJson
25
+ ? null
26
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Caricamento PR in review...')}).start()
27
+ const {reviewing} = await listMyPRs(config.org)
29
28
  spinner?.stop()
30
29
 
31
- if (isJson) return { reviewing }
30
+ if (isJson) return {reviewing}
32
31
 
33
32
  if (reviewing.length === 0) {
34
33
  this.log(chalk.dim('Nessuna PR assegnata per review.'))
35
- return { reviewing }
34
+ return {reviewing}
36
35
  }
37
36
 
38
37
  this.log(chalk.bold(`\nPR ASSEGNATE PER REVIEW (${reviewing.length}):`))
39
38
  this.log(
40
39
  renderTable(reviewing, [
41
- { header: '#', key: 'number', width: 6 },
42
- { header: 'Titolo', key: 'title', width: 45 },
43
- { header: 'Autore', key: 'author', width: 20 },
44
- { header: 'Branch', key: 'headBranch', width: 30 },
45
- { header: 'CI', key: 'ciStatus', width: 10, format: (v) => colorStatus(String(v)) },
40
+ {header: '#', key: 'number', width: 6},
41
+ {header: 'Titolo', key: 'title', width: 45},
42
+ {header: 'Autore', key: 'author', width: 20},
43
+ {header: 'Branch', key: 'headBranch', width: 30},
44
+ {header: 'CI', key: 'ciStatus', width: 10, format: (v) => colorStatus(String(v))},
46
45
  ]),
47
46
  )
48
47
 
49
- return { reviewing }
48
+ return {reviewing}
50
49
  }
51
50
  }
@@ -1,9 +1,9 @@
1
- import { Command, Flags } from '@oclif/core'
1
+ import {Command, Flags} from '@oclif/core'
2
2
  import chalk from 'chalk'
3
3
  import ora from 'ora'
4
- import { listMyPRs } from '../../services/github.js'
5
- import { loadConfig } from '../../services/config.js'
6
- import { renderTable, colorStatus } from '../../formatters/table.js'
4
+ import {listMyPRs} from '../../services/github.js'
5
+ import {loadConfig} from '../../services/config.js'
6
+ import {renderTable, colorStatus} from '../../formatters/table.js'
7
7
 
8
8
  export default class PRStatus extends Command {
9
9
  static description = 'Stato delle tue PR aperte (come autore e come reviewer)'
@@ -17,12 +17,12 @@ export default class PRStatus extends Command {
17
17
  static enableJsonFlag = true
18
18
 
19
19
  static flags = {
20
- author: Flags.boolean({ description: 'Solo PR dove sei autore', default: false }),
21
- reviewer: Flags.boolean({ description: 'Solo PR dove sei reviewer', default: false }),
20
+ author: Flags.boolean({description: 'Solo PR dove sei autore', default: false}),
21
+ reviewer: Flags.boolean({description: 'Solo PR dove sei reviewer', default: false}),
22
22
  }
23
23
 
24
24
  async run() {
25
- const { flags } = await this.parse(PRStatus)
25
+ const {flags} = await this.parse(PRStatus)
26
26
  const isJson = flags.json
27
27
  const config = await loadConfig()
28
28
 
@@ -30,8 +30,10 @@ export default class PRStatus extends Command {
30
30
  this.error('GitHub org not configured. Run `dvmi init` to set up your environment.')
31
31
  }
32
32
 
33
- const spinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Fetching PRs...') }).start()
34
- const { authored, reviewing } = await listMyPRs(config.org)
33
+ const spinner = isJson
34
+ ? null
35
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Fetching PRs...')}).start()
36
+ const {authored, reviewing} = await listMyPRs(config.org)
35
37
  spinner?.stop()
36
38
 
37
39
  const showAuthored = !flags.reviewer || flags.author
@@ -46,25 +48,29 @@ export default class PRStatus extends Command {
46
48
 
47
49
  if (showAuthored && authored.length > 0) {
48
50
  this.log(chalk.bold('\nYOUR PRS:'))
49
- this.log(renderTable(authored, [
50
- { header: 'Repo', key: 'headBranch', width: 30, format: (v) => String(v).split('/')[0] },
51
- { header: 'Title', key: 'title', width: 40 },
52
- { header: 'CI', key: 'ciStatus', width: 10, format: (v) => colorStatus(String(v)) },
53
- { header: 'Review', key: 'reviewStatus', width: 20, format: (v) => colorStatus(String(v)) },
54
- ]))
51
+ this.log(
52
+ renderTable(authored, [
53
+ {header: 'Repo', key: 'headBranch', width: 30, format: (v) => String(v).split('/')[0]},
54
+ {header: 'Title', key: 'title', width: 40},
55
+ {header: 'CI', key: 'ciStatus', width: 10, format: (v) => colorStatus(String(v))},
56
+ {header: 'Review', key: 'reviewStatus', width: 20, format: (v) => colorStatus(String(v))},
57
+ ]),
58
+ )
55
59
  } else if (showAuthored) {
56
60
  this.log(chalk.dim('No authored PRs found.'))
57
61
  }
58
62
 
59
63
  if (showReviewing && reviewing.length > 0) {
60
64
  this.log(chalk.bold('\nREVIEW REQUESTED:'))
61
- this.log(renderTable(reviewing, [
62
- { header: 'Title', key: 'title', width: 40 },
63
- { header: 'Author', key: 'author', width: 20 },
64
- { header: 'CI', key: 'ciStatus', width: 10, format: (v) => colorStatus(String(v)) },
65
- ]))
65
+ this.log(
66
+ renderTable(reviewing, [
67
+ {header: 'Title', key: 'title', width: 40},
68
+ {header: 'Author', key: 'author', width: 20},
69
+ {header: 'CI', key: 'ciStatus', width: 10, format: (v) => colorStatus(String(v))},
70
+ ]),
71
+ )
66
72
  }
67
73
 
68
- return { authored, reviewing }
74
+ return {authored, reviewing}
69
75
  }
70
76
  }
@@ -1,11 +1,11 @@
1
- import { Command, Args, Flags } from '@oclif/core'
1
+ import {Command, Args, Flags} from '@oclif/core'
2
2
  import ora from 'ora'
3
3
  import chalk from 'chalk'
4
- import { select } from '@inquirer/prompts'
5
- import { searchSkills } from '../../services/skills-sh.js'
6
- import { fetchAwesomeEntries, AWESOME_CATEGORIES } from '../../services/awesome-copilot.js'
7
- import { formatSkillTable, formatAwesomeTable } from '../../formatters/prompts.js'
8
- import { DvmiError } from '../../utils/errors.js'
4
+ import {select} from '@inquirer/prompts'
5
+ import {searchSkills} from '../../services/skills-sh.js'
6
+ import {fetchAwesomeEntries, AWESOME_CATEGORIES} from '../../services/awesome-copilot.js'
7
+ import {formatSkillTable, formatAwesomeTable} from '../../formatters/prompts.js'
8
+ import {DvmiError} from '../../utils/errors.js'
9
9
 
10
10
  /** @import { Skill, AwesomeEntry } from '../../types.js' */
11
11
 
@@ -45,7 +45,7 @@ export default class PromptsBrowse extends Command {
45
45
  }
46
46
 
47
47
  async run() {
48
- const { args, flags } = await this.parse(PromptsBrowse)
48
+ const {args, flags} = await this.parse(PromptsBrowse)
49
49
  const isJson = flags.json
50
50
  const source = args.source
51
51
 
@@ -81,7 +81,7 @@ export default class PromptsBrowse extends Command {
81
81
  } catch (err) {
82
82
  spinner?.fail()
83
83
  if (err instanceof DvmiError) {
84
- this.error(err.message, { exit: err.exitCode, suggestions: [err.hint] })
84
+ this.error(err.message, {exit: err.exitCode, suggestions: [err.hint]})
85
85
  }
86
86
  throw err
87
87
  }
@@ -89,7 +89,7 @@ export default class PromptsBrowse extends Command {
89
89
  spinner?.stop()
90
90
 
91
91
  if (isJson) {
92
- return { skills, total: skills.length }
92
+ return {skills, total: skills.length}
93
93
  }
94
94
 
95
95
  this.log(
@@ -99,7 +99,7 @@ export default class PromptsBrowse extends Command {
99
99
  )
100
100
  this.log(formatSkillTable(skills))
101
101
 
102
- return { skills, total: skills.length }
102
+ return {skills, total: skills.length}
103
103
  }
104
104
 
105
105
  // source === 'awesome'
@@ -118,7 +118,7 @@ export default class PromptsBrowse extends Command {
118
118
  } catch (err) {
119
119
  spinner?.fail()
120
120
  if (err instanceof DvmiError) {
121
- this.error(err.message, { exit: err.exitCode, suggestions: [err.hint] })
121
+ this.error(err.message, {exit: err.exitCode, suggestions: [err.hint]})
122
122
  }
123
123
  throw err
124
124
  }
@@ -126,7 +126,7 @@ export default class PromptsBrowse extends Command {
126
126
  spinner?.stop()
127
127
 
128
128
  if (isJson) {
129
- return { entries, total: entries.length, category }
129
+ return {entries, total: entries.length, category}
130
130
  }
131
131
 
132
132
  this.log(
@@ -140,8 +140,8 @@ export default class PromptsBrowse extends Command {
140
140
 
141
141
  if (entries.length > 0) {
142
142
  try {
143
- const choices = entries.map((e) => ({ name: `${e.name} ${chalk.dim(e.url)}`, value: e }))
144
- choices.push({ name: chalk.dim('← Exit'), value: /** @type {AwesomeEntry} */ (null) })
143
+ const choices = entries.map((e) => ({name: `${e.name} ${chalk.dim(e.url)}`, value: e}))
144
+ choices.push({name: chalk.dim('← Exit'), value: /** @type {AwesomeEntry} */ (null)})
145
145
 
146
146
  const selected = await select({
147
147
  message: 'Select an entry to view its URL (or Exit):',
@@ -159,6 +159,6 @@ export default class PromptsBrowse extends Command {
159
159
  }
160
160
  }
161
161
 
162
- return { entries, total: entries.length, category }
162
+ return {entries, total: entries.length, category}
163
163
  }
164
164
  }
@@ -1,11 +1,11 @@
1
- import { Command, Args, Flags } from '@oclif/core'
1
+ import {Command, Args, Flags} from '@oclif/core'
2
2
  import ora from 'ora'
3
3
  import chalk from 'chalk'
4
- import { select, confirm } from '@inquirer/prompts'
5
- import { join } from 'node:path'
6
- import { listPrompts, downloadPrompt } from '../../services/prompts.js'
7
- import { loadConfig } from '../../services/config.js'
8
- import { DvmiError } from '../../utils/errors.js'
4
+ import {select, confirm} from '@inquirer/prompts'
5
+ import {join} from 'node:path'
6
+ import {listPrompts, downloadPrompt} from '../../services/prompts.js'
7
+ import {loadConfig} from '../../services/config.js'
8
+ import {DvmiError} from '../../utils/errors.js'
9
9
 
10
10
  /** @import { Prompt } from '../../types.js' */
11
11
 
@@ -38,7 +38,7 @@ export default class PromptsDownload extends Command {
38
38
  }
39
39
 
40
40
  async run() {
41
- const { args, flags } = await this.parse(PromptsDownload)
41
+ const {args, flags} = await this.parse(PromptsDownload)
42
42
  const isJson = flags.json
43
43
 
44
44
  // Determine local prompts directory from config or default to cwd/.prompts
@@ -48,8 +48,7 @@ export default class PromptsDownload extends Command {
48
48
  } catch {
49
49
  /* use defaults */
50
50
  }
51
- const localDir =
52
- process.env.DVMI_PROMPTS_DIR ?? config.promptsDir ?? join(process.cwd(), DEFAULT_PROMPTS_DIR)
51
+ const localDir = process.env.DVMI_PROMPTS_DIR ?? config.promptsDir ?? join(process.cwd(), DEFAULT_PROMPTS_DIR)
53
52
 
54
53
  // Resolve path interactively if not provided (only in interactive mode)
55
54
  let relativePath = args.path
@@ -74,7 +73,7 @@ export default class PromptsDownload extends Command {
74
73
  } catch (err) {
75
74
  spinner.fail()
76
75
  if (err instanceof DvmiError) {
77
- this.error(err.message, { exit: err.exitCode, suggestions: [err.hint] })
76
+ this.error(err.message, {exit: err.exitCode, suggestions: [err.hint]})
78
77
  }
79
78
  throw err
80
79
  }
@@ -82,14 +81,14 @@ export default class PromptsDownload extends Command {
82
81
 
83
82
  if (prompts.length === 0) {
84
83
  this.log(chalk.yellow('No prompts found in the repository.'))
85
- return { downloaded: [], skipped: [] }
84
+ return {downloaded: [], skipped: []}
86
85
  }
87
86
 
88
87
  const choices = prompts.map((p) => ({
89
88
  name: `${p.path} ${chalk.dim(p.title)}`,
90
89
  value: p.path,
91
90
  }))
92
- relativePath = await select({ message: 'Select a prompt to download:', choices })
91
+ relativePath = await select({message: 'Select a prompt to download:', choices})
93
92
  }
94
93
 
95
94
  // Attempt download (skips automatically if file exists and --overwrite not set)
@@ -103,11 +102,11 @@ export default class PromptsDownload extends Command {
103
102
 
104
103
  let result
105
104
  try {
106
- result = await downloadPrompt(relativePath, localDir, { overwrite: flags.overwrite })
105
+ result = await downloadPrompt(relativePath, localDir, {overwrite: flags.overwrite})
107
106
  } catch (err) {
108
107
  spinner?.fail()
109
108
  if (err instanceof DvmiError) {
110
- this.error(err.message, { exit: err.exitCode, suggestions: [err.hint] })
109
+ this.error(err.message, {exit: err.exitCode, suggestions: [err.hint]})
111
110
  }
112
111
  throw err
113
112
  }
@@ -122,10 +121,10 @@ export default class PromptsDownload extends Command {
122
121
  })
123
122
  if (shouldOverwrite) {
124
123
  try {
125
- result = await downloadPrompt(relativePath, localDir, { overwrite: true })
124
+ result = await downloadPrompt(relativePath, localDir, {overwrite: true})
126
125
  } catch (err) {
127
126
  if (err instanceof DvmiError) {
128
- this.error(err.message, { exit: err.exitCode, suggestions: [err.hint] })
127
+ this.error(err.message, {exit: err.exitCode, suggestions: [err.hint]})
129
128
  }
130
129
  throw err
131
130
  }
@@ -1,9 +1,9 @@
1
- import { Command, Flags } from '@oclif/core'
1
+ import {Command, Flags} from '@oclif/core'
2
2
  import ora from 'ora'
3
3
  import chalk from 'chalk'
4
- import { loadConfig } from '../../services/config.js'
5
- import { isUvInstalled, isSpecifyInstalled, installSpecifyCli, runSpecifyInit } from '../../services/speckit.js'
6
- import { DvmiError } from '../../utils/errors.js'
4
+ import {loadConfig} from '../../services/config.js'
5
+ import {isUvInstalled, isSpecifyInstalled, installSpecifyCli, runSpecifyInit} from '../../services/speckit.js'
6
+ import {DvmiError} from '../../utils/errors.js'
7
7
 
8
8
  /**
9
9
  * Map from dvmi's `aiTool` config values to spec-kit's `--ai` flag values.
@@ -26,8 +26,7 @@ export default class PromptsInstallSpeckit extends Command {
26
26
 
27
27
  static flags = {
28
28
  ai: Flags.string({
29
- description:
30
- 'AI agent to pass to `specify init --ai` (defaults to the aiTool set in `dvmi init`)',
29
+ description: 'AI agent to pass to `specify init --ai` (defaults to the aiTool set in `dvmi init`)',
31
30
  options: ['opencode', 'copilot', 'claude', 'gemini', 'cursor-agent', 'codex', 'windsurf', 'kiro-cli', 'amp'],
32
31
  }),
33
32
  force: Flags.boolean({
@@ -42,7 +41,7 @@ export default class PromptsInstallSpeckit extends Command {
42
41
  }
43
42
 
44
43
  async run() {
45
- const { flags } = await this.parse(PromptsInstallSpeckit)
44
+ const {flags} = await this.parse(PromptsInstallSpeckit)
46
45
 
47
46
  // ── 1. Require uv ────────────────────────────────────────────────────────
48
47
  if (!(await isUvInstalled())) {
@@ -57,15 +56,15 @@ export default class PromptsInstallSpeckit extends Command {
57
56
 
58
57
  if (!alreadyInstalled || flags.reinstall) {
59
58
  const label = alreadyInstalled ? 'Reinstalling specify-cli...' : 'Installing specify-cli...'
60
- const spinner = ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')(label) }).start()
59
+ const spinner = ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')(label)}).start()
61
60
 
62
61
  try {
63
- await installSpecifyCli({ force: flags.reinstall })
62
+ await installSpecifyCli({force: flags.reinstall})
64
63
  spinner.succeed(chalk.green('specify-cli installed'))
65
64
  } catch (err) {
66
65
  spinner.fail()
67
66
  if (err instanceof DvmiError) {
68
- this.error(err.message, { exit: 1, suggestions: [err.hint] })
67
+ this.error(err.message, {exit: 1, suggestions: [err.hint]})
69
68
  }
70
69
  throw err
71
70
  }
@@ -86,10 +85,10 @@ export default class PromptsInstallSpeckit extends Command {
86
85
  this.log('')
87
86
 
88
87
  try {
89
- await runSpecifyInit(process.cwd(), { ai: aiFlag, force: flags.force })
88
+ await runSpecifyInit(process.cwd(), {ai: aiFlag, force: flags.force})
90
89
  } catch (err) {
91
90
  if (err instanceof DvmiError) {
92
- this.error(err.message, { exit: 1, suggestions: [err.hint] })
91
+ this.error(err.message, {exit: 1, suggestions: [err.hint]})
93
92
  }
94
93
  throw err
95
94
  }