@ozzylabs/feedradar 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +79 -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 +70 -1
- package/dist/cli/routine/generate-pipeline.d.ts.map +1 -1
- package/dist/cli/routine/generate-pipeline.js +273 -44
- package/dist/cli/routine/generate-pipeline.js.map +1 -1
- package/dist/cli/routine/generate-watch.d.ts +10 -1
- package/dist/cli/routine/generate-watch.d.ts.map +1 -1
- package/dist/cli/routine/generate-watch.js +49 -37
- 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 +206 -182
- package/dist/cli/source.js.map +1 -1
- 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/types.d.ts +9 -0
- package/dist/core/feeds/types.d.ts.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/watcher.d.ts +11 -0
- package/dist/core/watcher.d.ts.map +1 -1
- package/dist/core/watcher.js +21 -5
- 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 +993 -0
- package/dist/i18n/messages/en.d.ts.map +1 -0
- package/dist/i18n/messages/en.js +1096 -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 +970 -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/recipe.d.ts +1 -1
- package/dist/schemas/source.d.ts +3 -3
- 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 +30 -41
- 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 +211 -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/triage.js
CHANGED
|
@@ -10,10 +10,27 @@ import { createProgressReporter } from "../core/progress.js";
|
|
|
10
10
|
import { statusForTriageDecision } from "../core/transitions.js";
|
|
11
11
|
import { buildTriagePrompt, parseTriageResponse, TriageResponseParseError, triageItems, } from "../core/triage/index.js";
|
|
12
12
|
import { loadSources } from "../core/watcher.js";
|
|
13
|
+
import { createTranslator } from "../i18n/index.js";
|
|
13
14
|
import { TriageDecisionValueSchema } from "../schemas/item.js";
|
|
14
15
|
import { AgentIdSchema } from "../schemas/research.js";
|
|
15
16
|
import { SourceTriagePolicySchema } from "../schemas/source.js";
|
|
16
17
|
import { resolveCommitPathInside } from "./_commit-path.js";
|
|
18
|
+
import { LangFlagError, parseLangFlag, resolveWorkspaceLocale } from "./_locale.js";
|
|
19
|
+
/**
|
|
20
|
+
* Validation error thrown by the sync `parseTriageRunArgs` that carries a
|
|
21
|
+
* message *key* instead of pre-rendered English (#336). The parser runs before
|
|
22
|
+
* the locale is resolved, so it tags the error with a catalog key and the
|
|
23
|
+
* caller renders it once the translator is in scope. Raw-token errors (unknown
|
|
24
|
+
* option / unexpected argument) stay plain `Error`s and echo verbatim.
|
|
25
|
+
*/
|
|
26
|
+
class TriageArgError extends Error {
|
|
27
|
+
key;
|
|
28
|
+
constructor(key) {
|
|
29
|
+
super(key);
|
|
30
|
+
this.key = key;
|
|
31
|
+
this.name = "TriageArgError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
17
34
|
function splitCsv(value) {
|
|
18
35
|
return value
|
|
19
36
|
.split(",")
|
|
@@ -39,19 +56,19 @@ function parseTriageRunArgs(args) {
|
|
|
39
56
|
}
|
|
40
57
|
if (a === "--dry-run") {
|
|
41
58
|
if (out.mode)
|
|
42
|
-
throw new
|
|
59
|
+
throw new TriageArgError("cli.triage.modesExclusive");
|
|
43
60
|
out.mode = "dry-run";
|
|
44
61
|
continue;
|
|
45
62
|
}
|
|
46
63
|
if (a === "--apply") {
|
|
47
64
|
if (out.mode)
|
|
48
|
-
throw new
|
|
65
|
+
throw new TriageArgError("cli.triage.modesExclusive");
|
|
49
66
|
out.mode = "apply";
|
|
50
67
|
continue;
|
|
51
68
|
}
|
|
52
69
|
if (a === "--interactive") {
|
|
53
70
|
if (out.mode)
|
|
54
|
-
throw new
|
|
71
|
+
throw new TriageArgError("cli.triage.modesExclusive");
|
|
55
72
|
out.mode = "interactive";
|
|
56
73
|
continue;
|
|
57
74
|
}
|
|
@@ -119,7 +136,7 @@ function parseTriageRunArgs(args) {
|
|
|
119
136
|
throw new Error(`unexpected positional argument: ${a}`);
|
|
120
137
|
}
|
|
121
138
|
if (out.verbose && out.quiet) {
|
|
122
|
-
throw new
|
|
139
|
+
throw new TriageArgError("cli.triage.verboseQuietExclusive");
|
|
123
140
|
}
|
|
124
141
|
return out;
|
|
125
142
|
}
|
|
@@ -157,66 +174,14 @@ function parseTriageFeedbackArgs(args) {
|
|
|
157
174
|
}
|
|
158
175
|
return out;
|
|
159
176
|
}
|
|
160
|
-
function printRunHelp(log) {
|
|
161
|
-
log("
|
|
162
|
-
log(" radar triage --emit-payload [--source <id>] [options]");
|
|
163
|
-
log(" radar triage --commit <path>");
|
|
164
|
-
log("");
|
|
165
|
-
log("Classify `detected` items using the configured per-source triage policy.");
|
|
166
|
-
log("");
|
|
167
|
-
log("Modes (mutually exclusive; default: --dry-run):");
|
|
168
|
-
log(" --dry-run print proposed decisions to stdout (no disk writes)");
|
|
169
|
-
log(" --apply write decisions to items/<id>.yaml + transition status");
|
|
170
|
-
log(" --interactive --dry-run output → $EDITOR → confirm → apply");
|
|
171
|
-
log("");
|
|
172
|
-
log("Options:");
|
|
173
|
-
log(" --source <id> limit triage to a single source");
|
|
174
|
-
log(" --filter-tags <a,b> matchedKeywords allow-list (comma-separated)");
|
|
175
|
-
log(" --triage-agent <id> override policy.agent for this run");
|
|
176
|
-
log(" --policy <path> override per-source policy with a YAML file");
|
|
177
|
-
log(" --max-items N hard cap on items triaged in this run");
|
|
178
|
-
log(" --audit-log <path> append JSONL audit records of every triage call");
|
|
179
|
-
log(" --emit-payload Host-agent mode (ADR-0019): print the triage payload to");
|
|
180
|
-
log(" stdout and DO NOT spawn an agent. The interactive host");
|
|
181
|
-
log(" session classifies the items itself, writes a decisions");
|
|
182
|
-
log(" JSON, then finalizes with `radar triage --commit <path>`.");
|
|
183
|
-
log(" Requires a single source group: pass --source unless only");
|
|
184
|
-
log(" one source has detected items. Interactive/opt-in only —");
|
|
185
|
-
log(" CI/headless must use the default spawn path.");
|
|
186
|
-
log(" --commit <path> Host-agent mode (ADR-0019): validate a host-written");
|
|
187
|
-
log(" decisions JSON (under <cwd>/triage/) against the source's");
|
|
188
|
-
log(" policy + detected items and apply the status transitions.");
|
|
189
|
-
log(" -v, --verbose verbose progress output");
|
|
190
|
-
log(" -q, --quiet suppress progress output entirely");
|
|
191
|
-
log("");
|
|
192
|
-
log("Sources missing a `triagePolicy:` block are skipped with a warning. See");
|
|
193
|
-
log("ADR-0018 for the policy schema reference.");
|
|
177
|
+
function printRunHelp(t, log) {
|
|
178
|
+
log(t("cli.triage.runHelp"));
|
|
194
179
|
}
|
|
195
|
-
function printFeedbackHelp(log) {
|
|
196
|
-
log("
|
|
197
|
-
log("");
|
|
198
|
-
log("Record human feedback on a prior triage decision (ADR-0018 §W5).");
|
|
199
|
-
log("Feedback is appended to items/<id>.yaml > triage.feedback, used by");
|
|
200
|
-
log("`radar triage stats` (#242) for policy tuning.");
|
|
201
|
-
log("");
|
|
202
|
-
log("Options:");
|
|
203
|
-
log(" --correct mark the prior triage decision as correct");
|
|
204
|
-
log(" --wrong mark the prior triage decision as wrong");
|
|
205
|
-
log(" --reason <text> free-form rationale (recommended for --wrong)");
|
|
180
|
+
function printFeedbackHelp(t, log) {
|
|
181
|
+
log(t("cli.triage.feedbackHelp"));
|
|
206
182
|
}
|
|
207
|
-
function printTriageHelp(log) {
|
|
208
|
-
log("
|
|
209
|
-
log("");
|
|
210
|
-
log("Subcommands:");
|
|
211
|
-
log(" feedback <item-id> --correct | --wrong [--reason <text>]");
|
|
212
|
-
log(" stats [--since <duration>] [--source <id>] [--json]");
|
|
213
|
-
log("");
|
|
214
|
-
log("Run modes (when no subcommand given):");
|
|
215
|
-
log(" --dry-run print proposed decisions");
|
|
216
|
-
log(" --apply write decisions to items/<id>.yaml");
|
|
217
|
-
log(" --interactive edit decisions in $EDITOR before applying");
|
|
218
|
-
log("");
|
|
219
|
-
log("Run `radar triage --help` for the full option list.");
|
|
183
|
+
function printTriageHelp(t, log) {
|
|
184
|
+
log(t("cli.triage.help"));
|
|
220
185
|
}
|
|
221
186
|
async function pathExists(p) {
|
|
222
187
|
try {
|
|
@@ -376,16 +341,16 @@ async function promptConfirm(message) {
|
|
|
376
341
|
* resolved groups plus the full `detected` set (for the decision table) and the
|
|
377
342
|
* items dir.
|
|
378
343
|
*/
|
|
379
|
-
async function prepareTriageGroups(parsed, cwd, io) {
|
|
344
|
+
async function prepareTriageGroups(parsed, cwd, io, t) {
|
|
380
345
|
const { log, warn, error } = io;
|
|
381
346
|
const sourcesDir = join(cwd, "sources");
|
|
382
347
|
if (!(await pathExists(sourcesDir))) {
|
|
383
|
-
error("triage
|
|
348
|
+
error(t("cli.triage.noSourcesDir"));
|
|
384
349
|
return { exitCode: 1 };
|
|
385
350
|
}
|
|
386
351
|
const sources = await loadSources(sourcesDir, error);
|
|
387
352
|
if (sources.length === 0) {
|
|
388
|
-
log("triage
|
|
353
|
+
log(t("cli.triage.noSourcesDefined"));
|
|
389
354
|
return { exitCode: 0 };
|
|
390
355
|
}
|
|
391
356
|
let policyOverride = null;
|
|
@@ -396,7 +361,7 @@ async function prepareTriageGroups(parsed, cwd, io) {
|
|
|
396
361
|
}
|
|
397
362
|
const itemsDir = join(cwd, "items");
|
|
398
363
|
if (!(await pathExists(itemsDir))) {
|
|
399
|
-
log("triage
|
|
364
|
+
log(t("cli.triage.noItemsDir"));
|
|
400
365
|
return { exitCode: 0 };
|
|
401
366
|
}
|
|
402
367
|
let allItems;
|
|
@@ -415,11 +380,11 @@ async function prepareTriageGroups(parsed, cwd, io) {
|
|
|
415
380
|
detected = detected.filter((i) => i.matchedKeywords.some((k) => tags.has(k)));
|
|
416
381
|
}
|
|
417
382
|
if (detected.length === 0) {
|
|
418
|
-
log("triage
|
|
383
|
+
log(t("cli.triage.noDetectedMatch"));
|
|
419
384
|
return { exitCode: 0 };
|
|
420
385
|
}
|
|
421
386
|
if (parsed.maxItems !== undefined && detected.length > parsed.maxItems) {
|
|
422
|
-
warn(
|
|
387
|
+
warn(t("cli.triage.maxItemsExceeded", { detected: detected.length, maxItems: parsed.maxItems }));
|
|
423
388
|
detected = detected.slice(0, parsed.maxItems);
|
|
424
389
|
}
|
|
425
390
|
const sourcesById = new Map(sources.map((s) => [s.id, s]));
|
|
@@ -436,7 +401,7 @@ async function prepareTriageGroups(parsed, cwd, io) {
|
|
|
436
401
|
const source = sourcesById.get(sourceId);
|
|
437
402
|
const policy = policyOverride ?? source?.triagePolicy;
|
|
438
403
|
if (!policy) {
|
|
439
|
-
warn(
|
|
404
|
+
warn(t("cli.triage.skippingNoPolicy", { count: groupItems.length, sourceId }));
|
|
440
405
|
continue;
|
|
441
406
|
}
|
|
442
407
|
const triageAgent = parsed.triageAgent ?? policy.agent;
|
|
@@ -444,7 +409,7 @@ async function prepareTriageGroups(parsed, cwd, io) {
|
|
|
444
409
|
// agent call (or emitting a payload) — typos like `gemnini-cli` fail fast.
|
|
445
410
|
const validated = AgentIdSchema.safeParse(triageAgent);
|
|
446
411
|
if (!validated.success) {
|
|
447
|
-
error(
|
|
412
|
+
error(t("cli.triage.invalidTriageAgent", { agent: triageAgent }));
|
|
448
413
|
return { exitCode: 2 };
|
|
449
414
|
}
|
|
450
415
|
groups.push({ sourceId, items: groupItems, policy, triageAgent });
|
|
@@ -487,20 +452,21 @@ const TriageDecisionsFileSchema = z.object({
|
|
|
487
452
|
* detected items the user must narrow with `--source` (mirrors the ADR-0020
|
|
488
453
|
* "one item set at a time" host-mode posture).
|
|
489
454
|
*/
|
|
490
|
-
async function runTriageEmitPayload(parsed, cwd, io) {
|
|
455
|
+
async function runTriageEmitPayload(parsed, cwd, io, t) {
|
|
491
456
|
const { log, error } = io;
|
|
492
|
-
const prepared = await prepareTriageGroups(parsed, cwd, io);
|
|
457
|
+
const prepared = await prepareTriageGroups(parsed, cwd, io, t);
|
|
493
458
|
if ("exitCode" in prepared)
|
|
494
459
|
return prepared.exitCode;
|
|
495
460
|
const { groups } = prepared;
|
|
496
461
|
if (groups.length === 0) {
|
|
497
|
-
log("triage
|
|
462
|
+
log(t("cli.triage.noItemsTriaged"));
|
|
498
463
|
return 0;
|
|
499
464
|
}
|
|
500
465
|
if (groups.length > 1) {
|
|
501
|
-
error(
|
|
502
|
-
|
|
503
|
-
.join(", ")
|
|
466
|
+
error(t("cli.triage.emitPayloadSingleSource", {
|
|
467
|
+
count: groups.length,
|
|
468
|
+
sources: groups.map((g) => g.sourceId).join(", "),
|
|
469
|
+
}));
|
|
504
470
|
return 2;
|
|
505
471
|
}
|
|
506
472
|
const group = groups[0];
|
|
@@ -528,7 +494,7 @@ async function runTriageEmitPayload(parsed, cwd, io) {
|
|
|
528
494
|
* host misled by injected content into committing an arbitrary path is rejected
|
|
529
495
|
* at the CLI boundary.
|
|
530
496
|
*/
|
|
531
|
-
async function runTriageCommit(parsed, commitPath, cwd, options, io) {
|
|
497
|
+
async function runTriageCommit(parsed, commitPath, cwd, options, io, t) {
|
|
532
498
|
const { log, warn, error } = io;
|
|
533
499
|
const now = options.now ?? defaultNow;
|
|
534
500
|
const guard = await resolveCommitPathInside(cwd, "triage", commitPath);
|
|
@@ -538,7 +504,7 @@ async function runTriageCommit(parsed, commitPath, cwd, options, io) {
|
|
|
538
504
|
}
|
|
539
505
|
const resolved = guard.resolved;
|
|
540
506
|
if (!(await pathExists(resolved))) {
|
|
541
|
-
error(
|
|
507
|
+
error(t("cli.triage.decisionsFileNotFound", { path: resolved }));
|
|
542
508
|
return 1;
|
|
543
509
|
}
|
|
544
510
|
let raw;
|
|
@@ -570,7 +536,7 @@ async function runTriageCommit(parsed, commitPath, cwd, options, io) {
|
|
|
570
536
|
// `triage.agent`). Reject upfront rather than persisting a bogus origin.
|
|
571
537
|
const agentValid = AgentIdSchema.safeParse(file.agent);
|
|
572
538
|
if (!agentValid.success) {
|
|
573
|
-
error(
|
|
539
|
+
error(t("cli.triage.invalidDecisionsAgent", { agent: file.agent }));
|
|
574
540
|
return 1;
|
|
575
541
|
}
|
|
576
542
|
const triageAgent = file.agent;
|
|
@@ -586,7 +552,7 @@ async function runTriageCommit(parsed, commitPath, cwd, options, io) {
|
|
|
586
552
|
}
|
|
587
553
|
const sourcesDir = join(cwd, "sources");
|
|
588
554
|
if (!policyOverride && !(await pathExists(sourcesDir))) {
|
|
589
|
-
error("triage
|
|
555
|
+
error(t("cli.triage.noSourcesDir"));
|
|
590
556
|
return 1;
|
|
591
557
|
}
|
|
592
558
|
let policy = policyOverride;
|
|
@@ -594,11 +560,11 @@ async function runTriageCommit(parsed, commitPath, cwd, options, io) {
|
|
|
594
560
|
const sources = await loadSources(sourcesDir, error);
|
|
595
561
|
const source = sources.find((s) => s.id === file.sourceId);
|
|
596
562
|
if (!source) {
|
|
597
|
-
error(
|
|
563
|
+
error(t("cli.triage.unknownSource", { sourceId: file.sourceId }));
|
|
598
564
|
return 1;
|
|
599
565
|
}
|
|
600
566
|
if (!source.triagePolicy) {
|
|
601
|
-
error(
|
|
567
|
+
error(t("cli.triage.sourceNoPolicy", { sourceId: file.sourceId }));
|
|
602
568
|
return 1;
|
|
603
569
|
}
|
|
604
570
|
policy = source.triagePolicy;
|
|
@@ -610,7 +576,7 @@ async function runTriageCommit(parsed, commitPath, cwd, options, io) {
|
|
|
610
576
|
// spawn path's behavior.
|
|
611
577
|
const itemsDir = join(cwd, "items");
|
|
612
578
|
if (!(await pathExists(itemsDir))) {
|
|
613
|
-
error("triage
|
|
579
|
+
error(t("cli.triage.noItemsDirCommit"));
|
|
614
580
|
return 1;
|
|
615
581
|
}
|
|
616
582
|
let allItems;
|
|
@@ -623,7 +589,7 @@ async function runTriageCommit(parsed, commitPath, cwd, options, io) {
|
|
|
623
589
|
}
|
|
624
590
|
const detected = allItems.filter((i) => i.status === "detected");
|
|
625
591
|
if (detected.length === 0) {
|
|
626
|
-
error(
|
|
592
|
+
error(t("cli.triage.noDetectedForSource", { sourceId: file.sourceId }));
|
|
627
593
|
return 1;
|
|
628
594
|
}
|
|
629
595
|
// Re-validate through the SAME parser the spawn path runs. We feed the raw
|
|
@@ -682,7 +648,7 @@ async function runTriageCommit(parsed, commitPath, cwd, options, io) {
|
|
|
682
648
|
error(`triage: failed to write items: ${e instanceof Error ? e.message : String(e)}`);
|
|
683
649
|
return 1;
|
|
684
650
|
}
|
|
685
|
-
log(
|
|
651
|
+
log(t("cli.triage.committed", { count: updated.length, sourceId: file.sourceId }));
|
|
686
652
|
for (const row of formatDecisionTable(detected, decisions))
|
|
687
653
|
log(row);
|
|
688
654
|
return 0;
|
|
@@ -692,6 +658,7 @@ async function runTriageCommit(parsed, commitPath, cwd, options, io) {
|
|
|
692
658
|
* when the first positional is `feedback`, otherwise runs the triage flow.
|
|
693
659
|
*/
|
|
694
660
|
export async function runTriage(args, options = {}) {
|
|
661
|
+
const cwd = options.cwd ?? process.cwd();
|
|
695
662
|
const log = options.io?.log ?? ((m) => console.log(m));
|
|
696
663
|
const warn = options.io?.warn ?? ((m) => console.warn(m));
|
|
697
664
|
const error = options.io?.error ?? ((m) => console.error(m));
|
|
@@ -703,7 +670,18 @@ export async function runTriage(args, options = {}) {
|
|
|
703
670
|
return runTriageStats(rest, options, { log, warn, error });
|
|
704
671
|
}
|
|
705
672
|
if (first === "help") {
|
|
706
|
-
|
|
673
|
+
// `help` subcommand: resolve a translator for the localized overview. A
|
|
674
|
+
// leading `--lang` is read-only here (no further args to forward).
|
|
675
|
+
const langFlag = (() => {
|
|
676
|
+
try {
|
|
677
|
+
return parseLangFlag(args).flag;
|
|
678
|
+
}
|
|
679
|
+
catch {
|
|
680
|
+
return undefined;
|
|
681
|
+
}
|
|
682
|
+
})();
|
|
683
|
+
const locale = await resolveWorkspaceLocale({ flag: langFlag, cwd, warn: error });
|
|
684
|
+
printTriageHelp(createTranslator(locale), log);
|
|
707
685
|
return 0;
|
|
708
686
|
}
|
|
709
687
|
// `--help` / `-h` flow through to the run-mode parser so the user sees
|
|
@@ -723,16 +701,34 @@ async function runTriageRun(args, options, io) {
|
|
|
723
701
|
const { log, warn, error } = io;
|
|
724
702
|
const cwd = options.cwd ?? process.cwd();
|
|
725
703
|
const now = options.now ?? defaultNow;
|
|
704
|
+
// Strip `--lang <en|ja>` before `parseTriageRunArgs` (which rejects unknown
|
|
705
|
+
// flags), then resolve the UI locale for the help text.
|
|
706
|
+
let langState;
|
|
707
|
+
try {
|
|
708
|
+
langState = parseLangFlag(args);
|
|
709
|
+
}
|
|
710
|
+
catch (e) {
|
|
711
|
+
if (e instanceof LangFlagError) {
|
|
712
|
+
error(`triage: ${e.message}`);
|
|
713
|
+
return 2;
|
|
714
|
+
}
|
|
715
|
+
throw e;
|
|
716
|
+
}
|
|
717
|
+
const locale = await resolveWorkspaceLocale({ flag: langState.flag, cwd, warn: error });
|
|
718
|
+
const t = createTranslator(locale);
|
|
726
719
|
let parsed;
|
|
727
720
|
try {
|
|
728
|
-
parsed = parseTriageRunArgs(
|
|
721
|
+
parsed = parseTriageRunArgs(langState.rest);
|
|
729
722
|
}
|
|
730
723
|
catch (e) {
|
|
731
|
-
|
|
724
|
+
// Keyed validation errors are translated now that the locale is resolved;
|
|
725
|
+
// raw-token errors (unknown option / unexpected argument) echo verbatim.
|
|
726
|
+
const message = e instanceof TriageArgError ? t(e.key) : e instanceof Error ? e.message : String(e);
|
|
727
|
+
error(`triage: ${message}`);
|
|
732
728
|
return 2;
|
|
733
729
|
}
|
|
734
730
|
if (parsed.help) {
|
|
735
|
-
printRunHelp(log);
|
|
731
|
+
printRunHelp(t, log);
|
|
736
732
|
return 0;
|
|
737
733
|
}
|
|
738
734
|
// Host-agent commit (#279 / ADR-0019). Independent of the run modes: it takes
|
|
@@ -740,23 +736,23 @@ async function runTriageRun(args, options, io) {
|
|
|
740
736
|
// it must not be confused with `--dry-run` / `--apply` / `--interactive`.
|
|
741
737
|
if (parsed.commit !== undefined) {
|
|
742
738
|
if (parsed.mode) {
|
|
743
|
-
error("triage
|
|
739
|
+
error(t("cli.triage.commitIncompatibleModes"));
|
|
744
740
|
return 2;
|
|
745
741
|
}
|
|
746
742
|
if (parsed.emitPayload) {
|
|
747
|
-
error("triage
|
|
743
|
+
error(t("cli.triage.commitIncompatibleEmitPayload"));
|
|
748
744
|
return 2;
|
|
749
745
|
}
|
|
750
|
-
return runTriageCommit(parsed, parsed.commit, cwd, options, io);
|
|
746
|
+
return runTriageCommit(parsed, parsed.commit, cwd, options, io, t);
|
|
751
747
|
}
|
|
752
748
|
// Host-agent emit (#279 / ADR-0019). Mutually exclusive with the apply / dry
|
|
753
749
|
// / interactive run modes — it prints a payload instead of running an agent.
|
|
754
750
|
if (parsed.emitPayload) {
|
|
755
751
|
if (parsed.mode) {
|
|
756
|
-
error("triage
|
|
752
|
+
error(t("cli.triage.emitPayloadIncompatibleModes"));
|
|
757
753
|
return 2;
|
|
758
754
|
}
|
|
759
|
-
return runTriageEmitPayload(parsed, cwd, io);
|
|
755
|
+
return runTriageEmitPayload(parsed, cwd, io, t);
|
|
760
756
|
}
|
|
761
757
|
const mode = parsed.mode ?? "dry-run";
|
|
762
758
|
// Progress reporter (#197 / ADR-0015). The triage CLI uses the spinner
|
|
@@ -768,7 +764,7 @@ async function runTriageRun(args, options, io) {
|
|
|
768
764
|
const reporter = createProgressReporter({ level });
|
|
769
765
|
// Shared PRE block: load + filter + group detected items and resolve each
|
|
770
766
|
// group's policy / agent (also used by `--emit-payload`).
|
|
771
|
-
const prepared = await prepareTriageGroups(parsed, cwd, io);
|
|
767
|
+
const prepared = await prepareTriageGroups(parsed, cwd, io, t);
|
|
772
768
|
if ("exitCode" in prepared)
|
|
773
769
|
return prepared.exitCode;
|
|
774
770
|
const { groups, detected, itemsDir } = prepared;
|
|
@@ -779,7 +775,11 @@ async function runTriageRun(args, options, io) {
|
|
|
779
775
|
const allDecisions = new Map();
|
|
780
776
|
const allErrors = [];
|
|
781
777
|
for (const { sourceId, items: groupItems, policy, triageAgent } of groups) {
|
|
782
|
-
reporter.phase(
|
|
778
|
+
reporter.phase(t("cli.triage.progressTriaging", {
|
|
779
|
+
count: groupItems.length,
|
|
780
|
+
sourceId,
|
|
781
|
+
agent: triageAgent,
|
|
782
|
+
}));
|
|
783
783
|
let result;
|
|
784
784
|
try {
|
|
785
785
|
result = await triageItems(groupItems, {
|
|
@@ -807,13 +807,13 @@ async function runTriageRun(args, options, io) {
|
|
|
807
807
|
warn(`triage: ${e}`);
|
|
808
808
|
}
|
|
809
809
|
if (allDecisions.size === 0) {
|
|
810
|
-
log("triage
|
|
810
|
+
log(t("cli.triage.noItemsTriaged"));
|
|
811
811
|
return 0;
|
|
812
812
|
}
|
|
813
813
|
// Render the decision table. Used by every mode.
|
|
814
814
|
const rows = formatDecisionTable(detected, allDecisions);
|
|
815
815
|
if (mode === "dry-run") {
|
|
816
|
-
log("triage
|
|
816
|
+
log(t("cli.triage.dryRunNoChanges"));
|
|
817
817
|
for (const row of rows)
|
|
818
818
|
log(row);
|
|
819
819
|
return 0;
|
|
@@ -841,9 +841,9 @@ async function runTriageRun(args, options, io) {
|
|
|
841
841
|
return 1;
|
|
842
842
|
}
|
|
843
843
|
const confirm = options.confirm ?? promptConfirm;
|
|
844
|
-
const confirmed = await confirm("
|
|
844
|
+
const confirmed = await confirm(t("cli.triage.confirmApply"));
|
|
845
845
|
if (!confirmed) {
|
|
846
|
-
log("triage
|
|
846
|
+
log(t("cli.triage.abortedByUser"));
|
|
847
847
|
return 0;
|
|
848
848
|
}
|
|
849
849
|
}
|
|
@@ -855,7 +855,7 @@ async function runTriageRun(args, options, io) {
|
|
|
855
855
|
error(`triage: failed to write items: ${e instanceof Error ? e.message : String(e)}`);
|
|
856
856
|
return 1;
|
|
857
857
|
}
|
|
858
|
-
log(
|
|
858
|
+
log(t("cli.triage.applied", { count: allUpdated.length }));
|
|
859
859
|
for (const row of rows)
|
|
860
860
|
log(row);
|
|
861
861
|
return 0;
|
|
@@ -874,34 +874,47 @@ async function runTriageFeedback(args, options) {
|
|
|
874
874
|
const log = options.io?.log ?? ((m) => console.log(m));
|
|
875
875
|
const error = options.io?.error ?? ((m) => console.error(m));
|
|
876
876
|
const now = options.now ?? defaultNow;
|
|
877
|
+
let langState;
|
|
878
|
+
try {
|
|
879
|
+
langState = parseLangFlag(args);
|
|
880
|
+
}
|
|
881
|
+
catch (e) {
|
|
882
|
+
if (e instanceof LangFlagError) {
|
|
883
|
+
error(`triage feedback: ${e.message}`);
|
|
884
|
+
return 2;
|
|
885
|
+
}
|
|
886
|
+
throw e;
|
|
887
|
+
}
|
|
888
|
+
const locale = await resolveWorkspaceLocale({ flag: langState.flag, cwd, warn: error });
|
|
889
|
+
const t = createTranslator(locale);
|
|
877
890
|
let parsed;
|
|
878
891
|
try {
|
|
879
|
-
parsed = parseTriageFeedbackArgs(
|
|
892
|
+
parsed = parseTriageFeedbackArgs(langState.rest);
|
|
880
893
|
}
|
|
881
894
|
catch (e) {
|
|
882
895
|
error(`triage feedback: ${e instanceof Error ? e.message : String(e)}`);
|
|
883
896
|
return 2;
|
|
884
897
|
}
|
|
885
898
|
if (parsed.help) {
|
|
886
|
-
printFeedbackHelp(log);
|
|
899
|
+
printFeedbackHelp(t, log);
|
|
887
900
|
return 0;
|
|
888
901
|
}
|
|
889
902
|
if (!parsed.itemId) {
|
|
890
|
-
error("triage
|
|
891
|
-
printFeedbackHelp(error);
|
|
903
|
+
error(t("cli.triage.feedbackMissingItemId"));
|
|
904
|
+
printFeedbackHelp(t, error);
|
|
892
905
|
return 2;
|
|
893
906
|
}
|
|
894
907
|
if (parsed.correct && parsed.wrong) {
|
|
895
|
-
error("triage
|
|
908
|
+
error(t("cli.triage.feedbackModesExclusive"));
|
|
896
909
|
return 2;
|
|
897
910
|
}
|
|
898
911
|
if (!parsed.correct && !parsed.wrong) {
|
|
899
|
-
error("triage
|
|
912
|
+
error(t("cli.triage.feedbackModeRequired"));
|
|
900
913
|
return 2;
|
|
901
914
|
}
|
|
902
915
|
const itemsDir = join(cwd, "items");
|
|
903
916
|
if (!(await pathExists(itemsDir))) {
|
|
904
|
-
error(
|
|
917
|
+
error(t("cli.triage.feedbackItemsDirNotFound"));
|
|
905
918
|
return 1;
|
|
906
919
|
}
|
|
907
920
|
let items;
|
|
@@ -914,11 +927,11 @@ async function runTriageFeedback(args, options) {
|
|
|
914
927
|
}
|
|
915
928
|
const item = items.find((i) => i.id === parsed.itemId);
|
|
916
929
|
if (!item) {
|
|
917
|
-
error(
|
|
930
|
+
error(t("cli.triage.feedbackItemNotFound", { id: parsed.itemId }));
|
|
918
931
|
return 1;
|
|
919
932
|
}
|
|
920
933
|
if (!item.triage) {
|
|
921
|
-
error(
|
|
934
|
+
error(t("cli.triage.feedbackNoPriorDecision", { id: item.id }));
|
|
922
935
|
return 1;
|
|
923
936
|
}
|
|
924
937
|
// Overwrite-semantic: replace the existing feedback array with a single
|
|
@@ -947,7 +960,7 @@ async function runTriageFeedback(args, options) {
|
|
|
947
960
|
return 1;
|
|
948
961
|
}
|
|
949
962
|
const verdict = parsed.correct ? "correct" : "wrong";
|
|
950
|
-
log(
|
|
963
|
+
log(t("cli.triage.feedbackRecorded", { sourceId: item.sourceId, id: item.id, verdict }));
|
|
951
964
|
return 0;
|
|
952
965
|
}
|
|
953
966
|
function parseTriageStatsArgs(args) {
|
|
@@ -983,19 +996,8 @@ function parseTriageStatsArgs(args) {
|
|
|
983
996
|
}
|
|
984
997
|
return out;
|
|
985
998
|
}
|
|
986
|
-
function printStatsHelp(log) {
|
|
987
|
-
log("
|
|
988
|
-
log("");
|
|
989
|
-
log("Aggregate triage decisions and human feedback (ADR-0018 §W5, #242).");
|
|
990
|
-
log("Use after running `radar triage --apply` for some weeks; the output");
|
|
991
|
-
log("highlights precision / recall drift and suggests `triagePolicy.rules:`");
|
|
992
|
-
log("tweaks. See docs/user-guide.md `policy tuning workflow` for the");
|
|
993
|
-
log("recommended monthly loop.");
|
|
994
|
-
log("");
|
|
995
|
-
log("Options:");
|
|
996
|
-
log(" --since <duration> only count items triaged within the cutoff (e.g. 30d, 24h)");
|
|
997
|
-
log(" --source <id> limit stats to a single source (default: all sources)");
|
|
998
|
-
log(" --json emit machine-readable JSON instead of the text report");
|
|
999
|
+
function printStatsHelp(t, log) {
|
|
1000
|
+
log(t("cli.triage.statsHelp"));
|
|
999
1001
|
}
|
|
1000
1002
|
/**
|
|
1001
1003
|
* Parse `Nd | Nh | Nm | Ns` into a `Date` cutoff. Returns `null` when the
|
|
@@ -1298,23 +1300,36 @@ async function runTriageStats(args, options, io) {
|
|
|
1298
1300
|
const cwd = options.cwd ?? process.cwd();
|
|
1299
1301
|
const nowFn = options.now ?? defaultNow;
|
|
1300
1302
|
const now = new Date(nowFn());
|
|
1303
|
+
let langState;
|
|
1304
|
+
try {
|
|
1305
|
+
langState = parseLangFlag(args);
|
|
1306
|
+
}
|
|
1307
|
+
catch (e) {
|
|
1308
|
+
if (e instanceof LangFlagError) {
|
|
1309
|
+
error(`triage stats: ${e.message}`);
|
|
1310
|
+
return 2;
|
|
1311
|
+
}
|
|
1312
|
+
throw e;
|
|
1313
|
+
}
|
|
1314
|
+
const locale = await resolveWorkspaceLocale({ flag: langState.flag, cwd, warn: error });
|
|
1315
|
+
const t = createTranslator(locale);
|
|
1301
1316
|
let parsed;
|
|
1302
1317
|
try {
|
|
1303
|
-
parsed = parseTriageStatsArgs(
|
|
1318
|
+
parsed = parseTriageStatsArgs(langState.rest);
|
|
1304
1319
|
}
|
|
1305
1320
|
catch (e) {
|
|
1306
1321
|
error(`triage stats: ${e instanceof Error ? e.message : String(e)}`);
|
|
1307
1322
|
return 2;
|
|
1308
1323
|
}
|
|
1309
1324
|
if (parsed.help) {
|
|
1310
|
-
printStatsHelp(log);
|
|
1325
|
+
printStatsHelp(t, log);
|
|
1311
1326
|
return 0;
|
|
1312
1327
|
}
|
|
1313
1328
|
let sinceCutoff = null;
|
|
1314
1329
|
if (parsed.since) {
|
|
1315
1330
|
sinceCutoff = parseSinceCutoffForStats(parsed.since, now);
|
|
1316
1331
|
if (!sinceCutoff) {
|
|
1317
|
-
error(
|
|
1332
|
+
error(t("cli.triage.statsInvalidSince", { since: parsed.since }));
|
|
1318
1333
|
return 2;
|
|
1319
1334
|
}
|
|
1320
1335
|
}
|
|
@@ -1328,7 +1343,7 @@ async function runTriageStats(args, options, io) {
|
|
|
1328
1343
|
}, null, 2));
|
|
1329
1344
|
}
|
|
1330
1345
|
else {
|
|
1331
|
-
log("triage
|
|
1346
|
+
log(t("cli.triage.statsNoItemsDir"));
|
|
1332
1347
|
}
|
|
1333
1348
|
return 0;
|
|
1334
1349
|
}
|
|
@@ -1382,7 +1397,7 @@ async function runTriageStats(args, options, io) {
|
|
|
1382
1397
|
return 0;
|
|
1383
1398
|
}
|
|
1384
1399
|
if (perSource.length === 0) {
|
|
1385
|
-
log("triage
|
|
1400
|
+
log(t("cli.triage.statsNoMatch"));
|
|
1386
1401
|
return 0;
|
|
1387
1402
|
}
|
|
1388
1403
|
let first = true;
|
|
@@ -1409,7 +1424,8 @@ export const __test__ = {
|
|
|
1409
1424
|
};
|
|
1410
1425
|
export const triageCommand = {
|
|
1411
1426
|
name: "triage",
|
|
1412
|
-
summary: "LLM-based triage of detected items
|
|
1427
|
+
summary: "LLM-based triage of detected items",
|
|
1428
|
+
summaryKey: "cli.summary.triage",
|
|
1413
1429
|
run: (args) => runTriage(args),
|
|
1414
1430
|
};
|
|
1415
1431
|
//# sourceMappingURL=triage.js.map
|