@ozzylabs/feedradar 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/README.ja.md +31 -6
  2. package/README.md +31 -6
  3. package/dist/agents/claude-code.d.ts +12 -1
  4. package/dist/agents/claude-code.d.ts.map +1 -1
  5. package/dist/agents/claude-code.js +9 -5
  6. package/dist/agents/claude-code.js.map +1 -1
  7. package/dist/agents/codex-cli.d.ts +7 -1
  8. package/dist/agents/codex-cli.d.ts.map +1 -1
  9. package/dist/agents/codex-cli.js +9 -5
  10. package/dist/agents/codex-cli.js.map +1 -1
  11. package/dist/agents/copilot.d.ts +7 -1
  12. package/dist/agents/copilot.d.ts.map +1 -1
  13. package/dist/agents/copilot.js +9 -5
  14. package/dist/agents/copilot.js.map +1 -1
  15. package/dist/agents/gemini-cli.d.ts +7 -1
  16. package/dist/agents/gemini-cli.d.ts.map +1 -1
  17. package/dist/agents/gemini-cli.js +9 -5
  18. package/dist/agents/gemini-cli.js.map +1 -1
  19. package/dist/agents/index.d.ts +1 -1
  20. package/dist/agents/index.d.ts.map +1 -1
  21. package/dist/agents/types.d.ts +33 -0
  22. package/dist/agents/types.d.ts.map +1 -1
  23. package/dist/cli/_progress.d.ts +138 -0
  24. package/dist/cli/_progress.d.ts.map +1 -0
  25. package/dist/cli/_progress.js +176 -0
  26. package/dist/cli/_progress.js.map +1 -0
  27. package/dist/cli/doctor.d.ts +20 -0
  28. package/dist/cli/doctor.d.ts.map +1 -1
  29. package/dist/cli/doctor.js +291 -2
  30. package/dist/cli/doctor.js.map +1 -1
  31. package/dist/cli/index.d.ts.map +1 -1
  32. package/dist/cli/index.js +2 -0
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/research.d.ts +18 -20
  35. package/dist/cli/research.d.ts.map +1 -1
  36. package/dist/cli/research.js +318 -203
  37. package/dist/cli/research.js.map +1 -1
  38. package/dist/cli/respawn.d.ts +53 -0
  39. package/dist/cli/respawn.d.ts.map +1 -0
  40. package/dist/cli/respawn.js +120 -0
  41. package/dist/cli/respawn.js.map +1 -0
  42. package/dist/cli/review.d.ts +7 -0
  43. package/dist/cli/review.d.ts.map +1 -1
  44. package/dist/cli/review.js +46 -1
  45. package/dist/cli/review.js.map +1 -1
  46. package/dist/cli/source.d.ts +23 -2
  47. package/dist/cli/source.d.ts.map +1 -1
  48. package/dist/cli/source.js +425 -7
  49. package/dist/cli/source.js.map +1 -1
  50. package/dist/cli/update.d.ts +7 -0
  51. package/dist/cli/update.d.ts.map +1 -1
  52. package/dist/cli/update.js +41 -1
  53. package/dist/cli/update.js.map +1 -1
  54. package/dist/cli/watch.d.ts.map +1 -1
  55. package/dist/cli/watch.js +65 -3
  56. package/dist/cli/watch.js.map +1 -1
  57. package/dist/cli/workflow/generate-combined.d.ts +100 -0
  58. package/dist/cli/workflow/generate-combined.d.ts.map +1 -0
  59. package/dist/cli/workflow/generate-combined.js +387 -0
  60. package/dist/cli/workflow/generate-combined.js.map +1 -0
  61. package/dist/cli/workflow/generate-watch.d.ts +142 -0
  62. package/dist/cli/workflow/generate-watch.d.ts.map +1 -0
  63. package/dist/cli/workflow/generate-watch.js +338 -0
  64. package/dist/cli/workflow/generate-watch.js.map +1 -0
  65. package/dist/cli/workflow.d.ts +29 -0
  66. package/dist/cli/workflow.d.ts.map +1 -0
  67. package/dist/cli/workflow.js +66 -0
  68. package/dist/cli/workflow.js.map +1 -0
  69. package/dist/core/feeds/_fetch.d.ts +103 -0
  70. package/dist/core/feeds/_fetch.d.ts.map +1 -0
  71. package/dist/core/feeds/_fetch.js +364 -0
  72. package/dist/core/feeds/_fetch.js.map +1 -0
  73. package/dist/core/feeds/_jsonpath.d.ts +57 -0
  74. package/dist/core/feeds/_jsonpath.d.ts.map +1 -0
  75. package/dist/core/feeds/_jsonpath.js +207 -0
  76. package/dist/core/feeds/_jsonpath.js.map +1 -0
  77. package/dist/core/feeds/github-api.d.ts.map +1 -1
  78. package/dist/core/feeds/github-api.js +2 -1
  79. package/dist/core/feeds/github-api.js.map +1 -1
  80. package/dist/core/feeds/html-js.d.ts +29 -0
  81. package/dist/core/feeds/html-js.d.ts.map +1 -1
  82. package/dist/core/feeds/html-js.js +86 -2
  83. package/dist/core/feeds/html-js.js.map +1 -1
  84. package/dist/core/feeds/html.d.ts.map +1 -1
  85. package/dist/core/feeds/html.js +2 -1
  86. package/dist/core/feeds/html.js.map +1 -1
  87. package/dist/core/feeds/index.d.ts +1 -1
  88. package/dist/core/feeds/index.d.ts.map +1 -1
  89. package/dist/core/feeds/index.js +4 -0
  90. package/dist/core/feeds/index.js.map +1 -1
  91. package/dist/core/feeds/json-api.d.ts +3 -0
  92. package/dist/core/feeds/json-api.d.ts.map +1 -0
  93. package/dist/core/feeds/json-api.js +723 -0
  94. package/dist/core/feeds/json-api.js.map +1 -0
  95. package/dist/core/feeds/json-feed.d.ts +11 -0
  96. package/dist/core/feeds/json-feed.d.ts.map +1 -0
  97. package/dist/core/feeds/json-feed.js +242 -0
  98. package/dist/core/feeds/json-feed.js.map +1 -0
  99. package/dist/core/feeds/npm-registry.d.ts.map +1 -1
  100. package/dist/core/feeds/npm-registry.js +2 -1
  101. package/dist/core/feeds/npm-registry.js.map +1 -1
  102. package/dist/core/feeds/rss.d.ts.map +1 -1
  103. package/dist/core/feeds/rss.js +2 -1
  104. package/dist/core/feeds/rss.js.map +1 -1
  105. package/dist/core/feeds/types.d.ts +123 -0
  106. package/dist/core/feeds/types.d.ts.map +1 -1
  107. package/dist/core/progress.d.ts +101 -0
  108. package/dist/core/progress.d.ts.map +1 -0
  109. package/dist/core/progress.js +212 -0
  110. package/dist/core/progress.js.map +1 -0
  111. package/dist/core/proxy.d.ts +87 -0
  112. package/dist/core/proxy.d.ts.map +1 -0
  113. package/dist/core/proxy.js +146 -0
  114. package/dist/core/proxy.js.map +1 -0
  115. package/dist/core/recipes.d.ts +138 -0
  116. package/dist/core/recipes.d.ts.map +1 -0
  117. package/dist/core/recipes.js +238 -0
  118. package/dist/core/recipes.js.map +1 -0
  119. package/dist/core/watcher.d.ts +61 -1
  120. package/dist/core/watcher.d.ts.map +1 -1
  121. package/dist/core/watcher.js +99 -2
  122. package/dist/core/watcher.js.map +1 -1
  123. package/dist/index.js +17 -4
  124. package/dist/index.js.map +1 -1
  125. package/dist/recipes/aws-whats-new.yaml +61 -0
  126. package/dist/recipes/dev-to.yaml +40 -0
  127. package/dist/schemas/index.d.ts +1 -0
  128. package/dist/schemas/index.d.ts.map +1 -1
  129. package/dist/schemas/index.js +1 -0
  130. package/dist/schemas/index.js.map +1 -1
  131. package/dist/schemas/recipe.d.ts +115 -0
  132. package/dist/schemas/recipe.d.ts.map +1 -0
  133. package/dist/schemas/recipe.js +54 -0
  134. package/dist/schemas/recipe.js.map +1 -0
  135. package/dist/schemas/source.d.ts +130 -0
  136. package/dist/schemas/source.d.ts.map +1 -1
  137. package/dist/schemas/source.js +130 -0
  138. package/dist/schemas/source.js.map +1 -1
  139. package/dist/templates/agents/AGENTS.md +31 -3
  140. package/dist/templates/feedradar.md +23 -8
  141. package/dist/templates/workflows/combined.template.yaml.tmpl +110 -0
  142. package/dist/templates/workflows/watch.template.yaml.tmpl +103 -0
  143. package/dist/templates/workflows/watch.yaml +5 -1
  144. package/package.json +2 -3
