@ozzylabs/feedradar 0.1.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/LICENSE +21 -0
- package/README.md +104 -0
- package/dist/agents/_boundary.d.ts +44 -0
- package/dist/agents/_boundary.d.ts.map +1 -0
- package/dist/agents/_boundary.js +59 -0
- package/dist/agents/_boundary.js.map +1 -0
- package/dist/agents/claude-code.d.ts +32 -0
- package/dist/agents/claude-code.d.ts.map +1 -0
- package/dist/agents/claude-code.js +256 -0
- package/dist/agents/claude-code.js.map +1 -0
- package/dist/agents/codex-cli.d.ts +31 -0
- package/dist/agents/codex-cli.d.ts.map +1 -0
- package/dist/agents/codex-cli.js +303 -0
- package/dist/agents/codex-cli.js.map +1 -0
- package/dist/agents/copilot.d.ts +29 -0
- package/dist/agents/copilot.d.ts.map +1 -0
- package/dist/agents/copilot.js +282 -0
- package/dist/agents/copilot.js.map +1 -0
- package/dist/agents/gemini-cli.d.ts +30 -0
- package/dist/agents/gemini-cli.d.ts.map +1 -0
- package/dist/agents/gemini-cli.js +316 -0
- package/dist/agents/gemini-cli.js.map +1 -0
- package/dist/agents/index.d.ts +12 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +33 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/types.d.ts +103 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/claude-skills/dismiss/SKILL.md +41 -0
- package/dist/claude-skills/research/SKILL.md +45 -0
- package/dist/claude-skills/review/SKILL.md +45 -0
- package/dist/claude-skills/update/SKILL.md +49 -0
- package/dist/cli/dismiss.d.ts +28 -0
- package/dist/cli/dismiss.d.ts.map +1 -0
- package/dist/cli/dismiss.js +122 -0
- package/dist/cli/dismiss.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +64 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +148 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +578 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/research.d.ts +30 -0
- package/dist/cli/research.d.ts.map +1 -0
- package/dist/cli/research.js +313 -0
- package/dist/cli/research.js.map +1 -0
- package/dist/cli/review.d.ts +34 -0
- package/dist/cli/review.d.ts.map +1 -0
- package/dist/cli/review.js +418 -0
- package/dist/cli/review.js.map +1 -0
- package/dist/cli/source.d.ts +57 -0
- package/dist/cli/source.d.ts.map +1 -0
- package/dist/cli/source.js +511 -0
- package/dist/cli/source.js.map +1 -0
- package/dist/cli/update.d.ts +43 -0
- package/dist/cli/update.d.ts.map +1 -0
- package/dist/cli/update.js +429 -0
- package/dist/cli/update.js.map +1 -0
- package/dist/cli/watch.d.ts +22 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/cli/watch.js +101 -0
- package/dist/cli/watch.js.map +1 -0
- package/dist/core/config.d.ts +60 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +101 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/feeds/derive-id.d.ts +43 -0
- package/dist/core/feeds/derive-id.d.ts.map +1 -0
- package/dist/core/feeds/derive-id.js +66 -0
- package/dist/core/feeds/derive-id.js.map +1 -0
- package/dist/core/feeds/github-api.d.ts +69 -0
- package/dist/core/feeds/github-api.d.ts.map +1 -0
- package/dist/core/feeds/github-api.js +161 -0
- package/dist/core/feeds/github-api.js.map +1 -0
- package/dist/core/feeds/github-releases.d.ts +3 -0
- package/dist/core/feeds/github-releases.d.ts.map +1 -0
- package/dist/core/feeds/github-releases.js +85 -0
- package/dist/core/feeds/github-releases.js.map +1 -0
- package/dist/core/feeds/html.d.ts +10 -0
- package/dist/core/feeds/html.d.ts.map +1 -0
- package/dist/core/feeds/html.js +263 -0
- package/dist/core/feeds/html.js.map +1 -0
- package/dist/core/feeds/index.d.ts +5 -0
- package/dist/core/feeds/index.d.ts.map +1 -0
- package/dist/core/feeds/index.js +18 -0
- package/dist/core/feeds/index.js.map +1 -0
- package/dist/core/feeds/npm-registry.d.ts +36 -0
- package/dist/core/feeds/npm-registry.d.ts.map +1 -0
- package/dist/core/feeds/npm-registry.js +200 -0
- package/dist/core/feeds/npm-registry.js.map +1 -0
- package/dist/core/feeds/rss.d.ts +12 -0
- package/dist/core/feeds/rss.d.ts.map +1 -0
- package/dist/core/feeds/rss.js +222 -0
- package/dist/core/feeds/rss.js.map +1 -0
- package/dist/core/feeds/types.d.ts +45 -0
- package/dist/core/feeds/types.d.ts.map +1 -0
- package/dist/core/feeds/types.js +2 -0
- package/dist/core/feeds/types.js.map +1 -0
- package/dist/core/filter.d.ts +25 -0
- package/dist/core/filter.d.ts.map +1 -0
- package/dist/core/filter.js +123 -0
- package/dist/core/filter.js.map +1 -0
- package/dist/core/injection-detector.d.ts +57 -0
- package/dist/core/injection-detector.d.ts.map +1 -0
- package/dist/core/injection-detector.js +109 -0
- package/dist/core/injection-detector.js.map +1 -0
- package/dist/core/items.d.ts +20 -0
- package/dist/core/items.d.ts.map +1 -0
- package/dist/core/items.js +105 -0
- package/dist/core/items.js.map +1 -0
- package/dist/core/state.d.ts +12 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +42 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/templates.d.ts +21 -0
- package/dist/core/templates.d.ts.map +1 -0
- package/dist/core/templates.js +52 -0
- package/dist/core/templates.js.map +1 -0
- package/dist/core/watcher.d.ts +72 -0
- package/dist/core/watcher.d.ts.map +1 -0
- package/dist/core/watcher.js +240 -0
- package/dist/core/watcher.js.map +1 -0
- package/dist/gemini-commands/dismiss.toml +2 -0
- package/dist/gemini-commands/research.toml +2 -0
- package/dist/gemini-commands/review.toml +2 -0
- package/dist/gemini-commands/update.toml +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/config.d.ts +39 -0
- package/dist/schemas/config.d.ts.map +1 -0
- package/dist/schemas/config.js +23 -0
- package/dist/schemas/config.js.map +1 -0
- package/dist/schemas/index.d.ts +6 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +6 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/item.d.ts +38 -0
- package/dist/schemas/item.d.ts.map +1 -0
- package/dist/schemas/item.js +34 -0
- package/dist/schemas/item.js.map +1 -0
- package/dist/schemas/research.d.ts +82 -0
- package/dist/schemas/research.d.ts.map +1 -0
- package/dist/schemas/research.js +45 -0
- package/dist/schemas/research.js.map +1 -0
- package/dist/schemas/source.d.ts +139 -0
- package/dist/schemas/source.d.ts.map +1 -0
- package/dist/schemas/source.js +127 -0
- package/dist/schemas/source.js.map +1 -0
- package/dist/schemas/state.d.ts +19 -0
- package/dist/schemas/state.d.ts.map +1 -0
- package/dist/schemas/state.js +12 -0
- package/dist/schemas/state.js.map +1 -0
- package/dist/skills/research/SKILL.md +156 -0
- package/dist/skills/review/SKILL.md +173 -0
- package/dist/skills/update/SKILL.md +200 -0
- package/dist/templates/agents/AGENTS.md +161 -0
- package/dist/templates/claude/CLAUDE.md +5 -0
- package/dist/templates/default.md +16 -0
- package/dist/templates/feedradar.md +165 -0
- package/dist/templates/routines/watch-daily.md +42 -0
- package/dist/templates/workflows/watch.yaml +70 -0
- package/package.json +73 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import { access, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import matter from "gray-matter";
|
|
4
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
5
|
+
import { getAgentAdapter } from "../agents/index.js";
|
|
6
|
+
import { getDefaultAgent, loadRadarConfig, RadarConfigError } from "../core/config.js";
|
|
7
|
+
import { loadItems } from "../core/items.js";
|
|
8
|
+
import { loadTemplate } from "../core/templates.js";
|
|
9
|
+
import { AgentIdSchema, ResearchFrontmatterSchema } from "../schemas/index.js";
|
|
10
|
+
/**
|
|
11
|
+
* gray-matter defaults to js-yaml which auto-converts ISO 8601 strings to
|
|
12
|
+
* `Date` objects. That breaks `ResearchFrontmatterSchema`, which expects
|
|
13
|
+
* timestamps as strings. Swap in the `yaml` v2 engine for parity with the
|
|
14
|
+
* `research` / `review` commands (see src/cli/research.ts for the same
|
|
15
|
+
* rationale).
|
|
16
|
+
*/
|
|
17
|
+
const matterOptions = {
|
|
18
|
+
engines: {
|
|
19
|
+
yaml: {
|
|
20
|
+
parse: (s) => parseYaml(s),
|
|
21
|
+
stringify: (data) => stringifyYaml(data),
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
function parseArgs(args) {
|
|
26
|
+
const out = {};
|
|
27
|
+
for (let i = 0; i < args.length; i++) {
|
|
28
|
+
const a = args[i];
|
|
29
|
+
if (a === "-h" || a === "--help") {
|
|
30
|
+
out.help = true;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (a === "--agent") {
|
|
34
|
+
out.agent = args[++i];
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (a === "--template") {
|
|
38
|
+
out.template = args[++i];
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (a?.startsWith("--")) {
|
|
42
|
+
throw new Error(`unknown option: ${a}`);
|
|
43
|
+
}
|
|
44
|
+
if (!out.researchId) {
|
|
45
|
+
out.researchId = a;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`unexpected positional argument: ${a}`);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function printHelp(log) {
|
|
53
|
+
log("Usage: radar update <research-id> [--agent <agent-id>] [--template <template-id>]");
|
|
54
|
+
log("");
|
|
55
|
+
log("Arguments:");
|
|
56
|
+
log(" <research-id> Research id (basename of research/<id>.md without .md)");
|
|
57
|
+
log("");
|
|
58
|
+
log("Options:");
|
|
59
|
+
log(" --agent <agent-id> claude-code | codex-cli | gemini-cli | copilot (default: claude-code)");
|
|
60
|
+
log(" --template <id> Template id under templates/ (default: default)");
|
|
61
|
+
log("");
|
|
62
|
+
log("Generates research/<base>_v<n+1>.md from the supplied predecessor id,");
|
|
63
|
+
log("writing `supersedes: <prev id>` into the new file's frontmatter. The");
|
|
64
|
+
log("predecessor file is never modified (immutable history, ADR-0003), and");
|
|
65
|
+
log("the linked items/<id>.yaml `status` is left untouched (ADR-0008).");
|
|
66
|
+
}
|
|
67
|
+
async function pathExists(p) {
|
|
68
|
+
try {
|
|
69
|
+
await access(p);
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resolve `<research-id>` to an absolute path under `research/`.
|
|
78
|
+
*
|
|
79
|
+
* Accepts both `<id>` and `<id>.md` for ergonomics. Rejects path-escape
|
|
80
|
+
* attempts (`..`, `/`, `\`) so a malicious id cannot read or write outside
|
|
81
|
+
* the workspace `research/` directory. Mirrors the pattern in
|
|
82
|
+
* `src/cli/review.ts`.
|
|
83
|
+
*/
|
|
84
|
+
function resolveResearchPath(cwd, researchId) {
|
|
85
|
+
const id = researchId.endsWith(".md") ? researchId.slice(0, -3) : researchId;
|
|
86
|
+
if (id.includes("/") || id.includes("\\") || id.includes("..")) {
|
|
87
|
+
throw new Error(`invalid <research-id>: '${researchId}'`);
|
|
88
|
+
}
|
|
89
|
+
return { id, path: join(cwd, "research", `${id}.md`) };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Parse the version suffix from a research id.
|
|
93
|
+
*
|
|
94
|
+
* Research filenames follow `<YYYYMMDD>_<slug>_v<N>` (ADR-0003). The base
|
|
95
|
+
* portion (everything before `_v<N>`) is preserved across versions; only the
|
|
96
|
+
* numeric suffix increments. Reject ids that do not match the contract so we
|
|
97
|
+
* fail fast rather than emit a file with a malformed name.
|
|
98
|
+
*/
|
|
99
|
+
function parseResearchId(id) {
|
|
100
|
+
const match = id.match(/^(.+)_v(\d+)$/);
|
|
101
|
+
if (!match) {
|
|
102
|
+
throw new Error(`invalid research id '${id}': expected <base>_v<N> (ADR-0003 filename format)`);
|
|
103
|
+
}
|
|
104
|
+
const version = Number.parseInt(match[2], 10);
|
|
105
|
+
if (!Number.isFinite(version) || version < 1) {
|
|
106
|
+
throw new Error(`invalid version suffix in research id '${id}'`);
|
|
107
|
+
}
|
|
108
|
+
return { base: match[1], version };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Locate the items/<sourceId>/<itemId>.yaml files for a research file's
|
|
112
|
+
* `itemIds`. Same shape as the helper in `src/cli/review.ts`; we re-implement
|
|
113
|
+
* here (rather than export the review version) because `update` does not need
|
|
114
|
+
* the snapshot/rollback machinery and a duplicated helper keeps the import
|
|
115
|
+
* graph local. Returns `null` when no items dir exists.
|
|
116
|
+
*/
|
|
117
|
+
async function findItemsForResearch(cwd, itemIds) {
|
|
118
|
+
const itemsDir = join(cwd, "items");
|
|
119
|
+
if (!(await pathExists(itemsDir)))
|
|
120
|
+
return [];
|
|
121
|
+
const allItems = await loadItems(itemsDir);
|
|
122
|
+
const out = [];
|
|
123
|
+
for (const id of itemIds) {
|
|
124
|
+
const match = allItems.find((i) => i.id === id);
|
|
125
|
+
if (match)
|
|
126
|
+
out.push(match);
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Implementation of `radar update <research-id>`.
|
|
132
|
+
*
|
|
133
|
+
* High-level flow (Phase 5, [#41](https://github.com/ozzy-labs/feedradar/issues/41)):
|
|
134
|
+
* 1. Parse + validate args (agent defaults to `claude-code`, template to `default`).
|
|
135
|
+
* 2. Resolve `research/<research-id>.md` and parse its frontmatter.
|
|
136
|
+
* 3. Compute the new id `<base>_v<n+1>` and refuse to overwrite an existing
|
|
137
|
+
* `research/<new-id>.md` (idempotent re-runs are user-facing errors).
|
|
138
|
+
* 4. Locate the linked items (read-only; their `status` is preserved per
|
|
139
|
+
* ADR-0008 / skill-design.md §8.4).
|
|
140
|
+
* 5. Reject `update` when any linked item is in `detected` / `dismissed`
|
|
141
|
+
* (no v1 research exists, or the user opted out of researching it).
|
|
142
|
+
* 6. Load `templates/<template-id>.md` (empty body when default is absent).
|
|
143
|
+
* 7. Invoke the adapter; the agent writes the new file at `outputPath`.
|
|
144
|
+
* 8. Re-read the new file, validate frontmatter against
|
|
145
|
+
* `ResearchFrontmatterSchema`, and assert v+1 invariants:
|
|
146
|
+
* - `id` matches the computed new id
|
|
147
|
+
* - `itemIds` / `templateId` / `createdAt` preserved from v(N)
|
|
148
|
+
* - `supersedes` equals the v(N) id
|
|
149
|
+
* - `reviewedAt` / `reviewedBy` are `null` (review state does NOT carry
|
|
150
|
+
* across versions per ADR-0003 / skill-design.md §8.3)
|
|
151
|
+
* 9. **No items.yaml mutation** — `update` is intentionally inert wrt item
|
|
152
|
+
* status. The v1 review event (if any) remains the authoritative record.
|
|
153
|
+
*
|
|
154
|
+
* Unlike `review`, `update` does not snapshot/restore: writing a new file is
|
|
155
|
+
* additive, so a failed adapter just leaves a missing / partial new file that
|
|
156
|
+
* the user can re-run after fixing the underlying issue. The predecessor file
|
|
157
|
+
* is never opened for writing.
|
|
158
|
+
*/
|
|
159
|
+
export async function runUpdate(args, options = {}) {
|
|
160
|
+
const cwd = options.cwd ?? process.cwd();
|
|
161
|
+
const log = options.io?.log ?? ((m) => console.log(m));
|
|
162
|
+
const warn = options.io?.warn ?? ((m) => console.warn(m));
|
|
163
|
+
const error = options.io?.error ?? ((m) => console.error(m));
|
|
164
|
+
let parsed;
|
|
165
|
+
try {
|
|
166
|
+
parsed = parseArgs(args);
|
|
167
|
+
}
|
|
168
|
+
catch (e) {
|
|
169
|
+
error(`update: ${e instanceof Error ? e.message : String(e)}`);
|
|
170
|
+
return 2;
|
|
171
|
+
}
|
|
172
|
+
if (parsed.help) {
|
|
173
|
+
printHelp(log);
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
if (!parsed.researchId) {
|
|
177
|
+
error("update: missing <research-id>");
|
|
178
|
+
printHelp(error);
|
|
179
|
+
return 2;
|
|
180
|
+
}
|
|
181
|
+
// Resolve the agent honoring the priority chain. We do not yet have a
|
|
182
|
+
// `defaultUpdateAgent` field in `radar.config.yaml` (out of scope for #41
|
|
183
|
+
// per the Issue body), so resolution is:
|
|
184
|
+
// explicit --agent > defaultResearchAgent > hard-coded claude-code
|
|
185
|
+
// We pick `research` as the borrowed default because `update` shares its
|
|
186
|
+
// SKILL body (rewrite-and-supersede strategy reuses the research procedure;
|
|
187
|
+
// skill-design.md §8.2). When `radar.config.yaml` grows a dedicated
|
|
188
|
+
// `defaultUpdateAgent`, this fallback chain becomes a thin pass-through.
|
|
189
|
+
let explicitAgent;
|
|
190
|
+
if (parsed.agent !== undefined) {
|
|
191
|
+
const agentResult = AgentIdSchema.safeParse(parsed.agent);
|
|
192
|
+
if (!agentResult.success) {
|
|
193
|
+
error(`update: invalid --agent '${parsed.agent}' (expected: claude-code | codex-cli | gemini-cli | copilot)`);
|
|
194
|
+
return 2;
|
|
195
|
+
}
|
|
196
|
+
explicitAgent = agentResult.data;
|
|
197
|
+
}
|
|
198
|
+
let agent;
|
|
199
|
+
try {
|
|
200
|
+
const config = await loadRadarConfig(cwd);
|
|
201
|
+
agent = await getDefaultAgent("research", {
|
|
202
|
+
explicit: explicitAgent,
|
|
203
|
+
configOverride: config,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
catch (e) {
|
|
207
|
+
if (e instanceof RadarConfigError) {
|
|
208
|
+
error(`update: ${e.message}`);
|
|
209
|
+
return 2;
|
|
210
|
+
}
|
|
211
|
+
throw e;
|
|
212
|
+
}
|
|
213
|
+
const templateId = parsed.template ?? "default";
|
|
214
|
+
// Resolve predecessor research path.
|
|
215
|
+
let prevId;
|
|
216
|
+
let prevPath;
|
|
217
|
+
try {
|
|
218
|
+
const resolved = resolveResearchPath(cwd, parsed.researchId);
|
|
219
|
+
prevId = resolved.id;
|
|
220
|
+
prevPath = resolved.path;
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
error(`update: ${e instanceof Error ? e.message : String(e)}`);
|
|
224
|
+
return 2;
|
|
225
|
+
}
|
|
226
|
+
if (!(await pathExists(prevPath))) {
|
|
227
|
+
error(`update: research file not found: ${prevPath}`);
|
|
228
|
+
return 1;
|
|
229
|
+
}
|
|
230
|
+
// Parse predecessor frontmatter.
|
|
231
|
+
let prevBody;
|
|
232
|
+
try {
|
|
233
|
+
prevBody = await readFile(prevPath, "utf8");
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
error(`update: failed to read research file: ${e instanceof Error ? e.message : String(e)}`);
|
|
237
|
+
return 1;
|
|
238
|
+
}
|
|
239
|
+
let prevFrontmatterRaw;
|
|
240
|
+
try {
|
|
241
|
+
prevFrontmatterRaw = matter(prevBody, matterOptions).data;
|
|
242
|
+
}
|
|
243
|
+
catch (e) {
|
|
244
|
+
error(`update: failed to parse frontmatter: ${e instanceof Error ? e.message : String(e)}`);
|
|
245
|
+
return 1;
|
|
246
|
+
}
|
|
247
|
+
const prevResult = ResearchFrontmatterSchema.safeParse(prevFrontmatterRaw);
|
|
248
|
+
if (!prevResult.success) {
|
|
249
|
+
error(`update: predecessor frontmatter does not match ResearchFrontmatterSchema:`);
|
|
250
|
+
for (const issue of prevResult.error.issues) {
|
|
251
|
+
error(` - ${issue.path.join(".") || "<root>"}: ${issue.message}`);
|
|
252
|
+
}
|
|
253
|
+
return 1;
|
|
254
|
+
}
|
|
255
|
+
const prevFm = prevResult.data;
|
|
256
|
+
// Sanity check: the id in the frontmatter must match the filename id so
|
|
257
|
+
// `supersedes: <prev id>` we write below points at a real predecessor.
|
|
258
|
+
if (prevFm.id !== prevId) {
|
|
259
|
+
error(`update: predecessor frontmatter id '${prevFm.id}' does not match filename id '${prevId}'`);
|
|
260
|
+
return 1;
|
|
261
|
+
}
|
|
262
|
+
// Compute the new id: increment the version suffix on the predecessor id.
|
|
263
|
+
let base;
|
|
264
|
+
let prevVersion;
|
|
265
|
+
try {
|
|
266
|
+
const parsedId = parseResearchId(prevId);
|
|
267
|
+
base = parsedId.base;
|
|
268
|
+
prevVersion = parsedId.version;
|
|
269
|
+
}
|
|
270
|
+
catch (e) {
|
|
271
|
+
error(`update: ${e instanceof Error ? e.message : String(e)}`);
|
|
272
|
+
return 1;
|
|
273
|
+
}
|
|
274
|
+
const newVersion = prevVersion + 1;
|
|
275
|
+
const newId = `${base}_v${newVersion}`;
|
|
276
|
+
const outputPath = join(cwd, "research", `${newId}.md`);
|
|
277
|
+
if (await pathExists(outputPath)) {
|
|
278
|
+
error(`update: ${outputPath} already exists. v${newVersion} was already generated — pick a different predecessor or remove the stale file.`);
|
|
279
|
+
return 1;
|
|
280
|
+
}
|
|
281
|
+
// Resolve linked items. They are needed by the adapter (passed through as
|
|
282
|
+
// context) but `update` does NOT mutate them — per ADR-0008 / ADR-0003 the
|
|
283
|
+
// items.yaml `status` is invariant under update.
|
|
284
|
+
const linkedItems = await findItemsForResearch(cwd, prevFm.itemIds);
|
|
285
|
+
if (linkedItems.length === 0) {
|
|
286
|
+
error(`update: no items/<id>.yaml found for itemIds=[${prevFm.itemIds.join(", ")}] referenced by ${prevFm.id}`);
|
|
287
|
+
return 1;
|
|
288
|
+
}
|
|
289
|
+
if (linkedItems.length !== prevFm.itemIds.length) {
|
|
290
|
+
const found = new Set(linkedItems.map((i) => i.id));
|
|
291
|
+
const missing = prevFm.itemIds.filter((id) => !found.has(id));
|
|
292
|
+
error(`update: ${missing.length} linked item(s) not found: ${missing.join(", ")} (referenced by ${prevFm.id})`);
|
|
293
|
+
return 1;
|
|
294
|
+
}
|
|
295
|
+
// Per skill-design.md §8.4, `update` requires an existing research file —
|
|
296
|
+
// i.e. the item must have been at `researched` or `reviewed` at some point.
|
|
297
|
+
// We block `detected` / `dismissed` because there is no v1 to supersede in
|
|
298
|
+
// those states (research never happened, or the user opted out).
|
|
299
|
+
const invalidStatus = linkedItems.filter((i) => i.status !== "researched" && i.status !== "reviewed");
|
|
300
|
+
if (invalidStatus.length > 0) {
|
|
301
|
+
error(`update: linked items must be in status 'researched' or 'reviewed'. Offenders: ${invalidStatus
|
|
302
|
+
.map((i) => `${i.id}=${i.status}`)
|
|
303
|
+
.join(", ")}`);
|
|
304
|
+
return 1;
|
|
305
|
+
}
|
|
306
|
+
// Surface any prompt-injection pre-filter hits recorded by the watcher
|
|
307
|
+
// (ADR-0009 M1a / M5a — Adopt). Audit-only: `update` still generates a v+1
|
|
308
|
+
// from possibly tainted content, but the user gets the audit signal before
|
|
309
|
+
// spending tokens on the regeneration pass.
|
|
310
|
+
for (const linked of linkedItems) {
|
|
311
|
+
if (linked.injectionFlags.length > 0) {
|
|
312
|
+
warn(`update: item '${linked.id}' has ${linked.injectionFlags.length} injection flag(s): ${linked.injectionFlags.join(", ")} (audit-only; v+1 will regenerate research from the same source content)`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Load template.
|
|
316
|
+
const templatesDir = join(cwd, "templates");
|
|
317
|
+
let template;
|
|
318
|
+
try {
|
|
319
|
+
template = await loadTemplate(templateId, templatesDir);
|
|
320
|
+
}
|
|
321
|
+
catch (e) {
|
|
322
|
+
error(`update: ${e instanceof Error ? e.message : String(e)}`);
|
|
323
|
+
return 1;
|
|
324
|
+
}
|
|
325
|
+
log(`update: invoking ${agent} adapter for research '${prevFm.id}' -> ${base}_v${newVersion}.md`);
|
|
326
|
+
// Invoke adapter. We do not snapshot the predecessor file: the adapter
|
|
327
|
+
// writes a new file at outputPath; if it fails, no rollback is necessary
|
|
328
|
+
// because the predecessor was never touched and the new file simply does
|
|
329
|
+
// not appear (or is partial, in which case the user removes it and retries).
|
|
330
|
+
const adapter = getAgentAdapter(agent);
|
|
331
|
+
try {
|
|
332
|
+
await adapter.update({
|
|
333
|
+
agent,
|
|
334
|
+
templateId,
|
|
335
|
+
templateBody: template.body,
|
|
336
|
+
prevResearch: {
|
|
337
|
+
frontmatter: prevFm,
|
|
338
|
+
body: prevBody,
|
|
339
|
+
},
|
|
340
|
+
items: linkedItems,
|
|
341
|
+
outputPath,
|
|
342
|
+
cwd,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
catch (e) {
|
|
346
|
+
error(`update: adapter failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
347
|
+
return 1;
|
|
348
|
+
}
|
|
349
|
+
// Re-read and validate the produced file.
|
|
350
|
+
if (!(await pathExists(outputPath))) {
|
|
351
|
+
error(`update: adapter completed but did not write ${outputPath} (agent ignored the output path?)`);
|
|
352
|
+
return 1;
|
|
353
|
+
}
|
|
354
|
+
let newBody;
|
|
355
|
+
try {
|
|
356
|
+
newBody = await readFile(outputPath, "utf8");
|
|
357
|
+
}
|
|
358
|
+
catch (e) {
|
|
359
|
+
error(`update: failed to read generated report: ${e instanceof Error ? e.message : String(e)}`);
|
|
360
|
+
return 1;
|
|
361
|
+
}
|
|
362
|
+
let newFrontmatterRaw;
|
|
363
|
+
try {
|
|
364
|
+
newFrontmatterRaw = matter(newBody, matterOptions).data;
|
|
365
|
+
}
|
|
366
|
+
catch (e) {
|
|
367
|
+
error(`update: failed to parse new frontmatter: ${e instanceof Error ? e.message : String(e)}`);
|
|
368
|
+
return 1;
|
|
369
|
+
}
|
|
370
|
+
const newResult = ResearchFrontmatterSchema.safeParse(newFrontmatterRaw);
|
|
371
|
+
if (!newResult.success) {
|
|
372
|
+
error(`update: new frontmatter does not match ResearchFrontmatterSchema:`);
|
|
373
|
+
for (const issue of newResult.error.issues) {
|
|
374
|
+
error(` - ${issue.path.join(".") || "<root>"}: ${issue.message}`);
|
|
375
|
+
}
|
|
376
|
+
return 1;
|
|
377
|
+
}
|
|
378
|
+
const newFm = newResult.data;
|
|
379
|
+
// v+1 invariants. We enforce these in the CLI so a misbehaving agent
|
|
380
|
+
// cannot silently corrupt the versioning chain. Drift is collected before
|
|
381
|
+
// we attempt repair, then a single corrected file is written.
|
|
382
|
+
const drift = [];
|
|
383
|
+
if (newFm.id !== newId)
|
|
384
|
+
drift.push(`id (${newId} expected, got ${newFm.id})`);
|
|
385
|
+
if (JSON.stringify(newFm.itemIds) !== JSON.stringify(prevFm.itemIds))
|
|
386
|
+
drift.push(`itemIds (${prevFm.itemIds.join(",")} -> ${newFm.itemIds.join(",")})`);
|
|
387
|
+
if (newFm.templateId !== prevFm.templateId)
|
|
388
|
+
drift.push(`templateId (${prevFm.templateId} -> ${newFm.templateId})`);
|
|
389
|
+
if (newFm.createdAt !== prevFm.createdAt)
|
|
390
|
+
drift.push(`createdAt (${prevFm.createdAt} -> ${newFm.createdAt})`);
|
|
391
|
+
if (newFm.supersedes !== prevFm.id)
|
|
392
|
+
drift.push(`supersedes (expected '${prevFm.id}', got '${newFm.supersedes ?? "null"}')`);
|
|
393
|
+
if (newFm.reviewedAt !== null)
|
|
394
|
+
drift.push(`reviewedAt (expected null on v+1, got ${newFm.reviewedAt})`);
|
|
395
|
+
if (newFm.reviewedBy !== null)
|
|
396
|
+
drift.push(`reviewedBy (expected null on v+1, got ${newFm.reviewedBy})`);
|
|
397
|
+
// The agent field may legitimately differ from v(N) when the user runs
|
|
398
|
+
// `update` with a different --agent (skill-design.md §8.3 marks `agent` as
|
|
399
|
+
// mutable across versions). We assert it matches the invoking agent so a
|
|
400
|
+
// misbehaving adapter cannot lie about who produced the v+1.
|
|
401
|
+
if (newFm.agent !== agent)
|
|
402
|
+
drift.push(`agent (expected '${agent}' for v+1 write, got '${newFm.agent}')`);
|
|
403
|
+
if (drift.length > 0) {
|
|
404
|
+
warn(`update: agent emitted drift in frontmatter; auto-correcting: ${drift.join("; ")}`);
|
|
405
|
+
const parsedDoc = matter(newBody, matterOptions);
|
|
406
|
+
const corrected = {
|
|
407
|
+
id: newId,
|
|
408
|
+
itemIds: prevFm.itemIds,
|
|
409
|
+
agent,
|
|
410
|
+
templateId: prevFm.templateId,
|
|
411
|
+
createdAt: prevFm.createdAt,
|
|
412
|
+
updatedAt: newFm.updatedAt ?? new Date().toISOString(),
|
|
413
|
+
reviewedAt: null,
|
|
414
|
+
reviewedBy: null,
|
|
415
|
+
supersedes: prevFm.id,
|
|
416
|
+
};
|
|
417
|
+
const rewritten = matter.stringify(parsedDoc.content, corrected, matterOptions);
|
|
418
|
+
await writeFile(outputPath, rewritten, "utf8");
|
|
419
|
+
}
|
|
420
|
+
log(`update: wrote ${outputPath}`);
|
|
421
|
+
log(`update: supersedes ${prevFm.id} (items.yaml status unchanged per ADR-0008)`);
|
|
422
|
+
return 0;
|
|
423
|
+
}
|
|
424
|
+
export const updateCommand = {
|
|
425
|
+
name: "update",
|
|
426
|
+
summary: "Refresh existing research reports against the latest items",
|
|
427
|
+
run: (args) => runUpdate(args),
|
|
428
|
+
};
|
|
429
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/cli/update.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Command } from "./index.js";
|
|
2
|
+
export interface WatchIO {
|
|
3
|
+
log?: (message: string) => void;
|
|
4
|
+
warn?: (message: string) => void;
|
|
5
|
+
error?: (message: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface WatchCommandOptions {
|
|
8
|
+
cwd?: string;
|
|
9
|
+
io?: WatchIO;
|
|
10
|
+
/** Test seam: override the adapter HTTP fetcher. */
|
|
11
|
+
fetch?: typeof globalThis.fetch;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Implementation of `watch run`.
|
|
15
|
+
*
|
|
16
|
+
* Wraps `watchRun` from `core/watcher` with CLI-level concerns (argument
|
|
17
|
+
* parsing, exit code, error sink wiring). The exit code is non-zero when
|
|
18
|
+
* any source errored, so CI pipelines can surface partial failures.
|
|
19
|
+
*/
|
|
20
|
+
export declare function runWatch(args: string[], options?: WatchCommandOptions): Promise<number>;
|
|
21
|
+
export declare const watchCommand: Command;
|
|
22
|
+
//# sourceMappingURL=watch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/cli/watch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,OAAO;IACtB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AA2CD;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4CjG;AAED,eAAO,MAAM,YAAY,EAAE,OAgB1B,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { watchRun } from "../core/watcher.js";
|
|
2
|
+
function parseRunArgs(args) {
|
|
3
|
+
const out = {};
|
|
4
|
+
for (let i = 0; i < args.length; i++) {
|
|
5
|
+
const a = args[i];
|
|
6
|
+
if (a === "-h" || a === "--help") {
|
|
7
|
+
out.help = true;
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
if (a === "--source") {
|
|
11
|
+
out.sourceId = args[++i];
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (a === "--bootstrap") {
|
|
15
|
+
out.bootstrap = true;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (a?.startsWith("--")) {
|
|
19
|
+
throw new Error(`unknown option: ${a}`);
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`unexpected argument: ${a}`);
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
function printWatchHelp(log) {
|
|
26
|
+
log("Usage: radar watch <run> [options]");
|
|
27
|
+
log("");
|
|
28
|
+
log("Subcommands:");
|
|
29
|
+
log(" run [--source <id>] [--bootstrap] Fetch sources and produce items");
|
|
30
|
+
log("");
|
|
31
|
+
log("Options for run:");
|
|
32
|
+
log(" --source <id> Limit the run to a single source id");
|
|
33
|
+
log(" --bootstrap Seed lastSeenIds without emitting items (suppress initial noise)");
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Implementation of `watch run`.
|
|
37
|
+
*
|
|
38
|
+
* Wraps `watchRun` from `core/watcher` with CLI-level concerns (argument
|
|
39
|
+
* parsing, exit code, error sink wiring). The exit code is non-zero when
|
|
40
|
+
* any source errored, so CI pipelines can surface partial failures.
|
|
41
|
+
*/
|
|
42
|
+
export async function runWatch(args, options = {}) {
|
|
43
|
+
const cwd = options.cwd ?? process.cwd();
|
|
44
|
+
const log = options.io?.log ?? ((m) => console.log(m));
|
|
45
|
+
const warn = options.io?.warn ?? ((m) => console.warn(m));
|
|
46
|
+
const error = options.io?.error ?? ((m) => console.error(m));
|
|
47
|
+
let parsed;
|
|
48
|
+
try {
|
|
49
|
+
parsed = parseRunArgs(args);
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
error(`watch run: ${e instanceof Error ? e.message : String(e)}`);
|
|
53
|
+
return 2;
|
|
54
|
+
}
|
|
55
|
+
if (parsed.help) {
|
|
56
|
+
printWatchHelp(log);
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
let result;
|
|
60
|
+
try {
|
|
61
|
+
result = await watchRun({
|
|
62
|
+
cwd,
|
|
63
|
+
sourceId: parsed.sourceId,
|
|
64
|
+
bootstrap: parsed.bootstrap,
|
|
65
|
+
fetch: options.fetch,
|
|
66
|
+
log,
|
|
67
|
+
warn,
|
|
68
|
+
error,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
error(`watch run: ${e instanceof Error ? e.message : String(e)}`);
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
const totalDetected = Object.values(result.detected).reduce((acc, list) => acc + list.length, 0);
|
|
76
|
+
if (parsed.bootstrap) {
|
|
77
|
+
log(`watch run: bootstrap complete (${Object.keys(result.states).length} sources)`);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
log(`watch run: ${totalDetected} new item(s) across ${Object.keys(result.states).length} source(s)`);
|
|
81
|
+
}
|
|
82
|
+
return result.errors.length > 0 ? 1 : 0;
|
|
83
|
+
}
|
|
84
|
+
export const watchCommand = {
|
|
85
|
+
name: "watch",
|
|
86
|
+
summary: "Fetch sources and produce filtered items (run)",
|
|
87
|
+
run: async (args) => {
|
|
88
|
+
const [sub, ...rest] = args;
|
|
89
|
+
if (!sub || sub === "-h" || sub === "--help" || sub === "help") {
|
|
90
|
+
printWatchHelp((m) => console.log(m));
|
|
91
|
+
return sub ? 0 : 2;
|
|
92
|
+
}
|
|
93
|
+
if (sub === "run") {
|
|
94
|
+
return runWatch(rest);
|
|
95
|
+
}
|
|
96
|
+
console.error(`watch: unknown subcommand '${sub}'`);
|
|
97
|
+
printWatchHelp((m) => console.error(m));
|
|
98
|
+
return 2;
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=watch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/cli/watch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { AgentId, ConfigurableCommand, RadarConfig } from "../schemas/index.js";
|
|
2
|
+
/** File name of the workspace config, located at the workspace root. */
|
|
3
|
+
export declare const CONFIG_FILENAME = "radar.config.yaml";
|
|
4
|
+
/**
|
|
5
|
+
* Hard-coded fallback when neither `--agent` nor `radar.config.yaml` sets a
|
|
6
|
+
* default. Documented in user-guide.md and architecture.md (ADR-0001).
|
|
7
|
+
*/
|
|
8
|
+
export declare const HARDCODED_DEFAULT_AGENT: AgentId;
|
|
9
|
+
/**
|
|
10
|
+
* Error raised when `radar.config.yaml` exists but fails to parse or violates
|
|
11
|
+
* `RadarConfigSchema`. Throws (rather than fall-back to defaults) because the
|
|
12
|
+
* user explicitly authored a config and silently ignoring it would surprise
|
|
13
|
+
* them and mask typos like `defaultResarchAgent`.
|
|
14
|
+
*/
|
|
15
|
+
export declare class RadarConfigError extends Error {
|
|
16
|
+
constructor(message: string);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Load `radar.config.yaml` from a workspace root.
|
|
20
|
+
*
|
|
21
|
+
* Behavior:
|
|
22
|
+
* - File missing -> returns an empty config (`{}`).
|
|
23
|
+
* - File present and valid -> returns the parsed `RadarConfig`.
|
|
24
|
+
* - File present but malformed (YAML parse error / schema violation)
|
|
25
|
+
* -> throws `RadarConfigError` with a contextual message.
|
|
26
|
+
*
|
|
27
|
+
* Returning `{}` for the missing-file case lets callers treat absence as
|
|
28
|
+
* "use the hard-coded default" without an extra `await pathExists` round-trip.
|
|
29
|
+
*/
|
|
30
|
+
export declare function loadRadarConfig(cwd: string): Promise<RadarConfig>;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the agent to use for a given command, honoring the priority order
|
|
33
|
+
* mandated by [#30]:
|
|
34
|
+
*
|
|
35
|
+
* explicit `--agent` > `radar.config.yaml` default > hard-coded default
|
|
36
|
+
*
|
|
37
|
+
* `explicit` is the value the caller pulled from the CLI args (already
|
|
38
|
+
* validated against `AgentIdSchema`); pass `undefined` when the user did not
|
|
39
|
+
* supply `--agent`. The config is read fresh on each call so the resolver
|
|
40
|
+
* stays stateless — the caller is free to load once per command and pass it
|
|
41
|
+
* in via `configOverride` when chaining multiple resolves.
|
|
42
|
+
*
|
|
43
|
+
* Adding a new configurable command:
|
|
44
|
+
* 1. Extend `ConfigurableCommand` in `src/schemas/config.ts`.
|
|
45
|
+
* 2. Add the corresponding field to `RadarConfigSchema`.
|
|
46
|
+
* 3. Add a branch to the switch below.
|
|
47
|
+
*
|
|
48
|
+
* The switch is exhaustive (TypeScript flags missing branches via the
|
|
49
|
+
* `never` arm), so step 3 is enforced at compile time.
|
|
50
|
+
*/
|
|
51
|
+
export interface GetDefaultAgentOptions {
|
|
52
|
+
/** Explicit `--agent <id>` value, if the user supplied one. */
|
|
53
|
+
explicit?: AgentId;
|
|
54
|
+
/** Workspace root containing `radar.config.yaml`. Defaults to `process.cwd()`. */
|
|
55
|
+
cwd?: string;
|
|
56
|
+
/** Pre-loaded config; skips disk read when supplied. */
|
|
57
|
+
configOverride?: RadarConfig;
|
|
58
|
+
}
|
|
59
|
+
export declare function getDefaultAgent(command: ConfigurableCommand, options?: GetDefaultAgentOptions): Promise<AgentId>;
|
|
60
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGrF,wEAAwE;AACxE,eAAO,MAAM,eAAe,sBAAsB,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,OAAuB,CAAC;AAW9D;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAgCvE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,sBAAsB;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,cAAc,CAAC,EAAE,WAAW,CAAC;CAC9B;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,OAAO,CAAC,CAUlB"}
|