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,4 +1,4 @@
1
- import { load } from 'js-yaml'
1
+ import {load} from 'js-yaml'
2
2
 
3
3
  /** @import { APIEndpoint, AsyncChannel } from '../types.js' */
4
4
 
@@ -45,7 +45,7 @@ export function isAsyncApi(doc) {
45
45
  export function parseOpenApi(content) {
46
46
  const doc = parseYamlOrJson(content)
47
47
  if (!doc || !isOpenApi(doc)) {
48
- return { endpoints: [], error: 'Not a valid OpenAPI/Swagger document' }
48
+ return {endpoints: [], error: 'Not a valid OpenAPI/Swagger document'}
49
49
  }
50
50
 
51
51
  /** @type {APIEndpoint[]} */
@@ -58,9 +58,7 @@ export function parseOpenApi(content) {
58
58
  if (!['get', 'post', 'put', 'patch', 'delete', 'head', 'options'].includes(method)) continue
59
59
  const operation = /** @type {Record<string, unknown>} */ (op)
60
60
  const rawParams = /** @type {Array<Record<string, unknown>>} */ (operation.parameters ?? [])
61
- const parameters = rawParams
62
- .map((p) => (p.required ? `${p.name}*` : String(p.name)))
63
- .join(', ')
61
+ const parameters = rawParams.map((p) => (p.required ? `${p.name}*` : String(p.name))).join(', ')
64
62
  endpoints.push({
65
63
  method: method.toUpperCase(),
66
64
  path,
@@ -70,7 +68,7 @@ export function parseOpenApi(content) {
70
68
  }
71
69
  }
72
70
 
73
- return { endpoints, error: null }
71
+ return {endpoints, error: null}
74
72
  }
75
73
 
76
74
  /**
@@ -81,7 +79,7 @@ export function parseOpenApi(content) {
81
79
  export function parseAsyncApi(content) {
82
80
  const doc = parseYamlOrJson(content)
83
81
  if (!doc || !isAsyncApi(doc)) {
84
- return { channels: [], error: 'Not a valid AsyncAPI document' }
82
+ return {channels: [], error: 'Not a valid AsyncAPI document'}
85
83
  }
86
84
 
87
85
  /** @type {AsyncChannel[]} */
@@ -98,7 +96,7 @@ export function parseAsyncApi(content) {
98
96
  return String(ch.$ref ?? '').includes(channelName) || String(ch ?? '') === channelName
99
97
  })
100
98
  if (matchingOps.length === 0) {
101
- channels.push({ channel: channelName, operation: '—', summary: '', message: '—' })
99
+ channels.push({channel: channelName, operation: '—', summary: '', message: '—'})
102
100
  }
103
101
  for (const op of matchingOps) {
104
102
  const msgTitle = resolveMessageTitle(op.messages)
@@ -129,7 +127,7 @@ export function parseAsyncApi(content) {
129
127
  }
130
128
  }
131
129
 
132
- return { channels, error: null }
130
+ return {channels, error: null}
133
131
  }
134
132
 
135
133
  /**
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk'
2
- import { marked } from 'marked'
3
- import { renderTable } from './table.js'
2
+ import {marked} from 'marked'
3
+ import {renderTable} from './table.js'
4
4
 
5
5
  /** @import { Prompt, Skill, AwesomeEntry } from '../types.js' */
6
6
 
@@ -16,31 +16,28 @@ export function formatPromptTable(prompts) {
16
16
  return chalk.dim('No prompts found.')
17
17
  }
18
18
 
19
- return renderTable(
20
- /** @type {Record<string, unknown>[]} */ (prompts),
21
- [
22
- {
23
- header: 'Title',
24
- key: 'title',
25
- width: 36,
26
- colorize: (v) => chalk.hex('#FF9A5C')(v),
27
- },
28
- {
29
- header: 'Category',
30
- key: 'category',
31
- width: 16,
32
- format: (v) => v ?? '—',
33
- colorize: (v) => chalk.hex('#4A9EFF')(v),
34
- },
35
- {
36
- header: 'Description',
37
- key: 'description',
38
- width: 60,
39
- format: (v) => v ?? '—',
40
- colorize: (v) => chalk.white(v),
41
- },
42
- ],
43
- )
19
+ return renderTable(/** @type {Record<string, unknown>[]} */ (prompts), [
20
+ {
21
+ header: 'Title',
22
+ key: 'title',
23
+ width: 36,
24
+ colorize: (v) => chalk.hex('#FF9A5C')(v),
25
+ },
26
+ {
27
+ header: 'Category',
28
+ key: 'category',
29
+ width: 16,
30
+ format: (v) => v ?? '',
31
+ colorize: (v) => chalk.hex('#4A9EFF')(v),
32
+ },
33
+ {
34
+ header: 'Description',
35
+ key: 'description',
36
+ width: 60,
37
+ format: (v) => v ?? '',
38
+ colorize: (v) => chalk.white(v),
39
+ },
40
+ ])
44
41
  }
