devvami 1.4.2 → 1.5.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.
Files changed (96) hide show
  1. package/README.md +72 -0
  2. package/oclif.manifest.json +275 -235
  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 +257 -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 +215 -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 +349 -0
  62. package/src/services/ai-env-deployer.js +650 -0
  63. package/src/services/ai-env-scanner.js +983 -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 +117 -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 +1184 -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 +1089 -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,17 +1,17 @@
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 { loadConfig } from '../../services/config.js'
5
- import { listDocs, detectCurrentRepo } from '../../services/docs.js'
6
- import { renderTable } from '../../formatters/table.js'
4
+ import {loadConfig} from '../../services/config.js'
5
+ import {listDocs, detectCurrentRepo} from '../../services/docs.js'
6
+ import {renderTable} from '../../formatters/table.js'
7
7
 
8
8
  /**
9
9
  * @param {string} type
10
10
  * @returns {string}
11
11
  */
12
12
  function typeColor(type) {
13
- if (type === 'readme') return chalk.cyan(type)
14
- if (type === 'swagger') return chalk.yellow(type)
13
+ if (type === 'readme') return chalk.cyan(type)
14
+ if (type === 'swagger') return chalk.yellow(type)
15
15
  if (type === 'asyncapi') return chalk.green(type)
16
16
  return chalk.dim(type)
17
17
  }
