@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.
- package/README.ja.md +51 -13
- package/README.md +51 -13
- package/dist/agents/_boundary.d.ts +21 -0
- package/dist/agents/_boundary.d.ts.map +1 -1
- package/dist/agents/_boundary.js +34 -0
- package/dist/agents/_boundary.js.map +1 -1
- package/dist/agents/claude-code.d.ts.map +1 -1
- package/dist/agents/claude-code.js +14 -6
- package/dist/agents/claude-code.js.map +1 -1
- package/dist/agents/codex-cli.d.ts.map +1 -1
- package/dist/agents/codex-cli.js +13 -7
- package/dist/agents/codex-cli.js.map +1 -1
- package/dist/agents/copilot.d.ts.map +1 -1
- package/dist/agents/copilot.js +13 -6
- package/dist/agents/copilot.js.map +1 -1
- package/dist/agents/gemini-cli.d.ts.map +1 -1
- package/dist/agents/gemini-cli.js +13 -6
- package/dist/agents/gemini-cli.js.map +1 -1
- package/dist/agents/types.d.ts +26 -0
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/claude-skills/dismiss/SKILL.md +4 -4
- package/dist/claude-skills/research/SKILL.md +2 -3
- package/dist/claude-skills/review/SKILL.md +2 -2
- package/dist/claude-skills/update/SKILL.md +7 -7
- package/dist/cli/_locale.d.ts +96 -0
- package/dist/cli/_locale.d.ts.map +1 -0
- package/dist/cli/_locale.js +130 -0
- package/dist/cli/_locale.js.map +1 -0
- package/dist/cli/_progress.d.ts +30 -1
- package/dist/cli/_progress.d.ts.map +1 -1
- package/dist/cli/_progress.js +9 -1
- package/dist/cli/_progress.js.map +1 -1
- package/dist/cli/dismiss.d.ts.map +1 -1
- package/dist/cli/dismiss.js +61 -54
- package/dist/cli/dismiss.js.map +1 -1
- package/dist/cli/doctor.d.ts +8 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +91 -60
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +36 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +81 -18
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +15 -0
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +149 -51
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/items.d.ts.map +1 -1
- package/dist/cli/items.js +51 -30
- package/dist/cli/items.js.map +1 -1
- package/dist/cli/research.d.ts.map +1 -1
- package/dist/cli/research.js +138 -109
- package/dist/cli/research.js.map +1 -1
- package/dist/cli/review.d.ts.map +1 -1
- package/dist/cli/review.js +114 -92
- package/dist/cli/review.js.map +1 -1
- package/dist/cli/routine/fire.d.ts +3 -2
- package/dist/cli/routine/fire.d.ts.map +1 -1
- package/dist/cli/routine/fire.js +30 -25
- package/dist/cli/routine/fire.js.map +1 -1
- package/dist/cli/routine/generate-pipeline.d.ts +24 -10
- package/dist/cli/routine/generate-pipeline.d.ts.map +1 -1
- package/dist/cli/routine/generate-pipeline.js +158 -83
- package/dist/cli/routine/generate-pipeline.js.map +1 -1
- package/dist/cli/routine/generate-watch.d.ts +56 -1
- package/dist/cli/routine/generate-watch.d.ts.map +1 -1
- package/dist/cli/routine/generate-watch.js +116 -42
- package/dist/cli/routine/generate-watch.js.map +1 -1
- package/dist/cli/routine.d.ts.map +1 -1
- package/dist/cli/routine.js +28 -24
- package/dist/cli/routine.js.map +1 -1
- package/dist/cli/source.d.ts.map +1 -1
- package/dist/cli/source.js +212 -182
- package/dist/cli/source.js.map +1 -1
- package/dist/cli/state.d.ts +43 -0
- package/dist/cli/state.d.ts.map +1 -0
- package/dist/cli/state.js +177 -0
- package/dist/cli/state.js.map +1 -0
- package/dist/cli/triage.d.ts.map +1 -1
- package/dist/cli/triage.js +146 -130
- package/dist/cli/triage.js.map +1 -1
- package/dist/cli/undismiss.d.ts.map +1 -1
- package/dist/cli/undismiss.js +32 -25
- package/dist/cli/undismiss.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +77 -61
- package/dist/cli/update.js.map +1 -1
- package/dist/cli/watch.d.ts.map +1 -1
- package/dist/cli/watch.js +71 -31
- package/dist/cli/watch.js.map +1 -1
- package/dist/cli/workflow/generate-combined-with-triage.d.ts +9 -2
- package/dist/cli/workflow/generate-combined-with-triage.d.ts.map +1 -1
- package/dist/cli/workflow/generate-combined-with-triage.js +120 -71
- package/dist/cli/workflow/generate-combined-with-triage.js.map +1 -1
- package/dist/cli/workflow/generate-combined.d.ts +8 -1
- package/dist/cli/workflow/generate-combined.d.ts.map +1 -1
- package/dist/cli/workflow/generate-combined.js +39 -33
- package/dist/cli/workflow/generate-combined.js.map +1 -1
- package/dist/cli/workflow/generate-watch.d.ts +10 -1
- package/dist/cli/workflow/generate-watch.d.ts.map +1 -1
- package/dist/cli/workflow/generate-watch.js +37 -30
- package/dist/cli/workflow/generate-watch.js.map +1 -1
- package/dist/cli/workflow.d.ts.map +1 -1
- package/dist/cli/workflow.js +28 -23
- package/dist/cli/workflow.js.map +1 -1
- package/dist/core/config.d.ts +2 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +14 -4
- package/dist/core/config.js.map +1 -1
- package/dist/core/feeds/html-js.d.ts.map +1 -1
- package/dist/core/feeds/html-js.js +16 -9
- package/dist/core/feeds/html-js.js.map +1 -1
- package/dist/core/feeds/json-api.d.ts.map +1 -1
- package/dist/core/feeds/json-api.js +38 -21
- package/dist/core/feeds/json-api.js.map +1 -1
- package/dist/core/feeds/types.d.ts +9 -0
- package/dist/core/feeds/types.d.ts.map +1 -1
- package/dist/core/filter.d.ts +20 -12
- package/dist/core/filter.d.ts.map +1 -1
- package/dist/core/filter.js +87 -46
- package/dist/core/filter.js.map +1 -1
- package/dist/core/locale.d.ts +69 -0
- package/dist/core/locale.d.ts.map +1 -0
- package/dist/core/locale.js +74 -0
- package/dist/core/locale.js.map +1 -0
- package/dist/core/state.d.ts +20 -0
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/state.js +26 -0
- package/dist/core/state.js.map +1 -1
- package/dist/core/triage/prompt.d.ts.map +1 -1
- package/dist/core/triage/prompt.js +18 -4
- package/dist/core/triage/prompt.js.map +1 -1
- package/dist/core/watcher.d.ts +28 -0
- package/dist/core/watcher.d.ts.map +1 -1
- package/dist/core/watcher.js +77 -8
- package/dist/core/watcher.js.map +1 -1
- package/dist/i18n/index.d.ts +57 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +49 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/messages/en.d.ts +1049 -0
- package/dist/i18n/messages/en.d.ts.map +1 -0
- package/dist/i18n/messages/en.js +1152 -0
- package/dist/i18n/messages/en.js.map +1 -0
- package/dist/i18n/messages/ja.d.ts +13 -0
- package/dist/i18n/messages/ja.d.ts.map +1 -0
- package/dist/i18n/messages/ja.js +1010 -0
- package/dist/i18n/messages/ja.js.map +1 -0
- package/dist/schemas/config.d.ts +7 -0
- package/dist/schemas/config.d.ts.map +1 -1
- package/dist/schemas/config.js +5 -0
- package/dist/schemas/config.js.map +1 -1
- package/dist/schemas/item.d.ts +1 -0
- package/dist/schemas/item.d.ts.map +1 -1
- package/dist/schemas/item.js +15 -0
- package/dist/schemas/item.js.map +1 -1
- package/dist/schemas/recipe.d.ts +7 -1
- package/dist/schemas/recipe.d.ts.map +1 -1
- package/dist/schemas/recipe.js +1 -0
- package/dist/schemas/recipe.js.map +1 -1
- package/dist/schemas/source.d.ts +40 -18
- package/dist/schemas/source.d.ts.map +1 -1
- package/dist/schemas/source.js +84 -23
- package/dist/schemas/source.js.map +1 -1
- package/dist/skills/research/SKILL.md +13 -12
- package/dist/skills/review/SKILL.md +13 -12
- package/dist/skills/update/SKILL.md +19 -19
- package/dist/templates/en/agents/AGENTS.md +284 -0
- package/dist/templates/en/claude/CLAUDE.md +5 -0
- package/dist/templates/en/default.md +16 -0
- package/dist/templates/en/digest.md +66 -0
- package/dist/templates/en/feedradar.md +235 -0
- package/dist/templates/{routines → en/routines}/pipeline.yaml.tmpl +93 -34
- package/dist/templates/{routines → en/routines}/watch-daily.yaml +12 -15
- package/dist/templates/{routines → en/routines}/watch.yaml.tmpl +11 -14
- package/dist/templates/{workflows → en/workflows}/combined-with-triage.template.yaml.tmpl +3 -3
- package/dist/templates/{workflows → en/workflows}/combined.template.yaml.tmpl +6 -6
- package/dist/templates/{workflows → en/workflows}/watch.template.yaml.tmpl +8 -8
- package/dist/templates/{workflows → en/workflows}/watch.yaml +3 -3
- package/dist/templates/{agents → ja/agents}/AGENTS.md +16 -16
- package/dist/templates/{digest.md → ja/digest.md} +5 -6
- package/dist/templates/{feedradar.md → ja/feedradar.md} +12 -12
- package/dist/templates/ja/routines/pipeline.yaml.tmpl +267 -0
- package/dist/templates/ja/routines/watch-daily.yaml +151 -0
- package/dist/templates/ja/routines/watch.yaml.tmpl +145 -0
- package/dist/templates/ja/workflows/combined-with-triage.template.yaml.tmpl +123 -0
- package/dist/templates/ja/workflows/combined.template.yaml.tmpl +109 -0
- package/dist/templates/ja/workflows/watch.template.yaml.tmpl +100 -0
- package/dist/templates/ja/workflows/watch.yaml +73 -0
- package/package.json +1 -1
- /package/dist/templates/{claude → ja/claude}/CLAUDE.md +0 -0
- /package/dist/templates/{default.md → ja/default.md} +0 -0
package/dist/cli/research.js
CHANGED
|
@@ -23,8 +23,10 @@ import { getDefaultAgent, loadRadarConfig, RadarConfigError } from "../core/conf
|
|
|
23
23
|
import { loadItems, saveItems } from "../core/items.js";
|
|
24
24
|
import { loadTemplate } from "../core/templates.js";
|
|
25
25
|
import { isValidTransition } from "../core/transitions.js";
|
|
26
|
+
import { createTranslator } from "../i18n/index.js";
|
|
26
27
|
import { AgentIdSchema, ResearchFrontmatterSchema } from "../schemas/index.js";
|
|
27
28
|
import { resolveCommitPathInside } from "./_commit-path.js";
|
|
29
|
+
import { LangFlagError, resolveCommandLocale } from "./_locale.js";
|
|
28
30
|
import { buildAgentProgressCallback, buildReporter, ProgressFlagError, parseProgressFlags, pollOutputFileSize, } from "./_progress.js";
|
|
29
31
|
/**
|
|
30
32
|
* Default hard-cap for `radar research --batch`.
|
|
@@ -114,55 +116,8 @@ function parseArgs(args) {
|
|
|
114
116
|
}
|
|
115
117
|
return out;
|
|
116
118
|
}
|
|
117
|
-
function printHelp(log) {
|
|
118
|
-
log("
|
|
119
|
-
log(" radar research <item-id> [--agent <agent-id>] [--template <template-id>]");
|
|
120
|
-
log(" radar research --digest <item-id> <item-id> ... [--triage-group <group>] [--agent <agent-id>] [--template <id>]");
|
|
121
|
-
log(` radar research --batch [--status <status>] [--max-items N] [--filter-tags <list>] [--agent <id>]`);
|
|
122
|
-
log(" radar research <item-id> --emit-payload [--digest <ids...>] [--template <id>]");
|
|
123
|
-
log(" radar research --commit <path>");
|
|
124
|
-
log("");
|
|
125
|
-
log("Arguments:");
|
|
126
|
-
log(" <item-id> Item id (matches items/<sourceId>/<item-id>.yaml)");
|
|
127
|
-
log(" Pass 2 or more ids together with --digest to bundle them.");
|
|
128
|
-
log(" Omit positional ids with --batch — items are discovered.");
|
|
129
|
-
log("");
|
|
130
|
-
log("Options:");
|
|
131
|
-
log(" --agent <agent-id> claude-code | codex-cli | gemini-cli | copilot (default: claude-code)");
|
|
132
|
-
log(" --template <id> Template id under templates/ (default: default; digest: digest)");
|
|
133
|
-
log(" --digest Bundle multiple items into a single digest report (ADR-0011)");
|
|
134
|
-
log(" --triage-group <group> Digest-mode slug source (ADR-0018 §W-H): name the digest");
|
|
135
|
-
log(" file after this triage.group instead of the matchedKeywords");
|
|
136
|
-
log(" frequency. Required to keep per-group digests unique on the");
|
|
137
|
-
log(" same day when a single-keyword source emits multiple groups");
|
|
138
|
-
log(" (#255). Falls back to the matchedKeywords slug when omitted.");
|
|
139
|
-
log(" --batch Research every item matching --status (and --filter-tags)");
|
|
140
|
-
log(" respecting the --max-items hard-cap (ADR-0014 D3a).");
|
|
141
|
-
log(" --status <status> Batch-mode filter: detected | triaged_research");
|
|
142
|
-
log(" (default: detected). `triaged_research` consumes items");
|
|
143
|
-
log(" the triage adapter promoted (ADR-0018 §W-B) and");
|
|
144
|
-
log(" transitions them to `researched` on success.");
|
|
145
|
-
log(` --max-items N Batch-mode hard-cap on processed items (default: ${RESEARCH_BATCH_DEFAULT_MAX_ITEMS}).`);
|
|
146
|
-
log(" Excess items are dropped and announced via warn() so a runaway");
|
|
147
|
-
log(" detection cannot blow the cap from inside a workflow.");
|
|
148
|
-
log(" --filter-tags <list> Batch-mode comma-separated allow-list matched against");
|
|
149
|
-
log(" each item's matchedKeywords (case-insensitive). Default: all.");
|
|
150
|
-
log(" --emit-payload Host-agent mode (ADR-0019): print the research payload to");
|
|
151
|
-
log(" stdout and DO NOT spawn an agent. The interactive host");
|
|
152
|
-
log(" session runs the SKILL procedure itself, then finalizes");
|
|
153
|
-
log(" with `radar research --commit <path>`. Interactive/opt-in");
|
|
154
|
-
log(" only — CI/headless must use the default spawn path.");
|
|
155
|
-
log(" --commit <path> Host-agent mode (ADR-0019): validate an externally-written");
|
|
156
|
-
log(" report (under <cwd>/research/) against ResearchFrontmatter-");
|
|
157
|
-
log(" Schema and apply the detected → researched transition.");
|
|
158
|
-
log(" --verbose Stream the agent CLI's stdout/stderr in addition to phase markers.");
|
|
159
|
-
log(" --quiet Suppress phase markers and spinner; print only the completion line.");
|
|
160
|
-
log(" Equivalent to setting RADAR_NO_PROGRESS=1 (ADR-0015 D2).");
|
|
161
|
-
log("");
|
|
162
|
-
log("Output:");
|
|
163
|
-
log(" single-item: research/<YYYYMMDD>_<slug>_v1.md (ADR-0003)");
|
|
164
|
-
log(" digest: research/<YYYYMMDD>_digest_<slug>_v1.md (ADR-0011)");
|
|
165
|
-
log(" batch: one single-item report per matched item (no digest aggregation).");
|
|
119
|
+
function printHelp(t, log) {
|
|
120
|
+
log(t("cli.research.help", { maxItems: RESEARCH_BATCH_DEFAULT_MAX_ITEMS }));
|
|
166
121
|
}
|
|
167
122
|
async function pathExists(p) {
|
|
168
123
|
try {
|
|
@@ -295,12 +250,12 @@ async function findItems(cwd, itemIds) {
|
|
|
295
250
|
}
|
|
296
251
|
return { items: matched };
|
|
297
252
|
}
|
|
298
|
-
async function resolveAgent(cwd, rawAgent, error) {
|
|
253
|
+
async function resolveAgent(cwd, rawAgent, error, t) {
|
|
299
254
|
let explicitAgent;
|
|
300
255
|
if (rawAgent !== undefined) {
|
|
301
256
|
const agentResult = AgentIdSchema.safeParse(rawAgent);
|
|
302
257
|
if (!agentResult.success) {
|
|
303
|
-
error(
|
|
258
|
+
error(t("cli.agent.invalid", { cmd: "research", agent: rawAgent }));
|
|
304
259
|
return { exitCode: 2 };
|
|
305
260
|
}
|
|
306
261
|
explicitAgent = agentResult.data;
|
|
@@ -332,7 +287,7 @@ async function resolveAgent(cwd, rawAgent, error) {
|
|
|
332
287
|
* collision backstop as the spawn path, without the model-call step.
|
|
333
288
|
*/
|
|
334
289
|
async function prepareResearch(params) {
|
|
335
|
-
const { cwd, items, digest, templateId, now, triageGroup, warn, error, progress } = params;
|
|
290
|
+
const { cwd, items, digest, templateId, now, triageGroup, warn, error, progress, t } = params;
|
|
336
291
|
for (const item of items) {
|
|
337
292
|
if (item.injectionFlags.length > 0) {
|
|
338
293
|
warn(`research: item '${item.id}' has ${item.injectionFlags.length} injection flag(s): ${item.injectionFlags.join(", ")} (audit-only; use \`radar dismiss\` to skip)`);
|
|
@@ -352,16 +307,18 @@ async function prepareResearch(params) {
|
|
|
352
307
|
}
|
|
353
308
|
const outputPath = join(cwd, "research", filename);
|
|
354
309
|
if (await pathExists(outputPath)) {
|
|
355
|
-
error(
|
|
310
|
+
error(t("cli.research.alreadyExists", { path: outputPath }));
|
|
356
311
|
return { exitCode: 1 };
|
|
357
312
|
}
|
|
358
313
|
// Phase marker: items resolved (ADR-0015 D4 "Loaded <noun>"). One marker
|
|
359
314
|
// per invocation regardless of digest cardinality so the progress stream
|
|
360
315
|
// stays uniform between single / digest / batch modes.
|
|
361
|
-
progress.phase(digest
|
|
316
|
+
progress.phase(digest
|
|
317
|
+
? t("cli.progress.loadedItems", { count: items.length })
|
|
318
|
+
: t("cli.progress.loadedItem", { id: items[0].id }), items.map((i) => i.id).join(", "));
|
|
362
319
|
// Phase marker: template resolved. Echoes the actual template id so a
|
|
363
320
|
// user running `--template deep-dive` sees the value flow through.
|
|
364
|
-
progress.phase(
|
|
321
|
+
progress.phase(t("cli.progress.loadedTemplate", { templateId }));
|
|
365
322
|
return { outputPath, filename };
|
|
366
323
|
}
|
|
367
324
|
/**
|
|
@@ -381,7 +338,7 @@ async function prepareResearch(params) {
|
|
|
381
338
|
* of them.
|
|
382
339
|
*/
|
|
383
340
|
async function finalizeResearch(params) {
|
|
384
|
-
const { cwd, outputPath, log, warn, error, progress } = params;
|
|
341
|
+
const { cwd, outputPath, log, warn, error, progress, t } = params;
|
|
385
342
|
if (!(await pathExists(outputPath))) {
|
|
386
343
|
error(`research: report was not written to ${outputPath} (did not write the output path?)`);
|
|
387
344
|
return 1;
|
|
@@ -413,7 +370,7 @@ async function finalizeResearch(params) {
|
|
|
413
370
|
// Phase marker: schema check passed. Emitted before the status transition
|
|
414
371
|
// so the user sees the validation outcome separately from the items.yaml
|
|
415
372
|
// write that follows.
|
|
416
|
-
progress.phase("
|
|
373
|
+
progress.phase(t("cli.progress.frontmatterValidated"));
|
|
417
374
|
const reviewedDrift = fmResult.data.reviewedAt !== null || fmResult.data.reviewedBy !== null;
|
|
418
375
|
const supersedesDrift = fmResult.data.supersedes !== null;
|
|
419
376
|
if (reviewedDrift || supersedesDrift) {
|
|
@@ -483,21 +440,25 @@ async function finalizeResearch(params) {
|
|
|
483
440
|
error(` (research file was written: ${outputPath})`);
|
|
484
441
|
return 1;
|
|
485
442
|
}
|
|
486
|
-
log(
|
|
443
|
+
log(t("cli.research.wrote", { path: outputPath }));
|
|
487
444
|
for (const item of updated) {
|
|
488
445
|
const from = transitions.get(item.id);
|
|
489
446
|
if (from !== undefined && item.status === "researched") {
|
|
490
447
|
// Phase marker: status transition. We emit one phase per item rather
|
|
491
448
|
// than collapsing them so the digest case stays explicit about what
|
|
492
449
|
// moved. The arrow uses U+2192 (→) per ADR-0015 D4 examples.
|
|
493
|
-
progress.phase(
|
|
494
|
-
log(
|
|
450
|
+
progress.phase(t("cli.progress.statusTransition", { from, to: "researched" }), `items/${item.sourceId}/${item.id}.yaml`);
|
|
451
|
+
log(t("cli.research.transitioned", { sourceId: item.sourceId, id: item.id }));
|
|
495
452
|
}
|
|
496
453
|
}
|
|
497
454
|
return 0;
|
|
498
455
|
}
|
|
499
456
|
async function processResearchInvocation(params) {
|
|
500
|
-
const { cwd, items, digest, agent, templateId, template, now, triageGroup, log, warn, error, progress, } = params;
|
|
457
|
+
const { cwd, items, digest, agent, templateId, template, locale, now, triageGroup, log, warn, error, progress, } = params;
|
|
458
|
+
// Translator for the user-facing progress phase labels (#313). Derived from
|
|
459
|
+
// the same resolved locale already used for the report-output language
|
|
460
|
+
// (#316) so the spinner / phase markers and the report body agree.
|
|
461
|
+
const t = createTranslator(locale);
|
|
501
462
|
const prepared = await prepareResearch({
|
|
502
463
|
cwd,
|
|
503
464
|
items,
|
|
@@ -508,6 +469,7 @@ async function processResearchInvocation(params) {
|
|
|
508
469
|
warn,
|
|
509
470
|
error,
|
|
510
471
|
progress,
|
|
472
|
+
t,
|
|
511
473
|
});
|
|
512
474
|
if ("exitCode" in prepared)
|
|
513
475
|
return prepared.exitCode;
|
|
@@ -520,8 +482,8 @@ async function processResearchInvocation(params) {
|
|
|
520
482
|
// Phase marker + spinner: agent spawn. We pair `phase("Spawning …")` with
|
|
521
483
|
// `start("Agent running…")` so the marker is printed once for scrollback
|
|
522
484
|
// and the spinner row carries the live `[mm:ss]` heartbeat + metrics.
|
|
523
|
-
progress.phase(
|
|
524
|
-
progress.start("
|
|
485
|
+
progress.phase(t("cli.progress.spawning", { agent }), `cwd: ${cwd}`);
|
|
486
|
+
progress.start(t("cli.progress.agentRunning"));
|
|
525
487
|
const adapterStartedAt = Date.now();
|
|
526
488
|
const polling = pollOutputFileSize({ path: outputPath, reporter: progress });
|
|
527
489
|
let adapterExitCode = 0;
|
|
@@ -533,13 +495,14 @@ async function processResearchInvocation(params) {
|
|
|
533
495
|
items,
|
|
534
496
|
outputPath,
|
|
535
497
|
cwd,
|
|
498
|
+
locale,
|
|
536
499
|
onProgress: buildAgentProgressCallback(progress),
|
|
537
500
|
});
|
|
538
501
|
}
|
|
539
502
|
catch (e) {
|
|
540
503
|
adapterExitCode = 1;
|
|
541
504
|
polling.stop();
|
|
542
|
-
progress.fail("
|
|
505
|
+
progress.fail(t("cli.progress.agentFailed"), e instanceof Error ? e.message : String(e));
|
|
543
506
|
error(`research: adapter failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
544
507
|
return 1;
|
|
545
508
|
}
|
|
@@ -547,9 +510,9 @@ async function processResearchInvocation(params) {
|
|
|
547
510
|
polling.stop();
|
|
548
511
|
}
|
|
549
512
|
if (adapterExitCode === 0) {
|
|
550
|
-
progress.succeed(
|
|
513
|
+
progress.succeed(t("cli.progress.agentCompleted", { exitCode: adapterExitCode }), Date.now() - adapterStartedAt);
|
|
551
514
|
}
|
|
552
|
-
return finalizeResearch({ cwd, outputPath, items, log, warn, error, progress });
|
|
515
|
+
return finalizeResearch({ cwd, outputPath, items, log, warn, error, progress, t });
|
|
553
516
|
}
|
|
554
517
|
/**
|
|
555
518
|
* Host-agent emit path (#254 / ADR-0019): run the same PRE block as the spawn
|
|
@@ -559,7 +522,7 @@ async function processResearchInvocation(params) {
|
|
|
559
522
|
* `radar research --commit`.
|
|
560
523
|
*/
|
|
561
524
|
async function runResearchEmitPayload(params) {
|
|
562
|
-
const { cwd, items, digest, agent, templateId, template, now, triageGroup, log, warn, error, progress, } = params;
|
|
525
|
+
const { cwd, items, digest, agent, templateId, template, now, triageGroup, log, warn, error, progress, t, } = params;
|
|
563
526
|
const prepared = await prepareResearch({
|
|
564
527
|
cwd,
|
|
565
528
|
items,
|
|
@@ -570,6 +533,7 @@ async function runResearchEmitPayload(params) {
|
|
|
570
533
|
warn,
|
|
571
534
|
error,
|
|
572
535
|
progress,
|
|
536
|
+
t,
|
|
573
537
|
});
|
|
574
538
|
if ("exitCode" in prepared)
|
|
575
539
|
return prepared.exitCode;
|
|
@@ -594,7 +558,7 @@ async function runResearchEmitPayload(params) {
|
|
|
594
558
|
* code, not just SKILL guidance).
|
|
595
559
|
*/
|
|
596
560
|
async function runResearchCommit(params) {
|
|
597
|
-
const { cwd, commitPath, log, warn, error, progress } = params;
|
|
561
|
+
const { cwd, commitPath, log, warn, error, progress, t } = params;
|
|
598
562
|
const guard = await resolveCommitPathInside(cwd, "research", commitPath);
|
|
599
563
|
if ("error" in guard) {
|
|
600
564
|
error(`research: ${guard.error}`);
|
|
@@ -608,18 +572,19 @@ async function runResearchCommit(params) {
|
|
|
608
572
|
warn,
|
|
609
573
|
error,
|
|
610
574
|
progress,
|
|
575
|
+
t,
|
|
611
576
|
});
|
|
612
577
|
}
|
|
613
|
-
function parseMaxItems(raw, error) {
|
|
578
|
+
function parseMaxItems(raw, error, t) {
|
|
614
579
|
if (raw === undefined)
|
|
615
580
|
return RESEARCH_BATCH_DEFAULT_MAX_ITEMS;
|
|
616
581
|
if (!/^[0-9]+$/.test(raw)) {
|
|
617
|
-
error(
|
|
582
|
+
error(t("cli.research.invalidMaxItemsInteger", { raw }));
|
|
618
583
|
return null;
|
|
619
584
|
}
|
|
620
585
|
const n = Number.parseInt(raw, 10);
|
|
621
586
|
if (!Number.isFinite(n) || n <= 0) {
|
|
622
|
-
error(
|
|
587
|
+
error(t("cli.research.invalidMaxItemsPositive", { raw }));
|
|
623
588
|
return null;
|
|
624
589
|
}
|
|
625
590
|
return n;
|
|
@@ -634,30 +599,33 @@ function parseFilterTags(raw) {
|
|
|
634
599
|
.filter((s) => s.length > 0)),
|
|
635
600
|
];
|
|
636
601
|
}
|
|
637
|
-
async function runResearchBatch(parsed, cwd, log, warn, error, progress) {
|
|
602
|
+
async function runResearchBatch(parsed, cwd, locale, log, warn, error, progress, t) {
|
|
638
603
|
if (parsed.itemIds.length > 0) {
|
|
639
|
-
error(
|
|
604
|
+
error(t("cli.research.batchIncompatiblePositional", { count: parsed.itemIds.length }));
|
|
640
605
|
return 2;
|
|
641
606
|
}
|
|
642
607
|
if (parsed.digest) {
|
|
643
|
-
error("research
|
|
608
|
+
error(t("cli.research.batchIncompatibleDigest"));
|
|
644
609
|
return 2;
|
|
645
610
|
}
|
|
646
611
|
if (parsed.triageGroup !== undefined) {
|
|
647
|
-
error("research
|
|
612
|
+
error(t("cli.research.batchIncompatibleTriageGroup"));
|
|
648
613
|
return 2;
|
|
649
614
|
}
|
|
650
615
|
const rawStatus = parsed.status ?? "detected";
|
|
651
616
|
if (!RESEARCH_BATCH_ALLOWED_STATUSES.includes(rawStatus)) {
|
|
652
|
-
error(
|
|
617
|
+
error(t("cli.research.invalidStatus", {
|
|
618
|
+
status: rawStatus,
|
|
619
|
+
allowed: RESEARCH_BATCH_ALLOWED_STATUSES.join(" | "),
|
|
620
|
+
}));
|
|
653
621
|
return 2;
|
|
654
622
|
}
|
|
655
623
|
const status = rawStatus;
|
|
656
|
-
const maxItems = parseMaxItems(parsed.maxItems, error);
|
|
624
|
+
const maxItems = parseMaxItems(parsed.maxItems, error, t);
|
|
657
625
|
if (maxItems === null)
|
|
658
626
|
return 2;
|
|
659
627
|
const filterTags = parseFilterTags(parsed.filterTags);
|
|
660
|
-
const agentResult = await resolveAgent(cwd, parsed.agent, error);
|
|
628
|
+
const agentResult = await resolveAgent(cwd, parsed.agent, error, t);
|
|
661
629
|
if ("exitCode" in agentResult)
|
|
662
630
|
return agentResult.exitCode;
|
|
663
631
|
const agent = agentResult.agent;
|
|
@@ -689,17 +657,24 @@ async function runResearchBatch(parsed, cwd, log, warn, error, progress) {
|
|
|
689
657
|
return ap < bp ? -1 : 1;
|
|
690
658
|
return a.id.localeCompare(b.id);
|
|
691
659
|
});
|
|
660
|
+
const tagsSuffix = filterTags.length > 0 ? `, tags=${filterTags.join(",")}` : "";
|
|
692
661
|
if (matches.length === 0) {
|
|
693
|
-
log(
|
|
662
|
+
log(t("cli.research.noItemsMatched", { status, tags: tagsSuffix }));
|
|
694
663
|
return 0;
|
|
695
664
|
}
|
|
696
665
|
let selected = matches;
|
|
697
666
|
if (matches.length > maxItems) {
|
|
698
667
|
const dropped = matches.length - maxItems;
|
|
699
|
-
warn(
|
|
668
|
+
warn(t("cli.research.capReached", { maxItems, dropped, matched: matches.length }));
|
|
700
669
|
selected = matches.slice(0, maxItems);
|
|
701
670
|
}
|
|
702
|
-
log(
|
|
671
|
+
log(t("cli.research.batchWillProcess", {
|
|
672
|
+
count: selected.length,
|
|
673
|
+
status,
|
|
674
|
+
tags: tagsSuffix,
|
|
675
|
+
agent,
|
|
676
|
+
cap: maxItems,
|
|
677
|
+
}));
|
|
703
678
|
const now = new Date();
|
|
704
679
|
for (const item of selected) {
|
|
705
680
|
const exitCode = await processResearchInvocation({
|
|
@@ -709,6 +684,7 @@ async function runResearchBatch(parsed, cwd, log, warn, error, progress) {
|
|
|
709
684
|
agent,
|
|
710
685
|
templateId,
|
|
711
686
|
template,
|
|
687
|
+
locale,
|
|
712
688
|
now,
|
|
713
689
|
log,
|
|
714
690
|
warn,
|
|
@@ -716,11 +692,11 @@ async function runResearchBatch(parsed, cwd, log, warn, error, progress) {
|
|
|
716
692
|
progress,
|
|
717
693
|
});
|
|
718
694
|
if (exitCode !== 0) {
|
|
719
|
-
error(
|
|
695
|
+
error(t("cli.research.batchHalted", { id: item.id, exitCode }));
|
|
720
696
|
return exitCode;
|
|
721
697
|
}
|
|
722
698
|
}
|
|
723
|
-
log(
|
|
699
|
+
log(t("cli.research.batchCompleted", { count: selected.length }));
|
|
724
700
|
return 0;
|
|
725
701
|
}
|
|
726
702
|
export async function runResearch(args, options = {}) {
|
|
@@ -743,20 +719,56 @@ export async function runResearch(args, options = {}) {
|
|
|
743
719
|
}
|
|
744
720
|
throw e;
|
|
745
721
|
}
|
|
746
|
-
//
|
|
747
|
-
//
|
|
748
|
-
//
|
|
749
|
-
|
|
722
|
+
// Resolve the report-output locale (#316) BEFORE the command-specific parser
|
|
723
|
+
// sees argv: `resolveCommandLocale` strips `--lang` so `parseArgs` never trips
|
|
724
|
+
// on it (mirrors the `--verbose` / `--quiet` strip above). `config.locale` is
|
|
725
|
+
// the lowest-priority source; we read it best-effort here (RadarConfigError is
|
|
726
|
+
// surfaced authoritatively by `resolveAgent` below, so we tolerate it as
|
|
727
|
+
// "no config locale" and let the agent resolver report the malformed config).
|
|
728
|
+
// Resolved first so the progress reporter (#313) and the report-output
|
|
729
|
+
// language (#316) share the same locale.
|
|
730
|
+
let configLocale;
|
|
731
|
+
try {
|
|
732
|
+
configLocale = (await loadRadarConfig(cwd)).locale;
|
|
733
|
+
}
|
|
734
|
+
catch {
|
|
735
|
+
configLocale = undefined;
|
|
736
|
+
}
|
|
737
|
+
let langRest;
|
|
738
|
+
let locale;
|
|
739
|
+
try {
|
|
740
|
+
const resolved = resolveCommandLocale(progressState.rest, configLocale, { warn });
|
|
741
|
+
langRest = resolved.rest;
|
|
742
|
+
locale = resolved.locale;
|
|
743
|
+
}
|
|
744
|
+
catch (e) {
|
|
745
|
+
if (e instanceof LangFlagError) {
|
|
746
|
+
error(`research: ${e.message}`);
|
|
747
|
+
return 2;
|
|
748
|
+
}
|
|
749
|
+
throw e;
|
|
750
|
+
}
|
|
751
|
+
// Tests inject a reporter directly; production constructs one from the flag
|
|
752
|
+
// state + resolved locale (so the reporter's phase labels are localized,
|
|
753
|
+
// #313). Either way the per-invocation state stays local — there is no shared
|
|
754
|
+
// global reporter, so concurrent CLI invocations are isolated.
|
|
755
|
+
const progress = options.progress ?? buildReporter({ level: progressState.level, locale });
|
|
756
|
+
// Translator for the user-facing progress phase labels (#313), bound to the
|
|
757
|
+
// resolved locale. Threaded into the phase-emitting helpers below so the
|
|
758
|
+
// milestone strings track the report-output language. Built independently of
|
|
759
|
+
// `progress` so a test-injected reporter (which has no bundled translator)
|
|
760
|
+
// still gets localized phase labels.
|
|
761
|
+
const t = createTranslator(locale);
|
|
750
762
|
let parsed;
|
|
751
763
|
try {
|
|
752
|
-
parsed = parseArgs(
|
|
764
|
+
parsed = parseArgs(langRest);
|
|
753
765
|
}
|
|
754
766
|
catch (e) {
|
|
755
767
|
error(`research: ${e instanceof Error ? e.message : String(e)}`);
|
|
756
768
|
return 2;
|
|
757
769
|
}
|
|
758
770
|
if (parsed.help) {
|
|
759
|
-
printHelp(log);
|
|
771
|
+
printHelp(t, log);
|
|
760
772
|
return 0;
|
|
761
773
|
}
|
|
762
774
|
// Host-agent commit (#254 / ADR-0019). Independent of agent / template /
|
|
@@ -765,64 +777,78 @@ export async function runResearch(args, options = {}) {
|
|
|
765
777
|
// <item-id> arguments.
|
|
766
778
|
if (parsed.commit !== undefined) {
|
|
767
779
|
if (parsed.batch) {
|
|
768
|
-
error("research
|
|
780
|
+
error(t("cli.research.commitIncompatibleBatch"));
|
|
769
781
|
return 2;
|
|
770
782
|
}
|
|
771
783
|
if (parsed.digest) {
|
|
772
|
-
error("research
|
|
784
|
+
error(t("cli.research.commitIncompatibleDigest"));
|
|
773
785
|
return 2;
|
|
774
786
|
}
|
|
775
787
|
if (parsed.emitPayload) {
|
|
776
|
-
error("research
|
|
788
|
+
error(t("cli.research.commitIncompatibleEmitPayload"));
|
|
777
789
|
return 2;
|
|
778
790
|
}
|
|
779
791
|
if (parsed.triageGroup !== undefined) {
|
|
780
|
-
error("research
|
|
792
|
+
error(t("cli.research.commitIncompatibleTriageGroup"));
|
|
781
793
|
return 2;
|
|
782
794
|
}
|
|
783
795
|
if (parsed.itemIds.length > 0) {
|
|
784
|
-
error(
|
|
796
|
+
error(t("cli.research.commitTakesPath", {
|
|
797
|
+
count: parsed.itemIds.length,
|
|
798
|
+
ids: parsed.itemIds.join(", "),
|
|
799
|
+
}));
|
|
785
800
|
return 2;
|
|
786
801
|
}
|
|
787
|
-
return runResearchCommit({
|
|
802
|
+
return runResearchCommit({
|
|
803
|
+
cwd,
|
|
804
|
+
commitPath: parsed.commit,
|
|
805
|
+
log,
|
|
806
|
+
warn,
|
|
807
|
+
error,
|
|
808
|
+
progress,
|
|
809
|
+
t,
|
|
810
|
+
});
|
|
788
811
|
}
|
|
789
812
|
if (parsed.emitPayload && parsed.batch) {
|
|
790
|
-
error("research
|
|
813
|
+
error(t("cli.research.emitPayloadIncompatibleBatch"));
|
|
791
814
|
return 2;
|
|
792
815
|
}
|
|
793
816
|
if (parsed.batch) {
|
|
794
|
-
return runResearchBatch(parsed, cwd, log, warn, error, progress);
|
|
817
|
+
return runResearchBatch(parsed, cwd, locale, log, warn, error, progress, t);
|
|
795
818
|
}
|
|
796
819
|
if (parsed.status !== undefined) {
|
|
797
|
-
error("research
|
|
820
|
+
error(t("cli.research.statusRequiresBatch"));
|
|
798
821
|
return 2;
|
|
799
822
|
}
|
|
800
823
|
if (parsed.maxItems !== undefined) {
|
|
801
|
-
error("research
|
|
824
|
+
error(t("cli.research.maxItemsRequiresBatch"));
|
|
802
825
|
return 2;
|
|
803
826
|
}
|
|
804
827
|
if (parsed.filterTags !== undefined) {
|
|
805
|
-
error("research
|
|
828
|
+
error(t("cli.research.filterTagsRequiresBatch"));
|
|
806
829
|
return 2;
|
|
807
830
|
}
|
|
808
831
|
if (parsed.triageGroup !== undefined && !parsed.digest) {
|
|
809
|
-
error("research
|
|
832
|
+
error(t("cli.research.triageGroupRequiresDigest"));
|
|
810
833
|
return 2;
|
|
811
834
|
}
|
|
812
835
|
if (parsed.itemIds.length === 0) {
|
|
813
|
-
error("research
|
|
814
|
-
printHelp(error);
|
|
836
|
+
error(t("cli.research.missingItemId"));
|
|
837
|
+
printHelp(t, error);
|
|
815
838
|
return 2;
|
|
816
839
|
}
|
|
817
840
|
if (!parsed.digest && parsed.itemIds.length > 1) {
|
|
818
|
-
error(
|
|
841
|
+
error(t("cli.research.multipleRequireDigest", {
|
|
842
|
+
count: parsed.itemIds.length,
|
|
843
|
+
ids: parsed.itemIds.join(", "),
|
|
844
|
+
}));
|
|
819
845
|
return 2;
|
|
820
846
|
}
|
|
821
847
|
if (parsed.digest && parsed.itemIds.length < 2) {
|
|
822
|
-
error(
|
|
848
|
+
error(t("cli.research.digestRequiresTwo", { count: parsed.itemIds.length }));
|
|
823
849
|
return 2;
|
|
824
850
|
}
|
|
825
|
-
const agentResult = await resolveAgent(cwd, parsed.agent, error);
|
|
851
|
+
const agentResult = await resolveAgent(cwd, parsed.agent, error, t);
|
|
826
852
|
if ("exitCode" in agentResult)
|
|
827
853
|
return agentResult.exitCode;
|
|
828
854
|
const agent = agentResult.agent;
|
|
@@ -831,20 +857,20 @@ export async function runResearch(args, options = {}) {
|
|
|
831
857
|
if (parsed.digest) {
|
|
832
858
|
const result = await findItems(cwd, parsed.itemIds);
|
|
833
859
|
if ("missing" in result) {
|
|
834
|
-
error(
|
|
860
|
+
error(t("cli.research.itemNotFound", { id: result.missing }));
|
|
835
861
|
return 1;
|
|
836
862
|
}
|
|
837
863
|
items = result.items;
|
|
838
864
|
const dismissed = items.filter((i) => i.status === "dismissed");
|
|
839
865
|
if (dismissed.length > 0) {
|
|
840
|
-
error(
|
|
866
|
+
error(t("cli.research.digestDismissed", { ids: dismissed.map((i) => i.id).join(", ") }));
|
|
841
867
|
return 1;
|
|
842
868
|
}
|
|
843
869
|
}
|
|
844
870
|
else {
|
|
845
871
|
const found = await findItem(cwd, parsed.itemIds[0]);
|
|
846
872
|
if (!found) {
|
|
847
|
-
error(
|
|
873
|
+
error(t("cli.research.itemNotFound", { id: parsed.itemIds[0] }));
|
|
848
874
|
return 1;
|
|
849
875
|
}
|
|
850
876
|
items = [found.item];
|
|
@@ -875,6 +901,7 @@ export async function runResearch(args, options = {}) {
|
|
|
875
901
|
warn,
|
|
876
902
|
error,
|
|
877
903
|
progress,
|
|
904
|
+
t,
|
|
878
905
|
});
|
|
879
906
|
}
|
|
880
907
|
return processResearchInvocation({
|
|
@@ -884,6 +911,7 @@ export async function runResearch(args, options = {}) {
|
|
|
884
911
|
agent,
|
|
885
912
|
templateId,
|
|
886
913
|
template,
|
|
914
|
+
locale,
|
|
887
915
|
now: new Date(),
|
|
888
916
|
triageGroup: parsed.triageGroup,
|
|
889
917
|
log,
|
|
@@ -895,6 +923,7 @@ export async function runResearch(args, options = {}) {
|
|
|
895
923
|
export const researchCommand = {
|
|
896
924
|
name: "research",
|
|
897
925
|
summary: "Generate Markdown research reports from items via an AI agent",
|
|
926
|
+
summaryKey: "cli.summary.research",
|
|
898
927
|
run: (args) => runResearch(args),
|
|
899
928
|
};
|
|
900
929
|
//# sourceMappingURL=research.js.map
|