45
42
 
46
43
  /**
@@ -63,7 +60,7 @@ export function formatPromptBody(prompt) {
63
60
  .join('\n')
64
61
 
65
62
  // Render markdown to plain terminal text by stripping HTML tags from marked output
66
- const rendered = marked(prompt.body, { async: false })
63
+ const rendered = marked(prompt.body, {async: false})
67
64
  const plain = String(rendered)
68
65
  .replace(/<[^>]+>/g, '')
69
66
  .replace(/&amp;/g, '&')
@@ -91,31 +88,28 @@ export function formatSkillTable(skills) {
91
88
  return chalk.dim('No skills found.')
92
89
  }
93
90
 
94
- return renderTable(
95
- /** @type {Record<string, unknown>[]} */ (skills),
96
- [
97
- {
98
- header: 'Name',
99
- key: 'name',
100
- width: 36,
101
- colorize: (v) => chalk.hex('#FF9A5C')(v),
102
- },
103
- {
104
- header: 'Installs',
105
- key: 'installs',
106
- width: 10,
107
- format: (v) => (v != null ? String(v) : '—'),
108
- colorize: (v) => chalk.hex('#4A9EFF')(v),
109
- },
110
- {
111
- header: 'Description',
112
- key: 'description',
113
- width: 60,
114
- format: (v) => v ?? '—',
115
- colorize: (v) => chalk.white(v),
116
- },
117
- ],
118
- )
91
+ return renderTable(/** @type {Record<string, unknown>[]} */ (skills), [
92
+ {
93
+ header: 'Name',
94
+ key: 'name',
95
+ width: 36,
96
+ colorize: (v) => chalk.hex('#FF9A5C')(v),
97
+ },
98
+ {
99
+ header: 'Installs',
100
+ key: 'installs',
101
+ width: 10,
102
+ format: (v) => (v != null ? String(v) : ''),
103
+ colorize: (v) => chalk.hex('#4A9EFF')(v),
104
+ },
105
+ {
106
+ header: 'Description',
107
+ key: 'description',
108
+ width: 60,
109
+ format: (v) => v ?? '',
110
+ colorize: (v) => chalk.white(v),
111
+ },
112
+ ])
119
113
  }
120
114
 
121
115
  /**
@@ -131,29 +125,26 @@ export function formatAwesomeTable(entries, category) {
131
125
  return chalk.dim(category ? `No entries found for category "${category}".` : 'No entries found.')
132
126
  }
133
127
 
134
- return renderTable(
135
- /** @type {Record<string, unknown>[]} */ (entries),
136
- [
137
- {
138
- header: 'Name',
139
- key: 'name',
140
- width: 36,
141
- colorize: (v) => chalk.hex('#FF9A5C')(v),
142
- },
143
- {
144
- header: 'Category',
145
- key: 'category',
146
- width: 14,
147
- format: (v) => v ?? '—',
148
- colorize: (v) => chalk.hex('#4A9EFF')(v),
149
- },
150
- {
151
- header: 'Description',
152
- key: 'description',
153
- width: 58,
154
- format: (v) => v ?? '—',
155
- colorize: (v) => chalk.white(v),
156
- },
157
- ],
158
- )
128
+ return renderTable(/** @type {Record<string, unknown>[]} */ (entries), [
129
+ {
130
+ header: 'Name',
131
+ key: 'name',
132
+ width: 36,
133
+ colorize: (v) => chalk.hex('#FF9A5C')(v),
134
+ },
135
+ {
136
+ header: 'Category',
137
+ key: 'category',
138
+ width: 14,
139
+ format: (v) => v ?? '',
140
+ colorize: (v) => chalk.hex('#4A9EFF')(v),
141
+ },
142
+ {
143
+ header: 'Description',
144
+ key: 'description',
145
+ width: 58,
146
+ format: (v) => v ?? '',
147
+ colorize: (v) => chalk.white(v),
148
+ },
149
+ ])
159
150
  }
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk'
2
- import { deriveOverallStatus } from '../services/security.js'
2
+ import {deriveOverallStatus} from '../services/security.js'
3
3
 
4
4
  /** @import { SetupSession, SecurityToolStatus, PlatformInfo } from '../types.js' */
5
5
 
@@ -116,4 +116,4 @@ export function formatSecuritySummary(session, platformInfo) {
116
116
  * @param {SecurityToolStatus[]} tools
117
117
  * @returns {'success'|'partial'|'not-configured'}
118
118
  */
119
- export { deriveOverallStatus }
119
+ export {deriveOverallStatus}
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk'
2
- import { colorStatus } from './table.js'
2
+ import {colorStatus} from './table.js'
3
3
 
4
4
  /** @import { DoctorCheck } from '../types.js' */
5
5
 
@@ -51,9 +51,7 @@ export function renderTable(rows, columns) {
51
51
  })
