@ozzylabs/feedradar 0.1.9 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +6 -3
- package/README.md +6 -3
- package/dist/agents/_boundary.d.ts +44 -0
- package/dist/agents/_boundary.d.ts.map +1 -1
- package/dist/agents/_boundary.js +80 -0
- package/dist/agents/_boundary.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 +162 -0
- package/dist/cli/routine/generate-pipeline.d.ts.map +1 -0
- package/dist/cli/routine/generate-pipeline.js +480 -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 +450 -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/templates/agents/AGENTS.md +1 -1
- package/dist/templates/routines/pipeline.yaml.tmpl +222 -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,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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"}
|
|
@@ -0,0 +1,450 @@
|
|
|
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 { loadSources } from "../../core/watcher.js";
|
|
5
|
+
/**
|
|
6
|
+
* Models supported by `--model`. Claude Routines run on the subscription
|
|
7
|
+
* Claude session (ADR-0020 D2), so the only valid models are Claude family
|
|
8
|
+
* identifiers. The literal list keeps the help text and validation in sync.
|
|
9
|
+
*/
|
|
10
|
+
export const SUPPORTED_MODELS = [
|
|
11
|
+
"claude-sonnet-4-6",
|
|
12
|
+
"claude-opus-4-7",
|
|
13
|
+
"claude-haiku-4-5",
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Collect the distinct outbound hosts a routine must reach to fetch the
|
|
17
|
+
* workspace's subscribed feeds.
|
|
18
|
+
*
|
|
19
|
+
* Reads `sources/*.yaml` from `cwd` and extracts the URL hostname of every
|
|
20
|
+
* source (skipping `npm-registry`, which has a fixed `registry.npmjs.org`
|
|
21
|
+
* host injected separately, and bare-package URLs that do not parse). The
|
|
22
|
+
* result drives the `network_access` block (`renderNetworkAccessBlock`):
|
|
23
|
+
* Claude Routines' Default **Trusted** mode returns `403`
|
|
24
|
+
* (`x-deny-reason: host_not_allowed`) for any host outside its built-in
|
|
25
|
+
* allowlist, so a watch/pipeline routine that fetches arbitrary RSS / HTTP
|
|
26
|
+
* feeds needs **Custom** access scoped to exactly these hosts (ADR-0020 D3c /
|
|
27
|
+
* ADR-0009 D5b — outbound limited to `sources/*.yaml` hosts; we do NOT open
|
|
28
|
+
* `Full`).
|
|
29
|
+
*
|
|
30
|
+
* Malformed source files are skipped (reported via `onError`) rather than
|
|
31
|
+
* aborting — mirrors `loadSources`. Returns a sorted, de-duplicated list.
|
|
32
|
+
*/
|
|
33
|
+
export async function collectSourceHosts(cwd, onError = () => { }) {
|
|
34
|
+
const sources = await loadSources(join(cwd, "sources"), onError);
|
|
35
|
+
const hosts = new Set();
|
|
36
|
+
for (const source of sources) {
|
|
37
|
+
// npm-registry accepts a bare-package form (`@scope/pkg`) that is not a
|
|
38
|
+
// URL; its real outbound host is the npm registry, added below.
|
|
39
|
+
if (source.kind === "npm-registry") {
|
|
40
|
+
hosts.add("registry.npmjs.org");
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
hosts.add(new URL(source.url).hostname.toLowerCase());
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Non-URL source.url (should not happen for non-npm kinds after schema
|
|
48
|
+
// validation) — skip rather than emit a broken allowlist entry.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return [...hosts].sort();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Render the `environment.network_access` YAML block for a routine template.
|
|
55
|
+
*
|
|
56
|
+
* Claude Routines network modes are **Trusted / Custom / Full** (NOT the
|
|
57
|
+
* `trusted / none / open` an earlier template comment claimed). The Default
|
|
58
|
+
* **Trusted** mode allowlists only a curated set of hosts and returns `403`
|
|
59
|
+
* (`x-deny-reason: host_not_allowed`) for anything else, so it CANNOT reach
|
|
60
|
+
* arbitrary subscribed feeds. We therefore emit **Custom** scoped to the
|
|
61
|
+
* subscribed-feed hosts (ADR-0020 D3c / ADR-0009 D5b) and never `Full`, which
|
|
62
|
+
* would defeat the limited-egress intent.
|
|
63
|
+
*
|
|
64
|
+
* `network_access` mirrors the Web UI "Network access" field; the routine
|
|
65
|
+
* YAML is pasted into that form by hand (Routines has no declarative apply
|
|
66
|
+
* API), so the host allowlist itself is registered in the Web UI's Custom
|
|
67
|
+
* editor. The emitted block records `custom` plus, as a comment, the exact
|
|
68
|
+
* host list to paste (or, when none can be enumerated, an explicit
|
|
69
|
+
* instruction to add the subscribed-feed hosts there).
|
|
70
|
+
*/
|
|
71
|
+
export function renderNetworkAccessBlock(hosts) {
|
|
72
|
+
const lines = [
|
|
73
|
+
" # Network access mode: Trusted / Custom / Full (Web UI: Environment > Network access).",
|
|
74
|
+
" # Trusted (Default): only a curated host allowlist; ANY other host gets",
|
|
75
|
+
" # 403 (x-deny-reason: host_not_allowed) — so it CANNOT fetch arbitrary feeds.",
|
|
76
|
+
" # Custom: you supply the allowlist — use this, scoped to your subscribed feeds.",
|
|
77
|
+
" # Full: unrestricted egress — NOT used (ADR-0020 D3c / ADR-0009 D5b limit",
|
|
78
|
+
" # outbound to sources/*.yaml hosts; never open the routine to any host).",
|
|
79
|
+
];
|
|
80
|
+
if (hosts.length > 0) {
|
|
81
|
+
lines.push(" # Register these subscribed-feed hosts (from sources/*.yaml) in the Web UI", " # Custom network access allowlist:");
|
|
82
|
+
for (const host of hosts) {
|
|
83
|
+
lines.push(` # - ${host}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
lines.push(" # No sources/*.yaml hosts could be enumerated at generate time. In the Web UI", " # Custom network access allowlist, add each subscribed-feed host this routine", " # must fetch (the hostnames from your sources/*.yaml `url:` fields).");
|
|
88
|
+
}
|
|
89
|
+
lines.push(" network_access: custom");
|
|
90
|
+
return lines.join("\n");
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Resolve the directory holding the bundled routine templates.
|
|
94
|
+
*
|
|
95
|
+
* Mirrors `resolveTemplatesRoot` in `src/cli/workflow/generate-watch.ts`:
|
|
96
|
+
* the compiled CLI lives at `dist/cli/routine/generate-watch.js`, so the
|
|
97
|
+
* bundled templates sit at `../../templates` relative to this module.
|
|
98
|
+
* During tests we may run from source (`src/cli/routine/generate-watch.ts`),
|
|
99
|
+
* where the same relative path lands at `src/templates/`.
|
|
100
|
+
*/
|
|
101
|
+
async function resolveTemplatesRoot() {
|
|
102
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
103
|
+
return resolve(here, "..", "..", "templates");
|
|
104
|
+
}
|
|
105
|
+
async function pathExists(p) {
|
|
106
|
+
try {
|
|
107
|
+
await access(p);
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Validate a 5-field POSIX cron expression for a Claude Routine schedule.
|
|
116
|
+
*
|
|
117
|
+
* DRY note (ADR-0020 / #280): the structural 5-field grammar deliberately
|
|
118
|
+
* mirrors `isValidCron` in `src/cli/workflow/generate-watch.ts`, but the two
|
|
119
|
+
* are kept SEPARATE on purpose. GitHub Actions accepts arbitrary sub-hourly
|
|
120
|
+
* cron (e.g. `*\/5 * * * *`); Claude Routines enforce a **1-hour minimum
|
|
121
|
+
* interval**. Sharing one validator would force the workflow side to either
|
|
122
|
+
* over-reject (breaking existing 5-minute GHA crons) or this side to
|
|
123
|
+
* under-reject (generating a routine that the Web UI rejects on apply). So
|
|
124
|
+
* this validator layers a sub-hourly rejection (`isSubHourlyCron`) on top of
|
|
125
|
+
* the same structural check.
|
|
126
|
+
*
|
|
127
|
+
* Range bounds are NOT enforced (the Web UI rejects out-of-range on apply);
|
|
128
|
+
* keeping the check structural avoids a cron-parser dependency.
|
|
129
|
+
*/
|
|
130
|
+
export function isValidCron(expr) {
|
|
131
|
+
const trimmed = expr.trim();
|
|
132
|
+
if (trimmed.length === 0)
|
|
133
|
+
return false;
|
|
134
|
+
const fields = trimmed.split(/\s+/);
|
|
135
|
+
if (fields.length !== 5)
|
|
136
|
+
return false;
|
|
137
|
+
const tokenPattern = /^(?:\*|\d+(?:-\d+)?)(?:\/\d+)?$/;
|
|
138
|
+
for (const field of fields) {
|
|
139
|
+
if (field.length === 0)
|
|
140
|
+
return false;
|
|
141
|
+
for (const token of field.split(",")) {
|
|
142
|
+
if (token.length === 0)
|
|
143
|
+
return false;
|
|
144
|
+
if (!tokenPattern.test(token))
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Detect a cron expression that would fire more often than once per hour.
|
|
152
|
+
*
|
|
153
|
+
* Claude Routines reject sub-hourly schedules (ADR-0020: minimum interval is
|
|
154
|
+
* 1 hour). We catch the common sub-hourly shapes in the MINUTE field (field
|
|
155
|
+
* 0) before the file is written so the user gets a clear error instead of a
|
|
156
|
+
* Web UI rejection at apply time:
|
|
157
|
+
*
|
|
158
|
+
* - `*` in the minute field => fires every minute.
|
|
159
|
+
* - `*\/N` step in the minute field => fires N times within the hour.
|
|
160
|
+
* - a comma list with 2+ distinct minutes (e.g. `0,30 * * * *`) => fires
|
|
161
|
+
* multiple times per hour.
|
|
162
|
+
* - a range with `-` (e.g. `0-30 * * * *`) => fires every minute in range.
|
|
163
|
+
*
|
|
164
|
+
* A single fixed minute (`0`, `30`, `15`) is hourly-or-coarser and accepted.
|
|
165
|
+
* Assumes `isValidCron(expr)` already passed (5 well-formed fields).
|
|
166
|
+
*/
|
|
167
|
+
export function isSubHourlyCron(expr) {
|
|
168
|
+
const fields = expr.trim().split(/\s+/);
|
|
169
|
+
const minute = fields[0];
|
|
170
|
+
if (minute === undefined)
|
|
171
|
+
return true;
|
|
172
|
+
if (minute === "*")
|
|
173
|
+
return true;
|
|
174
|
+
if (minute.includes("/"))
|
|
175
|
+
return true; // step => multiple firings/hour
|
|
176
|
+
if (minute.includes("-"))
|
|
177
|
+
return true; // range => every minute in range
|
|
178
|
+
// Comma list: more than one distinct minute => multiple firings/hour.
|
|
179
|
+
const minutes = new Set(minute.split(","));
|
|
180
|
+
if (minutes.size > 1)
|
|
181
|
+
return true;
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Validate that the requested `--output` path lands under `.claude/routines/`
|
|
186
|
+
* relative to the workspace root and ends in `.yaml`.
|
|
187
|
+
*
|
|
188
|
+
* DRY note (ADR-0020 / #280): the traversal + absolute-path rejection mirrors
|
|
189
|
+
* `isSafeWorkflowPath` in `src/cli/workflow/generate-watch.ts`, but the
|
|
190
|
+
* allowed directory (`.claude/routines/` vs `.github/workflows/`) and
|
|
191
|
+
* extension policy (`.yaml` only — the Web UI form is YAML, `.yml` would not
|
|
192
|
+
* match the README convention) differ, so the two are kept separate.
|
|
193
|
+
*/
|
|
194
|
+
export function isSafeRoutinePath(outputPath, cwd) {
|
|
195
|
+
if (isAbsolute(outputPath)) {
|
|
196
|
+
const allowedDir = resolve(cwd, ".claude", "routines");
|
|
197
|
+
const resolved = resolve(outputPath);
|
|
198
|
+
const rel = relative(allowedDir, resolved);
|
|
199
|
+
if (rel.startsWith("..") || isAbsolute(rel))
|
|
200
|
+
return false;
|
|
201
|
+
return /\.yaml$/i.test(resolved);
|
|
202
|
+
}
|
|
203
|
+
const normalized = normalize(outputPath);
|
|
204
|
+
if (normalized.split(/[\\/]/).includes(".."))
|
|
205
|
+
return false;
|
|
206
|
+
const required = `${join(".claude", "routines")}/`;
|
|
207
|
+
const unixified = normalized.replace(/\\/g, "/");
|
|
208
|
+
if (!unixified.startsWith(required.replace(/\\/g, "/")))
|
|
209
|
+
return false;
|
|
210
|
+
return /\.yaml$/i.test(unixified);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Render the bundled routine template by substituting `{{name}}` /
|
|
214
|
+
* `{{repository}}` / `{{cron}}` / `{{timezone}}` / `{{model}}` placeholders.
|
|
215
|
+
*
|
|
216
|
+
* Literal `replace` (not a templating engine): the placeholders are simple
|
|
217
|
+
* tokens and we must not expand any other `{{...}}`-looking text in the body.
|
|
218
|
+
* Exported for unit testing in isolation.
|
|
219
|
+
*/
|
|
220
|
+
export function renderWatchRoutineTemplate(template, values) {
|
|
221
|
+
return template
|
|
222
|
+
.replace(/\{\{name\}\}/g, values.name)
|
|
223
|
+
.replace(/\{\{repository\}\}/g, values.repository)
|
|
224
|
+
.replace(/\{\{cron\}\}/g, values.cron)
|
|
225
|
+
.replace(/\{\{timezone\}\}/g, values.timezone)
|
|
226
|
+
.replace(/\{\{model\}\}/g, values.model)
|
|
227
|
+
.replace(/\{\{networkAccessBlock\}\}/g, values.networkAccessBlock);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Core implementation of `radar routine generate watch` (ADR-0020 D5 `watch`).
|
|
231
|
+
*
|
|
232
|
+
* Validates the cron (5-field + 1-hour-minimum), output path, and repository,
|
|
233
|
+
* renders the bundled `watch.yaml.tmpl`, and writes it under
|
|
234
|
+
* `.claude/routines/`. The completion stdout tells the user how to paste the
|
|
235
|
+
* multi-line fields into the Web UI (yq extraction) and how to apply the
|
|
236
|
+
* schedule via `/schedule`, since Routines has no declarative apply API
|
|
237
|
+
* (ADR-0020 D1).
|
|
238
|
+
*/
|
|
239
|
+
export async function generateWatchRoutine(options) {
|
|
240
|
+
const { cwd, name, repository, cron, timezone, model, output, force } = options;
|
|
241
|
+
const log = options.io?.log ?? ((m) => console.log(m));
|
|
242
|
+
const warn = options.io?.warn ?? ((m) => console.warn(m));
|
|
243
|
+
if (!isValidCron(cron)) {
|
|
244
|
+
throw new Error(`invalid --cron expression '${cron}' (expected 5-field POSIX cron, e.g. "0 * * * *")`);
|
|
245
|
+
}
|
|
246
|
+
if (isSubHourlyCron(cron)) {
|
|
247
|
+
throw new Error(`invalid --cron '${cron}': Claude Routines require a minimum interval of 1 hour ` +
|
|
248
|
+
`(use a fixed minute, e.g. "0 * * * *" hourly or "0 0 * * *" daily; ` +
|
|
249
|
+
`sub-hourly forms like "*/5 * * * *" or "0,30 * * * *" are rejected)`);
|
|
250
|
+
}
|
|
251
|
+
if (!isSafeRoutinePath(output, cwd)) {
|
|
252
|
+
throw new Error(`invalid --output '${output}' (must be a relative path under .claude/routines/ ending in .yaml)`);
|
|
253
|
+
}
|
|
254
|
+
if (!/^[^/\s]+\/[^/\s]+$/.test(repository)) {
|
|
255
|
+
throw new Error(`invalid --repo '${repository}' (expected owner/repo)`);
|
|
256
|
+
}
|
|
257
|
+
const templatesRoot = options.templatesRoot ?? (await resolveTemplatesRoot());
|
|
258
|
+
const templatePath = join(templatesRoot, "routines", "watch.yaml.tmpl");
|
|
259
|
+
if (!(await pathExists(templatePath))) {
|
|
260
|
+
throw new Error(`bundled template not found: ${templatePath}`);
|
|
261
|
+
}
|
|
262
|
+
const template = await readFile(templatePath, "utf8");
|
|
263
|
+
const hosts = await collectSourceHosts(cwd, (m) => warn(`routine generate watch: ${m}`));
|
|
264
|
+
const rendered = renderWatchRoutineTemplate(template, {
|
|
265
|
+
name,
|
|
266
|
+
repository,
|
|
267
|
+
cron,
|
|
268
|
+
timezone,
|
|
269
|
+
model,
|
|
270
|
+
networkAccessBlock: renderNetworkAccessBlock(hosts),
|
|
271
|
+
});
|
|
272
|
+
const destAbs = isAbsolute(output) ? output : join(cwd, output);
|
|
273
|
+
const destRel = isAbsolute(output) ? relative(cwd, output) : output;
|
|
274
|
+
if ((await pathExists(destAbs)) && !force) {
|
|
275
|
+
throw new Error(`output file already exists: ${destRel} (use --force to overwrite)`);
|
|
276
|
+
}
|
|
277
|
+
if ((await pathExists(destAbs)) && force) {
|
|
278
|
+
warn(`routine generate watch: overwriting existing file ${destRel}`);
|
|
279
|
+
}
|
|
280
|
+
await mkdir(dirname(destAbs), { recursive: true });
|
|
281
|
+
await writeFile(destAbs, rendered, "utf8");
|
|
282
|
+
log(`routine generate watch: wrote ${destRel}`);
|
|
283
|
+
log(`routine generate watch: name='${name}', repo='${repository}', cron='${cron}', model='${model}'`);
|
|
284
|
+
log("");
|
|
285
|
+
log("Routines has no declarative apply API — paste this routine into the Web UI by hand:");
|
|
286
|
+
log(" 1. Open https://claude.ai/code/routines and click New routine.");
|
|
287
|
+
log(" 2. Fill the form fields from the YAML (Name / Model / Repositories / Trigger / Permissions).");
|
|
288
|
+
log(" 3. For the multi-line Instructions and Setup script fields, extract them with yq:");
|
|
289
|
+
log(` yq -r '.instructions' ${destRel}`);
|
|
290
|
+
log(` yq -r '.environment.setup_script' ${destRel}`);
|
|
291
|
+
log(" 4. After registering, copy the issued routine_id (trig_xxxx) back into the YAML and set status: active.");
|
|
292
|
+
log("");
|
|
293
|
+
log("Note on /schedule (Claude Code): it is conversational — `/schedule <description>`");
|
|
294
|
+
log("to create one, plus `list` / `update` / `run` subcommands. There is no flag-based");
|
|
295
|
+
log("form (no `--name` / `--cron` / `--repo` arguments). It also cannot ingest this YAML");
|
|
296
|
+
log("verbatim, so for the long Instructions field the Web UI paste flow above (yq");
|
|
297
|
+
log("extraction) is the practical path. Finally, the unrestricted-git-push permission an");
|
|
298
|
+
log("auto-merge routine needs is set only via the Web UI 'Allow unrestricted branch");
|
|
299
|
+
log("pushes' toggle — /schedule cannot configure it.");
|
|
300
|
+
log("");
|
|
301
|
+
log("Output gate (ADR-0020 D3a): this routine writes to a claude/* branch / PR only — never main directly.");
|
|
302
|
+
return { outputPath: destRel };
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Parse `routine generate watch` flags.
|
|
306
|
+
*
|
|
307
|
+
* `--output` defaults to `.claude/routines/<name>.yaml`, so it is resolved
|
|
308
|
+
* AFTER the loop once `--name` is known (a `--output` flag, if given, wins).
|
|
309
|
+
*/
|
|
310
|
+
export function parseGenerateWatchRoutineArgs(args) {
|
|
311
|
+
let name = "feedradar-watch";
|
|
312
|
+
let repository = "<owner>/<repo>";
|
|
313
|
+
let cron = "0 * * * *"; // hourly — the Routines minimum interval.
|
|
314
|
+
let timezone = "UTC";
|
|
315
|
+
let model = "claude-sonnet-4-6";
|
|
316
|
+
let output;
|
|
317
|
+
let force = false;
|
|
318
|
+
let help = false;
|
|
319
|
+
for (let i = 0; i < args.length; i++) {
|
|
320
|
+
const a = args[i];
|
|
321
|
+
if (a === "-h" || a === "--help") {
|
|
322
|
+
help = true;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (a === "--name") {
|
|
326
|
+
const value = args[++i];
|
|
327
|
+
if (value === undefined)
|
|
328
|
+
throw new Error(`option ${a} requires a value`);
|
|
329
|
+
name = value;
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
if (a === "--repo" || a === "--repository") {
|
|
333
|
+
const value = args[++i];
|
|
334
|
+
if (value === undefined)
|
|
335
|
+
throw new Error(`option ${a} requires a value`);
|
|
336
|
+
repository = value;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (a === "--cron") {
|
|
340
|
+
const value = args[++i];
|
|
341
|
+
if (value === undefined)
|
|
342
|
+
throw new Error(`option ${a} requires a value`);
|
|
343
|
+
cron = value;
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
if (a === "--timezone" || a === "--tz") {
|
|
347
|
+
const value = args[++i];
|
|
348
|
+
if (value === undefined)
|
|
349
|
+
throw new Error(`option ${a} requires a value`);
|
|
350
|
+
timezone = value;
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (a === "--model") {
|
|
354
|
+
const value = args[++i];
|
|
355
|
+
if (value === undefined)
|
|
356
|
+
throw new Error(`option ${a} requires a value`);
|
|
357
|
+
if (!SUPPORTED_MODELS.includes(value)) {
|
|
358
|
+
throw new Error(`option --model expects one of: ${SUPPORTED_MODELS.join(" | ")}, got '${value}'`);
|
|
359
|
+
}
|
|
360
|
+
model = value;
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
if (a === "--output") {
|
|
364
|
+
const value = args[++i];
|
|
365
|
+
if (value === undefined)
|
|
366
|
+
throw new Error(`option ${a} requires a value`);
|
|
367
|
+
output = value;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (a === "--force" || a === "-f") {
|
|
371
|
+
force = true;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (a?.startsWith("--") || a?.startsWith("-")) {
|
|
375
|
+
throw new Error(`unknown option: ${a}`);
|
|
376
|
+
}
|
|
377
|
+
throw new Error(`unexpected positional argument: ${a}`);
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
name,
|
|
381
|
+
repository,
|
|
382
|
+
cron,
|
|
383
|
+
timezone,
|
|
384
|
+
model,
|
|
385
|
+
output: output ?? join(".claude", "routines", `${name}.yaml`),
|
|
386
|
+
force,
|
|
387
|
+
help,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
export function printGenerateWatchRoutineHelp(log) {
|
|
391
|
+
log("Usage: radar routine generate watch [options]");
|
|
392
|
+
log("");
|
|
393
|
+
log("Generates a Claude Code Routine YAML that runs `radar watch run` on a schedule");
|
|
394
|
+
log("and commits detected items/state to a claude/* branch (ADR-0020 D5 `watch`).");
|
|
395
|
+
log("The routine completes in one Claude session — it does NOT spawn other agents.");
|
|
396
|
+
log("");
|
|
397
|
+
log("Options:");
|
|
398
|
+
log(' --name <name> Routine name (default: "feedradar-watch")');
|
|
399
|
+
log(" Also the default output filename.");
|
|
400
|
+
log(" --repo <owner/repo> Target repository (default: <owner>/<repo>)");
|
|
401
|
+
log(' --cron <expression> 5-field cron, min interval 1 HOUR (default: "0 * * * *")');
|
|
402
|
+
log(' Sub-hourly (e.g. "*/5 * * * *") is rejected.');
|
|
403
|
+
log(' --timezone <tz> Schedule timezone (default: "UTC")');
|
|
404
|
+
log(` --model <name> ${SUPPORTED_MODELS.join(" | ")}`);
|
|
405
|
+
log(" (default: claude-sonnet-4-6)");
|
|
406
|
+
log(" --output <path> Output file under .claude/routines/");
|
|
407
|
+
log(" (default: .claude/routines/<name>.yaml)");
|
|
408
|
+
log(" --force, -f Overwrite existing output file");
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Entry point invoked by `runRoutine` (in `src/cli/routine.ts`) when the user
|
|
412
|
+
* types `radar routine generate watch`. Translates parsed flags into
|
|
413
|
+
* `generateWatchRoutine` arguments and surfaces validation errors with the
|
|
414
|
+
* `routine generate watch:` prefix to match the rest of the CLI.
|
|
415
|
+
*/
|
|
416
|
+
export async function runGenerateWatchRoutine(args, io = {}, cwd = process.cwd()) {
|
|
417
|
+
const log = io.log ?? ((m) => console.log(m));
|
|
418
|
+
const error = io.error ?? ((m) => console.error(m));
|
|
419
|
+
let parsed;
|
|
420
|
+
try {
|
|
421
|
+
parsed = parseGenerateWatchRoutineArgs(args);
|
|
422
|
+
}
|
|
423
|
+
catch (e) {
|
|
424
|
+
error(`routine generate watch: ${e instanceof Error ? e.message : String(e)}`);
|
|
425
|
+
return 2;
|
|
426
|
+
}
|
|
427
|
+
if (parsed.help) {
|
|
428
|
+
printGenerateWatchRoutineHelp(log);
|
|
429
|
+
return 0;
|
|
430
|
+
}
|
|
431
|
+
try {
|
|
432
|
+
await generateWatchRoutine({
|
|
433
|
+
cwd,
|
|
434
|
+
name: parsed.name,
|
|
435
|
+
repository: parsed.repository,
|
|
436
|
+
cron: parsed.cron,
|
|
437
|
+
timezone: parsed.timezone,
|
|
438
|
+
model: parsed.model,
|
|
439
|
+
output: parsed.output,
|
|
440
|
+
force: parsed.force,
|
|
441
|
+
io,
|
|
442
|
+
});
|
|
443
|
+
return 0;
|
|
444
|
+
}
|
|
445
|
+
catch (e) {
|
|
446
|
+
error(`routine generate watch: ${e instanceof Error ? e.message : String(e)}`);
|
|
447
|
+
return 1;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
//# sourceMappingURL=generate-watch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-watch.js","sourceRoot":"","sources":["../../../src/cli/routine/generate-watch.ts"],"names":[],"mappings":"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"}
|