@payconductor/houndsman 0.1.4 → 0.3.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/package.json +1 -1
- package/src/cli/arg-parser.ts +8 -0
- package/src/cli/commands/command.ts +23 -0
- package/src/cli/commands/context-command.ts +38 -0
- package/src/cli/commands/doctor-command.ts +55 -0
- package/src/cli/commands/findings-command.ts +28 -0
- package/src/cli/commands/help-command.ts +13 -0
- package/src/cli/commands/registry.ts +34 -0
- package/src/cli/commands/review-command.ts +22 -0
- package/src/cli/commands/server-command.ts +37 -0
- package/src/cli/commands/setup-command.ts +34 -0
- package/src/cli/commands/watchdog-command.ts +34 -0
- package/src/cli/help.ts +43 -0
- package/src/cli/index.ts +62 -260
- package/src/core/arg-parser.ts +48 -0
- package/src/core/commands/findings.ts +163 -147
- package/src/core/commands/review.ts +18 -35
- package/src/core/commands/scaffold.ts +174 -64
- package/src/core/commands/setup.ts +16 -11
- package/src/core/commands/update.ts +7 -7
- package/src/core/commands/watchdog.ts +8 -80
- package/src/core/config.ts +9 -19
- package/src/core/defaults.ts +20 -0
- package/src/core/diff-source.ts +1 -13
- package/src/core/doc-discoverer.ts +25 -15
- package/src/core/domain/budget.ts +42 -0
- package/src/core/domain/classification.ts +94 -0
- package/src/core/domain/diff.ts +53 -0
- package/src/core/domain/watchdog.ts +96 -0
- package/src/core/engine/carryover.ts +67 -0
- package/src/core/engine/houndsman-engine.ts +51 -0
- package/src/core/engine/review-scope.ts +95 -0
- package/src/core/engine/review-session.ts +51 -0
- package/src/core/file-classifier.ts +11 -67
- package/src/core/findings-state.ts +189 -38
- package/src/core/git-revision.ts +14 -40
- package/src/core/git-runner.ts +46 -0
- package/src/core/github-review.ts +423 -0
- package/src/core/orchestrator.ts +116 -337
- package/src/core/paths.ts +9 -0
- package/src/core/pr-context.ts +28 -54
- package/src/core/project-config.ts +57 -0
- package/src/core/renderers/batch-presenter.ts +40 -0
- package/src/core/renderers/brief-renderer.ts +23 -0
- package/src/core/renderers/findings-presenter.ts +74 -0
- package/src/core/renderers/manifest-writer.ts +112 -0
- package/src/core/renderers/report-renderer.ts +66 -0
- package/src/core/renderers/verify-presenter.ts +50 -0
- package/src/core/renderers/watchdog-presenter.ts +100 -0
- package/src/core/report-builder.ts +12 -7
- package/src/core/run-layout.ts +108 -0
- package/src/core/run-status.ts +6 -11
- package/src/core/services/clock.ts +12 -0
- package/src/core/services/file-classifier.ts +34 -0
- package/src/core/services/fix-batch-builder.ts +13 -0
- package/src/core/services/gh-client.ts +54 -0
- package/src/core/services/git-repository.ts +123 -0
- package/src/core/services/project-files.ts +79 -0
- package/src/core/services/verifier.ts +58 -0
- package/src/core/verify.ts +2 -1
- package/src/core/watchdog-registry.ts +58 -16
- package/src/index.ts +31 -1
- package/src/server/client/App.tsx +174 -49
- package/src/server/client/ArtifactsPanel.tsx +2 -2
- package/src/server/client/AutoRefresh.tsx +38 -0
- package/src/server/client/Filters.tsx +72 -53
- package/src/server/client/FindingCard.tsx +212 -71
- package/src/server/client/FindingsList.tsx +8 -6
- package/src/server/client/RunList.tsx +30 -7
- package/src/server/client/SubmitModal.tsx +418 -0
- package/src/server/client/SummaryBar.tsx +65 -12
- package/src/server/client/constants.ts +9 -12
- package/src/server/client/format.ts +40 -0
- package/src/server/client/icons.tsx +143 -0
- package/src/server/client/types.ts +69 -0
- package/src/server/index.ts +316 -222
- package/src/server/run-repository.ts +291 -0
- package/src/tests/__snapshots__/baseline.test.ts.snap +814 -0
- package/src/tests/baseline.test.ts +513 -0
- package/src/tests/cli.test.ts +167 -0
- package/src/tests/domain.test.ts +328 -0
- package/src/tests/github-review.test.ts +320 -0
- package/src/tests/infrastructure.test.ts +210 -0
- package/src/tests/server.test.ts +117 -0
package/src/cli/index.ts
CHANGED
|
@@ -7,11 +7,14 @@
|
|
|
7
7
|
* cwd) e orquestra funções/classes de `core/`. Não importa nenhum caminho do
|
|
8
8
|
* projeto consumidor estaticamente.
|
|
9
9
|
*
|
|
10
|
+
* Cada comando carrega seus módulos por import dinâmico (lazy) — o startup não
|
|
11
|
+
* paga por `server`, Jev (axios) ou `orchestrator` que o comando não usa.
|
|
12
|
+
*
|
|
10
13
|
* Comandos:
|
|
11
14
|
* (default) Passo 1 — preparar (classificação + briefs + manifesto)
|
|
12
15
|
* --finalize Passo 3 — consolidar os findings em review.md
|
|
13
16
|
* context Passo 0 — coletar o contexto da PR via `gh`
|
|
14
|
-
* findings <sub> Triagem/correção dos findings (list/set/done/sync/batch/verify/...)
|
|
17
|
+
* findings <sub> Triagem/correção dos findings (list/set/done/sync/batch/verify/submit/...)
|
|
15
18
|
* server Sobe a UI local de triagem (Bun.serve)
|
|
16
19
|
* setup Cria a configuração inicial do projeto (config + skills)
|
|
17
20
|
* update Sincroniza os arquivos auto-gerados (.houndsman/core)
|
|
@@ -23,274 +26,73 @@
|
|
|
23
26
|
* `--config <path>` · `--clean` · `--full` · `--no-jev` · `--fail-on-blocking`.
|
|
24
27
|
*/
|
|
25
28
|
|
|
26
|
-
import { runContextCommand } from "../core/commands/context";
|
|
27
|
-
import { runFindingsCommand, runVerifyCommand } from "../core/commands/findings";
|
|
28
|
-
import { parseReviewArgs } from "../core/commands/review";
|
|
29
|
-
import { runSetupCommand } from "../core/commands/setup";
|
|
30
|
-
import { runLibTests } from "../core/commands/test";
|
|
31
|
-
import { runUpdateCommand } from "../core/commands/update";
|
|
32
|
-
import { renderWatchdogContext } from "../core/commands/watchdog";
|
|
33
|
-
import { type HoundsmanConfig, resolveArtifactDir } from "../core/config";
|
|
34
29
|
import { loadConfig } from "../core/config-loader";
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
--exec <e> pending | in_progress | done | stale
|
|
69
|
-
--comment <text> Comentário/instrução adicional
|
|
70
|
-
--watchdog <id> Filtra por watchdog (list)
|
|
71
|
-
--severity <s> Filtra por severidade (list, batch)
|
|
72
|
-
--file <text> Filtra por trecho do caminho (list)
|
|
73
|
-
--all Inclui findings ausentes do último relatório
|
|
74
|
-
--json Saída JSON
|
|
75
|
-
--limit <n> Limita o nº de itens (list)
|
|
76
|
-
--group <key> Grupo alvo no batch (default: o primeiro)
|
|
77
|
-
--offset <n> Paginação dentro do grupo (batch)
|
|
78
|
-
--include-done Inclui já concluídos no batch
|
|
79
|
-
--apply (verify) Marca os resolvidos como done
|
|
80
|
-
--fail-on-persisting (verify) Exit 1 se algum blocking persistir`;
|
|
81
|
-
|
|
82
|
-
function readFlag(args: string[], flag: string): string | undefined {
|
|
83
|
-
const index = args.indexOf(flag);
|
|
84
|
-
return index >= 0 ? args[index + 1] : undefined;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Passo 0 — contexto da PR via `gh`. */
|
|
88
|
-
function runContext(config: HoundsmanConfig, args: string[]): void {
|
|
89
|
-
const { paths, context } = runContextCommand(config, {
|
|
90
|
-
branch: readFlag(args, "--branch"),
|
|
91
|
-
dir: readFlag(args, "--dir"),
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
if (context.available) {
|
|
95
|
-
const src = context.source;
|
|
96
|
-
const label = [src?.number ? `#${src.number}` : "", src?.title ?? ""]
|
|
97
|
-
.filter(Boolean)
|
|
98
|
-
.join(" ");
|
|
99
|
-
console.log(
|
|
100
|
-
`[houndsman] PR ${label} (${src?.head} → ${src?.base}), ${context.stats?.files ?? 0} arquivo(s).`,
|
|
101
|
-
);
|
|
102
|
-
console.log(`[houndsman] contexto: ${relFromRoot(paths.context)}`);
|
|
103
|
-
console.log(`[houndsman] brief: ${relFromRoot(paths.brief)}`);
|
|
104
|
-
console.log(
|
|
105
|
-
`[houndsman] próximo passo: subagente resume em ${relFromRoot(paths.summary)}`,
|
|
106
|
-
);
|
|
107
|
-
} else {
|
|
108
|
-
console.log(
|
|
109
|
-
`[houndsman] contexto indisponível (${context.reason}): ${context.detail}`,
|
|
110
|
-
);
|
|
111
|
-
console.log(`[houndsman] marcador: ${relFromRoot(paths.context)}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** Valida a configuração do projeto e o ambiente. */
|
|
116
|
-
async function runDoctor(config: HoundsmanConfig): Promise<void> {
|
|
117
|
-
console.log("→ Validando configuração do projeto...");
|
|
118
|
-
const { problems, warnings, jevQuestions } = checkRegistry(config);
|
|
119
|
-
|
|
120
|
-
if (problems.length > 0) {
|
|
121
|
-
console.error("\n❌ Problemas na configuração de revisão:\n");
|
|
122
|
-
for (const problem of problems) console.error(` - ${problem}`);
|
|
123
|
-
process.exit(1);
|
|
124
|
-
}
|
|
125
|
-
console.log(
|
|
126
|
-
` ✅ ${config.watchdogIds.length} watchdogs, ${jevQuestions} pergunta(s) Jev.`,
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
if (warnings.length > 0) {
|
|
130
|
-
console.log(`\n⚠️ ${warnings.length} aviso(s) (não bloqueantes):`);
|
|
131
|
-
for (const warning of warnings) console.log(` - ${warning}`);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
console.log("\n→ Ambiente:");
|
|
135
|
-
console.log(` ${Bun.which("git") ? "✅" : "❌"} git`);
|
|
136
|
-
console.log(` ${Bun.which("gh") ? "✅" : "⚠️ "} gh (opcional — contexto da PR)`);
|
|
137
|
-
await reportJevCredential();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Reporta o estado da credencial Jev: presença e validade (chamada mínima).
|
|
142
|
-
* Fail-open — nunca altera o exit code (`doctor` é diagnóstico).
|
|
143
|
-
*/
|
|
144
|
-
async function reportJevCredential(): Promise<void> {
|
|
145
|
-
const client = new JevClient();
|
|
146
|
-
if (!client.isConfigured()) {
|
|
147
|
-
console.log(" ⚠️ TYPESAFE_API_KEY (opcional — pré-análise Jev)");
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
const check = await client.validateCredential();
|
|
151
|
-
if (check.ok) {
|
|
152
|
-
console.log(` ✅ Jev: credencial válida (modelo ${check.model}).`);
|
|
153
|
-
} else if (check.kind === "invalid") {
|
|
154
|
-
console.log(` ❌ Jev: credencial inválida — ${check.detail}`);
|
|
155
|
-
} else {
|
|
156
|
-
console.log(` ⚠️ Jev: não foi possível validar — ${check.detail}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/** Contexto de um watchdog. */
|
|
161
|
-
function runWatchdog(config: HoundsmanConfig, args: string[]): void {
|
|
162
|
-
const id = args.find((arg) => !arg.startsWith("-"));
|
|
163
|
-
|
|
164
|
-
if (!id || renderWatchdogContext(config, id) === null) {
|
|
165
|
-
console.error(
|
|
166
|
-
id
|
|
167
|
-
? `Watchdog '${id}' não existe.`
|
|
168
|
-
: "Informe o id do watchdog: houndsman watchdog <id>",
|
|
169
|
-
);
|
|
170
|
-
console.error(`Ids válidos: ${config.watchdogIds.join(", ")}`);
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
console.log(
|
|
175
|
-
renderWatchdogContext(config, id, {
|
|
176
|
-
withRules: args.includes("--rules"),
|
|
177
|
-
json: args.includes("--json"),
|
|
178
|
-
}),
|
|
179
|
-
);
|
|
30
|
+
import { HoundsmanEngine } from "../core/engine/houndsman-engine";
|
|
31
|
+
import { ArgParser } from "./arg-parser";
|
|
32
|
+
import type { CliContext } from "./commands/command";
|
|
33
|
+
import { ContextCommand } from "./commands/context-command";
|
|
34
|
+
import { DoctorCommand } from "./commands/doctor-command";
|
|
35
|
+
import { FindingsCommand } from "./commands/findings-command";
|
|
36
|
+
import { HelpCommand } from "./commands/help-command";
|
|
37
|
+
import { CommandRegistry } from "./commands/registry";
|
|
38
|
+
import { ReviewCommand } from "./commands/review-command";
|
|
39
|
+
import { ServerCommand } from "./commands/server-command";
|
|
40
|
+
import { SetupCommand, TestCommand, UpdateCommand } from "./commands/setup-command";
|
|
41
|
+
import { WatchdogCommand } from "./commands/watchdog-command";
|
|
42
|
+
import { HELP } from "./help";
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Registry de comandos
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
/** Monta o registry com todos os comandos do binário. */
|
|
49
|
+
function buildRegistry(): CommandRegistry {
|
|
50
|
+
const doctor = new DoctorCommand();
|
|
51
|
+
return new CommandRegistry()
|
|
52
|
+
.register(new ReviewCommand(), { fallback: true })
|
|
53
|
+
.register(new HelpCommand())
|
|
54
|
+
.register(new ContextCommand())
|
|
55
|
+
.register(new FindingsCommand())
|
|
56
|
+
.register(new WatchdogCommand())
|
|
57
|
+
.register(new ServerCommand())
|
|
58
|
+
.register(new SetupCommand())
|
|
59
|
+
.register(new UpdateCommand())
|
|
60
|
+
.register(new TestCommand())
|
|
61
|
+
.register(doctor)
|
|
62
|
+
.registerAs("check", doctor);
|
|
180
63
|
}
|
|
181
64
|
|
|
182
65
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
* o JSON. O `verify` é assíncrono (consulta opcional ao Jev) — roteado à parte.
|
|
186
|
-
*/
|
|
187
|
-
async function runFindings(config: HoundsmanConfig, args: string[]): Promise<void> {
|
|
188
|
-
const sub = args.find((arg) => !arg.startsWith("-"));
|
|
189
|
-
const result =
|
|
190
|
-
sub === "verify"
|
|
191
|
-
? await runVerifyCommand(config, args)
|
|
192
|
-
: runFindingsCommand(config, args);
|
|
193
|
-
if (result.exitCode !== 0) {
|
|
194
|
-
console.error(result.output);
|
|
195
|
-
process.exit(result.exitCode);
|
|
196
|
-
}
|
|
197
|
-
console.log(result.output);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/** Abre a URL no navegador padrão (best-effort, por plataforma). */
|
|
201
|
-
function openBrowser(url: string): void {
|
|
202
|
-
try {
|
|
203
|
-
const command =
|
|
204
|
-
process.platform === "win32"
|
|
205
|
-
? ["cmd", "/c", "start", "", url]
|
|
206
|
-
: process.platform === "darwin"
|
|
207
|
-
? ["open", url]
|
|
208
|
-
: ["xdg-open", url];
|
|
209
|
-
Bun.spawn(command, { stdout: "ignore", stderr: "ignore" });
|
|
210
|
-
} catch {
|
|
211
|
-
// best-effort — se não abrir, o usuário copia a URL.
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/** Subcomando `server` — sobe a UI local de triagem. */
|
|
216
|
-
function runServer(config: HoundsmanConfig, args: string[]): void {
|
|
217
|
-
const port = Number(readFlag(args, "--port")) || 7331;
|
|
218
|
-
const host = readFlag(args, "--host") ?? "127.0.0.1";
|
|
219
|
-
const { url } = startServer({ config, port, host });
|
|
220
|
-
console.log(`[houndsman] servidor em ${url}`);
|
|
221
|
-
console.log("[houndsman] Ctrl+C para encerrar.");
|
|
222
|
-
if (args.includes("--open")) openBrowser(url);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/** Passo 1/3 — preparar ou finalizar. */
|
|
226
|
-
async function runReview(config: HoundsmanConfig, args: string[]): Promise<void> {
|
|
227
|
-
const opts = parseReviewArgs(args, resolveArtifactDir(config));
|
|
228
|
-
const branch = resolveBranch(opts);
|
|
229
|
-
|
|
230
|
-
if (opts.finalize) {
|
|
231
|
-
const result = finalize(opts, branch, config);
|
|
232
|
-
if (opts.failOnBlocking && result.blockers > 0) process.exit(1);
|
|
233
|
-
} else {
|
|
234
|
-
await prepare(opts, branch, config);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Executa um subcomando. `--config` sobrepõe a descoberta automática.
|
|
240
|
-
* Comandos que não precisam de projeto (`test`, `setup`) são tratados antes do load.
|
|
66
|
+
* Bootstrap do CLI: resolve o comando no registry, carrega a config sob demanda
|
|
67
|
+
* e delega a execução (o comando decide log/exit).
|
|
241
68
|
*/
|
|
242
69
|
export async function main(): Promise<void> {
|
|
243
70
|
const argv = process.argv.slice(2);
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
if (command === "update") {
|
|
256
|
-
await runUpdateCommand({ cwd: process.cwd(), args: rest });
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (command === "help") {
|
|
261
|
-
console.info(HELP);
|
|
262
|
-
return;
|
|
71
|
+
const parser = ArgParser.from(argv);
|
|
72
|
+
const commandName = parser.command();
|
|
73
|
+
const registry = buildRegistry();
|
|
74
|
+
const command = registry.resolve(commandName);
|
|
75
|
+
|
|
76
|
+
if (!command) {
|
|
77
|
+
console.error(`Subcomando desconhecido: '${commandName}'.\n`);
|
|
78
|
+
console.error(HELP);
|
|
79
|
+
process.exit(1);
|
|
263
80
|
}
|
|
264
81
|
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return;
|
|
280
|
-
case "findings":
|
|
281
|
-
await runFindings(config, rest);
|
|
282
|
-
return;
|
|
283
|
-
case "server":
|
|
284
|
-
runServer(config, rest);
|
|
285
|
-
return;
|
|
286
|
-
case undefined:
|
|
287
|
-
await runReview(config, rest);
|
|
288
|
-
return;
|
|
289
|
-
default:
|
|
290
|
-
console.error(`Subcomando desconhecido: '${command}'.\n`);
|
|
291
|
-
console.error(HELP);
|
|
292
|
-
process.exit(1);
|
|
293
|
-
}
|
|
82
|
+
const context: CliContext = {
|
|
83
|
+
cwd: process.cwd(),
|
|
84
|
+
out: (message) => console.log(message),
|
|
85
|
+
err: (message) => console.error(message),
|
|
86
|
+
engine: async () => {
|
|
87
|
+
const { config } = await loadConfig({
|
|
88
|
+
configPath: parser.value("--config"),
|
|
89
|
+
});
|
|
90
|
+
return new HoundsmanEngine(config);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const code = await command.run(parser.rest(), context);
|
|
95
|
+
if (code !== 0) process.exit(code);
|
|
294
96
|
}
|
|
295
97
|
|
|
296
98
|
if (import.meta.main) {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser simples de argv (biblioteca) — compartilhado por CLI e comandos.
|
|
3
|
+
*
|
|
4
|
+
* Não valida flags: só lê valores/booleanos e separa o subcomando.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** argv com leitura de flags por nome. */
|
|
8
|
+
export class ArgParser {
|
|
9
|
+
readonly argv: string[];
|
|
10
|
+
|
|
11
|
+
constructor(argv: readonly string[]) {
|
|
12
|
+
this.argv = [...argv];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
static from(argv: readonly string[]): ArgParser {
|
|
16
|
+
return new ArgParser(argv);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Valor da flag (`--x y`), se presente. */
|
|
20
|
+
value(flag: string): string | undefined {
|
|
21
|
+
const index = this.argv.indexOf(flag);
|
|
22
|
+
return index >= 0 ? this.argv[index + 1] : undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** `true` se a flag está presente. */
|
|
26
|
+
has(flag: string): boolean {
|
|
27
|
+
return this.argv.includes(flag);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Primeiro token que não é flag (subcomando). */
|
|
31
|
+
command(): string | undefined {
|
|
32
|
+
const first = this.argv[0];
|
|
33
|
+
return first && !first.startsWith("-") ? first : undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Argumentos sem o subcomando. */
|
|
37
|
+
rest(): string[] {
|
|
38
|
+
const command = this.command();
|
|
39
|
+
if (!command) return this.argv;
|
|
40
|
+
const index = this.argv.indexOf(command);
|
|
41
|
+
return [...this.argv.slice(0, index), ...this.argv.slice(index + 1)];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Todos os tokens que não começam com `-`. */
|
|
45
|
+
positionals(): string[] {
|
|
46
|
+
return this.argv.filter((arg) => !arg.startsWith("-"));
|
|
47
|
+
}
|
|
48
|
+
}
|