@@ -38,12 +38,12 @@ export default class DocsList extends Command {
38
38
  static enableJsonFlag = true
39
39
 
40
40
  static flags = {
41
- repo: Flags.string({ char: 'r', description: 'Nome del repository (default: repo nella directory corrente)' }),
42
- search: Flags.string({ char: 's', description: 'Filtra per nome o percorso (case-insensitive)' }),
41
+ repo: Flags.string({char: 'r', description: 'Nome del repository (default: repo nella directory corrente)'}),
42
+ search: Flags.string({char: 's', description: 'Filtra per nome o percorso (case-insensitive)'}),
43
43
  }
44
44
 
45
45
  async run() {
46
- const { flags } = await this.parse(DocsList)
46
+ const {flags} = await this.parse(DocsList)
47
47
  const isJson = flags.json
48
48
  const config = await loadConfig()
49
49
 
@@ -55,13 +55,15 @@ export default class DocsList extends Command {
55
55
  repo = flags.repo
56
56
  } else {
57
57
  try {
58
- ;({ owner, repo } = await detectCurrentRepo())
58
+ ;({owner, repo} = await detectCurrentRepo())
59
59
  } catch (err) {
60
60
  this.error(/** @type {Error} */ (err).message)
61
61
  }
62
62
  }
63
63
 
64
- const spinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Fetching documentation...') }).start()
64
+ const spinner = isJson
65
+ ? null
66
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Fetching documentation...')}).start()
65
67
  let entries
66
68
  try {
67
69
  entries = await listDocs(owner, repo)
@@ -77,34 +79,36 @@ export default class DocsList extends Command {
77
79
  ? entries.filter((e) => e.name.toLowerCase().includes(q) || e.path.toLowerCase().includes(q))
78
80
  : entries
79
81
 
80
- if (isJson) return { repo, owner, entries: filtered, total: filtered.length }
82
+ if (isJson) return {repo, owner, entries: filtered, total: filtered.length}
81
83
 
82
84
  if (entries.length === 0) {
83
85
  this.log(chalk.dim(`No documentation found in ${owner}/${repo}.`))
84
- return { repo, owner, entries: [], total: 0 }
86
+ return {repo, owner, entries: [], total: 0}
85
87
  }
86
88
 
87
89
  if (filtered.length === 0) {
88
90
  this.log(chalk.dim(`No documentation matching "${flags.search}" in ${owner}/${repo}.`))
89
- return { repo, owner, entries: [], total: 0 }
91
+ return {repo, owner, entries: [], total: 0}
90
92
  }
91
93
 
92
- const filterInfo = q ? chalk.dim(` — search: ${chalk.white(`"${flags.search}"`)}`): ''
94
+ const filterInfo = q ? chalk.dim(` — search: ${chalk.white(`"${flags.search}"`)}`) : ''
93
95
  this.log(
94
96
  chalk.bold(`\nDocumentation in ${owner}/${repo}`) +
95
- filterInfo +
96
- chalk.dim(` (${filtered.length}${filtered.length < entries.length ? `/${entries.length}` : ''})`) +
97
- '\n',
97
+ filterInfo +
98
+ chalk.dim(` (${filtered.length}${filtered.length < entries.length ? `/${entries.length}` : ''})`) +
99
+ '\n',
98
100
  )
99
101
 
100
- this.log(renderTable(filtered, [
101
- { header: 'Type', key: 'type', width: 10, colorize: typeColor },
102
- { header: 'Name', key: 'name', width: 30 },
103
- { header: 'Path', key: 'path', width: 50 },
104
- { header: 'Size', key: 'size', width: 8, format: (v) => formatSize(Number(v)) },
105
- ]))
102
+ this.log(
103
+ renderTable(filtered, [
104
+ {header: 'Type', key: 'type', width: 10, colorize: typeColor},
105
+ {header: 'Name', key: 'name', width: 30},
106
+ {header: 'Path', key: 'path', width: 50},
107
+ {header: 'Size', key: 'size', width: 8, format: (v) => formatSize(Number(v))},
108
+ ]),
109
+ )
106
110
  this.log('')
107
111
 
108
- return { repo, owner, entries: filtered, total: filtered.length }
112
+ return {repo, owner, entries: filtered, total: filtered.length}
109
113
  }
110
114
  }
@@ -1,13 +1,13 @@
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 { loadConfig } from '../../services/config.js'
5
- import { listRepos } from '../../services/github.js'
6
- import { listProjectsDocs } from '../../services/docs.js'
7
- import { renderTable } from '../../formatters/table.js'
4
+ import {loadConfig} from '../../services/config.js'
5
+ import {listRepos} from '../../services/github.js'
6
+ import {listProjectsDocs} from '../../services/docs.js'
7
+ import {renderTable} from '../../formatters/table.js'
8
8
 
9
9
  export default class DocsProjects extends Command {
10
- static description = 'Mostra la documentazione disponibile per ogni repository dell\'organizzazione'
10
+ static description = "Mostra la documentazione disponibile per ogni repository dell'organizzazione"
11
11
 
12
12
  static examples = [
13
13
  '<%= config.bin %> docs projects',
@@ -18,11 +18,11 @@ export default class DocsProjects extends Command {
18
18
  static enableJsonFlag = true
19
19
 
20
20
  static flags = {
21
- search: Flags.string({ char: 's', description: 'Filtra per nome repository (case-insensitive)' }),
21
+ search: Flags.string({char: 's', description: 'Filtra per nome repository (case-insensitive)'}),
22
22
  }
23
23
 
24
24
  async run() {
25
- const { flags } = await this.parse(DocsProjects)
25
+ const {flags} = await this.parse(DocsProjects)
26
26
  const isJson = flags.json
27
27
  const config = await loadConfig()
28
28
 
@@ -31,7 +31,9 @@ export default class DocsProjects extends Command {
31
31
  }
32
32
 
33
33
  // 1. Fetch all repos
34
- const repoSpinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Fetching repositories...') }).start()
34
+ const repoSpinner = isJson
35
+ ? null
36
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Fetching repositories...')}).start()
35
37
  let repos
36
38
  try {
37
39
  repos = await listRepos(config.org)
@@ -43,7 +45,7 @@ export default class DocsProjects extends Command {
43
45
 
44
46
  if (repos.length === 0) {
45
47
  this.log(chalk.dim(`No repositories found in organization "${config.org}".`))
46
- return { org: config.org, projects: [], total: 0 }
48
+ return {org: config.org, projects: [], total: 0}
47
49
  }
48
50
 
49
51
  // 2. Filter by search
@@ -52,11 +54,17 @@ export default class DocsProjects extends Command {
52
54
 
53
55
  if (filteredRepos.length === 0) {
54
56
  this.log(chalk.dim(`No repositories matching "${flags.search}" in ${config.org}.`))
55
- return { org: config.org, projects: [], total: 0 }
57
+ return {org: config.org, projects: [], total: 0}
56
58
  }
57
59
 
58
60
  // 3. Scan each repo for docs
59
- const scanSpinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')(`Scanning docs in ${filteredRepos.length} repositories...`) }).start()
61
+ const scanSpinner = isJson
62
+ ? null
63
+ : ora({
64
+ spinner: 'arc',
65
+ color: false,
66
+ text: chalk.hex('#FF6B2B')(`Scanning docs in ${filteredRepos.length} repositories...`),
67
+ }).start()
60
68
 
61
69
  const repoNames = filteredRepos.map((r) => r.name)
62
70
  let projects
@@ -68,25 +76,51 @@ export default class DocsProjects extends Command {
68
76
  }
69
77
  scanSpinner?.stop()
70
78
 
71
- if (isJson) return { org: config.org, projects, total: projects.length }
79
+ if (isJson) return {org: config.org, projects, total: projects.length}
72
80
 
73
81
  const filterInfo = q ? chalk.dim(` — search: ${chalk.white(`"${flags.search}"`)}`) : ''
74
82
  this.log(
75
83
  chalk.bold(`\nDocumentation overview for ${config.org}`) +
76
- filterInfo +
77
- chalk.dim(` (${projects.length}${projects.length < repos.length ? `/${repos.length}` : ''})`) +
78
- '\n',
84
+ filterInfo +
85
+ chalk.dim(` (${projects.length}${projects.length < repos.length ? `/${repos.length}` : ''})`) +
86
+ '\n',
79
87
  )
80
88
 
81
- this.log(renderTable(projects, [
82
- { header: 'Repository', key: 'repo', width: 40 },
83
- { header: 'README', key: 'hasReadme', width: 8, format: (v) => v ? '✓' : '—', colorize: (v) => v === '✓' ? chalk.green(v) : chalk.dim(v) },
84
- { header: 'Docs', key: 'docsCount', width: 6, format: (v) => Number(v) > 0 ? String(v) : '—', colorize: (v) => v !== '—' ? chalk.cyan(v) : chalk.dim(v) },
85
- { header: 'Swagger', key: 'hasSwagger', width: 9, format: (v) => v ? '✓' : '—', colorize: (v) => v === '✓' ? chalk.yellow(v) : chalk.dim(v) },
86
- { header: 'AsyncAPI', key: 'hasAsyncApi', width: 10, format: (v) => v ? '✓' : '—', colorize: (v) => v === '✓' ? chalk.green(v) : chalk.dim(v) },
87
- ]))
89
+ this.log(
90
+ renderTable(projects, [
91
+ {header: 'Repository', key: 'repo', width: 40},
92
+ {
93
+ header: 'README',
94
+ key: 'hasReadme',
95
+ width: 8,
96
+ format: (v) => (v ? '✓' : '—'),
97
+ colorize: (v) => (v === '✓' ? chalk.green(v) : chalk.dim(v)),
98
+ },
99
+ {
100
+ header: 'Docs',
101
+ key: 'docsCount',
102
+ width: 6,
103
+ format: (v) => (Number(v) > 0 ? String(v) : '—'),
104
+ colorize: (v) => (v !== '—' ? chalk.cyan(v) : chalk.dim(v)),
105
+ },
106
+ {
107
+ header: 'Swagger',
108
+ key: 'hasSwagger',
109
+ width: 9,
110
+ format: (v) => (v ? '✓' : '—'),
111
+ colorize: (v) => (v === '✓' ? chalk.yellow(v) : chalk.dim(v)),
112
+ },
113
+ {
114
+ header: 'AsyncAPI',
115
+ key: 'hasAsyncApi',
116
+ width: 10,
117
+ format: (v) => (v ? '✓' : '—'),
118
+ colorize: (v) => (v === '✓' ? chalk.green(v) : chalk.dim(v)),
119
+ },
120
+ ]),
121
+ )
88
122
  this.log('')
89
123
 
90
- return { org: config.org, projects, total: projects.length }
124
+ return {org: config.org, projects, total: projects.length}
91
125
  }
92
126
  }
@@ -1,12 +1,12 @@
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 { loadConfig } from '../../services/config.js'
5
- import { listDocs, readFile, detectCurrentRepo, detectApiSpecType } from '../../services/docs.js'
6
- import { renderMarkdown, extractMermaidBlocks, toMermaidLiveUrl } from '../../formatters/markdown.js'
7
- import { parseOpenApi, parseAsyncApi } from '../../formatters/openapi.js'
8
- import { renderTable } from '../../formatters/table.js'
9
- import { openBrowser } from '../../utils/open-browser.js'
4
+ import {loadConfig} from '../../services/config.js'
5
+ import {listDocs, readFile, detectCurrentRepo, detectApiSpecType} from '../../services/docs.js'
6
+ import {renderMarkdown, extractMermaidBlocks, toMermaidLiveUrl} from '../../formatters/markdown.js'
7
+ import {parseOpenApi, parseAsyncApi} from '../../formatters/openapi.js'
8
+ import {renderTable} from '../../formatters/table.js'
9
+ import {openBrowser} from '../../utils/open-browser.js'
10
10
 
11
11
  /**
12
12
  * @param {string} method
@@ -52,17 +52,17 @@ export default class DocsRead extends Command {
52
52
  static enableJsonFlag = true
53
53
 
54
54
  static args = {
55
- file: Args.string({ description: 'Percorso del file da leggere (default: README)', required: false }),
55
+ file: Args.string({description: 'Percorso del file da leggere (default: README)', required: false}),
56
56
  }
57
57
 
58
58
  static flags = {
59
- repo: Flags.string({ char: 'r', description: 'Nome del repository (default: repo nella directory corrente)' }),
60
- raw: Flags.boolean({ description: 'Mostra contenuto grezzo senza parsing speciale', default: false }),
61
- render: Flags.boolean({ description: 'Apri i diagrammi Mermaid nel browser via mermaid.live', default: false }),
59
+ repo: Flags.string({char: 'r', description: 'Nome del repository (default: repo nella directory corrente)'}),
60
+ raw: Flags.boolean({description: 'Mostra contenuto grezzo senza parsing speciale', default: false}),
61
+ render: Flags.boolean({description: 'Apri i diagrammi Mermaid nel browser via mermaid.live', default: false}),
62
62
  }
63
63
 
64
64
  async run() {
65
- const { args, flags } = await this.parse(DocsRead)
65
+ const {args, flags} = await this.parse(DocsRead)
66
66
  const isJson = flags.json
67
67
  const config = await loadConfig()
68
68
 
@@ -74,7 +74,7 @@ export default class DocsRead extends Command {
74
74
  repo = flags.repo
75
75
  } else {
76
76
  try {
77
- ;({ owner, repo } = await detectCurrentRepo())
77
+ ;({owner, repo} = await detectCurrentRepo())
78
78
  } catch (err) {
79
79
  this.error(/** @type {Error} */ (err).message)
80
80
  }
@@ -83,7 +83,9 @@ export default class DocsRead extends Command {
83
83
  // Resolve file path
84
84
  let filePath = args.file
85
85
  if (!filePath) {
86
- const spinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Looking for README...') }).start()
86
+ const spinner = isJson
87
+ ? null
88
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Looking for README...')}).start()
87
89
  let entries
88
90
  try {
89
91
  entries = await listDocs(owner, repo)
@@ -95,24 +97,35 @@ export default class DocsRead extends Command {
95
97
  const readme = entries.find((e) => e.type === 'readme')
96
98
  if (!readme) {
97
99
  this.log(chalk.dim(`No README found in ${owner}/${repo}.`))
98
- return { repo, owner, path: null, type: null, content: null, size: 0 }
100
+ return {repo, owner, path: null, type: null, content: null, size: 0}
99
101
  }
100
102
  filePath = readme.path
101
103
  }
102
104
 
103
105
  // Fetch content
104
- const spinner2 = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')(`Reading ${filePath}...`) }).start()
105
- let content
106
- try {
107
- content = await readFile(owner, repo, filePath)
108
- } catch {
109
- spinner2?.stop()
110
- this.error(`File "${filePath}" not found in ${owner}/${repo}. Run \`dvmi docs list\` to see available documentation.`)
111
- }
106
+ const spinner2 = isJson
107
+ ? null
108
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')(`Reading ${filePath}...`)}).start()
109
+ let content
110
+ try {
111
+ content = await readFile(owner, repo, filePath)
112
+ } catch {
113
+ spinner2?.stop()
114
+ this.error(
115
+ `File "${filePath}" not found in ${owner}/${repo}. Run \`dvmi docs list\` to see available documentation.`,
116
+ )
117
+ }
112
118
  spinner2?.stop()
113
119
 
114
120
  if (isJson) {
115
- return { repo, owner, path: filePath, type: detectApiSpecType(filePath, content) ?? 'doc', content, size: content.length }
121
+ return {
122
+ repo,
123
+ owner,
124
+ path: filePath,
125
+ type: detectApiSpecType(filePath, content) ?? 'doc',
126
+ content,
127
+ size: content.length,
128
+ }
116
129
  }
117
130
 
118
131
  // Handle --render (Mermaid)
@@ -133,33 +146,37 @@ export default class DocsRead extends Command {
133
146
 
134
147
  // Render
135
148
  if (!flags.raw && specType === 'swagger') {
136
- const { endpoints, error } = parseOpenApi(content)
149
+ const {endpoints, error} = parseOpenApi(content)
137
150
  if (error || endpoints.length === 0) {
138
151
  this.log(chalk.yellow(`⚠ Could not parse "${filePath}" as OpenAPI spec (showing raw content). ${error ?? ''}`))
139
152
  this.log(content)
140
153
  } else {
141
154
  this.log(chalk.bold(`\nAPI Endpoints — ${filePath}\n`))
142
- this.log(renderTable(endpoints, [
143
- { header: 'Method', key: 'method', width: 8, colorize: methodColor },
144
- { header: 'Path', key: 'path', width: 45 },
145
- { header: 'Summary', key: 'summary', width: 40 },
146
- { header: 'Parameters', key: 'parameters', width: 30, format: (v) => v || '—' },
147
- ]))
155
+ this.log(
156
+ renderTable(endpoints, [
157
+ {header: 'Method', key: 'method', width: 8, colorize: methodColor},
158
+ {header: 'Path', key: 'path', width: 45},
159
+ {header: 'Summary', key: 'summary', width: 40},
160
+ {header: 'Parameters', key: 'parameters', width: 30, format: (v) => v || '—'},
161
+ ]),
162
+ )
148
163
  this.log('')
149
164
  }
150
165
  } else if (!flags.raw && specType === 'asyncapi') {
151
- const { channels, error } = parseAsyncApi(content)
166
+ const {channels, error} = parseAsyncApi(content)
152
167
  if (error || channels.length === 0) {
153
168
  this.log(chalk.yellow(`⚠ Could not parse "${filePath}" as AsyncAPI spec (showing raw content). ${error ?? ''}`))
154
169
  this.log(content)
155
170
  } else {
156
171
  this.log(chalk.bold(`\nAsyncAPI Channels — ${filePath}\n`))
157
- this.log(renderTable(channels, [
158
- { header: 'Channel', key: 'channel', width: 35 },
159
- { header: 'Operation', key: 'operation', width: 12, colorize: opColor },
160
- { header: 'Summary', key: 'summary', width: 40 },
161
- { header: 'Message', key: 'message', width: 25, format: (v) => v || '—' },
162
- ]))
172
+ this.log(
173
+ renderTable(channels, [
174
+ {header: 'Channel', key: 'channel', width: 35},
175
+ {header: 'Operation', key: 'operation', width: 12, colorize: opColor},
176
+ {header: 'Summary', key: 'summary', width: 40},
177
+ {header: 'Message', key: 'message', width: 25, format: (v) => v || '—'},
178
+ ]),
179
+ )
163
180
  this.log('')
164
181
  }
165
182
  } else {
@@ -167,6 +184,6 @@ export default class DocsRead extends Command {
167
184
  this.log(renderMarkdown(content))
168
185
  }
169
186
 
170
- return { repo, owner, path: filePath, type: specType ?? 'doc', content, size: content.length }
187
+ return {repo, owner, path: filePath, type: specType ?? 'doc', content, size: content.length}
171
188
  }
172
189
  }
@@ -1,9 +1,9 @@
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 { loadConfig } from '../../services/config.js'
5
- import { searchDocs, detectCurrentRepo } from '../../services/docs.js'
6
- import { renderTable } from '../../formatters/table.js'
4
+ import {loadConfig} from '../../services/config.js'
5
+ import {searchDocs, detectCurrentRepo} from '../../services/docs.js'
6
+ import {renderTable} from '../../formatters/table.js'
7
7
 
8
8
  const MAX_MATCHES_PER_FILE = 3
9
9
 
@@ -19,15 +19,15 @@ export default class DocsSearch extends Command {
19
19
  static enableJsonFlag = true
20
20
 
21
21
  static args = {
22
- term: Args.string({ description: 'Termine di ricerca (case-insensitive)', required: true }),
22
+ term: Args.string({description: 'Termine di ricerca (case-insensitive)', required: true}),
23
23
  }
24
24
 
25
25
  static flags = {
26
- repo: Flags.string({ char: 'r', description: 'Nome del repository (default: repo nella directory corrente)' }),
26
+ repo: Flags.string({char: 'r', description: 'Nome del repository (default: repo nella directory corrente)'}),
27
27
  }
28
28
 
29
29
  async run() {
30
- const { args, flags } = await this.parse(DocsSearch)
30
+ const {args, flags} = await this.parse(DocsSearch)
31
31
  const isJson = flags.json
32
32
  const config = await loadConfig()
33
33
 
@@ -39,13 +39,15 @@ export default class DocsSearch extends Command {
39
39
  repo = flags.repo
40
40
  } else {
41
41
  try {
42
- ;({ owner, repo } = await detectCurrentRepo())
42
+ ;({owner, repo} = await detectCurrentRepo())
43
43
  } catch (err) {
44
44
  this.error(/** @type {Error} */ (err).message)
45
45
  }
46
46
  }
47
47
 
48
- const spinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')(`Searching "${args.term}" in docs...`) }).start()
48
+ const spinner = isJson
49
+ ? null
50
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')(`Searching "${args.term}" in docs...`)}).start()
49
51
  let matches
50
52
  try {
51
53
  matches = await searchDocs(owner, repo, args.term)
@@ -55,14 +57,17 @@ export default class DocsSearch extends Command {
55
57
  }
56
58
  spinner?.stop()
57
59
 
58
- if (isJson) return { repo, owner, term: args.term, matches, total: matches.length }
60
+ if (isJson) return {repo, owner, term: args.term, matches, total: matches.length}
59
61
 
60
62
  if (matches.length === 0) {
61
63
  this.log(chalk.dim(`No matches found for "${args.term}" in ${owner}/${repo} documentation.`))
62
- return { repo, owner, term: args.term, matches: [], total: 0 }
64
+ return {repo, owner, term: args.term, matches: [], total: 0}
63
65
  }
64
66
 
65
- this.log(chalk.bold(`\nSearch results for "${args.term}" in ${owner}/${repo}`) + chalk.dim(` (${matches.length} match${matches.length === 1 ? '' : 'es'})\n`))
67
+ this.log(
68
+ chalk.bold(`\nSearch results for "${args.term}" in ${owner}/${repo}`) +
69
+ chalk.dim(` (${matches.length} match${matches.length === 1 ? '' : 'es'})\n`),
70
+ )
66
71
 
67
72
  // Group by file and limit rows
68
73
  /** @type {Map<string, import('../../types.js').SearchMatch[]>} */
@@ -80,24 +85,31 @@ export default class DocsSearch extends Command {
80
85
  rows.push(...shown)
81
86
  const extra = fileMatches.length - shown.length
82
87
  if (extra > 0) {
83
- rows.push({ file: '', line: 0, context: chalk.dim(`(+${extra} more in this file)`), occurrences: 0 })
88
+ rows.push({file: '', line: 0, context: chalk.dim(`(+${extra} more in this file)`), occurrences: 0})
84
89
  }
85
90
  }
86
91
 
87
92
  const q = args.term.toLowerCase()
88
- this.log(renderTable(rows, [
89
- { header: 'File', key: 'file', width: 35 },
90
- { header: 'Line', key: 'line', width: 5, format: (v) => Number(v) === 0 ? '' : String(v) },
91
- { header: 'Context', key: 'context', width: 65, format: (v) => {
92
- const s = String(v)
93
- // highlight term
94
- const re = new RegExp(q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi')
95
- return s.replace(re, (m) => chalk.yellow.bold(m))
96
- }},
97
- { header: 'Matches', key: 'occurrences', width: 8, format: (v) => Number(v) === 0 ? '' : `${v}` },
98
- ]))
93
+ this.log(
94
+ renderTable(rows, [
95
+ {header: 'File', key: 'file', width: 35},
96
+ {header: 'Line', key: 'line', width: 5, format: (v) => (Number(v) === 0 ? '' : String(v))},
97
+ {
98
+ header: 'Context',
99
+ key: 'context',
100
+ width: 65,
101
+ format: (v) => {
102
+ const s = String(v)
103
+ // highlight term
104
+ const re = new RegExp(q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi')
105
+ return s.replace(re, (m) => chalk.yellow.bold(m))
106
+ },
107
+ },
108
+ {header: 'Matches', key: 'occurrences', width: 8, format: (v) => (Number(v) === 0 ? '' : `${v}`)},
109
+ ]),
110
+ )
99
111
  this.log('')
100
112
 
101
- return { repo, owner, term: args.term, matches, total: matches.length }
113
+ return {repo, owner, term: args.term, matches, total: matches.length}
102
114
  }
103
115
  }
@@ -1,10 +1,10 @@
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 { typewriterLine } from '../utils/typewriter.js'
5
- import { which, exec } from '../services/shell.js'
6
- import { checkGitHubAuth, checkAWSAuth } from '../services/auth.js'
7
- import { formatDoctorCheck, formatDoctorSummary } from '../formatters/status.js'
4
+ import {typewriterLine} from '../utils/typewriter.js'
5
+ import {which, exec} from '../services/shell.js'
6
+ import {checkGitHubAuth, checkAWSAuth} from '../services/auth.js'
7
+ import {formatDoctorCheck, formatDoctorSummary} from '../formatters/status.js'
8
8
 
9
9
  /** @import { DoctorCheck } from '../types.js' */
10
10
 
@@ -20,28 +20,30 @@ export default class Doctor extends Command {
20
20
  static enableJsonFlag = true
21
21
 
22
22
  static flags = {
23
- verbose: Flags.boolean({ description: 'Mostra dettagli aggiuntivi', default: false }),
23
+ verbose: Flags.boolean({description: 'Mostra dettagli aggiuntivi', default: false}),
24
24
  }
25
25
 
26
26
  async run() {
27
- const { flags } = await this.parse(Doctor)
27
+ const {flags} = await this.parse(Doctor)
28
28
  const isJson = flags.json
29
29
 
30
- const spinner = isJson ? null : ora({ spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Running diagnostics...') }).start()
30
+ const spinner = isJson
31
+ ? null
32
+ : ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Running diagnostics...')}).start()
31
33
 
32
34
  /** @type {DoctorCheck[]} */
33
35
  const checks = []
34
36
 
35
37
  // Software checks
36
38
  const softwareChecks = [
37
- { name: 'Node.js', cmd: 'node', args: ['--version'], required: '>=24' },
38
- { name: 'nvm', cmd: 'nvm', args: ['--version'], required: null },
39
- { name: 'npm', cmd: 'npm', args: ['--version'], required: null },
40
- { name: 'Git', cmd: 'git', args: ['--version'], required: null },
41
- { name: 'gh CLI', cmd: 'gh', args: ['--version'], required: null },
42
- { name: 'Docker', cmd: 'docker', args: ['--version'], required: null },
43
- { name: 'AWS CLI', cmd: 'aws', args: ['--version'], required: null },
44
- { name: 'aws-vault', cmd: 'aws-vault', args: ['--version'], required: null },
39
+ {name: 'Node.js', cmd: 'node', args: ['--version'], required: '>=24'},
40
+ {name: 'nvm', cmd: 'nvm', args: ['--version'], required: null},
41
+ {name: 'npm', cmd: 'npm', args: ['--version'], required: null},
42
+ {name: 'Git', cmd: 'git', args: ['--version'], required: null},
43
+ {name: 'gh CLI', cmd: 'gh', args: ['--version'], required: null},
44
+ {name: 'Docker', cmd: 'docker', args: ['--version'], required: null},
45
+ {name: 'AWS CLI', cmd: 'aws', args: ['--version'], required: null},
46
+ {name: 'aws-vault', cmd: 'aws-vault', args: ['--version'], required: null},
45
47
  ]
46
48
 
47
49
  for (const check of softwareChecks) {
@@ -68,23 +70,23 @@ export default class Doctor extends Command {
68
70
  }
69
71
 
70
72
  // Auth checks
71
- const ghAuth = await checkGitHubAuth()
72
- checks.push({
73
- name: 'GitHub auth',
74
- status: ghAuth.authenticated ? 'ok' : 'fail',
75
- version: ghAuth.authenticated ? ghAuth.username ?? null : null,
76
- required: null,
77
- hint: ghAuth.authenticated ? null : 'Run `dvmi auth login`',
78
- })
79
-
80
- const awsAuth = await checkAWSAuth()
81
- checks.push({
82
- name: 'AWS auth',
83
- status: awsAuth.authenticated ? 'ok' : 'warn',
84
- version: awsAuth.authenticated ? awsAuth.account ?? null : null,
85
- required: null,
86
- hint: awsAuth.authenticated ? null : 'Run `dvmi auth login --aws`',
87
- })
73
+ const ghAuth = await checkGitHubAuth()
74
+ checks.push({
75
+ name: 'GitHub auth',
76
+ status: ghAuth.authenticated ? 'ok' : 'fail',
77
+ version: ghAuth.authenticated ? (ghAuth.username ?? null) : null,
78
+ required: null,
79
+ hint: ghAuth.authenticated ? null : 'Run `dvmi auth login`',
80
+ })
81
+
82
+ const awsAuth = await checkAWSAuth()
83
+ checks.push({
84
+ name: 'AWS auth',
85
+ status: awsAuth.authenticated ? 'ok' : 'warn',
86
+ version: awsAuth.authenticated ? (awsAuth.account ?? null) : null,
87
+ required: null,
88
+ hint: awsAuth.authenticated ? null : 'Run `dvmi auth login --aws`',
89
+ })
88
90
 
89
91
  spinner?.stop()
90
92
 
@@ -94,7 +96,7 @@ export default class Doctor extends Command {
94
96
  fail: checks.filter((c) => c.status === 'fail').length,
95
97
  }
96
98
 
97
- if (isJson) return { checks, summary }
99
+ if (isJson) return {checks, summary}
98
100
 
99
101
  await typewriterLine('Environment Diagnostics')
100
102
  for (const check of checks) {
@@ -110,6 +112,6 @@ export default class Doctor extends Command {
110
112
  }
111
113
  }
112
114
 
113
- return { checks, summary }
115
+ return {checks, summary}
114
116
  }
115
117
  }