@ozzylabs/feedradar 0.2.0 → 0.2.2

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 (164) hide show
  1. package/README.ja.md +51 -13
  2. package/README.md +51 -13
  3. package/dist/agents/_boundary.d.ts +21 -0
  4. package/dist/agents/_boundary.d.ts.map +1 -1
  5. package/dist/agents/_boundary.js +34 -0
  6. package/dist/agents/_boundary.js.map +1 -1
  7. package/dist/agents/claude-code.d.ts.map +1 -1
  8. package/dist/agents/claude-code.js +14 -6
  9. package/dist/agents/claude-code.js.map +1 -1
  10. package/dist/agents/codex-cli.d.ts.map +1 -1
  11. package/dist/agents/codex-cli.js +13 -7
  12. package/dist/agents/codex-cli.js.map +1 -1
  13. package/dist/agents/copilot.d.ts.map +1 -1
  14. package/dist/agents/copilot.js +13 -6
  15. package/dist/agents/copilot.js.map +1 -1
  16. package/dist/agents/gemini-cli.d.ts.map +1 -1
  17. package/dist/agents/gemini-cli.js +13 -6
  18. package/dist/agents/gemini-cli.js.map +1 -1
  19. package/dist/agents/types.d.ts +26 -0
  20. package/dist/agents/types.d.ts.map +1 -1
  21. package/dist/claude-skills/dismiss/SKILL.md +4 -4
  22. package/dist/claude-skills/research/SKILL.md +2 -3
  23. package/dist/claude-skills/review/SKILL.md +2 -2
  24. package/dist/claude-skills/update/SKILL.md +7 -7
  25. package/dist/cli/_locale.d.ts +96 -0
  26. package/dist/cli/_locale.d.ts.map +1 -0
  27. package/dist/cli/_locale.js +130 -0
  28. package/dist/cli/_locale.js.map +1 -0
  29. package/dist/cli/_progress.d.ts +30 -1
  30. package/dist/cli/_progress.d.ts.map +1 -1
  31. package/dist/cli/_progress.js +9 -1
  32. package/dist/cli/_progress.js.map +1 -1
  33. package/dist/cli/dismiss.d.ts.map +1 -1
  34. package/dist/cli/dismiss.js +61 -54
  35. package/dist/cli/dismiss.js.map +1 -1
  36. package/dist/cli/doctor.d.ts +8 -0
  37. package/dist/cli/doctor.d.ts.map +1 -1
  38. package/dist/cli/doctor.js +91 -60
  39. package/dist/cli/doctor.js.map +1 -1
  40. package/dist/cli/index.d.ts +36 -1
  41. package/dist/cli/index.d.ts.map +1 -1
  42. package/dist/cli/index.js +79 -18
  43. package/dist/cli/index.js.map +1 -1
  44. package/dist/cli/init.d.ts +15 -0
  45. package/dist/cli/init.d.ts.map +1 -1
  46. package/dist/cli/init.js +149 -51
  47. package/dist/cli/init.js.map +1 -1
  48. package/dist/cli/items.d.ts.map +1 -1
  49. package/dist/cli/items.js +51 -30
  50. package/dist/cli/items.js.map +1 -1
  51. package/dist/cli/research.d.ts.map +1 -1
  52. package/dist/cli/research.js +138 -109
  53. package/dist/cli/research.js.map +1 -1
  54. package/dist/cli/review.d.ts.map +1 -1
  55. package/dist/cli/review.js +114 -92
  56. package/dist/cli/review.js.map +1 -1
  57. package/dist/cli/routine/fire.d.ts +3 -2
  58. package/dist/cli/routine/fire.d.ts.map +1 -1
  59. package/dist/cli/routine/fire.js +30 -25
  60. package/dist/cli/routine/fire.js.map +1 -1
  61. package/dist/cli/routine/generate-pipeline.d.ts +70 -1
  62. package/dist/cli/routine/generate-pipeline.d.ts.map +1 -1
  63. package/dist/cli/routine/generate-pipeline.js +273 -44
  64. package/dist/cli/routine/generate-pipeline.js.map +1 -1
  65. package/dist/cli/routine/generate-watch.d.ts +10 -1
  66. package/dist/cli/routine/generate-watch.d.ts.map +1 -1
  67. package/dist/cli/routine/generate-watch.js +49 -37
  68. package/dist/cli/routine/generate-watch.js.map +1 -1
  69. package/dist/cli/routine.d.ts.map +1 -1
  70. package/dist/cli/routine.js +28 -24
  71. package/dist/cli/routine.js.map +1 -1
  72. package/dist/cli/source.d.ts.map +1 -1
  73. package/dist/cli/source.js +206 -182
  74. package/dist/cli/source.js.map +1 -1
  75. package/dist/cli/triage.d.ts.map +1 -1
  76. package/dist/cli/triage.js +146 -130
  77. package/dist/cli/triage.js.map +1 -1
  78. package/dist/cli/undismiss.d.ts.map +1 -1
  79. package/dist/cli/undismiss.js +32 -25
  80. package/dist/cli/undismiss.js.map +1 -1
  81. package/dist/cli/update.d.ts.map +1 -1
  82. package/dist/cli/update.js +77 -61
  83. package/dist/cli/update.js.map +1 -1
  84. package/dist/cli/watch.d.ts.map +1 -1
  85. package/dist/cli/watch.js +71 -31
  86. package/dist/cli/watch.js.map +1 -1
  87. package/dist/cli/workflow/generate-combined-with-triage.d.ts +9 -2
  88. package/dist/cli/workflow/generate-combined-with-triage.d.ts.map +1 -1
  89. package/dist/cli/workflow/generate-combined-with-triage.js +120 -71
  90. package/dist/cli/workflow/generate-combined-with-triage.js.map +1 -1
  91. package/dist/cli/workflow/generate-combined.d.ts +8 -1
  92. package/dist/cli/workflow/generate-combined.d.ts.map +1 -1
  93. package/dist/cli/workflow/generate-combined.js +39 -33
  94. package/dist/cli/workflow/generate-combined.js.map +1 -1
  95. package/dist/cli/workflow/generate-watch.d.ts +10 -1
  96. package/dist/cli/workflow/generate-watch.d.ts.map +1 -1
  97. package/dist/cli/workflow/generate-watch.js +37 -30
  98. package/dist/cli/workflow/generate-watch.js.map +1 -1
  99. package/dist/cli/workflow.d.ts.map +1 -1
  100. package/dist/cli/workflow.js +28 -23
  101. package/dist/cli/workflow.js.map +1 -1
  102. package/dist/core/config.d.ts +2 -1
  103. package/dist/core/config.d.ts.map +1 -1
  104. package/dist/core/config.js +14 -4
  105. package/dist/core/config.js.map +1 -1
  106. package/dist/core/feeds/html-js.d.ts.map +1 -1
  107. package/dist/core/feeds/html-js.js +16 -9
  108. package/dist/core/feeds/html-js.js.map +1 -1
  109. package/dist/core/feeds/types.d.ts +9 -0
  110. package/dist/core/feeds/types.d.ts.map +1 -1
  111. package/dist/core/locale.d.ts +69 -0
  112. package/dist/core/locale.d.ts.map +1 -0
  113. package/dist/core/locale.js +74 -0
  114. package/dist/core/locale.js.map +1 -0
  115. package/dist/core/watcher.d.ts +11 -0
  116. package/dist/core/watcher.d.ts.map +1 -1
  117. package/dist/core/watcher.js +21 -5
  118. package/dist/core/watcher.js.map +1 -1
  119. package/dist/i18n/index.d.ts +57 -0
  120. package/dist/i18n/index.d.ts.map +1 -0
  121. package/dist/i18n/index.js +49 -0
  122. package/dist/i18n/index.js.map +1 -0
  123. package/dist/i18n/messages/en.d.ts +993 -0
  124. package/dist/i18n/messages/en.d.ts.map +1 -0
  125. package/dist/i18n/messages/en.js +1096 -0
  126. package/dist/i18n/messages/en.js.map +1 -0
  127. package/dist/i18n/messages/ja.d.ts +13 -0
  128. package/dist/i18n/messages/ja.d.ts.map +1 -0
  129. package/dist/i18n/messages/ja.js +970 -0
  130. package/dist/i18n/messages/ja.js.map +1 -0
  131. package/dist/schemas/config.d.ts +7 -0
  132. package/dist/schemas/config.d.ts.map +1 -1
  133. package/dist/schemas/config.js +5 -0
  134. package/dist/schemas/config.js.map +1 -1
  135. package/dist/schemas/recipe.d.ts +1 -1
  136. package/dist/schemas/source.d.ts +3 -3
  137. package/dist/skills/research/SKILL.md +13 -12
  138. package/dist/skills/review/SKILL.md +13 -12
  139. package/dist/skills/update/SKILL.md +19 -19
  140. package/dist/templates/en/agents/AGENTS.md +284 -0
  141. package/dist/templates/en/claude/CLAUDE.md +5 -0
  142. package/dist/templates/en/default.md +16 -0
  143. package/dist/templates/en/digest.md +66 -0
  144. package/dist/templates/en/feedradar.md +235 -0
  145. package/dist/templates/{routines → en/routines}/pipeline.yaml.tmpl +30 -41
  146. package/dist/templates/{routines → en/routines}/watch-daily.yaml +12 -15
  147. package/dist/templates/{routines → en/routines}/watch.yaml.tmpl +11 -14
  148. package/dist/templates/{workflows → en/workflows}/combined-with-triage.template.yaml.tmpl +3 -3
  149. package/dist/templates/{workflows → en/workflows}/combined.template.yaml.tmpl +6 -6
  150. package/dist/templates/{workflows → en/workflows}/watch.template.yaml.tmpl +8 -8
  151. package/dist/templates/{workflows → en/workflows}/watch.yaml +3 -3
  152. package/dist/templates/{agents → ja/agents}/AGENTS.md +16 -16
  153. package/dist/templates/{digest.md → ja/digest.md} +5 -6
  154. package/dist/templates/{feedradar.md → ja/feedradar.md} +12 -12
  155. package/dist/templates/ja/routines/pipeline.yaml.tmpl +211 -0
  156. package/dist/templates/ja/routines/watch-daily.yaml +151 -0
  157. package/dist/templates/ja/routines/watch.yaml.tmpl +145 -0
  158. package/dist/templates/ja/workflows/combined-with-triage.template.yaml.tmpl +123 -0
  159. package/dist/templates/ja/workflows/combined.template.yaml.tmpl +109 -0
  160. package/dist/templates/ja/workflows/watch.template.yaml.tmpl +100 -0
  161. package/dist/templates/ja/workflows/watch.yaml +73 -0
  162. package/package.json +1 -1
  163. /package/dist/templates/{claude → ja/claude}/CLAUDE.md +0 -0
  164. /package/dist/templates/{default.md → ja/default.md} +0 -0
