@ozzylabs/feedradar 0.1.8 → 0.2.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/README.ja.md +6 -3
- package/README.md +6 -3
- package/dist/agents/_boundary.d.ts +61 -4
- package/dist/agents/_boundary.d.ts.map +1 -1
- package/dist/agents/_boundary.js +143 -18
- package/dist/agents/_boundary.js.map +1 -1
- package/dist/agents/claude-code.d.ts.map +1 -1
- package/dist/agents/claude-code.js +33 -93
- package/dist/agents/claude-code.js.map +1 -1
- package/dist/agents/codex-cli.d.ts.map +1 -1
- package/dist/agents/codex-cli.js +34 -93
- package/dist/agents/codex-cli.js.map +1 -1
- package/dist/agents/copilot.d.ts.map +1 -1
- package/dist/agents/copilot.js +33 -93
- package/dist/agents/copilot.js.map +1 -1
- package/dist/agents/gemini-cli.d.ts.map +1 -1
- package/dist/agents/gemini-cli.js +33 -93
- package/dist/agents/gemini-cli.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +10 -3
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/routine/fire.d.ts +100 -0
- package/dist/cli/routine/fire.d.ts.map +1 -0
- package/dist/cli/routine/fire.js +196 -0
- package/dist/cli/routine/fire.js.map +1 -0
- package/dist/cli/routine/generate-pipeline.d.ts +100 -0
- package/dist/cli/routine/generate-pipeline.d.ts.map +1 -0
- package/dist/cli/routine/generate-pipeline.js +311 -0
- package/dist/cli/routine/generate-pipeline.js.map +1 -0
- package/dist/cli/routine/generate-watch.d.ts +174 -0
- package/dist/cli/routine/generate-watch.d.ts.map +1 -0
- package/dist/cli/routine/generate-watch.js +445 -0
- package/dist/cli/routine/generate-watch.js.map +1 -0
- package/dist/cli/routine.d.ts +32 -0
- package/dist/cli/routine.d.ts.map +1 -0
- package/dist/cli/routine.js +74 -0
- package/dist/cli/routine.js.map +1 -0
- package/dist/cli/triage.d.ts.map +1 -1
- package/dist/cli/triage.js +383 -78
- package/dist/cli/triage.js.map +1 -1
- package/dist/core/feeds/json-api.d.ts.map +1 -1
- package/dist/core/feeds/json-api.js +14 -0
- package/dist/core/feeds/json-api.js.map +1 -1
- package/dist/core/feeds/types.d.ts +14 -0
- package/dist/core/feeds/types.d.ts.map +1 -1
- package/dist/core/triage/adapter.d.ts +45 -0
- package/dist/core/triage/adapter.d.ts.map +1 -1
- package/dist/core/triage/adapter.js +50 -11
- package/dist/core/triage/adapter.js.map +1 -1
- package/dist/core/watcher.d.ts.map +1 -1
- package/dist/core/watcher.js +12 -2
- package/dist/core/watcher.js.map +1 -1
- package/dist/skills/research/SKILL.md +17 -6
- package/dist/skills/review/SKILL.md +13 -5
- package/dist/skills/update/SKILL.md +13 -5
- package/dist/templates/agents/AGENTS.md +1 -1
- package/dist/templates/routines/pipeline.yaml.tmpl +231 -0
- package/dist/templates/routines/watch-daily.yaml +157 -0
- package/dist/templates/routines/watch.yaml.tmpl +151 -0
- package/package.json +1 -1
- package/dist/templates/routines/watch-daily.md +0 -42
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { collectSourceHosts, isSafeRoutinePath, isSubHourlyCron, isValidCron, renderNetworkAccessBlock, SUPPORTED_MODELS, } from "./generate-watch.js";
|
|
5
|
+
/**
|
|
6
|
+
* `radar routine generate pipeline` (ADR-0020 D5 `pipeline`).
|
|
7
|
+
*
|
|
8
|
+
* Emits a Claude Routine YAML whose single session runs the FULL FeedRadar
|
|
9
|
+
* pipeline in sequence — `radar watch run` -> triage -> research -> review —
|
|
10
|
+
* processing items ONE AT A TIME via the self-session `--emit-payload` /
|
|
11
|
+
* `--commit` entrypoints (NOT `--batch`; ADR-0020 D2). The blast radius is
|
|
12
|
+
* bounded by CLI flags (`--max-items` / `--limit`), not the prompt's
|
|
13
|
+
* discretion (D3e), so a `--max-items` flag is the only structural addition
|
|
14
|
+
* over the `watch` generator.
|
|
15
|
+
*
|
|
16
|
+
* The cron / output-path / repository validators and the model roster are
|
|
17
|
+
* shared with `generate-watch.ts` rather than re-declared: routines enforce
|
|
18
|
+
* the identical 1-hour-minimum cron and `.claude/routines/*.yaml` output
|
|
19
|
+
* gate regardless of `<type>`, so a single source keeps the two in lockstep.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Default for `--max-items`: how many items one run may triage / research /
|
|
23
|
+
* review. Kept small so the routine's per-run blast radius stays bounded by a
|
|
24
|
+
* deterministic CLI cap (ADR-0020 D3e) rather than the prompt's discretion.
|
|
25
|
+
* Mirrors the conservative default of the GHA `combined-with-triage` cap.
|
|
26
|
+
*/
|
|
27
|
+
export const PIPELINE_DEFAULT_MAX_ITEMS = 10;
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the directory holding the bundled routine templates.
|
|
30
|
+
*
|
|
31
|
+
* Mirrors `resolveTemplatesRoot` in `generate-watch.ts`: the compiled CLI lives
|
|
32
|
+
* at `dist/cli/routine/generate-pipeline.js`, so the bundled templates sit at
|
|
33
|
+
* `../../templates` relative to this module. Tests run from source
|
|
34
|
+
* (`src/cli/routine/generate-pipeline.ts`), where the same relative path lands
|
|
35
|
+
* at `src/templates/`.
|
|
36
|
+
*/
|
|
37
|
+
async function resolveTemplatesRoot() {
|
|
38
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
39
|
+
return resolve(here, "..", "..", "templates");
|
|
40
|
+
}
|
|
41
|
+
async function pathExists(p) {
|
|
42
|
+
try {
|
|
43
|
+
await access(p);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Render the bundled `pipeline.yaml.tmpl` by substituting the `{{name}}` /
|
|
52
|
+
* `{{repository}}` / `{{cron}}` / `{{timezone}}` / `{{model}}` / `{{maxItems}}`
|
|
53
|
+
* placeholders.
|
|
54
|
+
*
|
|
55
|
+
* Literal `replace` (not a templating engine): the placeholders are simple
|
|
56
|
+
* tokens and we must not expand any other `{{...}}`-looking text in the body.
|
|
57
|
+
* Exported for unit testing in isolation.
|
|
58
|
+
*/
|
|
59
|
+
export function renderPipelineRoutineTemplate(template, values) {
|
|
60
|
+
return template
|
|
61
|
+
.replace(/\{\{name\}\}/g, values.name)
|
|
62
|
+
.replace(/\{\{repository\}\}/g, values.repository)
|
|
63
|
+
.replace(/\{\{cron\}\}/g, values.cron)
|
|
64
|
+
.replace(/\{\{timezone\}\}/g, values.timezone)
|
|
65
|
+
.replace(/\{\{model\}\}/g, values.model)
|
|
66
|
+
.replace(/\{\{maxItems\}\}/g, String(values.maxItems))
|
|
67
|
+
.replace(/\{\{networkAccessBlock\}\}/g, values.networkAccessBlock);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Core implementation of `radar routine generate pipeline` (ADR-0020 D5
|
|
71
|
+
* `pipeline`).
|
|
72
|
+
*
|
|
73
|
+
* Validates the cron (5-field + 1-hour-minimum), output path, repository, and
|
|
74
|
+
* `--max-items` (>= 1), renders the bundled `pipeline.yaml.tmpl`, and writes it
|
|
75
|
+
* under `.claude/routines/`. The completion stdout tells the user how to paste
|
|
76
|
+
* the multi-line fields into the Web UI (yq extraction) and how to apply the
|
|
77
|
+
* schedule via `/schedule`, since Routines has no declarative apply API
|
|
78
|
+
* (ADR-0020 D1).
|
|
79
|
+
*/
|
|
80
|
+
export async function generatePipelineRoutine(options) {
|
|
81
|
+
const { cwd, name, repository, cron, timezone, model, maxItems, output, force } = options;
|
|
82
|
+
const log = options.io?.log ?? ((m) => console.log(m));
|
|
83
|
+
const warn = options.io?.warn ?? ((m) => console.warn(m));
|
|
84
|
+
if (!isValidCron(cron)) {
|
|
85
|
+
throw new Error(`invalid --cron expression '${cron}' (expected 5-field POSIX cron, e.g. "0 * * * *")`);
|
|
86
|
+
}
|
|
87
|
+
if (isSubHourlyCron(cron)) {
|
|
88
|
+
throw new Error(`invalid --cron '${cron}': Claude Routines require a minimum interval of 1 hour ` +
|
|
89
|
+
`(use a fixed minute, e.g. "0 * * * *" hourly or "0 0 * * *" daily; ` +
|
|
90
|
+
`sub-hourly forms like "*/5 * * * *" or "0,30 * * * *" are rejected)`);
|
|
91
|
+
}
|
|
92
|
+
if (!Number.isInteger(maxItems) || maxItems < 1) {
|
|
93
|
+
throw new Error(`invalid --max-items '${maxItems}' (expected a positive integer)`);
|
|
94
|
+
}
|
|
95
|
+
if (!isSafeRoutinePath(output, cwd)) {
|
|
96
|
+
throw new Error(`invalid --output '${output}' (must be a relative path under .claude/routines/ ending in .yaml)`);
|
|
97
|
+
}
|
|
98
|
+
if (!/^[^/\s]+\/[^/\s]+$/.test(repository)) {
|
|
99
|
+
throw new Error(`invalid --repo '${repository}' (expected owner/repo)`);
|
|
100
|
+
}
|
|
101
|
+
const templatesRoot = options.templatesRoot ?? (await resolveTemplatesRoot());
|
|
102
|
+
const templatePath = join(templatesRoot, "routines", "pipeline.yaml.tmpl");
|
|
103
|
+
if (!(await pathExists(templatePath))) {
|
|
104
|
+
throw new Error(`bundled template not found: ${templatePath}`);
|
|
105
|
+
}
|
|
106
|
+
const template = await readFile(templatePath, "utf8");
|
|
107
|
+
const hosts = await collectSourceHosts(cwd, (m) => warn(`routine generate pipeline: ${m}`));
|
|
108
|
+
const rendered = renderPipelineRoutineTemplate(template, {
|
|
109
|
+
name,
|
|
110
|
+
repository,
|
|
111
|
+
cron,
|
|
112
|
+
timezone,
|
|
113
|
+
model,
|
|
114
|
+
maxItems,
|
|
115
|
+
networkAccessBlock: renderNetworkAccessBlock(hosts),
|
|
116
|
+
});
|
|
117
|
+
const destAbs = isAbsolute(output) ? output : join(cwd, output);
|
|
118
|
+
const destRel = isAbsolute(output) ? relative(cwd, output) : output;
|
|
119
|
+
if ((await pathExists(destAbs)) && !force) {
|
|
120
|
+
throw new Error(`output file already exists: ${destRel} (use --force to overwrite)`);
|
|
121
|
+
}
|
|
122
|
+
if ((await pathExists(destAbs)) && force) {
|
|
123
|
+
warn(`routine generate pipeline: overwriting existing file ${destRel}`);
|
|
124
|
+
}
|
|
125
|
+
await mkdir(dirname(destAbs), { recursive: true });
|
|
126
|
+
await writeFile(destAbs, rendered, "utf8");
|
|
127
|
+
log(`routine generate pipeline: wrote ${destRel}`);
|
|
128
|
+
log(`routine generate pipeline: name='${name}', repo='${repository}', cron='${cron}', model='${model}', max-items=${maxItems}`);
|
|
129
|
+
log("");
|
|
130
|
+
log("Routines has no declarative apply API — paste this routine into the Web UI by hand:");
|
|
131
|
+
log(" 1. Open https://claude.ai/code/routines and click New routine.");
|
|
132
|
+
log(" 2. Fill the form fields from the YAML (Name / Model / Repositories / Trigger / Permissions).");
|
|
133
|
+
log(" 3. For the multi-line Instructions and Setup script fields, extract them with yq:");
|
|
134
|
+
log(` yq -r '.instructions' ${destRel}`);
|
|
135
|
+
log(` yq -r '.environment.setup_script' ${destRel}`);
|
|
136
|
+
log(" 4. After registering, copy the issued routine_id (trig_xxxx) back into the YAML and set status: active.");
|
|
137
|
+
log("");
|
|
138
|
+
log("Or apply the schedule from the CLI with /schedule, e.g.:");
|
|
139
|
+
log(` /schedule create --name '${name}' --cron '${cron}' --repo '${repository}'`);
|
|
140
|
+
log("");
|
|
141
|
+
log("Single Claude session, no spawn (ADR-0020 D2): unlike the GHA combined-with-triage");
|
|
142
|
+
log("workflow, there is NO cross-agent review here — one Claude does every step.");
|
|
143
|
+
log(`Item caps are CLI-enforced (ADR-0020 D3e): triage --max-items ${maxItems} / items --limit ${maxItems}.`);
|
|
144
|
+
log("Output gate (ADR-0020 D3a): this routine writes to a claude/* branch / PR only — never main directly.");
|
|
145
|
+
return { outputPath: destRel };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Parse `routine generate pipeline` flags.
|
|
149
|
+
*
|
|
150
|
+
* `--output` defaults to `.claude/routines/<name>.yaml`, so it is resolved
|
|
151
|
+
* AFTER the loop once `--name` is known (a `--output` flag, if given, wins).
|
|
152
|
+
*/
|
|
153
|
+
export function parseGeneratePipelineRoutineArgs(args) {
|
|
154
|
+
let name = "feedradar-pipeline";
|
|
155
|
+
let repository = "<owner>/<repo>";
|
|
156
|
+
let cron = "0 * * * *"; // hourly — the Routines minimum interval.
|
|
157
|
+
let timezone = "UTC";
|
|
158
|
+
let model = "claude-sonnet-4-6";
|
|
159
|
+
let maxItems = PIPELINE_DEFAULT_MAX_ITEMS;
|
|
160
|
+
let output;
|
|
161
|
+
let force = false;
|
|
162
|
+
let help = false;
|
|
163
|
+
for (let i = 0; i < args.length; i++) {
|
|
164
|
+
const a = args[i];
|
|
165
|
+
if (a === "-h" || a === "--help") {
|
|
166
|
+
help = true;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (a === "--name") {
|
|
170
|
+
const value = args[++i];
|
|
171
|
+
if (value === undefined)
|
|
172
|
+
throw new Error(`option ${a} requires a value`);
|
|
173
|
+
name = value;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (a === "--repo" || a === "--repository") {
|
|
177
|
+
const value = args[++i];
|
|
178
|
+
if (value === undefined)
|
|
179
|
+
throw new Error(`option ${a} requires a value`);
|
|
180
|
+
repository = value;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (a === "--cron") {
|
|
184
|
+
const value = args[++i];
|
|
185
|
+
if (value === undefined)
|
|
186
|
+
throw new Error(`option ${a} requires a value`);
|
|
187
|
+
cron = value;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (a === "--timezone" || a === "--tz") {
|
|
191
|
+
const value = args[++i];
|
|
192
|
+
if (value === undefined)
|
|
193
|
+
throw new Error(`option ${a} requires a value`);
|
|
194
|
+
timezone = value;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (a === "--model") {
|
|
198
|
+
const value = args[++i];
|
|
199
|
+
if (value === undefined)
|
|
200
|
+
throw new Error(`option ${a} requires a value`);
|
|
201
|
+
if (!SUPPORTED_MODELS.includes(value)) {
|
|
202
|
+
throw new Error(`option --model expects one of: ${SUPPORTED_MODELS.join(" | ")}, got '${value}'`);
|
|
203
|
+
}
|
|
204
|
+
model = value;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (a === "--max-items") {
|
|
208
|
+
const value = args[++i];
|
|
209
|
+
if (value === undefined)
|
|
210
|
+
throw new Error(`option ${a} requires a value`);
|
|
211
|
+
const n = Number(value);
|
|
212
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
213
|
+
throw new Error(`option --max-items expects a positive integer, got '${value}'`);
|
|
214
|
+
}
|
|
215
|
+
maxItems = n;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (a === "--output") {
|
|
219
|
+
const value = args[++i];
|
|
220
|
+
if (value === undefined)
|
|
221
|
+
throw new Error(`option ${a} requires a value`);
|
|
222
|
+
output = value;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (a === "--force" || a === "-f") {
|
|
226
|
+
force = true;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (a?.startsWith("--") || a?.startsWith("-")) {
|
|
230
|
+
throw new Error(`unknown option: ${a}`);
|
|
231
|
+
}
|
|
232
|
+
throw new Error(`unexpected positional argument: ${a}`);
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
name,
|
|
236
|
+
repository,
|
|
237
|
+
cron,
|
|
238
|
+
timezone,
|
|
239
|
+
model,
|
|
240
|
+
maxItems,
|
|
241
|
+
output: output ?? join(".claude", "routines", `${name}.yaml`),
|
|
242
|
+
force,
|
|
243
|
+
help,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
export function printGeneratePipelineRoutineHelp(log) {
|
|
247
|
+
log("Usage: radar routine generate pipeline [options]");
|
|
248
|
+
log("");
|
|
249
|
+
log("Generates a Claude Code Routine YAML whose single session runs the FULL");
|
|
250
|
+
log("pipeline — `radar watch run` -> triage -> research -> review — IN SEQUENCE,");
|
|
251
|
+
log("processing items ONE AT A TIME (ADR-0020 D5 `pipeline`). It does NOT spawn");
|
|
252
|
+
log("other agents (D2), so the cross-agent review of the GHA combined-with-triage");
|
|
253
|
+
log("workflow is NOT present. Per-run item count is bounded by CLI flags (D3e).");
|
|
254
|
+
log("");
|
|
255
|
+
log("Options:");
|
|
256
|
+
log(' --name <name> Routine name (default: "feedradar-pipeline")');
|
|
257
|
+
log(" Also the default output filename.");
|
|
258
|
+
log(" --repo <owner/repo> Target repository (default: <owner>/<repo>)");
|
|
259
|
+
log(' --cron <expression> 5-field cron, min interval 1 HOUR (default: "0 * * * *")');
|
|
260
|
+
log(' Sub-hourly (e.g. "*/5 * * * *") is rejected.');
|
|
261
|
+
log(' --timezone <tz> Schedule timezone (default: "UTC")');
|
|
262
|
+
log(` --model <name> ${SUPPORTED_MODELS.join(" | ")}`);
|
|
263
|
+
log(" (default: claude-sonnet-4-6)");
|
|
264
|
+
log(` --max-items N Hard cap on items triaged/researched/reviewed per run`);
|
|
265
|
+
log(` (default: ${PIPELINE_DEFAULT_MAX_ITEMS}). Drives triage --max-items and items --limit.`);
|
|
266
|
+
log(" --output <path> Output file under .claude/routines/");
|
|
267
|
+
log(" (default: .claude/routines/<name>.yaml)");
|
|
268
|
+
log(" --force, -f Overwrite existing output file");
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Entry point invoked by `runRoutine` (in `src/cli/routine.ts`) when the user
|
|
272
|
+
* types `radar routine generate pipeline`. Translates parsed flags into
|
|
273
|
+
* `generatePipelineRoutine` arguments and surfaces validation errors with the
|
|
274
|
+
* `routine generate pipeline:` prefix to match the rest of the CLI.
|
|
275
|
+
*/
|
|
276
|
+
export async function runGeneratePipelineRoutine(args, io = {}, cwd = process.cwd()) {
|
|
277
|
+
const log = io.log ?? ((m) => console.log(m));
|
|
278
|
+
const error = io.error ?? ((m) => console.error(m));
|
|
279
|
+
let parsed;
|
|
280
|
+
try {
|
|
281
|
+
parsed = parseGeneratePipelineRoutineArgs(args);
|
|
282
|
+
}
|
|
283
|
+
catch (e) {
|
|
284
|
+
error(`routine generate pipeline: ${e instanceof Error ? e.message : String(e)}`);
|
|
285
|
+
return 2;
|
|
286
|
+
}
|
|
287
|
+
if (parsed.help) {
|
|
288
|
+
printGeneratePipelineRoutineHelp(log);
|
|
289
|
+
return 0;
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
await generatePipelineRoutine({
|
|
293
|
+
cwd,
|
|
294
|
+
name: parsed.name,
|
|
295
|
+
repository: parsed.repository,
|
|
296
|
+
cron: parsed.cron,
|
|
297
|
+
timezone: parsed.timezone,
|
|
298
|
+
model: parsed.model,
|
|
299
|
+
maxItems: parsed.maxItems,
|
|
300
|
+
output: parsed.output,
|
|
301
|
+
force: parsed.force,
|
|
302
|
+
io,
|
|
303
|
+
});
|
|
304
|
+
return 0;
|
|
305
|
+
}
|
|
306
|
+
catch (e) {
|
|
307
|
+
error(`routine generate pipeline: ${e instanceof Error ? e.message : String(e)}`);
|
|
308
|
+
return 1;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=generate-pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-pipeline.js","sourceRoot":"","sources":["../../../src/cli/routine/generate-pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sinks for the `routine generate watch` command's user-facing output.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `WorkflowIO` in `src/cli/workflow.ts`: the CLI binds these to
|
|
5
|
+
* `console.*` by default; tests inject capturing sinks so they can assert
|
|
6
|
+
* against printed lines without poking at stdio.
|
|
7
|
+
*/
|
|
8
|
+
export interface RoutineIO {
|
|
9
|
+
log?: (message: string) => void;
|
|
10
|
+
warn?: (message: string) => void;
|
|
11
|
+
error?: (message: string) => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Models supported by `--model`. Claude Routines run on the subscription
|
|
15
|
+
* Claude session (ADR-0020 D2), so the only valid models are Claude family
|
|
16
|
+
* identifiers. The literal list keeps the help text and validation in sync.
|
|
17
|
+
*/
|
|
18
|
+
export declare const SUPPORTED_MODELS: readonly ["claude-sonnet-4-6", "claude-opus-4-7", "claude-haiku-4-5"];
|
|
19
|
+
export type SupportedModel = (typeof SUPPORTED_MODELS)[number];
|
|
20
|
+
/**
|
|
21
|
+
* Collect the distinct outbound hosts a routine must reach to fetch the
|
|
22
|
+
* workspace's subscribed feeds.
|
|
23
|
+
*
|
|
24
|
+
* Reads `sources/*.yaml` from `cwd` and extracts the URL hostname of every
|
|
25
|
+
* source (skipping `npm-registry`, which has a fixed `registry.npmjs.org`
|
|
26
|
+
* host injected separately, and bare-package URLs that do not parse). The
|
|
27
|
+
* result drives the `network_access` block (`renderNetworkAccessBlock`):
|
|
28
|
+
* Claude Routines' Default **Trusted** mode returns `403`
|
|
29
|
+
* (`x-deny-reason: host_not_allowed`) for any host outside its built-in
|
|
30
|
+
* allowlist, so a watch/pipeline routine that fetches arbitrary RSS / HTTP
|
|
31
|
+
* feeds needs **Custom** access scoped to exactly these hosts (ADR-0020 D3c /
|
|
32
|
+
* ADR-0009 D5b — outbound limited to `sources/*.yaml` hosts; we do NOT open
|
|
33
|
+
* `Full`).
|
|
34
|
+
*
|
|
35
|
+
* Malformed source files are skipped (reported via `onError`) rather than
|
|
36
|
+
* aborting — mirrors `loadSources`. Returns a sorted, de-duplicated list.
|
|
37
|
+
*/
|
|
38
|
+
export declare function collectSourceHosts(cwd: string, onError?: (message: string) => void): Promise<string[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Render the `environment.network_access` YAML block for a routine template.
|
|
41
|
+
*
|
|
42
|
+
* Claude Routines network modes are **Trusted / Custom / Full** (NOT the
|
|
43
|
+
* `trusted / none / open` an earlier template comment claimed). The Default
|
|
44
|
+
* **Trusted** mode allowlists only a curated set of hosts and returns `403`
|
|
45
|
+
* (`x-deny-reason: host_not_allowed`) for anything else, so it CANNOT reach
|
|
46
|
+
* arbitrary subscribed feeds. We therefore emit **Custom** scoped to the
|
|
47
|
+
* subscribed-feed hosts (ADR-0020 D3c / ADR-0009 D5b) and never `Full`, which
|
|
48
|
+
* would defeat the limited-egress intent.
|
|
49
|
+
*
|
|
50
|
+
* `network_access` mirrors the Web UI "Network access" field; the routine
|
|
51
|
+
* YAML is pasted into that form by hand (Routines has no declarative apply
|
|
52
|
+
* API), so the host allowlist itself is registered in the Web UI's Custom
|
|
53
|
+
* editor. The emitted block records `custom` plus, as a comment, the exact
|
|
54
|
+
* host list to paste (or, when none can be enumerated, an explicit
|
|
55
|
+
* instruction to add the subscribed-feed hosts there).
|
|
56
|
+
*/
|
|
57
|
+
export declare function renderNetworkAccessBlock(hosts: string[]): string;
|
|
58
|
+
/**
|
|
59
|
+
* Validate a 5-field POSIX cron expression for a Claude Routine schedule.
|
|
60
|
+
*
|
|
61
|
+
* DRY note (ADR-0020 / #280): the structural 5-field grammar deliberately
|
|
62
|
+
* mirrors `isValidCron` in `src/cli/workflow/generate-watch.ts`, but the two
|
|
63
|
+
* are kept SEPARATE on purpose. GitHub Actions accepts arbitrary sub-hourly
|
|
64
|
+
* cron (e.g. `*\/5 * * * *`); Claude Routines enforce a **1-hour minimum
|
|
65
|
+
* interval**. Sharing one validator would force the workflow side to either
|
|
66
|
+
* over-reject (breaking existing 5-minute GHA crons) or this side to
|
|
67
|
+
* under-reject (generating a routine that the Web UI rejects on apply). So
|
|
68
|
+
* this validator layers a sub-hourly rejection (`isSubHourlyCron`) on top of
|
|
69
|
+
* the same structural check.
|
|
70
|
+
*
|
|
71
|
+
* Range bounds are NOT enforced (the Web UI rejects out-of-range on apply);
|
|
72
|
+
* keeping the check structural avoids a cron-parser dependency.
|
|
73
|
+
*/
|
|
74
|
+
export declare function isValidCron(expr: string): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Detect a cron expression that would fire more often than once per hour.
|
|
77
|
+
*
|
|
78
|
+
* Claude Routines reject sub-hourly schedules (ADR-0020: minimum interval is
|
|
79
|
+
* 1 hour). We catch the common sub-hourly shapes in the MINUTE field (field
|
|
80
|
+
* 0) before the file is written so the user gets a clear error instead of a
|
|
81
|
+
* Web UI rejection at apply time:
|
|
82
|
+
*
|
|
83
|
+
* - `*` in the minute field => fires every minute.
|
|
84
|
+
* - `*\/N` step in the minute field => fires N times within the hour.
|
|
85
|
+
* - a comma list with 2+ distinct minutes (e.g. `0,30 * * * *`) => fires
|
|
86
|
+
* multiple times per hour.
|
|
87
|
+
* - a range with `-` (e.g. `0-30 * * * *`) => fires every minute in range.
|
|
88
|
+
*
|
|
89
|
+
* A single fixed minute (`0`, `30`, `15`) is hourly-or-coarser and accepted.
|
|
90
|
+
* Assumes `isValidCron(expr)` already passed (5 well-formed fields).
|
|
91
|
+
*/
|
|
92
|
+
export declare function isSubHourlyCron(expr: string): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Validate that the requested `--output` path lands under `.claude/routines/`
|
|
95
|
+
* relative to the workspace root and ends in `.yaml`.
|
|
96
|
+
*
|
|
97
|
+
* DRY note (ADR-0020 / #280): the traversal + absolute-path rejection mirrors
|
|
98
|
+
* `isSafeWorkflowPath` in `src/cli/workflow/generate-watch.ts`, but the
|
|
99
|
+
* allowed directory (`.claude/routines/` vs `.github/workflows/`) and
|
|
100
|
+
* extension policy (`.yaml` only — the Web UI form is YAML, `.yml` would not
|
|
101
|
+
* match the README convention) differ, so the two are kept separate.
|
|
102
|
+
*/
|
|
103
|
+
export declare function isSafeRoutinePath(outputPath: string, cwd: string): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Render the bundled routine template by substituting `{{name}}` /
|
|
106
|
+
* `{{repository}}` / `{{cron}}` / `{{timezone}}` / `{{model}}` placeholders.
|
|
107
|
+
*
|
|
108
|
+
* Literal `replace` (not a templating engine): the placeholders are simple
|
|
109
|
+
* tokens and we must not expand any other `{{...}}`-looking text in the body.
|
|
110
|
+
* Exported for unit testing in isolation.
|
|
111
|
+
*/
|
|
112
|
+
export declare function renderWatchRoutineTemplate(template: string, values: {
|
|
113
|
+
name: string;
|
|
114
|
+
repository: string;
|
|
115
|
+
cron: string;
|
|
116
|
+
timezone: string;
|
|
117
|
+
model: string;
|
|
118
|
+
networkAccessBlock: string;
|
|
119
|
+
}): string;
|
|
120
|
+
export interface GenerateWatchRoutineOptions {
|
|
121
|
+
cwd: string;
|
|
122
|
+
name: string;
|
|
123
|
+
repository: string;
|
|
124
|
+
cron: string;
|
|
125
|
+
timezone: string;
|
|
126
|
+
model: SupportedModel;
|
|
127
|
+
output: string;
|
|
128
|
+
force: boolean;
|
|
129
|
+
/** Test seam: override the templates root location. */
|
|
130
|
+
templatesRoot?: string;
|
|
131
|
+
io?: RoutineIO;
|
|
132
|
+
}
|
|
133
|
+
export interface GenerateWatchRoutineResult {
|
|
134
|
+
/** Relative path (from `cwd`) of the file that was written. */
|
|
135
|
+
outputPath: string;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Core implementation of `radar routine generate watch` (ADR-0020 D5 `watch`).
|
|
139
|
+
*
|
|
140
|
+
* Validates the cron (5-field + 1-hour-minimum), output path, and repository,
|
|
141
|
+
* renders the bundled `watch.yaml.tmpl`, and writes it under
|
|
142
|
+
* `.claude/routines/`. The completion stdout tells the user how to paste the
|
|
143
|
+
* multi-line fields into the Web UI (yq extraction) and how to apply the
|
|
144
|
+
* schedule via `/schedule`, since Routines has no declarative apply API
|
|
145
|
+
* (ADR-0020 D1).
|
|
146
|
+
*/
|
|
147
|
+
export declare function generateWatchRoutine(options: GenerateWatchRoutineOptions): Promise<GenerateWatchRoutineResult>;
|
|
148
|
+
interface ParsedFlags {
|
|
149
|
+
name: string;
|
|
150
|
+
repository: string;
|
|
151
|
+
cron: string;
|
|
152
|
+
timezone: string;
|
|
153
|
+
model: SupportedModel;
|
|
154
|
+
output: string;
|
|
155
|
+
force: boolean;
|
|
156
|
+
help: boolean;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Parse `routine generate watch` flags.
|
|
160
|
+
*
|
|
161
|
+
* `--output` defaults to `.claude/routines/<name>.yaml`, so it is resolved
|
|
162
|
+
* AFTER the loop once `--name` is known (a `--output` flag, if given, wins).
|
|
163
|
+
*/
|
|
164
|
+
export declare function parseGenerateWatchRoutineArgs(args: string[]): ParsedFlags;
|
|
165
|
+
export declare function printGenerateWatchRoutineHelp(log: (m: string) => void): void;
|
|
166
|
+
/**
|
|
167
|
+
* Entry point invoked by `runRoutine` (in `src/cli/routine.ts`) when the user
|
|
168
|
+
* types `radar routine generate watch`. Translates parsed flags into
|
|
169
|
+
* `generateWatchRoutine` arguments and surfaces validation errors with the
|
|
170
|
+
* `routine generate watch:` prefix to match the rest of the CLI.
|
|
171
|
+
*/
|
|
172
|
+
export declare function runGenerateWatchRoutine(args: string[], io?: RoutineIO, cwd?: string): Promise<number>;
|
|
173
|
+
export {};
|
|
174
|
+
//# sourceMappingURL=generate-watch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-watch.d.ts","sourceRoot":"","sources":["../../../src/cli/routine/generate-watch.ts"],"names":[],"mappings":"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"}
|