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
|
@@ -1,34 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {Command, Flags} from '@oclif/core'
|
|
2
2
|
import ora from 'ora'
|
|
3
3
|
import chalk from 'chalk'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
4
|
+
import {detectPlatform} from '../../services/platform.js'
|
|
5
|
+
import {isChezmoiInstalled, getChezmoiConfig, getManagedFiles, getChezmoiRemote} from '../../services/dotfiles.js'
|
|
6
|
+
import {loadConfig} from '../../services/config.js'
|
|
7
|
+
import {formatDotfilesStatus} from '../../formatters/dotfiles.js'
|
|
8
|
+
import {DvmiError} from '../../utils/errors.js'
|
|
9
9
|
|
|
10
10
|
/** @import { DotfilesStatusResult } from '../../types.js' */
|
|
11
11
|
|
|
12
12
|
export default class DotfilesStatus extends Command {
|
|
13
13
|
static description = 'Show chezmoi dotfiles status: managed files, encryption state, and sync health'
|
|
14
14
|
|
|
15
|
-
static examples = [
|
|
16
|
-
'<%= config.bin %> dotfiles status',
|
|
17
|
-
'<%= config.bin %> dotfiles status --json',
|
|
18
|
-
]
|
|
15
|
+
static examples = ['<%= config.bin %> dotfiles status', '<%= config.bin %> dotfiles status --json']
|
|
19
16
|
|
|
20
17
|
static enableJsonFlag = true
|
|
21
18
|
|
|
22
19
|
static flags = {
|
|
23
|
-
help: Flags.help({
|
|
20
|
+
help: Flags.help({char: 'h'}),
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
async run() {
|
|
27
|
-
const {
|
|
24
|
+
const {flags} = await this.parse(DotfilesStatus)
|
|
28
25
|
const isJson = flags.json
|
|
29
26
|
|
|
30
27
|
const platformInfo = await detectPlatform()
|
|
31
|
-
const {
|
|
28
|
+
const {platform} = platformInfo
|
|
32
29
|
|
|
33
30
|
const config = await loadConfig()
|
|
34
31
|
const enabled = config.dotfiles?.enabled === true
|
|
@@ -47,7 +44,7 @@ export default class DotfilesStatus extends Command {
|
|
|
47
44
|
repo: null,
|
|
48
45
|
sourceDir: null,
|
|
49
46
|
files: [],
|
|
50
|
-
summary: {
|
|
47
|
+
summary: {total: 0, encrypted: 0, plaintext: 0},
|
|
51
48
|
}
|
|
52
49
|
|
|
53
50
|
if (isJson) return notConfiguredResult
|
|
@@ -56,14 +53,17 @@ export default class DotfilesStatus extends Command {
|
|
|
56
53
|
}
|
|
57
54
|
|
|
58
55
|
if (!chezmoiInstalled) {
|
|
59
|
-
const hint =
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
const hint =
|
|
57
|
+
platform === 'macos'
|
|
58
|
+
? 'Run `brew install chezmoi` or visit https://chezmoi.io/install'
|
|
59
|
+
: 'Run `sh -c "$(curl -fsLS get.chezmoi.io)"` or visit https://chezmoi.io/install'
|
|
62
60
|
throw new DvmiError('chezmoi is not installed', hint)
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
// Gather data
|
|
66
|
-
const spinner = isJson
|
|
64
|
+
const spinner = isJson
|
|
65
|
+
? null
|
|
66
|
+
: ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Gathering dotfiles status...')}).start()
|
|
67
67
|
|
|
68
68
|
const [chezmoiConfig, files, remote] = await Promise.all([
|
|
69
69
|
getChezmoiConfig(),
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {Command, Flags, Args} from '@oclif/core'
|
|
2
2
|
import ora from 'ora'
|
|
3
3
|
import chalk from 'chalk'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
4
|
+
import {confirm, input, select} from '@inquirer/prompts'
|
|
5
|
+
import {detectPlatform} from '../../services/platform.js'
|
|
6
|
+
import {isChezmoiInstalled, getChezmoiRemote, hasLocalChanges} from '../../services/dotfiles.js'
|
|
7
|
+
import {loadConfig, saveConfig} from '../../services/config.js'
|
|
8
|
+
import {exec, execOrThrow} from '../../services/shell.js'
|
|
9
|
+
import {formatDotfilesSync} from '../../formatters/dotfiles.js'
|
|
10
|
+
import {DvmiError} from '../../utils/errors.js'
|
|
11
11
|
|
|
12
12
|
/** @import { DotfilesSyncResult } from '../../types.js' */
|
|
13
13
|
|
|
@@ -26,18 +26,18 @@ export default class DotfilesSync extends Command {
|
|
|
26
26
|
static enableJsonFlag = true
|
|
27
27
|
|
|
28
28
|
static flags = {
|
|
29
|
-
help: Flags.help({
|
|
30
|
-
push: Flags.boolean({
|
|
31
|
-
pull: Flags.boolean({
|
|
32
|
-
'dry-run': Flags.boolean({
|
|
29
|
+
help: Flags.help({char: 'h'}),
|
|
30
|
+
push: Flags.boolean({description: 'Push local changes to remote', default: false}),
|
|
31
|
+
pull: Flags.boolean({description: 'Pull remote changes and apply', default: false}),
|
|
32
|
+
'dry-run': Flags.boolean({description: 'Show what would change without applying', default: false}),
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
static args = {
|
|
36
|
-
repo: Args.string({
|
|
36
|
+
repo: Args.string({description: 'Remote repository URL (for initial remote setup)', required: false}),
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
async run() {
|
|
40
|
-
const {
|
|
40
|
+
const {flags, args} = await this.parse(DotfilesSync)
|
|
41
41
|
const isJson = flags.json
|
|
42
42
|
const isPush = flags.push
|
|
43
43
|
const isPull = flags.pull
|
|
@@ -56,30 +56,27 @@ export default class DotfilesSync extends Command {
|
|
|
56
56
|
const isCI = process.env.CI === 'true'
|
|
57
57
|
const isNonInteractive = !process.stdout.isTTY
|
|
58
58
|
if ((isCI || isNonInteractive) && !isJson) {
|
|
59
|
-
this.error(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
)
|
|
59
|
+
this.error('This command requires an interactive terminal (TTY). Run with --json for a non-interactive sync.', {
|
|
60
|
+
exit: 1,
|
|
61
|
+
})
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
const config = await loadConfig()
|
|
66
65
|
if (!config.dotfiles?.enabled) {
|
|
67
|
-
throw new DvmiError(
|
|
68
|
-
'Chezmoi dotfiles management is not configured',
|
|
69
|
-
'Run `dvmi dotfiles setup` first',
|
|
70
|
-
)
|
|
66
|
+
throw new DvmiError('Chezmoi dotfiles management is not configured', 'Run `dvmi dotfiles setup` first')
|
|
71
67
|
}
|
|
72
68
|
|
|
73
69
|
const chezmoiInstalled = await isChezmoiInstalled()
|
|
74
70
|
if (!chezmoiInstalled) {
|
|
75
71
|
const platformInfo = await detectPlatform()
|
|
76
|
-
const hint =
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
const hint =
|
|
73
|
+
platformInfo.platform === 'macos'
|
|
74
|
+
? 'Run `brew install chezmoi` or visit https://chezmoi.io/install'
|
|
75
|
+
: 'Run `sh -c "$(curl -fsLS get.chezmoi.io)"` or visit https://chezmoi.io/install'
|
|
79
76
|
throw new DvmiError('chezmoi is not installed', hint)
|
|
80
77
|
}
|
|
81
78
|
|
|
82
|
-
const remote = config.dotfiles?.repo ?? await getChezmoiRemote()
|
|
79
|
+
const remote = config.dotfiles?.repo ?? (await getChezmoiRemote())
|
|
83
80
|
|
|
84
81
|
// --json mode: attempt push/pull or report status
|
|
85
82
|
if (isJson) {
|
|
@@ -105,7 +102,13 @@ export default class DotfilesSync extends Command {
|
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
/** @type {DotfilesSyncResult} */
|
|
108
|
-
return {
|
|
105
|
+
return {
|
|
106
|
+
action: 'skipped',
|
|
107
|
+
repo: effectiveRemote ?? null,
|
|
108
|
+
status: 'skipped',
|
|
109
|
+
message: 'No action specified',
|
|
110
|
+
conflicts: [],
|
|
111
|
+
}
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
// ---------------------------------------------------------------------------
|
|
@@ -140,22 +143,29 @@ export default class DotfilesSync extends Command {
|
|
|
140
143
|
const action = await select({
|
|
141
144
|
message: 'What would you like to do?',
|
|
142
145
|
choices: [
|
|
143
|
-
{
|
|
144
|
-
{
|
|
145
|
-
{
|
|
146
|
+
{name: 'Push local changes to remote', value: 'push'},
|
|
147
|
+
{name: 'Pull remote changes and apply', value: 'pull'},
|
|
148
|
+
{name: 'Cancel', value: 'cancel'},
|
|
146
149
|
],
|
|
147
150
|
})
|
|
148
151
|
|
|
149
152
|
if (action === 'cancel') {
|
|
150
153
|
/** @type {DotfilesSyncResult} */
|
|
151
|
-
const cancelResult = {
|
|
154
|
+
const cancelResult = {
|
|
155
|
+
action: 'skipped',
|
|
156
|
+
repo: effectiveRemote ?? null,
|
|
157
|
+
status: 'skipped',
|
|
158
|
+
message: 'Cancelled by user',
|
|
159
|
+
conflicts: [],
|
|
160
|
+
}
|
|
152
161
|
this.log(formatDotfilesSync(cancelResult))
|
|
153
162
|
return cancelResult
|
|
154
163
|
}
|
|
155
164
|
|
|
156
|
-
const result =
|
|
157
|
-
|
|
158
|
-
|
|
165
|
+
const result =
|
|
166
|
+
action === 'push'
|
|
167
|
+
? await this._push(effectiveRemote, isDryRun, false)
|
|
168
|
+
: await this._pull(effectiveRemote, isDryRun, false)
|
|
159
169
|
|
|
160
170
|
this.log(formatDotfilesSync(result))
|
|
161
171
|
return result
|
|
@@ -174,25 +184,25 @@ export default class DotfilesSync extends Command {
|
|
|
174
184
|
const choice = await select({
|
|
175
185
|
message: 'Connect to an existing dotfiles repository or create a new one?',
|
|
176
186
|
choices: [
|
|
177
|
-
{
|
|
178
|
-
{
|
|
187
|
+
{name: 'Connect to existing repository', value: 'existing'},
|
|
188
|
+
{name: 'Create new repository on GitHub', value: 'new'},
|
|
179
189
|
],
|
|
180
190
|
})
|
|
181
191
|
|
|
182
192
|
let repoUrl = ''
|
|
183
193
|
|
|
184
194
|
if (choice === 'existing') {
|
|
185
|
-
repoUrl = await input({
|
|
195
|
+
repoUrl = await input({message: 'Repository URL (SSH or HTTPS):'})
|
|
186
196
|
} else {
|
|
187
|
-
const repoName = await input({
|
|
188
|
-
const isPrivate = await confirm({
|
|
197
|
+
const repoName = await input({message: 'Repository name:', default: 'dotfiles'})
|
|
198
|
+
const isPrivate = await confirm({message: 'Make repository private?', default: true})
|
|
189
199
|
|
|
190
200
|
if (!isDryRun) {
|
|
191
201
|
try {
|
|
192
202
|
const visFlag = isPrivate ? '--private' : '--public'
|
|
193
203
|
await execOrThrow('gh', ['repo', 'create', repoName, visFlag, '--confirm'])
|
|
194
204
|
// Get the SSH URL from the created repo
|
|
195
|
-
const {
|
|
205
|
+
const {exec} = await import('../../services/shell.js')
|
|
196
206
|
const result = await exec('gh', ['repo', 'view', repoName, '--json', 'sshUrl', '--jq', '.sshUrl'])
|
|
197
207
|
repoUrl = result.stdout.trim() || `git@github.com:${repoName}.git`
|
|
198
208
|
} catch {
|
|
@@ -211,7 +221,7 @@ export default class DotfilesSync extends Command {
|
|
|
211
221
|
await execOrThrow('chezmoi', ['git', '--', 'remote', 'add', 'origin', repoUrl])
|
|
212
222
|
await execOrThrow('chezmoi', ['git', '--', 'push', '-u', 'origin', 'main'])
|
|
213
223
|
// Save repo to dvmi config
|
|
214
|
-
config.dotfiles = {
|
|
224
|
+
config.dotfiles = {...(config.dotfiles ?? {enabled: true}), repo: repoUrl}
|
|
215
225
|
await saveConfig(config)
|
|
216
226
|
} catch (err) {
|
|
217
227
|
/** @type {DotfilesSyncResult} */
|
|
@@ -232,7 +242,9 @@ export default class DotfilesSync extends Command {
|
|
|
232
242
|
action: 'init-remote',
|
|
233
243
|
repo: repoUrl,
|
|
234
244
|
status: isDryRun ? 'skipped' : 'success',
|
|
235
|
-
message: isDryRun
|
|
245
|
+
message: isDryRun
|
|
246
|
+
? `Would configure remote: ${repoUrl}`
|
|
247
|
+
: 'Remote repository configured and initial push completed',
|
|
236
248
|
conflicts: [],
|
|
237
249
|
}
|
|
238
250
|
this.log(formatDotfilesSync(result))
|
|
@@ -271,7 +283,9 @@ export default class DotfilesSync extends Command {
|
|
|
271
283
|
}
|
|
272
284
|
}
|
|
273
285
|
|
|
274
|
-
const spinner = isJson
|
|
286
|
+
const spinner = isJson
|
|
287
|
+
? null
|
|
288
|
+
: ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Pushing to remote...')}).start()
|
|
275
289
|
|
|
276
290
|
try {
|
|
277
291
|
// Stage all changes
|
|
@@ -282,7 +296,7 @@ export default class DotfilesSync extends Command {
|
|
|
282
296
|
await execOrThrow('chezmoi', ['git', '--', 'push', 'origin', 'HEAD'])
|
|
283
297
|
spinner?.succeed(chalk.green('Pushed to remote'))
|
|
284
298
|
|
|
285
|
-
return {
|
|
299
|
+
return {action: 'push', repo: remote, status: 'success', message: 'Changes pushed to remote', conflicts: []}
|
|
286
300
|
} catch (err) {
|
|
287
301
|
spinner?.fail(chalk.red('Push failed'))
|
|
288
302
|
return {
|
|
@@ -327,7 +341,9 @@ export default class DotfilesSync extends Command {
|
|
|
327
341
|
}
|
|
328
342
|
}
|
|
329
343
|
|
|
330
|
-
const spinner = isJson
|
|
344
|
+
const spinner = isJson
|
|
345
|
+
? null
|
|
346
|
+
: ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Pulling from remote...')}).start()
|
|
331
347
|
|
|
332
348
|
try {
|
|
333
349
|
// Check if chezmoi init was done with this remote (first-time pull)
|
|
@@ -360,7 +376,7 @@ export default class DotfilesSync extends Command {
|
|
|
360
376
|
}
|
|
361
377
|
}
|
|
362
378
|
|
|
363
|
-
return {
|
|
379
|
+
return {action: 'pull', repo: remote, status: 'success', message: 'Remote changes applied', conflicts: []}
|
|
364
380
|
} catch (err) {
|
|
365
381
|
spinner?.fail(chalk.red('Pull failed'))
|
|
366
382
|
return {
|