@@ -0,0 +1,1096 @@
1
+ /**
2
+ * English message catalog — the source of truth for the i18n layer (ADR-0021,
3
+ * epic #307 P2).
4
+ *
5
+ * `en` defines the canonical key set and value shapes; every other locale
6
+ * catalog (currently {@link import("./ja.js").ja}) is required by the
7
+ * {@link Messages} type to mirror these keys exactly. A value is either:
8
+ *
9
+ * - a plain `string` (no interpolation), or
10
+ * - a function `(params) => string` that receives a typed params object and
11
+ * returns the rendered string.
12
+ *
13
+ * The function form is the single, unified interpolation scheme for this layer
14
+ * (no `{name}` placeholder string-replacement, no template engine, no new
15
+ * runtime dependency): parameters are ordinary typed function arguments, so a
16
+ * missing or misspelled param is a *compile* error, and `createTranslator`'s
17
+ * `t(key, params?)` signature can derive its param type straight from the
18
+ * catalog entry.
19
+ *
20
+ * Keys are namespaced by domain (`common.*`, `cli.*`) to keep future additions
21
+ * collision-free as later P3/P4/P5 issues add their own message families. This
22
+ * PR only fills the keys needed to prove the wiring on `src/cli/index.ts`'s
23
+ * global help / version / unknown-command paths.
24
+ */
25
+ export const en = {
26
+ // --- global help (radar --help / radar / radar help) ----------------------
27
+ /** First line of `radar --help`: the product tagline. */
28
+ "cli.help.tagline": "FeedRadar — Multi-agent CLI for blog/release feed research",
29
+ /** Usage synopsis line. */
30
+ "cli.help.usage": "Usage: radar <command> [options]",
31
+ /** Section heading listing the subcommands. */
32
+ "cli.help.commandsHeading": "Commands:",
33
+ /** Section heading listing the global options. */
34
+ "cli.help.optionsHeading": "Options:",
35
+ /** `-h, --help` option description. */
36
+ "cli.help.optionHelp": "Show this help",
37
+ /** `-v, --version` option description. */
38
+ "cli.help.optionVersion": "Show version",
39
+ /** `--lang <en|ja>` option description. */
40
+ "cli.help.optionLang": "UI language (overrides RADAR_LANG / config)",
41
+ // --- unknown command error ------------------------------------------------
42
+ /** stderr line when an unrecognized subcommand is given. */
43
+ "cli.error.unknownCommand": ({ command }) => `radar: unknown command '${command}'`,
44
+ /** Follow-up hint pointing the user at `radar --help`. */
45
+ "cli.error.unknownCommandHint": "Run 'radar --help' for available commands.",
46
+ // --- progress phase markers (ProgressReporter, ADR-0015, #313) ------------
47
+ // User-facing phase labels emitted by `research` / `review` / `update` via
48
+ // `progress.phase()` / `start()` / `succeed()` / `fail()`. The verb-forms
49
+ // follow ADR-0015 D4 (`Loaded …`, `Spawning …`, `Agent running…`, `Agent
50
+ // completed (…)`, `Status: … → …`). Agent stdout/stderr pass-through
51
+ // (`reporter.raw`) is NOT translated — it is external output forwarded
52
+ // verbatim.
53
+ /** "Loaded item: <id>" — single-item PRE phase marker. */
54
+ "cli.progress.loadedItem": ({ id }) => `Loaded item: ${id}`,
55
+ /** "Loaded N items" — digest/batch PRE phase marker. */
56
+ "cli.progress.loadedItems": ({ count }) => `Loaded ${count} items`,
57
+ /** "Loaded template: <id>.md" — template-resolved phase marker. */
58
+ "cli.progress.loadedTemplate": ({ templateId }) => `Loaded template: ${templateId}.md`,
59
+ /** "Spawning <agent>" — agent-spawn phase marker. */
60
+ "cli.progress.spawning": ({ agent }) => `Spawning ${agent}`,
61
+ /** Spinner label while the agent runs. */
62
+ "cli.progress.agentRunning": "Agent running",
63
+ /** "Agent completed (exit <code>)" — agent success line. */
64
+ "cli.progress.agentCompleted": ({ exitCode }) => `Agent completed (exit ${exitCode})`,
65
+ /** Agent-failure spinner-fail label. */
66
+ "cli.progress.agentFailed": "Agent failed",
67
+ /** "Frontmatter validated" — schema-check phase marker. */
68
+ "cli.progress.frontmatterValidated": "Frontmatter validated",
69
+ /** "Status: <from> → <to>" — state-machine transition phase marker. */
70
+ "cli.progress.statusTransition": ({ from, to }) => `Status: ${from} → ${to}`,
71
+ // --- watch-flow progress markers (#337, deferred from #313) ---------------
72
+ // Per-source phase markers emitted by `radar watch run` (`src/core/watcher.ts`)
73
+ // and the html-js Playwright adapter (`src/core/feeds/html-js.ts`). The
74
+ // embedded values (source id, page counters, selector name, elapsed mm:ss)
75
+ // are functional fields and stay verbatim; only the surrounding prose is
76
+ // translated. Internal debug logs are NOT translated (ADR-0021 boundary).
77
+ /**
78
+ * "[<source-id>] <facet>Page <i>/<n>: <items> items fetched" — paginating-
79
+ * adapter (json-api) page phase marker. `facet` is a pre-built functional
80
+ * prefix (e.g. `year=2018 (15/23) `) or empty; the page / item counters are
81
+ * functional and stay verbatim. Only the "Page … items fetched" prose is
82
+ * translated.
83
+ */
84
+ "cli.progress.watchPage": ({ sourceId, facet, page, pageTotal, items, }) => `[${sourceId}] ${facet}Page ${page}/${pageTotal}: ${items} items fetched`,
85
+ /** "[<source-id>] Completed: <total> total, <fresh> new" — per-source success line. */
86
+ "cli.progress.watchSourceCompleted": ({ sourceId, total, fresh, }) => `[${sourceId}] Completed: ${total} total, ${fresh} new`,
87
+ /** "Still waiting for "<sel>"… [<mm:ss>]" — long html-js selector-wait reminder. */
88
+ "cli.progress.stillWaiting": ({ selector, elapsed, }) => `Still waiting for "${selector}"… [${elapsed}]`,
89
+ /** "[<source-id>] Fetching…" — per-source start-of-fetch phase marker. */
90
+ "cli.progress.watchFetching": ({ sourceId }) => `[${sourceId}] Fetching…`,
91
+ /** "kind: <kind>" — side-metric info shown alongside the Fetching phase. */
92
+ "cli.progress.watchKindInfo": ({ kind }) => `kind: ${kind}`,
93
+ /** "[<source-id>] Failed" — per-source fetch-failure phase label. */
94
+ "cli.progress.watchFailed": ({ sourceId }) => `[${sourceId}] Failed`,
95
+ /** "Launching Chromium…" — html-js browser-launch phase marker. */
96
+ "cli.progress.htmlJsLaunching": "Launching Chromium…",
97
+ /** "Navigating to <url>…" — html-js page-navigation phase marker. */
98
+ "cli.progress.htmlJsNavigating": ({ url }) => `Navigating to ${url}…`,
99
+ /** "Waiting for selector "<sel>" (timeout: <ms>ms)…" — html-js selector-wait phase marker. */
100
+ "cli.progress.htmlJsWaitingSelector": ({ selector, timeout, }) => `Waiting for selector "${selector}" (timeout: ${timeout}ms)…`,
101
+ /** "Capturing page content…" — html-js content-capture phase marker. */
102
+ "cli.progress.htmlJsCapturing": "Capturing page content…",
103
+ /** "Closing browser…" — html-js browser-close phase marker. */
104
+ "cli.progress.htmlJsClosing": "Closing browser…",
105
+ // --- command summaries (global help list, #311) ---------------------------
106
+ // One-line descriptions shown by `radar --help`. Mirror each command's
107
+ // `Command.summary`; the dispatcher renders them via `t()`.
108
+ "cli.summary.init": "Initialize a workspace (sources/items/state/research/templates)",
109
+ "cli.summary.source": "Manage feed sources (add | list | recipes | remove | test)",
110
+ "cli.summary.watch": "Fetch sources and produce filtered items (run)",
111
+ "cli.summary.research": "Generate Markdown research reports from items via an AI agent",
112
+ "cli.summary.triage": "LLM-based triage of detected items",
113
+ "cli.summary.dismiss": "Mark detected items as dismissed (single id, multiple ids, or --batch)",
114
+ "cli.summary.undismiss": "Reverse a dismiss (`dismissed → detected`)",
115
+ "cli.summary.items": "Inspect items in the workspace (list | ...)",
116
+ "cli.summary.review": "Cross-review existing research reports using a different AI agent",
117
+ "cli.summary.update": "Refresh existing research reports against the latest items",
118
+ "cli.summary.doctor": "Diagnose workspace, agent CLIs, and html-js Playwright install",
119
+ "cli.summary.workflow": "Generate GitHub Actions workflows (generate <type>)",
120
+ "cli.summary.routine": "Manage Claude Code Routines (generate <type> / fire <trig_id>)",
121
+ // --- research help (#311) -------------------------------------------------
122
+ "cli.research.help": ({ maxItems }) => `Usage:
123
+ radar research <item-id> [--agent <agent-id>] [--template <template-id>]
124
+ radar research --digest <item-id> <item-id> ... [--triage-group <group>] [--agent <agent-id>] [--template <id>]
125
+ radar research --batch [--status <status>] [--max-items N] [--filter-tags <list>] [--agent <id>]
126
+ radar research <item-id> --emit-payload [--digest <ids...>] [--template <id>]
127
+ radar research --commit <path>
128
+
129
+ Arguments:
130
+ <item-id> Item id (matches items/<sourceId>/<item-id>.yaml)
131
+ Pass 2 or more ids together with --digest to bundle them.
132
+ Omit positional ids with --batch — items are discovered.
133
+
134
+ Options:
135
+ --agent <agent-id> claude-code | codex-cli | gemini-cli | copilot (default: claude-code)
136
+ --template <id> Template id under templates/ (default: default; digest: digest)
137
+ --digest Bundle multiple items into a single digest report
138
+ --triage-group <group> Digest-mode slug source: name the digest
139
+ file after this triage.group instead of the matchedKeywords
140
+ frequency. Required to keep per-group digests unique on the
141
+ same day when a single-keyword source emits multiple groups
142
+ (#255). Falls back to the matchedKeywords slug when omitted.
143
+ --batch Research every item matching --status (and --filter-tags)
144
+ respecting the --max-items hard-cap.
145
+ --status <status> Batch-mode filter: detected | triaged_research
146
+ (default: detected). \`triaged_research\` consumes items
147
+ the triage adapter promoted and
148
+ transitions them to \`researched\` on success.
149
+ --max-items N Batch-mode hard-cap on processed items (default: ${maxItems}).
150
+ Excess items are dropped and announced via warn() so a runaway
151
+ detection cannot blow the cap from inside a workflow.
152
+ --filter-tags <list> Batch-mode comma-separated allow-list matched against
153
+ each item's matchedKeywords (case-insensitive). Default: all.
154
+ --emit-payload Host-agent mode: print the research payload to
155
+ stdout and DO NOT spawn an agent. The interactive host
156
+ session runs the SKILL procedure itself, then finalizes
157
+ with \`radar research --commit <path>\`. Interactive/opt-in
158
+ only — CI/headless must use the default spawn path.
159
+ --commit <path> Host-agent mode: validate an externally-written
160
+ report (under <cwd>/research/) against ResearchFrontmatter-
161
+ Schema and apply the detected → researched transition.
162
+ --verbose Stream the agent CLI's stdout/stderr in addition to phase markers.
163
+ --quiet Suppress phase markers and spinner; print only the completion line.
164
+ Equivalent to setting RADAR_NO_PROGRESS=1.
165
+
166
+ Output:
167
+ single-item: research/<YYYYMMDD>_<slug>_v1.md
168
+ digest: research/<YYYYMMDD>_digest_<slug>_v1.md
169
+ batch: one single-item report per matched item (no digest aggregation).`,
170
+ // --- review help (#311) ---------------------------------------------------
171
+ "cli.review.help": ({ maxItems }) => `Usage:
172
+ radar review <research-id> [--agent <agent-id>] [--template <template-id>]
173
+ radar review --batch [--status <status>] [--max-items N] [--filter-tags <list>] [--agent <id>]
174
+ radar review <research-id> --emit-payload [--agent <id>] [--template <id>]
175
+ radar review --commit <path>
176
+
177
+ Arguments:
178
+ <research-id> Research id (basename of research/<id>.md without .md)
179
+ Omit with --batch — research files are discovered.
180
+
181
+ Options:
182
+ --agent <agent-id> claude-code | codex-cli | gemini-cli | copilot (default: claude-code)
183
+ --template <id> Template id under templates/ (default: default)
184
+ --batch Review every un-reviewed research file whose linked
185
+ items match --status (and --filter-tags), respecting
186
+ --max-items (default: ${maxItems}).
187
+ --status <status> Batch-mode filter: researched (default).
188
+ \`researched → reviewed\` is the only legal transition;
189
+ other values are rejected.
190
+ --max-items N Batch-mode hard-cap on processed reports (default: ${maxItems}).
191
+ --filter-tags <list> Batch-mode comma-separated allow-list matched against
192
+ each linked item's matchedKeywords (case-insensitive).
193
+ --emit-payload Host-agent mode: print the review payload to
194
+ stdout and DO NOT spawn an agent. The interactive host
195
+ session reviews the research file in place itself, then
196
+ finalizes with \`radar review --commit <path>\`.
197
+ Interactive/opt-in only — CI/headless must use the
198
+ default spawn path.
199
+ --commit <path> Host-agent mode: validate an externally-
200
+ reviewed report (under <cwd>/research/) against
201
+ ResearchFrontmatterSchema, assert the host stamped
202
+ reviewedAt / reviewedBy, and apply the researched →
203
+ reviewed transition for the linked items.
204
+ --verbose Stream the agent CLI's stdout/stderr in addition to phase markers.
205
+ --quiet Suppress phase markers and spinner; print only the completion line.
206
+ Equivalent to setting RADAR_NO_PROGRESS=1.
207
+
208
+ Appends a review block to research/<research-id>.md, stamps the
209
+ frontmatter \`reviewedAt\` / \`reviewedBy\`, and transitions the linked
210
+ items/<id>.yaml \`status\` from \`researched\` to \`reviewed\`. Both updates
211
+ happen atomically — a partial failure rolls back the research file.`,
212
+ // --- update help (#311) ---------------------------------------------------
213
+ "cli.update.help": `Usage:
214
+ radar update <research-id> [--agent <agent-id>] [--template <template-id>]
215
+ radar update <research-id> --emit-payload [--template <id>]
216
+ radar update --commit <path>
217
+
218
+ Arguments:
219
+ <research-id> Research id (basename of research/<id>.md without .md)
220
+
221
+ Options:
222
+ --agent <agent-id> claude-code | codex-cli | gemini-cli | copilot (default: claude-code)
223
+ --template <id> Template id under templates/ (default: default)
224
+ --emit-payload Host-agent mode: print the update payload to
225
+ stdout and DO NOT spawn an agent. The interactive host
226
+ session runs the SKILL procedure itself, then finalizes
227
+ with \`radar update --commit <path>\`. Interactive/opt-in
228
+ only — CI/headless must use the default spawn path.
229
+ --commit <path> Host-agent mode: validate an externally-written
230
+ v+1 report (under <cwd>/research/) against ResearchFrontmatter-
231
+ Schema, assert the v+1 invariants against the \`supersedes\`
232
+ predecessor, and leave items.yaml untouched.
233
+ --verbose Stream the agent CLI's stdout/stderr in addition to phase markers.
234
+ --quiet Suppress phase markers and spinner; print only the completion line.
235
+ Equivalent to setting RADAR_NO_PROGRESS=1.
236
+
237
+ Generates research/<base>_v<n+1>.md from the supplied predecessor id,
238
+ writing \`supersedes: <prev id>\` into the new file's frontmatter. The
239
+ predecessor file is never modified (immutable history), and
240
+ the linked items/<id>.yaml \`status\` is left untouched.`,
241
+ // --- triage help (#311) ---------------------------------------------------
242
+ "cli.triage.runHelp": `Usage: radar triage [--dry-run | --apply | --interactive] [options]
243
+ radar triage --emit-payload [--source <id>] [options]
244
+ radar triage --commit <path>
245
+
246
+ Classify \`detected\` items using the configured per-source triage policy.
247
+
248
+ Modes (mutually exclusive; default: --dry-run):
249
+ --dry-run print proposed decisions to stdout (no disk writes)
250
+ --apply write decisions to items/<id>.yaml + transition status
251
+ --interactive --dry-run output → $EDITOR → confirm → apply
252
+
253
+ Options:
254
+ --source <id> limit triage to a single source
255
+ --filter-tags <a,b> matchedKeywords allow-list (comma-separated)
256
+ --triage-agent <id> override policy.agent for this run
257
+ --policy <path> override per-source policy with a YAML file
258
+ --max-items N hard cap on items triaged in this run
259
+ --audit-log <path> append JSONL audit records of every triage call
260
+ --emit-payload Host-agent mode: print the triage payload to
261
+ stdout and DO NOT spawn an agent. The interactive host
262
+ session classifies the items itself, writes a decisions
263
+ JSON, then finalizes with \`radar triage --commit <path>\`.
264
+ Requires a single source group: pass --source unless only
265
+ one source has detected items. Interactive/opt-in only —
266
+ CI/headless must use the default spawn path.
267
+ --commit <path> Host-agent mode: validate a host-written
268
+ decisions JSON (under <cwd>/triage/) against the source's
269
+ policy + detected items and apply the status transitions.
270
+ -v, --verbose verbose progress output
271
+ -q, --quiet suppress progress output entirely
272
+
273
+ Sources missing a \`triagePolicy:\` block are skipped with a warning.`,
274
+ "cli.triage.feedbackHelp": `Usage: radar triage feedback <item-id> --correct | --wrong [--reason <text>]
275
+
276
+ Record human feedback on a prior triage decision.
277
+ Feedback is appended to items/<id>.yaml > triage.feedback, used by
278
+ \`radar triage stats\` (#242) for policy tuning.
279
+
280
+ Options:
281
+ --correct mark the prior triage decision as correct
282
+ --wrong mark the prior triage decision as wrong
283
+ --reason <text> free-form rationale (recommended for --wrong)`,
284
+ "cli.triage.statsHelp": `Usage: radar triage stats [--since <duration>] [--source <id>] [--json]
285
+
286
+ Aggregate triage decisions and human feedback.
287
+ Use after running \`radar triage --apply\` for some weeks; the output
288
+ highlights precision / recall drift and suggests \`triagePolicy.rules:\`
289
+ tweaks. See docs/user-guide.md \`policy tuning workflow\` for the
290
+ recommended monthly loop.
291
+
292
+ Options:
293
+ --since <duration> only count items triaged within the cutoff (e.g. 30d, 24h)
294
+ --source <id> limit stats to a single source (default: all sources)
295
+ --json emit machine-readable JSON instead of the text report`,
296
+ "cli.triage.help": `Usage: radar triage <subcommand|--apply|--dry-run|--interactive> [...]
297
+
298
+ Subcommands:
299
+ feedback <item-id> --correct | --wrong [--reason <text>]
300
+ stats [--since <duration>] [--source <id>] [--json]
301
+
302
+ Run modes (when no subcommand given):
303
+ --dry-run print proposed decisions
304
+ --apply write decisions to items/<id>.yaml
305
+ --interactive edit decisions in $EDITOR before applying
306
+
307
+ Run \`radar triage --help\` for the full option list.`,
308
+ // --- dismiss / undismiss help (#311) --------------------------------------
309
+ "cli.dismiss.help": ({ maxItems }) => `Usage:
310
+ radar dismiss <item-id> [<item-id> ...]
311
+ radar dismiss --batch [--status <status>] [--max-items N] [--filter-tags <list>]
312
+
313
+ Arguments:
314
+ <item-id> Item id (matches items/<sourceId>/<item-id>.yaml)
315
+ Pass 2 or more ids to dismiss them in one call.
316
+ Omit positional ids with --batch — items are discovered.
317
+
318
+ Options:
319
+ --batch Dismiss every item matching --status (and --filter-tags)
320
+ respecting the --max-items hard-cap (default: ${maxItems}).
321
+ --status <status> Batch-mode filter: detected | triaged_unsure (default: detected).
322
+ Only these two statuses can transition to \`dismissed\`
323
+ per the state machine; other values are rejected.
324
+ --max-items N Batch-mode hard-cap on processed items (default: ${maxItems}).
325
+ Excess items are dropped and announced via warn() so a runaway
326
+ --backfill cannot blow the cap from inside a workflow.
327
+ --filter-tags <list> Batch-mode comma-separated allow-list matched against
328
+ each item's matchedKeywords (case-insensitive). Default: all.
329
+
330
+ Transitions the item's status to \`dismissed\`. Valid from \`detected\`
331
+ or \`triaged_unsure\`; items in \`researched\` / \`reviewed\` / \`dismissed\` /
332
+ \`triaged_research\` / \`triaged_digest\` cannot be dismissed.
333
+
334
+ Inverse: \`radar undismiss <item-id> [--force]\`.`,
335
+ "cli.undismiss.help": `Usage: radar undismiss <item-id> [--force]
336
+
337
+ Arguments:
338
+ <item-id> Item id (matches items/<sourceId>/<item-id>.yaml)
339
+
340
+ Options:
341
+ --force, -f Required when reversing a human-origin dismiss
342
+
343
+ Reverses \`dismissed → detected\`.
344
+ Triage-origin dismisses revert silently; human-origin dismisses require --force.
345
+
346
+ Inverse of \`radar dismiss\`.`,
347
+ // --- items help (#311) ----------------------------------------------------
348
+ "cli.items.listHelp": `Usage: radar items list [filters] [output options]
349
+
350
+ Filters:
351
+ --status <status> detected | triaged_research | triaged_digest |
352
+ triaged_unsure | researched | reviewed | dismissed
353
+ --source <id> limit to one source
354
+ --triage-group <name> items whose triage.group == <name>
355
+ (used by digest workflow)
356
+ --since <duration> drop items older than the cutoff (e.g. 7d, 24h)
357
+ --limit N cap result count
358
+
359
+ Output options:
360
+ --json emit JSON array (one object per item)
361
+ --field <expr> emit one item field per row (e.g. id, sourceId,
362
+ triage.decision). Supports nested dot paths.`,
363
+ "cli.items.help": `Usage: radar items <list> [...]
364
+
365
+ Subcommands:
366
+ list [filters] List items matching the supplied filters`,
367
+ // --- watch help (#311) ----------------------------------------------------
368
+ "cli.watch.help": `Usage: radar watch <run> [options]
369
+
370
+ Subcommands:
371
+ run [--source <id>] [--bootstrap | --backfill [--max-pages N]]
372
+ Fetch sources and produce items
373
+
374
+ Options for run:
375
+ --source <id> Limit the run to a single source id
376
+ --bootstrap Seed lastSeenIds without emitting items (suppress initial noise)
377
+ --backfill Fetch all available history pages and emit items for each.
378
+ Supported fully by kind: json-api / github-releases / npm-registry.
379
+ Other kinds (rss / html / html-js) only return their current page.
380
+ --max-pages N Override pagination.maxPages cap (requires --backfill).
381
+ Applies to INNER pagination only — facet sweep
382
+ always walks every facet value regardless of this flag.
383
+ -v, --verbose Enable progress-reporter raw() pass-through (adapter stdout).
384
+ -q, --quiet Suppress the per-source progress reporter (legacy 1-line log
385
+ remains). RADAR_NO_PROGRESS=1 has the same effect.`,
386
+ // --- doctor help (#311) ---------------------------------------------------
387
+ "cli.doctor.help": `Usage: radar doctor [--no-proxy-check]
388
+
389
+ Diagnose the workspace and report dependency / configuration health.
390
+
391
+ Checks performed:
392
+ - Workspace directories (sources/, items/, state/, research/, templates/)
393
+ - radar.config.yaml schema validity
394
+ - Agent CLI availability (claude / codex / gemini / copilot)
395
+ - Playwright + Chromium install (only if html-js sources configured)
396
+ - Proxy env vars (HTTPS_PROXY / HTTP_PROXY / ALL_PROXY) with credential masking
397
+ - NODE_USE_ENV_PROXY status (engaged when radar self-respawned for proxy)
398
+ - NODE_EXTRA_CA_CERTS status (required for TLS-intercepting proxies)
399
+ - Live proxy healthcheck (HTTPS request to api.github.com)
400
+
401
+ Options:
402
+ --no-proxy-check Skip the live proxy healthcheck (offline-friendly)
403
+
404
+ Exit codes:
405
+ 0 all ok (warnings may appear, but no errors)
406
+ 1 one or more error-level checks failed`,
407
+ // --- source help (#311) ---------------------------------------------------
408
+ "cli.source.addHelp": `Usage: radar source add <id> --kind <kind> --url <url> [options]
409
+ radar source add <id> --recipe <name> [overrides]
410
+
411
+ Options:
412
+ --kind <kind> rss | html | html-js | github-releases | npm-registry | json-feed | json-api
413
+ --url <url> fetch target URL
414
+ --recipe <name> apply a bundled recipe (see \`radar source recipes\`).
415
+ Mutually exclusive with --kind / --url / --selector-* /
416
+ --pagination-*; --name / --tags / --keywords /
417
+ --exclude-keywords still override the recipe defaults.
418
+ --name <name> display name (defaults to <id>)
419
+ --tags <a,b> comma-separated tags
420
+ --keywords <a,b> comma-separated include keywords
421
+ (required for useful output — empty = match nothing)
422
+ --exclude-keywords <a,b> comma-separated exclude keywords
423
+ --selector-<field> <css> CSS selector for kind=html / html-js (required: item, title, link)
424
+ optional: summary, publishedAt, body, tags
425
+ For kind=html-js, selectors evaluate against the post-JS DOM.
426
+ The \`js:\` block (waitFor / timeout / userAgent) cannot be set
427
+ via flags; edit sources/<id>.yaml after add.
428
+
429
+ For kind=json-api:
430
+ --pagination-strategy <s> page | offset | cursor | link-header | token | none (default: page)
431
+ --pagination-param <name> query param name for the page/offset/cursor value
432
+ --pagination-start N initial page/offset value (default: 0)
433
+ --page-size N items per page
434
+ --page-size-param <name> query param name for the page-size value
435
+ --max-pages N hard cap on pages traversed (default: 20)
436
+ --next-cursor-path <jp> JSONPath-lite to the next-cursor value (cursor/token strategy)
437
+ --total-path <jp> JSONPath-lite to the total-count value (backfill early-stop hint)
438
+
439
+ Selector fields (\`jsonSelectors.*\`) for kind=json-api cannot be set via flags;
440
+ the schema has a default fallback chain (items / title / link / publishedAt / summary),
441
+ so simple APIs work without selectors. Edit sources/<id>.yaml directly when explicit
442
+ selectors are needed (nested fields, non-standard envelopes).
443
+
444
+ Facet sweep (e.g. year-by-year sweep) cannot be configured via flags;
445
+ and bundle the year sweep through \`--recipe aws-whats-new\`. Recipe-only structural field.`,
446
+ "cli.source.listHelp": `Usage: radar source list [--enabled-only] [-v|--verbose]
447
+
448
+ Lists sources/*.yaml in tabular form: id / kind / url / tags.
449
+
450
+ Options:
451
+ --enabled-only Reserved for forward compatibility (currently a no-op).
452
+ -v, --verbose Print a detailed block per source including keywords,
453
+ trustLevel, and lastFetchedAt (from state/<id>.yaml).`,
454
+ "cli.source.removeHelp": `Usage: radar source remove <id>
455
+
456
+ Deletes sources/<id>.yaml. state/<id>.yaml and items/ are preserved.`,
457
+ "cli.source.testHelp": `Usage: radar source test <id> [--limit N] [--show-content]
458
+
459
+ Dry-run a single source: fetch, filter, and print matched items.
460
+ state/ and items/ are not touched (no persistence). Useful for tuning
461
+ keywords when adding a new source.
462
+
463
+ For kind=json-api, \`source test\` fetches PAGE 0 ONLY.
464
+ Pagination is NOT walked even when the recipe declares multiple pages —
465
+ \`--limit N\` caps how many matched items are PRINTED, it does not change
466
+ the page budget. Use \`radar watch run --backfill\` for full-history ingest.
467
+ Page 0's \`Link\` header / \`nextCursor\` extraction is surfaced via
468
+ \`--show-content\` for pagination tuning without state mutation.
469
+
470
+ For facet-sweep recipes, \`source test\` probes a SINGLE
471
+ facet value: range facets use the upper bound (latest year), enum facets
472
+ use the first listed value. A warning names which value was tested so
473
+ keyword tuning is not silently scoped to one slice. Run \`radar watch run
474
+ --backfill\` to sweep every facet value.
475
+
476
+ Options:
477
+ --limit N Maximum number of matched items to print (default 10)
478
+ --show-content Also print the first 200 chars of each item's body, plus
479
+ (kind=json-api) the selector adoption table and pagination
480
+ preview (would-be next URL / Link header / nextCursor).
481
+ -v, --verbose Enable progress-reporter raw() pass-through (adapter stdout).
482
+ Most useful with kind=html-js (Playwright phase markers).
483
+ -q, --quiet Suppress the progress reporter entirely. RADAR_NO_PROGRESS=1
484
+ has the same effect.`,
485
+ "cli.source.recipesHelp": `Usage: radar source recipes
486
+
487
+ List bundled recipes (recipes/*.yaml in the radar package).
488
+ Each recipe can be applied via:
489
+ radar source add <id> --recipe <name> [--keywords <kw>] [--tags <t>] [--name <display>]
490
+
491
+ Bundled recipes ship with the radar npm package; user-authored recipes are
492
+ not yet supported. To add a new bundled recipe, contribute a YAML to the
493
+ radar repo's recipes/ directory.`,
494
+ "cli.source.help": `Usage: radar source <add|list|recipes|remove|test> [...]
495
+
496
+ Subcommands:
497
+ add <id> --kind <kind> --url <url> [...]
498
+ add <id> --recipe <name> [--keywords <kw>] [--tags <t>] [--name <display>]
499
+ list [--enabled-only]
500
+ recipes
501
+ remove <id>
502
+ test <id> [--limit N] [--show-content]`,
503
+ // --- workflow help (#311) -------------------------------------------------
504
+ "cli.workflow.help": `Usage: radar workflow <subcommand> [...]
505
+
506
+ Subcommands:
507
+ generate <type> Generate a GitHub Actions workflow YAML
508
+ Types: watch | combined | combined-with-triage
509
+
510
+ Run \`radar workflow generate <type> --help\` for type-specific options.`,
511
+ "cli.workflow.generateHelp": `Usage: radar workflow generate <type> [options]
512
+
513
+ Types:
514
+ watch Periodic \`radar watch run\` (cron + state commit with rebase retry)
515
+ combined Periodic \`radar watch run\` -> auto research --batch with hard cap
516
+ combined-with-triage \`watch run\` -> \`triage --apply\` -> \`research --batch\` -> per-group \`research --digest\` -> \`review --batch\` in one job
517
+
518
+ Run \`radar workflow generate <type> --help\` for type-specific options.`,
519
+ // --- per-type workflow generate help (#337, deferred from #311) -----------
520
+ // Type-specific `--help` bodies for `radar workflow generate <type>`. The
521
+ // option names / cron expression / secret names are functional fields and
522
+ // stay verbatim; only the natural-language descriptions are translated.
523
+ "cli.workflow.generateWatchHelp": `Usage: radar workflow generate watch [options]
524
+
525
+ Generates a GitHub Actions workflow that runs \`radar watch run\` on a cron schedule.
526
+ The generated workflow includes git pull --rebase retry logic to mitigate push
527
+ conflicts with other concurrent workflows.
528
+
529
+ Options:
530
+ --cron <expression> 5-field cron expression (default: "0 0 * * *")
531
+ --output <path> Output file under .github/workflows/
532
+ (default: .github/workflows/feedradar-watch.yaml)
533
+ --agent <name> claude-code | codex-cli | gemini-cli | copilot (default: claude-code)
534
+ Determines which secret name the workflow references.
535
+ --force, -f Overwrite existing output file
536
+ --lang <en|ja> Language for the generated YAML's comments / step names
537
+ (default: en; also honors RADAR_LANG and config.locale)
538
+
539
+ Required secrets (Settings → Secrets and variables → Actions):
540
+ ANTHROPIC_API_KEY when --agent claude-code (default)
541
+ OPENAI_API_KEY when --agent codex-cli
542
+ GEMINI_API_KEY when --agent gemini-cli
543
+ GITHUB_TOKEN auto-provisioned for --agent copilot (no setup needed)`,
544
+ "cli.workflow.generateCombinedHelp": ({ maxItems }) => `Usage: radar workflow generate combined [options]
545
+
546
+ Generates a GitHub Actions workflow that chains \`radar watch run\` ->
547
+ a no-new-items guard -> \`radar research --batch\` with hard-capped cost
548
+ controls.
549
+
550
+ Options:
551
+ --watch-cron <expression> 5-field cron expression (default: "0 0 * * *")
552
+ --output <path> Output file under .github/workflows/
553
+ (default: .github/workflows/feedradar-combined.yaml)
554
+ --agent <name> claude-code | codex-cli | gemini-cli | copilot (default: claude-code)
555
+ --max-items N Hard cap on auto-research per run (default: ${maxItems})
556
+ --filter-tags <list> Comma-separated allow-list of matchedKeywords
557
+ (default: unset, matches every detected item)
558
+ --force, -f Overwrite existing output file
559
+ --lang <en|ja> Language for the generated YAML's comments / step names
560
+ (default: en; also honors RADAR_LANG and config.locale)
561
+
562
+ Required secrets (Settings → Secrets and variables → Actions):
563
+ ANTHROPIC_API_KEY when --agent claude-code (default)
564
+ OPENAI_API_KEY when --agent codex-cli
565
+ GEMINI_API_KEY when --agent gemini-cli
566
+ GITHUB_TOKEN auto-provisioned for --agent copilot (no setup needed)`,
567
+ "cli.workflow.generateCombinedWithTriageHelp": ({ watchCron, output, maxItems, }) => `Usage: radar workflow generate combined-with-triage [options]
568
+
569
+ Generates a GitHub Actions workflow that chains \`radar watch run\` ->
570
+ \`radar triage --apply\` -> \`radar research --batch --status triaged_research\` ->
571
+ per-group \`radar research --digest\` -> \`radar review --batch\` in one job.
572
+
573
+ Options:
574
+ --watch-cron <expression> 5-field cron expression (default: "${watchCron}")
575
+ --output <path> Output file under .github/workflows/
576
+ (default: ${output})
577
+ --triage-agent <name> claude-code | codex-cli | gemini-cli | copilot (default: gemini-cli)
578
+ --research-agent <name> claude-code | codex-cli | gemini-cli | copilot (default: claude-code)
579
+ --review-agent <name> claude-code | codex-cli | gemini-cli | copilot (default: codex-cli)
580
+ --max-items N Hard cap on research --batch per run (default: ${maxItems})
581
+ --slack-webhook <ref> Secret reference (e.g. secrets.SLACK_WEBHOOK) for the
582
+ triaged_unsure-queue alert (optional)
583
+ --output-mode <mode> pr | direct-commit (default: pr). 'pr' opens a
584
+ review PR; 'direct-commit' commits & pushes straight
585
+ to the default branch (drops pull-requests: write)
586
+ --force, -f Overwrite existing output file
587
+ --lang <en|ja> Language for the generated YAML's comments / step names
588
+ (default: en; also honors RADAR_LANG and config.locale)
589
+
590
+ Required secrets (Settings → Secrets and variables → Actions):
591
+ ANTHROPIC_API_KEY when any role uses --agent claude-code
592
+ OPENAI_API_KEY when any role uses --agent codex-cli
593
+ GEMINI_API_KEY when any role uses --agent gemini-cli (default for triage)
594
+ GITHUB_TOKEN auto-provisioned (no manual setup needed)`,
595
+ // --- routine help (#311) --------------------------------------------------
596
+ "cli.routine.help": `Usage: radar routine <subcommand> [...]
597
+
598
+ Subcommands:
599
+ generate <type> Generate a Claude Code Routine YAML (.claude/routines/)
600
+ Types: watch | pipeline
601
+ fire <trig_id> Trigger a registered routine from the outside (/fire API)
602
+
603
+ Run \`radar routine <subcommand> --help\` for subcommand-specific options.`,
604
+ "cli.routine.generateHelp": `Usage: radar routine generate <type> [options]
605
+
606
+ Types:
607
+ watch Periodic \`radar watch run\` self-session routine; commits items/state to a claude/* branch
608
+ pipeline Full watch -> triage -> research -> review self-session routine, one item at a time
609
+
610
+ Run \`radar routine generate <type> --help\` for type-specific options.`,
611
+ // --- per-type routine generate / fire help (#337, deferred from #311) -----
612
+ // Type-specific `--help` bodies for `radar routine generate <type>` and
613
+ // `radar routine fire`. Model ids / cron / token env var name are functional
614
+ // fields and stay verbatim; only the prose is translated. `models` is the
615
+ // pre-joined `SUPPORTED_MODELS.join(" | ")` string so the catalog stays
616
+ // agnostic of the model list's contents.
617
+ "cli.routine.generateWatchHelp": ({ models }) => `Usage: radar routine generate watch [options]
618
+
619
+ Generates a Claude Code Routine YAML that runs \`radar watch run\` on a schedule
620
+ and commits detected items/state to a claude/* branch.
621
+ The routine completes in one Claude session — it does NOT spawn other agents.
622
+
623
+ Options:
624
+ --name <name> Routine name (default: "feedradar-watch")
625
+ Also the default output filename.
626
+ --repo <owner/repo> Target repository (default: <owner>/<repo>)
627
+ --cron <expression> 5-field cron, min interval 1 HOUR (default: "0 * * * *")
628
+ Sub-hourly (e.g. "*/5 * * * *") is rejected.
629
+ --timezone <tz> Schedule timezone (default: "UTC")
630
+ --model <name> ${models}
631
+ (default: claude-sonnet-4-6)
632
+ --output <path> Output file under .claude/routines/
633
+ (default: .claude/routines/<name>.yaml)
634
+ --force, -f Overwrite existing output file
635
+ --lang <en|ja> Language for the generated YAML's notes / instructions / comments
636
+ (default: en; also honors RADAR_LANG and config.locale)`,
637
+ "cli.routine.generatePipelineHelp": ({ models, maxItems, }) => `Usage: radar routine generate pipeline [options]
638
+
639
+ Generates a Claude Code Routine YAML whose single session runs the FULL
640
+ pipeline — \`radar watch run\` -> triage -> research -> review — IN SEQUENCE,
641
+ processing items ONE AT A TIME. It does NOT spawn
642
+ other agents, so the cross-agent review of the GHA combined-with-triage
643
+ workflow is NOT present. Per-run item count is bounded by CLI flags.
644
+
645
+ Options:
646
+ --name <name> Routine name (default: "feedradar-pipeline")
647
+ Also the default output filename.
648
+ --repo <owner/repo> Target repository (default: <owner>/<repo>)
649
+ --cron <expression> 5-field cron, min interval 1 HOUR (default: "0 * * * *")
650
+ Sub-hourly (e.g. "*/5 * * * *") is rejected.
651
+ --timezone <tz> Schedule timezone (default: "UTC")
652
+ --model <name> ${models}
653
+ (default: claude-sonnet-4-6)
654
+ --max-items N Hard cap on items triaged/researched/reviewed per run
655
+ (default: ${maxItems}). Drives triage --max-items and items --limit.
656
+ --output-mode <mode> pr | auto-merge (default: pr). 'auto-merge' squash-merges
657
+ the routine's own PR to main (requires the Web UI 'Allow
658
+ unrestricted branch pushes' toggle).
659
+ --output <path> Output file under .claude/routines/
660
+ (default: .claude/routines/<name>.yaml)
661
+ --force, -f Overwrite existing output file
662
+ --lang <en|ja> Language for the generated YAML's notes / instructions / comments
663
+ (default: en; also honors RADAR_LANG and config.locale)`,
664
+ "cli.routine.fireHelp": ({ tokenEnv }) => `Usage: radar routine fire <trig_id> [options]
665
+
666
+ Triggers a registered Claude Code Routine from the outside via the
667
+ /fire API. The call returns as soon as the routine session
668
+ is created — it does NOT wait for the session to finish.
669
+
670
+ Arguments:
671
+ <trig_id> Routine id from the Web UI (starts with 'trig_')
672
+
673
+ Options:
674
+ --text <msg> Free-form launch context (request body \`text\`).
675
+ The API does not parse it; it is passed as-is.
676
+ --token-env <NAME> Env var holding the per-routine bearer token
677
+ (default: ${tokenEnv}).
678
+ --lang <en|ja> UI language for this command's messages / help
679
+ (default: en; also honors RADAR_LANG and config.locale)
680
+
681
+ The per-routine token is issued ONCE in the Web UI (Regenerate / Revoke
682
+ there) and is read from the environment — it is never accepted as a flag
683
+ and never printed.`,
684
+ // --- user-facing errors & result notifications (#312) ---------------------
685
+ // Catalog of the user-facing error / result-notification strings emitted by
686
+ // the CLI commands (option-validation errors, file/state errors needing user
687
+ // action, and completion notices). Developer-facing internal logs / traces
688
+ // are intentionally NOT translated (ADR-0021 boundary). Dynamic values are
689
+ // interpolated via the function-entry params, not placeholder strings.
690
+ // dismiss (#312)
691
+ "cli.dismiss.batchIncompatiblePositional": ({ count }) => `dismiss: --batch is incompatible with positional <item-id> arguments (got ${count})`,
692
+ "cli.dismiss.invalidStatus": ({ status, allowed }) => `dismiss: invalid --status '${status}' (expected: ${allowed})`,
693
+ "cli.dismiss.statusRequiresBatch": "dismiss: --status requires --batch",
694
+ "cli.dismiss.maxItemsRequiresBatch": "dismiss: --max-items requires --batch",
695
+ "cli.dismiss.filterTagsRequiresBatch": "dismiss: --filter-tags requires --batch",
696
+ "cli.dismiss.invalidMaxItemsInteger": ({ raw }) => `dismiss: invalid --max-items '${raw}' (expected positive integer)`,
697
+ "cli.dismiss.invalidMaxItemsPositive": ({ raw }) => `dismiss: invalid --max-items '${raw}' (must be > 0)`,
698
+ "cli.dismiss.missingItemId": "dismiss: missing <item-id>",
699
+ "cli.dismiss.itemNotFound": ({ id }) => `dismiss: item '${id}' not found under items/`,
700
+ "cli.dismiss.itemWrongStatus": ({ id, status, allowed, nextStatuses, }) => `dismiss: item '${id}' is in status '${status}', expected one of ${allowed} (dismiss transitions to 'dismissed' only from these). Valid next statuses for '${status}': ${nextStatuses}`,
701
+ "cli.dismiss.failedUpdate": ({ reason }) => `dismiss: failed to update item status: ${reason}`,
702
+ "cli.dismiss.transitioned": ({ sourceId, id }) => `dismiss: items/${sourceId}/${id}.yaml status -> dismissed`,
703
+ "cli.dismiss.noItemsMatched": ({ status, tags }) => `dismiss: no items matched --batch filters (status=${status}${tags})`,
704
+ "cli.dismiss.capReached": ({ maxItems, dropped, matched, }) => `dismiss: --max-items ${maxItems} cap reached; dropping ${dropped} excess item(s) (matched ${matched})`,
705
+ "cli.dismiss.batchWillProcess": ({ count, status, tags, cap, }) => `dismiss: --batch will process ${count} item(s) (status=${status}${tags}, cap=${cap})`,
706
+ "cli.dismiss.batchCompleted": ({ count }) => `dismiss: --batch completed ${count} item(s)`,
707
+ // undismiss (#312)
708
+ "cli.undismiss.missingItemId": "undismiss: missing <item-id>",
709
+ "cli.undismiss.itemsDirNotFound": "undismiss: items/ not found (run `radar init`)",
710
+ "cli.undismiss.itemNotFound": ({ id }) => `undismiss: item '${id}' not found under items/`,
711
+ "cli.undismiss.notDismissed": ({ id, status }) => `undismiss: item '${id}' is in status '${status}', expected 'dismissed' (undismiss reverses a dismiss, not other transitions)`,
712
+ "cli.undismiss.forbiddenTransition": "undismiss: state machine forbids 'dismissed → detected' (internal error)",
713
+ "cli.undismiss.humanOriginRequiresForce": ({ id }) => `undismiss: item '${id}' was dismissed by human; pass --force to revert (this is a deliberate safety gate)`,
714
+ "cli.undismiss.failedUpdate": ({ reason }) => `undismiss: failed to update item: ${reason}`,
715
+ "cli.undismiss.revertedHumanOrigin": ({ id }) => `undismiss: reverted human-origin dismiss for '${id}' (used --force)`,
716
+ "cli.undismiss.transitioned": ({ sourceId, id }) => `undismiss: items/${sourceId}/${id}.yaml status -> detected`,
717
+ // doctor diagnostics (#312)
718
+ "cli.doctor.workspaceDirExists": ({ dir }) => `${dir}/ exists`,
719
+ "cli.doctor.workspaceDirMissing": ({ dir }) => `${dir}/ missing — run \`radar init\` to scaffold the workspace`,
720
+ "cli.doctor.configValid": "radar.config.yaml valid (or absent — defaults apply)",
721
+ "cli.doctor.configInvalid": ({ reason }) => `radar.config.yaml invalid: ${reason}`,
722
+ "cli.doctor.agentFound": ({ agent, binary, path, }) => `${agent}: ${binary} found at ${path}`,
723
+ "cli.doctor.agentMissing": ({ agent, binary }) => `${agent}: ${binary} not found in PATH (install it to use \`radar research --agent ${agent}\`)`,
724
+ "cli.doctor.playwrightNotRequired": "playwright: not required (no html-js sources configured)",
725
+ "cli.doctor.playwrightOk": ({ path }) => `playwright: ok — chromium at ${path}`,
726
+ "cli.doctor.playwrightModuleMissing": ({ sources, hint, }) => `playwright: module not installed (required by html-js sources: ${sources})\n ${hint}`,
727
+ "cli.doctor.playwrightChromiumMissing": ({ path, sources, hint, }) => `playwright: chromium missing at '${path}' (required by html-js sources: ${sources})\n ${hint}`,
728
+ "cli.doctor.proxyEnvAllProxyOnly": ({ source, masked, }) => `proxy: detected via $${source}=${masked} (Node --use-env-proxy ignores ALL_PROXY; set HTTPS_PROXY or HTTP_PROXY instead)`,
729
+ "cli.doctor.proxyEnvDetected": ({ source, masked }) => `proxy: detected via $${source}=${masked}`,
730
+ "cli.doctor.proxyEnvNone": "proxy: no proxy env var set (HTTPS_PROXY / HTTP_PROXY / ALL_PROXY)",
731
+ "cli.doctor.proxyActive": "proxy: NODE_USE_ENV_PROXY active (auto-applied by radar)",
732
+ "cli.doctor.proxyActiveMissing": "proxy: NODE_USE_ENV_PROXY not set; if fetch ignores HTTPS_PROXY, re-run radar via the bin (not direct import)",
733
+ "cli.doctor.proxyActiveNotRequired": "proxy: NODE_USE_ENV_PROXY not required (no proxy detected)",
734
+ "cli.doctor.tlsCaSet": ({ path }) => `tls: NODE_EXTRA_CA_CERTS=${path}`,
735
+ "cli.doctor.tlsCaUnset": "tls: NODE_EXTRA_CA_CERTS not set (TLS-intercepting proxies may fail)",
736
+ "cli.doctor.healthcheckSkippedFlag": "proxy healthcheck: skipped (--no-proxy-check)",
737
+ "cli.doctor.healthcheckSkippedNoProxy": "proxy healthcheck: skipped (no proxy detected)",
738
+ "cli.doctor.healthcheck407": ({ url }) => `proxy healthcheck: 407 Proxy Authentication Required from ${url} (check userinfo in $HTTPS_PROXY)`,
739
+ "cli.doctor.healthcheckOk": ({ status, statusText, elapsed, }) => `proxy healthcheck: ok (${status} ${statusText} from api.github.com in ${elapsed}ms)`,
740
+ "cli.doctor.healthcheckOther": ({ status, statusText, elapsed, }) => `proxy healthcheck: ${status} ${statusText} from api.github.com in ${elapsed}ms`.trimEnd(),
741
+ "cli.doctor.healthcheckTimeout": ({ elapsed }) => `proxy healthcheck: timeout after ${elapsed}ms (proxy may be unreachable; verify $HTTPS_PROXY host:port)`,
742
+ "cli.doctor.healthcheckTls": ({ code }) => `proxy healthcheck: TLS error (${code}). Set NODE_EXTRA_CA_CERTS=/path/to/corp-ca.pem to trust your proxy's intercepting CA.`,
743
+ "cli.doctor.healthcheckRefused": "proxy healthcheck: connection refused. Verify the proxy host:port in $HTTPS_PROXY is reachable.",
744
+ "cli.doctor.healthcheckDns": ({ code }) => `proxy healthcheck: DNS lookup failed (${code}). The proxy host in $HTTPS_PROXY can't be resolved.`,
745
+ "cli.doctor.healthcheckResetTimeout": ({ code }) => `proxy healthcheck: connection ${code === "ETIMEDOUT" ? "timed out" : "reset"} (${code}).`,
746
+ "cli.doctor.healthcheckFailed": ({ reason }) => `proxy healthcheck: failed — ${reason}`,
747
+ "cli.doctor.summary": ({ ok, warn, error, }) => `doctor: ${ok} ok, ${warn} warn, ${error} error`,
748
+ // init result summary / next-steps (#312)
749
+ "cli.init.workspaceReady": ({ cwd }) => `init: workspace ready at ${cwd}`,
750
+ "cli.init.directoriesCreated": ({ dirs }) => `init: directories created: ${dirs}`,
751
+ "cli.init.skillsCopied": ({ files }) => `init: skills copied: ${files}`,
752
+ "cli.init.filesSkipped": ({ files }) => `init: files skipped: ${files}`,
753
+ "cli.init.nextSteps": "init: next steps — read FEEDRADAR.md for natural-language and slash usage.",
754
+ // items (#312)
755
+ "cli.items.unknownSubcommand": ({ sub }) => `items: unknown subcommand '${sub}'`,
756
+ "cli.items.invalidStatus": ({ status, allowed }) => `items list: invalid --status '${status}' (expected: ${allowed})`,
757
+ "cli.items.invalidSince": ({ since }) => `items list: invalid --since '${since}' (expected Ns | Nm | Nh | Nd)`,
758
+ "cli.items.noItemsDir": "items list: no items/ directory (run `radar init` first)",
759
+ "cli.items.noMatch": "items list: no items match the filter",
760
+ // watch (#312 / #336)
761
+ // The `--bootstrap`/`--backfill` mutual-exclusion + `--max-pages requires
762
+ // --backfill` errors are thrown from the sync `parseRunArgs` (before a
763
+ // translator is in scope). #336 keys the errors (`WatchArgError.key`) so the
764
+ // caller translates them once the locale is resolved.
765
+ "cli.watch.bootstrapBackfillExclusive": "--bootstrap and --backfill are mutually exclusive",
766
+ "cli.watch.maxPagesRequiresBackfill": "--max-pages requires --backfill",
767
+ "cli.watch.verboseQuietExclusive": "--verbose and --quiet are mutually exclusive",
768
+ "cli.watch.unknownSubcommand": ({ sub }) => `watch: unknown subcommand '${sub}'`,
769
+ "cli.watch.bootstrapComplete": ({ sources }) => `watch run: bootstrap complete (${sources} sources)`,
770
+ "cli.watch.backfillComplete": ({ total, sources }) => `watch run: backfill complete — ${total} item(s) ingested across ${sources} source(s)`,
771
+ "cli.watch.runComplete": ({ total, sources }) => `watch run: ${total} new item(s) across ${sources} source(s)`,
772
+ // zod-validation error preamble (#312) ------------------------------------
773
+ // The localized issue bodies come from zod's per-locale messages (wired via
774
+ // `applyZodLocale`); only the wrapping preamble lines are translated here.
775
+ "cli.config.schemaViolation": ({ file, issues }) => `${file} schema violation:\n${issues}`,
776
+ "cli.config.failedRead": ({ file, reason }) => `failed to read ${file}: ${reason}`,
777
+ "cli.config.failedParse": ({ file, reason }) => `failed to parse ${file} as YAML: ${reason}`,
778
+ // --- remaining user-facing errors & notifications (#336) ------------------
779
+ // Catalogue of the user-facing validation errors, file/state errors needing
780
+ // user action, and completion notices emitted by research / review / update /
781
+ // source / triage. Internal debug logs / traces (frontmatter parse failures,
782
+ // adapter rollback diagnostics, schema-issue dumps) stay untranslated per the
783
+ // ADR-0021 boundary. The `<cmd>:` prefix is kept verbatim so existing scripts
784
+ // and the test suite key off the stable command tag.
785
+ // shared: invalid --agent (research / review / update / triage all reuse the
786
+ // same adapter id allow-list)
787
+ "cli.agent.invalid": ({ cmd, agent }) => `${cmd}: invalid --agent '${agent}' (expected: claude-code | codex-cli | gemini-cli | copilot)`,
788
+ // research (#336)
789
+ "cli.research.batchIncompatiblePositional": ({ count }) => `research: --batch is incompatible with positional <item-id> arguments (got ${count})`,
790
+ "cli.research.batchIncompatibleDigest": "research: --batch is incompatible with --digest",
791
+ "cli.research.batchIncompatibleTriageGroup": "research: --batch is incompatible with --triage-group",
792
+ "cli.research.invalidStatus": ({ status, allowed, }) => `research: invalid --status '${status}' (expected: ${allowed})`,
793
+ "cli.research.invalidMaxItemsInteger": ({ raw }) => `research: invalid --max-items '${raw}' (expected positive integer)`,
794
+ "cli.research.invalidMaxItemsPositive": ({ raw }) => `research: invalid --max-items '${raw}' (must be > 0)`,
795
+ "cli.research.commitIncompatibleBatch": "research: --commit is incompatible with --batch",
796
+ "cli.research.commitIncompatibleDigest": "research: --commit is incompatible with --digest",
797
+ "cli.research.commitIncompatibleEmitPayload": "research: --commit is incompatible with --emit-payload",
798
+ "cli.research.commitIncompatibleTriageGroup": "research: --commit is incompatible with --triage-group",
799
+ "cli.research.commitTakesPath": ({ count, ids }) => `research: --commit takes a <path>, not <item-id> arguments (got ${count}: ${ids})`,
800
+ "cli.research.emitPayloadIncompatibleBatch": "research: --emit-payload is incompatible with --batch",
801
+ "cli.research.statusRequiresBatch": "research: --status requires --batch",
802
+ "cli.research.maxItemsRequiresBatch": "research: --max-items requires --batch",
803
+ "cli.research.filterTagsRequiresBatch": "research: --filter-tags requires --batch",
804
+ "cli.research.triageGroupRequiresDigest": "research: --triage-group requires --digest",
805
+ "cli.research.missingItemId": "research: missing <item-id>",
806
+ "cli.research.multipleRequireDigest": ({ count, ids }) => `research: multiple <item-id> arguments require --digest (got ${count}: ${ids})`,
807
+ "cli.research.digestRequiresTwo": ({ count }) => `research: --digest requires 2 or more <item-id> arguments (got ${count})`,
808
+ "cli.research.itemNotFound": ({ id }) => `research: item '${id}' not found under items/`,
809
+ "cli.research.digestDismissed": ({ ids }) => `research: cannot include dismissed items in a digest: ${ids}`,
810
+ "cli.research.alreadyExists": ({ path }) => `research: ${path} already exists (use \`radar update\` to re-research)`,
811
+ "cli.research.noItemsMatched": ({ status, tags }) => `research: no items matched --batch filters (status=${status}${tags})`,
812
+ "cli.research.capReached": ({ maxItems, dropped, matched, }) => `research: --max-items ${maxItems} cap reached; dropping ${dropped} excess item(s) (matched ${matched})`,
813
+ "cli.research.batchWillProcess": ({ count, status, tags, agent, cap, }) => `research: --batch will process ${count} item(s) (status=${status}${tags}, agent=${agent}, cap=${cap})`,
814
+ "cli.research.batchHalted": ({ id, exitCode }) => `research: --batch halted on item '${id}' (exit ${exitCode})`,
815
+ "cli.research.batchCompleted": ({ count }) => `research: --batch completed ${count} item(s)`,
816
+ "cli.research.wrote": ({ path }) => `research: wrote ${path}`,
817
+ "cli.research.transitioned": ({ sourceId, id }) => `research: items/${sourceId}/${id}.yaml status -> researched`,
818
+ // review (#336)
819
+ "cli.review.batchIncompatiblePositional": ({ researchId }) => `review: --batch is incompatible with positional <research-id> ('${researchId}')`,
820
+ "cli.review.invalidStatus": ({ status, allowed }) => `review: invalid --status '${status}' (expected: ${allowed})`,
821
+ "cli.review.invalidMaxItemsInteger": ({ raw }) => `review: invalid --max-items '${raw}' (expected positive integer)`,
822
+ "cli.review.invalidMaxItemsPositive": ({ raw }) => `review: invalid --max-items '${raw}' (must be > 0)`,
823
+ "cli.review.commitIncompatibleBatch": "review: --commit is incompatible with --batch",
824
+ "cli.review.commitIncompatibleEmitPayload": "review: --commit is incompatible with --emit-payload",
825
+ "cli.review.commitTakesPath": ({ researchId }) => `review: --commit takes a <path>, not a <research-id> argument (got '${researchId}')`,
826
+ "cli.review.emitPayloadIncompatibleBatch": "review: --emit-payload is incompatible with --batch",
827
+ "cli.review.statusRequiresBatch": "review: --status requires --batch",
828
+ "cli.review.maxItemsRequiresBatch": "review: --max-items requires --batch",
829
+ "cli.review.filterTagsRequiresBatch": "review: --filter-tags requires --batch",
830
+ "cli.review.missingResearchId": "review: missing <research-id>",
831
+ "cli.review.fileNotFound": ({ path }) => `review: research file not found: ${path}`,
832
+ "cli.review.batchFoundNone": "review: --batch found no un-reviewed research/*.md files",
833
+ "cli.review.batchMatchedZero": ({ status, tags }) => `review: --batch matched 0 research file(s) (status=${status}${tags})`,
834
+ "cli.review.capReached": ({ maxItems, dropped, matched, }) => `review: --max-items ${maxItems} cap reached; dropping ${dropped} excess research file(s) (matched ${matched})`,
835
+ "cli.review.batchWillProcess": ({ count, status, tags, agent, cap, }) => `review: --batch will process ${count} research file(s) (status=${status}${tags}, agent=${agent}, cap=${cap})`,
836
+ "cli.review.batchHalted": ({ researchId, exitCode, }) => `review: --batch halted on research '${researchId}' (exit ${exitCode})`,
837
+ "cli.review.batchCompleted": ({ count }) => `review: --batch completed ${count} research file(s)`,
838
+ "cli.review.commitNotStamped": ({ id, reviewedAt, reviewedBy, }) => `review: --commit report '${id}' is not stamped (reviewedAt=${reviewedAt}, reviewedBy=${reviewedBy}); the host session must stamp the review before committing`,
839
+ "cli.review.alreadyReviewed": ({ id, reviewedAt, reviewedBy, }) => `review: research '${id}' is already reviewed (reviewedAt=${reviewedAt}, reviewedBy=${reviewedBy})`,
840
+ "cli.review.wroteCommit": ({ path }) => `review: wrote ${path}`,
841
+ "cli.review.stamped": ({ path, reviewedAt, reviewedBy, }) => `review: stamped ${path} reviewedAt=${reviewedAt} reviewedBy=${reviewedBy}`,
842
+ "cli.review.transitioned": ({ sourceId, id }) => `review: items/${sourceId}/${id}.yaml status -> reviewed`,
843
+ // update (#336)
844
+ "cli.update.commitIncompatibleEmitPayload": "update: --commit is incompatible with --emit-payload",
845
+ "cli.update.commitTakesPath": ({ researchId }) => `update: --commit takes a <path>, not a <research-id> (got '${researchId}')`,
846
+ "cli.update.missingResearchId": "update: missing <research-id>",
847
+ "cli.update.fileNotFound": ({ path }) => `update: research file not found: ${path}`,
848
+ "cli.update.alreadyExists": ({ path, version }) => `update: ${path} already exists. v${version} was already generated — pick a different predecessor or remove the stale file.`,
849
+ "cli.update.commitSupersedesNull": "update: --commit report has `supersedes: null`. update finalizes a v+1 (use `radar research --commit` for a v1).",
850
+ "cli.update.wrote": ({ path }) => `update: wrote ${path}`,
851
+ "cli.update.supersedes": ({ prevId }) => `update: supersedes ${prevId} (items.yaml status unchanged)`,
852
+ // source (#336)
853
+ "cli.source.missingId": ({ sub }) => `source ${sub}: missing <id>`,
854
+ "cli.source.invalidId": ({ sub, id }) => `source ${sub}: invalid <id> '${id}' (must match [A-Za-z0-9][A-Za-z0-9._-]*)`,
855
+ "cli.source.kindRequired": "source add: --kind is required",
856
+ "cli.source.urlRequired": "source add: --url is required",
857
+ "cli.source.invalidKind": ({ kind }) => `source add: invalid --kind '${kind}' (expected: rss | html | html-js | github-releases | npm-registry | json-feed | json-api)`,
858
+ "cli.source.paginationOnlyJsonApi": ({ kind }) => `source add: --pagination-* flags are only valid with --kind json-api (got --kind '${kind}')`,
859
+ "cli.source.validationFailed": "source add: validation failed",
860
+ "cli.source.recipeForbiddenFlags": ({ recipe, flags, }) => `source add: --recipe '${recipe}' supplies kind / url / structural fields; the following flags are not allowed with --recipe: ${flags}`,
861
+ "cli.source.recipeInvalidSource": ({ recipe }) => `source add: recipe '${recipe}' produced an invalid source`,
862
+ "cli.source.alreadyExists": ({ id }) => `source add: '${id}' already exists (sources/${id}.yaml)`,
863
+ "cli.source.created": ({ id }) => `source add: created sources/${id}.yaml`,
864
+ "cli.source.createdFromRecipe": ({ id, recipe }) => `source add: created sources/${id}.yaml from recipe '${recipe}'`,
865
+ "cli.source.noKeywordsWarn": ({ id }) => `source add: warning — '${id}' has no keywords; all fetched items will be filtered out. Edit sources/${id}.yaml or re-add with --keywords to start ingesting.`,
866
+ "cli.source.noKeywordsWarnRecipe": ({ id }) => `source add: warning — '${id}' has no keywords; all fetched items will be filtered out. Re-add with --keywords or edit sources/${id}.yaml to start ingesting.`,
867
+ "cli.source.listNoDir": "source list: no sources directory (run `radar init` first)",
868
+ "cli.source.listNoSources": "source list: no sources defined (use `radar source add ...`)",
869
+ "cli.source.removeNotFound": ({ id }) => `source remove: '${id}' not found (sources/${id}.yaml)`,
870
+ "cli.source.deleted": ({ id }) => `source remove: deleted sources/${id}.yaml`,
871
+ "cli.source.testNotFound": ({ id }) => `source test: '${id}' not found (sources/${id}.yaml)`,
872
+ "cli.source.recipesNone": "source recipes: no recipes bundled (recipes/ is empty or absent)",
873
+ "cli.source.unknownSubcommand": ({ sub }) => `source: unknown subcommand '${sub}'`,
874
+ // triage (#336)
875
+ "cli.triage.modesExclusive": "--dry-run / --apply / --interactive are mutually exclusive",
876
+ "cli.triage.verboseQuietExclusive": "--verbose and --quiet are mutually exclusive",
877
+ "cli.triage.commitIncompatibleModes": "triage: --commit is incompatible with --dry-run / --apply / --interactive",
878
+ "cli.triage.commitIncompatibleEmitPayload": "triage: --commit is incompatible with --emit-payload",
879
+ "cli.triage.emitPayloadIncompatibleModes": "triage: --emit-payload is incompatible with --dry-run / --apply / --interactive",
880
+ "cli.triage.emitPayloadSingleSource": ({ count, sources, }) => `triage: --emit-payload requires a single source group, but ${count} sources have detected items (${sources}). Narrow with --source <id>.`,
881
+ "cli.triage.invalidTriageAgent": ({ agent }) => `triage: --triage-agent '${agent}' is not a valid agent id (claude-code | codex-cli | gemini-cli | copilot)`,
882
+ "cli.triage.noSourcesDir": "triage: no sources/ directory (run `radar init` first)",
883
+ "cli.triage.noSourcesDefined": "triage: no sources defined; nothing to triage",
884
+ "cli.triage.noItemsDir": "triage: no items/ directory; nothing to triage",
885
+ "cli.triage.noDetectedMatch": "triage: no detected items match the filter (nothing to do)",
886
+ "cli.triage.maxItemsExceeded": ({ detected, maxItems, }) => `triage: ${detected} detected item(s) exceed --max-items ${maxItems}; processing the first ${maxItems} only`,
887
+ "cli.triage.skippingNoPolicy": ({ count, sourceId, }) => `triage: skipping ${count} item(s) from source '${sourceId}' (no triagePolicy configured)`,
888
+ "cli.triage.noItemsTriaged": "triage: no items were triaged (all sources skipped)",
889
+ "cli.triage.dryRunNoChanges": "triage: dry-run — no changes written",
890
+ "cli.triage.abortedByUser": "triage: aborted by user",
891
+ "cli.triage.applied": ({ count }) => `triage: applied ${count} decision(s)`,
892
+ "cli.triage.committed": ({ count, sourceId }) => `triage: committed ${count} decision(s) for source '${sourceId}'`,
893
+ "cli.triage.decisionsFileNotFound": ({ path }) => `triage: decisions file not found: ${path}`,
894
+ "cli.triage.unknownSource": ({ sourceId }) => `triage: decisions file references unknown source '${sourceId}'`,
895
+ "cli.triage.sourceNoPolicy": ({ sourceId }) => `triage: source '${sourceId}' has no triagePolicy (cannot validate decisions; pass --policy <path>)`,
896
+ "cli.triage.noItemsDirCommit": "triage: no items/ directory; nothing to commit",
897
+ "cli.triage.noDetectedForSource": ({ sourceId }) => `triage: no detected items remain for source '${sourceId}' (already triaged, or wrong source?)`,
898
+ "cli.triage.invalidDecisionsAgent": ({ agent }) => `triage: decisions file agent '${agent}' is not a valid agent id (claude-code | codex-cli | gemini-cli | copilot)`,
899
+ "cli.triage.feedbackMissingItemId": "triage feedback: missing <item-id>",
900
+ "cli.triage.feedbackModesExclusive": "triage feedback: --correct and --wrong are mutually exclusive",
901
+ "cli.triage.feedbackModeRequired": "triage feedback: one of --correct | --wrong is required",
902
+ "cli.triage.feedbackItemsDirNotFound": "triage feedback: items/ not found (run `radar init`)",
903
+ "cli.triage.feedbackItemNotFound": ({ id }) => `triage feedback: item '${id}' not found under items/`,
904
+ "cli.triage.feedbackNoPriorDecision": ({ id }) => `triage feedback: item '${id}' has no prior triage decision to give feedback on`,
905
+ "cli.triage.feedbackRecorded": ({ sourceId, id, verdict, }) => `triage feedback: items/${sourceId}/${id}.yaml feedback -> ${verdict}`,
906
+ "cli.triage.statsInvalidSince": ({ since }) => `triage stats: invalid --since '${since}' (expected Ns | Nm | Nh | Nd)`,
907
+ "cli.triage.statsNoItemsDir": "triage stats: no items/ directory (run `radar init` first)",
908
+ "cli.triage.statsNoMatch": "triage stats: no triaged items match the filter (nothing to report)",
909
+ // --- init help (#311) -----------------------------------------------------
910
+ "cli.init.help": `Usage: radar init [--lang <en|ja>] [--force] [--with-routines] [--with-actions]
911
+ [--no-claude-skills] [--no-gemini-commands]
912
+ [--no-agents-md] [--no-claude-md] [--no-templates]
913
+ [--no-feedradar-md]
914
+
915
+ Creates the workspace directories and copies bundled skills:
916
+ - Engine SKILLs (SSoT): .agents/skills/{research,review,update}/SKILL.md
917
+ - Claude Code slash-command wrappers: .claude/skills/{research,review,update,dismiss}/SKILL.md
918
+ - Gemini CLI slash commands: .gemini/commands/{research,review,update,dismiss}.toml
919
+ - Agent-agnostic instructions: AGENTS.md (auto-read by Codex / Gemini / Copilot)
920
+ - Claude Code workspace instructions: CLAUDE.md (imports @AGENTS.md so Claude reads it)
921
+ - Starter report templates: templates/default.md (single item) and templates/digest.md (multi-item digest)
922
+ - Human-facing workspace guide: FEEDRADAR.md (natural-language / slash usage)
923
+
924
+ Options:
925
+ --lang <en|ja> Language for generated report templates and workspace docs
926
+ (default: en; also honors RADAR_LANG; persisted to radar.config.yaml)
927
+ --force Overwrite existing files
928
+ --with-routines Generate .claude/routines/watch-daily.yaml (Claude Routines scaffold)
929
+ --with-actions Generate .github/workflows/watch.yaml (GitHub Actions cron scaffold)
930
+ --no-claude-skills Skip writing slash-command wrappers to .claude/skills/
931
+ (useful if @ozzylabs/skills Renovate preset manages that directory)
932
+ --no-gemini-commands Skip writing Gemini CLI slash commands to .gemini/commands/
933
+ (engine SKILLs still serve interactive Gemini via dual-mode)
934
+ --no-agents-md Skip writing AGENTS.md at the workspace root
935
+ (useful if the workspace already has its own AGENTS.md;
936
+ implies --no-claude-md since the bundled CLAUDE.md imports @AGENTS.md)
937
+ --no-claude-md Skip writing CLAUDE.md at the workspace root
938
+ (useful if the workspace already has its own CLAUDE.md)
939
+ --no-templates Skip writing templates/default.md and templates/digest.md
940
+ (research engine SKILL falls back to its built-in structure)
941
+ --no-feedradar-md Skip writing FEEDRADAR.md at the workspace root
942
+ (useful if the workspace already has its own user-facing docs)`,
943
+ // --- audit gap follow-up: dispatcher errors (#342 A1) ---------------------
944
+ // `unknown subcommand` / `unknown type` errors emitted by the workflow /
945
+ // routine dispatchers. The dispatcher already resolved a translator for its
946
+ // help text; these errors now route through it too. The command/subcommand
947
+ // tag (`workflow` / `routine` / `workflow generate` / `routine generate`)
948
+ // stays verbatim so scripts key off the stable prefix.
949
+ "cli.workflow.unknownSubcommand": ({ sub }) => `workflow: unknown subcommand '${sub}'`,
950
+ "cli.workflow.unknownType": ({ type }) => `workflow generate: unknown type '${type}'`,
951
+ "cli.routine.unknownSubcommand": ({ sub }) => `routine: unknown subcommand '${sub}'`,
952
+ "cli.routine.unknownType": ({ type }) => `routine generate: unknown type '${type}'`,
953
+ // --- audit gap follow-up: workflow generate summaries (#342 A2) -----------
954
+ // The generate-completion summary lines (`wrote …`, the key=value detail
955
+ // rows, the "Required secrets" heading, and the post-edit warnings) were
956
+ // English-only even though the locale is resolved. Embedded values
957
+ // (destRel / cron / agent / max-items / secret names) stay verbatim; only
958
+ // the surrounding prose is translated.
959
+ "cli.workflow.generateWatchWrote": ({ path }) => `workflow generate watch: wrote ${path}`,
960
+ "cli.workflow.generateWatchSummary": ({ cron, agent }) => `workflow generate watch: cron='${cron}', agent='${agent}'`,
961
+ "cli.workflow.generateWatchOverwriting": ({ path }) => `workflow generate watch: overwriting existing file ${path}`,
962
+ "cli.workflow.requiredSecretsHeading": "Required GitHub Actions secrets (Settings → Secrets and variables → Actions):",
963
+ "cli.workflow.secretCopilotToken": " GITHUB_TOKEN — auto-provisioned by GitHub Actions (no manual setup needed)",
964
+ "cli.workflow.secretAgentKey": ({ envKey, agent }) => ` ${envKey} — required for the '${agent}' agent`,
965
+ "cli.workflow.secretGithubTokenAuto": " GITHUB_TOKEN — auto-provisioned by GitHub Actions (no manual setup needed)",
966
+ "cli.workflow.generateCombinedWrote": ({ path }) => `workflow generate combined: wrote ${path}`,
967
+ "cli.workflow.generateCombinedOverwriting": ({ path }) => `workflow generate combined: overwriting existing file ${path}`,
968
+ "cli.workflow.detailAgent": ({ agent }) => ` agent: ${agent}`,
969
+ "cli.workflow.detailCron": ({ cron }) => ` cron: ${cron}`,
970
+ "cli.workflow.detailMaxItems": ({ maxItems }) => ` max-items: ${maxItems}`,
971
+ "cli.workflow.detailFilterTags": ({ tags }) => ` filter-tags: ${tags}`,
972
+ "cli.workflow.filterTagsNone": "(none)",
973
+ "cli.workflow.maxItemsCapWarning": ({ cmd }) => `${cmd}: the --max-items cap is also enforced by \`radar research --batch\`; editing the YAML alone will not raise it`,
974
+ "cli.workflow.generateCombinedWithTriageWrote": ({ path }) => `workflow generate combined-with-triage: wrote ${path}`,
975
+ "cli.workflow.generateCombinedWithTriageOverwriting": ({ path }) => `workflow generate combined-with-triage: overwriting existing file ${path}`,
976
+ "cli.workflow.detailWatchCron": ({ cron }) => ` watch-cron: ${cron}`,
977
+ "cli.workflow.detailTriageAgent": ({ agent }) => ` triage-agent: ${agent}`,
978
+ "cli.workflow.detailResearchAgent": ({ agent }) => ` research-agent: ${agent}`,
979
+ "cli.workflow.detailReviewAgent": ({ agent }) => ` review-agent: ${agent}`,
980
+ "cli.workflow.detailMaxItemsWide": ({ maxItems }) => ` max-items: ${maxItems}`,
981
+ "cli.workflow.detailOutputMode": ({ mode }) => ` output-mode: ${mode}`,
982
+ "cli.workflow.detailSlackWebhook": ({ webhook }) => ` slack-webhook: ${webhook}`,
983
+ "cli.workflow.slackWebhookNone": "(none — notify step no-ops)",
984
+ "cli.workflow.secretsNoneAutoToken": " (none — every selected agent rides the auto-provisioned GITHUB_TOKEN)",
985
+ "cli.workflow.secretGithubTokenAutoNoSetup": " GITHUB_TOKEN (auto-provisioned, no setup needed)",
986
+ // --- audit gap follow-up: routine generate summaries (#342 A2) ------------
987
+ // The routine generate completion blocks (`wrote …`, the parameter summary,
988
+ // the Web UI paste instructions, the /schedule note, and the output-gate
989
+ // line) were English-only. Embedded values (destRel / name / repo / cron /
990
+ // model / max-items / output-mode) stay verbatim.
991
+ "cli.routine.generateWatchWrote": ({ path }) => `routine generate watch: wrote ${path}`,
992
+ "cli.routine.generateWatchSummary": ({ name, repo, cron, model, }) => `routine generate watch: name='${name}', repo='${repo}', cron='${cron}', model='${model}'`,
993
+ "cli.routine.generateWatchOverwriting": ({ path }) => `routine generate watch: overwriting existing file ${path}`,
994
+ "cli.routine.generatePipelineWrote": ({ path }) => `routine generate pipeline: wrote ${path}`,
995
+ "cli.routine.generatePipelineSummary": ({ name, repo, cron, model, maxItems, outputMode, }) => `routine generate pipeline: name='${name}', repo='${repo}', cron='${cron}', model='${model}', max-items=${maxItems}, output-mode='${outputMode}'`,
996
+ "cli.routine.generatePipelineOverwriting": ({ path }) => `routine generate pipeline: overwriting existing file ${path}`,
997
+ "cli.routine.autoMergeWarning": ({ cmd }) => `${cmd}: --output-mode auto-merge sets ` +
998
+ "`allow_unrestricted_git_push: true`, but that is NECESSARY, NOT SUFFICIENT — " +
999
+ "you must ALSO turn ON the Web UI 'Allow unrestricted branch pushes' toggle " +
1000
+ "(the RemoteTrigger API does not accept this field). Note that unattended AI " +
1001
+ "output then lands on the default branch with NO human review.",
1002
+ // The Web UI paste flow (shared by watch / pipeline). `path` is the rendered
1003
+ // routine's relative path, interpolated into the yq lines.
1004
+ "cli.routine.pasteNoApi": "Routines has no declarative apply API — paste this routine into the Web UI by hand:",
1005
+ "cli.routine.pasteStep1": " 1. Open https://claude.ai/code/routines and click New routine.",
1006
+ "cli.routine.pasteStep2": " 2. Fill the form fields from the YAML (Name / Model / Repositories / Trigger / Permissions).",
1007
+ "cli.routine.pasteStep3": " 3. For the multi-line Instructions and Setup script fields, extract them with yq:",
1008
+ "cli.routine.pasteYqInstructions": ({ path }) => ` yq -r '.instructions' ${path}`,
1009
+ "cli.routine.pasteYqSetupScript": ({ path }) => ` yq -r '.environment.setup_script' ${path}`,
1010
+ "cli.routine.pasteStep4": " 4. After registering, copy the issued routine_id (trig_xxxx) back into the YAML and set status: active.",
1011
+ "cli.routine.scheduleNote1": "Note on /schedule (Claude Code): it is conversational — `/schedule <description>`",
1012
+ "cli.routine.scheduleNote2": "to create one, plus `list` / `update` / `run` subcommands. There is no flag-based",
1013
+ "cli.routine.scheduleNote3": "form (no `--name` / `--cron` / `--repo` arguments). It also cannot ingest this YAML",
1014
+ "cli.routine.scheduleNote4": "verbatim, so for the long Instructions field the Web UI paste flow above (yq",
1015
+ "cli.routine.scheduleNote5": "extraction) is the practical path. Finally, the unrestricted-git-push permission an",
1016
+ "cli.routine.scheduleNote6": "auto-merge routine needs is set only via the Web UI 'Allow unrestricted branch",
1017
+ "cli.routine.scheduleNote7": "pushes' toggle — /schedule cannot configure it.",
1018
+ "cli.routine.outputGateBranchPr": "Output gate: this routine writes to a claude/* branch / PR only — never main directly.",
1019
+ "cli.routine.outputGateAutoMerge": "Output gate: this routine opens a claude/* PR then squash-merges it to main (review-complete via step 5).",
1020
+ "cli.routine.pipelineNoSpawn1": "Single Claude session, no spawn: unlike the GHA combined-with-triage",
1021
+ "cli.routine.pipelineNoSpawn2": "workflow, there is NO cross-agent review here — one Claude does every step.",
1022
+ "cli.routine.pipelineItemCaps": ({ maxItems }) => `Item caps are CLI-enforced: triage --max-items ${maxItems} / items --limit ${maxItems}.`,
1023
+ // routine fire result notification (#342 A2-adjacent: fire completion lines)
1024
+ "cli.routine.fireTriggered": ({ routineId, status, }) => `routine fire: triggered ${routineId} (HTTP ${status}).`,
1025
+ "cli.routine.fireSessionCreated": "The session was created — this call does not wait for it to finish.",
1026
+ // --- audit gap follow-up: init operational warnings (#342 A3) -------------
1027
+ // Operational warnings emitted while init copies bundled assets / writes the
1028
+ // config locale. These are user-facing ("here is what init skipped and why")
1029
+ // even though the post-run summary was already localized in #312. Paths are
1030
+ // interpolated verbatim.
1031
+ "cli.init.bundledSkillNotFound": ({ src }) => `init: bundled skill not found, skipped: ${src}`,
1032
+ "cli.init.bundledClaudeSkillNotFound": ({ src }) => `init: bundled claude discovery skill not found, skipped: ${src}`,
1033
+ "cli.init.bundledGeminiCommandNotFound": ({ src }) => `init: bundled gemini command not found, skipped: ${src}`,
1034
+ "cli.init.bundledTemplateNotFound": ({ src }) => `init: bundled template not found, skipped: ${src}`,
1035
+ "cli.init.skippedExisting": ({ file }) => `init: skipped existing file (use --force to overwrite): ${file}`,
1036
+ "cli.init.skippedClaudeMdNoAgentsMd": "init: skipped CLAUDE.md because --no-agents-md was passed (the bundled CLAUDE.md imports @AGENTS.md and would dangle)",
1037
+ "cli.init.configLocaleNotYaml": ({ file, reason }) => `init: skipped writing ${file} locale (existing file is not valid YAML: ${reason})`,
1038
+ "cli.init.configLocaleNotMapping": ({ file }) => `init: skipped writing ${file} locale (existing file is not a mapping)`,
1039
+ "cli.init.configLocaleSkippedUpdate": ({ file, current, locale, }) => `init: skipped updating ${file} locale '${current}' -> '${locale}' (use --force to overwrite)`,
1040
+ // --- audit gap follow-up: source list/test/recipes display (#342 A4) ------
1041
+ // The `source list -v` / `source test` / `source recipes` display output
1042
+ // (field labels, the fetched/filtered/matched summary, the selector-adoption
1043
+ // and pagination-preview blocks, and the recipes table headings/prose) were
1044
+ // English (or mixed en/ja) literals. Field IDs / values stay verbatim; only
1045
+ // labels and prose are translated.
1046
+ "cli.source.fieldKind": ({ value }) => ` kind: ${value}`,
1047
+ "cli.source.fieldUrl": ({ value }) => ` url: ${value}`,
1048
+ "cli.source.fieldName": ({ value }) => ` name: ${value}`,
1049
+ "cli.source.fieldTags": ({ value }) => ` tags: ${value}`,
1050
+ "cli.source.fieldKeywords": ({ value }) => ` keywords: ${value}`,
1051
+ "cli.source.fieldExcludeKeywords": ({ value }) => ` excludeKeywords: ${value}`,
1052
+ "cli.source.fieldTrustLevel": ({ value }) => ` trustLevel: ${value}`,
1053
+ "cli.source.fieldLastFetchedAt": ({ value }) => ` lastFetchedAt: ${value}`,
1054
+ "cli.source.keywordsEmpty": "(none — items will be filtered out)",
1055
+ "cli.source.valueNone": "-",
1056
+ "cli.source.listHeaderId": "ID",
1057
+ "cli.source.listHeaderKind": "KIND",
1058
+ "cli.source.listHeaderUrl": "URL",
1059
+ "cli.source.listHeaderTags": "TAGS",
1060
+ "cli.source.testHeading": ({ id }) => `source test: ${id}`,
1061
+ "cli.source.testCounts": ({ fetched, filtered, matched, }) => ` fetched: ${fetched} / filtered: ${filtered} / matched: ${matched}`,
1062
+ "cli.source.facetSweepNotice": ({ facet, testedValue, totalValues, }) => `source test: facet sweep enabled: testing only ${facet}=${testedValue} (the other ${totalValues} facet value(s) are NOT walked). ` +
1063
+ "Range facets test the upper bound (latest value). Run `radar watch run --backfill` to verify every facet value.",
1064
+ "cli.source.selectorAdoptionHeading": " selector adoption:",
1065
+ "cli.source.selectorNoCandidate": ({ field }) => ` ${field}: (no candidate matched)`,
1066
+ "cli.source.selectorAdopted": ({ field, path }) => ` ${field} ← adopted ${path}`,
1067
+ "cli.source.paginationPreviewHeading": " pagination preview (page 0 only — state not mutated):",
1068
+ "cli.source.paginationStrategy": ({ strategy }) => ` strategy: ${strategy}`,
1069
+ "cli.source.paginationNextUrl": ({ nextUrl }) => ` nextUrl: ${nextUrl}`,
1070
+ "cli.source.paginationEndOfPagination": "(end of pagination)",
1071
+ "cli.source.paginationLinkNext": ({ value }) => ` Link rel=next: ${value}`,
1072
+ "cli.source.paginationNextCursor": ({ value }) => ` nextCursor: ${value}`,
1073
+ "cli.source.paginationAbsent": "(absent)",
1074
+ "cli.source.testNoMatched": " (no matched items)",
1075
+ "cli.source.testShowing": ({ shown, total }) => `Showing ${shown} of ${total} matched item(s):`,
1076
+ "cli.source.testItemTitle": ({ index, title }) => ` ${index}. ${title}`,
1077
+ "cli.source.testItemUrl": ({ url }) => ` url: ${url}`,
1078
+ "cli.source.testItemMatchedKeywords": ({ value }) => ` matchedKeywords: ${value}`,
1079
+ "cli.source.testItemContent": ({ value }) => ` content: ${value}`,
1080
+ "cli.source.testMoreItems": ({ count }) => ` … ${count} more (raise --limit to see them)`,
1081
+ "cli.source.recipesNoValid": "source recipes: no valid recipes found (all bundled entries failed to load)",
1082
+ "cli.source.recipesHeaderName": "NAME",
1083
+ "cli.source.recipesHeaderKind": "KIND",
1084
+ "cli.source.recipesHeaderDescription": "DESCRIPTION",
1085
+ "cli.source.recipesErrorsHeading": "Recipes with errors:",
1086
+ "cli.source.recipesErrorRow": ({ name, error }) => ` ${name}: ${error}`,
1087
+ "cli.source.recipesErrorUnknown": "(unknown error)",
1088
+ "cli.source.recipesApplyHeading": "Apply a recipe with:",
1089
+ "cli.source.recipesApplyExample": " radar source add <id> --recipe <name> [--keywords <kw>] [--tags <t>] [--name <display>]",
1090
+ // --- audit gap follow-up: triage progress + confirm prompt (#342 A6/B1) ---
1091
+ // The per-source triage progress marker and the interactive apply-confirm
1092
+ // prompt were English literals. Source id / agent / count stay verbatim.
1093
+ "cli.triage.progressTriaging": ({ count, sourceId, agent, }) => `Triaging ${count} item(s) from source '${sourceId}' via ${agent}`,
1094
+ "cli.triage.confirmApply": "Apply these decisions? [y/N]",
1095
+ };
1096
+ //# sourceMappingURL=en.js.map