52
52
 
53
53
  // Header row
54
- const header = columns
55
- .map((col, i) => chalk.bold.white(col.header.padEnd(widths[i])))
56
- .join(' ')
54
+ const header = columns.map((col, i) => chalk.bold.white(col.header.padEnd(widths[i]))).join(' ')
57
55
 
58
56
  // Divider
59
57
  const divider = chalk.dim(widths.map((w) => '─'.repeat(w)).join(' '))
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk'
2
- import { renderTable } from './table.js'
3
- import { NVD_ATTRIBUTION } from '../services/nvd.js'
2
+ import {renderTable} from './table.js'
3
+ import {NVD_ATTRIBUTION} from '../services/nvd.js'
4
4
 
5
5
  /** @import { CveSearchResult, CveDetail, VulnerabilityFinding, ScanResult } from '../types.js' */
6
6
 
@@ -11,11 +11,16 @@ import { NVD_ATTRIBUTION } from '../services/nvd.js'
11
11
  */
12
12
  export function colorSeverity(severity) {
13
13
  switch (severity) {
14
- case 'Critical': return chalk.red.bold(severity)
15
- case 'High': return chalk.red(severity)
16
- case 'Medium': return chalk.yellow(severity)
17
- case 'Low': return chalk.blue(severity)
18
- default: return chalk.gray(severity)
14
+ case 'Critical':
15
+ return chalk.red.bold(severity)
16
+ case 'High':
17
+ return chalk.red(severity)
18
+ case 'Medium':
19
+ return chalk.yellow(severity)
20
+ case 'Low':
21
+ return chalk.blue(severity)
22
+ default:
23
+ return chalk.gray(severity)
19
24
  }
20
25
  }
21
26
 
@@ -83,16 +88,21 @@ export function formatCveSearchTable(results, keyword, days, totalResults) {
83
88
  }))
84
89
 
85
90
  const table = renderTable(rows, [
86
- { header: 'CVE ID', key: 'id', colorize: (v) => chalk.cyan(v) },
87
- { header: 'Severity', key: 'severity', colorize: (v) => colorSeverity(v) },
88
- { header: 'Score', key: 'score', width: 5 },
89
- { header: 'Published', key: 'published', width: 10 },
90
- { header: 'Description', key: 'description', width: 90 },
91
- { header: 'Reference', key: 'reference', width: 45 },
91
+ {header: 'CVE ID', key: 'id', colorize: (v) => chalk.cyan(v)},
92
+ {header: 'Severity', key: 'severity', colorize: (v) => colorSeverity(v)},
93
+ {header: 'Score', key: 'score', width: 5},
94
+ {header: 'Published', key: 'published', width: 10},
95
+ {header: 'Description', key: 'description', width: 90},
96
+ {header: 'Reference', key: 'reference', width: 45},
92
97
  ])
93
98
 
94
99
  // Indent table by 2 spaces
95
- lines.push(table.split('\n').map((l) => ` ${l}`).join('\n'))
100
+ lines.push(
101
+ table
102
+ .split('\n')
103
+ .map((l) => ` ${l}`)
104
+ .join('\n'),
105
+ )
96
106
  lines.push('')
97
107
  lines.push(`Showing ${results.length} of ${totalResults} results.`)
98
108
  lines.push(chalk.dim(NVD_ATTRIBUTION))
@@ -250,14 +260,17 @@ export function formatFindingsTable(findings) {
250
260
  }))
251
261
 
252
262
  const table = renderTable(rows, [
253
- { header: 'Package', key: 'pkg', width: 20 },
254
- { header: 'Version', key: 'version', width: 12 },
255
- { header: 'Severity', key: 'severity', colorize: (v) => colorSeverity(v) },
256
- { header: 'CVE', key: 'cve', colorize: (v) => (v !== '—' ? chalk.cyan(v) : chalk.gray(v)) },
257
- { header: 'Title', key: 'title', width: 40 },
263
+ {header: 'Package', key: 'pkg', width: 20},
264
+ {header: 'Version', key: 'version', width: 12},
265
+ {header: 'Severity', key: 'severity', colorize: (v) => colorSeverity(v)},
266
+ {header: 'CVE', key: 'cve', colorize: (v) => (v !== '—' ? chalk.cyan(v) : chalk.gray(v))},
267
+ {header: 'Title', key: 'title', width: 40},
258
268
  ])
259
269
 
260
- return table.split('\n').map((l) => ` ${l}`).join('\n')
270
+ return table
271
+ .split('\n')
272
+ .map((l) => ` ${l}`)
273
+ .join('\n')
261
274
  }
262
275
 
263
276
  /**