@gaberrb/polypus 0.3.0 → 0.4.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 +23 -0
- package/dist/index.js +403 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
6
|
-
import pc12 from "picocolors";
|
|
5
|
+
import pc13 from "picocolors";
|
|
7
6
|
|
|
8
7
|
// src/cli/commands/add-agent.ts
|
|
9
8
|
import pc from "picocolors";
|
|
@@ -74,11 +73,13 @@ var en = {
|
|
|
74
73
|
"cli.description": "Agentic coding harness that makes any AI API generate and apply code \u2014 OpenRouter, Ollama, and any OpenAI-compatible endpoint.",
|
|
75
74
|
"cli.opt.lang": "interface language: pt-BR | en",
|
|
76
75
|
"cli.cmd.setup": "Interactive setup wizard (configure agents, keys, permissions)",
|
|
76
|
+
"cli.cmd.init": "Scaffold a .poly/ workspace (agents.md, skills, SDD spec template, README)",
|
|
77
|
+
"cli.opt.force": "overwrite files that already exist",
|
|
77
78
|
"cli.cmd.addAgent": "Register a new agent (API key + model)",
|
|
78
79
|
"cli.cmd.removeAgent": "Remove a configured agent",
|
|
79
80
|
"cli.cmd.listAgents": "List configured agents",
|
|
80
81
|
"cli.cmd.run": "Run a coding task with an agent",
|
|
81
|
-
"cli.cmd.swarm": "Split a task across multiple agents working in parallel git worktrees",
|
|
82
|
+
"cli.cmd.swarm": "Split a task across multiple agents working in parallel git worktrees (requires 3+ configured agents)",
|
|
82
83
|
"cli.cmd.models": "Browse OpenRouter models (price, context, tool support)",
|
|
83
84
|
"cli.cmd.prd": "Generate a PRD from a GitHub issue (uses a free OpenRouter model)",
|
|
84
85
|
"cli.arg.prdIssue": "issue number to turn into a PRD",
|
|
@@ -164,6 +165,7 @@ var en = {
|
|
|
164
165
|
].join("\n"),
|
|
165
166
|
// swarm
|
|
166
167
|
"swarm.noAgents": "No agents configured. Run `polypus setup` or `polypus add-agent` first.",
|
|
168
|
+
"swarm.needsAgents": "Swarm mode needs at least {min} configured agents (you have {have}). Add more with `polypus add-agent`, or use `polypus run` for a single agent.",
|
|
167
169
|
"swarm.status": "swarm agents=[{agents}] workspace={workspace}",
|
|
168
170
|
"swarm.bypassNote": "Workers run in bypass mode inside isolated git worktrees; branches are merged at the end.",
|
|
169
171
|
"swarm.decomposed": "Decomposed into {n} subtask(s):",
|
|
@@ -189,6 +191,12 @@ var en = {
|
|
|
189
191
|
"swarm.conflictsHeader": "\u26A0 {n} branch(es) had merge conflicts (kept for inspection):",
|
|
190
192
|
"swarm.statusDone": "done",
|
|
191
193
|
"swarm.statusIncomplete": "incomplete",
|
|
194
|
+
// init
|
|
195
|
+
"init.created": "\u2713 .poly scaffolded:",
|
|
196
|
+
"init.skipped": "Kept (already existed):",
|
|
197
|
+
"init.allExist": "Nothing to do \u2014 .poly already has these files:",
|
|
198
|
+
"init.forceHint": "Run `polypus init --force` to overwrite them.",
|
|
199
|
+
"init.tip": "Tip: edit .poly/agents.md \u2014 Polypus loads it into the agent's context automatically.",
|
|
192
200
|
// wizard
|
|
193
201
|
"wizard.title": " polypus setup ",
|
|
194
202
|
"wizard.intro": [
|
|
@@ -274,7 +282,8 @@ var en = {
|
|
|
274
282
|
"welcome.hints": "Type your task and press Enter \xB7 ESC cancels \xB7 /help \xB7 /exit",
|
|
275
283
|
"welcome.firstRun": "No agents configured yet \u2014 let's set you up.",
|
|
276
284
|
// agent system prompt
|
|
277
|
-
"prompt.language": "Communicate with the user in {language}."
|
|
285
|
+
"prompt.language": "Communicate with the user in {language}.",
|
|
286
|
+
"prompt.projectInstructions": "Project-specific operating instructions follow, loaded from `.poly/agents.md`. Treat them as authoritative for how to work in THIS repo. Paths they reference (e.g. skills/*.md, ../context.md, ../rules.md) are relative to the `.poly/` directory \u2014 read those files when relevant before acting:"
|
|
278
287
|
};
|
|
279
288
|
var ptBR = {
|
|
280
289
|
"common.default": "padr\xE3o",
|
|
@@ -283,11 +292,13 @@ var ptBR = {
|
|
|
283
292
|
"cli.description": "Harness ag\xEAntico que faz qualquer API de IA gerar e aplicar c\xF3digo \u2014 OpenRouter, Ollama e qualquer endpoint compat\xEDvel com OpenAI.",
|
|
284
293
|
"cli.opt.lang": "idioma da interface: pt-BR | en",
|
|
285
294
|
"cli.cmd.setup": "Assistente de configura\xE7\xE3o interativo (agentes, chaves, permiss\xF5es)",
|
|
295
|
+
"cli.cmd.init": "Cria um workspace .poly/ (agents.md, skills, template de spec SDD, README)",
|
|
296
|
+
"cli.opt.force": "sobrescreve arquivos que j\xE1 existem",
|
|
286
297
|
"cli.cmd.addAgent": "Cadastra um novo agente (chave de API + modelo)",
|
|
287
298
|
"cli.cmd.removeAgent": "Remove um agente configurado",
|
|
288
299
|
"cli.cmd.listAgents": "Lista os agentes configurados",
|
|
289
300
|
"cli.cmd.run": "Executa uma tarefa de c\xF3digo com um agente",
|
|
290
|
-
"cli.cmd.swarm": "Divide uma tarefa entre v\xE1rios agentes
|
|
301
|
+
"cli.cmd.swarm": "Divide uma tarefa entre v\xE1rios agentes em git worktrees paralelas (requer 3+ agentes configurados)",
|
|
291
302
|
"cli.cmd.models": "Explora os modelos do OpenRouter (pre\xE7o, contexto, suporte a tools)",
|
|
292
303
|
"cli.cmd.prd": "Gera um PRD a partir de uma issue do GitHub (usa um modelo gratuito do OpenRouter)",
|
|
293
304
|
"cli.arg.prdIssue": "n\xFAmero da issue para transformar em PRD",
|
|
@@ -369,6 +380,7 @@ var ptBR = {
|
|
|
369
380
|
"Qualquer outra coisa \xE9 enviada ao agente como tarefa."
|
|
370
381
|
].join("\n"),
|
|
371
382
|
"swarm.noAgents": "Nenhum agente configurado. Rode `polypus setup` ou `polypus add-agent` primeiro.",
|
|
383
|
+
"swarm.needsAgents": "O modo swarm precisa de pelo menos {min} agentes configurados (voc\xEA tem {have}). Adicione mais com `polypus add-agent`, ou use `polypus run` para um agente s\xF3.",
|
|
372
384
|
"swarm.status": "swarm agentes=[{agents}] workspace={workspace}",
|
|
373
385
|
"swarm.bypassNote": "Os workers rodam em modo bypass dentro de git worktrees isoladas; os branches s\xE3o mesclados no final.",
|
|
374
386
|
"swarm.decomposed": "Dividido em {n} subtarefa(s):",
|
|
@@ -394,6 +406,12 @@ var ptBR = {
|
|
|
394
406
|
"swarm.conflictsHeader": "\u26A0 {n} branch(es) tiveram conflitos de merge (mantidos para inspe\xE7\xE3o):",
|
|
395
407
|
"swarm.statusDone": "ok",
|
|
396
408
|
"swarm.statusIncomplete": "incompleta",
|
|
409
|
+
// init
|
|
410
|
+
"init.created": "\u2713 .poly criado:",
|
|
411
|
+
"init.skipped": "Mantidos (j\xE1 existiam):",
|
|
412
|
+
"init.allExist": "Nada a fazer \u2014 o .poly j\xE1 tem estes arquivos:",
|
|
413
|
+
"init.forceHint": "Rode `polypus init --force` para sobrescrev\xEA-los.",
|
|
414
|
+
"init.tip": "Dica: edite o .poly/agents.md \u2014 o Polypus carrega ele no contexto do agente automaticamente.",
|
|
397
415
|
"wizard.title": " configura\xE7\xE3o do polypus ",
|
|
398
416
|
"wizard.intro": [
|
|
399
417
|
"O Polypus comanda qualquer API de IA para ler e escrever c\xF3digo neste tipo de projeto.",
|
|
@@ -455,6 +473,7 @@ var ptBR = {
|
|
|
455
473
|
"wizard.envInvalid": "Use letras, d\xEDgitos e sublinhados",
|
|
456
474
|
"wizard.keyPrompt": "Chave de API (armazenada em texto puro no arquivo de config)",
|
|
457
475
|
"prompt.language": "Comunique-se com o usu\xE1rio em {language}.",
|
|
476
|
+
"prompt.projectInstructions": "Seguem instru\xE7\xF5es operacionais espec\xEDficas do projeto, carregadas de `.poly/agents.md`. Trate-as como autoritativas para trabalhar NESTE reposit\xF3rio. Os caminhos que elas citam (ex.: skills/*.md, ../context.md, ../rules.md) s\xE3o relativos \xE0 pasta `.poly/` \u2014 leia esses arquivos quando relevante antes de agir:",
|
|
458
477
|
"models.fetching": "Buscando modelos do OpenRouter\u2026",
|
|
459
478
|
"models.fetchError": "N\xE3o foi poss\xEDvel buscar modelos: {msg}",
|
|
460
479
|
"models.none": "Nenhum modelo corresponde aos filtros.",
|
|
@@ -1025,6 +1044,10 @@ function basePreamble(ctx) {
|
|
|
1025
1044
|
"- Do not ask for permission and do not say you cannot edit files \u2014 you can. Just emit the tool calls.",
|
|
1026
1045
|
"- Make the changes directly. When the task is fully done, call the `finish` tool with a short summary.",
|
|
1027
1046
|
t("prompt.language", { language: LOCALE_NAMES[getLocale()] }),
|
|
1047
|
+
ctx.projectInstructions ? `
|
|
1048
|
+
${t("prompt.projectInstructions")}
|
|
1049
|
+
|
|
1050
|
+
${ctx.projectInstructions}` : "",
|
|
1028
1051
|
ctx.briefing ? `
|
|
1029
1052
|
Your assigned task:
|
|
1030
1053
|
${ctx.briefing}` : ""
|
|
@@ -1668,6 +1691,23 @@ function formatSchema(spec) {
|
|
|
1668
1691
|
return lines.join("\n") || " (no parameters)";
|
|
1669
1692
|
}
|
|
1670
1693
|
|
|
1694
|
+
// src/core/agent/project-context.ts
|
|
1695
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
1696
|
+
import { join as join2 } from "path";
|
|
1697
|
+
var INSTRUCTION_FILES = [join2(".poly", "agents.md"), "AGENTS.md"];
|
|
1698
|
+
var MAX_CHARS2 = 8e3;
|
|
1699
|
+
async function loadProjectInstructions(workspace) {
|
|
1700
|
+
for (const rel of INSTRUCTION_FILES) {
|
|
1701
|
+
try {
|
|
1702
|
+
const raw = (await readFile5(join2(workspace, rel), "utf8")).trim();
|
|
1703
|
+
if (!raw) continue;
|
|
1704
|
+
return raw.length > MAX_CHARS2 ? raw.slice(0, MAX_CHARS2) + "\n\u2026(truncated)" : raw;
|
|
1705
|
+
} catch {
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
return void 0;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1671
1711
|
// src/core/agent/loop.ts
|
|
1672
1712
|
function looksLikeStall(text2) {
|
|
1673
1713
|
const lc = text2.toLowerCase();
|
|
@@ -1710,10 +1750,12 @@ async function runAgent(opts) {
|
|
|
1710
1750
|
const maxReprompts = opts.maxReprompts ?? 3;
|
|
1711
1751
|
const driver = makeDriver(agent.toolMode, toolSpecs());
|
|
1712
1752
|
const ctx = { workspace: opts.workspace, permissions };
|
|
1713
|
-
const
|
|
1714
|
-
|
|
1753
|
+
const seeding = !(opts.history && opts.history.length > 0);
|
|
1754
|
+
const promptContext = seeding && opts.promptContext.projectInstructions === void 0 ? { ...opts.promptContext, projectInstructions: await loadProjectInstructions(opts.workspace) } : opts.promptContext;
|
|
1755
|
+
const messages = seeding ? [
|
|
1756
|
+
{ role: "system", content: driver.systemPrompt(promptContext) },
|
|
1715
1757
|
{ role: "user", content: opts.task }
|
|
1716
|
-
];
|
|
1758
|
+
] : [...opts.history, { role: "user", content: opts.task }];
|
|
1717
1759
|
let consecutiveNoTool = 0;
|
|
1718
1760
|
let lastFailSig = "";
|
|
1719
1761
|
let failStreak = 0;
|
|
@@ -2182,6 +2224,23 @@ async function promptApiKey(provider) {
|
|
|
2182
2224
|
|
|
2183
2225
|
// src/ui/banner.ts
|
|
2184
2226
|
import pc5 from "picocolors";
|
|
2227
|
+
|
|
2228
|
+
// src/core/version.ts
|
|
2229
|
+
import { createRequire } from "module";
|
|
2230
|
+
function resolveVersion() {
|
|
2231
|
+
const require2 = createRequire(import.meta.url);
|
|
2232
|
+
for (const rel of ["../package.json", "../../package.json"]) {
|
|
2233
|
+
try {
|
|
2234
|
+
const version = require2(rel).version;
|
|
2235
|
+
if (typeof version === "string" && version.length > 0) return version;
|
|
2236
|
+
} catch {
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
return "0.0.0";
|
|
2240
|
+
}
|
|
2241
|
+
var VERSION = resolveVersion();
|
|
2242
|
+
|
|
2243
|
+
// src/ui/banner.ts
|
|
2185
2244
|
var RESET = "\x1B[0m";
|
|
2186
2245
|
var useColor = (Boolean(process.stdout.isTTY) || Boolean(process.env.FORCE_COLOR)) && !process.env.NO_COLOR;
|
|
2187
2246
|
var animated = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR && !process.env.POLYPUS_NO_ANIM;
|
|
@@ -2255,7 +2314,7 @@ function colorChar(rowIdx, ch) {
|
|
|
2255
2314
|
function renderArtRow(rowIdx, line) {
|
|
2256
2315
|
return [...center(line)].map((ch) => colorChar(rowIdx, ch)).join("");
|
|
2257
2316
|
}
|
|
2258
|
-
var tagline = () => c1(t("welcome.tagline")) + pc5.dim(
|
|
2317
|
+
var tagline = () => c1(t("welcome.tagline")) + pc5.dim(` v${VERSION}`);
|
|
2259
2318
|
function authorLine() {
|
|
2260
2319
|
return pc5.dim("by ") + c2(AUTHOR.name) + pc5.dim(" \xB7 ") + c1(AUTHOR.github) + pc5.dim(" \xB7 ") + c1(AUTHOR.linkedin);
|
|
2261
2320
|
}
|
|
@@ -2678,13 +2737,302 @@ async function setup() {
|
|
|
2678
2737
|
await runWizard();
|
|
2679
2738
|
}
|
|
2680
2739
|
|
|
2681
|
-
// src/cli/commands/
|
|
2740
|
+
// src/cli/commands/init.ts
|
|
2682
2741
|
import pc8 from "picocolors";
|
|
2683
2742
|
|
|
2743
|
+
// src/core/scaffold/init.ts
|
|
2744
|
+
import { mkdir as mkdir3, writeFile as writeFile4, access } from "fs/promises";
|
|
2745
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
2746
|
+
|
|
2747
|
+
// src/core/scaffold/templates.ts
|
|
2748
|
+
function polyTemplates(locale) {
|
|
2749
|
+
return locale === "pt-BR" ? PT : EN;
|
|
2750
|
+
}
|
|
2751
|
+
var EN = {
|
|
2752
|
+
"agents.md": `# agents.md \u2014 how an AI agent operates this repo
|
|
2753
|
+
|
|
2754
|
+
> Local workspace under \`.poly/\`. Conditions any AI agent (Polypus, Claude, \u2026)
|
|
2755
|
+
> to work the way this project expects. **Polypus loads this file automatically**
|
|
2756
|
+
> into the agent's system prompt on every run, so keep it accurate and lean.
|
|
2757
|
+
|
|
2758
|
+
## Role
|
|
2759
|
+
|
|
2760
|
+
You implement changes end to end \u2014 from understanding the task to a reviewable
|
|
2761
|
+
result \u2014 respecting the rules below.
|
|
2762
|
+
|
|
2763
|
+
## Golden rules
|
|
2764
|
+
|
|
2765
|
+
1. Green before a PR: the project builds, type-checks and tests pass.
|
|
2766
|
+
2. Keep docs/changelog in sync with behavior changes.
|
|
2767
|
+
3. Confirm before irreversible actions (publishing, deleting, force-pushing).
|
|
2768
|
+
4. Small, targeted changes over broad rewrites.
|
|
2769
|
+
|
|
2770
|
+
## Skills index
|
|
2771
|
+
|
|
2772
|
+
| Skill | When to use |
|
|
2773
|
+
|-------|-------------|
|
|
2774
|
+
| [skills/coding.md](skills/coding.md) | Technical standards for any code change |
|
|
2775
|
+
| [skills/spec-driven.md](skills/spec-driven.md) | Write a spec before non-trivial work |
|
|
2776
|
+
|
|
2777
|
+
## Environment
|
|
2778
|
+
|
|
2779
|
+
- Describe the OS, shell, package manager and any tooling the agent needs.
|
|
2780
|
+
- Note where credentials/CLIs live and how commands are run.
|
|
2781
|
+
`,
|
|
2782
|
+
"README.md": `# .poly \u2014 your project's AI operating manual
|
|
2783
|
+
|
|
2784
|
+
\`.poly/\` is a small, local workspace that teaches AI agents how to work in THIS
|
|
2785
|
+
repository. Gitignore it to keep it personal, or commit it to standardize the
|
|
2786
|
+
workflow across your team.
|
|
2787
|
+
|
|
2788
|
+
## What's inside
|
|
2789
|
+
|
|
2790
|
+
- **\`agents.md\`** \u2014 the entry point: role, golden rules and an index of skills.
|
|
2791
|
+
Polypus loads it automatically into the agent's system prompt on every run.
|
|
2792
|
+
- **\`skills/\`** \u2014 focused how-to guides the agent reads when relevant.
|
|
2793
|
+
- **\`templates/spec.md\`** \u2014 a lean Spec-Driven Development (SDD) template.
|
|
2794
|
+
|
|
2795
|
+
## How it works
|
|
2796
|
+
|
|
2797
|
+
1. You describe a task to the agent.
|
|
2798
|
+
2. The agent reads \`agents.md\`, follows the golden rules and opens the skills it needs.
|
|
2799
|
+
3. For non-trivial work, it writes a spec first from \`templates/spec.md\`.
|
|
2800
|
+
|
|
2801
|
+
## Extend it
|
|
2802
|
+
|
|
2803
|
+
- Edit \`agents.md\` to encode your conventions.
|
|
2804
|
+
- Add one skill file per recurring workflow (releases, reviews, migrations\u2026).
|
|
2805
|
+
- Reference new skills from \`agents.md\` so the agent can discover them.
|
|
2806
|
+
|
|
2807
|
+
Regenerate any missing files with \`polypus init\` (existing files are preserved;
|
|
2808
|
+
use \`--force\` to overwrite).
|
|
2809
|
+
`,
|
|
2810
|
+
"skills/coding.md": `# skill: coding
|
|
2811
|
+
|
|
2812
|
+
Technical standards for changes in this repo.
|
|
2813
|
+
|
|
2814
|
+
## Principles
|
|
2815
|
+
|
|
2816
|
+
- Match the style, naming and structure of the surrounding code.
|
|
2817
|
+
- Prefer small, targeted edits over broad rewrites.
|
|
2818
|
+
- Add or update tests with every behavior change.
|
|
2819
|
+
|
|
2820
|
+
## Checklist before opening a PR
|
|
2821
|
+
|
|
2822
|
+
- [ ] Builds and type-checks
|
|
2823
|
+
- [ ] Tests pass
|
|
2824
|
+
- [ ] Docs / changelog updated when behavior changed
|
|
2825
|
+
`,
|
|
2826
|
+
"skills/spec-driven.md": `# skill: spec-driven development
|
|
2827
|
+
|
|
2828
|
+
For anything non-trivial, write a short spec BEFORE coding.
|
|
2829
|
+
|
|
2830
|
+
## Flow
|
|
2831
|
+
|
|
2832
|
+
1. Copy \`templates/spec.md\` into your issue (or \`specs/<slug>.md\`).
|
|
2833
|
+
2. Fill **Why / What / Acceptance criteria / Out of scope**.
|
|
2834
|
+
3. Get a thumbs-up, then implement to the acceptance criteria.
|
|
2835
|
+
4. Keep the spec updated if scope changes.
|
|
2836
|
+
|
|
2837
|
+
Lean by design: if a section is empty, delete it.
|
|
2838
|
+
`,
|
|
2839
|
+
"templates/spec.md": `# Spec: <title>
|
|
2840
|
+
|
|
2841
|
+
> Status: draft \xB7 Owner: <name> \xB7 Updated: <yyyy-mm-dd>
|
|
2842
|
+
|
|
2843
|
+
## Why
|
|
2844
|
+
|
|
2845
|
+
What problem are we solving, and for whom? Why now?
|
|
2846
|
+
|
|
2847
|
+
## What
|
|
2848
|
+
|
|
2849
|
+
The change in plain terms \u2014 the behavior a user will actually see.
|
|
2850
|
+
|
|
2851
|
+
## Acceptance criteria
|
|
2852
|
+
|
|
2853
|
+
- [ ] Observable outcome 1
|
|
2854
|
+
- [ ] Observable outcome 2
|
|
2855
|
+
|
|
2856
|
+
## Out of scope
|
|
2857
|
+
|
|
2858
|
+
- Things we are explicitly NOT doing here.
|
|
2859
|
+
|
|
2860
|
+
## Notes / open questions
|
|
2861
|
+
|
|
2862
|
+
- \u2026
|
|
2863
|
+
`
|
|
2864
|
+
};
|
|
2865
|
+
var PT = {
|
|
2866
|
+
"agents.md": `# agents.md \u2014 como um agente de IA opera este reposit\xF3rio
|
|
2867
|
+
|
|
2868
|
+
> Workspace local em \`.poly/\`. Condiciona qualquer agente de IA (Polypus, Claude\u2026)
|
|
2869
|
+
> a trabalhar do jeito que este projeto espera. **O Polypus carrega este arquivo
|
|
2870
|
+
> automaticamente** no system prompt do agente a cada execu\xE7\xE3o \u2014 mantenha-o
|
|
2871
|
+
> preciso e enxuto.
|
|
2872
|
+
|
|
2873
|
+
## Papel
|
|
2874
|
+
|
|
2875
|
+
Voc\xEA implementa mudan\xE7as de ponta a ponta \u2014 do entendimento da tarefa a um
|
|
2876
|
+
resultado revis\xE1vel \u2014 respeitando as regras abaixo.
|
|
2877
|
+
|
|
2878
|
+
## Regras de ouro
|
|
2879
|
+
|
|
2880
|
+
1. Verde antes do PR: o projeto builda, passa no type-check e nos testes.
|
|
2881
|
+
2. Mantenha docs/changelog em sincronia com mudan\xE7as de comportamento.
|
|
2882
|
+
3. Confirme antes de a\xE7\xF5es irrevers\xEDveis (publicar, deletar, force-push).
|
|
2883
|
+
4. Mudan\xE7as pequenas e focadas em vez de reescritas amplas.
|
|
2884
|
+
|
|
2885
|
+
## \xCDndice de skills
|
|
2886
|
+
|
|
2887
|
+
| Skill | Quando usar |
|
|
2888
|
+
|-------|-------------|
|
|
2889
|
+
| [skills/coding.md](skills/coding.md) | Padr\xF5es t\xE9cnicos para qualquer mudan\xE7a de c\xF3digo |
|
|
2890
|
+
| [skills/spec-driven.md](skills/spec-driven.md) | Escrever um spec antes de trabalho n\xE3o-trivial |
|
|
2891
|
+
|
|
2892
|
+
## Ambiente
|
|
2893
|
+
|
|
2894
|
+
- Descreva SO, shell, gerenciador de pacotes e ferramentas que o agente precisa.
|
|
2895
|
+
- Anote onde ficam credenciais/CLIs e como os comandos s\xE3o executados.
|
|
2896
|
+
`,
|
|
2897
|
+
"README.md": `# .poly \u2014 o manual de opera\xE7\xE3o de IA do seu projeto
|
|
2898
|
+
|
|
2899
|
+
O \`.poly/\` \xE9 um workspace local e pequeno que ensina agentes de IA a trabalhar
|
|
2900
|
+
NESTE reposit\xF3rio. Coloque no .gitignore para mant\xEA-lo pessoal, ou commite para
|
|
2901
|
+
padronizar o fluxo entre o time.
|
|
2902
|
+
|
|
2903
|
+
## O que tem dentro
|
|
2904
|
+
|
|
2905
|
+
- **\`agents.md\`** \u2014 o ponto de entrada: papel, regras de ouro e um \xEDndice de skills.
|
|
2906
|
+
O Polypus carrega ele automaticamente no system prompt do agente a cada execu\xE7\xE3o.
|
|
2907
|
+
- **\`skills/\`** \u2014 guias pr\xE1ticos e focados que o agente l\xEA quando relevante.
|
|
2908
|
+
- **\`templates/spec.md\`** \u2014 um template enxuto de Spec-Driven Development (SDD).
|
|
2909
|
+
|
|
2910
|
+
## Como funciona
|
|
2911
|
+
|
|
2912
|
+
1. Voc\xEA descreve uma tarefa ao agente.
|
|
2913
|
+
2. O agente l\xEA o \`agents.md\`, segue as regras de ouro e abre as skills necess\xE1rias.
|
|
2914
|
+
3. Para trabalho n\xE3o-trivial, escreve um spec primeiro a partir de \`templates/spec.md\`.
|
|
2915
|
+
|
|
2916
|
+
## Como estender
|
|
2917
|
+
|
|
2918
|
+
- Edite o \`agents.md\` para codificar suas conven\xE7\xF5es.
|
|
2919
|
+
- Adicione um arquivo de skill por fluxo recorrente (releases, reviews, migra\xE7\xF5es\u2026).
|
|
2920
|
+
- Referencie as novas skills no \`agents.md\` para o agente descobri-las.
|
|
2921
|
+
|
|
2922
|
+
Regenere arquivos que faltarem com \`polypus init\` (os existentes s\xE3o preservados;
|
|
2923
|
+
use \`--force\` para sobrescrever).
|
|
2924
|
+
`,
|
|
2925
|
+
"skills/coding.md": `# skill: coding
|
|
2926
|
+
|
|
2927
|
+
Padr\xF5es t\xE9cnicos para mudan\xE7as neste reposit\xF3rio.
|
|
2928
|
+
|
|
2929
|
+
## Princ\xEDpios
|
|
2930
|
+
|
|
2931
|
+
- Siga o estilo, a nomenclatura e a estrutura do c\xF3digo ao redor.
|
|
2932
|
+
- Prefira edi\xE7\xF5es pequenas e focadas a reescritas amplas.
|
|
2933
|
+
- Adicione ou atualize testes a cada mudan\xE7a de comportamento.
|
|
2934
|
+
|
|
2935
|
+
## Checklist antes de abrir um PR
|
|
2936
|
+
|
|
2937
|
+
- [ ] Builda e passa no type-check
|
|
2938
|
+
- [ ] Testes passam
|
|
2939
|
+
- [ ] Docs / changelog atualizados quando o comportamento mudou
|
|
2940
|
+
`,
|
|
2941
|
+
"skills/spec-driven.md": `# skill: spec-driven development
|
|
2942
|
+
|
|
2943
|
+
Para qualquer coisa n\xE3o-trivial, escreva um spec curto ANTES de codar.
|
|
2944
|
+
|
|
2945
|
+
## Fluxo
|
|
2946
|
+
|
|
2947
|
+
1. Copie \`templates/spec.md\` para a issue (ou \`specs/<slug>.md\`).
|
|
2948
|
+
2. Preencha **Por qu\xEA / O qu\xEA / Crit\xE9rios de aceite / Fora de escopo**.
|
|
2949
|
+
3. Valide com um "ok", ent\xE3o implemente at\xE9 os crit\xE9rios de aceite.
|
|
2950
|
+
4. Mantenha o spec atualizado se o escopo mudar.
|
|
2951
|
+
|
|
2952
|
+
Enxuto por design: se uma se\xE7\xE3o ficar vazia, apague-a.
|
|
2953
|
+
`,
|
|
2954
|
+
"templates/spec.md": `# Spec: <t\xEDtulo>
|
|
2955
|
+
|
|
2956
|
+
> Status: rascunho \xB7 Dono: <nome> \xB7 Atualizado: <aaaa-mm-dd>
|
|
2957
|
+
|
|
2958
|
+
## Por qu\xEA
|
|
2959
|
+
|
|
2960
|
+
Que problema estamos resolvendo, e para quem? Por que agora?
|
|
2961
|
+
|
|
2962
|
+
## O qu\xEA
|
|
2963
|
+
|
|
2964
|
+
A mudan\xE7a em termos simples \u2014 o comportamento que o usu\xE1rio vai realmente ver.
|
|
2965
|
+
|
|
2966
|
+
## Crit\xE9rios de aceite
|
|
2967
|
+
|
|
2968
|
+
- [ ] Resultado observ\xE1vel 1
|
|
2969
|
+
- [ ] Resultado observ\xE1vel 2
|
|
2970
|
+
|
|
2971
|
+
## Fora de escopo
|
|
2972
|
+
|
|
2973
|
+
- Coisas que explicitamente N\xC3O faremos aqui.
|
|
2974
|
+
|
|
2975
|
+
## Notas / d\xFAvidas em aberto
|
|
2976
|
+
|
|
2977
|
+
- \u2026
|
|
2978
|
+
`
|
|
2979
|
+
};
|
|
2980
|
+
|
|
2981
|
+
// src/core/scaffold/init.ts
|
|
2982
|
+
async function scaffoldPoly(workspace, opts) {
|
|
2983
|
+
const templates = polyTemplates(opts.locale);
|
|
2984
|
+
const created = [];
|
|
2985
|
+
const skipped = [];
|
|
2986
|
+
for (const [rel, content] of Object.entries(templates)) {
|
|
2987
|
+
const display = `.poly/${rel}`;
|
|
2988
|
+
const abs = join3(workspace, ".poly", ...rel.split("/"));
|
|
2989
|
+
if (!opts.force && await exists(abs)) {
|
|
2990
|
+
skipped.push(display);
|
|
2991
|
+
continue;
|
|
2992
|
+
}
|
|
2993
|
+
await mkdir3(dirname3(abs), { recursive: true });
|
|
2994
|
+
await writeFile4(abs, content, "utf8");
|
|
2995
|
+
created.push(display);
|
|
2996
|
+
}
|
|
2997
|
+
return { created, skipped };
|
|
2998
|
+
}
|
|
2999
|
+
async function exists(path) {
|
|
3000
|
+
try {
|
|
3001
|
+
await access(path);
|
|
3002
|
+
return true;
|
|
3003
|
+
} catch {
|
|
3004
|
+
return false;
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
// src/cli/commands/init.ts
|
|
3009
|
+
async function init(opts) {
|
|
3010
|
+
const { created, skipped } = await scaffoldPoly(process.cwd(), {
|
|
3011
|
+
force: Boolean(opts.force),
|
|
3012
|
+
locale: getLocale()
|
|
3013
|
+
});
|
|
3014
|
+
if (created.length === 0) {
|
|
3015
|
+
console.log(pc8.yellow(t("init.allExist")));
|
|
3016
|
+
for (const f of skipped) console.log(pc8.dim(` ${f}`));
|
|
3017
|
+
console.log(pc8.dim(t("init.forceHint")));
|
|
3018
|
+
return;
|
|
3019
|
+
}
|
|
3020
|
+
console.log(pc8.green(t("init.created")));
|
|
3021
|
+
for (const f of created) console.log(pc8.dim(` ${f}`));
|
|
3022
|
+
if (skipped.length > 0) {
|
|
3023
|
+
console.log(pc8.dim(t("init.skipped")));
|
|
3024
|
+
for (const f of skipped) console.log(pc8.dim(` ${f}`));
|
|
3025
|
+
}
|
|
3026
|
+
console.log("\n" + t("init.tip"));
|
|
3027
|
+
}
|
|
3028
|
+
|
|
3029
|
+
// src/cli/commands/swarm.ts
|
|
3030
|
+
import pc9 from "picocolors";
|
|
3031
|
+
|
|
2684
3032
|
// src/core/git/worktree.ts
|
|
2685
3033
|
import { mkdtemp } from "fs/promises";
|
|
2686
3034
|
import { tmpdir } from "os";
|
|
2687
|
-
import { join as
|
|
3035
|
+
import { join as join4 } from "path";
|
|
2688
3036
|
import { simpleGit } from "simple-git";
|
|
2689
3037
|
async function ensureRepo(workspace) {
|
|
2690
3038
|
const git = simpleGit(workspace);
|
|
@@ -2705,7 +3053,7 @@ async function identityArgs(git) {
|
|
|
2705
3053
|
}
|
|
2706
3054
|
async function createWorktree(git, label) {
|
|
2707
3055
|
const branch = `polypus/${label}-${Date.now().toString(36)}`;
|
|
2708
|
-
const path = await mkdtemp(
|
|
3056
|
+
const path = await mkdtemp(join4(tmpdir(), "polypus-wt-"));
|
|
2709
3057
|
await git.raw(["worktree", "add", "-b", branch, path, "HEAD"]);
|
|
2710
3058
|
return { path, branch };
|
|
2711
3059
|
}
|
|
@@ -3017,8 +3365,15 @@ function pad(s, n) {
|
|
|
3017
3365
|
}
|
|
3018
3366
|
|
|
3019
3367
|
// src/cli/commands/swarm.ts
|
|
3368
|
+
var MIN_SWARM_AGENTS = 3;
|
|
3369
|
+
function canSwarm(agentCount) {
|
|
3370
|
+
return agentCount >= MIN_SWARM_AGENTS;
|
|
3371
|
+
}
|
|
3020
3372
|
async function swarm(task, opts) {
|
|
3021
3373
|
const config = await loadConfig();
|
|
3374
|
+
if (!canSwarm(config.agents.length)) {
|
|
3375
|
+
throw new Error(t("swarm.needsAgents", { min: MIN_SWARM_AGENTS, have: config.agents.length }));
|
|
3376
|
+
}
|
|
3022
3377
|
const selected = opts.agents ? opts.agents.split(",").map((s) => s.trim()).filter(Boolean) : config.agents.map((a) => a.name);
|
|
3023
3378
|
if (selected.length === 0) {
|
|
3024
3379
|
throw new Error(t("swarm.noAgents"));
|
|
@@ -3029,9 +3384,9 @@ async function swarm(task, opts) {
|
|
|
3029
3384
|
return createProvider(a);
|
|
3030
3385
|
});
|
|
3031
3386
|
console.log(
|
|
3032
|
-
|
|
3387
|
+
pc9.dim(t("swarm.status", { agents: resolved.map((a) => a.config.name).join(", "), workspace: process.cwd() }))
|
|
3033
3388
|
);
|
|
3034
|
-
console.log(
|
|
3389
|
+
console.log(pc9.yellow(t("swarm.bypassNote") + "\n"));
|
|
3035
3390
|
const view = new SwarmView(resolved[0].config.name);
|
|
3036
3391
|
view.start();
|
|
3037
3392
|
let result;
|
|
@@ -3058,25 +3413,25 @@ async function swarm(task, opts) {
|
|
|
3058
3413
|
view.stop();
|
|
3059
3414
|
}
|
|
3060
3415
|
console.log("");
|
|
3061
|
-
console.log(
|
|
3416
|
+
console.log(pc9.bold("\n" + t("swarm.summary")));
|
|
3062
3417
|
for (const o of result.outcomes) {
|
|
3063
|
-
const status = o.finished ?
|
|
3064
|
-
const committed = o.committed ? "" :
|
|
3065
|
-
console.log(` ${
|
|
3418
|
+
const status = o.finished ? pc9.green(t("swarm.statusDone")) : pc9.yellow(t("swarm.statusIncomplete"));
|
|
3419
|
+
const committed = o.committed ? "" : pc9.dim(` (${t("swarm.noChanges")})`);
|
|
3420
|
+
console.log(` ${pc9.bold(o.subtask.id)} [${o.agentName}] ${status}${committed} \u2014 ${o.subtask.title}`);
|
|
3066
3421
|
}
|
|
3067
3422
|
const conflicts = result.merges.filter((m) => !m.ok);
|
|
3068
3423
|
if (conflicts.length > 0) {
|
|
3069
|
-
console.log(
|
|
3424
|
+
console.log(pc9.red("\n" + t("swarm.conflictsHeader", { n: conflicts.length })));
|
|
3070
3425
|
for (const m of conflicts) {
|
|
3071
|
-
console.log(
|
|
3426
|
+
console.log(pc9.red(` ${m.branch}: ${m.conflicts.join(", ")}`));
|
|
3072
3427
|
}
|
|
3073
3428
|
} else {
|
|
3074
|
-
console.log(
|
|
3429
|
+
console.log(pc9.green("\n" + t("swarm.allMerged")));
|
|
3075
3430
|
}
|
|
3076
3431
|
}
|
|
3077
3432
|
|
|
3078
3433
|
// src/cli/commands/models.ts
|
|
3079
|
-
import
|
|
3434
|
+
import pc10 from "picocolors";
|
|
3080
3435
|
import * as p3 from "@clack/prompts";
|
|
3081
3436
|
async function models(opts) {
|
|
3082
3437
|
const apiKey = await resolveOpenRouterKey();
|
|
@@ -3085,9 +3440,9 @@ async function models(opts) {
|
|
|
3085
3440
|
let all;
|
|
3086
3441
|
try {
|
|
3087
3442
|
all = await listOpenRouterModels(apiKey);
|
|
3088
|
-
spin.stop(
|
|
3443
|
+
spin.stop(pc10.green("\u2713 OpenRouter"));
|
|
3089
3444
|
} catch (err) {
|
|
3090
|
-
spin.stop(
|
|
3445
|
+
spin.stop(pc10.red(t("models.fetchError", { msg: err.message })), 2);
|
|
3091
3446
|
return;
|
|
3092
3447
|
}
|
|
3093
3448
|
const filtered = filterModels(all, {
|
|
@@ -3101,26 +3456,26 @@ async function models(opts) {
|
|
|
3101
3456
|
printModelsTable(filtered, limit, all.length);
|
|
3102
3457
|
}
|
|
3103
3458
|
function printModelsTable(models2, limit, total) {
|
|
3104
|
-
console.log(
|
|
3459
|
+
console.log(pc10.dim(t("models.legend")));
|
|
3105
3460
|
if (models2.length === 0) {
|
|
3106
|
-
console.log(
|
|
3461
|
+
console.log(pc10.yellow(t("models.none")));
|
|
3107
3462
|
return;
|
|
3108
3463
|
}
|
|
3109
3464
|
const rows = models2.slice(0, limit);
|
|
3110
3465
|
console.log(
|
|
3111
|
-
" " +
|
|
3466
|
+
" " + pc10.dim(t("models.colTools").padEnd(6)) + pc10.dim(t("models.colPrice").padEnd(16)) + pc10.dim(t("models.colCtx").padEnd(9)) + pc10.dim(t("models.colModel"))
|
|
3112
3467
|
);
|
|
3113
3468
|
for (const m of rows) {
|
|
3114
3469
|
console.log(" " + modelRow(m));
|
|
3115
3470
|
}
|
|
3116
|
-
console.log(
|
|
3471
|
+
console.log(pc10.dim("\n" + t("models.shown", { shown: rows.length, total })));
|
|
3117
3472
|
}
|
|
3118
3473
|
function modelRow(m) {
|
|
3119
|
-
const tools = m.supportsTools ?
|
|
3474
|
+
const tools = m.supportsTools ? pc10.green("\u{1F6E0}".padEnd(5)) : pc10.dim("\u2014".padEnd(5));
|
|
3120
3475
|
const price = `${fmtPrice(m.promptPrice)}/${fmtPrice(m.completionPrice)}`;
|
|
3121
|
-
const priceColored = (m.free ?
|
|
3122
|
-
const ctx =
|
|
3123
|
-
return `${tools} ${priceColored}${ctx}${
|
|
3476
|
+
const priceColored = (m.free ? pc10.green : pc10.yellow)(price.padEnd(16));
|
|
3477
|
+
const ctx = pc10.cyan(fmtContext(m.contextLength).padEnd(9));
|
|
3478
|
+
return `${tools} ${priceColored}${ctx}${pc10.bold(m.id)}`;
|
|
3124
3479
|
}
|
|
3125
3480
|
async function resolveOpenRouterKey() {
|
|
3126
3481
|
if (process.env.OPENROUTER_API_KEY) return process.env.OPENROUTER_API_KEY;
|
|
@@ -3134,10 +3489,10 @@ async function resolveOpenRouterKey() {
|
|
|
3134
3489
|
}
|
|
3135
3490
|
|
|
3136
3491
|
// src/cli/commands/prd.ts
|
|
3137
|
-
import { writeFile as
|
|
3492
|
+
import { writeFile as writeFile5, readFile as readFile6 } from "fs/promises";
|
|
3138
3493
|
import { execFile } from "child_process";
|
|
3139
3494
|
import { promisify as promisify2 } from "util";
|
|
3140
|
-
import
|
|
3495
|
+
import pc11 from "picocolors";
|
|
3141
3496
|
|
|
3142
3497
|
// src/core/agent/prd.ts
|
|
3143
3498
|
var SYSTEM = [
|
|
@@ -3265,15 +3620,15 @@ async function prd(issueRef, opts) {
|
|
|
3265
3620
|
const guide = readProjectGuide(["context.md"]);
|
|
3266
3621
|
const markdown = await withRetry(() => generatePrd(issue, provider, guide));
|
|
3267
3622
|
if (opts.out) {
|
|
3268
|
-
await
|
|
3269
|
-
console.error(
|
|
3623
|
+
await writeFile5(opts.out, markdown + "\n", "utf8");
|
|
3624
|
+
console.error(pc11.green(t("prd.wrote", { path: opts.out })));
|
|
3270
3625
|
} else {
|
|
3271
3626
|
process.stdout.write(markdown + "\n");
|
|
3272
3627
|
}
|
|
3273
3628
|
}
|
|
3274
3629
|
async function loadIssue(issueRef, input) {
|
|
3275
3630
|
if (input) {
|
|
3276
|
-
const raw = input === "-" ? await readStdin() : await
|
|
3631
|
+
const raw = input === "-" ? await readStdin() : await readFile6(input, "utf8");
|
|
3277
3632
|
return normalize2(JSON.parse(stripBom(raw)));
|
|
3278
3633
|
}
|
|
3279
3634
|
const num = numericRef(issueRef);
|
|
@@ -3292,10 +3647,10 @@ function normalize2(raw) {
|
|
|
3292
3647
|
}
|
|
3293
3648
|
|
|
3294
3649
|
// src/cli/commands/review.ts
|
|
3295
|
-
import { writeFile as
|
|
3650
|
+
import { writeFile as writeFile6, readFile as readFile7 } from "fs/promises";
|
|
3296
3651
|
import { execFile as execFile2 } from "child_process";
|
|
3297
3652
|
import { promisify as promisify3 } from "util";
|
|
3298
|
-
import
|
|
3653
|
+
import pc12 from "picocolors";
|
|
3299
3654
|
|
|
3300
3655
|
// src/core/agent/review.ts
|
|
3301
3656
|
var MAX_DIFF_CHARS = Number(process.env.POLYPUS_MAX_DIFF_CHARS) || 6e4;
|
|
@@ -3361,14 +3716,14 @@ async function review(prRef, opts) {
|
|
|
3361
3716
|
const guide = readProjectGuide(["rules.md", "context.md"]);
|
|
3362
3717
|
const markdown = await withRetry(() => reviewDiff(diff, meta, provider, guide));
|
|
3363
3718
|
if (opts.out) {
|
|
3364
|
-
await
|
|
3365
|
-
console.error(
|
|
3719
|
+
await writeFile6(opts.out, markdown + "\n", "utf8");
|
|
3720
|
+
console.error(pc12.green(t("review.wrote", { path: opts.out })));
|
|
3366
3721
|
} else {
|
|
3367
3722
|
process.stdout.write(markdown + "\n");
|
|
3368
3723
|
}
|
|
3369
3724
|
}
|
|
3370
3725
|
async function loadDiff(num, input) {
|
|
3371
|
-
if (input) return input === "-" ? readStdin() :
|
|
3726
|
+
if (input) return input === "-" ? readStdin() : readFile7(input, "utf8");
|
|
3372
3727
|
const { stdout: stdout2 } = await exec3("gh", ["pr", "diff", num]);
|
|
3373
3728
|
return stdout2;
|
|
3374
3729
|
}
|
|
@@ -3380,7 +3735,7 @@ async function loadMeta(num, input) {
|
|
|
3380
3735
|
}
|
|
3381
3736
|
|
|
3382
3737
|
// src/cli/index.ts
|
|
3383
|
-
import { join as
|
|
3738
|
+
import { join as join5 } from "path";
|
|
3384
3739
|
|
|
3385
3740
|
// src/core/config/dotenv.ts
|
|
3386
3741
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
@@ -3409,12 +3764,11 @@ function loadDotenv(paths) {
|
|
|
3409
3764
|
}
|
|
3410
3765
|
|
|
3411
3766
|
// src/cli/index.ts
|
|
3412
|
-
var { version: pkgVersion } = createRequire(import.meta.url)("../package.json");
|
|
3413
3767
|
async function launchInteractive() {
|
|
3414
3768
|
const config = await loadConfig();
|
|
3415
3769
|
if (config.agents.length === 0) {
|
|
3416
3770
|
console.log(banner());
|
|
3417
|
-
console.log(" " +
|
|
3771
|
+
console.log(" " + pc13.yellow(t("welcome.firstRun")) + "\n");
|
|
3418
3772
|
await setup();
|
|
3419
3773
|
}
|
|
3420
3774
|
await run(void 0, {});
|
|
@@ -3435,8 +3789,9 @@ async function resolveLocale() {
|
|
|
3435
3789
|
}
|
|
3436
3790
|
function buildProgram() {
|
|
3437
3791
|
const program = new Command();
|
|
3438
|
-
program.name("polypus").description(t("cli.description")).version(
|
|
3792
|
+
program.name("polypus").description(t("cli.description")).version(VERSION).option("--lang <locale>", t("cli.opt.lang")).action(() => launchInteractive());
|
|
3439
3793
|
program.command("setup").description(t("cli.cmd.setup")).action(() => setup());
|
|
3794
|
+
program.command("init").option("--force", t("cli.opt.force")).description(t("cli.cmd.init")).action((opts) => init(opts));
|
|
3440
3795
|
program.command("add-agent").argument("<name>", t("cli.arg.addAgentName")).requiredOption("--provider <provider>", t("cli.opt.provider")).requiredOption("--model <model>", t("cli.opt.model")).option("--api-key <key>", t("cli.opt.apiKey")).option("--base-url <url>", t("cli.opt.baseUrl")).option("--tool-mode <mode>", t("cli.opt.toolMode"), "auto").option("--set-default", t("cli.opt.setDefault")).description(t("cli.cmd.addAgent")).action((name, opts) => addAgent(name, opts));
|
|
3441
3796
|
program.command("remove-agent").argument("<name>", t("cli.arg.removeAgentName")).description(t("cli.cmd.removeAgent")).action((name) => removeAgent(name));
|
|
3442
3797
|
program.command("list-agents").alias("agents").description(t("cli.cmd.listAgents")).action(() => listAgents());
|
|
@@ -3449,11 +3804,11 @@ function buildProgram() {
|
|
|
3449
3804
|
}
|
|
3450
3805
|
async function main() {
|
|
3451
3806
|
try {
|
|
3452
|
-
loadDotenv([
|
|
3807
|
+
loadDotenv([join5(configDir(), ".env"), join5(process.cwd(), ".env")]);
|
|
3453
3808
|
await resolveLocale();
|
|
3454
3809
|
await buildProgram().parseAsync(process.argv);
|
|
3455
3810
|
} catch (err) {
|
|
3456
|
-
console.error(
|
|
3811
|
+
console.error(pc13.red(`\u2717 ${err.message}`));
|
|
3457
3812
|
process.exitCode = 1;
|
|
3458
3813
|
}
|
|
3459
3814
|
}
|