@dougharness/cli 1.0.2 → 1.1.1
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/dist/bin.js +18 -3
- package/dist/bin.js.map +1 -1
- package/dist/board-serve.js +14 -7
- package/dist/board-serve.js.map +1 -1
- package/dist/board.d.ts +1 -1
- package/dist/board.js +42 -9
- package/dist/board.js.map +1 -1
- package/dist/detect/repo.js +14 -1
- package/dist/detect/repo.js.map +1 -1
- package/dist/detect/types.d.ts +2 -0
- package/dist/doctor.d.ts +46 -0
- package/dist/doctor.js +491 -0
- package/dist/doctor.js.map +1 -0
- package/dist/generate/agents.d.ts +7 -0
- package/dist/generate/agents.js +62 -12
- package/dist/generate/agents.js.map +1 -1
- package/dist/generate/claude-md.js +4 -0
- package/dist/generate/claude-md.js.map +1 -1
- package/dist/generate/config.d.ts +8 -0
- package/dist/generate/config.js +4 -1
- package/dist/generate/config.js.map +1 -1
- package/dist/generate/proposal.js +15 -6
- package/dist/generate/proposal.js.map +1 -1
- package/dist/generate/settings.d.ts +2 -0
- package/dist/generate/settings.js +17 -2
- package/dist/generate/settings.js.map +1 -1
- package/dist/init.d.ts +14 -0
- package/dist/init.js +94 -8
- package/dist/init.js.map +1 -1
- package/dist/memory-setup.d.ts +28 -0
- package/dist/memory-setup.js +326 -0
- package/dist/memory-setup.js.map +1 -0
- package/node_modules/@dougharness/flow/.claude-plugin/plugin.json +16 -4
- package/node_modules/@dougharness/flow/agents/lead.md +1 -1
- package/node_modules/@dougharness/flow/agents/planner.md +3 -3
- package/node_modules/@dougharness/flow/agents/researcher.md +2 -2
- package/node_modules/@dougharness/flow/hooks/hooks.json +16 -0
- package/node_modules/@dougharness/flow/lib/board.mjs +171 -13
- package/node_modules/@dougharness/flow/lib/code-index.mjs +18 -3
- package/node_modules/@dougharness/flow/lib/context.mjs +57 -0
- package/node_modules/@dougharness/flow/lib/cost.mjs +155 -7
- package/node_modules/@dougharness/flow/lib/embeddings.mjs +188 -24
- package/node_modules/@dougharness/flow/lib/land.mjs +74 -10
- package/node_modules/@dougharness/flow/lib/learn.mjs +4 -4
- package/node_modules/@dougharness/flow/lib/memory.mjs +368 -50
- package/node_modules/@dougharness/flow/lib/plan.mjs +11 -1
- package/node_modules/@dougharness/flow/package.json +2 -1
- package/node_modules/@dougharness/flow/scripts/board.mjs +86 -25
- package/node_modules/@dougharness/flow/scripts/context.mjs +50 -0
- package/node_modules/@dougharness/flow/scripts/cost.mjs +15 -3
- package/node_modules/@dougharness/flow/scripts/memory.mjs +197 -37
- package/node_modules/@dougharness/flow/scripts/plan.mjs +54 -64
- package/node_modules/@dougharness/flow/scripts/session-recall.mjs +132 -0
- package/node_modules/@dougharness/flow/skills/doug-hand/SKILL.md +105 -0
- package/node_modules/@dougharness/flow/skills/doug-learn/SKILL.md +1 -1
- package/node_modules/@dougharness/flow/skills/doug-next/SKILL.md +50 -14
- package/node_modules/@dougharness/flow/skills/doug-plan/SKILL.md +1 -1
- package/node_modules/@dougharness/flow/skills/doug-swarm/SKILL.md +7 -7
- package/node_modules/@dougharness/flow/skills/research/SKILL.md +4 -2
- package/node_modules/@dougharness/flow/skills/run-report/SKILL.md +14 -13
- package/node_modules/@dougharness/flow/skills/swarm-launch/SKILL.md +1 -1
- package/node_modules/@dougharness/flow/workflows/doug-implement.js +145 -86
- package/node_modules/@dougharness/gates/.claude-plugin/plugin.json +1 -1
- package/node_modules/@dougharness/gates/hooks/hooks.json +11 -0
- package/node_modules/@dougharness/gates/lib/baseline.mjs +166 -1
- package/node_modules/@dougharness/gates/lib/bash-rules.mjs +21 -14
- package/node_modules/@dougharness/gates/lib/block-ledger.mjs +58 -1
- package/node_modules/@dougharness/gates/lib/checkpoint.mjs +1 -1
- package/node_modules/@dougharness/gates/lib/config.mjs +20 -0
- package/node_modules/@dougharness/gates/lib/docs-gate.mjs +95 -0
- package/node_modules/@dougharness/gates/lib/evidence.mjs +2 -0
- package/node_modules/@dougharness/gates/lib/gate-lock.mjs +94 -0
- package/node_modules/@dougharness/gates/lib/run.mjs +4 -0
- package/node_modules/@dougharness/gates/lib/test-totals.mjs +40 -7
- package/node_modules/@dougharness/gates/lib/trace.mjs +24 -7
- package/node_modules/@dougharness/gates/lib/verified-tree.mjs +225 -0
- package/node_modules/@dougharness/gates/package.json +1 -1
- package/node_modules/@dougharness/gates/scripts/research-cap.mjs +133 -5
- package/node_modules/@dougharness/gates/scripts/stop-gate.mjs +558 -197
- package/node_modules/@dougharness/gates/scripts/trace.mjs +5 -1
- package/node_modules/@dougharness/gates/scripts/turn-start.mjs +17 -0
- package/node_modules/@dougharness/gates/scripts/verify.mjs +201 -0
- package/package.json +4 -4
- package/templates/board-app.js +4 -3
- package/node_modules/@dougharness/flow/lib/rehearse.mjs +0 -825
- package/node_modules/@dougharness/flow/scripts/rehearse.mjs +0 -129
- package/node_modules/@dougharness/flow/skills/core-next/SKILL.md +0 -92
- package/node_modules/@dougharness/flow/skills/harness-fix/SKILL.md +0 -85
package/dist/init.js
CHANGED
|
@@ -1,21 +1,98 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { resolve } from "node:path";
|
|
3
4
|
import { detect } from "./detect/index.js";
|
|
4
5
|
import { buildProposal, summarize } from "./generate/proposal.js";
|
|
5
6
|
import { renderDiff } from "./diff.js";
|
|
6
7
|
import { applyChanges } from "./apply.js";
|
|
7
|
-
|
|
8
|
+
import { readMemoryConfig } from "@dougharness/flow/lib/embeddings.mjs";
|
|
9
|
+
import { runMemorySetup } from "./memory-setup.js";
|
|
10
|
+
import { collectChecks, renderDoctor } from "./doctor.js";
|
|
11
|
+
export const READY_LINE = "You're ready. Next:";
|
|
12
|
+
export const NOT_READY_LINE = "Not ready: fix the fail lines above, then run doug doctor.";
|
|
13
|
+
const GREENFIELD_LINE = 'Empty repository: start with a walking skeleton (https://github.com/eschwa3/dougharness/blob/main/docs/getting-started.md#starting-from-an-empty-repository, "Starting from an empty repository"), then re-run doug init so the gates detect its commands. /doug-start will automate this (ADR 0012).';
|
|
14
|
+
async function defaultAsk(question) {
|
|
8
15
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
9
16
|
const answer = await new Promise((res) => rl.question(question, res));
|
|
10
17
|
rl.close();
|
|
11
|
-
return
|
|
18
|
+
return answer;
|
|
19
|
+
}
|
|
20
|
+
// Offer semantic memory setup. Never throws and never changes init's exit code.
|
|
21
|
+
async function memoryStep(opts, dir, isTTY, ask, log) {
|
|
22
|
+
try {
|
|
23
|
+
if (readMemoryConfig(dir).embeddings) {
|
|
24
|
+
log("Semantic memory is configured.");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (opts.yes || !isTTY) {
|
|
28
|
+
log("Semantic memory is off (keyword-only). Enable it any time: doug memory setup");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const answer = (await ask("Set up semantic memory now? [Y/n] ")).trim().toLowerCase();
|
|
32
|
+
if (answer !== "" && !answer.startsWith("y"))
|
|
33
|
+
return;
|
|
34
|
+
const deps = { ...opts.memorySetup, ask, isTTY };
|
|
35
|
+
if (!deps.out && opts.quiet)
|
|
36
|
+
deps.out = () => { };
|
|
37
|
+
else if (!deps.out && opts.out)
|
|
38
|
+
deps.out = opts.out;
|
|
39
|
+
await runMemorySetup({ dir }, deps);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
43
|
+
log(`Semantic memory step failed (${msg}). Run: doug memory setup`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function cliVersion() {
|
|
47
|
+
try {
|
|
48
|
+
return String(createRequire(import.meta.url)("../package.json").version);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return "unknown";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Closing setup check plus the ready screen. Never throws and never changes init's exit code.
|
|
55
|
+
async function finalScreen(opts, dir, isEmpty, wroteClaudeMd, log) {
|
|
56
|
+
log("");
|
|
57
|
+
log("Setup check (the same checks as doug doctor):");
|
|
58
|
+
let ready = false;
|
|
59
|
+
try {
|
|
60
|
+
const initFix = resolve(opts.dir) === resolve(opts.doctor?.cwd ?? process.cwd()) ? "doug init" : `doug init ${/\s/.test(opts.dir) ? JSON.stringify(opts.dir) : opts.dir}`;
|
|
61
|
+
const checks = await collectChecks(dir, opts.doctor ?? {}, initFix);
|
|
62
|
+
const counts = { ok: 0, warn: 0, fail: 0, skip: 0 };
|
|
63
|
+
for (const c of checks)
|
|
64
|
+
counts[c.status]++;
|
|
65
|
+
const report = { dir, cliVersion: opts.doctor?.cliVersion ?? cliVersion(), ok: counts.fail === 0, counts, checks };
|
|
66
|
+
log(renderDoctor(report).replace(/\n$/, ""));
|
|
67
|
+
ready = report.ok;
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
71
|
+
log(`Setup check failed (${msg}). Run: doug doctor`);
|
|
72
|
+
}
|
|
73
|
+
log("");
|
|
74
|
+
log(ready ? READY_LINE : NOT_READY_LINE);
|
|
75
|
+
log(" 1. claude open Claude Code here; the hooks are active immediately");
|
|
76
|
+
log(isEmpty
|
|
77
|
+
? ' 2. write the walking-skeleton card first (docs/getting-started.md, "Starting from an empty repository"), then re-run doug init'
|
|
78
|
+
: " 2. doug board add <id> --title <t> --goal <g> queue a card; doug board serve --open shows the board");
|
|
79
|
+
log(" 3. /doug-next in Claude Code: plan, approve, and run the next Ready card");
|
|
80
|
+
if (wroteClaudeMd)
|
|
81
|
+
log(" Edit CLAUDE.md's Gotchas section by hand; keep it short.");
|
|
82
|
+
log(" Re-check any time: doug doctor");
|
|
12
83
|
}
|
|
13
84
|
export async function runInit(opts) {
|
|
14
85
|
const dir = resolve(opts.dir);
|
|
15
86
|
const log = (s) => {
|
|
16
|
-
if (
|
|
87
|
+
if (opts.quiet)
|
|
88
|
+
return;
|
|
89
|
+
if (opts.out)
|
|
90
|
+
opts.out(s);
|
|
91
|
+
else
|
|
17
92
|
process.stdout.write(s + "\n");
|
|
18
93
|
};
|
|
94
|
+
const isTTY = opts.isTTY ?? !!process.stdin.isTTY;
|
|
95
|
+
const ask = opts.ask ?? defaultAsk;
|
|
19
96
|
const detection = detect(dir);
|
|
20
97
|
const proposal = buildProposal(detection);
|
|
21
98
|
log("");
|
|
@@ -28,8 +105,17 @@ export async function runInit(opts) {
|
|
|
28
105
|
log(` - ${n}`);
|
|
29
106
|
}
|
|
30
107
|
log("");
|
|
108
|
+
if (detection.repo.isEmpty) {
|
|
109
|
+
log(GREENFIELD_LINE);
|
|
110
|
+
log("");
|
|
111
|
+
}
|
|
31
112
|
if (proposal.changes.length === 0) {
|
|
32
113
|
log("Nothing to change. The project is already configured.");
|
|
114
|
+
if (!opts.dryRun) {
|
|
115
|
+
await memoryStep(opts, dir, isTTY, ask, log);
|
|
116
|
+
if (!opts.quiet)
|
|
117
|
+
await finalScreen(opts, dir, detection.repo.isEmpty, false, log);
|
|
118
|
+
}
|
|
33
119
|
return 0;
|
|
34
120
|
}
|
|
35
121
|
const hookFiles = proposal.changes.filter((c) => c.path.startsWith(".doug/hooks/"));
|
|
@@ -49,11 +135,11 @@ export async function runInit(opts) {
|
|
|
49
135
|
return 0;
|
|
50
136
|
}
|
|
51
137
|
if (!opts.yes) {
|
|
52
|
-
if (!
|
|
138
|
+
if (!isTTY) {
|
|
53
139
|
log("Not a TTY and --yes not given. Nothing written.");
|
|
54
140
|
return 2;
|
|
55
141
|
}
|
|
56
|
-
const ok = await
|
|
142
|
+
const ok = /^y(es)?$/i.test((await ask("Apply these changes? [y/N] ")).trim());
|
|
57
143
|
if (!ok) {
|
|
58
144
|
log("Aborted. Nothing written.");
|
|
59
145
|
return 1;
|
|
@@ -61,9 +147,9 @@ export async function runInit(opts) {
|
|
|
61
147
|
}
|
|
62
148
|
const written = applyChanges(dir, proposal.changes);
|
|
63
149
|
log(`Wrote ${written.length} files.`);
|
|
64
|
-
log
|
|
65
|
-
|
|
66
|
-
|
|
150
|
+
await memoryStep(opts, dir, isTTY, ask, log);
|
|
151
|
+
if (!opts.quiet)
|
|
152
|
+
await finalScreen(opts, dir, detection.repo.isEmpty, written.includes("CLAUDE.md"), log);
|
|
67
153
|
return 0;
|
|
68
154
|
}
|
|
69
155
|
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface Endpoint {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
url: string;
|
|
5
|
+
hint: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const ENDPOINTS: Endpoint[];
|
|
8
|
+
export interface MemorySetupDeps {
|
|
9
|
+
endpoints?: Endpoint[];
|
|
10
|
+
ask?: (question: string) => Promise<string>;
|
|
11
|
+
isTTY?: boolean;
|
|
12
|
+
platform?: string;
|
|
13
|
+
out?: (line: string) => void;
|
|
14
|
+
listTimeoutMs?: number;
|
|
15
|
+
embedTimeoutMs?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface MemorySetupOpts {
|
|
18
|
+
dir: string;
|
|
19
|
+
url?: string;
|
|
20
|
+
yes?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare const MEMORY_USAGE = "Usage: doug memory setup [dir] [--url <url>] [--yes]\n";
|
|
23
|
+
export declare function normaliseUrl(u: string): string;
|
|
24
|
+
export declare function runMemorySetup(opts: MemorySetupOpts, deps?: MemorySetupDeps): Promise<{
|
|
25
|
+
status: "enabled" | "keyword-only";
|
|
26
|
+
reason: string;
|
|
27
|
+
}>;
|
|
28
|
+
export declare function runMemory(argv: string[]): Promise<number>;
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
// `doug memory setup`: find a running local embedding server, verify it, and (on the user's yes) enable it in
|
|
2
|
+
// .doug/config.json. Never installs anything, never runs a child process, never throws for a bad server.
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { createInterface } from "node:readline";
|
|
6
|
+
import { checkEmbeddingSanity, createProvider, DEFAULT_SANITY_PAIRS, doctorReport, loadSanityPairs, probeProvider, readMemoryConfig } from "@dougharness/flow/lib/embeddings.mjs";
|
|
7
|
+
import { embedLessons, openMemory } from "@dougharness/flow/lib/memory.mjs";
|
|
8
|
+
const LLAMA_HINT = "llama-server -m <embedding model>.gguf --embeddings";
|
|
9
|
+
// Conventional default ports. Verified: Ollama 11434, llama-server 8080, Unsloth Studio 8888.
|
|
10
|
+
// Unverified: LM Studio 1234, vLLM 8000, LocalAI on 8080; a wrong one just finds nothing.
|
|
11
|
+
export const ENDPOINTS = [
|
|
12
|
+
{ id: "ollama", label: "Ollama", url: "http://127.0.0.1:11434", hint: "ollama pull embeddinggemma" },
|
|
13
|
+
{ id: "unsloth", label: "Unsloth Studio", url: "http://127.0.0.1:8888", hint: "load or serve an embedding model in Unsloth Studio" },
|
|
14
|
+
{ id: "lmstudio", label: "LM Studio", url: "http://127.0.0.1:1234", hint: "load or serve an embedding model in LM Studio" },
|
|
15
|
+
{ id: "llama-server-or-localai", label: "llama-server or LocalAI", url: "http://127.0.0.1:8080", hint: LLAMA_HINT },
|
|
16
|
+
{ id: "vllm", label: "vLLM", url: "http://127.0.0.1:8000", hint: "load or serve an embedding model in vLLM" },
|
|
17
|
+
];
|
|
18
|
+
export const MEMORY_USAGE = "Usage: doug memory setup [dir] [--url <url>] [--yes]\n";
|
|
19
|
+
const KNOWN = ["embeddinggemma", "qwen3-embedding", "nomic-embed", "mxbai-embed", "bge", "snowflake-arctic-embed", "all-minilm"];
|
|
20
|
+
function baseName(id) {
|
|
21
|
+
const b = id.toLowerCase().split(/[/\\]/).pop() ?? "";
|
|
22
|
+
const i = b.indexOf(":");
|
|
23
|
+
return i === -1 ? b : b.slice(0, i);
|
|
24
|
+
}
|
|
25
|
+
function rank(id) {
|
|
26
|
+
const n = baseName(id);
|
|
27
|
+
const k = KNOWN.findIndex((p) => n.startsWith(p));
|
|
28
|
+
if (k !== -1)
|
|
29
|
+
return k;
|
|
30
|
+
return n.includes("embed") ? KNOWN.length : KNOWN.length + 1;
|
|
31
|
+
}
|
|
32
|
+
export function normaliseUrl(u) {
|
|
33
|
+
return u.trim().replace(/\/+$/, "").replace(/\/v1$/, "").replace(/\/+$/, "");
|
|
34
|
+
}
|
|
35
|
+
function reasonOf(err) {
|
|
36
|
+
const e = err;
|
|
37
|
+
if (e && (e.name === "TimeoutError" || e.name === "AbortError"))
|
|
38
|
+
return "timeout";
|
|
39
|
+
return e && e.message ? e.message : String(err);
|
|
40
|
+
}
|
|
41
|
+
async function probeModels(ep, timeoutMs) {
|
|
42
|
+
const base = normaliseUrl(ep.url);
|
|
43
|
+
const r = { ep, base, ok: false, ids: [], llama: false, reason: "" };
|
|
44
|
+
try {
|
|
45
|
+
const res = await fetch(`${base}/v1/models`, { signal: AbortSignal.timeout(timeoutMs) });
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
r.reason = `HTTP ${res.status}`;
|
|
48
|
+
return r;
|
|
49
|
+
}
|
|
50
|
+
const json = (await res.json());
|
|
51
|
+
if (!json || !Array.isArray(json.data)) {
|
|
52
|
+
r.reason = "malformed /v1/models body";
|
|
53
|
+
return r;
|
|
54
|
+
}
|
|
55
|
+
for (const item of json.data) {
|
|
56
|
+
if (item && typeof item.id === "string") {
|
|
57
|
+
r.ids.push(item.id);
|
|
58
|
+
if (item.owned_by === "llamacpp")
|
|
59
|
+
r.llama = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
r.ok = true;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
r.reason = reasonOf(err);
|
|
66
|
+
}
|
|
67
|
+
return r;
|
|
68
|
+
}
|
|
69
|
+
function modelMatches(reported, id) {
|
|
70
|
+
const a = baseName(reported);
|
|
71
|
+
const b = baseName(id);
|
|
72
|
+
return a.startsWith(b) || b.startsWith(a);
|
|
73
|
+
}
|
|
74
|
+
async function embedDims(base, id, timeoutMs, out) {
|
|
75
|
+
try {
|
|
76
|
+
const res = await fetch(`${base}/v1/embeddings`, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: { "content-type": "application/json" },
|
|
79
|
+
body: JSON.stringify({ model: id, input: ["probe"] }),
|
|
80
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
81
|
+
});
|
|
82
|
+
if (!res.ok) {
|
|
83
|
+
const body = (await res.text().catch(() => "")).slice(0, 200);
|
|
84
|
+
if (res.status === 501 || body.includes("--embeddings")) {
|
|
85
|
+
out(`${id}: the server was started without embeddings; restart it with --embeddings.`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
out(`${id}: embed failed: HTTP ${res.status}${body ? `: ${body}` : ""}`);
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const json = (await res.json());
|
|
93
|
+
const vec = json && Array.isArray(json.data) && json.data[0] ? json.data[0].embedding : null;
|
|
94
|
+
if (!Array.isArray(vec) || vec.length === 0) {
|
|
95
|
+
out(`${id}: embed failed: response had no embedding`);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
if (typeof json.model === "string" && !modelMatches(json.model, id)) {
|
|
99
|
+
out(`warning: asked for ${id} but the server reports ${json.model}`);
|
|
100
|
+
}
|
|
101
|
+
return vec.length;
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
out(`${id}: embed failed: ${reasonOf(err)}`);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function findPassing(servers, embedTimeoutMs, out) {
|
|
109
|
+
const passing = [];
|
|
110
|
+
for (const s of servers) {
|
|
111
|
+
if (!s.ok)
|
|
112
|
+
continue;
|
|
113
|
+
const hint = s.llama ? LLAMA_HINT : s.ep.hint;
|
|
114
|
+
const label = s.llama ? "llama-server" : s.ep.label;
|
|
115
|
+
const ordered = s.ids
|
|
116
|
+
.map((id, i) => ({ id, i }))
|
|
117
|
+
.sort((a, b) => rank(a.id) - rank(b.id) || a.i - b.i)
|
|
118
|
+
.map((x) => x.id);
|
|
119
|
+
if (ordered.length === 0) {
|
|
120
|
+
out(`${label} at ${s.base} lists no models. No embedding model: ${hint}`);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (rank(ordered[0]) > KNOWN.length)
|
|
124
|
+
out(`${label} at ${s.base}: no known embedding model listed. ${hint}`);
|
|
125
|
+
let reason = "no model passed verification";
|
|
126
|
+
let verified = false;
|
|
127
|
+
for (const id of ordered.slice(0, 3)) {
|
|
128
|
+
const dims = await embedDims(s.base, id, embedTimeoutMs, out);
|
|
129
|
+
if (dims === null) {
|
|
130
|
+
reason = `${id}: embed failed`;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const cfg = { embeddings: { provider: "openai-compatible", baseUrl: s.base, model: id, dims } };
|
|
135
|
+
const provider = createProvider(cfg, { timeoutMs: embedTimeoutMs });
|
|
136
|
+
const check = await checkEmbeddingSanity(provider, loadSanityPairs(DEFAULT_SANITY_PAIRS));
|
|
137
|
+
if (!check.ok) {
|
|
138
|
+
out(`${id}: failed the cosine sanity check: ${check.reason}`);
|
|
139
|
+
reason = `${id} failed the cosine sanity check: ${check.reason}`;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
passing.push({ dims, model: id, baseUrl: s.base, provider, label });
|
|
143
|
+
verified = true;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
out(`${id}: sanity check error: ${reasonOf(err)}`);
|
|
148
|
+
reason = `${id}: sanity check error: ${reasonOf(err)}`;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (!verified)
|
|
152
|
+
out(`${label} at ${s.base}: not offered, ${reason}`);
|
|
153
|
+
}
|
|
154
|
+
return passing;
|
|
155
|
+
}
|
|
156
|
+
function defaultAsk(question) {
|
|
157
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
158
|
+
return new Promise((resolve) => {
|
|
159
|
+
rl.question(question, (a) => {
|
|
160
|
+
rl.close();
|
|
161
|
+
resolve(a);
|
|
162
|
+
});
|
|
163
|
+
rl.once("close", () => resolve(""));
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function urlEndpoint(url) {
|
|
167
|
+
return { id: "url", label: "the given server", url, hint: "load or serve an embedding model in that server" };
|
|
168
|
+
}
|
|
169
|
+
export async function runMemorySetup(opts, deps = {}) {
|
|
170
|
+
const out = deps.out ?? ((l) => void process.stdout.write(l + "\n"));
|
|
171
|
+
const isTTY = deps.isTTY ?? !!process.stdin.isTTY;
|
|
172
|
+
const platform = deps.platform ?? process.platform;
|
|
173
|
+
const listTimeoutMs = deps.listTimeoutMs ?? 1500;
|
|
174
|
+
const embedTimeoutMs = deps.embedTimeoutMs ?? 15000;
|
|
175
|
+
const ask = deps.ask ?? defaultAsk;
|
|
176
|
+
const keywordOnly = (reason) => ({ status: "keyword-only", reason });
|
|
177
|
+
try {
|
|
178
|
+
const eps = [...(deps.endpoints ?? ENDPOINTS)];
|
|
179
|
+
if (opts.url) {
|
|
180
|
+
const given = normaliseUrl(opts.url);
|
|
181
|
+
if (!eps.some((ep) => normaliseUrl(ep.url) === given))
|
|
182
|
+
eps.push(urlEndpoint(opts.url));
|
|
183
|
+
}
|
|
184
|
+
let probed = await Promise.all(eps.map((ep) => probeModels(ep, listTimeoutMs)));
|
|
185
|
+
// Every unreachable or odd endpoint is reported as one line.
|
|
186
|
+
const report = (list) => {
|
|
187
|
+
for (const p of list) {
|
|
188
|
+
if (!p.ok)
|
|
189
|
+
out(`${p.ep.label} at ${p.base}: ${p.reason}`);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
report(probed);
|
|
193
|
+
let passing = await findPassing(probed, embedTimeoutMs, out);
|
|
194
|
+
if (passing.length === 0 && !probed.some((p) => p.ok) && isTTY && !opts.yes) {
|
|
195
|
+
const pasted = (await ask("No local embedding server found. Paste a server URL (Enter to skip): ")).trim();
|
|
196
|
+
if (pasted) {
|
|
197
|
+
const more = await probeModels(urlEndpoint(pasted), listTimeoutMs);
|
|
198
|
+
report([more]);
|
|
199
|
+
probed = [...probed, more];
|
|
200
|
+
passing = await findPassing([more], embedTimeoutMs, out);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (passing.length === 0) {
|
|
204
|
+
if (!probed.some((p) => p.ok)) {
|
|
205
|
+
const cmd = platform === "win32" ? "irm https://ollama.com/install.ps1 | iex" : "curl -fsSL https://ollama.com/install.sh | sh";
|
|
206
|
+
out("No local embedding server found. To get one, install Ollama:");
|
|
207
|
+
out(` ${cmd}`);
|
|
208
|
+
out(" ollama pull embeddinggemma");
|
|
209
|
+
return keywordOnly("no server reachable");
|
|
210
|
+
}
|
|
211
|
+
out("No server passed verification; memory stays keyword-only.");
|
|
212
|
+
return keywordOnly("no server passed verification");
|
|
213
|
+
}
|
|
214
|
+
let found = passing[0];
|
|
215
|
+
const foundLine = (v) => `Found ${v.label} at ${v.baseUrl}: model ${v.model}, ${v.dims} dims.`;
|
|
216
|
+
if (passing.length === 1)
|
|
217
|
+
out(foundLine(found));
|
|
218
|
+
else
|
|
219
|
+
passing.forEach((v, i) => out(`${i + 1}. ${foundLine(v)}`));
|
|
220
|
+
if (!opts.yes) {
|
|
221
|
+
if (!isTTY) {
|
|
222
|
+
out("Run `doug memory setup --yes` to enable it.");
|
|
223
|
+
return keywordOnly("not confirmed (no TTY)");
|
|
224
|
+
}
|
|
225
|
+
if (passing.length === 1) {
|
|
226
|
+
const answer = (await ask(`Enable semantic memory with ${found.model} at ${found.baseUrl}? [Y/n] `)).trim().toLowerCase();
|
|
227
|
+
if (answer !== "" && !answer.startsWith("y")) {
|
|
228
|
+
out("Not enabled.");
|
|
229
|
+
return keywordOnly("declined");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
const answer = (await ask(`Enable which server? [1-${passing.length}, Enter for 1, n for none] `)).trim();
|
|
234
|
+
const n = answer === "" ? 1 : /^\d+$/.test(answer) ? Number(answer) : 0;
|
|
235
|
+
if (n < 1 || n > passing.length) {
|
|
236
|
+
out("Not enabled.");
|
|
237
|
+
return keywordOnly("declined");
|
|
238
|
+
}
|
|
239
|
+
found = passing[n - 1];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const file = join(opts.dir, ".doug", "config.json");
|
|
243
|
+
let config = null;
|
|
244
|
+
try {
|
|
245
|
+
if (existsSync(file)) {
|
|
246
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
247
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
248
|
+
config = parsed;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
config = null;
|
|
253
|
+
}
|
|
254
|
+
if (!config) {
|
|
255
|
+
out("run doug init first (no readable .doug/config.json).");
|
|
256
|
+
return keywordOnly("no config");
|
|
257
|
+
}
|
|
258
|
+
const memory = config.memory && typeof config.memory === "object" && !Array.isArray(config.memory) ? config.memory : {};
|
|
259
|
+
config.memory = { ...memory, embeddings: { provider: "openai-compatible", baseUrl: found.baseUrl, model: found.model, dims: found.dims } };
|
|
260
|
+
writeFileSync(file, JSON.stringify(config, null, 2) + "\n");
|
|
261
|
+
out(`Wrote memory.embeddings to ${file}.`);
|
|
262
|
+
try {
|
|
263
|
+
const m = openMemory(opts.dir);
|
|
264
|
+
try {
|
|
265
|
+
const r = await embedLessons(m, found.provider);
|
|
266
|
+
out(`Embedded ${r.embedded}/${r.total} lesson(s)`);
|
|
267
|
+
if (r.failed)
|
|
268
|
+
out(`${r.failed} lesson(s) failed to embed: ${r.reason}`);
|
|
269
|
+
}
|
|
270
|
+
finally {
|
|
271
|
+
m.close();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
out(`Re-embedding existing lessons failed: ${reasonOf(err)}`);
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
const cfg = readMemoryConfig(opts.dir);
|
|
279
|
+
out(doctorReport(cfg, await probeProvider(cfg)));
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
out(`doctor report failed: ${reasonOf(err)}`);
|
|
283
|
+
}
|
|
284
|
+
return { status: "enabled", reason: "enabled" };
|
|
285
|
+
}
|
|
286
|
+
catch (err) {
|
|
287
|
+
out(`memory setup failed: ${reasonOf(err)}`);
|
|
288
|
+
return keywordOnly(reasonOf(err));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
export async function runMemory(argv) {
|
|
292
|
+
if (argv[0] !== "setup") {
|
|
293
|
+
process.stderr.write(`Unknown memory subcommand: ${argv[0] ?? "(none)"}\n${MEMORY_USAGE}`);
|
|
294
|
+
return 2;
|
|
295
|
+
}
|
|
296
|
+
let dir;
|
|
297
|
+
let url;
|
|
298
|
+
let yes = false;
|
|
299
|
+
const rest = argv.slice(1);
|
|
300
|
+
for (let i = 0; i < rest.length; i++) {
|
|
301
|
+
const a = rest[i];
|
|
302
|
+
if (a === "--yes" || a === "-y")
|
|
303
|
+
yes = true;
|
|
304
|
+
else if (a === "--url") {
|
|
305
|
+
const v = rest[++i];
|
|
306
|
+
if (v === undefined || v.startsWith("--")) {
|
|
307
|
+
process.stderr.write(`--url needs a value\n${MEMORY_USAGE}`);
|
|
308
|
+
return 2;
|
|
309
|
+
}
|
|
310
|
+
url = v;
|
|
311
|
+
}
|
|
312
|
+
else if (a.startsWith("-")) {
|
|
313
|
+
process.stderr.write(`Unknown flag: ${a}\n${MEMORY_USAGE}`);
|
|
314
|
+
return 2;
|
|
315
|
+
}
|
|
316
|
+
else if (dir === undefined)
|
|
317
|
+
dir = a;
|
|
318
|
+
else {
|
|
319
|
+
process.stderr.write(`Unexpected argument: ${a}\n${MEMORY_USAGE}`);
|
|
320
|
+
return 2;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const result = await runMemorySetup({ dir: dir ?? process.cwd(), url, yes });
|
|
324
|
+
return result.status === "enabled" ? 0 : 1;
|
|
325
|
+
}
|
|
326
|
+
//# sourceMappingURL=memory-setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-setup.js","sourceRoot":"","sources":["../src/memory-setup.ts"],"names":[],"mappings":"AAAA,8GAA8G;AAC9G,yGAAyG;AACzG,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,oBAAoB,EAAE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAElL,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AAS5E,MAAM,UAAU,GAAG,qDAAqD,CAAC;AAEzE,8FAA8F;AAC9F,0FAA0F;AAC1F,MAAM,CAAC,MAAM,SAAS,GAAe;IACnC,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,wBAAwB,EAAE,IAAI,EAAE,4BAA4B,EAAE;IACpG,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,uBAAuB,EAAE,IAAI,EAAE,oDAAoD,EAAE;IACpI,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,uBAAuB,EAAE,IAAI,EAAE,+CAA+C,EAAE;IAC3H,EAAE,EAAE,EAAE,yBAAyB,EAAE,KAAK,EAAE,yBAAyB,EAAE,GAAG,EAAE,uBAAuB,EAAE,IAAI,EAAE,UAAU,EAAE;IACnH,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,uBAAuB,EAAE,IAAI,EAAE,0CAA0C,EAAE;CAC9G,CAAC;AAkBF,MAAM,CAAC,MAAM,YAAY,GAAG,wDAAwD,CAAC;AAErF,MAAM,KAAK,GAAG,CAAC,gBAAgB,EAAE,iBAAiB,EAAE,aAAa,EAAE,aAAa,EAAE,KAAK,EAAE,wBAAwB,EAAE,YAAY,CAAC,CAAC;AAEjI,SAAS,QAAQ,CAAC,EAAU;IAC1B,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACtD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,IAAI,CAAC,EAAU;IACtB,MAAM,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvB,MAAM,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IACvB,OAAO,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED,SAAS,QAAQ,CAAC,GAAY;IAC5B,MAAM,CAAC,GAAG,GAA0C,CAAC;IACrD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC;QAAE,OAAO,SAAS,CAAC;IAClF,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAClD,CAAC;AAWD,KAAK,UAAU,WAAW,CAAC,EAAY,EAAE,SAAiB;IACxD,MAAM,IAAI,GAAG,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,CAAC,GAAW,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC7E,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,YAAY,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACzF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,CAAC,CAAC,MAAM,GAAG,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC;YAChC,OAAO,CAAC,CAAC;QACX,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAuB,CAAC;QACtD,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,CAAC,CAAC,MAAM,GAAG,2BAA2B,CAAC;YACvC,OAAO,CAAC,CAAC;QACX,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAmD,EAAE,CAAC;YAC5E,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;gBACxC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACpB,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU;oBAAE,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC;YACnD,CAAC;QACH,CAAC;QACD,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,CAAC,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,YAAY,CAAC,QAAgB,EAAE,EAAU;IAChD,MAAM,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC7B,MAAM,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvB,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;AAC5C,CAAC;AAUD,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,EAAU,EAAE,SAAiB,EAAE,GAAwB;IAC5F,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,gBAAgB,EAAE;YAC/C,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;YACrD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;SACvC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAC9D,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;gBACxD,GAAG,CAAC,GAAG,EAAE,4EAA4E,CAAC,CAAC;YACzF,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,GAAG,EAAE,wBAAwB,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3E,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA+D,CAAC;QAC9F,MAAM,GAAG,GAAG,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7F,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5C,GAAG,CAAC,GAAG,EAAE,2CAA2C,CAAC,CAAC;YACtD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC;YACpE,GAAG,CAAC,sBAAsB,EAAE,2BAA2B,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,GAAG,EAAE,mBAAmB,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,OAAiB,EAAE,cAAsB,EAAE,GAAwB;IAC5F,MAAM,OAAO,GAAe,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,CAAC,EAAE;YAAE,SAAS;QACpB,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC;QAC9C,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC;QACpD,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG;aAClB,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;aAC3B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;aACpD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACpB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,GAAG,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,IAAI,yCAAyC,IAAI,EAAE,CAAC,CAAC;YAC1E,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM;YAAE,GAAG,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,IAAI,sCAAsC,IAAI,EAAE,CAAC,CAAC;QAC5G,IAAI,MAAM,GAAG,8BAA8B,CAAC;QAC5C,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;YAC9D,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,MAAM,GAAG,GAAG,EAAE,gBAAgB,CAAC;gBAC/B,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,mBAAmB,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;gBAChG,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAC;gBACpE,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC,QAAQ,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAC,CAAC;gBAC1F,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;oBACd,GAAG,CAAC,GAAG,EAAE,qCAAqC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;oBAC9D,MAAM,GAAG,GAAG,EAAE,oCAAoC,KAAK,CAAC,MAAM,EAAE,CAAC;oBACjE,SAAS;gBACX,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;gBACpE,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM;YACR,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,GAAG,EAAE,yBAAyB,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACnD,MAAM,GAAG,GAAG,EAAE,yBAAyB,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACzD,CAAC;QACH,CAAC;QACD,IAAI,CAAC,QAAQ;YAAE,GAAG,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC,IAAI,kBAAkB,MAAM,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,UAAU,CAAC,QAAgB;IAClC,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;YAC1B,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,CAAC,CAAC,CAAC;QACb,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,IAAI,EAAE,iDAAiD,EAAE,CAAC;AAChH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAqB,EACrB,OAAwB,EAAE;IAE1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAC7E,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IACnD,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC;IACjD,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,KAAK,CAAC;IACpD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,UAAU,CAAC;IACnC,MAAM,WAAW,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,cAAuB,EAAE,MAAM,EAAE,CAAC,CAAC;IAEtF,IAAI,CAAC;QACH,MAAM,GAAG,GAAe,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC;QAC3D,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC;gBAAE,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACzF,CAAC;QACD,IAAI,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;QAChF,6DAA6D;QAC7D,MAAM,MAAM,GAAG,CAAC,IAAc,EAAE,EAAE;YAChC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,IAAI,CAAC,CAAC,CAAC,EAAE;oBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC;QACf,IAAI,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;QAE7D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YAC5E,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,uEAAuE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC3G,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,CAAC;gBACnE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;gBACf,MAAM,GAAG,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC;gBAC3B,OAAO,GAAG,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC9B,MAAM,GAAG,GAAG,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,0CAA0C,CAAC,CAAC,CAAC,+CAA+C,CAAC;gBAChI,GAAG,CAAC,8DAA8D,CAAC,CAAC;gBACpE,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;gBAChB,GAAG,CAAC,8BAA8B,CAAC,CAAC;gBACpC,OAAO,WAAW,CAAC,qBAAqB,CAAC,CAAC;YAC5C,CAAC;YACD,GAAG,CAAC,2DAA2D,CAAC,CAAC;YACjE,OAAO,WAAW,CAAC,+BAA+B,CAAC,CAAC;QACtD,CAAC;QAED,IAAI,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,SAAS,GAAG,CAAC,CAAW,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,OAAO,WAAW,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,IAAI,QAAQ,CAAC;QACzG,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;;YAC3C,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACjE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YACd,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,GAAG,CAAC,6CAA6C,CAAC,CAAC;gBACnD,OAAO,WAAW,CAAC,wBAAwB,CAAC,CAAC;YAC/C,CAAC;YACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,+BAA+B,KAAK,CAAC,KAAK,OAAO,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;gBAC1H,IAAI,MAAM,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC7C,GAAG,CAAC,cAAc,CAAC,CAAC;oBACpB,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC;gBACjC,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,2BAA2B,OAAO,CAAC,MAAM,6BAA6B,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC1G,MAAM,CAAC,GAAG,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACxE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;oBAChC,GAAG,CAAC,cAAc,CAAC,CAAC;oBACpB,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC;gBACjC,CAAC;gBACD,KAAK,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;QACpD,IAAI,MAAM,GAAmC,IAAI,CAAC;QAClD,IAAI,CAAC;YACH,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;gBACtD,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;oBAAE,MAAM,GAAG,MAAM,CAAC;YACtF,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,GAAG,CAAC,sDAAsD,CAAC,CAAC;YAC5D,OAAO,WAAW,CAAC,WAAW,CAAC,CAAC;QAClC,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,MAAM,CAAC,MAAkC,CAAC,CAAC,CAAC,EAAE,CAAC;QACrJ,MAAM,CAAC,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,mBAAmB,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC3I,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC5D,GAAG,CAAC,8BAA8B,IAAI,GAAG,CAAC,CAAC;QAE3C,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAChD,GAAG,CAAC,YAAY,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC;gBACnD,IAAI,CAAC,CAAC,MAAM;oBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,+BAA+B,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;YAC1E,CAAC;oBAAS,CAAC;gBACT,CAAC,CAAC,KAAK,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,yCAAyC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACvC,GAAG,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,yBAAyB,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAClD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,wBAAwB,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7C,OAAO,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc;IAC5C,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC;QACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC,CAAC;QAC3F,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,GAAuB,CAAC;IAC5B,IAAI,GAAuB,CAAC;IAC5B,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,IAAI;YAAE,GAAG,GAAG,IAAI,CAAC;aACvC,IAAI,CAAC,KAAK,OAAO,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACpB,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,YAAY,EAAE,CAAC,CAAC;gBAC7D,OAAO,CAAC,CAAC;YACX,CAAC;YACD,GAAG,GAAG,CAAC,CAAC;QACV,CAAC;aAAM,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC;YAC5D,OAAO,CAAC,CAAC;QACX,CAAC;aAAM,IAAI,GAAG,KAAK,SAAS;YAAE,GAAG,GAAG,CAAC,CAAC;aACjC,CAAC;YACJ,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC;YACnE,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7E,OAAO,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doug-flow",
|
|
3
3
|
"displayName": "Doug Flow",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.1.1",
|
|
5
5
|
"description": "Plan, approve, implement one task per worktree, verify, review. A deterministic Workflow-tool template plus planner, implementer, verifier, and reviewer subagents. The plan is a file; nothing is implemented until you approve it.",
|
|
6
|
-
"author": {
|
|
7
|
-
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Doug contributors"
|
|
8
|
+
},
|
|
9
|
+
"agents": [
|
|
10
|
+
"./agents/planner.md",
|
|
11
|
+
"./agents/researcher.md",
|
|
12
|
+
"./agents/lead.md",
|
|
13
|
+
"./agents/implementer.md",
|
|
14
|
+
"./agents/verifier.md",
|
|
15
|
+
"./agents/reviewer.md",
|
|
16
|
+
"./agents/adversary.md",
|
|
17
|
+
"./agents/adversary-claude.md"
|
|
18
|
+
],
|
|
8
19
|
"skills": "./skills/",
|
|
9
|
-
"workflows": "./workflows/"
|
|
20
|
+
"workflows": "./workflows/",
|
|
21
|
+
"hooks": "./hooks/hooks.json"
|
|
10
22
|
}
|
|
@@ -30,6 +30,6 @@ The workflow's own shape gate already keeps you out of a task with fewer than tw
|
|
|
30
30
|
|
|
31
31
|
## Re-briefing
|
|
32
32
|
|
|
33
|
-
A worker that returns `blocked: true` (not still-partial) gets you back once,
|
|
33
|
+
A worker that returns `blocked: true` (not still-partial) gets you back once, reading from the repository root, read-only, editing nothing and not using your earlier worktree, before the task blocks: return revised briefs for the unfinished pieces only. Every file in a revised brief is a file of a blocked brief - never a file a finished worker already committed, never a file outside the task. Revised briefs stay disjoint, the same rule as the first split. Most blocks are a missing piece of information, not a missing file: re-brief with the missing decision or interface, or re-split the blocked brief into smaller ones, rather than widening its files. `splitReason` says what changed and why, required as on the first split. Return `blocked: true` only when the block genuinely needs a file outside the ones you were given; the task then blocks with the original worker's reason. When the plan's worker check is on, the workflow has already checked each worker's files and commit before you ever see the list; a worker that failed it comes to you as a blocked brief like any other. At merge time you merge only what is listed.
|
|
34
34
|
|
|
35
35
|
Your final output is the structured result, not a message to a person.
|
|
@@ -12,13 +12,13 @@ You are the planner. You produce a plan, not code. The plan is a file that a hum
|
|
|
12
12
|
|
|
13
13
|
## What you do
|
|
14
14
|
|
|
15
|
-
1. Read the request. Read the parts of the repository it touches: entry points, the files that will change, their tests, the build and test commands (CLAUDE.md and .doug/config.json list them). Before deciding tasks, run `node "
|
|
15
|
+
1. Read the request. Read the parts of the repository it touches: entry points, the files that will change, their tests, the build and test commands (CLAUDE.md and .doug/config.json list them). Before deciding tasks, run `node "<scripts>/memory.mjs" recall "<the request in one line>" --k 8`, where `<scripts>` is the directory the prompt names after `Scripts:`, else `$CLAUDE_PLUGIN_ROOT/scripts` when that variable is set and non-empty, else skip the command and say so. Use the `Scripts:` directory as given, even when it lies under `~/.claude/plugins/cache` (a marketplace install's does). Never search the filesystem for another copy (no `find /`), and never pick a version under `~/.claude/plugins/cache` yourself; a subagent may not have `CLAUDE_PLUGIN_ROOT` set. When the command runs, treat each returned lesson as a constraint the plan must respect, citing the lesson id in the task spec it shaped; when the command prints keyword-only or no lessons, say nothing and go on. When `.doug/config.json` has `memory.index.enabled` true, also run `node "<scripts>/memory.mjs" index search "<the request in one line>" --k 8` (same `<scripts>` directory as above) and read the files it names before deciding tasks; when the index is off, the header says keyword-only with no chunks, or it prints nothing, say nothing and go on.
|
|
16
16
|
2. Decide the smallest set of tasks that achieves the goal. One task for single-file work. Three to five only for a real feature. Never more than eight: `plan.mjs validate` warns past five and past eight, and the user sees the warning at approval; split the work into two cards instead.
|
|
17
17
|
3. For each task, list the files it owns. Two tasks may not own the same file unless one depends on the other. Tests belong to the task that changes the behavior they test.
|
|
18
18
|
4. Order consumers after producers. A task whose spec or acceptance names a file, route, or subcommand another task owns or introduces (a page that PUTs to a server route, docs that name a CLI subcommand, a test that runs another task's file) must list that task in `dependsOn`, so it lands in a later level. The adversary judges a task branch against the whole plan goal, and a consumer in the same level as its producer is judged before the producer's work exists on its branch: on board-reorder (2026-09-07) that blocked four runs in a row. `plan.mjs validate` reports the pair when a spec or verify command names a file another task owns with no dependency between them; routes and subcommands it cannot see, so check those yourself.
|
|
19
19
|
5. Give each task a verification command that exercises its behavior, preferably a single test file. Give the plan integration commands from the project's own scripts (typecheck, lint, test).
|
|
20
|
-
6. Write acceptance criteria as observable outcomes a verifier can check by running something, not by reading code. Give every criterion that a command can check its command: an entry { "text", "command" } where command is one shell line that exits 0 when the criterion holds and runs from the repository root of a worktree with only the project's installed tools (no network, no npx-install). The verifier runs each command in the task worktree and reports its exit code; plan.mjs land runs them all in the integration worktree after the verify commands and refuses to land on any non-zero exit. A criterion that no command can check stays a plain string. Check each command against its text before writing it: the statusline plan once required grep -c statusline.mjs README.md to be at least 2 when the README only ever mentions it once.
|
|
21
|
-
7. Write `.doug/plan.json` with `Write`. Do not touch any other file. Then run `node "
|
|
20
|
+
6. Write acceptance criteria as observable outcomes a verifier can check by running something, not by reading code. Give every criterion that a command can check its command: an entry { "text", "command" } where command is one shell line that exits 0 when the criterion holds and runs from the repository root of a worktree with only the project's installed tools (no network, no npx-install). The verifier runs each command in the task worktree and reports its exit code; plan.mjs land runs them all in the integration worktree after the verify commands and refuses to land on any non-zero exit. A criterion that no command can check stays a plain string. An acceptance command never repeats a verify command (plan.verify or any task's verify): the verifiers, the integrate stage and land already run those. Name a narrower check (one test file, `-t <name>`) or leave the criterion as prose. Check each command against its text before writing it: the statusline plan once required grep -c statusline.mjs README.md to be at least 2 when the README only ever mentions it once.
|
|
21
|
+
7. Write `.doug/plan.json` with `Write`. Do not touch any other file. Then run `node "<scripts>/plan.mjs" validate` (the same `<scripts>` directory as step 1; when none is known, skip it and say so), and fix any errors it reports. When the request names a draft path instead (`.doug/.state/drafts/<card>.json`: the card is one of several planned in parallel for one plan), write the plan there, leave `.doug/plan.json` alone, and validate with `node "<scripts>/plan.mjs" validate --file <that path>`; `plan.mjs merge` joins the drafts afterwards and sequences any file two cards own, so plan the card on its own.
|
|
22
22
|
8. Return the rendered plan (the `show` output) so the user can decide.
|
|
23
23
|
|
|
24
24
|
## Plan file schema
|
|
@@ -18,14 +18,14 @@ You are the researcher. You were given one question, and a plan will be built on
|
|
|
18
18
|
3. Prefer an observation to a description when you can get one without changing anything: run the CLI read-only, fetch the page, read the installed package under `node_modules`. Say which of the two each fact rests on, since documentation and behavior drift apart.
|
|
19
19
|
4. Never install, never write, never change the machine: no package installs, no global config, no files in the checkout, no network calls beyond reading pages. If a fact needs an install to observe, mark it unverified and name the command that would show it.
|
|
20
20
|
5. Date what you read. A documentation page carries the date you fetched it and, when the page shows one, its version.
|
|
21
|
-
6. Budget: at most 6 WebSearch
|
|
21
|
+
6. Budget: at most 6 WebSearch, WebFetch, curl, or wget calls for your question, counted together (research.maxFetches; a hook denies the next one). Try sources in the order the question lists them, and write your findings before the budget runs out, marking anything still unanswered unverified.
|
|
22
22
|
|
|
23
23
|
## Output
|
|
24
24
|
|
|
25
25
|
Your final output is the findings, not a message to a person. Give them in this shape so the research skill can merge them into one note with the other researchers' answers:
|
|
26
26
|
|
|
27
27
|
- **Fact**: one or two sentences, phrased as the plan will use it.
|
|
28
|
-
- **Source**: a
|
|
28
|
+
- **Source**: a page quoted verbatim with its line number in the raw page (`curl -sL <url> | grep -n '<phrase>'`), or the command you ran with its relevant output quoted; one source per fact. Say `observed` for a command's output and `documented` for a page. A quote you could read only without a line number (a WebFetch summary, a rendered page) is marked `no line`: the research skill keeps a fact the design depends on unverified until it has the line.
|
|
29
29
|
- **Unverified**: every fact you could not confirm, marked `unverified`, with what you tried. An unverified line is what keeps the planner from building on it; leaving it out is a guess by omission.
|
|
30
30
|
|
|
31
31
|
Repeat for each fact the question needs. Do not pad: a question with one fact gets one fact.
|