@@ -0,0 +1,387 @@
1
+ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, isAbsolute, join, normalize, relative, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { RESEARCH_BATCH_DEFAULT_MAX_ITEMS } from "../research.js";
5
+ import { SUPPORTED_AGENTS } from "./generate-watch.js";
6
+ /**
7
+ * `radar workflow generate combined` (#189 / ADR-0014).
8
+ *
9
+ * Emits a GitHub Actions workflow that chains `radar watch run` -> "skip on
10
+ * no-new-items" guard -> `radar research --batch` in a single job so the
11
+ * detection-to-research delay collapses to one cron tick. The `--max-items`
12
+ * hard cap is rendered as a YAML literal AND re-enforced by the CLI (ADR-0014
13
+ * D3a "二重防御"), so a runaway detection or a hand-edited workflow cannot
14
+ * blow the cap inside one invocation.
15
+ *
16
+ * The four supported `--agent` values map to ADR-0014 D5's API-key-only
17
+ * secrets table (never OAuth). The generator emits the correct `env:` block
18
+ * in both the watch and research steps and prints the secrets the user must
19
+ * register so they do not have to grep the YAML.
20
+ */
21
+ /** Default cron expression — daily 00:00 UTC, matching ADR-0004 / watch template. */
22
+ const DEFAULT_CRON = "0 0 * * *";
23
+ /** Default output path relative to the workspace root (ADR-0014 D6). */
24
+ const DEFAULT_OUTPUT = join(".github", "workflows", "feedradar-combined.yaml");
25
+ /**
26
+ * Agent-specific `env:` blocks emitted into the generated workflow.
27
+ *
28
+ * Each block sits under ` env:` and is two-space-indented from the
29
+ * `name:` step header (i.e. 10 leading spaces per line). All four agents
30
+ * stay on API-key auth — never OAuth — per ADR-0014 D5.
31
+ *
32
+ * `claude-code` / `codex-cli` / `gemini-cli` also expose `GITHUB_TOKEN`
33
+ * so the github-releases adapter gets the 5000 req/h ceiling instead of 60.
34
+ * `copilot` reuses `secrets.GITHUB_TOKEN` natively (the Copilot CLI
35
+ * authenticates via the GH token).
36
+ */
37
+ const AGENT_SECRETS_BLOCKS = {
38
+ "claude-code": [
39
+ " ANTHROPIC_API_KEY: $" + "{{ secrets.ANTHROPIC_API_KEY }}",
40
+ " GITHUB_TOKEN: $" + "{{ secrets.GITHUB_TOKEN }}",
41
+ ].join("\n"),
42
+ "codex-cli": [
43
+ " OPENAI_API_KEY: $" + "{{ secrets.OPENAI_API_KEY }}",
44
+ " GITHUB_TOKEN: $" + "{{ secrets.GITHUB_TOKEN }}",
45
+ ].join("\n"),
46
+ "gemini-cli": [
47
+ " GEMINI_API_KEY: $" + "{{ secrets.GEMINI_API_KEY }}",
48
+ " GITHUB_TOKEN: $" + "{{ secrets.GITHUB_TOKEN }}",
49
+ ].join("\n"),
50
+ copilot: [" GITHUB_TOKEN: $" + "{{ secrets.GITHUB_TOKEN }}"].join("\n"),
51
+ };
52
+ /**
53
+ * Human-readable list of secrets each agent requires the user to register
54
+ * under Settings -> Secrets and variables -> Actions. Emitted after a
55
+ * successful generation so users do not have to grep the YAML to find what
56
+ * to register.
57
+ */
58
+ const AGENT_SECRET_NAMES = {
59
+ "claude-code": ["ANTHROPIC_API_KEY", "GITHUB_TOKEN (auto-provisioned)"],
60
+ "codex-cli": ["OPENAI_API_KEY", "GITHUB_TOKEN (auto-provisioned)"],
61
+ "gemini-cli": ["GEMINI_API_KEY", "GITHUB_TOKEN (auto-provisioned)"],
62
+ copilot: ["GITHUB_TOKEN (auto-provisioned)"],
63
+ };
64
+ async function pathExists(p) {
65
+ try {
66
+ await access(p);
67
+ return true;
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ }
73
+ /**
74
+ * Mirrors `resolveTemplatesRoot` in `generate-watch.ts` so both generators
75
+ * find the bundled templates dir under both compiled (`dist/`) and source
76
+ * (`src/`) layouts.
77
+ */
78
+ async function resolveTemplatesRoot() {
79
+ const here = dirname(fileURLToPath(import.meta.url));
80
+ return resolve(here, "..", "..", "templates");
81
+ }
82
+ /**
83
+ * Validate a 5-field POSIX cron expression. Borrowed verbatim from
84
+ * `generate-watch.ts` `isValidCron` so both type generators apply the same
85
+ * shape check (structural only — range validation is GitHub Actions's job).
86
+ */
87
+ export function isValidCron(expr) {
88
+ const trimmed = expr.trim();
89
+ if (trimmed.length === 0)
90
+ return false;
91
+ const fields = trimmed.split(/\s+/);
92
+ if (fields.length !== 5)
93
+ return false;
94
+ const tokenPattern = /^(?:\*|\d+(?:-\d+)?)(?:\/\d+)?$/;
95
+ for (const field of fields) {
96
+ if (field.length === 0)
97
+ return false;
98
+ const tokens = field.split(",");
99
+ for (const token of tokens) {
100
+ if (token.length === 0)
101
+ return false;
102
+ if (!tokenPattern.test(token))
103
+ return false;
104
+ }
105
+ }
106
+ return true;
107
+ }
108
+ /**
109
+ * Validate that the requested `--output` path lands under
110
+ * `.github/workflows/`. Mirrors `isSafeWorkflowPath` in `generate-watch.ts`.
111
+ */
112
+ export function isSafeWorkflowPath(outputPath, cwd) {
113
+ if (isAbsolute(outputPath)) {
114
+ const allowedDir = resolve(cwd, ".github", "workflows");
115
+ const resolved = resolve(outputPath);
116
+ const rel = relative(allowedDir, resolved);
117
+ if (rel.startsWith("..") || isAbsolute(rel))
118
+ return false;
119
+ return /\.(ya?ml)$/i.test(resolved);
120
+ }
121
+ const normalized = normalize(outputPath);
122
+ if (normalized.split(/[\\/]/).includes(".."))
123
+ return false;
124
+ const required = `${join(".github", "workflows")}/`;
125
+ const unixified = normalized.replace(/\\/g, "/");
126
+ if (!unixified.startsWith(required.replace(/\\/g, "/")))
127
+ return false;
128
+ return /\.(ya?ml)$/i.test(unixified);
129
+ }
130
+ /**
131
+ * Validate `--max-items` as a positive integer. Mirrors `parseMaxItems` in
132
+ * `src/cli/research.ts` so the same input is accepted at both layers (CLI ->
133
+ * workflow YAML -> `radar research --batch`).
134
+ */
135
+ export function isValidMaxItems(raw) {
136
+ if (!/^[0-9]+$/.test(raw))
137
+ return false;
138
+ const n = Number.parseInt(raw, 10);
139
+ return Number.isFinite(n) && n > 0;
140
+ }
141
+ /**
142
+ * Render the `--filter-tags` literal as it should appear on the generated
143
+ * `radar research --batch` line.
144
+ *
145
+ * When the user omits the flag, returns an empty string so the resulting
146
+ * line collapses to `radar research --batch --status detected --max-items N
147
+ * --agent <id>` without a stray `--filter-tags`. When present, each tag is
148
+ * trimmed, lower-cased, and deduped (matching the CLI parser) and rendered
149
+ * with a leading space so the template's `--max-items {{maxItems}}{{filterTags}}`
150
+ * concatenation reads naturally.
151
+ */
152
+ export function renderFilterTagsLiteral(raw) {
153
+ if (raw === undefined || raw.trim() === "")
154
+ return "";
155
+ const tags = [
156
+ ...new Set(raw
157
+ .split(",")
158
+ .map((s) => s.trim().toLowerCase())
159
+ .filter((s) => s.length > 0)),
160
+ ];
161
+ if (tags.length === 0)
162
+ return "";
163
+ return ` --filter-tags ${tags.join(",")}`;
164
+ }
165
+ /**
166
+ * Render the bundled template by substituting `{{cron}}` / `{{maxItems}}` /
167
+ * `{{filterTags}}` / `{{agent}}` / `{{secretsBlock}}` placeholders.
168
+ *
169
+ * Substitution is a literal `replaceAll` so each placeholder lands
170
+ * atomically in every site it appears (the template intentionally uses
171
+ * `{{secretsBlock}}` twice, in the watch and research steps).
172
+ */
173
+ export function renderCombinedTemplate(template, values) {
174
+ return template
175
+ .replaceAll("{{cron}}", values.cron)
176
+ .replaceAll("{{maxItems}}", String(values.maxItems))
177
+ .replaceAll("{{filterTags}}", values.filterTagsLiteral)
178
+ .replaceAll("{{agent}}", values.agent)
179
+ .replaceAll("{{secretsBlock}}", values.secretsBlock);
180
+ }
181
+ /**
182
+ * Core implementation of `radar workflow generate combined`.
183
+ *
184
+ * Validates inputs, reads the bundled `combined.template.yaml.tmpl`,
185
+ * substitutes placeholders, and writes the result. The completion stdout
186
+ * lines tell the user exactly which secrets to register so they do not have
187
+ * to hunt through ADR-0014 D5 by hand. The trailing `warn()` line surfaces
188
+ * the CLI-layer hard-cap double-defense so the user does not assume
189
+ * editing the YAML alone will raise `--max-items`.
190
+ */
191
+ export async function generateCombined(options) {
192
+ const { cwd, watchCron, output, agent, maxItems, filterTags, force } = options;
193
+ const log = options.io?.log ?? ((m) => console.log(m));
194
+ const warn = options.io?.warn ?? ((m) => console.warn(m));
195
+ if (!isValidCron(watchCron)) {
196
+ throw new Error(`invalid --watch-cron expression '${watchCron}' (expected 5-field POSIX cron, e.g. "0 0 * * *")`);
197
+ }
198
+ if (!isSafeWorkflowPath(output, cwd)) {
199
+ throw new Error(`invalid --output '${output}' (must be a relative path under .github/workflows/ ending in .yaml or .yml)`);
200
+ }
201
+ if (!Number.isInteger(maxItems) || maxItems <= 0) {
202
+ throw new Error(`invalid --max-items '${maxItems}' (must be a positive integer)`);
203
+ }
204
+ const templatesRoot = options.templatesRoot ?? (await resolveTemplatesRoot());
205
+ const templatePath = join(templatesRoot, "workflows", "combined.template.yaml.tmpl");
206
+ if (!(await pathExists(templatePath))) {
207
+ throw new Error(`bundled template not found: ${templatePath}`);
208
+ }
209
+ const template = await readFile(templatePath, "utf8");
210
+ // `renderFilterTagsLiteral` accepts the raw CLI string; `generateCombined`
211
+ // accepts a pre-parsed array (so test code can build one without going
212
+ // through CLI parsing). Re-render here so both entrypoints agree on the
213
+ // literal shape.
214
+ const filterTagsLiteral = filterTags.length === 0 ? "" : ` --filter-tags ${filterTags.join(",")}`;
215
+ const rendered = renderCombinedTemplate(template, {
216
+ cron: watchCron,
217
+ maxItems,
218
+ filterTagsLiteral,
219
+ agent,
220
+ secretsBlock: AGENT_SECRETS_BLOCKS[agent],
221
+ });
222
+ const destAbs = isAbsolute(output) ? output : join(cwd, output);
223
+ const destRel = isAbsolute(output) ? relative(cwd, output) : output;
224
+ if ((await pathExists(destAbs)) && !force) {
225
+ throw new Error(`output file already exists: ${destRel} (use --force to overwrite)`);
226
+ }
227
+ if ((await pathExists(destAbs)) && force) {
228
+ warn(`workflow generate combined: overwriting existing file ${destRel}`);
229
+ }
230
+ await mkdir(dirname(destAbs), { recursive: true });
231
+ await writeFile(destAbs, rendered, "utf8");
232
+ log(`workflow generate combined: wrote ${destRel}`);
233
+ log(` agent: ${agent}`);
234
+ log(` cron: ${watchCron}`);
235
+ log(` max-items: ${maxItems}`);
236
+ log(` filter-tags: ${filterTags.length === 0 ? "(none)" : filterTags.join(",")}`);
237
+ log("");
238
+ log("Required GitHub Actions secrets (Settings → Secrets and variables → Actions):");
239
+ for (const s of AGENT_SECRET_NAMES[agent]) {
240
+ log(` ${s}`);
241
+ }
242
+ // Surface the hard-cap double-defense so the user knows editing the YAML
243
+ // alone will not lift the CLI cap (ADR-0014 D3a tail).
244
+ warn("workflow generate combined: the --max-items cap is also enforced by `radar research --batch`; editing the YAML alone will not raise it");
245
+ return { outputPath: destRel, requiredSecrets: AGENT_SECRET_NAMES[agent] };
246
+ }
247
+ /**
248
+ * Parse `workflow generate combined` flags.
249
+ *
250
+ * Throws on missing values, unknown flags, unsupported `--agent` choices,
251
+ * and malformed `--max-items` so the caller can surface validation errors
252
+ * before any IO happens.
253
+ */
254
+ export function parseGenerateCombinedArgs(args) {
255
+ let watchCron = DEFAULT_CRON;
256
+ let output = DEFAULT_OUTPUT;
257
+ let agent = "claude-code";
258
+ let maxItems = RESEARCH_BATCH_DEFAULT_MAX_ITEMS;
259
+ let filterTags = [];
260
+ let force = false;
261
+ let help = false;
262
+ for (let i = 0; i < args.length; i++) {
263
+ const a = args[i];
264
+ if (a === "-h" || a === "--help") {
265
+ help = true;
266
+ continue;
267
+ }
268
+ if (a === "--watch-cron") {
269
+ const value = args[++i];
270
+ if (value === undefined)
271
+ throw new Error(`option ${a} requires a value`);
272
+ watchCron = value;
273
+ continue;
274
+ }
275
+ if (a === "--output") {
276
+ const value = args[++i];
277
+ if (value === undefined)
278
+ throw new Error(`option ${a} requires a value`);
279
+ output = value;
280
+ continue;
281
+ }
282
+ if (a === "--agent") {
283
+ const value = args[++i];
284
+ if (value === undefined)
285
+ throw new Error(`option ${a} requires a value`);
286
+ if (!SUPPORTED_AGENTS.includes(value)) {
287
+ throw new Error(`option --agent expects one of: ${SUPPORTED_AGENTS.join(" | ")}, got '${value}'`);
288
+ }
289
+ agent = value;
290
+ continue;
291
+ }
292
+ if (a === "--max-items") {
293
+ const value = args[++i];
294
+ if (value === undefined)
295
+ throw new Error(`option ${a} requires a value`);
296
+ if (!isValidMaxItems(value)) {
297
+ throw new Error(`option --max-items expects a positive integer, got '${value}'`);
298
+ }
299
+ maxItems = Number.parseInt(value, 10);
300
+ continue;
301
+ }
302
+ if (a === "--filter-tags") {
303
+ const value = args[++i];
304
+ if (value === undefined)
305
+ throw new Error(`option ${a} requires a value`);
306
+ filterTags = [
307
+ ...new Set(value
308
+ .split(",")
309
+ .map((s) => s.trim().toLowerCase())
310
+ .filter((s) => s.length > 0)),
311
+ ];
312
+ continue;
313
+ }
314
+ if (a === "--force" || a === "-f") {
315
+ force = true;
316
+ continue;
317
+ }
318
+ if (a?.startsWith("--") || a?.startsWith("-")) {
319
+ throw new Error(`unknown option: ${a}`);
320
+ }
321
+ throw new Error(`unexpected positional argument: ${a}`);
322
+ }
323
+ return { watchCron, output, agent, maxItems, filterTags, force, help };
324
+ }
325
+ export function printGenerateCombinedHelp(log) {
326
+ log("Usage: radar workflow generate combined [options]");
327
+ log("");
328
+ log("Generates a GitHub Actions workflow that chains `radar watch run` ->");
329
+ log("a no-new-items guard -> `radar research --batch` with hard-capped cost");
330
+ log("controls (ADR-0014 D2 / D3 / D4 / D5).");
331
+ log("");
332
+ log("Options:");
333
+ log(' --watch-cron <expression> 5-field cron expression (default: "0 0 * * *")');
334
+ log(" --output <path> Output file under .github/workflows/");
335
+ log(" (default: .github/workflows/feedradar-combined.yaml)");
336
+ log(" --agent <name> claude-code | codex-cli | gemini-cli | copilot (default: claude-code)");
337
+ log(` --max-items N Hard cap on auto-research per run (default: ${RESEARCH_BATCH_DEFAULT_MAX_ITEMS})`);
338
+ log(" --filter-tags <list> Comma-separated allow-list of matchedKeywords");
339
+ log(" (default: unset, matches every detected item)");
340
+ log(" --force, -f Overwrite existing output file");
341
+ log("");
342
+ log("Required secrets (Settings → Secrets and variables → Actions):");
343
+ log(" ANTHROPIC_API_KEY when --agent claude-code (default)");
344
+ log(" OPENAI_API_KEY when --agent codex-cli");
345
+ log(" GEMINI_API_KEY when --agent gemini-cli");
346
+ log(" GITHUB_TOKEN auto-provisioned for --agent copilot (no setup needed)");
347
+ }
348
+ /**
349
+ * Entry point invoked by `runWorkflow` (in `src/cli/workflow.ts`) when the
350
+ * user types `radar workflow generate combined`. Translates parsed flags
351
+ * into `generateCombined` arguments and surfaces validation errors with the
352
+ * `workflow generate combined:` prefix to match the rest of the CLI.
353
+ */
354
+ export async function runGenerateCombined(args, io = {}, cwd = process.cwd()) {
355
+ const log = io.log ?? ((m) => console.log(m));
356
+ const error = io.error ?? ((m) => console.error(m));
357
+ let parsed;
358
+ try {
359
+ parsed = parseGenerateCombinedArgs(args);
360
+ }
361
+ catch (e) {
362
+ error(`workflow generate combined: ${e instanceof Error ? e.message : String(e)}`);
363
+ return 2;
364
+ }
365
+ if (parsed.help) {
366
+ printGenerateCombinedHelp(log);
367
+ return 0;
368
+ }
369
+ try {
370
+ await generateCombined({
371
+ cwd,
372
+ watchCron: parsed.watchCron,
373
+ output: parsed.output,
374
+ agent: parsed.agent,
375
+ maxItems: parsed.maxItems,
376
+ filterTags: parsed.filterTags,
377
+ force: parsed.force,
378
+ io,
379
+ });
380
+ return 0;
381
+ }
382
+ catch (e) {
383
+ error(`workflow generate combined: ${e instanceof Error ? e.message : String(e)}`);
384
+ return 1;
385
+ }
386
+ }
387
+ //# sourceMappingURL=generate-combined.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-combined.js","sourceRoot":"","sources":["../../../src/cli/workflow/generate-combined.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Sinks for the `workflow generate watch` command's user-facing output.
3
+ *
4
+ * The CLI binds these to `console.*` by default; tests inject capturing
5
+ * sinks so they can assert against printed lines without poking at stdio.
6
+ */
7
+ export interface WorkflowIO {
8
+ log?: (message: string) => void;
9
+ warn?: (message: string) => void;
10
+ error?: (message: string) => void;
11
+ }
12
+ /**
13
+ * Agents supported by `--agent`. The literal list mirrors ADR-0014 D5's
14
+ * auth-policy table: each agent maps to a single repo secret name and the
15
+ * generator writes that name into the rendered workflow's `env:` block.
16
+ *
17
+ * `copilot` is included for completeness, but uses the auto-provisioned
18
+ * `GITHUB_TOKEN` rather than a user-managed secret — see `agentEnvKey` below.
19
+ */
20
+ export declare const SUPPORTED_AGENTS: readonly ["claude-code", "codex-cli", "gemini-cli", "copilot"];
21
+ export type SupportedAgent = (typeof SUPPORTED_AGENTS)[number];
22
+ /**
23
+ * Map an agent identifier to its required workflow secret name (ADR-0014 D5).
24
+ *
25
+ * The generator writes the result into the rendered workflow as both the
26
+ * `env:` key and the `${{ secrets.<NAME> }}` reference, so the user only
27
+ * needs to register a single secret named after the table below.
28
+ *
29
+ * `copilot` uses the auto-provisioned `GITHUB_TOKEN`; no user action needed.
30
+ * The completion message still surfaces this so the user can confirm
31
+ * `permissions: contents: write` is enough (which it is — Copilot CLI rides
32
+ * the `GITHUB_TOKEN` granted to the job).
33
+ */
34
+ export declare function agentEnvKey(agent: SupportedAgent): string;
35
+ /**
36
+ * Validate a 5-field POSIX cron expression.
37
+ *
38
+ * GitHub Actions accepts the standard 5-field crontab format (minute, hour,
39
+ * day-of-month, month, day-of-week). We do not aim for byte-perfect parity
40
+ * with the upstream cron parser (e.g. `@daily` aliases) — those are
41
+ * actively documented as unsupported by GitHub Actions, so rejecting them
42
+ * here protects the user from generating a workflow that silently never
43
+ * fires.
44
+ *
45
+ * The validator accepts each field as one of:
46
+ * - `*`
47
+ * - `<n>` or `<n>-<m>` (with optional `/<step>`)
48
+ * - `<a>,<b>,...` (comma list, each token validated independently)
49
+ * - star slash step (every N units, e.g. `0 *_/6 * * *` reading the
50
+ * underscore as a placeholder for the slash that would close this comment)
51
+ *
52
+ * Range bounds (e.g. month 1-12) are NOT enforced here; GitHub Actions
53
+ * rejects out-of-range expressions on workflow load. Keeping our check
54
+ * structural keeps the dep surface zero (no cron-parser library) without
55
+ * generating workflows that pass our check but fail GitHub's.
56
+ */
57
+ export declare function isValidCron(expr: string): boolean;
58
+ /**
59
+ * Validate that the requested `--output` path lands under
60
+ * `.github/workflows/` relative to the workspace root.
61
+ *
62
+ * Two attack/footgun classes are blocked:
63
+ *
64
+ * 1. **Traversal:** `../../etc/passwd` or `.github/workflows/../../foo`
65
+ * — `..` segments anywhere in the relative path push the file out of
66
+ * the allowed directory.
67
+ * 2. **Absolute paths:** `/etc/foo` — the workflow is meant to live in
68
+ * the workspace; an absolute path is almost certainly user error or
69
+ * malicious input.
70
+ *
71
+ * The check operates on the relative form of the resolved path so that
72
+ * `cwd` differences (test temp dirs vs `process.cwd()`) do not affect the
73
+ * verdict. We also require the file to end with `.yaml` or `.yml` so the
74
+ * GitHub Actions loader picks it up.
75
+ */
76
+ export declare function isSafeWorkflowPath(outputPath: string, cwd: string): boolean;
77
+ /**
78
+ * Render the bundled template by substituting `{{cron}}` / `{{output}}` /
79
+ * `{{agentEnvKey}}` placeholders with the user-supplied values.
80
+ *
81
+ * Substitution is intentionally a literal `replace`, not a templating
82
+ * engine: the placeholders are simple tokens, and we don't want to expand
83
+ * arbitrary GitHub Actions expressions (`${{ ... }}`) that already live in
84
+ * the template body.
85
+ *
86
+ * Exported for unit testing — tests can drive the renderer in isolation
87
+ * without touching the file system.
88
+ */
89
+ export declare function renderWatchTemplate(template: string, values: {
90
+ cron: string;
91
+ agentEnvKey: string;
92
+ }): string;
93
+ export interface GenerateWatchOptions {
94
+ cwd: string;
95
+ cron: string;
96
+ output: string;
97
+ agent: SupportedAgent;
98
+ force: boolean;
99
+ /** Test seam: override the templates root location. */
100
+ templatesRoot?: string;
101
+ io?: WorkflowIO;
102
+ }
103
+ export interface GenerateWatchResult {
104
+ /** Relative path (from `cwd`) of the file that was written. */
105
+ outputPath: string;
106
+ /** Name of the GitHub Actions secret the user must register. */
107
+ requiredSecret: string;
108
+ }
109
+ /**
110
+ * Core implementation of `radar workflow generate watch`.
111
+ *
112
+ * Validates the cron + output path, renders the template with the
113
+ * agent-appropriate secret name, and writes the result. The completion
114
+ * stdout lines (printed via `io.log`) tell the user exactly which secrets
115
+ * to register so they don't have to hunt through ADR-0014 D5 by hand.
116
+ */
117
+ export declare function generateWatch(options: GenerateWatchOptions): Promise<GenerateWatchResult>;
118
+ interface ParsedFlags {
119
+ cron: string;
120
+ output: string;
121
+ agent: SupportedAgent;
122
+ force: boolean;
123
+ help: boolean;
124
+ }
125
+ /**
126
+ * Parse `workflow generate watch` flags.
127
+ *
128
+ * Throws on flags that require a value but receive none, unknown flags, or
129
+ * unsupported `--agent` choices. Returning a structured object keeps `run`
130
+ * free of argument-parsing branches.
131
+ */
132
+ export declare function parseGenerateWatchArgs(args: string[]): ParsedFlags;
133
+ export declare function printGenerateWatchHelp(log: (m: string) => void): void;
134
+ /**
135
+ * Entry point invoked by `runWorkflow` (in `src/cli/workflow.ts`) when the
136
+ * user types `radar workflow generate watch`. Translates parsed flags into
137
+ * `generateWatch` arguments and surfaces validation errors with the
138
+ * `workflow generate watch:` prefix to match the rest of the CLI.
139
+ */
140
+ export declare function runGenerateWatch(args: string[], io?: WorkflowIO, cwd?: string): Promise<number>;
141
+ export {};
142
+ //# sourceMappingURL=generate-watch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-watch.d.ts","sourceRoot":"","sources":["../../../src/cli/workflow/generate-watch.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,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;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,gEAAiE,CAAC;AAC/F,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AA4B/D;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CAWzD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAkBjD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAsB3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC5C,MAAM,CAIR;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,EAAE,CAAC,EAAE,UAAU,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA2D/F;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,WAAW,CA+ClE;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAsBrE;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EAAE,EACd,EAAE,GAAE,UAAe,EACnB,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,MAAM,CAAC,CA8BjB"}