@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,39 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Schema for `radar.config.yaml`, the workspace-level configuration file
|
|
4
|
+
* placed at the workspace root (next to `sources/`, `items/`, etc.).
|
|
5
|
+
*
|
|
6
|
+
* Fields are intentionally minimal in Phase 2; the config is designed to grow
|
|
7
|
+
* one field at a time as new defaults are needed (per [#25] Phase 2 epic).
|
|
8
|
+
*
|
|
9
|
+
* - `defaultResearchAgent`: agent used when `radar research` is run
|
|
10
|
+
* without an explicit `--agent`. Falls through to the hard-coded default
|
|
11
|
+
* (`claude-code`) when unset.
|
|
12
|
+
* - `defaultReviewAgent`: same idea for `radar review`.
|
|
13
|
+
*
|
|
14
|
+
* Out-of-scope (tracked separately):
|
|
15
|
+
* - default agent for `update` (Phase 5)
|
|
16
|
+
* - agent-specific knobs (timeout / API key / etc.)
|
|
17
|
+
*/
|
|
18
|
+
export declare const RadarConfigSchema: z.ZodObject<{
|
|
19
|
+
defaultResearchAgent: z.ZodOptional<z.ZodEnum<{
|
|
20
|
+
"claude-code": "claude-code";
|
|
21
|
+
"codex-cli": "codex-cli";
|
|
22
|
+
"gemini-cli": "gemini-cli";
|
|
23
|
+
copilot: "copilot";
|
|
24
|
+
}>>;
|
|
25
|
+
defaultReviewAgent: z.ZodOptional<z.ZodEnum<{
|
|
26
|
+
"claude-code": "claude-code";
|
|
27
|
+
"codex-cli": "codex-cli";
|
|
28
|
+
"gemini-cli": "gemini-cli";
|
|
29
|
+
copilot: "copilot";
|
|
30
|
+
}>>;
|
|
31
|
+
}, z.core.$strip>;
|
|
32
|
+
export type RadarConfig = z.infer<typeof RadarConfigSchema>;
|
|
33
|
+
/**
|
|
34
|
+
* The set of commands for which a default agent can be configured. New
|
|
35
|
+
* commands are added here (rather than to a free-form `string`) so that the
|
|
36
|
+
* resolver below stays exhaustive and TypeScript flags missing branches.
|
|
37
|
+
*/
|
|
38
|
+
export type ConfigurableCommand = "research" | "review";
|
|
39
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/schemas/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;iBAG5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,QAAQ,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { AgentIdSchema } from "./research.js";
|
|
3
|
+
/**
|
|
4
|
+
* Schema for `radar.config.yaml`, the workspace-level configuration file
|
|
5
|
+
* placed at the workspace root (next to `sources/`, `items/`, etc.).
|
|
6
|
+
*
|
|
7
|
+
* Fields are intentionally minimal in Phase 2; the config is designed to grow
|
|
8
|
+
* one field at a time as new defaults are needed (per [#25] Phase 2 epic).
|
|
9
|
+
*
|
|
10
|
+
* - `defaultResearchAgent`: agent used when `radar research` is run
|
|
11
|
+
* without an explicit `--agent`. Falls through to the hard-coded default
|
|
12
|
+
* (`claude-code`) when unset.
|
|
13
|
+
* - `defaultReviewAgent`: same idea for `radar review`.
|
|
14
|
+
*
|
|
15
|
+
* Out-of-scope (tracked separately):
|
|
16
|
+
* - default agent for `update` (Phase 5)
|
|
17
|
+
* - agent-specific knobs (timeout / API key / etc.)
|
|
18
|
+
*/
|
|
19
|
+
export const RadarConfigSchema = z.object({
|
|
20
|
+
defaultResearchAgent: AgentIdSchema.optional(),
|
|
21
|
+
defaultReviewAgent: AgentIdSchema.optional(),
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/schemas/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,oBAAoB,EAAE,aAAa,CAAC,QAAQ,EAAE;IAC9C,kBAAkB,EAAE,aAAa,CAAC,QAAQ,EAAE;CAC7C,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Item status state machine (ADR-0008).
|
|
4
|
+
*
|
|
5
|
+
* detected ──► (dismissed | researched) ──► reviewed
|
|
6
|
+
*
|
|
7
|
+
* - `detected`: watch run emitted the item after filter
|
|
8
|
+
* - `dismissed`: user decided not to research (terminal)
|
|
9
|
+
* - `researched`: research report written
|
|
10
|
+
* - `reviewed`: research report reviewed (terminal happy path)
|
|
11
|
+
*/
|
|
12
|
+
export declare const ItemStatusSchema: z.ZodEnum<{
|
|
13
|
+
detected: "detected";
|
|
14
|
+
dismissed: "dismissed";
|
|
15
|
+
researched: "researched";
|
|
16
|
+
reviewed: "reviewed";
|
|
17
|
+
}>;
|
|
18
|
+
export type ItemStatus = z.infer<typeof ItemStatusSchema>;
|
|
19
|
+
export declare const ItemSchema: z.ZodObject<{
|
|
20
|
+
id: z.ZodString;
|
|
21
|
+
sourceId: z.ZodString;
|
|
22
|
+
title: z.ZodString;
|
|
23
|
+
url: z.ZodString;
|
|
24
|
+
publishedAt: z.ZodOptional<z.ZodString>;
|
|
25
|
+
fetchedAt: z.ZodString;
|
|
26
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
27
|
+
raw: z.ZodOptional<z.ZodUnknown>;
|
|
28
|
+
matchedKeywords: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
29
|
+
status: z.ZodDefault<z.ZodEnum<{
|
|
30
|
+
detected: "detected";
|
|
31
|
+
dismissed: "dismissed";
|
|
32
|
+
researched: "researched";
|
|
33
|
+
reviewed: "reviewed";
|
|
34
|
+
}>>;
|
|
35
|
+
injectionFlags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
36
|
+
}, z.core.$strip>;
|
|
37
|
+
export type Item = z.infer<typeof ItemSchema>;
|
|
38
|
+
//# sourceMappingURL=item.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../src/schemas/item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB;;;;;EAA8D,CAAC;AAC5F,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;iBAoBrB,CAAC;AACH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Item status state machine (ADR-0008).
|
|
4
|
+
*
|
|
5
|
+
* detected ──► (dismissed | researched) ──► reviewed
|
|
6
|
+
*
|
|
7
|
+
* - `detected`: watch run emitted the item after filter
|
|
8
|
+
* - `dismissed`: user decided not to research (terminal)
|
|
9
|
+
* - `researched`: research report written
|
|
10
|
+
* - `reviewed`: research report reviewed (terminal happy path)
|
|
11
|
+
*/
|
|
12
|
+
export const ItemStatusSchema = z.enum(["detected", "dismissed", "researched", "reviewed"]);
|
|
13
|
+
export const ItemSchema = z.object({
|
|
14
|
+
id: z.string().min(1),
|
|
15
|
+
sourceId: z.string().min(1),
|
|
16
|
+
title: z.string(),
|
|
17
|
+
url: z.string().url(),
|
|
18
|
+
publishedAt: z.string().datetime().optional(),
|
|
19
|
+
fetchedAt: z.string().datetime(),
|
|
20
|
+
summary: z.string().optional(),
|
|
21
|
+
raw: z.unknown().optional(),
|
|
22
|
+
matchedKeywords: z.array(z.string()).default([]),
|
|
23
|
+
status: ItemStatusSchema.default("detected"),
|
|
24
|
+
/**
|
|
25
|
+
* Prompt-injection pattern labels that fired when the watcher scanned
|
|
26
|
+
* `title` / `summary` / `raw` (ADR-0009 M1a — Adopt). Audit-only: a
|
|
27
|
+
* non-empty value does NOT change `status`, sanitize content, or block
|
|
28
|
+
* downstream commands. Existing items written before this field landed
|
|
29
|
+
* default to `[]` thanks to the schema default, so load-side compat is
|
|
30
|
+
* automatic.
|
|
31
|
+
*/
|
|
32
|
+
injectionFlags: z.array(z.string()).default([]),
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=item.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"item.js","sourceRoot":"","sources":["../../src/schemas/item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;AAG5F,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACrB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC7C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,GAAG,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC3B,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAChD,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,UAAU,CAAC;IAC5C;;;;;;;OAOG;IACH,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAChD,CAAC,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const AgentIdSchema: z.ZodEnum<{
|
|
3
|
+
"claude-code": "claude-code";
|
|
4
|
+
"codex-cli": "codex-cli";
|
|
5
|
+
"gemini-cli": "gemini-cli";
|
|
6
|
+
copilot: "copilot";
|
|
7
|
+
}>;
|
|
8
|
+
export type AgentId = z.infer<typeof AgentIdSchema>;
|
|
9
|
+
/**
|
|
10
|
+
* Frontmatter persisted on disk in `research/<id>.md`.
|
|
11
|
+
*
|
|
12
|
+
* `outputPath` is intentionally **omitted** here: the path is encoded in the
|
|
13
|
+
* filename itself, so storing it in the frontmatter would be redundant and
|
|
14
|
+
* invite drift between the two. Anything that needs the path can construct it
|
|
15
|
+
* from `id` (filename) or read it from the file system. This split keeps the
|
|
16
|
+
* persisted artifact ADR-0003-compliant while letting in-memory orchestration
|
|
17
|
+
* carry the resolved path through `Research`.
|
|
18
|
+
*
|
|
19
|
+
* Phase 1 contract: `reviewedAt` / `reviewedBy` are **always written as
|
|
20
|
+
* `null`** by the `research` command. They become non-null when Phase 2's
|
|
21
|
+
* `review` command stamps the file. See ADR-0003.
|
|
22
|
+
*
|
|
23
|
+
* Phase 5 contract: `supersedes` records the lineage between research file
|
|
24
|
+
* versions. v1 files write `null`; v(N+1) files write the previous version's
|
|
25
|
+
* `id` (filename without the `.md` extension). The field is `null`-defaulted
|
|
26
|
+
* so existing v1 frontmatter generated before Phase 5 (which omits the field
|
|
27
|
+
* entirely) parses without violating the schema. See ADR-0003.
|
|
28
|
+
*/
|
|
29
|
+
export declare const ResearchFrontmatterSchema: z.ZodObject<{
|
|
30
|
+
id: z.ZodString;
|
|
31
|
+
itemIds: z.ZodArray<z.ZodString>;
|
|
32
|
+
agent: z.ZodEnum<{
|
|
33
|
+
"claude-code": "claude-code";
|
|
34
|
+
"codex-cli": "codex-cli";
|
|
35
|
+
"gemini-cli": "gemini-cli";
|
|
36
|
+
copilot: "copilot";
|
|
37
|
+
}>;
|
|
38
|
+
templateId: z.ZodString;
|
|
39
|
+
createdAt: z.ZodString;
|
|
40
|
+
updatedAt: z.ZodNullable<z.ZodString>;
|
|
41
|
+
reviewedAt: z.ZodNullable<z.ZodString>;
|
|
42
|
+
reviewedBy: z.ZodNullable<z.ZodEnum<{
|
|
43
|
+
"claude-code": "claude-code";
|
|
44
|
+
"codex-cli": "codex-cli";
|
|
45
|
+
"gemini-cli": "gemini-cli";
|
|
46
|
+
copilot: "copilot";
|
|
47
|
+
}>>;
|
|
48
|
+
supersedes: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
49
|
+
}, z.core.$strip>;
|
|
50
|
+
export type ResearchFrontmatter = z.infer<typeof ResearchFrontmatterSchema>;
|
|
51
|
+
/**
|
|
52
|
+
* In-memory Research record used by the orchestration layer.
|
|
53
|
+
*
|
|
54
|
+
* Extends `ResearchFrontmatter` with `outputPath`, which the CLI needs to
|
|
55
|
+
* pass through to the agent adapter (so the agent knows where to write) and
|
|
56
|
+
* back to the caller (so the CLI can verify the file after the adapter
|
|
57
|
+
* returns). Persisted frontmatter intentionally drops this field.
|
|
58
|
+
*/
|
|
59
|
+
export declare const ResearchSchema: z.ZodObject<{
|
|
60
|
+
id: z.ZodString;
|
|
61
|
+
itemIds: z.ZodArray<z.ZodString>;
|
|
62
|
+
agent: z.ZodEnum<{
|
|
63
|
+
"claude-code": "claude-code";
|
|
64
|
+
"codex-cli": "codex-cli";
|
|
65
|
+
"gemini-cli": "gemini-cli";
|
|
66
|
+
copilot: "copilot";
|
|
67
|
+
}>;
|
|
68
|
+
templateId: z.ZodString;
|
|
69
|
+
createdAt: z.ZodString;
|
|
70
|
+
updatedAt: z.ZodNullable<z.ZodString>;
|
|
71
|
+
reviewedAt: z.ZodNullable<z.ZodString>;
|
|
72
|
+
reviewedBy: z.ZodNullable<z.ZodEnum<{
|
|
73
|
+
"claude-code": "claude-code";
|
|
74
|
+
"codex-cli": "codex-cli";
|
|
75
|
+
"gemini-cli": "gemini-cli";
|
|
76
|
+
copilot: "copilot";
|
|
77
|
+
}>>;
|
|
78
|
+
supersedes: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
79
|
+
outputPath: z.ZodString;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
export type Research = z.infer<typeof ResearchSchema>;
|
|
82
|
+
//# sourceMappingURL=research.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"research.d.ts","sourceRoot":"","sources":["../../src/schemas/research.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,aAAa;;;;;EAAgE,CAAC;AAC3F,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;iBAUpC,CAAC;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;iBAEzB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const AgentIdSchema = z.enum(["claude-code", "codex-cli", "gemini-cli", "copilot"]);
|
|
3
|
+
/**
|
|
4
|
+
* Frontmatter persisted on disk in `research/<id>.md`.
|
|
5
|
+
*
|
|
6
|
+
* `outputPath` is intentionally **omitted** here: the path is encoded in the
|
|
7
|
+
* filename itself, so storing it in the frontmatter would be redundant and
|
|
8
|
+
* invite drift between the two. Anything that needs the path can construct it
|
|
9
|
+
* from `id` (filename) or read it from the file system. This split keeps the
|
|
10
|
+
* persisted artifact ADR-0003-compliant while letting in-memory orchestration
|
|
11
|
+
* carry the resolved path through `Research`.
|
|
12
|
+
*
|
|
13
|
+
* Phase 1 contract: `reviewedAt` / `reviewedBy` are **always written as
|
|
14
|
+
* `null`** by the `research` command. They become non-null when Phase 2's
|
|
15
|
+
* `review` command stamps the file. See ADR-0003.
|
|
16
|
+
*
|
|
17
|
+
* Phase 5 contract: `supersedes` records the lineage between research file
|
|
18
|
+
* versions. v1 files write `null`; v(N+1) files write the previous version's
|
|
19
|
+
* `id` (filename without the `.md` extension). The field is `null`-defaulted
|
|
20
|
+
* so existing v1 frontmatter generated before Phase 5 (which omits the field
|
|
21
|
+
* entirely) parses without violating the schema. See ADR-0003.
|
|
22
|
+
*/
|
|
23
|
+
export const ResearchFrontmatterSchema = z.object({
|
|
24
|
+
id: z.string().min(1),
|
|
25
|
+
itemIds: z.array(z.string().min(1)).min(1),
|
|
26
|
+
agent: AgentIdSchema,
|
|
27
|
+
templateId: z.string().min(1),
|
|
28
|
+
createdAt: z.string().datetime(),
|
|
29
|
+
updatedAt: z.string().datetime().nullable(),
|
|
30
|
+
reviewedAt: z.string().datetime().nullable(),
|
|
31
|
+
reviewedBy: AgentIdSchema.nullable(),
|
|
32
|
+
supersedes: z.string().min(1).nullable().default(null),
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* In-memory Research record used by the orchestration layer.
|
|
36
|
+
*
|
|
37
|
+
* Extends `ResearchFrontmatter` with `outputPath`, which the CLI needs to
|
|
38
|
+
* pass through to the agent adapter (so the agent knows where to write) and
|
|
39
|
+
* back to the caller (so the CLI can verify the file after the adapter
|
|
40
|
+
* returns). Persisted frontmatter intentionally drops this field.
|
|
41
|
+
*/
|
|
42
|
+
export const ResearchSchema = ResearchFrontmatterSchema.extend({
|
|
43
|
+
outputPath: z.string().min(1),
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=research.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"research.js","sourceRoot":"","sources":["../../src/schemas/research.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC;AAG3F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACrB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1C,KAAK,EAAE,aAAa;IACpB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC3C,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC5C,UAAU,EAAE,aAAa,CAAC,QAAQ,EAAE;IACpC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CACvD,CAAC,CAAC;AAGH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,yBAAyB,CAAC,MAAM,CAAC;IAC7D,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC9B,CAAC,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const SourceKindSchema: z.ZodEnum<{
|
|
3
|
+
rss: "rss";
|
|
4
|
+
html: "html";
|
|
5
|
+
"github-releases": "github-releases";
|
|
6
|
+
"npm-registry": "npm-registry";
|
|
7
|
+
}>;
|
|
8
|
+
export type SourceKind = z.infer<typeof SourceKindSchema>;
|
|
9
|
+
/**
|
|
10
|
+
* Match modes for keyword evaluation (ADR-0006).
|
|
11
|
+
*
|
|
12
|
+
* - `word`: whole-word match (`\b<kw>\b`). Default; safest for casual blog feeds.
|
|
13
|
+
* - `substring`: simple substring match. Use for partial-token monitoring.
|
|
14
|
+
* - `regex`: keyword is compiled as a JavaScript RegExp. Invalid patterns throw.
|
|
15
|
+
* Beware ReDoS; users own pattern complexity.
|
|
16
|
+
*/
|
|
17
|
+
export declare const MatchModeSchema: z.ZodEnum<{
|
|
18
|
+
word: "word";
|
|
19
|
+
substring: "substring";
|
|
20
|
+
regex: "regex";
|
|
21
|
+
}>;
|
|
22
|
+
export type MatchMode = z.infer<typeof MatchModeSchema>;
|
|
23
|
+
/** Item fields a filter may inspect (ADR-0006). Adapters silently skip fields they cannot supply. */
|
|
24
|
+
export declare const MatchFieldSchema: z.ZodEnum<{
|
|
25
|
+
title: "title";
|
|
26
|
+
summary: "summary";
|
|
27
|
+
body: "body";
|
|
28
|
+
tags: "tags";
|
|
29
|
+
}>;
|
|
30
|
+
export type MatchField = z.infer<typeof MatchFieldSchema>;
|
|
31
|
+
/**
|
|
32
|
+
* Trust level for a Source (ADR-0009 M4).
|
|
33
|
+
*
|
|
34
|
+
* Tags whether the content returned by this source's adapter should be treated
|
|
35
|
+
* as agent-controllable. The default is `"untrusted"` so omitting the field
|
|
36
|
+
* (the only state existing source YAMLs are in) preserves the current
|
|
37
|
+
* defense-in-depth posture: every external feed is treated as adversarial
|
|
38
|
+
* until the user explicitly opts in.
|
|
39
|
+
*
|
|
40
|
+
* This issue only adds the field. Downstream policy branches (regex detection
|
|
41
|
+
* sensitivity, boundary marker strength, prompt builder behavior) land in a
|
|
42
|
+
* separate sub-issue so the schema change is reviewable in isolation.
|
|
43
|
+
*/
|
|
44
|
+
export declare const TrustLevelSchema: z.ZodEnum<{
|
|
45
|
+
trusted: "trusted";
|
|
46
|
+
untrusted: "untrusted";
|
|
47
|
+
}>;
|
|
48
|
+
export type TrustLevel = z.infer<typeof TrustLevelSchema>;
|
|
49
|
+
export declare const SourceFiltersSchema: z.ZodObject<{
|
|
50
|
+
keywords: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
51
|
+
excludeKeywords: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
52
|
+
matchMode: z.ZodDefault<z.ZodEnum<{
|
|
53
|
+
word: "word";
|
|
54
|
+
substring: "substring";
|
|
55
|
+
regex: "regex";
|
|
56
|
+
}>>;
|
|
57
|
+
matchFields: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
58
|
+
title: "title";
|
|
59
|
+
summary: "summary";
|
|
60
|
+
body: "body";
|
|
61
|
+
tags: "tags";
|
|
62
|
+
}>>>;
|
|
63
|
+
caseSensitive: z.ZodDefault<z.ZodBoolean>;
|
|
64
|
+
}, z.core.$strip>;
|
|
65
|
+
export type SourceFilters = z.infer<typeof SourceFiltersSchema>;
|
|
66
|
+
/**
|
|
67
|
+
* CSS selector ruleset for `kind: html` sources.
|
|
68
|
+
*
|
|
69
|
+
* HTML scraping cannot rely on a standard envelope the way RSS/Atom does, so
|
|
70
|
+
* each site declares the selectors needed to locate items and extract fields.
|
|
71
|
+
* The adapter walks every element matched by `item`, then applies the
|
|
72
|
+
* remaining selectors relative to that element.
|
|
73
|
+
*
|
|
74
|
+
* - `item` and `title` are required: without an item boundary or a title we
|
|
75
|
+
* cannot derive a stable id or produce a meaningful `Item`.
|
|
76
|
+
* - `link` is required because `Item.url` is the only field every downstream
|
|
77
|
+
* consumer (research / review / dedup) depends on.
|
|
78
|
+
* - `summary` / `publishedAt` / `body` / `tags` are optional; missing fields
|
|
79
|
+
* simply drop out of the normalized item (mirrors RSS adapter behavior).
|
|
80
|
+
* - `publishedAt` is parsed via `new Date(text)`; the adapter prefers a
|
|
81
|
+
* `datetime` / `content` attribute when the matched element exposes one
|
|
82
|
+
* (e.g. `<time datetime="…">`), falling back to text content.
|
|
83
|
+
*
|
|
84
|
+
* See `docs/design/source-html.md` for the parser choice rationale and the
|
|
85
|
+
* selector contract in full.
|
|
86
|
+
*/
|
|
87
|
+
export declare const SourceSelectorsSchema: z.ZodObject<{
|
|
88
|
+
item: z.ZodString;
|
|
89
|
+
title: z.ZodString;
|
|
90
|
+
link: z.ZodString;
|
|
91
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
92
|
+
publishedAt: z.ZodOptional<z.ZodString>;
|
|
93
|
+
body: z.ZodOptional<z.ZodString>;
|
|
94
|
+
tags: z.ZodOptional<z.ZodString>;
|
|
95
|
+
}, z.core.$strip>;
|
|
96
|
+
export type SourceSelectors = z.infer<typeof SourceSelectorsSchema>;
|
|
97
|
+
export declare const SourceSchema: z.ZodObject<{
|
|
98
|
+
id: z.ZodString;
|
|
99
|
+
kind: z.ZodEnum<{
|
|
100
|
+
rss: "rss";
|
|
101
|
+
html: "html";
|
|
102
|
+
"github-releases": "github-releases";
|
|
103
|
+
"npm-registry": "npm-registry";
|
|
104
|
+
}>;
|
|
105
|
+
url: z.ZodString;
|
|
106
|
+
name: z.ZodOptional<z.ZodString>;
|
|
107
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
108
|
+
filters: z.ZodDefault<z.ZodObject<{
|
|
109
|
+
keywords: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
110
|
+
excludeKeywords: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
111
|
+
matchMode: z.ZodDefault<z.ZodEnum<{
|
|
112
|
+
word: "word";
|
|
113
|
+
substring: "substring";
|
|
114
|
+
regex: "regex";
|
|
115
|
+
}>>;
|
|
116
|
+
matchFields: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
117
|
+
title: "title";
|
|
118
|
+
summary: "summary";
|
|
119
|
+
body: "body";
|
|
120
|
+
tags: "tags";
|
|
121
|
+
}>>>;
|
|
122
|
+
caseSensitive: z.ZodDefault<z.ZodBoolean>;
|
|
123
|
+
}, z.core.$strip>>;
|
|
124
|
+
selectors: z.ZodOptional<z.ZodObject<{
|
|
125
|
+
item: z.ZodString;
|
|
126
|
+
title: z.ZodString;
|
|
127
|
+
link: z.ZodString;
|
|
128
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
129
|
+
publishedAt: z.ZodOptional<z.ZodString>;
|
|
130
|
+
body: z.ZodOptional<z.ZodString>;
|
|
131
|
+
tags: z.ZodOptional<z.ZodString>;
|
|
132
|
+
}, z.core.$strip>>;
|
|
133
|
+
trustLevel: z.ZodDefault<z.ZodEnum<{
|
|
134
|
+
trusted: "trusted";
|
|
135
|
+
untrusted: "untrusted";
|
|
136
|
+
}>>;
|
|
137
|
+
}, z.core.$strip>;
|
|
138
|
+
export type Source = z.infer<typeof SourceSchema>;
|
|
139
|
+
//# sourceMappingURL=source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/schemas/source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,gBAAgB;;;;;EAA6D,CAAC;AAC3F,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe;;;;EAAyC,CAAC;AACtE,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,qGAAqG;AACrG,eAAO,MAAM,gBAAgB;;;;;EAA+C,CAAC;AAC7E,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB;;;EAAmC,CAAC;AACjE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;iBAM9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;iBAQhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAoBpE,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2CrB,CAAC;AACL,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const SourceKindSchema = z.enum(["rss", "html", "github-releases", "npm-registry"]);
|
|
3
|
+
/**
|
|
4
|
+
* Match modes for keyword evaluation (ADR-0006).
|
|
5
|
+
*
|
|
6
|
+
* - `word`: whole-word match (`\b<kw>\b`). Default; safest for casual blog feeds.
|
|
7
|
+
* - `substring`: simple substring match. Use for partial-token monitoring.
|
|
8
|
+
* - `regex`: keyword is compiled as a JavaScript RegExp. Invalid patterns throw.
|
|
9
|
+
* Beware ReDoS; users own pattern complexity.
|
|
10
|
+
*/
|
|
11
|
+
export const MatchModeSchema = z.enum(["word", "substring", "regex"]);
|
|
12
|
+
/** Item fields a filter may inspect (ADR-0006). Adapters silently skip fields they cannot supply. */
|
|
13
|
+
export const MatchFieldSchema = z.enum(["title", "summary", "body", "tags"]);
|
|
14
|
+
/**
|
|
15
|
+
* Trust level for a Source (ADR-0009 M4).
|
|
16
|
+
*
|
|
17
|
+
* Tags whether the content returned by this source's adapter should be treated
|
|
18
|
+
* as agent-controllable. The default is `"untrusted"` so omitting the field
|
|
19
|
+
* (the only state existing source YAMLs are in) preserves the current
|
|
20
|
+
* defense-in-depth posture: every external feed is treated as adversarial
|
|
21
|
+
* until the user explicitly opts in.
|
|
22
|
+
*
|
|
23
|
+
* This issue only adds the field. Downstream policy branches (regex detection
|
|
24
|
+
* sensitivity, boundary marker strength, prompt builder behavior) land in a
|
|
25
|
+
* separate sub-issue so the schema change is reviewable in isolation.
|
|
26
|
+
*/
|
|
27
|
+
export const TrustLevelSchema = z.enum(["trusted", "untrusted"]);
|
|
28
|
+
export const SourceFiltersSchema = z.object({
|
|
29
|
+
keywords: z.array(z.string()).default([]),
|
|
30
|
+
excludeKeywords: z.array(z.string()).default([]),
|
|
31
|
+
matchMode: MatchModeSchema.default("word"),
|
|
32
|
+
matchFields: z.array(MatchFieldSchema).default(["title", "summary"]),
|
|
33
|
+
caseSensitive: z.boolean().default(false),
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* CSS selector ruleset for `kind: html` sources.
|
|
37
|
+
*
|
|
38
|
+
* HTML scraping cannot rely on a standard envelope the way RSS/Atom does, so
|
|
39
|
+
* each site declares the selectors needed to locate items and extract fields.
|
|
40
|
+
* The adapter walks every element matched by `item`, then applies the
|
|
41
|
+
* remaining selectors relative to that element.
|
|
42
|
+
*
|
|
43
|
+
* - `item` and `title` are required: without an item boundary or a title we
|
|
44
|
+
* cannot derive a stable id or produce a meaningful `Item`.
|
|
45
|
+
* - `link` is required because `Item.url` is the only field every downstream
|
|
46
|
+
* consumer (research / review / dedup) depends on.
|
|
47
|
+
* - `summary` / `publishedAt` / `body` / `tags` are optional; missing fields
|
|
48
|
+
* simply drop out of the normalized item (mirrors RSS adapter behavior).
|
|
49
|
+
* - `publishedAt` is parsed via `new Date(text)`; the adapter prefers a
|
|
50
|
+
* `datetime` / `content` attribute when the matched element exposes one
|
|
51
|
+
* (e.g. `<time datetime="…">`), falling back to text content.
|
|
52
|
+
*
|
|
53
|
+
* See `docs/design/source-html.md` for the parser choice rationale and the
|
|
54
|
+
* selector contract in full.
|
|
55
|
+
*/
|
|
56
|
+
export const SourceSelectorsSchema = z.object({
|
|
57
|
+
item: z.string().min(1),
|
|
58
|
+
title: z.string().min(1),
|
|
59
|
+
link: z.string().min(1),
|
|
60
|
+
summary: z.string().optional(),
|
|
61
|
+
publishedAt: z.string().optional(),
|
|
62
|
+
body: z.string().optional(),
|
|
63
|
+
tags: z.string().optional(),
|
|
64
|
+
});
|
|
65
|
+
/**
|
|
66
|
+
* Validate `Source.url` per kind.
|
|
67
|
+
*
|
|
68
|
+
* Every kind except `npm-registry` requires a fully-qualified `http(s)` URL.
|
|
69
|
+
* The npm adapter accepts both the bare-package form (`@scope/pkg` or `pkg`)
|
|
70
|
+
* and the `https://www.npmjs.com/package/<pkg>` URL — see ADR-0002 — so we
|
|
71
|
+
* only enforce non-empty for that kind and let the adapter
|
|
72
|
+
* (`extractPackageName()`) canonicalize.
|
|
73
|
+
*/
|
|
74
|
+
function isValidHttpUrl(value) {
|
|
75
|
+
try {
|
|
76
|
+
const parsed = new URL(value);
|
|
77
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export const SourceSchema = z
|
|
84
|
+
.object({
|
|
85
|
+
id: z.string().min(1),
|
|
86
|
+
kind: SourceKindSchema,
|
|
87
|
+
url: z.string().min(1),
|
|
88
|
+
name: z.string().optional(),
|
|
89
|
+
tags: z.array(z.string()).default([]),
|
|
90
|
+
// Default `filters` to a fully-populated object so a source missing the
|
|
91
|
+
// entire `filters` block still parses. Zod 4's `.default()` takes the
|
|
92
|
+
// post-parse output (all required fields), so we cannot pass `{}`.
|
|
93
|
+
filters: SourceFiltersSchema.default({
|
|
94
|
+
keywords: [],
|
|
95
|
+
excludeKeywords: [],
|
|
96
|
+
matchMode: "word",
|
|
97
|
+
matchFields: ["title", "summary"],
|
|
98
|
+
caseSensitive: false,
|
|
99
|
+
}),
|
|
100
|
+
// `selectors` is required for `kind: html` and ignored for the other
|
|
101
|
+
// kinds. We model it as optional at the field level and enforce the
|
|
102
|
+
// "required when html" rule via a refinement so the same Source type
|
|
103
|
+
// serializes cleanly for both cases.
|
|
104
|
+
selectors: SourceSelectorsSchema.optional(),
|
|
105
|
+
// `trustLevel` defaults to `"untrusted"` so existing source YAMLs (which
|
|
106
|
+
// omit the field entirely) keep their current treatment. Per ADR-0009 M4
|
|
107
|
+
// this is schema-only; policy branches that read `trustLevel` arrive in a
|
|
108
|
+
// separate sub-issue.
|
|
109
|
+
trustLevel: TrustLevelSchema.default("untrusted"),
|
|
110
|
+
})
|
|
111
|
+
.superRefine((value, ctx) => {
|
|
112
|
+
if (value.kind !== "npm-registry" && !isValidHttpUrl(value.url)) {
|
|
113
|
+
ctx.addIssue({
|
|
114
|
+
code: "custom",
|
|
115
|
+
path: ["url"],
|
|
116
|
+
message: "Invalid url",
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (value.kind === "html" && value.selectors === undefined) {
|
|
120
|
+
ctx.addIssue({
|
|
121
|
+
code: "custom",
|
|
122
|
+
path: ["selectors"],
|
|
123
|
+
message: "selectors is required when kind is 'html'",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/schemas/source.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const SourceStateSchema: z.ZodObject<{
|
|
3
|
+
sourceId: z.ZodString;
|
|
4
|
+
lastFetchedAt: z.ZodOptional<z.ZodString>;
|
|
5
|
+
lastEtag: z.ZodOptional<z.ZodString>;
|
|
6
|
+
lastSeenIds: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
export type SourceState = z.infer<typeof SourceStateSchema>;
|
|
9
|
+
export declare const StateFileSchema: z.ZodObject<{
|
|
10
|
+
version: z.ZodLiteral<1>;
|
|
11
|
+
sources: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
12
|
+
sourceId: z.ZodString;
|
|
13
|
+
lastFetchedAt: z.ZodOptional<z.ZodString>;
|
|
14
|
+
lastEtag: z.ZodOptional<z.ZodString>;
|
|
15
|
+
lastSeenIds: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
16
|
+
}, z.core.$strip>>>;
|
|
17
|
+
}, z.core.$strip>;
|
|
18
|
+
export type StateFile = z.infer<typeof StateFileSchema>;
|
|
19
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/schemas/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iBAAiB;;;;;iBAK5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,eAAe;;;;;;;;iBAG1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const SourceStateSchema = z.object({
|
|
3
|
+
sourceId: z.string().min(1),
|
|
4
|
+
lastFetchedAt: z.string().datetime().optional(),
|
|
5
|
+
lastEtag: z.string().optional(),
|
|
6
|
+
lastSeenIds: z.array(z.string()).default([]),
|
|
7
|
+
});
|
|
8
|
+
export const StateFileSchema = z.object({
|
|
9
|
+
version: z.literal(1),
|
|
10
|
+
sources: z.array(SourceStateSchema).default([]),
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/schemas/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC/C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC7C,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAChD,CAAC,CAAC"}
|