@ozzylabs/feedradar 0.2.1 → 0.2.3

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