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.
- package/README.md +7 -0
- package/oclif.manifest.json +129 -89
- package/package.json +2 -1
- package/src/commands/auth/login.js +20 -16
- package/src/commands/changelog.js +12 -12
- package/src/commands/costs/get.js +14 -24
- package/src/commands/costs/trend.js +13 -24
- package/src/commands/create/repo.js +72 -54
- package/src/commands/docs/list.js +29 -25
- package/src/commands/docs/projects.js +58 -24
- package/src/commands/docs/read.js +56 -39
- package/src/commands/docs/search.js +37 -25
- package/src/commands/doctor.js +37 -35
- package/src/commands/dotfiles/add.js +51 -39
- package/src/commands/dotfiles/setup.js +62 -33
- package/src/commands/dotfiles/status.js +18 -18
- package/src/commands/dotfiles/sync.js +62 -46
- package/src/commands/init.js +143 -132
- package/src/commands/logs/index.js +10 -16
- package/src/commands/open.js +12 -12
- package/src/commands/pipeline/logs.js +8 -11
- package/src/commands/pipeline/rerun.js +21 -16
- package/src/commands/pipeline/status.js +28 -24
- package/src/commands/pr/create.js +40 -27
- package/src/commands/pr/detail.js +9 -7
- package/src/commands/pr/review.js +18 -19
- package/src/commands/pr/status.js +27 -21
- package/src/commands/prompts/browse.js +15 -15
- package/src/commands/prompts/download.js +15 -16
- package/src/commands/prompts/install-speckit.js +11 -12
- package/src/commands/prompts/list.js +12 -12
- package/src/commands/prompts/run.js +16 -19
- package/src/commands/repo/list.js +57 -41
- package/src/commands/search.js +20 -18
- package/src/commands/security/setup.js +38 -34
- package/src/commands/sync-config-ai/index.js +143 -0
- package/src/commands/tasks/assigned.js +43 -33
- package/src/commands/tasks/list.js +43 -33
- package/src/commands/tasks/today.js +32 -30
- package/src/commands/upgrade.js +18 -17
- package/src/commands/vuln/detail.js +8 -8
- package/src/commands/vuln/scan.js +39 -20
- package/src/commands/vuln/search.js +23 -18
- package/src/commands/welcome.js +2 -2
- package/src/commands/whoami.js +19 -23
- package/src/formatters/ai-config.js +127 -0
- package/src/formatters/charts.js +6 -23
- package/src/formatters/cost.js +1 -7
- package/src/formatters/dotfiles.js +48 -19
- package/src/formatters/markdown.js +11 -6
- package/src/formatters/openapi.js +7 -9
- package/src/formatters/prompts.js +69 -78
- package/src/formatters/security.js +2 -2
- package/src/formatters/status.js +1 -1
- package/src/formatters/table.js +1 -3
- package/src/formatters/vuln.js +33 -20
- package/src/help.js +162 -164
- package/src/hooks/init.js +1 -3
- package/src/hooks/postrun.js +5 -7
- package/src/index.js +1 -1
- package/src/services/ai-config-store.js +318 -0
- package/src/services/ai-env-deployer.js +444 -0
- package/src/services/ai-env-scanner.js +242 -0
- package/src/services/audit-detector.js +2 -2
- package/src/services/audit-runner.js +40 -31
- package/src/services/auth.js +9 -9
- package/src/services/awesome-copilot.js +7 -4
- package/src/services/aws-costs.js +22 -22
- package/src/services/clickup.js +26 -26
- package/src/services/cloudwatch-logs.js +5 -9
- package/src/services/config.js +13 -13
- package/src/services/docs.js +19 -20
- package/src/services/dotfiles.js +149 -51
- package/src/services/github.js +22 -24
- package/src/services/nvd.js +21 -31
- package/src/services/platform.js +2 -2
- package/src/services/prompts.js +23 -35
- package/src/services/security.js +135 -61
- package/src/services/shell.js +4 -4
- package/src/services/skills-sh.js +3 -9
- package/src/services/speckit.js +4 -7
- package/src/services/version-check.js +10 -10
- package/src/types.js +85 -0
- package/src/utils/aws-vault.js +18 -41
- package/src/utils/banner.js +5 -7
- package/src/utils/errors.js +42 -46
- package/src/utils/frontmatter.js +4 -4
- package/src/utils/gradient.js +18 -16
- package/src/utils/open-browser.js +3 -3
- package/src/utils/tui/form.js +1006 -0
- package/src/utils/tui/modal.js +15 -14
- package/src/utils/tui/navigable-table.js +16 -16
- package/src/utils/tui/tab-tui.js +800 -0
- package/src/utils/typewriter.js +3 -3
- package/src/utils/welcome.js +18 -21
- package/src/validators/repo-name.js +2 -2
package/src/utils/aws-vault.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {loadConfigSync} from '../services/config.js'
|
|
2
|
+
import {execa} from 'execa'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Returns the aws-vault exec prefix to prepend to AWS CLI commands.
|
|
@@ -44,10 +44,7 @@ export function awsVaultPrefix(config = null) {
|
|
|
44
44
|
* @returns {boolean}
|
|
45
45
|
*/
|
|
46
46
|
export function hasAwsCredentialEnv() {
|
|
47
|
-
return Boolean(
|
|
48
|
-
process.env.AWS_ACCESS_KEY_ID ||
|
|
49
|
-
process.env.AWS_SESSION_TOKEN,
|
|
50
|
-
)
|
|
47
|
+
return Boolean(process.env.AWS_ACCESS_KEY_ID || process.env.AWS_SESSION_TOKEN)
|
|
51
48
|
}
|
|
52
49
|
|
|
53
50
|
/**
|
|
@@ -79,24 +76,14 @@ export async function reexecCurrentCommandWithAwsVault(config = null) {
|
|
|
79
76
|
if (process.env.DVMI_AWS_VAULT_REEXEC === '1') return null
|
|
80
77
|
|
|
81
78
|
try {
|
|
82
|
-
const child = await execa(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
'
|
|
88
|
-
process.execPath,
|
|
89
|
-
...process.argv.slice(1),
|
|
90
|
-
],
|
|
91
|
-
{
|
|
92
|
-
reject: false,
|
|
93
|
-
stdio: 'inherit',
|
|
94
|
-
env: {
|
|
95
|
-
...process.env,
|
|
96
|
-
DVMI_AWS_VAULT_REEXEC: '1',
|
|
97
|
-
},
|
|
79
|
+
const child = await execa('aws-vault', ['exec', profile, '--', process.execPath, ...process.argv.slice(1)], {
|
|
80
|
+
reject: false,
|
|
81
|
+
stdio: 'inherit',
|
|
82
|
+
env: {
|
|
83
|
+
...process.env,
|
|
84
|
+
DVMI_AWS_VAULT_REEXEC: '1',
|
|
98
85
|
},
|
|
99
|
-
)
|
|
86
|
+
})
|
|
100
87
|
|
|
101
88
|
return child.exitCode ?? 1
|
|
102
89
|
} catch {
|
|
@@ -117,25 +104,15 @@ export async function reexecCurrentCommandWithAwsVaultProfile(profile, extraEnv
|
|
|
117
104
|
if (!profile) return null
|
|
118
105
|
|
|
119
106
|
try {
|
|
120
|
-
const child = await execa(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
'
|
|
126
|
-
|
|
127
|
-
...process.argv.slice(1),
|
|
128
|
-
],
|
|
129
|
-
{
|
|
130
|
-
reject: false,
|
|
131
|
-
stdio: 'inherit',
|
|
132
|
-
env: {
|
|
133
|
-
...process.env,
|
|
134
|
-
DVMI_AWS_VAULT_REEXEC: '1',
|
|
135
|
-
...extraEnv,
|
|
136
|
-
},
|
|
107
|
+
const child = await execa('aws-vault', ['exec', profile, '--', process.execPath, ...process.argv.slice(1)], {
|
|
108
|
+
reject: false,
|
|
109
|
+
stdio: 'inherit',
|
|
110
|
+
env: {
|
|
111
|
+
...process.env,
|
|
112
|
+
DVMI_AWS_VAULT_REEXEC: '1',
|
|
113
|
+
...extraEnv,
|
|
137
114
|
},
|
|
138
|
-
)
|
|
115
|
+
})
|
|
139
116
|
|
|
140
117
|
return child.exitCode ?? 1
|
|
141
118
|
} catch {
|
package/src/utils/banner.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import figlet from 'figlet'
|
|
2
2
|
import chalk from 'chalk'
|
|
3
|
-
import {
|
|
3
|
+
import {BRAND_GRADIENT, animateGradientBanner, isColorEnabled} from './gradient.js'
|
|
4
4
|
|
|
5
5
|
// Brand colors
|
|
6
6
|
export const ORANGE = '#FF6B2B'
|
|
@@ -24,13 +24,11 @@ function figletAsync(text, opts) {
|
|
|
24
24
|
* @returns {Promise<void>}
|
|
25
25
|
*/
|
|
26
26
|
export async function printBanner() {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
const art = await figletAsync('DVMI', {font: 'ANSI Shadow'})
|
|
28
|
+
const artLines = art.split('\n').filter((l) => l.trim() !== '')
|
|
29
|
+
const width = Math.max(...artLines.map((l) => l.length)) + 4
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
? chalk.hex(BLUE).bold(' Devvami Developer CLI')
|
|
33
|
-
: ' Devvami Developer CLI'
|
|
31
|
+
const tagline = isColorEnabled ? chalk.hex(BLUE).bold(' Devvami Developer CLI') : ' Devvami Developer CLI'
|
|
34
32
|
|
|
35
33
|
const separator = isColorEnabled
|
|
36
34
|
? chalk.hex(BLUE).dim('─'.repeat(Math.min(width, 60)))
|
package/src/utils/errors.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Base CLI error with an actionable hint for the user.
|
|
3
3
|
*/
|
|
4
4
|
export class DvmiError extends Error {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} message - Human-readable error message
|
|
7
|
+
* @param {string} hint - Actionable suggestion to resolve the error
|
|
8
|
+
* @param {number} [exitCode] - Process exit code (default: 1)
|
|
9
|
+
*/
|
|
10
|
+
constructor(message, hint, exitCode = 1) {
|
|
11
|
+
super(message)
|
|
12
|
+
this.name = 'DvmiError'
|
|
13
13
|
/** @type {string} */
|
|
14
14
|
this.hint = hint
|
|
15
15
|
/** @type {number} */
|
|
@@ -21,43 +21,39 @@ export class DvmiError extends Error {
|
|
|
21
21
|
* Validation error for invalid user input (exit code 2).
|
|
22
22
|
*/
|
|
23
23
|
export class ValidationError extends DvmiError {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} message
|
|
26
|
+
* @param {string} hint
|
|
27
|
+
*/
|
|
28
|
+
constructor(message, hint) {
|
|
29
|
+
super(message, hint, 2)
|
|
30
|
+
this.name = 'ValidationError'
|
|
31
|
+
// oclif reads this.oclif.exit to determine the process exit code
|
|
32
|
+
this.oclif = {exit: 2}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
49
|
-
this.name = 'AuthError'
|
|
50
|
-
}
|
|
51
|
-
}
|
|
36
|
+
/**
|
|
37
|
+
* Auth error for missing or expired authentication.
|
|
38
|
+
*/
|
|
39
|
+
export class AuthError extends DvmiError {
|
|
40
|
+
/**
|
|
41
|
+
* @param {string} service - Service name (e.g. "GitHub", "AWS")
|
|
42
|
+
*/
|
|
43
|
+
constructor(service) {
|
|
44
|
+
super(`${service} authentication required`, `Run \`dvmi auth login\` to authenticate`, 1)
|
|
45
|
+
this.name = 'AuthError'
|
|
46
|
+
}
|
|
47
|
+
}
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Format an error for display in the terminal.
|
|
51
|
+
* @param {Error} err
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
export function formatError(err) {
|
|
55
|
+
if (err instanceof DvmiError) {
|
|
56
|
+
return `Error: ${err.message}\nHint: ${err.hint}`
|
|
57
|
+
}
|
|
58
|
+
return `Error: ${err.message}`
|
|
59
|
+
}
|
package/src/utils/frontmatter.js
CHANGED
|
@@ -18,7 +18,7 @@ import yaml from 'js-yaml'
|
|
|
18
18
|
export function parseFrontmatter(content) {
|
|
19
19
|
const match = content.match(/^---\r?\n([\s\S]*?)---\r?\n?([\s\S]*)$/)
|
|
20
20
|
if (!match) {
|
|
21
|
-
return {
|
|
21
|
+
return {frontmatter: {}, body: content}
|
|
22
22
|
}
|
|
23
23
|
const rawYaml = match[1]
|
|
24
24
|
const body = match[2] ?? ''
|
|
@@ -28,9 +28,9 @@ export function parseFrontmatter(content) {
|
|
|
28
28
|
parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
29
29
|
? /** @type {Record<string, unknown>} */ (parsed)
|
|
30
30
|
: {}
|
|
31
|
-
return {
|
|
31
|
+
return {frontmatter, body}
|
|
32
32
|
} catch {
|
|
33
|
-
return {
|
|
33
|
+
return {frontmatter: {}, body: content}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -47,6 +47,6 @@ export function serializeFrontmatter(frontmatter, body) {
|
|
|
47
47
|
if (!frontmatter || Object.keys(frontmatter).length === 0) {
|
|
48
48
|
return body
|
|
49
49
|
}
|
|
50
|
-
const yamlStr = yaml.dump(frontmatter, {
|
|
50
|
+
const yamlStr = yaml.dump(frontmatter, {lineWidth: -1}).trimEnd()
|
|
51
51
|
return `---\n${yamlStr}\n---\n${body}`
|
|
52
52
|
}
|
package/src/utils/gradient.js
CHANGED
|
@@ -12,9 +12,9 @@ import readline from 'node:readline'
|
|
|
12
12
|
|
|
13
13
|
/** @type {GradientStop[]} */
|
|
14
14
|
export const BRAND_GRADIENT = [
|
|
15
|
-
[0,
|
|
16
|
-
[0,
|
|
17
|
-
[100,
|
|
15
|
+
[0, 212, 255], // #00D4FF — ciano elettrico
|
|
16
|
+
[0, 100, 255], // #0064FF — blu vivido
|
|
17
|
+
[100, 0, 220], // #6400DC — indaco profondo
|
|
18
18
|
]
|
|
19
19
|
|
|
20
20
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -51,24 +51,26 @@ export function gradientText(text, stops, phase = 0) {
|
|
|
51
51
|
|
|
52
52
|
const segments = stops.length - 1
|
|
53
53
|
|
|
54
|
-
return chars
|
|
55
|
-
|
|
54
|
+
return chars
|
|
55
|
+
.map((char, i) => {
|
|
56
|
+
if (char === ' ') return char
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
// Normalise t in [0, 1] with phase shift
|
|
59
|
+
const t = (i / Math.max(len - 1, 1) + phase) % 1
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
const seg = Math.min(Math.floor(t * segments), segments - 1)
|
|
62
|
+
const localT = t * segments - seg
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
const [r1, g1, b1] = stops[seg]
|
|
65
|
+
const [r2, g2, b2] = stops[seg + 1]
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
const r = Math.round(r1 + (r2 - r1) * localT)
|
|
68
|
+
const g = Math.round(g1 + (g2 - g1) * localT)
|
|
69
|
+
const b = Math.round(b1 + (b2 - b1) * localT)
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
return chalk.rgb(r, g, b)(char)
|
|
72
|
+
})
|
|
73
|
+
.join('')
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import open from 'open'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import {detectPlatform} from '../services/platform.js'
|
|
3
|
+
import {exec} from '../services/shell.js'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Open a URL in the default browser, using the platform-appropriate command.
|
|
@@ -8,7 +8,7 @@ import { exec } from '../services/shell.js'
|
|
|
8
8
|
* @returns {Promise<void>}
|
|
9
9
|
*/
|
|
10
10
|
export async function openBrowser(url) {
|
|
11
|
-
const {
|
|
11
|
+
const {platform, openCommand} = await detectPlatform()
|
|
12
12
|
|
|
13
13
|
if (platform === 'macos') {
|
|
14
14
|
await open(url)
|