wile 1.0.1 → 1.2.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 +14 -1
- package/dist/agent/.env.example +9 -11
- package/dist/agent/README.md +5 -6
- package/dist/agent/entrypoint.sh +1 -22
- package/dist/agent/scripts/mock-claude.sh +85 -10
- package/dist/agent/scripts/mock-codex.sh +85 -10
- package/dist/agent/scripts/mock-gemini.sh +85 -10
- package/dist/agent/scripts/prompt-compact.md +3 -2
- package/dist/agent/scripts/validate-prd.ts +111 -21
- package/dist/agent/scripts/wile-compact.sh +5 -16
- package/dist/agent/scripts/wile-preflight.sh +5 -16
- package/dist/agent/scripts/wile.sh +5 -16
- package/dist/cli.js +533 -116
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7451,6 +7451,166 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
7451
7451
|
import { existsSync } from "node:fs";
|
|
7452
7452
|
import { join, resolve, isAbsolute } from "node:path";
|
|
7453
7453
|
import { homedir } from "node:os";
|
|
7454
|
+
|
|
7455
|
+
// src/lib/env-schema.ts
|
|
7456
|
+
var ENV_VAR_DOC = [
|
|
7457
|
+
{
|
|
7458
|
+
key: "CODING_AGENT",
|
|
7459
|
+
section: "core",
|
|
7460
|
+
description: "Agent to run: CC (Claude), GC (Gemini), OC (OpenCode), CX (Codex).",
|
|
7461
|
+
requiredWhen: "Always.",
|
|
7462
|
+
defaultValue: "CC"
|
|
7463
|
+
},
|
|
7464
|
+
{
|
|
7465
|
+
key: "WILE_REPO_SOURCE",
|
|
7466
|
+
section: "core",
|
|
7467
|
+
description: "Where to get the repo from.",
|
|
7468
|
+
requiredWhen: "Always.",
|
|
7469
|
+
defaultValue: "github"
|
|
7470
|
+
},
|
|
7471
|
+
{
|
|
7472
|
+
key: "WILE_ENV_PROJECT_PATH",
|
|
7473
|
+
section: "core",
|
|
7474
|
+
description: "Path to project env file forwarded into Docker.",
|
|
7475
|
+
requiredWhen: "Optional.",
|
|
7476
|
+
defaultValue: ".wile/.env.project"
|
|
7477
|
+
},
|
|
7478
|
+
{
|
|
7479
|
+
key: "WILE_MAX_ITERATIONS",
|
|
7480
|
+
section: "core",
|
|
7481
|
+
description: "Default max loop iterations for `wile run`.",
|
|
7482
|
+
requiredWhen: "Optional.",
|
|
7483
|
+
defaultValue: "25"
|
|
7484
|
+
},
|
|
7485
|
+
{
|
|
7486
|
+
key: "GITHUB_TOKEN",
|
|
7487
|
+
section: "github",
|
|
7488
|
+
description: "GitHub token used for clone/push in GitHub repo mode.",
|
|
7489
|
+
requiredWhen: "Required when `WILE_REPO_SOURCE=github`.",
|
|
7490
|
+
secret: true
|
|
7491
|
+
},
|
|
7492
|
+
{
|
|
7493
|
+
key: "GITHUB_REPO_URL",
|
|
7494
|
+
section: "github",
|
|
7495
|
+
description: "Repository URL to clone in GitHub repo mode.",
|
|
7496
|
+
requiredWhen: "Required when `WILE_REPO_SOURCE=github`."
|
|
7497
|
+
},
|
|
7498
|
+
{
|
|
7499
|
+
key: "BRANCH_NAME",
|
|
7500
|
+
section: "github",
|
|
7501
|
+
description: "Branch Wile checks out and pushes to.",
|
|
7502
|
+
requiredWhen: "Required when `WILE_REPO_SOURCE=github`.",
|
|
7503
|
+
defaultValue: "main"
|
|
7504
|
+
},
|
|
7505
|
+
{
|
|
7506
|
+
key: "CC_CLAUDE_MODEL",
|
|
7507
|
+
section: "claude",
|
|
7508
|
+
description: "Claude model (`sonnet`, `opus`, `haiku`).",
|
|
7509
|
+
requiredWhen: "When `CODING_AGENT=CC`.",
|
|
7510
|
+
defaultValue: "opus"
|
|
7511
|
+
},
|
|
7512
|
+
{
|
|
7513
|
+
key: "CC_CLAUDE_CODE_OAUTH_TOKEN",
|
|
7514
|
+
section: "claude",
|
|
7515
|
+
description: "Claude Code OAuth token (subscription auth).",
|
|
7516
|
+
requiredWhen: "One of this or `CC_ANTHROPIC_API_KEY` when `CODING_AGENT=CC`.",
|
|
7517
|
+
secret: true
|
|
7518
|
+
},
|
|
7519
|
+
{
|
|
7520
|
+
key: "CC_ANTHROPIC_API_KEY",
|
|
7521
|
+
section: "claude",
|
|
7522
|
+
description: "Anthropic API key (pay-per-token auth).",
|
|
7523
|
+
requiredWhen: "One of this or `CC_CLAUDE_CODE_OAUTH_TOKEN` when `CODING_AGENT=CC`.",
|
|
7524
|
+
secret: true
|
|
7525
|
+
},
|
|
7526
|
+
{
|
|
7527
|
+
key: "OC_MODEL",
|
|
7528
|
+
section: "opencode",
|
|
7529
|
+
description: "OpenCode model id (native/free model).",
|
|
7530
|
+
requiredWhen: "Required when `CODING_AGENT=OC`.",
|
|
7531
|
+
defaultValue: "opencode/kimi-k2.5-free"
|
|
7532
|
+
},
|
|
7533
|
+
{
|
|
7534
|
+
key: "GEMINI_OAUTH_CREDS_B64",
|
|
7535
|
+
section: "gemini",
|
|
7536
|
+
description: "Base64-encoded `~/.gemini/oauth_creds.json`.",
|
|
7537
|
+
requiredWhen: "One of this or `GEMINI_API_KEY` when `CODING_AGENT=GC`.",
|
|
7538
|
+
secret: true
|
|
7539
|
+
},
|
|
7540
|
+
{
|
|
7541
|
+
key: "GEMINI_API_KEY",
|
|
7542
|
+
section: "gemini",
|
|
7543
|
+
description: "Gemini API key.",
|
|
7544
|
+
requiredWhen: "One of this or `GEMINI_OAUTH_CREDS_B64` when `CODING_AGENT=GC`.",
|
|
7545
|
+
secret: true
|
|
7546
|
+
},
|
|
7547
|
+
{
|
|
7548
|
+
key: "GEMINI_MODEL",
|
|
7549
|
+
section: "gemini",
|
|
7550
|
+
description: "Gemini model id.",
|
|
7551
|
+
requiredWhen: "When `CODING_AGENT=GC`.",
|
|
7552
|
+
defaultValue: "gemini-3-pro-preview"
|
|
7553
|
+
},
|
|
7554
|
+
{
|
|
7555
|
+
key: "CODEX_AUTH_JSON_B64",
|
|
7556
|
+
section: "codex",
|
|
7557
|
+
description: "Base64-encoded `~/.codex/auth.json` (subscription auth).",
|
|
7558
|
+
requiredWhen: "One of this, `CODEX_AUTH_JSON_PATH`, `CODEX_API_KEY`, or `OPENAI_API_KEY` when `CODING_AGENT=CX`.",
|
|
7559
|
+
secret: true
|
|
7560
|
+
},
|
|
7561
|
+
{
|
|
7562
|
+
key: "CODEX_AUTH_JSON_PATH",
|
|
7563
|
+
section: "codex",
|
|
7564
|
+
description: "Path to `~/.codex/auth.json` (used by `wile config`, then encoded).",
|
|
7565
|
+
requiredWhen: "Optional. Can be used instead of `CODEX_AUTH_JSON_B64` when generating config interactively."
|
|
7566
|
+
},
|
|
7567
|
+
{
|
|
7568
|
+
key: "CODEX_API_KEY",
|
|
7569
|
+
section: "codex",
|
|
7570
|
+
description: "OpenAI API key for Codex API-key auth.",
|
|
7571
|
+
requiredWhen: "One of this, `OPENAI_API_KEY`, `CODEX_AUTH_JSON_B64`, or `CODEX_AUTH_JSON_PATH` when `CODING_AGENT=CX`.",
|
|
7572
|
+
secret: true
|
|
7573
|
+
},
|
|
7574
|
+
{
|
|
7575
|
+
key: "OPENAI_API_KEY",
|
|
7576
|
+
section: "codex",
|
|
7577
|
+
description: "Alias fallback for `CODEX_API_KEY`.",
|
|
7578
|
+
requiredWhen: "Optional alias; counts as Codex API key when `CODING_AGENT=CX`.",
|
|
7579
|
+
secret: true
|
|
7580
|
+
},
|
|
7581
|
+
{
|
|
7582
|
+
key: "CODEX_MODEL",
|
|
7583
|
+
section: "codex",
|
|
7584
|
+
description: "Codex model override.",
|
|
7585
|
+
requiredWhen: "When `CODING_AGENT=CX`.",
|
|
7586
|
+
defaultValue: "gpt-5.3-codex"
|
|
7587
|
+
}
|
|
7588
|
+
];
|
|
7589
|
+
var ENV_FILE_KEY_ORDER = ENV_VAR_DOC.map((entry) => entry.key);
|
|
7590
|
+
var knownKeySet = new Set(ENV_FILE_KEY_ORDER);
|
|
7591
|
+
var isEnvFileKey = (key) => knownKeySet.has(key);
|
|
7592
|
+
var splitEnv = (raw) => {
|
|
7593
|
+
const known = {};
|
|
7594
|
+
const extra = {};
|
|
7595
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
7596
|
+
if (isEnvFileKey(key)) {
|
|
7597
|
+
known[key] = value;
|
|
7598
|
+
continue;
|
|
7599
|
+
}
|
|
7600
|
+
extra[key] = value;
|
|
7601
|
+
}
|
|
7602
|
+
return { known, extra };
|
|
7603
|
+
};
|
|
7604
|
+
var toEnvString = (known, extra = {}) => {
|
|
7605
|
+
const ordered = ENV_FILE_KEY_ORDER.filter((key) => known[key] !== undefined);
|
|
7606
|
+
const rest = Object.keys(extra).sort();
|
|
7607
|
+
const lines = [...ordered, ...rest].map((key) => `${key}=${known[key] ?? extra[key]}`);
|
|
7608
|
+
return lines.join(`
|
|
7609
|
+
`) + `
|
|
7610
|
+
`;
|
|
7611
|
+
};
|
|
7612
|
+
|
|
7613
|
+
// src/commands/config.ts
|
|
7454
7614
|
var prdExample = {
|
|
7455
7615
|
stories: [
|
|
7456
7616
|
{
|
|
@@ -7545,7 +7705,6 @@ var prdExample = {
|
|
|
7545
7705
|
var tips = {
|
|
7546
7706
|
oauth: "Tip: run 'claude setup-token' on your machine to generate an OAuth token (uses Pro/Max subscription).",
|
|
7547
7707
|
apiKey: "Tip: create an Anthropic API key in the console (uses API credits).",
|
|
7548
|
-
openrouter: "Tip: create an OpenRouter API key at https://openrouter.ai/keys (pay per token).",
|
|
7549
7708
|
github: "Tip: use a GitHub Personal Access Token (fine-grained recommended). Create at https://github.com/settings/tokens?type=beta with Contents (read/write) and Metadata (read).",
|
|
7550
7709
|
geminiOauth: "Tip: run 'gemini' locally and choose Login with Google to create ~/.gemini/oauth_creds.json.",
|
|
7551
7710
|
geminiApiKey: "Tip: create a Gemini API key at https://aistudio.google.com/app/apikey (pay per token).",
|
|
@@ -7553,11 +7712,246 @@ var tips = {
|
|
|
7553
7712
|
codexApiKey: "Tip: create an OpenAI API key at https://platform.openai.com/api-keys (pay per token)."
|
|
7554
7713
|
};
|
|
7555
7714
|
var nativeOcModels = [
|
|
7556
|
-
{ title: "
|
|
7557
|
-
{ title: "
|
|
7558
|
-
{ title: "
|
|
7559
|
-
{ title: "MiniMax M2.1", value: "opencode/minimax-m2.1-free" }
|
|
7715
|
+
{ title: "Kimi K2.5 Free (recommended)", value: "opencode/kimi-k2.5-free" },
|
|
7716
|
+
{ title: "MiniMax 2.5", value: "opencode/minimax-m2.5-free" },
|
|
7717
|
+
{ title: "Big Pickle", value: "opencode/big-pickle" }
|
|
7560
7718
|
];
|
|
7719
|
+
var nonInteractiveKeys = new Set([
|
|
7720
|
+
"codingAgent",
|
|
7721
|
+
"repoSource",
|
|
7722
|
+
"branchName",
|
|
7723
|
+
"envProjectPath",
|
|
7724
|
+
"maxIterations",
|
|
7725
|
+
"githubToken",
|
|
7726
|
+
"repoUrl",
|
|
7727
|
+
"ccAuthMethod",
|
|
7728
|
+
"ccAuthValue",
|
|
7729
|
+
"ccModel",
|
|
7730
|
+
"ocModel",
|
|
7731
|
+
"gcAuthMethod",
|
|
7732
|
+
"gcOauthPath",
|
|
7733
|
+
"gcApiKey",
|
|
7734
|
+
"gcModel",
|
|
7735
|
+
"cxAuthMethod",
|
|
7736
|
+
"cxAuthJsonPath",
|
|
7737
|
+
"cxApiKey",
|
|
7738
|
+
"cxModel"
|
|
7739
|
+
]);
|
|
7740
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7741
|
+
var asNonEmptyString = (value) => {
|
|
7742
|
+
if (typeof value !== "string") {
|
|
7743
|
+
return;
|
|
7744
|
+
}
|
|
7745
|
+
const trimmed = value.trim();
|
|
7746
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
7747
|
+
};
|
|
7748
|
+
var renderNonInteractiveConfigHelp = () => {
|
|
7749
|
+
const lines = [];
|
|
7750
|
+
lines.push("# Wile non-interactive config");
|
|
7751
|
+
lines.push("");
|
|
7752
|
+
lines.push("Use one flag for both docs and config application:");
|
|
7753
|
+
lines.push("- `bunx wile config --non-interactive` prints this help.");
|
|
7754
|
+
lines.push("- `bunx wile config --non-interactive '<json>'` validates and applies config.");
|
|
7755
|
+
lines.push("- Direct edits to `.wile/secrets/.env` are discouraged; prefer this command so validation runs before writing.");
|
|
7756
|
+
lines.push("");
|
|
7757
|
+
lines.push("The JSON payload is ideal to pass via `WILE_PROMPTS_INJECT`:");
|
|
7758
|
+
lines.push("```bash");
|
|
7759
|
+
lines.push(`export WILE_PROMPTS_INJECT='{"codingAgent":"OC","repoSource":"local","ocModel":"opencode/kimi-k2.5-free","branchName":"main","envProjectPath":".wile/.env.project","maxIterations":25}'`);
|
|
7760
|
+
lines.push('bunx wile config --non-interactive "$WILE_PROMPTS_INJECT"');
|
|
7761
|
+
lines.push("```");
|
|
7762
|
+
lines.push("");
|
|
7763
|
+
lines.push("Required fields:");
|
|
7764
|
+
lines.push("- Always: `codingAgent`, `repoSource`.");
|
|
7765
|
+
lines.push("- If `repoSource=github`: `githubToken`, `repoUrl`.");
|
|
7766
|
+
lines.push("- If `codingAgent=CC`: `ccAuthMethod`, `ccAuthValue`.");
|
|
7767
|
+
lines.push("- If `codingAgent=OC`: `ocModel`.");
|
|
7768
|
+
lines.push("- If `codingAgent=GC` and `gcAuthMethod=oauth`: `gcOauthPath`.");
|
|
7769
|
+
lines.push("- If `codingAgent=GC` and `gcAuthMethod=apiKey`: `gcApiKey`.");
|
|
7770
|
+
lines.push("- If `codingAgent=CX` and `cxAuthMethod=oauth`: `cxAuthJsonPath`.");
|
|
7771
|
+
lines.push("- If `codingAgent=CX` and `cxAuthMethod=apiKey`: `cxApiKey`.");
|
|
7772
|
+
lines.push("");
|
|
7773
|
+
lines.push("Optional fields:");
|
|
7774
|
+
lines.push("- `branchName` (default `main`)");
|
|
7775
|
+
lines.push("- `envProjectPath` (default `.wile/.env.project`)");
|
|
7776
|
+
lines.push("- `maxIterations` (default `25`)");
|
|
7777
|
+
lines.push("- `ccModel` (`sonnet` | `opus` | `haiku`, default `opus`)");
|
|
7778
|
+
lines.push("- `gcModel` (default `gemini-3-pro-preview`)");
|
|
7779
|
+
lines.push("- `cxModel` (default `gpt-5.3-codex`)");
|
|
7780
|
+
lines.push("");
|
|
7781
|
+
lines.push("Example (GitHub + Claude OAuth):");
|
|
7782
|
+
lines.push("```json");
|
|
7783
|
+
lines.push("{");
|
|
7784
|
+
lines.push(' "codingAgent": "CC",');
|
|
7785
|
+
lines.push(' "repoSource": "github",');
|
|
7786
|
+
lines.push(' "githubToken": "ghp_xxx",');
|
|
7787
|
+
lines.push(' "repoUrl": "https://github.com/owner/repo",');
|
|
7788
|
+
lines.push(' "branchName": "main",');
|
|
7789
|
+
lines.push(' "ccAuthMethod": "oauth",');
|
|
7790
|
+
lines.push(' "ccAuthValue": "claude_oauth_token",');
|
|
7791
|
+
lines.push(' "ccModel": "opus",');
|
|
7792
|
+
lines.push(' "maxIterations": 25');
|
|
7793
|
+
lines.push("}");
|
|
7794
|
+
lines.push("```");
|
|
7795
|
+
lines.push("");
|
|
7796
|
+
return lines.join(`
|
|
7797
|
+
`);
|
|
7798
|
+
};
|
|
7799
|
+
var parseNonInteractiveConfig = (raw) => {
|
|
7800
|
+
let parsed;
|
|
7801
|
+
try {
|
|
7802
|
+
parsed = JSON.parse(raw);
|
|
7803
|
+
} catch {
|
|
7804
|
+
return { errors: ["config is not valid JSON"] };
|
|
7805
|
+
}
|
|
7806
|
+
if (!isRecord(parsed)) {
|
|
7807
|
+
return { errors: ["config must be a JSON object"] };
|
|
7808
|
+
}
|
|
7809
|
+
const errors = [];
|
|
7810
|
+
for (const key of Object.keys(parsed)) {
|
|
7811
|
+
if (!nonInteractiveKeys.has(key)) {
|
|
7812
|
+
errors.push(`unknown key '${key}'`);
|
|
7813
|
+
}
|
|
7814
|
+
}
|
|
7815
|
+
const codingAgentRaw = parsed.codingAgent;
|
|
7816
|
+
const codingAgent = codingAgentRaw === "CC" || codingAgentRaw === "OC" || codingAgentRaw === "GC" || codingAgentRaw === "CX" ? codingAgentRaw : undefined;
|
|
7817
|
+
if (!codingAgent) {
|
|
7818
|
+
errors.push("codingAgent must be one of: CC, OC, GC, CX");
|
|
7819
|
+
}
|
|
7820
|
+
const repoSourceRaw = parsed.repoSource;
|
|
7821
|
+
const repoSource = repoSourceRaw === "github" || repoSourceRaw === "local" ? repoSourceRaw : undefined;
|
|
7822
|
+
if (!repoSource) {
|
|
7823
|
+
errors.push("repoSource must be one of: github, local");
|
|
7824
|
+
}
|
|
7825
|
+
const branchName = asNonEmptyString(parsed.branchName);
|
|
7826
|
+
const envProjectPath = asNonEmptyString(parsed.envProjectPath);
|
|
7827
|
+
const githubToken = asNonEmptyString(parsed.githubToken);
|
|
7828
|
+
const repoUrl = asNonEmptyString(parsed.repoUrl);
|
|
7829
|
+
const ccAuthValue = asNonEmptyString(parsed.ccAuthValue);
|
|
7830
|
+
const ocModel = asNonEmptyString(parsed.ocModel);
|
|
7831
|
+
const gcOauthPath = asNonEmptyString(parsed.gcOauthPath);
|
|
7832
|
+
const gcApiKey = asNonEmptyString(parsed.gcApiKey);
|
|
7833
|
+
const gcModel = asNonEmptyString(parsed.gcModel);
|
|
7834
|
+
const cxAuthJsonPath = asNonEmptyString(parsed.cxAuthJsonPath);
|
|
7835
|
+
const cxApiKey = asNonEmptyString(parsed.cxApiKey);
|
|
7836
|
+
const cxModel = asNonEmptyString(parsed.cxModel);
|
|
7837
|
+
if (repoSource === "github") {
|
|
7838
|
+
if (!githubToken) {
|
|
7839
|
+
errors.push("githubToken is required when repoSource=github");
|
|
7840
|
+
}
|
|
7841
|
+
if (!repoUrl) {
|
|
7842
|
+
errors.push("repoUrl is required when repoSource=github");
|
|
7843
|
+
}
|
|
7844
|
+
}
|
|
7845
|
+
const ccAuthMethodRaw = parsed.ccAuthMethod;
|
|
7846
|
+
const ccAuthMethod = ccAuthMethodRaw === "oauth" || ccAuthMethodRaw === "apiKey" ? ccAuthMethodRaw : undefined;
|
|
7847
|
+
const ccModelRaw = parsed.ccModel;
|
|
7848
|
+
const ccModel = ccModelRaw === "sonnet" || ccModelRaw === "opus" || ccModelRaw === "haiku" ? ccModelRaw : undefined;
|
|
7849
|
+
const gcAuthMethodRaw = parsed.gcAuthMethod;
|
|
7850
|
+
const gcAuthMethod = gcAuthMethodRaw === "oauth" || gcAuthMethodRaw === "apiKey" ? gcAuthMethodRaw : undefined;
|
|
7851
|
+
const cxAuthMethodRaw = parsed.cxAuthMethod;
|
|
7852
|
+
const cxAuthMethod = cxAuthMethodRaw === "oauth" || cxAuthMethodRaw === "apiKey" ? cxAuthMethodRaw : undefined;
|
|
7853
|
+
if (codingAgent === "CC") {
|
|
7854
|
+
if (!ccAuthMethod) {
|
|
7855
|
+
errors.push("ccAuthMethod is required when codingAgent=CC");
|
|
7856
|
+
}
|
|
7857
|
+
if (!ccAuthValue) {
|
|
7858
|
+
errors.push("ccAuthValue is required when codingAgent=CC");
|
|
7859
|
+
}
|
|
7860
|
+
if (parsed.ccModel !== undefined && !ccModel) {
|
|
7861
|
+
errors.push("ccModel must be one of: sonnet, opus, haiku");
|
|
7862
|
+
}
|
|
7863
|
+
}
|
|
7864
|
+
if (codingAgent === "OC" && !ocModel) {
|
|
7865
|
+
errors.push("ocModel is required when codingAgent=OC");
|
|
7866
|
+
}
|
|
7867
|
+
if (codingAgent === "GC") {
|
|
7868
|
+
if (!gcAuthMethod) {
|
|
7869
|
+
errors.push("gcAuthMethod is required when codingAgent=GC");
|
|
7870
|
+
} else if (gcAuthMethod === "oauth" && !gcOauthPath) {
|
|
7871
|
+
errors.push("gcOauthPath is required when codingAgent=GC and gcAuthMethod=oauth");
|
|
7872
|
+
} else if (gcAuthMethod === "apiKey" && !gcApiKey) {
|
|
7873
|
+
errors.push("gcApiKey is required when codingAgent=GC and gcAuthMethod=apiKey");
|
|
7874
|
+
}
|
|
7875
|
+
}
|
|
7876
|
+
if (codingAgent === "CX") {
|
|
7877
|
+
if (!cxAuthMethod) {
|
|
7878
|
+
errors.push("cxAuthMethod is required when codingAgent=CX");
|
|
7879
|
+
} else if (cxAuthMethod === "oauth" && !cxAuthJsonPath) {
|
|
7880
|
+
errors.push("cxAuthJsonPath is required when codingAgent=CX and cxAuthMethod=oauth");
|
|
7881
|
+
} else if (cxAuthMethod === "apiKey" && !cxApiKey) {
|
|
7882
|
+
errors.push("cxApiKey is required when codingAgent=CX and cxAuthMethod=apiKey");
|
|
7883
|
+
}
|
|
7884
|
+
}
|
|
7885
|
+
const maxIterationsRaw = parsed.maxIterations;
|
|
7886
|
+
let maxIterations;
|
|
7887
|
+
if (maxIterationsRaw !== undefined) {
|
|
7888
|
+
if (typeof maxIterationsRaw !== "number" || !Number.isFinite(maxIterationsRaw)) {
|
|
7889
|
+
errors.push("maxIterations must be a number");
|
|
7890
|
+
} else if (maxIterationsRaw <= 0) {
|
|
7891
|
+
errors.push("maxIterations must be greater than 0");
|
|
7892
|
+
} else {
|
|
7893
|
+
maxIterations = Math.floor(maxIterationsRaw);
|
|
7894
|
+
}
|
|
7895
|
+
}
|
|
7896
|
+
if (errors.length > 0 || !codingAgent || !repoSource) {
|
|
7897
|
+
return { errors };
|
|
7898
|
+
}
|
|
7899
|
+
return {
|
|
7900
|
+
config: {
|
|
7901
|
+
codingAgent,
|
|
7902
|
+
repoSource,
|
|
7903
|
+
branchName,
|
|
7904
|
+
envProjectPath,
|
|
7905
|
+
maxIterations,
|
|
7906
|
+
githubToken,
|
|
7907
|
+
repoUrl,
|
|
7908
|
+
ccAuthMethod,
|
|
7909
|
+
ccAuthValue,
|
|
7910
|
+
ccModel,
|
|
7911
|
+
ocModel,
|
|
7912
|
+
gcAuthMethod,
|
|
7913
|
+
gcOauthPath,
|
|
7914
|
+
gcApiKey,
|
|
7915
|
+
gcModel,
|
|
7916
|
+
cxAuthMethod,
|
|
7917
|
+
cxAuthJsonPath,
|
|
7918
|
+
cxApiKey,
|
|
7919
|
+
cxModel
|
|
7920
|
+
},
|
|
7921
|
+
errors: []
|
|
7922
|
+
};
|
|
7923
|
+
};
|
|
7924
|
+
var toPromptInjectValues = (config) => {
|
|
7925
|
+
const values = [config.codingAgent];
|
|
7926
|
+
if (config.codingAgent === "CC") {
|
|
7927
|
+
values.push(config.ccAuthMethod, config.ccAuthValue, config.ccModel ?? "opus");
|
|
7928
|
+
} else if (config.codingAgent === "OC") {
|
|
7929
|
+
values.push(config.ocModel ?? "opencode/kimi-k2.5-free");
|
|
7930
|
+
} else if (config.codingAgent === "GC") {
|
|
7931
|
+
values.push(config.gcAuthMethod);
|
|
7932
|
+
if (config.gcAuthMethod === "oauth") {
|
|
7933
|
+
values.push(config.gcOauthPath);
|
|
7934
|
+
} else {
|
|
7935
|
+
values.push(config.gcApiKey);
|
|
7936
|
+
}
|
|
7937
|
+
} else {
|
|
7938
|
+
values.push(config.cxAuthMethod);
|
|
7939
|
+
if (config.cxAuthMethod === "oauth") {
|
|
7940
|
+
values.push(config.cxAuthJsonPath);
|
|
7941
|
+
} else {
|
|
7942
|
+
values.push(config.cxApiKey);
|
|
7943
|
+
}
|
|
7944
|
+
values.push(config.cxModel ?? "gpt-5.3-codex");
|
|
7945
|
+
}
|
|
7946
|
+
values.push(config.repoSource);
|
|
7947
|
+
if (config.repoSource === "github") {
|
|
7948
|
+
values.push(config.githubToken, config.repoUrl);
|
|
7949
|
+
}
|
|
7950
|
+
values.push(config.branchName ?? "main");
|
|
7951
|
+
values.push(config.envProjectPath ?? ".wile/.env.project");
|
|
7952
|
+
values.push(config.maxIterations ?? 25);
|
|
7953
|
+
return values;
|
|
7954
|
+
};
|
|
7561
7955
|
var readEnvFile = async (path) => {
|
|
7562
7956
|
if (!existsSync(path)) {
|
|
7563
7957
|
return {};
|
|
@@ -7628,36 +8022,6 @@ var setIfDefined = (env, key, value) => {
|
|
|
7628
8022
|
}
|
|
7629
8023
|
env[key] = value;
|
|
7630
8024
|
};
|
|
7631
|
-
var toEnvString = (env) => {
|
|
7632
|
-
const orderedKeys = [
|
|
7633
|
-
"CODING_AGENT",
|
|
7634
|
-
"WILE_REPO_SOURCE",
|
|
7635
|
-
"WILE_ENV_PROJECT_PATH",
|
|
7636
|
-
"GITHUB_TOKEN",
|
|
7637
|
-
"GITHUB_REPO_URL",
|
|
7638
|
-
"BRANCH_NAME",
|
|
7639
|
-
"WILE_MAX_ITERATIONS",
|
|
7640
|
-
"CC_CLAUDE_MODEL",
|
|
7641
|
-
"CC_CLAUDE_CODE_OAUTH_TOKEN",
|
|
7642
|
-
"CC_ANTHROPIC_API_KEY",
|
|
7643
|
-
"OC_PROVIDER",
|
|
7644
|
-
"OC_MODEL",
|
|
7645
|
-
"OC_OPENROUTER_API_KEY",
|
|
7646
|
-
"GEMINI_OAUTH_CREDS_B64",
|
|
7647
|
-
"GEMINI_API_KEY",
|
|
7648
|
-
"CODEX_AUTH_JSON_B64",
|
|
7649
|
-
"CODEX_AUTH_JSON_PATH",
|
|
7650
|
-
"CODEX_API_KEY",
|
|
7651
|
-
"CODEX_MODEL"
|
|
7652
|
-
];
|
|
7653
|
-
const orderedSet = new Set(orderedKeys);
|
|
7654
|
-
const ordered = orderedKeys.filter((key) => (key in env));
|
|
7655
|
-
const rest = Object.keys(env).filter((key) => !orderedSet.has(key)).sort();
|
|
7656
|
-
const lines = [...ordered, ...rest].map((key) => `${key}=${env[key]}`);
|
|
7657
|
-
return lines.join(`
|
|
7658
|
-
`) + `
|
|
7659
|
-
`;
|
|
7660
|
-
};
|
|
7661
8025
|
var resolvePath = (cwd, input) => {
|
|
7662
8026
|
const expanded = input.startsWith("~") ? join(homedir(), input.slice(1)) : input;
|
|
7663
8027
|
return isAbsolute(expanded) ? expanded : resolve(cwd, expanded);
|
|
@@ -7666,7 +8030,28 @@ var readBase64File = async (path) => {
|
|
|
7666
8030
|
const contents = await readFile(path);
|
|
7667
8031
|
return Buffer.from(contents).toString("base64");
|
|
7668
8032
|
};
|
|
7669
|
-
var runConfig = async () => {
|
|
8033
|
+
var runConfig = async (options = {}) => {
|
|
8034
|
+
const nonInteractiveInput = options.nonInteractive;
|
|
8035
|
+
if (nonInteractiveInput === true) {
|
|
8036
|
+
process.stdout.write(renderNonInteractiveConfigHelp());
|
|
8037
|
+
return true;
|
|
8038
|
+
}
|
|
8039
|
+
let nonInteractiveConfig;
|
|
8040
|
+
if (typeof nonInteractiveInput === "string") {
|
|
8041
|
+
const parsed = parseNonInteractiveConfig(nonInteractiveInput);
|
|
8042
|
+
if (!parsed.config) {
|
|
8043
|
+
process.stdout.write(renderNonInteractiveConfigHelp());
|
|
8044
|
+
process.stdout.write(`
|
|
8045
|
+
`);
|
|
8046
|
+
console.error("Error: invalid non-interactive config.");
|
|
8047
|
+
for (const error of parsed.errors) {
|
|
8048
|
+
console.error(`- ${error}`);
|
|
8049
|
+
}
|
|
8050
|
+
return false;
|
|
8051
|
+
}
|
|
8052
|
+
nonInteractiveConfig = parsed.config;
|
|
8053
|
+
import_prompts.default.inject(toPromptInjectValues(parsed.config));
|
|
8054
|
+
}
|
|
7670
8055
|
const cwd = process.cwd();
|
|
7671
8056
|
const wileDir = join(cwd, ".wile");
|
|
7672
8057
|
const secretsDir = join(wileDir, "secrets");
|
|
@@ -7679,8 +8064,10 @@ var runConfig = async () => {
|
|
|
7679
8064
|
const preflightPath = join(wileDir, "preflight.md");
|
|
7680
8065
|
const agentsPath = join(wileDir, "AGENTS.md");
|
|
7681
8066
|
await mkdir(secretsDir, { recursive: true });
|
|
7682
|
-
|
|
7683
|
-
|
|
8067
|
+
if (!nonInteractiveConfig) {
|
|
8068
|
+
maybeInject();
|
|
8069
|
+
}
|
|
8070
|
+
const { known: existingEnv, extra: extraEnv } = splitEnv(await readEnvFile(envPath));
|
|
7684
8071
|
const codingAgentResponse = await prompt({
|
|
7685
8072
|
type: "select",
|
|
7686
8073
|
name: "codingAgent",
|
|
@@ -7699,10 +8086,7 @@ var runConfig = async () => {
|
|
|
7699
8086
|
let codexAuthMethod = null;
|
|
7700
8087
|
let authValueResponse = {};
|
|
7701
8088
|
let defaultModelResponse = {};
|
|
7702
|
-
let ocProviderResponse = {};
|
|
7703
|
-
let ocKeyResponse = {};
|
|
7704
8089
|
let ocModelResponse = {};
|
|
7705
|
-
let ocNativeModelResponse = {};
|
|
7706
8090
|
let geminiOauthPathResponse = {};
|
|
7707
8091
|
let geminiApiKeyResponse = {};
|
|
7708
8092
|
let codexAuthJsonPathResponse = {};
|
|
@@ -7742,45 +8126,14 @@ var runConfig = async () => {
|
|
|
7742
8126
|
initial: existingEnv.CC_CLAUDE_MODEL === "sonnet" ? 0 : existingEnv.CC_CLAUDE_MODEL === "haiku" ? 2 : 1
|
|
7743
8127
|
});
|
|
7744
8128
|
} else if (codingAgent === "OC") {
|
|
7745
|
-
const
|
|
7746
|
-
|
|
8129
|
+
const existingNativeIdx = nativeOcModels.findIndex((m) => m.value === existingEnv.OC_MODEL);
|
|
8130
|
+
ocModelResponse = await prompt({
|
|
7747
8131
|
type: "select",
|
|
7748
|
-
name: "
|
|
7749
|
-
message: "OpenCode
|
|
7750
|
-
choices:
|
|
7751
|
-
|
|
7752
|
-
{ title: "OpenRouter (pay per token)", value: "openrouter" }
|
|
7753
|
-
],
|
|
7754
|
-
initial: providerDefault === "openrouter" ? 1 : 0
|
|
8132
|
+
name: "ocModel",
|
|
8133
|
+
message: "OpenCode model (free)",
|
|
8134
|
+
choices: nativeOcModels,
|
|
8135
|
+
initial: existingNativeIdx >= 0 ? existingNativeIdx : 0
|
|
7755
8136
|
});
|
|
7756
|
-
const ocProvider2 = ocProviderResponse.ocProvider;
|
|
7757
|
-
if (ocProvider2 === "openrouter") {
|
|
7758
|
-
console.log("");
|
|
7759
|
-
console.log(tips.openrouter);
|
|
7760
|
-
console.log("");
|
|
7761
|
-
ocKeyResponse = await prompt({
|
|
7762
|
-
type: "password",
|
|
7763
|
-
name: "ocKey",
|
|
7764
|
-
message: "OpenRouter API key (press enter to keep existing)",
|
|
7765
|
-
initial: existingEnv.OC_OPENROUTER_API_KEY ?? ""
|
|
7766
|
-
});
|
|
7767
|
-
ocModelResponse = await prompt({
|
|
7768
|
-
type: "select",
|
|
7769
|
-
name: "ocModel",
|
|
7770
|
-
message: "OpenCode model (OpenRouter)",
|
|
7771
|
-
choices: [{ title: "glm-4.7", value: "glm-4.7" }],
|
|
7772
|
-
initial: 0
|
|
7773
|
-
});
|
|
7774
|
-
} else {
|
|
7775
|
-
const existingNativeIdx = nativeOcModels.findIndex((m) => m.value === existingEnv.OC_MODEL);
|
|
7776
|
-
ocNativeModelResponse = await prompt({
|
|
7777
|
-
type: "select",
|
|
7778
|
-
name: "ocNativeModel",
|
|
7779
|
-
message: "OpenCode model (free)",
|
|
7780
|
-
choices: nativeOcModels,
|
|
7781
|
-
initial: existingNativeIdx >= 0 ? existingNativeIdx : 0
|
|
7782
|
-
});
|
|
7783
|
-
}
|
|
7784
8137
|
} else if (codingAgent === "GC") {
|
|
7785
8138
|
const geminiAuthDefault = existingEnv.GEMINI_OAUTH_CREDS_B64 ? "oauth" : existingEnv.GEMINI_API_KEY ? "apiKey" : "oauth";
|
|
7786
8139
|
const geminiAuthResponse = await prompt({
|
|
@@ -7849,7 +8202,7 @@ var runConfig = async () => {
|
|
|
7849
8202
|
type: "text",
|
|
7850
8203
|
name: "codexModel",
|
|
7851
8204
|
message: "Codex model (press enter to keep existing)",
|
|
7852
|
-
initial: existingEnv.CODEX_MODEL ?? ""
|
|
8205
|
+
initial: existingEnv.CODEX_MODEL ?? "gpt-5.3-codex"
|
|
7853
8206
|
});
|
|
7854
8207
|
}
|
|
7855
8208
|
const repoSourceResponse = await prompt({
|
|
@@ -7902,10 +8255,9 @@ var runConfig = async () => {
|
|
|
7902
8255
|
const maxIterations = Number.isFinite(iterationsResponse.maxIterations) && iterationsResponse.maxIterations > 0 ? iterationsResponse.maxIterations : fallbackIterations;
|
|
7903
8256
|
const authFallback = authMethod === "oauth" ? existingEnv.CC_CLAUDE_CODE_OAUTH_TOKEN : existingEnv.CC_ANTHROPIC_API_KEY;
|
|
7904
8257
|
const authValue = codingAgent === "CC" ? coalesceValue(authValueResponse.authValue, authFallback) : undefined;
|
|
7905
|
-
const
|
|
7906
|
-
const ocKey = codingAgent === "OC" && ocProvider === "openrouter" ? coalesceValue(ocKeyResponse.ocKey, existingEnv.OC_OPENROUTER_API_KEY) : undefined;
|
|
7907
|
-
const ocModel = codingAgent === "OC" ? ocProvider === "openrouter" ? coalesceValue(ocModelResponse.ocModel, existingEnv.OC_MODEL ?? "glm-4.7") : coalesceValue(ocNativeModelResponse.ocNativeModel, existingEnv.OC_MODEL ?? "opencode/grok-code") : undefined;
|
|
8258
|
+
const ocModel = codingAgent === "OC" ? coalesceValue(ocModelResponse.ocModel, existingEnv.OC_MODEL ?? "opencode/kimi-k2.5-free") : undefined;
|
|
7908
8259
|
const geminiApiKey = codingAgent === "GC" ? coalesceValue(geminiApiKeyResponse.geminiApiKey, existingEnv.GEMINI_API_KEY) : undefined;
|
|
8260
|
+
const geminiModel = codingAgent === "GC" ? coalesceValue(nonInteractiveConfig?.gcModel, existingEnv.GEMINI_MODEL ?? "gemini-3-pro-preview") : undefined;
|
|
7909
8261
|
let geminiOauthCredsB64;
|
|
7910
8262
|
if (codingAgent === "GC" && geminiAuthMethod === "oauth") {
|
|
7911
8263
|
const configuredPath = coalesceValue(geminiOauthPathResponse.geminiOauthPath);
|
|
@@ -7935,7 +8287,7 @@ var runConfig = async () => {
|
|
|
7935
8287
|
codexAuthJsonB64 = existingEnv.CODEX_AUTH_JSON_B64;
|
|
7936
8288
|
}
|
|
7937
8289
|
}
|
|
7938
|
-
const codexModel = codingAgent === "CX" ? coalesceValue(codexModelResponse.codexModel, existingEnv.CODEX_MODEL) : undefined;
|
|
8290
|
+
const codexModel = codingAgent === "CX" ? coalesceValue(codexModelResponse.codexModel, existingEnv.CODEX_MODEL ?? "gpt-5.3-codex") : undefined;
|
|
7939
8291
|
const githubToken = repoSource === "github" ? coalesceValue(githubTokenResponse.githubToken, existingEnv.GITHUB_TOKEN) : existingEnv.GITHUB_TOKEN;
|
|
7940
8292
|
const repoUrl = repoSource === "github" ? coalesceValue(repoResponse.repoUrl, existingEnv.GITHUB_REPO_URL) : existingEnv.GITHUB_REPO_URL;
|
|
7941
8293
|
const envProjectPathValue = coalesceValue(envProjectPathResponse.envProjectPath, existingEnv.WILE_ENV_PROJECT_PATH ?? ".wile/.env.project") ?? ".wile/.env.project";
|
|
@@ -7956,17 +8308,14 @@ var runConfig = async () => {
|
|
|
7956
8308
|
setIfDefined(envOut, "CC_ANTHROPIC_API_KEY", authValue);
|
|
7957
8309
|
}
|
|
7958
8310
|
} else if (codingAgent === "OC") {
|
|
7959
|
-
setIfDefined(envOut, "
|
|
7960
|
-
setIfDefined(envOut, "OC_MODEL", ocModel ?? "opencode/grok-code");
|
|
7961
|
-
if (ocProvider === "openrouter") {
|
|
7962
|
-
setIfDefined(envOut, "OC_OPENROUTER_API_KEY", ocKey);
|
|
7963
|
-
}
|
|
8311
|
+
setIfDefined(envOut, "OC_MODEL", ocModel ?? "opencode/kimi-k2.5-free");
|
|
7964
8312
|
} else if (codingAgent === "GC") {
|
|
7965
8313
|
if (geminiAuthMethod === "apiKey") {
|
|
7966
8314
|
setIfDefined(envOut, "GEMINI_API_KEY", geminiApiKey);
|
|
7967
8315
|
} else {
|
|
7968
8316
|
setIfDefined(envOut, "GEMINI_OAUTH_CREDS_B64", geminiOauthCredsB64);
|
|
7969
8317
|
}
|
|
8318
|
+
setIfDefined(envOut, "GEMINI_MODEL", geminiModel);
|
|
7970
8319
|
} else if (codingAgent === "CX") {
|
|
7971
8320
|
if (codexAuthMethod === "apiKey") {
|
|
7972
8321
|
setIfDefined(envOut, "CODEX_API_KEY", codexApiKey);
|
|
@@ -7975,7 +8324,7 @@ var runConfig = async () => {
|
|
|
7975
8324
|
}
|
|
7976
8325
|
setIfDefined(envOut, "CODEX_MODEL", codexModel);
|
|
7977
8326
|
}
|
|
7978
|
-
await writeFile(envPath, toEnvString(envOut));
|
|
8327
|
+
await writeFile(envPath, toEnvString(envOut, extraEnv));
|
|
7979
8328
|
await ensureGitignore(gitignorePath);
|
|
7980
8329
|
const envProjectTarget = envProjectPathValue === ".wile/.env.project" ? envProjectPath : envProjectPathValue;
|
|
7981
8330
|
const envProjectResolved = envProjectTarget.startsWith("/") ? envProjectTarget : join(cwd, envProjectTarget);
|
|
@@ -8019,13 +8368,13 @@ Use bullet points for preflight checks, e.g.
|
|
|
8019
8368
|
"- If verification is a command, state the expected result of that command.",
|
|
8020
8369
|
"- Use one behavior per bullet.",
|
|
8021
8370
|
"- Keep IDs stable, unique, and numeric (e.g., 1, 2, 3).",
|
|
8022
|
-
"- Never reuse IDs listed in any story's `compactedFrom
|
|
8371
|
+
"- Never reuse IDs listed in any story's `compactedFrom` ranges.",
|
|
8023
8372
|
'- Avoid vague terms like "should" or "nice".',
|
|
8024
8373
|
"- Keep stories small enough to finish in one iteration.",
|
|
8025
8374
|
'- Use `status: "pending"` for work not done yet and `status: "done"` only after all acceptance criteria are verified.',
|
|
8026
8375
|
"- Use `dependsOn` to model prerequisites by story ID.",
|
|
8027
8376
|
"- `dependsOn` must reference active story IDs only; never reference compacted IDs.",
|
|
8028
|
-
|
|
8377
|
+
'- When compacting completed stories, add a canonical range string like `compactedFrom: "1..3,5"` to the summary done story.',
|
|
8029
8378
|
"- Prefer concrete files/commands only when they reflect the real outcome.",
|
|
8030
8379
|
"",
|
|
8031
8380
|
"Environment notes:",
|
|
@@ -8046,6 +8395,7 @@ Wile config complete.`);
|
|
|
8046
8395
|
if (!hadPreflight) {
|
|
8047
8396
|
console.log("Created .wile/preflight.md for preflight checks (optional).");
|
|
8048
8397
|
}
|
|
8398
|
+
return true;
|
|
8049
8399
|
};
|
|
8050
8400
|
|
|
8051
8401
|
// src/commands/compact.ts
|
|
@@ -8100,7 +8450,7 @@ var readWileConfig = (options = {}) => {
|
|
|
8100
8450
|
if (validate) {
|
|
8101
8451
|
ensureRequired(existsSync2(paths.envPath), "Missing .wile/secrets/.env. Run 'bunx wile config' first.");
|
|
8102
8452
|
}
|
|
8103
|
-
const env = parseEnvFile(paths.envPath);
|
|
8453
|
+
const { known: env } = splitEnv(parseEnvFile(paths.envPath));
|
|
8104
8454
|
const envProjectPath = resolveEnvProjectPath(options.cwd ?? process.cwd(), env.WILE_ENV_PROJECT_PATH);
|
|
8105
8455
|
const envProject = parseEnvFile(envProjectPath);
|
|
8106
8456
|
const repoSource = env.WILE_REPO_SOURCE || "github";
|
|
@@ -8115,10 +8465,6 @@ var readWileConfig = (options = {}) => {
|
|
|
8115
8465
|
ensureRequired(Boolean(env.CC_CLAUDE_CODE_OAUTH_TOKEN || env.CC_ANTHROPIC_API_KEY), "Either CC_CLAUDE_CODE_OAUTH_TOKEN or CC_ANTHROPIC_API_KEY is required in .wile/secrets/.env.");
|
|
8116
8466
|
}
|
|
8117
8467
|
if (env.CODING_AGENT === "OC") {
|
|
8118
|
-
const ocProvider = env.OC_PROVIDER || "native";
|
|
8119
|
-
if (ocProvider === "openrouter") {
|
|
8120
|
-
ensureRequired(Boolean(env.OC_OPENROUTER_API_KEY), "OC_OPENROUTER_API_KEY is required in .wile/secrets/.env for OpenCode with OpenRouter provider.");
|
|
8121
|
-
}
|
|
8122
8468
|
ensureRequired(Boolean(env.OC_MODEL), "OC_MODEL is required in .wile/secrets/.env for OpenCode.");
|
|
8123
8469
|
}
|
|
8124
8470
|
if (env.CODING_AGENT === "GC") {
|
|
@@ -8143,11 +8489,10 @@ var readWileConfig = (options = {}) => {
|
|
|
8143
8489
|
maxIterations: env.WILE_MAX_ITERATIONS,
|
|
8144
8490
|
ccClaudeCodeOauthToken: env.CC_CLAUDE_CODE_OAUTH_TOKEN,
|
|
8145
8491
|
ccAnthropicApiKey: env.CC_ANTHROPIC_API_KEY,
|
|
8146
|
-
ocProvider: env.OC_PROVIDER ?? "native",
|
|
8147
|
-
ocOpenrouterApiKey: env.OC_OPENROUTER_API_KEY,
|
|
8148
8492
|
ocModel: env.OC_MODEL,
|
|
8149
8493
|
geminiOauthCredsB64: env.GEMINI_OAUTH_CREDS_B64,
|
|
8150
8494
|
geminiApiKey: env.GEMINI_API_KEY,
|
|
8495
|
+
geminiModel: env.GEMINI_MODEL,
|
|
8151
8496
|
codexAuthJsonB64: env.CODEX_AUTH_JSON_B64,
|
|
8152
8497
|
codexAuthJsonPath: env.CODEX_AUTH_JSON_PATH,
|
|
8153
8498
|
codexApiKey: env.CODEX_API_KEY ?? env.OPENAI_API_KEY,
|
|
@@ -8195,6 +8540,53 @@ var toIntegerArray = (value, message) => {
|
|
|
8195
8540
|
}
|
|
8196
8541
|
return value;
|
|
8197
8542
|
};
|
|
8543
|
+
var compactedFromFormatError = 'must use canonical range syntax like "1..3,5" (sorted, non-overlapping).';
|
|
8544
|
+
var parseCompactedFrom = (value, label) => {
|
|
8545
|
+
const raw = toNonEmptyString(value, `${label}.compactedFrom ${compactedFromFormatError}`).trim();
|
|
8546
|
+
const tokens = raw.split(",").map((token) => token.trim());
|
|
8547
|
+
if (tokens.length === 0 || tokens.some((token) => token.length === 0)) {
|
|
8548
|
+
throw new Error(`${label}.compactedFrom ${compactedFromFormatError}`);
|
|
8549
|
+
}
|
|
8550
|
+
const parsed = [];
|
|
8551
|
+
for (const token of tokens) {
|
|
8552
|
+
const match = token.match(/^(-?\d+)(?:\.\.(-?\d+))?$/);
|
|
8553
|
+
if (!match) {
|
|
8554
|
+
throw new Error(`${label}.compactedFrom ${compactedFromFormatError}`);
|
|
8555
|
+
}
|
|
8556
|
+
const start = Number(match[1]);
|
|
8557
|
+
const end = match[2] === undefined ? start : Number(match[2]);
|
|
8558
|
+
if (!Number.isInteger(start) || !Number.isInteger(end) || start > end) {
|
|
8559
|
+
throw new Error(`${label}.compactedFrom ${compactedFromFormatError}`);
|
|
8560
|
+
}
|
|
8561
|
+
parsed.push({ start, end });
|
|
8562
|
+
}
|
|
8563
|
+
parsed.sort((a, b) => a.start === b.start ? a.end - b.end : a.start - b.start);
|
|
8564
|
+
for (let i = 1;i < parsed.length; i += 1) {
|
|
8565
|
+
if (parsed[i].start <= parsed[i - 1].end) {
|
|
8566
|
+
throw new Error(`${label}.compactedFrom ${compactedFromFormatError}`);
|
|
8567
|
+
}
|
|
8568
|
+
}
|
|
8569
|
+
const merged = [];
|
|
8570
|
+
for (const range of parsed) {
|
|
8571
|
+
const last = merged[merged.length - 1];
|
|
8572
|
+
if (!last || range.start > last.end + 1) {
|
|
8573
|
+
merged.push({ ...range });
|
|
8574
|
+
continue;
|
|
8575
|
+
}
|
|
8576
|
+
if (range.end > last.end) {
|
|
8577
|
+
last.end = range.end;
|
|
8578
|
+
}
|
|
8579
|
+
}
|
|
8580
|
+
const canonical = merged.map((range) => range.start === range.end ? `${range.start}` : `${range.start}..${range.end}`).join(",");
|
|
8581
|
+
const normalizedInput = tokens.join(",");
|
|
8582
|
+
if (normalizedInput !== canonical) {
|
|
8583
|
+
throw new Error(`${label}.compactedFrom ${compactedFromFormatError}`);
|
|
8584
|
+
}
|
|
8585
|
+
return {
|
|
8586
|
+
value: canonical,
|
|
8587
|
+
ranges: merged
|
|
8588
|
+
};
|
|
8589
|
+
};
|
|
8198
8590
|
var parsePrdStory = (storyRaw, index) => {
|
|
8199
8591
|
const label = `stories[${index}]`;
|
|
8200
8592
|
const story = toObject(storyRaw, `${label} must be an object.`);
|
|
@@ -8208,14 +8600,14 @@ var parsePrdStory = (storyRaw, index) => {
|
|
|
8208
8600
|
throw new Error(`${label}.status must be "pending" or "done".`);
|
|
8209
8601
|
}
|
|
8210
8602
|
let compactedFrom;
|
|
8603
|
+
let compactedFromRanges;
|
|
8211
8604
|
if (story.compactedFrom !== undefined) {
|
|
8212
|
-
compactedFrom = toIntegerArray(story.compactedFrom, `${label}.compactedFrom must be an array of integer story IDs.`);
|
|
8213
|
-
if (new Set(compactedFrom).size !== compactedFrom.length) {
|
|
8214
|
-
throw new Error(`${label}.compactedFrom must not contain duplicate IDs.`);
|
|
8215
|
-
}
|
|
8216
8605
|
if (status !== "done") {
|
|
8217
8606
|
throw new Error(`${label}.compactedFrom is only allowed when status is "done".`);
|
|
8218
8607
|
}
|
|
8608
|
+
const parsedCompactedFrom = parseCompactedFrom(story.compactedFrom, label);
|
|
8609
|
+
compactedFrom = parsedCompactedFrom.value;
|
|
8610
|
+
compactedFromRanges = parsedCompactedFrom.ranges;
|
|
8219
8611
|
}
|
|
8220
8612
|
return {
|
|
8221
8613
|
id,
|
|
@@ -8224,6 +8616,7 @@ var parsePrdStory = (storyRaw, index) => {
|
|
|
8224
8616
|
acceptanceCriteria,
|
|
8225
8617
|
dependsOn,
|
|
8226
8618
|
compactedFrom,
|
|
8619
|
+
compactedFromRanges,
|
|
8227
8620
|
status
|
|
8228
8621
|
};
|
|
8229
8622
|
};
|
|
@@ -8265,6 +8658,19 @@ var findDependencyCycle = (stories) => {
|
|
|
8265
8658
|
}
|
|
8266
8659
|
return null;
|
|
8267
8660
|
};
|
|
8661
|
+
var findReservedOwner = (id, reservedRanges) => {
|
|
8662
|
+
for (const range of reservedRanges) {
|
|
8663
|
+
if (id >= range.start && id <= range.end) {
|
|
8664
|
+
return range.ownerStoryId;
|
|
8665
|
+
}
|
|
8666
|
+
}
|
|
8667
|
+
return;
|
|
8668
|
+
};
|
|
8669
|
+
var toPublicStory = (story) => {
|
|
8670
|
+
const publicStory = { ...story };
|
|
8671
|
+
delete publicStory.compactedFromRanges;
|
|
8672
|
+
return publicStory;
|
|
8673
|
+
};
|
|
8268
8674
|
var validatePrd = (raw) => {
|
|
8269
8675
|
const payload = toObject(raw, "prd.json must be a JSON object.");
|
|
8270
8676
|
if (!Array.isArray(payload.stories)) {
|
|
@@ -8278,23 +8684,31 @@ var validatePrd = (raw) => {
|
|
|
8278
8684
|
}
|
|
8279
8685
|
storyById.set(story.id, story);
|
|
8280
8686
|
}
|
|
8281
|
-
const
|
|
8687
|
+
const reservedRanges = [];
|
|
8282
8688
|
for (const story of stories) {
|
|
8283
|
-
for (const
|
|
8284
|
-
|
|
8285
|
-
|
|
8689
|
+
for (const range of story.compactedFromRanges ?? []) {
|
|
8690
|
+
for (const existingRange of reservedRanges) {
|
|
8691
|
+
if (range.start <= existingRange.end && existingRange.start <= range.end) {
|
|
8692
|
+
const overlapId = Math.max(range.start, existingRange.start);
|
|
8693
|
+
throw new Error(`Compacted story id ${overlapId} is listed multiple times (stories ${existingRange.ownerStoryId} and ${story.id}).`);
|
|
8694
|
+
}
|
|
8286
8695
|
}
|
|
8287
|
-
|
|
8696
|
+
reservedRanges.push({
|
|
8697
|
+
start: range.start,
|
|
8698
|
+
end: range.end,
|
|
8699
|
+
ownerStoryId: story.id
|
|
8700
|
+
});
|
|
8288
8701
|
}
|
|
8289
8702
|
}
|
|
8290
|
-
for (const
|
|
8291
|
-
|
|
8292
|
-
|
|
8703
|
+
for (const story of stories) {
|
|
8704
|
+
const ownerStoryId = findReservedOwner(story.id, reservedRanges);
|
|
8705
|
+
if (ownerStoryId !== undefined) {
|
|
8706
|
+
throw new Error(`Story id ${story.id} is reserved by compactedFrom in story ${ownerStoryId}.`);
|
|
8293
8707
|
}
|
|
8294
8708
|
}
|
|
8295
8709
|
for (const story of stories) {
|
|
8296
8710
|
for (const depId of story.dependsOn) {
|
|
8297
|
-
const compactedOwner =
|
|
8711
|
+
const compactedOwner = findReservedOwner(depId, reservedRanges);
|
|
8298
8712
|
if (compactedOwner !== undefined) {
|
|
8299
8713
|
throw new Error(`Story ${story.id} depends on compacted story id ${depId} (compacted in story ${compactedOwner}).`);
|
|
8300
8714
|
}
|
|
@@ -8314,9 +8728,9 @@ var validatePrd = (raw) => {
|
|
|
8314
8728
|
throw new Error(`No runnable pending stories in prd.json. Pending stories are blocked: ${blockedIds}.`);
|
|
8315
8729
|
}
|
|
8316
8730
|
return {
|
|
8317
|
-
prd: { stories },
|
|
8318
|
-
pendingStories,
|
|
8319
|
-
runnableStory,
|
|
8731
|
+
prd: { stories: stories.map(toPublicStory) },
|
|
8732
|
+
pendingStories: pendingStories.map(toPublicStory),
|
|
8733
|
+
runnableStory: runnableStory ? toPublicStory(runnableStory) : null,
|
|
8320
8734
|
allDone: pendingStories.length === 0
|
|
8321
8735
|
};
|
|
8322
8736
|
};
|
|
@@ -8601,8 +9015,11 @@ var packageJsonPath = resolve4(new URL("../package.json", import.meta.url).pathn
|
|
|
8601
9015
|
var packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
|
|
8602
9016
|
var program2 = new Command;
|
|
8603
9017
|
program2.name("wile").description("Autonomous AI coding agent that ships features while you sleep").version(packageJson.version);
|
|
8604
|
-
program2.command("config").description("Configure the current project for Wile").action(async () => {
|
|
8605
|
-
await runConfig();
|
|
9018
|
+
program2.command("config").description("Configure the current project for Wile").option("--non-interactive [config]", "No value: print agent config docs. With JSON value: validate and apply non-interactive config.").action(async (options) => {
|
|
9019
|
+
const ok = await runConfig({ nonInteractive: options.nonInteractive });
|
|
9020
|
+
if (!ok) {
|
|
9021
|
+
process.exitCode = 1;
|
|
9022
|
+
}
|
|
8606
9023
|
});
|
|
8607
9024
|
program2.command("run").description("Run Wile on a repository").option("--repo <repo>", "Repository URL or local path").option("--max-iterations <count>", "Maximum iterations").option("--test", "Run in test mode").option("--debug", "Print debug info before running").action(async (options) => {
|
|
8608
9025
|
await runWile(options);
|