quest-loop 0.3.4 → 0.3.5
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/CHANGELOG.md +13 -0
- package/README.md +26 -14
- package/lib/cli.mjs +70 -10
- package/lib/codex-native.mjs +131 -3
- package/lib/help.mjs +17 -11
- package/package.json +1 -1
- package/skills/setup/SKILL.md +27 -11
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@ All notable changes to this project are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## [0.3.5] — 2026-07-08
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `quest codex doctor --fix` and `quest claude doctor --fix` now repair
|
|
13
|
+
Quest-owned native-agent templates and provider plugin install/version state,
|
|
14
|
+
then rerun the same doctor checks.
|
|
15
|
+
- `quest codex open` and `quest claude open` now run the provider health gate
|
|
16
|
+
before launching the interactive provider from the project root.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Stale Quest agent templates are replaced without `--force`; unrelated custom
|
|
20
|
+
files still require explicit replacement.
|
|
21
|
+
|
|
9
22
|
## [0.3.4] — 2026-07-08
|
|
10
23
|
|
|
11
24
|
### Changed
|
package/README.md
CHANGED
|
@@ -57,8 +57,8 @@ Install Quest's project-scoped Claude agent templates and verify the local
|
|
|
57
57
|
Claude setup with:
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
quest claude
|
|
61
|
-
quest claude
|
|
60
|
+
quest claude doctor --fix
|
|
61
|
+
quest claude open
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
For local development against a checkout, point Claude Code at the repo directly
|
|
@@ -84,16 +84,19 @@ Verify the install with:
|
|
|
84
84
|
|
|
85
85
|
```bash
|
|
86
86
|
codex plugin list --marketplace quest
|
|
87
|
-
quest codex
|
|
88
|
-
quest codex
|
|
87
|
+
quest codex doctor --fix
|
|
88
|
+
quest codex open
|
|
89
89
|
codex debug prompt-input "noop"
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
`quest codex doctor`
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
`quest codex doctor --fix` and `quest claude doctor --fix` install or refresh
|
|
93
|
+
Quest-owned native-agent templates and repair the provider plugin install/version
|
|
94
|
+
when the provider CLI supports it, then rerun doctor. `quest codex open` and
|
|
95
|
+
`quest claude open` run the same health gate before launching the interactive
|
|
96
|
+
provider. `codex debug prompt-input "noop"` should not print any hook parse
|
|
97
|
+
warnings.
|
|
95
98
|
|
|
96
|
-
#### Updating
|
|
99
|
+
#### Updating provider plugins
|
|
97
100
|
|
|
98
101
|
Codex installs plugins from a marketplace snapshot. Pulling this Git repo or
|
|
99
102
|
publishing a new tag is not enough to update the already-installed plugin cache.
|
|
@@ -103,10 +106,18 @@ Refresh the marketplace snapshot, then reinstall the plugin from it:
|
|
|
103
106
|
codex plugin marketplace upgrade quest
|
|
104
107
|
codex plugin add quest@quest
|
|
105
108
|
codex plugin list --marketplace quest
|
|
106
|
-
quest codex doctor
|
|
109
|
+
quest codex doctor --fix
|
|
107
110
|
```
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
Claude updates through its plugin manager:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
claude plugin update quest@quest
|
|
116
|
+
quest claude doctor --fix
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Then start a new provider session. If the Codex update contains hook changes,
|
|
120
|
+
re-run:
|
|
110
121
|
|
|
111
122
|
```bash
|
|
112
123
|
codex debug prompt-input "noop"
|
|
@@ -145,8 +156,9 @@ both providers:
|
|
|
145
156
|
- `.claude/agents/quest-executor.md`
|
|
146
157
|
- `.claude/agents/quest-reviewer.md`
|
|
147
158
|
|
|
148
|
-
Use `quest init --no-agents` when you only want the `.quests/` store.
|
|
149
|
-
|
|
159
|
+
Use `quest init --no-agents` when you only want the `.quests/` store. Stale
|
|
160
|
+
Quest-owned agent templates are replaced automatically. If an existing file is
|
|
161
|
+
not recognizable as a Quest agent template, init fails before creating
|
|
150
162
|
`.quests/`; inspect the conflicting files, run the explicit provider install
|
|
151
163
|
command with `--force` only if you intend to replace them, then rerun
|
|
152
164
|
`quest init`.
|
|
@@ -238,8 +250,8 @@ full base protocol lives in
|
|
|
238
250
|
| `quest amend` | Append a numbered protocol amendment (retro output) |
|
|
239
251
|
| `quest protocol` | Print the loop protocol + this store's local amendments |
|
|
240
252
|
| `quest runs` | Show headless runner activity (from `.quests/runs.ndjson`) |
|
|
241
|
-
| `quest codex` | Validate
|
|
242
|
-
| `quest claude` | Validate
|
|
253
|
+
| `quest codex` | Validate, repair, install, or open Codex-native setup |
|
|
254
|
+
| `quest claude` | Validate, repair, install, or open Claude-native setup |
|
|
243
255
|
|
|
244
256
|
## quest-run (headless runner)
|
|
245
257
|
|
package/lib/cli.mjs
CHANGED
|
@@ -5,13 +5,14 @@ import { parseArgs } from "node:util";
|
|
|
5
5
|
import { readFileSync, existsSync, writeFileSync, appendFileSync } from "node:fs";
|
|
6
6
|
import { join, dirname } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
8
9
|
import { ConfigError, findStoreDir, loadConfig } from "./config.mjs";
|
|
9
10
|
import { ContractError, lintRecord } from "./contract.mjs";
|
|
10
11
|
import * as local from "./store-local.mjs";
|
|
11
12
|
import * as github from "./store-github.mjs";
|
|
12
13
|
import { openStore } from "./store.mjs";
|
|
13
14
|
import { COMMANDS, renderCommandHelp, renderGeneralHelp, renderInitNextSteps, renderNoStore, renderStatusOverview } from "./help.mjs";
|
|
14
|
-
import { claudeDoctor, doctor as codexDoctor, installAgents as installCodexAgents, installClaudeAgents, versionInfo } from "./codex-native.mjs";
|
|
15
|
+
import { claudeDoctor, claudeDoctorFix, codexDoctorFix, doctor as codexDoctor, installAgents as installCodexAgents, installClaudeAgents, nativeProjectRoot, versionInfo } from "./codex-native.mjs";
|
|
15
16
|
|
|
16
17
|
const PLUGIN_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
17
18
|
|
|
@@ -92,28 +93,87 @@ function preflightInitAgentInstall(cwd, env) {
|
|
|
92
93
|
return plans;
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
function printDoctorResult(command, result, out) {
|
|
97
|
+
if (result.repairs?.length) {
|
|
98
|
+
out(`${command} doctor --fix: repairs`);
|
|
99
|
+
for (const r of result.repairs) out(` ${r.ok ? "OK " : "ERR"} ${r.name}: ${r.command} (${r.detail})`);
|
|
100
|
+
}
|
|
101
|
+
for (const c of result.checks) out(`${c.ok ? "OK " : "ERR"} ${c.name}: ${c.detail}`);
|
|
102
|
+
out(result.ok ? `${command} doctor: OK` : `${command} doctor: problems found`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function parseOpenArgs(args) {
|
|
106
|
+
const forwarded = [];
|
|
107
|
+
let dryRun = false;
|
|
108
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
109
|
+
const arg = args[i];
|
|
110
|
+
if (arg === "--") {
|
|
111
|
+
forwarded.push(...args.slice(i + 1));
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
if (arg === "--dry-run") {
|
|
115
|
+
dryRun = true;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (arg === "--help" || arg === "-h") return { help: true };
|
|
119
|
+
forwarded.push(arg);
|
|
120
|
+
}
|
|
121
|
+
return { dryRun, forwarded };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function shellQuote(arg) {
|
|
125
|
+
return /^[A-Za-z0-9_./:=@+-]+$/.test(arg) ? arg : `'${String(arg).replaceAll("'", "'\\''")}'`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function openInvocation(command, cwd, env, forwarded) {
|
|
129
|
+
const root = nativeProjectRoot(cwd, env);
|
|
130
|
+
if (command === "codex") return { cmd: "codex", args: ["-C", root, ...forwarded], cwd };
|
|
131
|
+
if (command === "claude") return { cmd: "claude", args: forwarded, cwd: root };
|
|
132
|
+
throw new Error(`unknown provider "${command}"`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function runNativeSetupCommand(command, args, { out, cwd, env }, { label, doctor, doctorFix, install }) {
|
|
96
136
|
const [subcommand, ...rest] = args;
|
|
97
137
|
if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
|
|
98
138
|
out(renderCommandHelp(command));
|
|
99
139
|
return 0;
|
|
100
140
|
}
|
|
101
141
|
if (subcommand === "doctor") {
|
|
102
|
-
const p = parse(command, rest, {}, { positionals: 0 });
|
|
142
|
+
const p = parse(command, rest, { fix: { type: "boolean" } }, { positionals: 0 });
|
|
103
143
|
if (p.help) {
|
|
104
144
|
out(renderCommandHelp(command));
|
|
105
145
|
return 0;
|
|
106
146
|
}
|
|
107
|
-
const result = doctor({ cwd, env });
|
|
147
|
+
const result = p.values.fix ? doctorFix({ cwd, env }) : doctor({ cwd, env });
|
|
108
148
|
if (p.values.json) out(JSON.stringify(result));
|
|
109
|
-
else
|
|
110
|
-
for (const c of result.checks) out(`${c.ok ? "OK " : "ERR"} ${c.name}: ${c.detail}`);
|
|
111
|
-
out(result.ok ? `${command} doctor: OK` : `${command} doctor: problems found`);
|
|
112
|
-
}
|
|
149
|
+
else printDoctorResult(command, result, out);
|
|
113
150
|
// 1 (generic diagnostic failure), not 5 — exit 5 is reserved for
|
|
114
151
|
// ContractError (a quest contract violation), which a doctor finding is not.
|
|
115
152
|
return result.ok ? 0 : 1;
|
|
116
153
|
}
|
|
154
|
+
if (subcommand === "open") {
|
|
155
|
+
const p = parseOpenArgs(rest);
|
|
156
|
+
if (p.help) {
|
|
157
|
+
out(renderCommandHelp(command));
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
const result = doctorFix({ cwd, env });
|
|
161
|
+
if (!result.ok) {
|
|
162
|
+
printDoctorResult(command, result, out);
|
|
163
|
+
out(`${command} open: not launching because doctor still reports problems`);
|
|
164
|
+
return 1;
|
|
165
|
+
}
|
|
166
|
+
const invocation = openInvocation(command, cwd, env, p.forwarded);
|
|
167
|
+
const rendered = [invocation.cmd, ...invocation.args].map(shellQuote).join(" ");
|
|
168
|
+
if (p.dryRun) {
|
|
169
|
+
out(`${command} open: health OK`);
|
|
170
|
+
out(`Would run: ${rendered}`);
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
out(`${command} open: health OK; launching ${rendered}`);
|
|
174
|
+
const res = spawnSync(invocation.cmd, invocation.args, { cwd: invocation.cwd, env, stdio: "inherit" });
|
|
175
|
+
return typeof res.status === "number" ? res.status : 1;
|
|
176
|
+
}
|
|
117
177
|
if (subcommand === "install-agents") {
|
|
118
178
|
const p = parse(command, rest, {
|
|
119
179
|
scope: { type: "string", default: "project" },
|
|
@@ -536,10 +596,10 @@ const HANDLERS = {
|
|
|
536
596
|
},
|
|
537
597
|
|
|
538
598
|
async codex(args, { out, cwd, env }) {
|
|
539
|
-
return runNativeSetupCommand("codex", args, { out, cwd, env }, { label: "Codex", doctor: codexDoctor, install: installCodexAgents });
|
|
599
|
+
return runNativeSetupCommand("codex", args, { out, cwd, env }, { label: "Codex", doctor: codexDoctor, doctorFix: codexDoctorFix, install: installCodexAgents });
|
|
540
600
|
},
|
|
541
601
|
|
|
542
602
|
async claude(args, { out, cwd, env }) {
|
|
543
|
-
return runNativeSetupCommand("claude", args, { out, cwd, env }, { label: "Claude", doctor: claudeDoctor, install: installClaudeAgents });
|
|
603
|
+
return runNativeSetupCommand("claude", args, { out, cwd, env }, { label: "Claude", doctor: claudeDoctor, doctorFix: claudeDoctorFix, install: installClaudeAgents });
|
|
544
604
|
},
|
|
545
605
|
};
|
package/lib/codex-native.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { fileURLToPath } from "node:url";
|
|
|
9
9
|
|
|
10
10
|
const PLUGIN_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
11
|
const AGENTS = ["quest-executor", "quest-reviewer"];
|
|
12
|
+
const QUEST_CODEX_MARKETPLACE_SOURCE = ["roberts", "reberski/quest"].join("");
|
|
12
13
|
const PROVIDERS = {
|
|
13
14
|
codex: {
|
|
14
15
|
label: "Codex",
|
|
@@ -160,7 +161,19 @@ export function versionInfo() {
|
|
|
160
161
|
};
|
|
161
162
|
}
|
|
162
163
|
|
|
163
|
-
function
|
|
164
|
+
function existingAgentMatchesName(provider, name, path) {
|
|
165
|
+
let text;
|
|
166
|
+
try {
|
|
167
|
+
text = readFileSync(path, "utf8");
|
|
168
|
+
} catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
if (provider === "codex") return new RegExp(`^\\s*name\\s*=\\s*"${name}"\\s*$`, "m").test(text);
|
|
172
|
+
if (provider === "claude") return new RegExp(`^\\s*name:\\s*${name}\\s*$`, "m").test(text);
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function installProviderAgents(provider, { scope = "project", dryRun = false, force = false, replaceStale = true, cwd = process.cwd(), env = process.env } = {}) {
|
|
164
177
|
const cfg = providerConfig(provider);
|
|
165
178
|
const dir = targetDir(provider, scope, cwd, env);
|
|
166
179
|
const dirConflicts = [dirname(dir), dir]
|
|
@@ -175,9 +188,10 @@ function installProviderAgents(provider, { scope = "project", dryRun = false, fo
|
|
|
175
188
|
const dest = join(dir, `${name}.${cfg.extension}`);
|
|
176
189
|
const text = readFileSync(src, "utf8");
|
|
177
190
|
const exists = existsSync(dest) || isSymlink(dest);
|
|
191
|
+
const staleQuestAgent = exists && !sameFile(dest, text) && existingAgentMatchesName(provider, name, dest);
|
|
178
192
|
let action;
|
|
179
193
|
if (exists && sameFile(dest, text)) action = "unchanged";
|
|
180
|
-
else if (exists && !force) action = "conflict";
|
|
194
|
+
else if (exists && !force && !(replaceStale && staleQuestAgent)) action = "conflict";
|
|
181
195
|
else action = exists ? "replace" : "create";
|
|
182
196
|
return { name, path: dest, action, text };
|
|
183
197
|
});
|
|
@@ -208,6 +222,10 @@ export function installClaudeAgents(options = {}) {
|
|
|
208
222
|
return installProviderAgents("claude", options);
|
|
209
223
|
}
|
|
210
224
|
|
|
225
|
+
export function nativeProjectRoot(cwd = process.cwd(), env = process.env) {
|
|
226
|
+
return projectRoot(cwd, env);
|
|
227
|
+
}
|
|
228
|
+
|
|
211
229
|
function questCliPathCheck(cwd, env, versions) {
|
|
212
230
|
const pathQuestVersion = runCmd("quest", ["--version"], { cwd, env });
|
|
213
231
|
const pathVersion = pathQuestVersion.status === 0 ? pathQuestVersion.stdout.trim() : null;
|
|
@@ -243,7 +261,7 @@ function nativeAgentsCheck(provider, cwd, env) {
|
|
|
243
261
|
}
|
|
244
262
|
const agentDetail = [
|
|
245
263
|
missing.length ? `missing: ${missing.join(", ")}` : null,
|
|
246
|
-
stale.length ? `stale (run install-agents
|
|
264
|
+
stale.length ? `stale (run doctor --fix or install-agents): ${stale.join(", ")}` : null,
|
|
247
265
|
].filter(Boolean).join("; ") || `installed ${cfg.label} templates current: ${Object.values(found).join(", ")}`;
|
|
248
266
|
return check(
|
|
249
267
|
"native-agents",
|
|
@@ -418,3 +436,113 @@ export function claudeDoctor({ cwd = process.cwd(), env = process.env } = {}) {
|
|
|
418
436
|
|
|
419
437
|
return { ok: checks.every((c) => c.ok), checks };
|
|
420
438
|
}
|
|
439
|
+
|
|
440
|
+
function checkByName(result, name) {
|
|
441
|
+
return result.checks.find((c) => c.name === name);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function cmdDetail(res) {
|
|
445
|
+
const text = [res.stdout, res.stderr, res.error?.message].filter(Boolean).join("\n").trim();
|
|
446
|
+
return text || `exit ${res.status ?? "unknown"}`;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function repairRecord(name, command, res, extra = {}) {
|
|
450
|
+
return {
|
|
451
|
+
name,
|
|
452
|
+
command: command.join(" "),
|
|
453
|
+
ok: res.status === 0,
|
|
454
|
+
detail: cmdDetail(res),
|
|
455
|
+
...extra,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function runRepairCommand(cmd, args, { cwd, env }) {
|
|
460
|
+
return runCmd(cmd, args, { cwd, env });
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function codexMarketplacePresent(cwd, env) {
|
|
464
|
+
const res = runCmd("codex", ["plugin", "marketplace", "list", "--json"], { cwd, env });
|
|
465
|
+
if (res.status !== 0) return { ok: false, present: false, res };
|
|
466
|
+
try {
|
|
467
|
+
const parsed = JSON.parse(res.stdout || "{}");
|
|
468
|
+
const marketplaces = Array.isArray(parsed.marketplaces) ? parsed.marketplaces : [];
|
|
469
|
+
return { ok: true, present: marketplaces.some((m) => m.name === "quest" || m.root?.includes("/quest")) };
|
|
470
|
+
} catch {
|
|
471
|
+
return { ok: false, present: false, res };
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function providerPluginNeedsRepair(provider, result) {
|
|
476
|
+
const names = provider === "codex"
|
|
477
|
+
? ["plugin-installed", "plugin-version", "hook-parser", "single-neutral-skill-root"]
|
|
478
|
+
: ["plugin-installed", "plugin-version"];
|
|
479
|
+
return names.some((name) => checkByName(result, name)?.ok === false);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function repairProviderPlugin(provider, before, { cwd, env }) {
|
|
483
|
+
const cliName = provider === "codex" ? "codex-cli" : "claude-cli";
|
|
484
|
+
if (!checkByName(before, cliName)?.ok || !providerPluginNeedsRepair(provider, before)) return [];
|
|
485
|
+
|
|
486
|
+
const repairs = [];
|
|
487
|
+
if (provider === "codex") {
|
|
488
|
+
const marketplace = codexMarketplacePresent(cwd, env);
|
|
489
|
+
if (!marketplace.ok) {
|
|
490
|
+
repairs.push(repairRecord("plugin-marketplace-list", ["codex", "plugin", "marketplace", "list", "--json"], marketplace.res));
|
|
491
|
+
return repairs;
|
|
492
|
+
}
|
|
493
|
+
if (!marketplace.present) {
|
|
494
|
+
const addMarketplaceArgs = ["plugin", "marketplace", "add", QUEST_CODEX_MARKETPLACE_SOURCE];
|
|
495
|
+
const addMarketplace = runRepairCommand("codex", addMarketplaceArgs, { cwd, env });
|
|
496
|
+
repairs.push(repairRecord("plugin-marketplace-add", ["codex", ...addMarketplaceArgs], addMarketplace));
|
|
497
|
+
if (addMarketplace.status !== 0) return repairs;
|
|
498
|
+
}
|
|
499
|
+
const upgrade = runRepairCommand("codex", ["plugin", "marketplace", "upgrade", "quest"], { cwd, env });
|
|
500
|
+
repairs.push(repairRecord("plugin-marketplace-upgrade", ["codex", "plugin", "marketplace", "upgrade", "quest"], upgrade));
|
|
501
|
+
if (upgrade.status !== 0) return repairs;
|
|
502
|
+
const addPlugin = runRepairCommand("codex", ["plugin", "add", "quest@quest"], { cwd, env });
|
|
503
|
+
repairs.push(repairRecord("plugin-add", ["codex", "plugin", "add", "quest@quest"], addPlugin));
|
|
504
|
+
return repairs;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const installed = checkByName(before, "plugin-installed")?.installed;
|
|
508
|
+
const subcommand = installed ? "update" : "install";
|
|
509
|
+
const res = runRepairCommand("claude", ["plugin", subcommand, "quest@quest"], { cwd, env });
|
|
510
|
+
repairs.push(repairRecord(`plugin-${subcommand}`, ["claude", "plugin", subcommand, "quest@quest"], res));
|
|
511
|
+
return repairs;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function repairProviderAgents(provider, before, { cwd, env }) {
|
|
515
|
+
if (checkByName(before, "native-agents")?.ok !== false) return [];
|
|
516
|
+
const result = installProviderAgents(provider, { scope: "project", force: false, replaceStale: true, cwd, env });
|
|
517
|
+
return [{
|
|
518
|
+
name: "native-agents",
|
|
519
|
+
command: `quest ${provider} install-agents --scope project`,
|
|
520
|
+
ok: result.ok,
|
|
521
|
+
detail: result.ok ? `${result.actions.map((a) => `${a.action} ${a.path}`).join("; ")}` : `conflicts: ${result.conflicts.map((c) => c.path).join(", ")}`,
|
|
522
|
+
result,
|
|
523
|
+
}];
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function doctorForProvider(provider, options) {
|
|
527
|
+
if (provider === "codex") return doctor(options);
|
|
528
|
+
if (provider === "claude") return claudeDoctor(options);
|
|
529
|
+
throw new Error(`unknown provider "${provider}"`);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function doctorWithFix(provider, { cwd = process.cwd(), env = process.env } = {}) {
|
|
533
|
+
const before = doctorForProvider(provider, { cwd, env });
|
|
534
|
+
const repairs = [
|
|
535
|
+
...repairProviderAgents(provider, before, { cwd, env }),
|
|
536
|
+
...repairProviderPlugin(provider, before, { cwd, env }),
|
|
537
|
+
];
|
|
538
|
+
const after = doctorForProvider(provider, { cwd, env });
|
|
539
|
+
return { ok: after.ok, checks: after.checks, repairs, before };
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export function codexDoctorFix(options = {}) {
|
|
543
|
+
return doctorWithFix("codex", options);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export function claudeDoctorFix(options = {}) {
|
|
547
|
+
return doctorWithFix("claude", options);
|
|
548
|
+
}
|
package/lib/help.mjs
CHANGED
|
@@ -138,30 +138,34 @@ export const COMMANDS = {
|
|
|
138
138
|
example: "quest runs --active",
|
|
139
139
|
},
|
|
140
140
|
codex: {
|
|
141
|
-
purpose: "Inspect
|
|
142
|
-
usage: "quest codex doctor [--json] | quest codex install-agents [--scope project|user] [--dry-run] [--force] [--json]",
|
|
141
|
+
purpose: "Inspect, repair, install, or open Quest's native Codex integration",
|
|
142
|
+
usage: "quest codex doctor [--fix] [--json] | quest codex open [--dry-run] [-- <codex args>] | quest codex install-agents [--scope project|user] [--dry-run] [--force] [--json]",
|
|
143
143
|
flags: [
|
|
144
144
|
["doctor", "check Codex CLI, multi-agent support, plugin install/version, hooks, skill roots, and native-agent templates"],
|
|
145
|
+
["--fix", "repair missing/stale Quest agents and Quest plugin install/version when possible, then rerun doctor"],
|
|
146
|
+
["open", "repair/check setup, then launch interactive Codex from the project root"],
|
|
145
147
|
["install-agents", "install quest-executor and quest-reviewer as native Codex custom agents"],
|
|
146
148
|
["--scope <s>", "project (default: .codex/agents at repo root) or user (~/.codex/agents)"],
|
|
147
149
|
["--dry-run", "show intended agent writes without changing files"],
|
|
148
|
-
["--force", "replace existing agent files"],
|
|
150
|
+
["--force", "replace existing non-Quest agent files"],
|
|
149
151
|
["--json", "machine-readable output"],
|
|
150
152
|
],
|
|
151
|
-
example: "quest codex
|
|
153
|
+
example: "quest codex doctor --fix && quest codex open",
|
|
152
154
|
},
|
|
153
155
|
claude: {
|
|
154
|
-
purpose: "Inspect
|
|
155
|
-
usage: "quest claude doctor [--json] | quest claude install-agents [--scope project|user] [--dry-run] [--force] [--json]",
|
|
156
|
+
purpose: "Inspect, repair, install, or open Quest's native Claude Code integration",
|
|
157
|
+
usage: "quest claude doctor [--fix] [--json] | quest claude open [--dry-run] [-- <claude args>] | quest claude install-agents [--scope project|user] [--dry-run] [--force] [--json]",
|
|
156
158
|
flags: [
|
|
157
159
|
["doctor", "check Claude CLI, plugin install/version, and native-agent templates"],
|
|
160
|
+
["--fix", "repair missing/stale Quest agents and Quest plugin install/version when possible, then rerun doctor"],
|
|
161
|
+
["open", "repair/check setup, then launch interactive Claude Code from the project root"],
|
|
158
162
|
["install-agents", "install quest-executor and quest-reviewer as native Claude Code custom agents"],
|
|
159
163
|
["--scope <s>", "project (default: .claude/agents at repo root) or user (~/.claude/agents)"],
|
|
160
164
|
["--dry-run", "show intended agent writes without changing files"],
|
|
161
|
-
["--force", "replace existing agent files"],
|
|
165
|
+
["--force", "replace existing non-Quest agent files"],
|
|
162
166
|
["--json", "machine-readable output"],
|
|
163
167
|
],
|
|
164
|
-
example: "quest claude
|
|
168
|
+
example: "quest claude doctor --fix && quest claude open",
|
|
165
169
|
},
|
|
166
170
|
};
|
|
167
171
|
|
|
@@ -221,9 +225,11 @@ export function renderInitNextSteps(backend, { agentsInstalled = true } = {}) {
|
|
|
221
225
|
"",
|
|
222
226
|
"Store created. Next steps:",
|
|
223
227
|
...(agentsInstalled ? [" 0. Restart agent sessions so newly installed project templates are loaded"] : []),
|
|
224
|
-
" 1.
|
|
225
|
-
" 2.
|
|
226
|
-
"
|
|
228
|
+
...(agentsInstalled ? [" 1. Repair/check providers: quest codex doctor --fix (or quest claude doctor --fix)"] : []),
|
|
229
|
+
...(agentsInstalled ? [" 2. Open provider: quest codex open (or quest claude open)"] : []),
|
|
230
|
+
` ${agentsInstalled ? "3" : "1"}. Author a quest: quest create --help (or $quest:plan in your agent session)`,
|
|
231
|
+
` ${agentsInstalled ? "4" : "2"}. Check it: quest lint --all`,
|
|
232
|
+
` ${agentsInstalled ? "5" : "3"}. Work it: $quest:work <id> in-session, or quest-run <id> headless`,
|
|
227
233
|
...(agentsInstalled ? [] : [" Agent templates skipped; install later with `quest codex install-agents --scope project` and `quest claude install-agents --scope project`."]),
|
|
228
234
|
...(backend === "github" ? ["", "Records live as GitHub issues; config and amendments stay local in .quests/."] : []),
|
|
229
235
|
].join("\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quest-loop",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Goal-loop engineering for coding agents: quest contracts, iterative execution with evidence checkpoints, Claude + Codex workers. Ships the `quest` store CLI and the `quest-run` headless runner.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": { "node": ">=20" },
|
package/skills/setup/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: setup
|
|
3
|
-
description: Use when setting up, validating, updating, or troubleshooting Quest's native Codex or Claude plugin integration.
|
|
4
|
-
argument-hint: "[doctor|install-agents|init]"
|
|
3
|
+
description: Use when setting up, validating, updating, opening, or troubleshooting Quest's native Codex or Claude plugin integration.
|
|
4
|
+
argument-hint: "[doctor|fix|open|install-agents|init]"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Set up Quest native agents
|
|
@@ -19,9 +19,10 @@ agent templates for both providers:
|
|
|
19
19
|
- `.claude/agents/quest-reviewer.md`
|
|
20
20
|
|
|
21
21
|
Use `quest init --no-agents` when you only want the quest store. If an existing
|
|
22
|
-
project template
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
project template is stale and still identifies itself as Quest's agent, init
|
|
23
|
+
replaces it. If another file conflicts, init fails before creating `.quests/`;
|
|
24
|
+
inspect the file, run the explicit provider install command with `--force` only
|
|
25
|
+
when you intend replacement, then rerun `quest init`.
|
|
25
26
|
|
|
26
27
|
Project-scoped agent templates install at the Git repository root. For a nested
|
|
27
28
|
quest store, use `quest init --no-agents` in the nested directory and set
|
|
@@ -32,7 +33,7 @@ quest store, use `quest init --no-agents` in the nested directory and set
|
|
|
32
33
|
Check the installed Codex-facing state from the actual Codex surfaces:
|
|
33
34
|
|
|
34
35
|
```bash
|
|
35
|
-
quest codex doctor
|
|
36
|
+
quest codex doctor --fix
|
|
36
37
|
```
|
|
37
38
|
|
|
38
39
|
This verifies the Codex CLI, `quest` binary on PATH, `multi_agent` and `goals`
|
|
@@ -45,7 +46,14 @@ use `quest-run --codex-goal-mode require` only as the headless fallback.
|
|
|
45
46
|
Check the installed Claude-facing state from the actual Claude surfaces:
|
|
46
47
|
|
|
47
48
|
```bash
|
|
48
|
-
quest claude doctor
|
|
49
|
+
quest claude doctor --fix
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Open an interactive provider only after the same repair/check gate passes:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
quest codex open
|
|
56
|
+
quest claude open
|
|
49
57
|
```
|
|
50
58
|
|
|
51
59
|
## Install Native Agents
|
|
@@ -70,9 +78,10 @@ quest claude install-agents --scope user
|
|
|
70
78
|
```
|
|
71
79
|
|
|
72
80
|
If an existing file conflicts, inspect it first. Use `--force` only when you
|
|
73
|
-
intend to replace that custom agent with Quest's bundled definition.
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
intend to replace that custom agent with Quest's bundled definition. Stale
|
|
82
|
+
Quest-owned templates are replaced without `--force`. Symlinked agent template
|
|
83
|
+
files can be written through with `--force`; keep the provider agent directories
|
|
84
|
+
themselves as real directories.
|
|
76
85
|
|
|
77
86
|
## Update Installed Plugin
|
|
78
87
|
|
|
@@ -82,7 +91,14 @@ refresh and reinstall, then start a new Codex thread:
|
|
|
82
91
|
```bash
|
|
83
92
|
codex plugin marketplace upgrade quest
|
|
84
93
|
codex plugin add quest@quest
|
|
85
|
-
quest codex doctor
|
|
94
|
+
quest codex doctor --fix
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Claude updates through its plugin manager. After updating, restart Claude Code:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
claude plugin update quest@quest
|
|
101
|
+
quest claude doctor --fix
|
|
86
102
|
```
|
|
87
103
|
|
|
88
104
|
If hooks changed, review/trust the updated hook definitions in Codex when
|