devvami 1.4.1 → 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 +41 -1
- 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 +95 -21
- 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 +25 -17
- 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,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {Command, Args, 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 {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 {
|
|
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, {
|
|
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 {
|
|
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 {
|
|
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, {
|
|
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 {
|
|
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) => ({
|
|
144
|
-
choices.push({
|
|
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 {
|
|
162
|
+
return {entries, total: entries.length, category}
|
|
163
163
|
}
|
|
164
164
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {Command, Args, 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 {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 {
|
|
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, {
|
|
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 {
|
|
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({
|
|
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, {
|
|
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, {
|
|
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, {
|
|
124
|
+
result = await downloadPrompt(relativePath, localDir, {overwrite: true})
|
|
126
125
|
} catch (err) {
|
|
127
126
|
if (err instanceof DvmiError) {
|
|
128
|
-
this.error(err.message, {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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({
|
|
59
|
+
const spinner = ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')(label)}).start()
|
|
61
60
|
|
|
62
61
|
try {
|
|
63
|
-
await installSpecifyCli({
|
|
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, {
|
|
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(), {
|
|
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, {
|
|
91
|
+
this.error(err.message, {exit: 1, suggestions: [err.hint]})
|
|
93
92
|
}
|
|
94
93
|
throw err
|
|
95
94
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
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 {
|
|
4
|
+
import {select} from '@inquirer/prompts'
|
|
5
|
+
import {listPrompts} from '../../services/prompts.js'
|
|
6
|
+
import {formatPromptTable, formatPromptBody} from '../../formatters/prompts.js'
|
|
7
|
+
import {DvmiError} from '../../utils/errors.js'
|
|
8
8
|
|
|
9
9
|
/** @import { Prompt } from '../../types.js' */
|
|
10
10
|
|
|
@@ -27,7 +27,7 @@ export default class PromptsList extends Command {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
async run() {
|
|
30
|
-
const {
|
|
30
|
+
const {flags} = await this.parse(PromptsList)
|
|
31
31
|
const isJson = flags.json
|
|
32
32
|
|
|
33
33
|
const spinner = isJson
|
|
@@ -45,7 +45,7 @@ export default class PromptsList extends Command {
|
|
|
45
45
|
} catch (err) {
|
|
46
46
|
spinner?.fail()
|
|
47
47
|
if (err instanceof DvmiError) {
|
|
48
|
-
this.error(err.message, {
|
|
48
|
+
this.error(err.message, {exit: err.exitCode, suggestions: [err.hint]})
|
|
49
49
|
}
|
|
50
50
|
throw err
|
|
51
51
|
}
|
|
@@ -65,7 +65,7 @@ export default class PromptsList extends Command {
|
|
|
65
65
|
: prompts
|
|
66
66
|
|
|
67
67
|
if (isJson) {
|
|
68
|
-
return {
|
|
68
|
+
return {prompts: filtered, total: filtered.length}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
if (filtered.length === 0) {
|
|
@@ -73,7 +73,7 @@ export default class PromptsList extends Command {
|
|
|
73
73
|
? chalk.dim(`No prompts matching "${flags.filter}".`)
|
|
74
74
|
: chalk.yellow('No prompts found in the repository.')
|
|
75
75
|
this.log(msg)
|
|
76
|
-
return {
|
|
76
|
+
return {prompts: [], total: 0}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const filterInfo = query ? chalk.dim(` — filter: ${chalk.white(`"${flags.filter}"`)}`) : ''
|
|
@@ -87,8 +87,8 @@ export default class PromptsList extends Command {
|
|
|
87
87
|
|
|
88
88
|
// Interactive selection to view full prompt content
|
|
89
89
|
try {
|
|
90
|
-
const choices = filtered.map((p) => ({
|
|
91
|
-
choices.push({
|
|
90
|
+
const choices = filtered.map((p) => ({name: p.title, value: p}))
|
|
91
|
+
choices.push({name: chalk.dim('← Exit'), value: /** @type {Prompt} */ (null)})
|
|
92
92
|
|
|
93
93
|
const selected = await select({
|
|
94
94
|
message: 'Select a prompt to view its content (or Exit):',
|
|
@@ -102,6 +102,6 @@ export default class PromptsList extends Command {
|
|
|
102
102
|
// User pressed Ctrl+C — exit gracefully
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
return {
|
|
105
|
+
return {prompts: filtered, total: filtered.length}
|
|
106
106
|
}
|
|
107
107
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {Command, Args, 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 {
|
|
9
|
-
import {
|
|
4
|
+
import {select, confirm} from '@inquirer/prompts'
|
|
5
|
+
import {join} from 'node:path'
|
|
6
|
+
import {readdir} from 'node:fs/promises'
|
|
7
|
+
import {resolveLocalPrompt, invokeTool, SUPPORTED_TOOLS} from '../../services/prompts.js'
|
|
8
|
+
import {loadConfig} from '../../services/config.js'
|
|
9
|
+
import {DvmiError} from '../../utils/errors.js'
|
|
10
10
|
|
|
11
11
|
/** @import { AITool } from '../../types.js' */
|
|
12
12
|
|
|
@@ -23,7 +23,7 @@ async function walkPrompts(dir, base) {
|
|
|
23
23
|
const results = []
|
|
24
24
|
let entries
|
|
25
25
|
try {
|
|
26
|
-
entries = await readdir(dir, {
|
|
26
|
+
entries = await readdir(dir, {withFileTypes: true})
|
|
27
27
|
} catch {
|
|
28
28
|
return results
|
|
29
29
|
}
|
|
@@ -67,7 +67,7 @@ export default class PromptsRun extends Command {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
async run() {
|
|
70
|
-
const {
|
|
70
|
+
const {args, flags} = await this.parse(PromptsRun)
|
|
71
71
|
const isJson = flags.json
|
|
72
72
|
|
|
73
73
|
// Load config
|
|
@@ -78,8 +78,7 @@ export default class PromptsRun extends Command {
|
|
|
78
78
|
/* use defaults */
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const localDir =
|
|
82
|
-
process.env.DVMI_PROMPTS_DIR ?? config.promptsDir ?? join(process.cwd(), DEFAULT_PROMPTS_DIR)
|
|
81
|
+
const localDir = process.env.DVMI_PROMPTS_DIR ?? config.promptsDir ?? join(process.cwd(), DEFAULT_PROMPTS_DIR)
|
|
83
82
|
|
|
84
83
|
// Resolve tool: --tool flag > config.aiTool
|
|
85
84
|
const toolName = /** @type {AITool | undefined} */ (flags.tool ?? config.aiTool)
|
|
@@ -111,7 +110,7 @@ export default class PromptsRun extends Command {
|
|
|
111
110
|
prompt = await resolveLocalPrompt(args.path, localDir)
|
|
112
111
|
} catch (err) {
|
|
113
112
|
if (err instanceof DvmiError) {
|
|
114
|
-
this.error(err.message, {
|
|
113
|
+
this.error(err.message, {exit: err.exitCode, suggestions: [err.hint]})
|
|
115
114
|
}
|
|
116
115
|
throw err
|
|
117
116
|
}
|
|
@@ -142,7 +141,7 @@ export default class PromptsRun extends Command {
|
|
|
142
141
|
|
|
143
142
|
relativePath = await select({
|
|
144
143
|
message: 'Select a local prompt to run:',
|
|
145
|
-
choices: localPaths.map((p) => ({
|
|
144
|
+
choices: localPaths.map((p) => ({name: p, value: p})),
|
|
146
145
|
})
|
|
147
146
|
}
|
|
148
147
|
|
|
@@ -168,7 +167,7 @@ export default class PromptsRun extends Command {
|
|
|
168
167
|
} catch (err) {
|
|
169
168
|
spinner.fail()
|
|
170
169
|
if (err instanceof DvmiError) {
|
|
171
|
-
this.error(err.message, {
|
|
170
|
+
this.error(err.message, {exit: err.exitCode, suggestions: [err.hint]})
|
|
172
171
|
}
|
|
173
172
|
throw err
|
|
174
173
|
}
|
|
@@ -180,14 +179,12 @@ export default class PromptsRun extends Command {
|
|
|
180
179
|
// This protects against prompt injection from tampered local files (originally
|
|
181
180
|
// downloaded from remote repositories). Skipped in CI/non-interactive environments.
|
|
182
181
|
if (!process.env.CI && process.stdin.isTTY) {
|
|
183
|
-
const preview = prompt.body.length > 500
|
|
184
|
-
? prompt.body.slice(0, 500) + chalk.dim('\n…[truncated]')
|
|
185
|
-
: prompt.body
|
|
182
|
+
const preview = prompt.body.length > 500 ? prompt.body.slice(0, 500) + chalk.dim('\n…[truncated]') : prompt.body
|
|
186
183
|
this.log(chalk.yellow('Prompt preview:'))
|
|
187
184
|
this.log(chalk.dim('─'.repeat(50)))
|
|
188
185
|
this.log(chalk.dim(preview))
|
|
189
186
|
this.log(chalk.dim('─'.repeat(50)) + '\n')
|
|
190
|
-
const ok = await confirm({
|
|
187
|
+
const ok = await confirm({message: `Run this prompt with ${toolName}?`, default: true})
|
|
191
188
|
if (!ok) {
|
|
192
189
|
this.log(chalk.dim('Aborted.'))
|
|
193
190
|
return
|
|
@@ -199,7 +196,7 @@ export default class PromptsRun extends Command {
|
|
|
199
196
|
await invokeTool(toolName, prompt.body)
|
|
200
197
|
} catch (err) {
|
|
201
198
|
if (err instanceof DvmiError) {
|
|
202
|
-
this.error(err.message, {
|
|
199
|
+
this.error(err.message, {exit: err.exitCode, suggestions: [err.hint]})
|
|
203
200
|
}
|
|
204
201
|
throw err
|
|
205
202
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
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 {
|
|
4
|
+
import {listRepos} from '../../services/github.js'
|
|
5
|
+
import {loadConfig} from '../../services/config.js'
|
|
6
|
+
import {renderTable} from '../../formatters/table.js'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @param {string} lang
|
|
@@ -13,22 +13,22 @@ function langColor(lang) {
|
|
|
13
13
|
const map = {
|
|
14
14
|
javascript: chalk.yellow,
|
|
15
15
|
typescript: chalk.blue,
|
|
16
|
-
python:
|
|
17
|
-
java:
|
|
18
|
-
go:
|
|
19
|
-
ruby:
|
|
20
|
-
rust:
|
|
21
|
-
kotlin:
|
|
22
|
-
swift:
|
|
23
|
-
php:
|
|
24
|
-
shell:
|
|
16
|
+
python: chalk.green,
|
|
17
|
+
java: chalk.red,
|
|
18
|
+
go: chalk.cyan,
|
|
19
|
+
ruby: chalk.magenta,
|
|
20
|
+
rust: chalk.hex('#CE422B'),
|
|
21
|
+
kotlin: chalk.hex('#7F52FF'),
|
|
22
|
+
swift: chalk.hex('#F05138'),
|
|
23
|
+
php: chalk.hex('#777BB4'),
|
|
24
|
+
shell: chalk.greenBright,
|
|
25
25
|
}
|
|
26
26
|
const fn = map[lang.toLowerCase()]
|
|
27
27
|
return fn ? fn(lang) : chalk.dim(lang)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export default class RepoList extends Command {
|
|
31
|
-
static description =
|
|
31
|
+
static description = "Lista repository dell'organizzazione"
|
|
32
32
|
|
|
33
33
|
static examples = [
|
|
34
34
|
'<%= config.bin %> repo list',
|
|
@@ -41,13 +41,13 @@ export default class RepoList extends Command {
|
|
|
41
41
|
static enableJsonFlag = true
|
|
42
42
|
|
|
43
43
|
static flags = {
|
|
44
|
-
language: Flags.string({
|
|
45
|
-
topic:
|
|
46
|
-
search:
|
|
44
|
+
language: Flags.string({description: 'Filtra per linguaggio'}),
|
|
45
|
+
topic: Flags.string({description: 'Filtra per topic'}),
|
|
46
|
+
search: Flags.string({char: 's', description: 'Cerca in nome e descrizione (case-insensitive)'}),
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
async run() {
|
|
50
|
-
const {
|
|
50
|
+
const {flags} = await this.parse(RepoList)
|
|
51
51
|
const isJson = flags.json
|
|
52
52
|
const config = await loadConfig()
|
|
53
53
|
|
|
@@ -55,7 +55,9 @@ export default class RepoList extends Command {
|
|
|
55
55
|
this.error('GitHub org not configured. Run `dvmi init` to set up your environment.')
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const spinner = isJson
|
|
58
|
+
const spinner = isJson
|
|
59
|
+
? null
|
|
60
|
+
: ora({spinner: 'arc', color: false, text: chalk.hex('#FF6B2B')('Fetching repositories...')}).start()
|
|
59
61
|
const repos = await listRepos(config.org, {
|
|
60
62
|
language: flags.language,
|
|
61
63
|
topic: flags.topic,
|
|
@@ -65,49 +67,63 @@ export default class RepoList extends Command {
|
|
|
65
67
|
// Search filter (name + description)
|
|
66
68
|
const searchQuery = flags.search?.toLowerCase()
|
|
67
69
|
const filtered = searchQuery
|
|
68
|
-
? repos.filter(
|
|
69
|
-
r.name.toLowerCase().includes(searchQuery) ||
|
|
70
|
-
r.description.toLowerCase().includes(searchQuery),
|
|
70
|
+
? repos.filter(
|
|
71
|
+
(r) => r.name.toLowerCase().includes(searchQuery) || r.description.toLowerCase().includes(searchQuery),
|
|
71
72
|
)
|
|
72
73
|
: repos
|
|
73
74
|
|
|
74
|
-
if (isJson) return {
|
|
75
|
+
if (isJson) return {repositories: filtered, total: filtered.length}
|
|
75
76
|
|
|
76
77
|
if (repos.length === 0) {
|
|
77
78
|
this.log(chalk.yellow('No repositories found matching your filters.'))
|
|
78
|
-
return {
|
|
79
|
+
return {repositories: [], total: 0}
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
if (filtered.length === 0) {
|
|
82
83
|
this.log(chalk.dim(`No repositories matching "${flags.search}".`))
|
|
83
|
-
return {
|
|
84
|
+
return {repositories: [], total: 0}
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
// Build filter info line
|
|
87
88
|
const filterInfo = [
|
|
88
89
|
flags.language && chalk.dim(`language: ${chalk.white(flags.language)}`),
|
|
89
|
-
flags.topic
|
|
90
|
-
flags.search
|
|
91
|
-
]
|
|
90
|
+
flags.topic && chalk.dim(`topic: ${chalk.white(flags.topic)}`),
|
|
91
|
+
flags.search && chalk.dim(`search: ${chalk.white(`"${flags.search}"`)}`),
|
|
92
|
+
]
|
|
93
|
+
.filter(Boolean)
|
|
94
|
+
.join(chalk.dim(' · '))
|
|
92
95
|
|
|
93
96
|
this.log(
|
|
94
97
|
chalk.bold(`\nRepositories in ${config.org}`) +
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
(filterInfo ? chalk.dim(' — ') + filterInfo : '') +
|
|
99
|
+
chalk.dim(` (${filtered.length}${filtered.length < repos.length ? `/${repos.length}` : ''})`) +
|
|
100
|
+
'\n',
|
|
98
101
|
)
|
|
99
102
|
|
|
100
|
-
this.log(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
103
|
+
this.log(
|
|
104
|
+
renderTable(filtered, [
|
|
105
|
+
{header: 'Name', key: 'name', width: 40},
|
|
106
|
+
{
|
|
107
|
+
header: 'Language',
|
|
108
|
+
key: 'language',
|
|
109
|
+
width: 14,
|
|
110
|
+
format: (v) => v || '—',
|
|
111
|
+
colorize: (v) => (v === '—' ? chalk.dim(v) : langColor(v)),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
header: 'Last push',
|
|
115
|
+
key: 'pushedAt',
|
|
116
|
+
width: 12,
|
|
117
|
+
format: (v) => {
|
|
118
|
+
const d = new Date(String(v))
|
|
119
|
+
return isNaN(d.getTime()) ? '—' : d.toLocaleDateString()
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{header: 'Description', key: 'description', width: 60, format: (v) => String(v || '—')},
|
|
123
|
+
]),
|
|
124
|
+
)
|
|
109
125
|
|
|
110
126
|
this.log('')
|
|
111
|
-
return {
|
|
127
|
+
return {repositories: filtered, total: filtered.length}
|
|
112
128
|
}
|
|
113
129
|
}
|
package/src/commands/search.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {Command, Args, Flags} from '@oclif/core'
|
|
2
2
|
import chalk from 'chalk'
|
|
3
3
|
import ora from 'ora'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import {searchCode} from '../services/github.js'
|
|
5
|
+
import {loadConfig} from '../services/config.js'
|
|
6
|
+
import {renderTable} from '../formatters/table.js'
|
|
7
7
|
|
|
8
8
|
export default class Search extends Command {
|
|
9
|
-
static description =
|
|
9
|
+
static description = "Cerca codice nei repository dell'organizzazione"
|
|
10
10
|
|
|
11
11
|
static examples = [
|
|
12
12
|
'<%= config.bin %> search "getUserById"',
|
|
@@ -17,17 +17,17 @@ export default class Search extends Command {
|
|
|
17
17
|
static enableJsonFlag = true
|
|
18
18
|
|
|
19
19
|
static args = {
|
|
20
|
-
term: Args.string({
|
|
20
|
+
term: Args.string({description: 'Termine di ricerca', required: true}),
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
static flags = {
|
|
24
|
-
language: Flags.string({
|
|
25
|
-
repo: Flags.string({
|
|
26
|
-
limit: Flags.integer({
|
|
24
|
+
language: Flags.string({description: 'Filtra per linguaggio'}),
|
|
25
|
+
repo: Flags.string({description: 'Cerca in un repo specifico'}),
|
|
26
|
+
limit: Flags.integer({description: 'Max risultati', default: 20}),
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
async run() {
|
|
30
|
-
const {
|
|
30
|
+
const {args, flags} = await this.parse(Search)
|
|
31
31
|
const isJson = flags.json
|
|
32
32
|
const config = await loadConfig()
|
|
33
33
|
|
|
@@ -43,20 +43,22 @@ export default class Search extends Command {
|
|
|
43
43
|
})
|
|
44
44
|
spinner?.stop()
|
|
45
45
|
|
|
46
|
-
if (isJson) return {
|
|
46
|
+
if (isJson) return {results, total: results.length}
|
|
47
47
|
|
|
48
48
|
if (results.length === 0) {
|
|
49
49
|
this.log(chalk.yellow(`No results found for "${args.term}" in the organization.`))
|
|
50
|
-
return {
|
|
50
|
+
return {results: [], total: 0}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
this.log(chalk.bold(`\n${results.length} result(s) for "${args.term}":\n`))
|
|
54
|
-
this.log(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
this.log(
|
|
55
|
+
renderTable(results, [
|
|
56
|
+
{header: 'Repo', key: 'repo', width: 25},
|
|
57
|
+
{header: 'File', key: 'file', width: 45},
|
|
58
|
+
{header: 'Match', key: 'match'},
|
|
59
|
+
]),
|
|
60
|
+
)
|
|
59
61
|
|
|
60
|
-
return {
|
|
62
|
+
return {results, total: results.length}
|
|
61
63
|
}
|
|
62
64
|
}
|