gwqadd 0.5.0 → 0.5.2
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 +8 -1
- package/bin/gwqadd.mjs +37 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -139,9 +139,16 @@ One question, one confirmation:
|
|
|
139
139
|
**The AI** is whichever of these is on your `PATH`:
|
|
140
140
|
|
|
141
141
|
```
|
|
142
|
-
claude -p → codex exec → opencode run → gemini -p
|
|
142
|
+
claude -p → codex exec --skip-git-repo-check (if Claude fails) → opencode run → gemini -p
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
+
Automatic detection tries Claude first and falls back to
|
|
146
|
+
`codex exec --skip-git-repo-check` when it fails or returns no usable branch
|
|
147
|
+
name, then continues through the other available CLIs. The flag is required
|
|
148
|
+
because the AI runs in an empty directory rather than the user's repository.
|
|
149
|
+
An explicit `--ai '<cmd>'` or `GWQADD_AI='<cmd>'` selects one command and keeps
|
|
150
|
+
the existing manual fallback if that command fails.
|
|
151
|
+
|
|
145
152
|
No API key to obtain, no account to create — it uses what you already have.
|
|
146
153
|
Expect 6–8 seconds, almost all of it the CLI's own start-up; an elapsed counter
|
|
147
154
|
runs while it works.
|
package/bin/gwqadd.mjs
CHANGED
|
@@ -76,11 +76,12 @@ NAMING HELP
|
|
|
76
76
|
modified, so the AI picks the prefix and the wording that match the repo. A
|
|
77
77
|
rejected suggestion is passed back as an exclusion, so "n" does not return it.
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
and
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
Automatic detection tries claude -p first, then codex exec
|
|
80
|
+
--skip-git-repo-check if Claude fails or returns no usable name, followed by
|
|
81
|
+
opencode run and gemini -p. Nothing is sent anywhere until you answer the
|
|
82
|
+
question, and nothing is created until you confirm. Override with --ai '<cmd>'
|
|
83
|
+
or GWQADD_AI='<cmd>' to use one command explicitly; disable with --no-ai or
|
|
84
|
+
GWQADD_AI=off, which leaves a plain prompt for an ASCII name.
|
|
84
85
|
|
|
85
86
|
--no-random (or GWQADD_RANDOM=off) starts at the description prompt instead.
|
|
86
87
|
--random goes the other way and never asks; it is the only naming path that
|
|
@@ -1228,7 +1229,9 @@ function namingPrompt(ctx, description, rejected) {
|
|
|
1228
1229
|
// answers a bare prompt headlessly.
|
|
1229
1230
|
const AI_CLIS = [
|
|
1230
1231
|
{ bin: 'claude', args: ['-p'] },
|
|
1231
|
-
|
|
1232
|
+
// runAi() deliberately uses an empty, non-Git directory; Codex requires this
|
|
1233
|
+
// opt-out of its repository check before it will accept that working root.
|
|
1234
|
+
{ bin: 'codex', args: ['exec', '--skip-git-repo-check'] },
|
|
1232
1235
|
{ bin: 'opencode', args: ['run'] },
|
|
1233
1236
|
{ bin: 'gemini', args: ['-p'] },
|
|
1234
1237
|
];
|
|
@@ -1243,9 +1246,15 @@ function detectAi() {
|
|
|
1243
1246
|
if (['off', '0', 'false', 'none'].includes(override)) return null;
|
|
1244
1247
|
// Split on whitespace only: quoting rules would be a shell of our own.
|
|
1245
1248
|
const parts = override.split(/\s+/).filter(Boolean);
|
|
1246
|
-
return {
|
|
1249
|
+
return {
|
|
1250
|
+
candidates: [{ bin: parts[0], args: parts.slice(1) }],
|
|
1251
|
+
automatic: false,
|
|
1252
|
+
};
|
|
1247
1253
|
}
|
|
1248
|
-
return
|
|
1254
|
+
return {
|
|
1255
|
+
candidates: AI_CLIS.filter((c) => commandExists(c.bin)),
|
|
1256
|
+
automatic: true,
|
|
1257
|
+
};
|
|
1249
1258
|
}
|
|
1250
1259
|
|
|
1251
1260
|
// These CLIs are agents, not text transformers: run one inside a repository and
|
|
@@ -1433,11 +1442,14 @@ async function composeBranchName(dir, repo, base) {
|
|
|
1433
1442
|
if (chosen) return chosen;
|
|
1434
1443
|
}
|
|
1435
1444
|
|
|
1436
|
-
const
|
|
1437
|
-
if (!
|
|
1445
|
+
const selection = detectAi();
|
|
1446
|
+
if (!selection || selection.candidates.length === 0) {
|
|
1447
|
+
return { branch: await typeItYourself(), named: 'manual' };
|
|
1448
|
+
}
|
|
1438
1449
|
|
|
1439
1450
|
const ctx = repoContext(dir, repo, base);
|
|
1440
1451
|
const rejected = [];
|
|
1452
|
+
let aiIndex = 0;
|
|
1441
1453
|
|
|
1442
1454
|
for (;;) {
|
|
1443
1455
|
log(`${dim('│')}`);
|
|
@@ -1447,17 +1459,23 @@ async function composeBranchName(dir, repo, base) {
|
|
|
1447
1459
|
// An empty answer is the escape hatch out of the AI entirely.
|
|
1448
1460
|
if (!description) return { branch: await typeItYourself(), named: 'manual' };
|
|
1449
1461
|
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1462
|
+
let ai;
|
|
1463
|
+
let candidates;
|
|
1464
|
+
for (;;) {
|
|
1465
|
+
ai = selection.candidates[aiIndex];
|
|
1466
|
+
const res = await askAi(ai, namingPrompt(ctx, description, rejected));
|
|
1467
|
+
candidates = res.ok ? parseCandidates(res.out) : [];
|
|
1468
|
+
if (res.ok && candidates.length > 0) break;
|
|
1469
|
+
|
|
1470
|
+
const reason = res.ok ? 'returned nothing usable' : 'failed';
|
|
1471
|
+
const next = selection.automatic
|
|
1472
|
+
? selection.candidates[aiIndex + 1]
|
|
1473
|
+
: null;
|
|
1474
|
+
warn(`${aiLabel(ai)} ${reason} — ${next ? `trying ${aiLabel(next)} instead` : 'name it yourself instead'}`);
|
|
1453
1475
|
const first = (res.err || '').trim().split('\n')[0];
|
|
1454
1476
|
if (first) log(`${dim('│')} ${dim(first.slice(0, 120))}`);
|
|
1455
|
-
return { branch: await typeItYourself(), named: 'manual' };
|
|
1456
|
-
|
|
1457
|
-
const candidates = parseCandidates(res.out);
|
|
1458
|
-
if (candidates.length === 0) {
|
|
1459
|
-
warn(`${aiLabel(ai)} returned nothing usable — name it yourself instead`);
|
|
1460
|
-
return { branch: await typeItYourself(), named: 'manual' };
|
|
1477
|
+
if (!next) return { branch: await typeItYourself(), named: 'manual' };
|
|
1478
|
+
aiIndex++;
|
|
1461
1479
|
}
|
|
1462
1480
|
|
|
1463
1481
|
const choice = await confirmCreate(candidates[0], ctx.base);
|