@basou/core 0.32.0 → 0.34.0

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/dist/index.js CHANGED
@@ -1080,7 +1080,7 @@ function isLazyExpired(approval, now) {
1080
1080
  }
1081
1081
 
1082
1082
  // src/decisions/decisions-renderer.ts
1083
- import { lstat } from "fs/promises";
1083
+ import { lstat as lstat2 } from "fs/promises";
1084
1084
  import { dirname as dirname2, join as join6, resolve } from "path";
1085
1085
 
1086
1086
  // src/events/event-replay.ts
@@ -1367,6 +1367,598 @@ async function readAllEvents(sessionDir, options = {}) {
1367
1367
  return out;
1368
1368
  }
1369
1369
 
1370
+ // src/project/relative-path.ts
1371
+ function normalizeRelativePath(p) {
1372
+ const trimmed = p.trim();
1373
+ const absolute = trimmed.startsWith("/");
1374
+ const out = [];
1375
+ for (const seg of trimmed.split("/")) {
1376
+ if (seg === "" || seg === ".") continue;
1377
+ if (seg === "..") {
1378
+ const top = out[out.length - 1];
1379
+ if (top !== void 0 && top !== "..") {
1380
+ out.pop();
1381
+ } else if (!absolute) {
1382
+ out.push("..");
1383
+ }
1384
+ continue;
1385
+ }
1386
+ out.push(seg);
1387
+ }
1388
+ const joined = out.join("/");
1389
+ if (absolute) return `/${joined}`;
1390
+ return joined.length === 0 ? "." : joined;
1391
+ }
1392
+
1393
+ // src/storage/manifest.ts
1394
+ import { lstat } from "fs/promises";
1395
+
1396
+ // src/schemas/manifest.schema.ts
1397
+ import { z as z4 } from "zod";
1398
+ var ProjectSchema = z4.looseObject({
1399
+ name: z4.string().optional(),
1400
+ description: z4.string().optional()
1401
+ });
1402
+ var CapabilitiesSchema = z4.looseObject({
1403
+ enabled: z4.array(z4.string())
1404
+ });
1405
+ var ApprovalConfigSchema = z4.looseObject({
1406
+ required_for: z4.array(z4.string()).optional(),
1407
+ default_risk_level: z4.enum(["low", "medium", "high", "critical"])
1408
+ });
1409
+ var ClaudeCodeAdapterConfigSchema = z4.looseObject({
1410
+ enabled: z4.boolean(),
1411
+ config_path: z4.string().optional()
1412
+ });
1413
+ var AdaptersSchema = z4.looseObject({
1414
+ "claude-code": ClaudeCodeAdapterConfigSchema
1415
+ });
1416
+ var GitConfigSchema = z4.looseObject({
1417
+ events_log: z4.enum(["ignore", "commit"]).default("ignore")
1418
+ });
1419
+ var SOURCE_ROOT_PATTERN = /^(?![~/\\])(?![A-Za-z]:)(?!\s)[^\0\\]*[^\0\\\s]$/;
1420
+ var SourceRootSchema = z4.string().min(1).regex(SOURCE_ROOT_PATTERN, {
1421
+ message: "source_roots entries must be relative paths (no absolute path, '~', '\\', or null byte)"
1422
+ });
1423
+ var ImportConfigSchema = z4.looseObject({
1424
+ source_roots: z4.array(SourceRootSchema).min(1).optional()
1425
+ });
1426
+ var RepoVisibilitySchema = z4.enum(["public", "private", "future-public"]);
1427
+ var RepoLanguageSchema = z4.enum(["en", "ja", "en+ja"]);
1428
+ var PublishKindSchema = z4.enum(["web", "npm"]);
1429
+ var RepoInstructionsSchema = z4.enum(["hub", "self"]);
1430
+ var PublishTargetSchema = z4.looseObject({
1431
+ kind: PublishKindSchema,
1432
+ visibility: RepoVisibilitySchema.optional(),
1433
+ language: RepoLanguageSchema.optional()
1434
+ });
1435
+ var RepoEntrySchema = z4.looseObject({
1436
+ path: SourceRootSchema,
1437
+ visibility: RepoVisibilitySchema.optional(),
1438
+ language: RepoLanguageSchema.optional(),
1439
+ publishes: z4.array(PublishTargetSchema).optional(),
1440
+ instructions: RepoInstructionsSchema.optional()
1441
+ });
1442
+ var WorkspaceMetaSchema = z4.looseObject({
1443
+ id: WorkspaceIdSchema,
1444
+ name: z4.string().min(1),
1445
+ created_at: IsoTimestampSchema,
1446
+ updated_at: IsoTimestampSchema,
1447
+ /**
1448
+ * The generated workspace view: a throwaway directory that aggregates the
1449
+ * roster repos via symlinks (one `<repo-basename>` symlink per repo). A path
1450
+ * relative to the manifest root, reusing the machine-portable source-root
1451
+ * constraint. Absent for a solo project (no view needed); `basou project
1452
+ * workspace` reconciles the view's symlinks to the declared roster.
1453
+ */
1454
+ view: SourceRootSchema.optional()
1455
+ });
1456
+ var ManifestSchema = z4.looseObject({
1457
+ schema_version: SchemaVersionSchema,
1458
+ // Same forward-compatible format gate as schema_version (accept 0.x.y, gate a
1459
+ // higher major with an upgrade error) rather than a hard literal. `basou_version`
1460
+ // is a format stamp, not the npm/product version; consolidating it with
1461
+ // schema_version is a candidate cleanup for the M4 freeze pass.
1462
+ basou_version: SchemaVersionSchema,
1463
+ workspace: WorkspaceMetaSchema,
1464
+ project: ProjectSchema,
1465
+ capabilities: CapabilitiesSchema,
1466
+ approval: ApprovalConfigSchema,
1467
+ adapters: AdaptersSchema,
1468
+ git: GitConfigSchema,
1469
+ import: ImportConfigSchema.optional(),
1470
+ repos: z4.array(RepoEntrySchema).min(1).optional()
1471
+ });
1472
+ var KNOWN_TOP_LEVEL_KEYS = new Set(Object.keys(ManifestSchema.shape));
1473
+ function unknownManifestKeys(manifest) {
1474
+ return Object.keys(manifest).filter((k) => !KNOWN_TOP_LEVEL_KEYS.has(k)).sort();
1475
+ }
1476
+
1477
+ // src/storage/manifest.ts
1478
+ function createManifest(input) {
1479
+ if (input.workspaceName.length === 0) {
1480
+ throw new Error("Workspace name is empty. Pass --name explicitly.");
1481
+ }
1482
+ const now = (input.now ?? /* @__PURE__ */ new Date()).toISOString();
1483
+ const workspaceId = input.workspaceId ?? prefixedUlid("ws");
1484
+ const project = {
1485
+ ...input.projectName !== void 0 ? { name: input.projectName } : {},
1486
+ ...input.projectDescription !== void 0 ? { description: input.projectDescription } : {}
1487
+ };
1488
+ const manifest = {
1489
+ schema_version: "0.1.0",
1490
+ basou_version: "0.1.0",
1491
+ workspace: {
1492
+ id: workspaceId,
1493
+ name: input.workspaceName,
1494
+ created_at: now,
1495
+ updated_at: now
1496
+ },
1497
+ project,
1498
+ capabilities: {
1499
+ enabled: ["core", "claude-code-adapter", "terminal-recording", "git-capability", "approval"]
1500
+ },
1501
+ approval: {
1502
+ required_for: ["destructive_command", "external_send"],
1503
+ default_risk_level: "medium"
1504
+ },
1505
+ adapters: {
1506
+ "claude-code": { enabled: true }
1507
+ },
1508
+ git: { events_log: "ignore" },
1509
+ ...input.sourceRoots !== void 0 && input.sourceRoots.length > 0 ? { import: { source_roots: input.sourceRoots } } : {}
1510
+ };
1511
+ return ManifestSchema.parse(manifest);
1512
+ }
1513
+ async function writeManifest(paths, manifest, options) {
1514
+ const force = options?.force === true;
1515
+ const validated = ManifestSchema.parse(manifest);
1516
+ delete validated.project.repository_url;
1517
+ if (!force) {
1518
+ let existed = false;
1519
+ try {
1520
+ await lstat(paths.files.manifest);
1521
+ existed = true;
1522
+ } catch (error) {
1523
+ if (!hasErrorCode2(error) || error.code !== "ENOENT") {
1524
+ throw new Error("Failed to inspect existing manifest", { cause: error });
1525
+ }
1526
+ }
1527
+ if (existed) {
1528
+ throw new Error("Already initialized. Use --force to overwrite.");
1529
+ }
1530
+ }
1531
+ await writeYamlFile(paths.files.manifest, validated);
1532
+ }
1533
+ async function readManifest(paths) {
1534
+ const raw = await readYamlFile(paths.files.manifest);
1535
+ return ManifestSchema.parse(raw);
1536
+ }
1537
+ function hasErrorCode2(error) {
1538
+ if (!(error instanceof Error)) return false;
1539
+ return typeof error.code === "string";
1540
+ }
1541
+
1542
+ // src/lib/view-strings.ts
1543
+ function resolveViewLanguage(manifest) {
1544
+ if (manifest === null) return "en";
1545
+ const anchor = manifest.repos?.find((r) => normalizeRelativePath(r.path) === ".");
1546
+ return anchor?.language === "ja" ? "ja" : "en";
1547
+ }
1548
+ async function resolveViewLanguageFromPaths(paths) {
1549
+ try {
1550
+ return resolveViewLanguage(await readManifest(paths));
1551
+ } catch {
1552
+ return "en";
1553
+ }
1554
+ }
1555
+ function viewStrings(language) {
1556
+ return language === "ja" ? JA : EN;
1557
+ }
1558
+ function relativeAgeEn(startedAt, now) {
1559
+ if (startedAt === null) return "(unknown)";
1560
+ const ms = now.getTime() - Date.parse(startedAt);
1561
+ if (!Number.isFinite(ms) || ms < 0) return "just now";
1562
+ if (ms < 6e4) return "just now";
1563
+ const totalMin = Math.floor(ms / 6e4);
1564
+ const days = Math.floor(totalMin / 1440);
1565
+ const hours = Math.floor(totalMin % 1440 / 60);
1566
+ const mins = totalMin % 60;
1567
+ if (days > 0) return hours > 0 ? `${days}d ${hours}h ago` : `${days}d ago`;
1568
+ if (hours > 0) return mins > 0 ? `${hours}h ${mins}m ago` : `${hours}h ago`;
1569
+ return `${mins}m ago`;
1570
+ }
1571
+ function relativeAgeJa(startedAt, now) {
1572
+ if (startedAt === null) return "(\u4E0D\u660E)";
1573
+ const ms = now.getTime() - Date.parse(startedAt);
1574
+ if (!Number.isFinite(ms) || ms < 0) return "\u305F\u3063\u305F\u4ECA";
1575
+ if (ms < 6e4) return "\u305F\u3063\u305F\u4ECA";
1576
+ const totalMin = Math.floor(ms / 6e4);
1577
+ const days = Math.floor(totalMin / 1440);
1578
+ const hours = Math.floor(totalMin % 1440 / 60);
1579
+ const mins = totalMin % 60;
1580
+ if (days > 0) return hours > 0 ? `${days}\u65E5${hours}\u6642\u9593\u524D` : `${days}\u65E5\u524D`;
1581
+ if (hours > 0) return mins > 0 ? `${hours}\u6642\u9593${mins}\u5206\u524D` : `${hours}\u6642\u9593\u524D`;
1582
+ return `${mins}\u5206\u524D`;
1583
+ }
1584
+ var EN = {
1585
+ relativeAge: relativeAgeEn,
1586
+ common: {
1587
+ lastSessionLabel: "Last session",
1588
+ latestDecisionLabel: "Latest decision",
1589
+ recentFilesLabel: "Recently changed files",
1590
+ trackWhyLabel: "Why",
1591
+ decisionOtherSessionNote: (sid) => `Note: this decision comes from a different session [${sid}] than the last session.`
1592
+ },
1593
+ orientation: {
1594
+ headingWhere: "## Where you are now",
1595
+ headingRecent: (n) => `## Recent direction (last ${n} sessions)`,
1596
+ headingInFlight: "## What is in flight",
1597
+ headingForward: "## Where you are heading",
1598
+ headingCurrency: "## Is this current",
1599
+ inFlightTasksHeading: (n) => `### In-flight tasks (${n})`,
1600
+ pendingApprovalsHeading: (n) => `### Pending approvals (${n})`,
1601
+ suspectSessionsHeading: (n) => `### Suspect sessions (${n})`,
1602
+ openTracksHeading: (n) => `### Open tracks (shown until closed) (${n})`,
1603
+ decisionStaleNote: (age) => `Note: this is the latest *recorded* decision. The latest activity (${age}) is more recent, so the current direction may not be reflected here (conversational decisions are not captured automatically; record this session's decisions with \`basou decision capture\`).`,
1604
+ outOfRootWarning: (count, files) => `\u26A0 ${count} outside source_roots (possibly another project): ${files}`,
1605
+ recentEmpty: "(no records yet)",
1606
+ recentDecisionsLabel: "Decisions",
1607
+ recentNextStepLabel: "Next step",
1608
+ recentChangedLabel: "Changed",
1609
+ trackCloseInstruction: "When finished, close it with `basou decision void <decision_id>`. It stays listed here every time until closed.",
1610
+ nextStepRecordedLabel: (age) => `Next step (recorded, ${age})`,
1611
+ noteStaleNote: (age) => `Note: work continued after this was recorded (latest activity ${age}), so this starting point may be stale.`,
1612
+ fallbackStaleDirection: "- (no planned tasks or recorded next step \u2014 the latest activity postdates the latest decision; ask the user for the continuation point)",
1613
+ fallbackStaleReferenceLabel: "Reference (possibly stale \u2014 not the current direction)",
1614
+ trackNudge: 'Once the next essential direction is settled, record it as a track with `basou decision capture` (`"kind":"track"`) / `basou decision record --track` \u2014 it stays surfaced here every session until closed.',
1615
+ federatedFreshnessNote: "Note: the freshness verdict covers only this machine's local store. Missed work on other hosts cannot be assessed here (run `basou refresh` on each host to sync).",
1616
+ bannerUnverifiable: (n) => `> \u26A0\uFE0F **Re-import needed** \u2014 ${n} session(s) changed in the native logs but cannot be imported by a plain refresh. Re-import with \`basou refresh --force\` (details under "Is this current" at the bottom).`,
1617
+ bannerStale: (parts) => `> \u26A0\uFE0F **Stale (uncaptured: ${parts})** \u2014 run \`basou refresh\` before starting work (details under "Is this current" at the bottom).`,
1618
+ partNew: (n) => `${n} new`,
1619
+ partUpdated: (n) => `${n} updated`,
1620
+ partsJoiner: ", ",
1621
+ verdictUnverifiable: (n) => [
1622
+ `\u26A0\uFE0F The native logs changed, but ${n} session(s) cannot be safely re-imported by a plain \`basou refresh\` (non-append changes, prior-chain mismatch, etc.).`,
1623
+ "Re-import with `basou refresh --force`. (`basou verify` is a different check \u2014 it inspects already-imported data for tampering/corruption, a separate axis from the suspect count in the header. A clean verify can still leave uncaptured work.)"
1624
+ ],
1625
+ verdictStale: (parts) => [
1626
+ `\u26A0\uFE0F Stale. There is uncaptured work since the last import (${parts}).`,
1627
+ "Run `basou refresh` before starting work."
1628
+ ],
1629
+ verdictUpdatedOnly: (n) => [
1630
+ `\u26A0\uFE0F ${n} session(s) have been updated. \`basou refresh\` can import them.`,
1631
+ "(A session still in progress keeps growing after each import, so it will keep appearing here \u2014 that is normal.)"
1632
+ ],
1633
+ verdictSuspectsAlso: (n) => `There are also ${n} suspect session(s) (see "Suspect sessions" above).`,
1634
+ verdictEmpty: [
1635
+ "\u2139\uFE0F No records yet.",
1636
+ "Work in this workspace and your current position will appear here."
1637
+ ],
1638
+ verdictUnprobed: (rel, tool) => [
1639
+ `\u2139\uFE0F Showing the last imported state. Last work: ${rel} (${tool}).`,
1640
+ "Run `basou refresh` to confirm this is current."
1641
+ ],
1642
+ verdictCurrent: (rel, tool, hasHosts) => hasHosts ? `\u2705 The capture on this host (local) is current. Last work: ${rel} (${tool}). No uncaptured native sessions.` : `\u2705 The capture is current. Last work: ${rel} (${tool}). No uncaptured native sessions.`,
1643
+ verdictSuspectsCaveat: (n) => `However, ${n} suspect session(s) need attention (see "Suspect sessions" above).`,
1644
+ verdictScopeDisclaimer: "Note: this verdict only checks whether captured native sessions are current and whether any are suspect. It does not detect planning-implementation drift or unrecorded decisions.",
1645
+ toolTerminal: "terminal",
1646
+ toolHuman: "manual note",
1647
+ toolImport: "another workspace",
1648
+ toolUnknown: "unknown"
1649
+ },
1650
+ handoff: {
1651
+ headingCurrentState: "## Current state",
1652
+ headingRecentFiles: "## Recently changed files",
1653
+ headingLatestDecision: "## Latest decision",
1654
+ headingOpenTracks: "## Open tracks (shown until closed)",
1655
+ headingUnresolved: "## Unresolved items",
1656
+ headingReadNext: "## Files to read next",
1657
+ headingNextWork: "## Work to do next",
1658
+ headingSessions: "## Sessions",
1659
+ lastTaskLabel: "Last task",
1660
+ decisionStaleNote: "Note: the latest activity postdates this decision. It may already be resolved in conversation \u2014 confirm the continuation point before resuming (conversational decisions are not captured automatically; record them with `basou decision capture`).",
1661
+ trackCloseInstruction: "When finished, close it with `basou decision void <decision_id>`."
1662
+ },
1663
+ decisions: {
1664
+ dateLabel: "date",
1665
+ trackKindLine: "- kind: track (stays in orient/handoff until closed)",
1666
+ decisionLabel: "decision"
1667
+ },
1668
+ report: {
1669
+ headingSummary: "## Summary",
1670
+ headingVolume: "## Work volume",
1671
+ headingDecisions: "## Decisions",
1672
+ headingApprovals: "## Approvals",
1673
+ headingTasks: "## Tasks",
1674
+ headingChangedFiles: "## Changed files",
1675
+ headingSessions: "## Sessions",
1676
+ headingIntegrity: "## Integrity"
1677
+ }
1678
+ };
1679
+ var JA = {
1680
+ relativeAge: relativeAgeJa,
1681
+ common: {
1682
+ lastSessionLabel: "\u6700\u7D42 session",
1683
+ latestDecisionLabel: "\u76F4\u8FD1\u306E\u5224\u65AD",
1684
+ recentFilesLabel: "\u76F4\u8FD1\u306E\u5909\u66F4\u30D5\u30A1\u30A4\u30EB",
1685
+ trackWhyLabel: "\u7406\u7531",
1686
+ decisionOtherSessionNote: (sid) => `\u6CE8: \u3053\u306E\u5224\u65AD\u306F\u6700\u7D42 session \u3068\u306F\u5225\u306E session [${sid}] \u306E\u3082\u306E\u3067\u3059\u3002`
1687
+ },
1688
+ orientation: {
1689
+ headingWhere: "## \u4ECA\u3069\u3053\u306B\u3044\u308B",
1690
+ headingRecent: (n) => `## \u6700\u8FD1\u306E\u6D41\u308C (\u76F4\u8FD1 ${n} session)`,
1691
+ headingInFlight: "## \u4F55\u304C\u52D5\u304F",
1692
+ headingForward: "## \u3069\u3053\u3078\u5411\u304B\u3046",
1693
+ headingCurrency: "## \u3053\u308C\u306F\u6700\u65B0\u304B",
1694
+ inFlightTasksHeading: (n) => `### \u9032\u884C\u4E2D task (${n})`,
1695
+ pendingApprovalsHeading: (n) => `### \u627F\u8A8D\u5F85\u3061 (${n})`,
1696
+ suspectSessionsHeading: (n) => `### \u8981\u6CE8\u610F session (${n})`,
1697
+ openTracksHeading: (n) => `### \u672A\u5B8C\u30C8\u30E9\u30C3\u30AF (close \u307E\u3067\u7D99\u7D9A\u8868\u793A) (${n})`,
1698
+ decisionStaleNote: (age) => `\u6CE8: \u3053\u308C\u306F\u6700\u5F8C\u306B\u300C\u8A18\u9332\u3055\u308C\u305F\u300D\u5224\u65AD\u3067\u3059\u3002\u6700\u7D42\u6D3B\u52D5 (${age}) \u306F\u3053\u308C\u3088\u308A\u5F8C\u306E\u305F\u3081\u3001\u73FE\u5728\u306E\u65B9\u91DD\u304C\u53CD\u6620\u3055\u308C\u3066\u3044\u306A\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059(\u4F1A\u8A71\u3067\u306E\u610F\u601D\u6C7A\u5B9A\u306F\u81EA\u52D5\u8A18\u9332\u3055\u308C\u307E\u305B\u3093\u3002\`basou decision capture\` \u3067\u3053\u306E session \u306E\u5224\u65AD\u3092\u8A18\u9332\u3067\u304D\u307E\u3059)\u3002`,
1699
+ outOfRootWarning: (count, files) => `\u26A0 source_roots \u5916 ${count} \u4EF6 (\u5225\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u53EF\u80FD\u6027): ${files}`,
1700
+ recentEmpty: "(\u307E\u3060\u8A18\u9332\u304C\u3042\u308A\u307E\u305B\u3093)",
1701
+ recentDecisionsLabel: "\u5224\u65AD",
1702
+ recentNextStepLabel: "\u6B21\u306E\u8D77\u70B9",
1703
+ recentChangedLabel: "\u5909\u66F4",
1704
+ trackCloseInstruction: "\u5B8C\u4E86\u3057\u305F\u3089 `basou decision void <decision_id>` \u3067\u9589\u3058\u3066\u304F\u3060\u3055\u3044\u3002\u9589\u3058\u308B\u307E\u3067\u6BCE\u56DE\u3053\u3053\u306B\u8868\u793A\u3055\u308C\u307E\u3059\u3002",
1705
+ nextStepRecordedLabel: (age) => `\u6B21\u306E\u8D77\u70B9 (\u8A18\u9332\u6E08\u307F, ${age})`,
1706
+ noteStaleNote: (age) => `\u6CE8: \u3053\u306E\u8D77\u70B9\u306E\u8A18\u9332\u5F8C (\u6700\u7D42\u6D3B\u52D5 ${age}) \u3082\u4F5C\u696D\u304C\u7D9A\u3044\u3066\u3044\u307E\u3059\u3002\u518D\u958B\u70B9\u304C\u53E4\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002`,
1707
+ fallbackStaleDirection: "- (no planned tasks or recorded next step \u2014 \u6700\u7D42\u6D3B\u52D5\u306F\u76F4\u8FD1\u306E\u5224\u65AD\u3088\u308A\u5F8C\u3067\u3059\u3002\u7D99\u7D9A\u70B9\u3092\u30E6\u30FC\u30B6\u306B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044)",
1708
+ fallbackStaleReferenceLabel: "\u53C2\u8003 (\u53E4\u3044\u53EF\u80FD\u6027\u30FB\u65B9\u91DD\u3067\u306F\u306A\u3044)",
1709
+ trackNudge: '\u6B21\u306B\u4F5C\u308B\u3079\u304D\u672C\u8CEA\u7684\u306A\u65B9\u5411\u6027\u304C\u5B9A\u307E\u3063\u305F\u3089 `basou decision capture` (`"kind":"track"`) / `basou decision record --track` \u3067 track \u5316\u3059\u308B\u3068\u3001close \u307E\u3067\u6BCE session \u3053\u3053\u306B\u7D99\u7D9A\u8868\u793A\u3055\u308C\u307E\u3059\u3002',
1710
+ federatedFreshnessNote: "\u6CE8: \u9BAE\u5EA6\u5224\u5B9A\u306F\u3053\u306E\u30DE\u30B7\u30F3\u306E\u30ED\u30FC\u30AB\u30EB\u30B9\u30C8\u30A2\u306E\u307F\u304C\u5BFE\u8C61\u3067\u3059\u3002\u4ED6\u30DB\u30B9\u30C8\u306E\u53D6\u308A\u3053\u307C\u3057\u306F\u5224\u5B9A\u3067\u304D\u307E\u305B\u3093(\u5404\u30DB\u30B9\u30C8\u3067 basou refresh \u3092\u5B9F\u884C\u3057\u540C\u671F\u3057\u3066\u304F\u3060\u3055\u3044)\u3002",
1711
+ bannerUnverifiable: (n) => `> \u26A0\uFE0F **\u518D\u53D6\u308A\u8FBC\u307F\u304C\u5FC5\u8981** \u2014 native \u30ED\u30B0\u304C\u5909\u5316\u3057\u305F\u304C\u901A\u5E38\u306E refresh \u3067\u306F\u53D6\u308A\u8FBC\u3081\u306A\u3044\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${n} \u4EF6\u3042\u308A\u307E\u3059\u3002\`basou refresh --force\` \u3067\u518D\u53D6\u308A\u8FBC\u307F\u3057\u3066\u304F\u3060\u3055\u3044(\u8A73\u7D30\u306F\u672B\u5C3E\u300C\u3053\u308C\u306F\u6700\u65B0\u304B\u300D)\u3002`,
1712
+ bannerStale: (parts) => `> \u26A0\uFE0F **\u53E4\u3044\u3067\u3059\uFF08\u672A\u53D6\u308A\u8FBC\u307F ${parts}\uFF09** \u2014 \u7740\u624B\u524D\u306B\u5FC5\u305A \`basou refresh\` \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044(\u8A73\u7D30\u306F\u672B\u5C3E\u300C\u3053\u308C\u306F\u6700\u65B0\u304B\u300D)\u3002`,
1713
+ partNew: (n) => `\u65B0\u898F ${n} \u4EF6`,
1714
+ partUpdated: (n) => `\u66F4\u65B0 ${n} \u4EF6`,
1715
+ partsJoiner: "\u30FB",
1716
+ verdictUnverifiable: (n) => [
1717
+ `\u26A0\uFE0F native \u30ED\u30B0\u304C\u5909\u5316\u3057\u307E\u3057\u305F\u304C\u3001\u901A\u5E38\u306E \`basou refresh\` \u3067\u306F\u5B89\u5168\u306B\u518D\u53D6\u308A\u8FBC\u307F\u3067\u304D\u306A\u3044\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${n} \u4EF6\u3042\u308A\u307E\u3059(\u975E\u8FFD\u8A18\u5909\u66F4\u30FB\u524D\u30C1\u30A7\u30FC\u30F3\u4E0D\u6574\u5408\u306A\u3069)\u3002`,
1718
+ "`basou refresh --force` \u3067\u518D\u53D6\u308A\u8FBC\u307F\u3057\u3066\u304F\u3060\u3055\u3044\u3002(`basou verify` \u306F\u5225\u7269=\u53D6\u308A\u8FBC\u307F\u6E08\u307F\u30C7\u30FC\u30BF\u306E\u6539\u7AC4/\u7834\u640D\u691C\u67FB\u3067\u3001\u30D8\u30C3\u30C0\u306E suspect \u3068\u306F\u5225\u8EF8\u3067\u3059\u3002verify \u304C clean \u3067\u3082\u672A\u53D6\u308A\u8FBC\u307F\u306F\u6B8B\u308A\u5F97\u307E\u3059\u3002)"
1719
+ ],
1720
+ verdictStale: (parts) => [
1721
+ `\u26A0\uFE0F \u53E4\u3044\u3067\u3059\u3002\u6700\u5F8C\u306E\u53D6\u308A\u8FBC\u307F\u4EE5\u964D\u306B\u672A\u53D6\u308A\u8FBC\u307F\u306E\u4F5C\u696D\u304C\u3042\u308A\u307E\u3059(${parts})\u3002`,
1722
+ "\u7740\u624B\u524D\u306B\u5FC5\u305A `basou refresh` \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
1723
+ ],
1724
+ verdictUpdatedOnly: (n) => [
1725
+ `\u26A0\uFE0F \u66F4\u65B0\u3055\u308C\u305F\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${n} \u4EF6\u3042\u308A\u307E\u3059\u3002\`basou refresh\` \u3067\u53D6\u308A\u8FBC\u3081\u307E\u3059\u3002`,
1726
+ "(\u9032\u884C\u4E2D\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u304C\u3042\u308B\u5834\u5408\u3001\u305D\u308C\u81EA\u8EAB\u306F\u53D6\u308A\u8FBC\u307F\u5F8C\u3082\u5897\u3048\u7D9A\u3051\u308B\u305F\u3081\u6B8B\u308A\u307E\u3059\uFF1D\u6B63\u5E38\u3067\u3059\u3002)"
1727
+ ],
1728
+ verdictSuspectsAlso: (n) => `\u307E\u305F\u8981\u6CE8\u610F\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${n} \u4EF6\u3042\u308A\u307E\u3059(\u4E0A\u8A18\u300C\u8981\u6CE8\u610F session\u300D\u53C2\u7167)\u3002`,
1729
+ verdictEmpty: [
1730
+ "\u2139\uFE0F \u307E\u3060\u8A18\u9332\u304C\u3042\u308A\u307E\u305B\u3093\u3002",
1731
+ "\u3053\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u3067\u4F5C\u696D\u3059\u308B\u3068\u3001\u3053\u3053\u306B\u73FE\u5728\u5730\u304C\u8868\u793A\u3055\u308C\u307E\u3059\u3002"
1732
+ ],
1733
+ verdictUnprobed: (rel, tool) => [
1734
+ `\u2139\uFE0F \u53D6\u308A\u8FBC\u307F\u6E08\u307F\u306E\u72B6\u614B\u3092\u8868\u793A\u3057\u3066\u3044\u307E\u3059\u3002\u6700\u5F8C\u306E\u4F5C\u696D\u306F ${rel}(${tool})\u3002`,
1735
+ "\u6700\u65B0\u304B\u78BA\u8A8D\u3059\u308B\u306B\u306F `basou refresh` \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
1736
+ ],
1737
+ verdictCurrent: (rel, tool, hasHosts) => `\u2705 ${hasHosts ? "\u3053\u306E\u30DB\u30B9\u30C8(\u30ED\u30FC\u30AB\u30EB)\u306E" : ""}\u53D6\u308A\u8FBC\u307F\u306F\u6700\u65B0\u3067\u3059\u3002\u6700\u5F8C\u306E\u4F5C\u696D\u306F ${rel}(${tool})\u3002\u672A\u53D6\u308A\u8FBC\u307F\u306E native \u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u3042\u308A\u307E\u305B\u3093\u3002`,
1738
+ verdictSuspectsCaveat: (n) => `\u305F\u3060\u3057\u8981\u6CE8\u610F\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${n} \u4EF6\u3042\u308A\u307E\u3059(\u4E0A\u8A18\u300C\u8981\u6CE8\u610F session\u300D\u53C2\u7167)\u3002`,
1739
+ verdictScopeDisclaimer: "\u6CE8: \u3053\u306E\u5224\u5B9A\u306F\u53D6\u308A\u8FBC\u307F\u6E08\u307F native \u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u9BAE\u5EA6\u3068 suspect \u306E\u6709\u7121\u3060\u3051\u3092\u898B\u307E\u3059\u3002\u8A08\u753B\u2194\u5B9F\u88C5\u306E\u30C9\u30EA\u30D5\u30C8\u3084\u672A\u8A18\u9332\u306E\u610F\u601D\u6C7A\u5B9A\u307E\u3067\u306F\u691C\u77E5\u3057\u307E\u305B\u3093\u3002",
1740
+ toolTerminal: "\u30BF\u30FC\u30DF\u30CA\u30EB",
1741
+ toolHuman: "\u624B\u52D5\u30E1\u30E2",
1742
+ toolImport: "\u4ED6\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9",
1743
+ toolUnknown: "\u4E0D\u660E"
1744
+ },
1745
+ handoff: {
1746
+ headingCurrentState: "## \u73FE\u5728\u306E\u72B6\u614B",
1747
+ headingRecentFiles: "## \u76F4\u8FD1\u306E\u5909\u66F4\u30D5\u30A1\u30A4\u30EB",
1748
+ headingLatestDecision: "## \u76F4\u8FD1\u306E\u5224\u65AD",
1749
+ headingOpenTracks: "## \u672A\u5B8C\u30C8\u30E9\u30C3\u30AF (close \u307E\u3067\u7D99\u7D9A\u8868\u793A)",
1750
+ headingUnresolved: "## \u672A\u6C7A\u4E8B\u9805",
1751
+ headingReadNext: "## \u6B21\u306B\u8AAD\u3080\u3079\u304D\u30D5\u30A1\u30A4\u30EB",
1752
+ headingNextWork: "## \u6B21\u306B\u5B9F\u884C\u3059\u3079\u304D\u4F5C\u696D",
1753
+ headingSessions: "## \u30BB\u30C3\u30B7\u30E7\u30F3\u4E00\u89A7",
1754
+ lastTaskLabel: "\u6700\u7D42 task",
1755
+ decisionStaleNote: "\u6CE8: \u6700\u7D42\u6D3B\u52D5\u306F\u3053\u306E\u5224\u65AD\u3088\u308A\u5F8C\u3067\u3059\u3002\u4F1A\u8A71\u3067\u65E2\u306B\u89E3\u6C7A\u6E08\u307F\u306E\u53EF\u80FD\u6027\u304C\u3042\u308B\u305F\u3081\u3001\u518D\u958B\u524D\u306B\u7D99\u7D9A\u70B9\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044(\u4F1A\u8A71\u3067\u306E\u610F\u601D\u6C7A\u5B9A\u306F\u81EA\u52D5\u8A18\u9332\u3055\u308C\u307E\u305B\u3093\u3002`basou decision capture` \u3067\u8A18\u9332\u3067\u304D\u307E\u3059)\u3002",
1756
+ trackCloseInstruction: "\u5B8C\u4E86\u3057\u305F\u3089 `basou decision void <decision_id>` \u3067\u9589\u3058\u3066\u304F\u3060\u3055\u3044\u3002"
1757
+ },
1758
+ decisions: {
1759
+ dateLabel: "\u6C7A\u5B9A\u65E5",
1760
+ trackKindLine: "- \u7A2E\u5225: track (close \u307E\u3067 orient/handoff \u306B\u7D99\u7D9A\u8868\u793A)",
1761
+ decisionLabel: "\u5224\u65AD"
1762
+ },
1763
+ report: {
1764
+ headingSummary: "## \u6982\u8981",
1765
+ headingVolume: "## \u4F5C\u696D\u91CF",
1766
+ headingDecisions: "## \u5224\u65AD",
1767
+ headingApprovals: "## \u627F\u8A8D",
1768
+ headingTasks: "## \u30BF\u30B9\u30AF",
1769
+ headingChangedFiles: "## \u5909\u66F4\u30D5\u30A1\u30A4\u30EB",
1770
+ headingSessions: "## \u30BB\u30C3\u30B7\u30E7\u30F3\u4E00\u89A7",
1771
+ headingIntegrity: "## \u6574\u5408\u6027"
1772
+ }
1773
+ };
1774
+ function resolveRepoContentLanguage(language) {
1775
+ return language === "ja" ? "ja" : "en";
1776
+ }
1777
+ function resolveAnchorContentLanguage(repos) {
1778
+ return resolveRepoContentLanguage(repos.find((r) => r.anchor === true)?.language);
1779
+ }
1780
+ function presetStrings(language) {
1781
+ return language === "ja" ? PRESET_JA : PRESET_EN;
1782
+ }
1783
+ var PRESET_EN = {
1784
+ repoBlock: {
1785
+ heading: "## Project configuration (generated by basou \u2014 the manifest is the source of truth)",
1786
+ intro: "This section is generated by `basou project preset` from the declarations in `.basou/manifest.yaml`. Edit the manifest, not this block (content outside the markers is preserved).",
1787
+ visibilityLabel: (v) => {
1788
+ switch (v) {
1789
+ case "public":
1790
+ return "public (the git history is public)";
1791
+ case "private":
1792
+ return "private (the git history is not public)";
1793
+ case "future-public":
1794
+ return "future-public (private today, planned to go public)";
1795
+ default:
1796
+ return "unset";
1797
+ }
1798
+ },
1799
+ sourceLanguageLabel: (l) => {
1800
+ switch (l) {
1801
+ case "en":
1802
+ return "en (commits, comments, and code in English)";
1803
+ case "ja":
1804
+ return "ja (commits, comments, and code in Japanese)";
1805
+ case "en+ja":
1806
+ return "en+ja (commits, comments, and code in English and Japanese)";
1807
+ default:
1808
+ return "unset";
1809
+ }
1810
+ },
1811
+ publishKindLabel: (k) => k === "web" ? "web (deployed)" : "npm (package)",
1812
+ publishVisibilityLabel: (v) => {
1813
+ switch (v) {
1814
+ case "public":
1815
+ return "public";
1816
+ case "private":
1817
+ return "private";
1818
+ case "future-public":
1819
+ return "future-public";
1820
+ default:
1821
+ return "visibility unset";
1822
+ }
1823
+ },
1824
+ contentLanguageLabel: (l) => l ?? "language unset",
1825
+ sourceVisibilityLabel: "Source visibility",
1826
+ sourceLanguageLineLabel: "Source language",
1827
+ publishesNone: "- Published surfaces: none",
1828
+ publishesHeader: "- Published surfaces:"
1829
+ },
1830
+ viewBlock: {
1831
+ heading: "## Workspace view layout (generated by basou \u2014 the manifest is the source of truth)",
1832
+ intro: "This section is generated by `basou project preset` from the declarations in `.basou/manifest.yaml`. Edit the manifest, not this block (content outside the markers is preserved).",
1833
+ selfNote: (viewName) => `This AGENTS.md is itself generated by basou (canonical: \`agents/${viewName}/AGENTS.md\`; content outside the markers is preserved).`,
1834
+ aggregates: (n) => `This directory is a **view** aggregating the ${n} declared repo(s) via symlinks. It holds no content of its own and is not under git.`,
1835
+ reposHeading: "### Aggregated repos",
1836
+ tableHeader: "| repo | visibility | language | instructions |",
1837
+ instructionsAnchor: "anchor (hand-maintained)",
1838
+ instructionsSelf: "self (the repo owns it)",
1839
+ instructionsHub: "hub (generated by basou)",
1840
+ unsetShort: "unset",
1841
+ commitHeading: "### Where to commit",
1842
+ commitBody: "You cannot commit in the view (it is not under git). Always `cd` into the actual repo before committing.",
1843
+ conventionsHeading: "### Required reading",
1844
+ conventionsBody: "The working conventions live in each repo's AGENTS.md. Read these before working.",
1845
+ principlesHeading: "### Key principles",
1846
+ principleStateless: "- This directory holds no state (not under git)",
1847
+ principleNoFiles: "- Do not place important files here directly (they belong in the repos)"
1848
+ },
1849
+ anchorStarter: {
1850
+ identityLine: (title) => `> This repository is the **planning master (anchor) of ${title}**. AI agents working here should read this file first.`,
1851
+ starterNote: "> This file is a starter that `basou project derive` generated **once** at greenfield bring-up. Hand-maintain it from here \u2014 basou never regenerates or overwrites it (there are no BASOU:GENERATED markers; edit freely).",
1852
+ basicsHeading: "## Project basics",
1853
+ basicsTodo: "<!-- TODO: these cannot be derived from the manifest. Fill them in. -->",
1854
+ commitHeading: "## Where to commit",
1855
+ commitPlanning: "- **This repository (the planning master)**: plans, designs, strategy docs.",
1856
+ commitImplementation: "- **Each implementation repo**: implementation code. Always `cd` into the target repo before committing.",
1857
+ commitView: "- **The workspace view**: not under git. You cannot commit in the view.",
1858
+ conventionsHeading: "## Required reading",
1859
+ conventionsBody: "The working conventions live in each repo's AGENTS.md. Read these before working.",
1860
+ viewPointerLine: (viewName) => `- ${viewName}/AGENTS.md (the workspace view, generated by basou) \u2014 **the authoritative, up-to-date repo roster (the live roster) lives there**`,
1861
+ policyHeading: "## Working policy (project specifics)",
1862
+ policyTodo: [
1863
+ "<!-- TODO: describe these for your project.",
1864
+ " - Current phase / key documents",
1865
+ " - Secrets handling (where NOT to write them)",
1866
+ " - Language policy (commits / comments / docs)",
1867
+ " - Commit discipline (avoid mixed commits, etc.)",
1868
+ "-->"
1869
+ ]
1870
+ }
1871
+ };
1872
+ var PRESET_JA = {
1873
+ repoBlock: {
1874
+ heading: "## \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u69CB\u6210(basou \u304C\u751F\u6210 \u2014 manifest \u304C\u6B63\u672C)",
1875
+ intro: "\u3053\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u306F `.basou/manifest.yaml` \u306E\u5BA3\u8A00\u304B\u3089 `basou project preset` \u304C\u751F\u6210\u3057\u307E\u3059\u3002\u7DE8\u96C6\u306F manifest \u5074\u3067\u884C\u3063\u3066\u304F\u3060\u3055\u3044(\u30DE\u30FC\u30AB\u30FC\u5916\u306E\u8A18\u8FF0\u306F\u4FDD\u6301\u3055\u308C\u307E\u3059)\u3002",
1876
+ visibilityLabel: (v) => {
1877
+ switch (v) {
1878
+ case "public":
1879
+ return "public(git \u5C65\u6B74\u306F\u516C\u958B)";
1880
+ case "private":
1881
+ return "private(git \u5C65\u6B74\u306F\u975E\u516C\u958B)";
1882
+ case "future-public":
1883
+ return "future-public(\u73FE\u5728\u306F\u975E\u516C\u958B\u30FB\u5C06\u6765\u516C\u958B\u4E88\u5B9A)";
1884
+ default:
1885
+ return "\u672A\u8A2D\u5B9A";
1886
+ }
1887
+ },
1888
+ sourceLanguageLabel: (l) => {
1889
+ switch (l) {
1890
+ case "en":
1891
+ return "en(commit\u30FB\u30B3\u30E1\u30F3\u30C8\u30FB\u30B3\u30FC\u30C9\u306F\u82F1\u8A9E)";
1892
+ case "ja":
1893
+ return "ja(commit\u30FB\u30B3\u30E1\u30F3\u30C8\u30FB\u30B3\u30FC\u30C9\u306F\u65E5\u672C\u8A9E)";
1894
+ case "en+ja":
1895
+ return "en+ja(commit\u30FB\u30B3\u30E1\u30F3\u30C8\u30FB\u30B3\u30FC\u30C9\u306F\u65E5\u82F1)";
1896
+ default:
1897
+ return "\u672A\u8A2D\u5B9A";
1898
+ }
1899
+ },
1900
+ publishKindLabel: (k) => k === "web" ? "web(\u30C7\u30D7\u30ED\u30A4)" : "npm(\u30D1\u30C3\u30B1\u30FC\u30B8)",
1901
+ publishVisibilityLabel: (v) => {
1902
+ switch (v) {
1903
+ case "public":
1904
+ return "\u516C\u958B";
1905
+ case "private":
1906
+ return "\u975E\u516C\u958B";
1907
+ case "future-public":
1908
+ return "\u5C06\u6765\u516C\u958B";
1909
+ default:
1910
+ return "\u53EF\u8996\u6027\u672A\u8A2D\u5B9A";
1911
+ }
1912
+ },
1913
+ contentLanguageLabel: (l) => l ?? "\u8A00\u8A9E\u672A\u8A2D\u5B9A",
1914
+ sourceVisibilityLabel: "\u30BD\u30FC\u30B9\u53EF\u8996\u6027",
1915
+ sourceLanguageLineLabel: "\u30BD\u30FC\u30B9\u8A00\u8A9E",
1916
+ publishesNone: "- \u914D\u4FE1\u7269: \u306A\u3057",
1917
+ publishesHeader: "- \u914D\u4FE1\u7269:"
1918
+ },
1919
+ viewBlock: {
1920
+ heading: "## workspace view \u69CB\u6210(basou \u304C\u751F\u6210 \u2014 manifest \u304C\u6B63\u672C)",
1921
+ intro: "\u3053\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u306F `.basou/manifest.yaml` \u306E\u5BA3\u8A00\u304B\u3089 `basou project preset` \u304C\u751F\u6210\u3057\u307E\u3059\u3002\u7DE8\u96C6\u306F manifest \u5074\u3067\u884C\u3063\u3066\u304F\u3060\u3055\u3044(\u30DE\u30FC\u30AB\u30FC\u5916\u306E\u8A18\u8FF0\u306F\u4FDD\u6301\u3055\u308C\u307E\u3059)\u3002",
1922
+ selfNote: (viewName) => `\u3053\u306E AGENTS.md \u81EA\u8EAB\u3082 basou \u306E\u751F\u6210\u7269\u3067\u3059(\u5B9F\u4F53: \`agents/${viewName}/AGENTS.md\`\u3001\u30DE\u30FC\u30AB\u30FC\u5916\u306E\u8A18\u8FF0\u306F\u4FDD\u6301\u3055\u308C\u307E\u3059)\u3002`,
1923
+ aggregates: (n) => `\u3053\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u3001\u5BA3\u8A00\u3055\u308C\u305F ${n} \u500B\u306E repo \u3092 symlink \u3067\u96C6\u7D04\u3059\u308B **view** \u3067\u3059\u3002\u5B9F\u4F53\u3092\u6301\u305F\u305A\u3001git \u7BA1\u7406\u5916\u3067\u3059\u3002`,
1924
+ reposHeading: "### \u96C6\u7D04\u3057\u3066\u3044\u308B repo",
1925
+ tableHeader: "| repo | \u53EF\u8996\u6027 | \u8A00\u8A9E | \u6307\u793A\u66F8 |",
1926
+ instructionsAnchor: "anchor(\u624B\u7BA1\u7406)",
1927
+ instructionsSelf: "self(repo \u304C\u81EA\u5DF1\u7BA1\u7406)",
1928
+ instructionsHub: "hub(basou \u304C\u751F\u6210)",
1929
+ unsetShort: "\u672A\u8A2D\u5B9A",
1930
+ commitHeading: "### \u3069\u3053\u3067 commit \u3059\u308B\u304B",
1931
+ commitBody: "view \u3067\u306F commit \u3067\u304D\u307E\u305B\u3093(git \u7BA1\u7406\u5916)\u3002\u5909\u66F4\u306F\u5FC5\u305A\u5B9F\u4F53\u306E repo \u306B `cd` \u3057\u3066\u304B\u3089 commit \u3057\u3066\u304F\u3060\u3055\u3044\u3002",
1932
+ conventionsHeading: "### \u5FC5\u305A\u8AAD\u3080\u3079\u304D\u898F\u7D04",
1933
+ conventionsBody: "\u4F5C\u696D\u898F\u7D04\u306F\u5404 repo \u306E AGENTS.md \u306B\u3042\u308A\u307E\u3059\u3002\u4EE5\u4E0B\u3092\u8AAD\u3093\u3067\u304B\u3089\u4F5C\u696D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
1934
+ principlesHeading: "### \u91CD\u8981\u539F\u5247",
1935
+ principleStateless: "- \u3053\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u72B6\u614B\u3092\u6301\u305F\u306A\u3044(git \u7BA1\u7406\u5916)",
1936
+ principleNoFiles: "- \u91CD\u8981\u306A\u30D5\u30A1\u30A4\u30EB\u3092\u3053\u3053\u306B\u76F4\u63A5\u7F6E\u304B\u306A\u3044(\u5B9F\u4F53\u306F\u5404 repo \u306B\u7F6E\u304F)"
1937
+ },
1938
+ anchorStarter: {
1939
+ identityLine: (title) => `> \u3053\u306E\u30EA\u30DD\u30B8\u30C8\u30EA\u306F **${title} \u306E planning master(anchor)** \u3067\u3059\u3002\u3053\u3053\u3067\u4F5C\u696D\u3059\u308B AI \u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306F\u3001\u307E\u305A\u3053\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044\u3002`,
1940
+ starterNote: "> \u3053\u306E\u30D5\u30A1\u30A4\u30EB\u306F `basou project derive` \u304C greenfield \u7ACB\u3061\u4E0A\u3052\u6642\u306B **\u4E00\u5EA6\u3060\u3051\u751F\u6210\u3057\u305F starter** \u3067\u3059\u3002\u4EE5\u5F8C\u306F\u624B\u7BA1\u7406\u3057\u3066\u304F\u3060\u3055\u3044 \u2014 basou \u306F\u518D\u751F\u6210\u3082\u4E0A\u66F8\u304D\u3082\u3057\u307E\u305B\u3093(BASOU:GENERATED \u30DE\u30FC\u30AB\u30FC\u306F\u7121\u304F\u3001\u81EA\u7531\u306B\u7DE8\u96C6\u3067\u304D\u307E\u3059)\u3002",
1941
+ basicsHeading: "## \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u57FA\u672C\u60C5\u5831",
1942
+ basicsTodo: "<!-- TODO: manifest \u304B\u3089\u306F\u5C0E\u51FA\u3067\u304D\u306A\u3044\u9805\u76EE\u3067\u3059\u3002\u57CB\u3081\u3066\u304F\u3060\u3055\u3044\u3002 -->",
1943
+ commitHeading: "## \u3069\u3053\u3067 commit \u3059\u308B\u304B",
1944
+ commitPlanning: "- **\u3053\u306E\u30EA\u30DD\u30B8\u30C8\u30EA(planning master)**: \u69CB\u60F3\u30FB\u8A08\u753B\u30FB\u8A2D\u8A08\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u3002",
1945
+ commitImplementation: "- **\u5404\u5B9F\u88C5 repo**: \u5B9F\u88C5\u30B3\u30FC\u30C9\u3002\u5FC5\u305A\u5BFE\u8C61 repo \u306B `cd` \u3057\u3066\u304B\u3089 commit \u3057\u3066\u304F\u3060\u3055\u3044\u3002",
1946
+ commitView: "- **workspace view**: git \u7BA1\u7406\u5916\u3002view \u3067\u306F commit \u3067\u304D\u307E\u305B\u3093\u3002",
1947
+ conventionsHeading: "## \u5FC5\u305A\u8AAD\u3080\u3079\u304D\u898F\u7D04",
1948
+ conventionsBody: "\u4F5C\u696D\u898F\u7D04\u306F\u5404 repo \u306E AGENTS.md \u306B\u3042\u308A\u307E\u3059\u3002\u4EE5\u4E0B\u3092\u8AAD\u3093\u3067\u304B\u3089\u4F5C\u696D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
1949
+ viewPointerLine: (viewName) => `- ${viewName}/AGENTS.md(workspace view\u30FBbasou \u304C\u751F\u6210)\u2014 **\u6700\u65B0\u306E repo \u69CB\u6210(roster)\u306F\u3053\u3053\u3092\u6B63\u3068\u3059\u308B**`,
1950
+ policyHeading: "## \u4F5C\u696D\u65B9\u91DD(\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u56FA\u6709\u4E8B\u9805)",
1951
+ policyTodo: [
1952
+ "<!-- TODO: \u4EE5\u4E0B\u3092\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306B\u5408\u308F\u305B\u3066\u8A18\u8FF0\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
1953
+ " - \u73FE\u5728\u306E\u30D5\u30A7\u30FC\u30BA / \u91CD\u8981\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8",
1954
+ " - \u6A5F\u5BC6\u60C5\u5831\u306E\u6271\u3044(\u3069\u3053\u306B\u66F8\u304B\u306A\u3044\u304B)",
1955
+ " - \u8A00\u8A9E\u30DD\u30EA\u30B7\u30FC(commit / \u30B3\u30E1\u30F3\u30C8 / \u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u306E\u8A00\u8A9E)",
1956
+ " - commit \u904B\u7528(\u6DF7\u5728\u30B3\u30DF\u30C3\u30C8\u3092\u907F\u3051\u308B \u7B49)",
1957
+ "-->"
1958
+ ]
1959
+ }
1960
+ };
1961
+
1370
1962
  // src/storage/sessions.ts
1371
1963
  import { readdir as readdir2 } from "fs/promises";
1372
1964
  import { join as join5 } from "path";
@@ -1573,8 +2165,8 @@ async function appendChainedEvent(paths, sessionId, event) {
1573
2165
  }
1574
2166
 
1575
2167
  // src/schemas/session.schema.ts
1576
- import { z as z4 } from "zod";
1577
- var SessionStatusSchema = z4.enum([
2168
+ import { z as z5 } from "zod";
2169
+ var SessionStatusSchema = z5.enum([
1578
2170
  "initialized",
1579
2171
  "running",
1580
2172
  "waiting_approval",
@@ -1584,7 +2176,7 @@ var SessionStatusSchema = z4.enum([
1584
2176
  "imported",
1585
2177
  "archived"
1586
2178
  ]);
1587
- var SessionSourceKindSchema = z4.enum([
2179
+ var SessionSourceKindSchema = z5.enum([
1588
2180
  "claude-code-adapter",
1589
2181
  "claude-code-import",
1590
2182
  "codex-adapter",
@@ -1593,13 +2185,13 @@ var SessionSourceKindSchema = z4.enum([
1593
2185
  "import",
1594
2186
  "terminal"
1595
2187
  ]);
1596
- var SessionSourceSchema = z4.looseObject({
2188
+ var SessionSourceSchema = z5.looseObject({
1597
2189
  kind: SessionSourceKindSchema,
1598
- version: z4.literal("0.1.0"),
2190
+ version: z5.literal("0.1.0"),
1599
2191
  // Optional id of the originating session in the SOURCE tool's own
1600
2192
  // namespace (e.g. the Claude Code session UUID for a `claude-code-import`).
1601
2193
  // Lets re-imports of the same source be deduplicated; absent for live runs.
1602
- external_id: z4.string().optional(),
2194
+ external_id: z5.string().optional(),
1603
2195
  // Byte size of the source native log at import time, recorded so a later
1604
2196
  // import can detect that an append-only transcript GREW and re-import it
1605
2197
  // (scoped, preserving the session id) instead of skipping it as already
@@ -1607,33 +2199,33 @@ var SessionSourceSchema = z4.looseObject({
1607
2199
  // external_id, metrics). Absent on sessions imported before this field
1608
2200
  // existed (treated as legacy: never auto-re-imported, populated on the next
1609
2201
  // fresh import or `--force`).
1610
- source_size_bytes: z4.number().int().nonnegative().optional()
2202
+ source_size_bytes: z5.number().int().nonnegative().optional()
1611
2203
  });
1612
- var InvocationSchema = z4.looseObject({
1613
- command: z4.string().min(1),
1614
- args: z4.array(z4.string()).default([]),
2204
+ var InvocationSchema = z5.looseObject({
2205
+ command: z5.string().min(1),
2206
+ args: z5.array(z5.string()).default([]),
1615
2207
  // Nullable to record signal-terminated runs where the child has no exit
1616
2208
  // code; the same nullability is mirrored in CommandExecutedEventSchema.
1617
- exit_code: z4.number().int().nullable()
2209
+ exit_code: z5.number().int().nullable()
1618
2210
  });
1619
- var SessionMetricsSchema = z4.looseObject({
1620
- output_tokens: z4.number().int().nonnegative().optional(),
1621
- input_tokens: z4.number().int().nonnegative().optional(),
1622
- cached_input_tokens: z4.number().int().nonnegative().optional(),
1623
- reasoning_output_tokens: z4.number().int().nonnegative().optional(),
1624
- active_time_ms: z4.number().int().nonnegative().optional(),
1625
- active_intervals: z4.array(z4.looseObject({ start: IsoTimestampSchema, end: IsoTimestampSchema })).optional(),
1626
- active_gap_cap_ms: z4.number().int().nonnegative().optional(),
1627
- active_time_method: z4.string().optional(),
1628
- machine_active_time_ms: z4.number().int().nonnegative().optional()
2211
+ var SessionMetricsSchema = z5.looseObject({
2212
+ output_tokens: z5.number().int().nonnegative().optional(),
2213
+ input_tokens: z5.number().int().nonnegative().optional(),
2214
+ cached_input_tokens: z5.number().int().nonnegative().optional(),
2215
+ reasoning_output_tokens: z5.number().int().nonnegative().optional(),
2216
+ active_time_ms: z5.number().int().nonnegative().optional(),
2217
+ active_intervals: z5.array(z5.looseObject({ start: IsoTimestampSchema, end: IsoTimestampSchema })).optional(),
2218
+ active_gap_cap_ms: z5.number().int().nonnegative().optional(),
2219
+ active_time_method: z5.string().optional(),
2220
+ machine_active_time_ms: z5.number().int().nonnegative().optional()
1629
2221
  });
1630
- var SessionIntegritySchema = z4.object({
1631
- head_hash: z4.string(),
1632
- event_count: z4.number().int().nonnegative()
2222
+ var SessionIntegritySchema = z5.object({
2223
+ head_hash: z5.string(),
2224
+ event_count: z5.number().int().nonnegative()
1633
2225
  }).strict();
1634
- var SessionInnerSchema = z4.looseObject({
2226
+ var SessionInnerSchema = z5.looseObject({
1635
2227
  id: SessionIdSchema,
1636
- label: z4.string().optional(),
2228
+ label: z5.string().optional(),
1637
2229
  task_id: TaskIdSchema.nullable().optional(),
1638
2230
  workspace_id: WorkspaceIdSchema,
1639
2231
  source: SessionSourceSchema,
@@ -1641,15 +2233,15 @@ var SessionInnerSchema = z4.looseObject({
1641
2233
  // ended_at is optional because initialized / running sessions have no end time yet.
1642
2234
  ended_at: IsoTimestampSchema.optional(),
1643
2235
  status: SessionStatusSchema,
1644
- working_directory: z4.string().min(1),
2236
+ working_directory: z5.string().min(1),
1645
2237
  invocation: InvocationSchema,
1646
- related_files: z4.array(z4.string()).default([]),
1647
- events_log: z4.string().default("events.jsonl"),
1648
- summary: z4.string().nullable().optional(),
2238
+ related_files: z5.array(z5.string()).default([]),
2239
+ events_log: z5.string().default("events.jsonl"),
2240
+ summary: z5.string().nullable().optional(),
1649
2241
  metrics: SessionMetricsSchema.optional(),
1650
2242
  integrity: SessionIntegritySchema.optional()
1651
2243
  });
1652
- var SessionSchema = z4.looseObject({
2244
+ var SessionSchema = z5.looseObject({
1653
2245
  schema_version: SchemaVersionSchema,
1654
2246
  session: SessionInnerSchema
1655
2247
  });
@@ -1855,7 +2447,7 @@ async function renderDecisions(input) {
1855
2447
  const abs = resolve(repoRoot, relPath);
1856
2448
  let exists;
1857
2449
  try {
1858
- await lstat(abs);
2450
+ await lstat2(abs);
1859
2451
  exists = true;
1860
2452
  } catch {
1861
2453
  exists = false;
@@ -1863,7 +2455,9 @@ async function renderDecisions(input) {
1863
2455
  fileExistenceCache.set(relPath, exists);
1864
2456
  return exists;
1865
2457
  }
2458
+ const language = input.language ?? await resolveViewLanguageFromPaths(input.paths);
1866
2459
  const body = await formatDecisionsBody({
2460
+ language,
1867
2461
  nowIso: input.nowIso,
1868
2462
  decisions,
1869
2463
  knownEventIds,
@@ -1872,6 +2466,7 @@ async function renderDecisions(input) {
1872
2466
  return { body, decisionCount: decisions.length };
1873
2467
  }
1874
2468
  async function formatDecisionsBody(args) {
2469
+ const t = viewStrings(args.language);
1875
2470
  const lines = [];
1876
2471
  lines.push("# Decisions");
1877
2472
  lines.push("");
@@ -1894,12 +2489,12 @@ async function formatDecisionsBody(args) {
1894
2489
  lines.push("");
1895
2490
  }
1896
2491
  const occurredDate = d.occurredAt.slice(0, 10);
1897
- lines.push(`- \u6C7A\u5B9A\u65E5: ${occurredDate}`);
2492
+ lines.push(`- ${t.decisions.dateLabel}: ${occurredDate}`);
1898
2493
  if (d.kind === "track" && d.voided === void 0) {
1899
- lines.push("- \u7A2E\u5225: track (close \u307E\u3067 orient/handoff \u306B\u7D99\u7D9A\u8868\u793A)");
2494
+ lines.push(t.decisions.trackKindLine);
1900
2495
  }
1901
2496
  lines.push(`- session: ${shortDecisionSessionId(d.sessionId)}`);
1902
- lines.push(`- \u5224\u65AD: ${d.title}`);
2497
+ lines.push(`- ${t.decisions.decisionLabel}: ${d.title}`);
1903
2498
  if (typeof d.rationale === "string" && d.rationale.length > 0) {
1904
2499
  lines.push(`- rationale: ${d.rationale}`);
1905
2500
  }
@@ -2147,27 +2742,27 @@ import { simpleGit } from "simple-git";
2147
2742
  import * as fsp from "fs/promises";
2148
2743
 
2149
2744
  // src/schemas/status.schema.ts
2150
- import { z as z5 } from "zod";
2151
- var StatusSchema = z5.object({
2745
+ import { z as z6 } from "zod";
2746
+ var StatusSchema = z6.object({
2152
2747
  // status.json is a rebuildable cache: exact-match-or-rebuild, not the
2153
2748
  // durable forward-compat gate.
2154
2749
  schema_version: CacheVersionSchema,
2155
2750
  generated_at: IsoTimestampSchema,
2156
- workspace: z5.object({
2751
+ workspace: z6.object({
2157
2752
  id: WorkspaceIdSchema,
2158
- name: z5.string().min(1),
2753
+ name: z6.string().min(1),
2159
2754
  // Mirrors the manifest's basou_version, so it uses the same
2160
2755
  // forward-compatible format gate (accept 0.x.y) rather than a literal.
2161
2756
  basou_version: SchemaVersionSchema
2162
2757
  }).strict(),
2163
- directories_present: z5.object({
2164
- sessions: z5.boolean(),
2165
- tasks: z5.boolean(),
2166
- approvals_pending: z5.boolean(),
2167
- approvals_resolved: z5.boolean(),
2168
- logs: z5.boolean(),
2169
- raw: z5.boolean(),
2170
- tmp: z5.boolean()
2758
+ directories_present: z6.object({
2759
+ sessions: z6.boolean(),
2760
+ tasks: z6.boolean(),
2761
+ approvals_pending: z6.boolean(),
2762
+ approvals_resolved: z6.boolean(),
2763
+ logs: z6.boolean(),
2764
+ raw: z6.boolean(),
2765
+ tmp: z6.boolean()
2171
2766
  }).strict()
2172
2767
  }).strict();
2173
2768
 
@@ -2186,7 +2781,7 @@ async function assertBasouRootSafe(rootPath) {
2186
2781
  try {
2187
2782
  stat4 = await fsp.lstat(rootPath);
2188
2783
  } catch (error) {
2189
- if (hasErrorCode2(error) && error.code === "ENOENT") {
2784
+ if (hasErrorCode3(error) && error.code === "ENOENT") {
2190
2785
  throw new Error("Basou workspace not found", { cause: error });
2191
2786
  }
2192
2787
  throw new Error("Failed to inspect .basou root", { cause: error });
@@ -2202,7 +2797,7 @@ async function dirPresent(path2) {
2202
2797
  try {
2203
2798
  return (await fsp.lstat(path2)).isDirectory();
2204
2799
  } catch (error) {
2205
- if (hasErrorCode2(error) && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
2800
+ if (hasErrorCode3(error) && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
2206
2801
  return false;
2207
2802
  }
2208
2803
  throw new Error("Failed to inspect .basou subdirectory", { cause: error });
@@ -2243,7 +2838,7 @@ async function readStatus(paths) {
2243
2838
  try {
2244
2839
  body = await fsp.readFile(paths.files.status, "utf8");
2245
2840
  } catch (error) {
2246
- if (hasErrorCode2(error) && error.code === "ENOENT") {
2841
+ if (hasErrorCode3(error) && error.code === "ENOENT") {
2247
2842
  throw new Error("Status file not found", { cause: error });
2248
2843
  }
2249
2844
  throw new Error("Failed to read status file", { cause: error });
@@ -2256,7 +2851,7 @@ async function readStatus(paths) {
2256
2851
  }
2257
2852
  return StatusSchema.parse(parsed);
2258
2853
  }
2259
- function hasErrorCode2(error) {
2854
+ function hasErrorCode3(error) {
2260
2855
  if (!(error instanceof Error)) return false;
2261
2856
  return typeof error.code === "string";
2262
2857
  }
@@ -2513,15 +3108,15 @@ import { createHash as createHash2 } from "crypto";
2513
3108
  import { mkdir as mkdir3, readdir as readdir4, readFile as readFile7, rename as rename2, stat as stat3, unlink as unlink3 } from "fs/promises";
2514
3109
  import { join as join12 } from "path";
2515
3110
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
2516
- import { z as z8 } from "zod";
3111
+ import { z as z9 } from "zod";
2517
3112
 
2518
3113
  // src/schemas/task.schema.ts
2519
- import { z as z6 } from "zod";
2520
- var TaskStatusSchema = z6.enum(["planned", "in_progress", "done", "cancelled"]);
2521
- var TaskInnerSchema = z6.looseObject({
3114
+ import { z as z7 } from "zod";
3115
+ var TaskStatusSchema = z7.enum(["planned", "in_progress", "done", "cancelled"]);
3116
+ var TaskInnerSchema = z7.looseObject({
2522
3117
  id: TaskIdSchema,
2523
- title: z6.string().min(1),
2524
- label: z6.string().min(1).optional(),
3118
+ title: z7.string().min(1),
3119
+ label: z7.string().min(1).optional(),
2525
3120
  status: TaskStatusSchema,
2526
3121
  created_at: IsoTimestampSchema,
2527
3122
  updated_at: IsoTimestampSchema,
@@ -2545,9 +3140,9 @@ var TaskInnerSchema = z6.looseObject({
2545
3140
  * task.md and immediately see related sessions. Defaults to `[]` for
2546
3141
  * backward compatibility.
2547
3142
  */
2548
- linked_sessions: z6.array(SessionIdSchema).default([])
3143
+ linked_sessions: z7.array(SessionIdSchema).default([])
2549
3144
  });
2550
- var TaskSchema = z6.looseObject({
3145
+ var TaskSchema = z7.looseObject({
2551
3146
  schema_version: SchemaVersionSchema,
2552
3147
  task: TaskInnerSchema
2553
3148
  });
@@ -2789,17 +3384,17 @@ import { readFile as readFile6 } from "fs/promises";
2789
3384
  import { join as join11 } from "path";
2790
3385
 
2791
3386
  // src/schemas/task-index.schema.ts
2792
- import { z as z7 } from "zod";
2793
- var TaskIndexEntrySchema = z7.object({
3387
+ import { z as z8 } from "zod";
3388
+ var TaskIndexEntrySchema = z8.object({
2794
3389
  id: TaskIdSchema,
2795
3390
  status: TaskStatusSchema,
2796
- label: z7.string().min(1).optional(),
3391
+ label: z8.string().min(1).optional(),
2797
3392
  updated_at: IsoTimestampSchema
2798
3393
  }).strict();
2799
- var TaskIndexSchema = z7.object({
3394
+ var TaskIndexSchema = z8.object({
2800
3395
  // Rebuildable cache: exact-match-or-rebuild, not the durable forward-compat gate.
2801
3396
  schema_version: CacheVersionSchema,
2802
- tasks: z7.array(TaskIndexEntrySchema),
3397
+ tasks: z8.array(TaskIndexEntrySchema),
2803
3398
  last_rebuilt_at: IsoTimestampSchema
2804
3399
  }).strict();
2805
3400
  var TASK_INDEX_SCHEMA_VERSION = "0.1.0";
@@ -2885,8 +3480,8 @@ var DEFAULT_ATTACHABLE_STATUSES2 = /* @__PURE__ */ new Set([
2885
3480
  "waiting_approval"
2886
3481
  ]);
2887
3482
  var InitialTaskStatusSchema = TaskStatusSchema;
2888
- var TaskTitleSchema = z8.string().min(1);
2889
- var TaskLabelSchema = z8.string().min(1);
3483
+ var TaskTitleSchema = z9.string().min(1);
3484
+ var TaskLabelSchema = z9.string().min(1);
2890
3485
  var CompletedAtSchema = IsoTimestampSchema;
2891
3486
  var TERMINAL_TASK_STATUSES = /* @__PURE__ */ new Set(["done", "cancelled"]);
2892
3487
  function isTerminalTaskStatus(status) {
@@ -4335,7 +4930,9 @@ async function renderHandoff(input) {
4335
4930
  const firstEntry = entries[0];
4336
4931
  const lastEntry = entries[entries.length - 1];
4337
4932
  const sessionRange = firstEntry !== void 0 && lastEntry !== void 0 ? `${shortIdWithPrefix(firstEntry.sessionId)}..${shortIdWithPrefix(lastEntry.sessionId)}` : "";
4933
+ const language = input.language ?? await resolveViewLanguageFromPaths(input.paths);
4338
4934
  const body = formatHandoffBody({
4935
+ language,
4339
4936
  nowIso: input.nowIso,
4340
4937
  sessionRange,
4341
4938
  sessionCount: entries.length,
@@ -4365,6 +4962,7 @@ async function renderHandoff(input) {
4365
4962
  };
4366
4963
  }
4367
4964
  function formatHandoffBody(args) {
4965
+ const t = viewStrings(args.language);
4368
4966
  const lines = [];
4369
4967
  lines.push("# Handoff");
4370
4968
  lines.push("");
@@ -4374,32 +4972,32 @@ function formatHandoffBody(args) {
4374
4972
  lines.push(`> Generated at ${args.nowIso}`);
4375
4973
  }
4376
4974
  lines.push("");
4377
- lines.push("## \u73FE\u5728\u306E\u72B6\u614B");
4975
+ lines.push(t.handoff.headingCurrentState);
4378
4976
  lines.push("");
4379
4977
  if (args.latestSession !== void 0) {
4380
4978
  const status = args.latestSession.session.session.status;
4381
4979
  const label = args.latestSession.session.session.label;
4382
4980
  const shortId2 = shortIdWithPrefix(args.latestSession.sessionId);
4383
4981
  if (label !== void 0 && label !== "") {
4384
- lines.push(`- \u6700\u7D42 session: ${label} (${status}) [${shortId2}]`);
4982
+ lines.push(`- ${t.common.lastSessionLabel}: ${label} (${status}) [${shortId2}]`);
4385
4983
  } else {
4386
- lines.push(`- \u6700\u7D42 session: ${shortId2} (${status})`);
4984
+ lines.push(`- ${t.common.lastSessionLabel}: ${shortId2} (${status})`);
4387
4985
  }
4388
4986
  } else {
4389
- lines.push("- \u6700\u7D42 session: (no live sessions)");
4987
+ lines.push(`- ${t.common.lastSessionLabel}: (no live sessions)`);
4390
4988
  }
4391
4989
  if (args.latestActivityRecord !== void 0) {
4392
4990
  const statusLabel = args.latestTaskDoc !== void 0 ? args.latestTaskDoc.task.task.status : "status unknown \u2014 task.md missing or invalid";
4393
4991
  const linkedCount = args.latestTaskDoc?.task.task.linked_sessions?.length;
4394
4992
  const linkedSuffix = linkedCount !== void 0 && linkedCount > 1 ? `, linked_sessions: ${linkedCount}` : "";
4395
4993
  lines.push(
4396
- `- \u6700\u7D42 task: ${args.latestActivityRecord.title} (${statusLabel}${linkedSuffix}) [${shortIdWithPrefix(args.latestActivityRecord.taskId)}]`
4994
+ `- ${t.handoff.lastTaskLabel}: ${args.latestActivityRecord.title} (${statusLabel}${linkedSuffix}) [${shortIdWithPrefix(args.latestActivityRecord.taskId)}]`
4397
4995
  );
4398
4996
  } else {
4399
- lines.push("- \u6700\u7D42 task: (no tasks recorded yet)");
4997
+ lines.push(`- ${t.handoff.lastTaskLabel}: (no tasks recorded yet)`);
4400
4998
  }
4401
4999
  lines.push("");
4402
- lines.push("## \u76F4\u8FD1\u306E\u5909\u66F4\u30D5\u30A1\u30A4\u30EB");
5000
+ lines.push(t.handoff.headingRecentFiles);
4403
5001
  lines.push("");
4404
5002
  if (args.displayedFiles.length === 0) {
4405
5003
  lines.push("(no related files recorded)");
@@ -4408,7 +5006,7 @@ function formatHandoffBody(args) {
4408
5006
  if (args.overflow > 0) lines.push(`- ... +${args.overflow} more`);
4409
5007
  }
4410
5008
  lines.push("");
4411
- lines.push("## \u76F4\u8FD1\u306E\u5224\u65AD");
5009
+ lines.push(t.handoff.headingLatestDecision);
4412
5010
  lines.push("");
4413
5011
  if (args.latestDecision === void 0) {
4414
5012
  lines.push("(no decisions recorded yet)");
@@ -4416,14 +5014,10 @@ function formatHandoffBody(args) {
4416
5014
  const last = args.latestDecision;
4417
5015
  lines.push(`- ${last.title} [${shortIdWithPrefix(last.decisionId)}]`);
4418
5016
  if (args.latestActivityAt !== null && isTrailingStale(args.latestActivityAt, last.occurredAt)) {
4419
- lines.push(
4420
- " - \u6CE8: \u6700\u7D42\u6D3B\u52D5\u306F\u3053\u306E\u5224\u65AD\u3088\u308A\u5F8C\u3067\u3059\u3002\u4F1A\u8A71\u3067\u65E2\u306B\u89E3\u6C7A\u6E08\u307F\u306E\u53EF\u80FD\u6027\u304C\u3042\u308B\u305F\u3081\u3001\u518D\u958B\u524D\u306B\u7D99\u7D9A\u70B9\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044(\u4F1A\u8A71\u3067\u306E\u610F\u601D\u6C7A\u5B9A\u306F\u81EA\u52D5\u8A18\u9332\u3055\u308C\u307E\u305B\u3093\u3002`basou decision capture` \u3067\u8A18\u9332\u3067\u304D\u307E\u3059)\u3002"
4421
- );
5017
+ lines.push(` - ${t.handoff.decisionStaleNote}`);
4422
5018
  }
4423
5019
  if (args.latestSession !== void 0 && last.sessionId !== args.latestSession.sessionId) {
4424
- lines.push(
4425
- ` - \u6CE8: \u3053\u306E\u5224\u65AD\u306F\u6700\u7D42 session \u3068\u306F\u5225\u306E session [${shortIdWithPrefix(last.sessionId)}] \u306E\u3082\u306E\u3067\u3059\u3002`
4426
- );
5020
+ lines.push(` - ${t.common.decisionOtherSessionNote(shortIdWithPrefix(last.sessionId))}`);
4427
5021
  }
4428
5022
  lines.push("");
4429
5023
  lines.push(`(${args.decisions.length} decisions total \u2014 see decisions.md)`);
@@ -4433,20 +5027,20 @@ function formatHandoffBody(args) {
4433
5027
  const TRACK_DISPLAY_LIMIT = 10;
4434
5028
  const shown = args.openTracks.slice(0, TRACK_DISPLAY_LIMIT);
4435
5029
  const overflow = args.openTracks.length - shown.length;
4436
- lines.push("## \u672A\u5B8C\u30C8\u30E9\u30C3\u30AF (close \u307E\u3067\u7D99\u7D9A\u8868\u793A)");
5030
+ lines.push(t.handoff.headingOpenTracks);
4437
5031
  lines.push("");
4438
- for (const t of shown) {
4439
- lines.push(`- ${t.title} [${shortIdWithPrefix(t.decisionId)}]`);
4440
- if (t.rationale !== null && t.rationale.trim() !== "") {
4441
- lines.push(` - \u7406\u7531: ${handoffRationale(t.rationale)}`);
5032
+ for (const track of shown) {
5033
+ lines.push(`- ${track.title} [${shortIdWithPrefix(track.decisionId)}]`);
5034
+ if (track.rationale !== null && track.rationale.trim() !== "") {
5035
+ lines.push(` - ${t.common.trackWhyLabel}: ${handoffRationale(track.rationale)}`);
4442
5036
  }
4443
5037
  }
4444
5038
  if (overflow > 0) lines.push(`- ... +${overflow} more (see decisions.md)`);
4445
5039
  lines.push("");
4446
- lines.push("\u5B8C\u4E86\u3057\u305F\u3089 `basou decision void <decision_id>` \u3067\u9589\u3058\u3066\u304F\u3060\u3055\u3044\u3002");
5040
+ lines.push(t.handoff.trackCloseInstruction);
4447
5041
  lines.push("");
4448
5042
  }
4449
- lines.push("## \u672A\u6C7A\u4E8B\u9805");
5043
+ lines.push(t.handoff.headingUnresolved);
4450
5044
  lines.push("");
4451
5045
  if (args.pendingApprovalsCount > 0) {
4452
5046
  lines.push(`- ${args.pendingApprovalsCount} pending approvals`);
@@ -4458,19 +5052,19 @@ function formatHandoffBody(args) {
4458
5052
  lines.push("(none)");
4459
5053
  }
4460
5054
  lines.push("");
4461
- lines.push("## \u6B21\u306B\u8AAD\u3080\u3079\u304D\u30D5\u30A1\u30A4\u30EB");
5055
+ lines.push(t.handoff.headingReadNext);
4462
5056
  lines.push("");
4463
5057
  lines.push("- .basou/decisions.md");
4464
5058
  for (const f of args.displayedFiles.slice(0, 3)) lines.push(`- ${f}`);
4465
5059
  lines.push("");
4466
- lines.push("## \u6B21\u306B\u5B9F\u884C\u3059\u3079\u304D\u4F5C\u696D");
5060
+ lines.push(t.handoff.headingNextWork);
4467
5061
  lines.push("");
4468
5062
  if (args.pendingTasks.length === 0) {
4469
5063
  lines.push("(no pending tasks)");
4470
5064
  } else {
4471
- for (const t of args.pendingTasks) {
5065
+ for (const t2 of args.pendingTasks) {
4472
5066
  lines.push(
4473
- `- ${t.task.task.title} (${t.task.task.status}) [${shortIdWithPrefix(t.task.task.id)}]`
5067
+ `- ${t2.task.task.title} (${t2.task.task.status}) [${shortIdWithPrefix(t2.task.task.id)}]`
4474
5068
  );
4475
5069
  }
4476
5070
  }
@@ -4479,7 +5073,7 @@ function formatHandoffBody(args) {
4479
5073
  const importedTableEntries = args.entries.filter(
4480
5074
  (e) => e.session.session.source.kind === "import"
4481
5075
  );
4482
- lines.push("## \u30BB\u30C3\u30B7\u30E7\u30F3\u4E00\u89A7");
5076
+ lines.push(t.handoff.headingSessions);
4483
5077
  lines.push("");
4484
5078
  if (args.entries.length === 0) {
4485
5079
  lines.push("(no sessions yet)");
@@ -4728,157 +5322,6 @@ async function classifyFilesBySourceRoot(input) {
4728
5322
 
4729
5323
  // src/orientation/orientation-renderer.ts
4730
5324
  import { dirname as dirname4, join as join15 } from "path";
4731
-
4732
- // src/storage/manifest.ts
4733
- import { lstat as lstat3 } from "fs/promises";
4734
-
4735
- // src/schemas/manifest.schema.ts
4736
- import { z as z9 } from "zod";
4737
- var ProjectSchema = z9.looseObject({
4738
- name: z9.string().optional(),
4739
- description: z9.string().optional()
4740
- });
4741
- var CapabilitiesSchema = z9.looseObject({
4742
- enabled: z9.array(z9.string())
4743
- });
4744
- var ApprovalConfigSchema = z9.looseObject({
4745
- required_for: z9.array(z9.string()).optional(),
4746
- default_risk_level: z9.enum(["low", "medium", "high", "critical"])
4747
- });
4748
- var ClaudeCodeAdapterConfigSchema = z9.looseObject({
4749
- enabled: z9.boolean(),
4750
- config_path: z9.string().optional()
4751
- });
4752
- var AdaptersSchema = z9.looseObject({
4753
- "claude-code": ClaudeCodeAdapterConfigSchema
4754
- });
4755
- var GitConfigSchema = z9.looseObject({
4756
- events_log: z9.enum(["ignore", "commit"]).default("ignore")
4757
- });
4758
- var SOURCE_ROOT_PATTERN = /^(?![~/\\])(?![A-Za-z]:)(?!\s)[^\0\\]*[^\0\\\s]$/;
4759
- var SourceRootSchema = z9.string().min(1).regex(SOURCE_ROOT_PATTERN, {
4760
- message: "source_roots entries must be relative paths (no absolute path, '~', '\\', or null byte)"
4761
- });
4762
- var ImportConfigSchema = z9.looseObject({
4763
- source_roots: z9.array(SourceRootSchema).min(1).optional()
4764
- });
4765
- var RepoVisibilitySchema = z9.enum(["public", "private", "future-public"]);
4766
- var RepoLanguageSchema = z9.enum(["en", "ja", "en+ja"]);
4767
- var PublishKindSchema = z9.enum(["web", "npm"]);
4768
- var RepoInstructionsSchema = z9.enum(["hub", "self"]);
4769
- var PublishTargetSchema = z9.looseObject({
4770
- kind: PublishKindSchema,
4771
- visibility: RepoVisibilitySchema.optional(),
4772
- language: RepoLanguageSchema.optional()
4773
- });
4774
- var RepoEntrySchema = z9.looseObject({
4775
- path: SourceRootSchema,
4776
- visibility: RepoVisibilitySchema.optional(),
4777
- language: RepoLanguageSchema.optional(),
4778
- publishes: z9.array(PublishTargetSchema).optional(),
4779
- instructions: RepoInstructionsSchema.optional()
4780
- });
4781
- var WorkspaceMetaSchema = z9.looseObject({
4782
- id: WorkspaceIdSchema,
4783
- name: z9.string().min(1),
4784
- created_at: IsoTimestampSchema,
4785
- updated_at: IsoTimestampSchema,
4786
- /**
4787
- * The generated workspace view: a throwaway directory that aggregates the
4788
- * roster repos via symlinks (one `<repo-basename>` symlink per repo). A path
4789
- * relative to the manifest root, reusing the machine-portable source-root
4790
- * constraint. Absent for a solo project (no view needed); `basou project
4791
- * workspace` reconciles the view's symlinks to the declared roster.
4792
- */
4793
- view: SourceRootSchema.optional()
4794
- });
4795
- var ManifestSchema = z9.looseObject({
4796
- schema_version: SchemaVersionSchema,
4797
- // Same forward-compatible format gate as schema_version (accept 0.x.y, gate a
4798
- // higher major with an upgrade error) rather than a hard literal. `basou_version`
4799
- // is a format stamp, not the npm/product version; consolidating it with
4800
- // schema_version is a candidate cleanup for the M4 freeze pass.
4801
- basou_version: SchemaVersionSchema,
4802
- workspace: WorkspaceMetaSchema,
4803
- project: ProjectSchema,
4804
- capabilities: CapabilitiesSchema,
4805
- approval: ApprovalConfigSchema,
4806
- adapters: AdaptersSchema,
4807
- git: GitConfigSchema,
4808
- import: ImportConfigSchema.optional(),
4809
- repos: z9.array(RepoEntrySchema).min(1).optional()
4810
- });
4811
- var KNOWN_TOP_LEVEL_KEYS = new Set(Object.keys(ManifestSchema.shape));
4812
- function unknownManifestKeys(manifest) {
4813
- return Object.keys(manifest).filter((k) => !KNOWN_TOP_LEVEL_KEYS.has(k)).sort();
4814
- }
4815
-
4816
- // src/storage/manifest.ts
4817
- function createManifest(input) {
4818
- if (input.workspaceName.length === 0) {
4819
- throw new Error("Workspace name is empty. Pass --name explicitly.");
4820
- }
4821
- const now = (input.now ?? /* @__PURE__ */ new Date()).toISOString();
4822
- const workspaceId = input.workspaceId ?? prefixedUlid("ws");
4823
- const project = {
4824
- ...input.projectName !== void 0 ? { name: input.projectName } : {},
4825
- ...input.projectDescription !== void 0 ? { description: input.projectDescription } : {}
4826
- };
4827
- const manifest = {
4828
- schema_version: "0.1.0",
4829
- basou_version: "0.1.0",
4830
- workspace: {
4831
- id: workspaceId,
4832
- name: input.workspaceName,
4833
- created_at: now,
4834
- updated_at: now
4835
- },
4836
- project,
4837
- capabilities: {
4838
- enabled: ["core", "claude-code-adapter", "terminal-recording", "git-capability", "approval"]
4839
- },
4840
- approval: {
4841
- required_for: ["destructive_command", "external_send"],
4842
- default_risk_level: "medium"
4843
- },
4844
- adapters: {
4845
- "claude-code": { enabled: true }
4846
- },
4847
- git: { events_log: "ignore" },
4848
- ...input.sourceRoots !== void 0 && input.sourceRoots.length > 0 ? { import: { source_roots: input.sourceRoots } } : {}
4849
- };
4850
- return ManifestSchema.parse(manifest);
4851
- }
4852
- async function writeManifest(paths, manifest, options) {
4853
- const force = options?.force === true;
4854
- const validated = ManifestSchema.parse(manifest);
4855
- delete validated.project.repository_url;
4856
- if (!force) {
4857
- let existed = false;
4858
- try {
4859
- await lstat3(paths.files.manifest);
4860
- existed = true;
4861
- } catch (error) {
4862
- if (!hasErrorCode3(error) || error.code !== "ENOENT") {
4863
- throw new Error("Failed to inspect existing manifest", { cause: error });
4864
- }
4865
- }
4866
- if (existed) {
4867
- throw new Error("Already initialized. Use --force to overwrite.");
4868
- }
4869
- }
4870
- await writeYamlFile(paths.files.manifest, validated);
4871
- }
4872
- async function readManifest(paths) {
4873
- const raw = await readYamlFile(paths.files.manifest);
4874
- return ManifestSchema.parse(raw);
4875
- }
4876
- function hasErrorCode3(error) {
4877
- if (!(error instanceof Error)) return false;
4878
- return typeof error.code === "string";
4879
- }
4880
-
4881
- // src/orientation/orientation-renderer.ts
4882
5325
  async function summarizeOrientation(input) {
4883
5326
  const limit = input.relatedFilesLimit ?? 10;
4884
5327
  const now = new Date(input.nowIso);
@@ -5111,10 +5554,12 @@ async function summarizeOrientation(input) {
5111
5554
  }
5112
5555
  async function renderOrientation(input) {
5113
5556
  const summary = await summarizeOrientation(input);
5557
+ const language = input.language ?? await resolveViewLanguageFromPaths(input.paths);
5114
5558
  return {
5115
5559
  body: formatOrientationBody(summary, {
5116
5560
  staleness: input.staleness ?? null,
5117
- verbose: input.verbose === true
5561
+ verbose: input.verbose === true,
5562
+ language
5118
5563
  }),
5119
5564
  sessionCount: summary.sessionCount,
5120
5565
  pendingApprovalsCount: summary.pendingApprovals.length,
@@ -5125,6 +5570,7 @@ async function renderOrientation(input) {
5125
5570
  };
5126
5571
  }
5127
5572
  function formatOrientationBody(summary, opts) {
5573
+ const t = viewStrings(opts.language);
5128
5574
  const lines = [];
5129
5575
  const now = new Date(summary.generatedAt);
5130
5576
  const newestRel = relativeAge(summary.freshness.newestStartedAt ?? void 0, now);
@@ -5138,100 +5584,100 @@ function formatOrientationBody(summary, opts) {
5138
5584
  lines.push(`> hosts: local, ${summary.hosts.join(", ")}`);
5139
5585
  }
5140
5586
  lines.push("");
5141
- const banner = stalenessBanner(opts.staleness);
5587
+ const banner = stalenessBanner(opts.staleness, t);
5142
5588
  if (banner.length > 0) {
5143
5589
  for (const line of banner) lines.push(line);
5144
5590
  lines.push("");
5145
5591
  }
5146
- lines.push("## \u4ECA\u3069\u3053\u306B\u3044\u308B");
5592
+ lines.push(t.orientation.headingWhere);
5147
5593
  lines.push("");
5148
5594
  if (summary.latestSession !== null) {
5149
5595
  const s = summary.latestSession;
5150
5596
  const sid = shortId(s.sessionId);
5151
5597
  if (s.label !== null && s.label !== "") {
5152
- lines.push(`- \u6700\u7D42 session: ${s.label} (${s.status}) [${sid}]${hostSuffix(s.host)}`);
5598
+ lines.push(
5599
+ `- ${t.common.lastSessionLabel}: ${s.label} (${s.status}) [${sid}]${hostSuffix(s.host)}`
5600
+ );
5153
5601
  } else {
5154
- lines.push(`- \u6700\u7D42 session: ${sid} (${s.status})${hostSuffix(s.host)}`);
5602
+ lines.push(`- ${t.common.lastSessionLabel}: ${sid} (${s.status})${hostSuffix(s.host)}`);
5155
5603
  }
5156
5604
  } else {
5157
- lines.push("- \u6700\u7D42 session: (no live sessions)");
5605
+ lines.push(`- ${t.common.lastSessionLabel}: (no live sessions)`);
5158
5606
  }
5159
5607
  if (summary.latestDecision !== null) {
5160
5608
  const dec = summary.latestDecision;
5161
- const decAge = relativeAgeJa(dec.occurredAt, now);
5609
+ const decAge = t.relativeAge(dec.occurredAt, now);
5162
5610
  lines.push(
5163
- `- \u76F4\u8FD1\u306E\u5224\u65AD: ${dec.title} [${shortId(dec.decisionId)}] (${decAge})${hostSuffix(dec.host)}`
5611
+ `- ${t.common.latestDecisionLabel}: ${dec.title} [${shortId(dec.decisionId)}] (${decAge})${hostSuffix(dec.host)}`
5164
5612
  );
5165
5613
  const activityAt = summary.freshness.latestActivityAt;
5166
5614
  if (activityAt !== null && isTrailingStale(activityAt, dec.occurredAt)) {
5167
- lines.push(
5168
- ` - \u6CE8: \u3053\u308C\u306F\u6700\u5F8C\u306B\u300C\u8A18\u9332\u3055\u308C\u305F\u300D\u5224\u65AD\u3067\u3059\u3002\u6700\u7D42\u6D3B\u52D5 (${relativeAgeJa(activityAt, now)}) \u306F\u3053\u308C\u3088\u308A\u5F8C\u306E\u305F\u3081\u3001\u73FE\u5728\u306E\u65B9\u91DD\u304C\u53CD\u6620\u3055\u308C\u3066\u3044\u306A\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059(\u4F1A\u8A71\u3067\u306E\u610F\u601D\u6C7A\u5B9A\u306F\u81EA\u52D5\u8A18\u9332\u3055\u308C\u307E\u305B\u3093\u3002\`basou decision capture\` \u3067\u3053\u306E session \u306E\u5224\u65AD\u3092\u8A18\u9332\u3067\u304D\u307E\u3059)\u3002`
5169
- );
5615
+ lines.push(` - ${t.orientation.decisionStaleNote(t.relativeAge(activityAt, now))}`);
5170
5616
  }
5171
5617
  if (summary.latestSession !== null && dec.sessionId !== summary.latestSession.sessionId) {
5172
- lines.push(
5173
- ` - \u6CE8: \u3053\u306E\u5224\u65AD\u306F\u6700\u7D42 session \u3068\u306F\u5225\u306E session [${shortId(dec.sessionId)}] \u306E\u3082\u306E\u3067\u3059\u3002`
5174
- );
5618
+ lines.push(` - ${t.common.decisionOtherSessionNote(shortId(dec.sessionId))}`);
5175
5619
  }
5176
5620
  if (summary.decisionCount > 1) {
5177
5621
  lines.push(` - ${summary.decisionCount} decisions total \u2014 see decisions.md`);
5178
5622
  }
5179
5623
  } else {
5180
- lines.push("- \u76F4\u8FD1\u306E\u5224\u65AD: (no decisions recorded yet; capture with `basou decision capture`)");
5624
+ lines.push(
5625
+ `- ${t.common.latestDecisionLabel}: (no decisions recorded yet; capture with \`basou decision capture\`)`
5626
+ );
5181
5627
  }
5182
5628
  if (summary.relatedFiles.displayed.length > 0) {
5183
5629
  const shown = summary.relatedFiles.displayed.join(", ");
5184
5630
  const more = summary.relatedFiles.overflow > 0 ? ` (... +${summary.relatedFiles.overflow} more)` : "";
5185
- lines.push(`- \u76F4\u8FD1\u306E\u5909\u66F4\u30D5\u30A1\u30A4\u30EB: ${shown}${more}`);
5631
+ lines.push(`- ${t.common.recentFilesLabel}: ${shown}${more}`);
5186
5632
  if (summary.relatedFiles.outOfRoot.length > 0) {
5187
5633
  const OUT_OF_ROOT_DISPLAY = 10;
5188
5634
  const out = summary.relatedFiles.outOfRoot;
5189
5635
  const shownOut = out.slice(0, OUT_OF_ROOT_DISPLAY).join(", ");
5190
5636
  const outMore = out.length > OUT_OF_ROOT_DISPLAY ? ` (... +${out.length - OUT_OF_ROOT_DISPLAY} more)` : "";
5191
- lines.push(
5192
- ` - \u26A0 source_roots \u5916 ${out.length} \u4EF6 (\u5225\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u53EF\u80FD\u6027): ${shownOut}${outMore}`
5193
- );
5637
+ lines.push(` - ${t.orientation.outOfRootWarning(out.length, `${shownOut}${outMore}`)}`);
5194
5638
  }
5195
5639
  } else {
5196
- lines.push("- \u76F4\u8FD1\u306E\u5909\u66F4\u30D5\u30A1\u30A4\u30EB: (none recorded)");
5640
+ lines.push(`- ${t.common.recentFilesLabel}: (none recorded)`);
5197
5641
  }
5198
5642
  lines.push("");
5199
- lines.push(`## \u6700\u8FD1\u306E\u6D41\u308C (\u76F4\u8FD1 ${RECENT_DIRECTION_SESSIONS} session)`);
5643
+ lines.push(t.orientation.headingRecent(RECENT_DIRECTION_SESSIONS));
5200
5644
  lines.push("");
5201
5645
  if (summary.recentDirection.length === 0) {
5202
- lines.push("- (\u307E\u3060\u8A18\u9332\u304C\u3042\u308A\u307E\u305B\u3093)");
5646
+ lines.push(`- ${t.orientation.recentEmpty}`);
5203
5647
  } else {
5204
5648
  for (const s of summary.recentDirection) {
5205
5649
  const sid = shortId(s.sessionId);
5206
- const age = relativeAgeJa(s.occurredAt, now);
5650
+ const age = t.relativeAge(s.occurredAt, now);
5207
5651
  const head = s.label !== null && s.label !== "" ? s.label : sid;
5208
5652
  lines.push(`- ${head} (${age})${hostSuffix(s.host)}`);
5209
5653
  if (s.decisions.length > 0) {
5210
5654
  const more = s.decisionsOverflow > 0 ? ` (+${s.decisionsOverflow})` : "";
5211
- lines.push(` - \u5224\u65AD: ${s.decisions.map(noteSummary).join("; ")}${more}`);
5655
+ lines.push(
5656
+ ` - ${t.orientation.recentDecisionsLabel}: ${s.decisions.map(noteSummary).join("; ")}${more}`
5657
+ );
5212
5658
  }
5213
5659
  for (const note of s.notes) {
5214
- lines.push(` - \u6B21\u306E\u8D77\u70B9: ${noteSummary(note)}`);
5660
+ lines.push(` - ${t.orientation.recentNextStepLabel}: ${noteSummary(note)}`);
5215
5661
  }
5216
5662
  if (s.files.length > 0) {
5217
- lines.push(` - \u5909\u66F4: ${s.files.join(", ")}`);
5663
+ lines.push(` - ${t.orientation.recentChangedLabel}: ${s.files.join(", ")}`);
5218
5664
  }
5219
5665
  }
5220
5666
  }
5221
5667
  lines.push("");
5222
- lines.push("## \u4F55\u304C\u52D5\u304F");
5668
+ lines.push(t.orientation.headingInFlight);
5223
5669
  lines.push("");
5224
- lines.push(`### \u9032\u884C\u4E2D task (${summary.inFlightTasks.length})`);
5670
+ lines.push(t.orientation.inFlightTasksHeading(summary.inFlightTasks.length));
5225
5671
  if (summary.inFlightTasks.length === 0) {
5226
5672
  lines.push("- (none)");
5227
5673
  } else {
5228
- for (const t of summary.inFlightTasks) {
5229
- const linkedSuffix = t.linkedSessions > 1 ? ` \u2014 linked_sessions: ${t.linkedSessions}` : "";
5230
- lines.push(`- ${t.title} (${t.status}) [${shortId(t.id)}]${linkedSuffix}`);
5674
+ for (const t2 of summary.inFlightTasks) {
5675
+ const linkedSuffix = t2.linkedSessions > 1 ? ` \u2014 linked_sessions: ${t2.linkedSessions}` : "";
5676
+ lines.push(`- ${t2.title} (${t2.status}) [${shortId(t2.id)}]${linkedSuffix}`);
5231
5677
  }
5232
5678
  }
5233
5679
  lines.push("");
5234
- lines.push(`### \u627F\u8A8D\u5F85\u3061 (${summary.pendingApprovals.length})`);
5680
+ lines.push(t.orientation.pendingApprovalsHeading(summary.pendingApprovals.length));
5235
5681
  if (summary.pendingApprovals.length === 0) {
5236
5682
  lines.push("- (none)");
5237
5683
  } else {
@@ -5243,7 +5689,7 @@ function formatOrientationBody(summary, opts) {
5243
5689
  }
5244
5690
  }
5245
5691
  lines.push("");
5246
- lines.push(`### \u8981\u6CE8\u610F session (${summary.suspects.length})`);
5692
+ lines.push(t.orientation.suspectSessionsHeading(summary.suspects.length));
5247
5693
  if (summary.suspects.length === 0) {
5248
5694
  lines.push("- (none)");
5249
5695
  } else {
@@ -5254,71 +5700,63 @@ function formatOrientationBody(summary, opts) {
5254
5700
  }
5255
5701
  }
5256
5702
  lines.push("");
5257
- lines.push("## \u3069\u3053\u3078\u5411\u304B\u3046");
5703
+ lines.push(t.orientation.headingForward);
5258
5704
  lines.push("");
5259
5705
  if (summary.openTracks.length > 0) {
5260
5706
  const TRACK_DISPLAY_LIMIT = 10;
5261
5707
  const shownTracks = summary.openTracks.slice(0, TRACK_DISPLAY_LIMIT);
5262
5708
  const trackOverflow = summary.openTracks.length - shownTracks.length;
5263
- lines.push(`### \u672A\u5B8C\u30C8\u30E9\u30C3\u30AF (close \u307E\u3067\u7D99\u7D9A\u8868\u793A) (${summary.openTracks.length})`);
5264
- for (const t of shownTracks) {
5265
- const trackAge = relativeAgeJa(t.occurredAt, now);
5266
- lines.push(`- ${t.title} [${shortId(t.decisionId)}] (${trackAge})${hostSuffix(t.host)}`);
5267
- if (t.rationale !== null && t.rationale.trim() !== "") {
5268
- lines.push(` - \u7406\u7531: ${trackRationale(t.rationale)}`);
5709
+ lines.push(t.orientation.openTracksHeading(summary.openTracks.length));
5710
+ for (const track of shownTracks) {
5711
+ const trackAge = t.relativeAge(track.occurredAt, now);
5712
+ lines.push(
5713
+ `- ${track.title} [${shortId(track.decisionId)}] (${trackAge})${hostSuffix(track.host)}`
5714
+ );
5715
+ if (track.rationale !== null && track.rationale.trim() !== "") {
5716
+ lines.push(` - ${t.common.trackWhyLabel}: ${trackRationale(track.rationale)}`);
5269
5717
  }
5270
5718
  }
5271
5719
  if (trackOverflow > 0) {
5272
5720
  lines.push(`- ... +${trackOverflow} more (see decisions.md)`);
5273
5721
  }
5274
- lines.push(
5275
- "\u5B8C\u4E86\u3057\u305F\u3089 `basou decision void <decision_id>` \u3067\u9589\u3058\u3066\u304F\u3060\u3055\u3044\u3002\u9589\u3058\u308B\u307E\u3067\u6BCE\u56DE\u3053\u3053\u306B\u8868\u793A\u3055\u308C\u307E\u3059\u3002"
5276
- );
5722
+ lines.push(t.orientation.trackCloseInstruction);
5277
5723
  lines.push("");
5278
5724
  }
5279
5725
  if (summary.latestNote !== null) {
5280
- const noteAge = relativeAgeJa(summary.latestNote.occurredAt, now);
5726
+ const noteAge = t.relativeAge(summary.latestNote.occurredAt, now);
5281
5727
  lines.push(
5282
- `- \u6B21\u306E\u8D77\u70B9 (\u8A18\u9332\u6E08\u307F, ${noteAge}): ${noteSummary(summary.latestNote.body)} [session ${shortId(summary.latestNote.sessionId)}]${hostSuffix(summary.latestNote.host)}`
5728
+ `- ${t.orientation.nextStepRecordedLabel(noteAge)}: ${noteSummary(summary.latestNote.body)} [session ${shortId(summary.latestNote.sessionId)}]${hostSuffix(summary.latestNote.host)}`
5283
5729
  );
5284
5730
  const activityAt = summary.freshness.latestActivityAt;
5285
5731
  if (activityAt !== null && isTrailingStale(activityAt, summary.latestNote.occurredAt)) {
5286
- lines.push(
5287
- ` - \u6CE8: \u3053\u306E\u8D77\u70B9\u306E\u8A18\u9332\u5F8C (\u6700\u7D42\u6D3B\u52D5 ${relativeAgeJa(activityAt, now)}) \u3082\u4F5C\u696D\u304C\u7D9A\u3044\u3066\u3044\u307E\u3059\u3002\u518D\u958B\u70B9\u304C\u53E4\u3044\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002`
5288
- );
5732
+ lines.push(` - ${t.orientation.noteStaleNote(t.relativeAge(activityAt, now))}`);
5289
5733
  }
5290
5734
  }
5291
- for (const t of summary.plannedTasks) {
5292
- lines.push(`- ${t.title} [${shortId(t.id)}]`);
5735
+ for (const task of summary.plannedTasks) {
5736
+ lines.push(`- ${task.title} [${shortId(task.id)}]`);
5293
5737
  }
5294
5738
  if (summary.openTracks.length === 0 && summary.latestNote === null && summary.plannedTasks.length === 0) {
5295
5739
  const dec = summary.latestDecision;
5296
5740
  if (dec === null) {
5297
5741
  lines.push("- (no planned tasks or recorded next step yet)");
5298
5742
  } else if (isTrailingStale(summary.freshness.latestActivityAt, dec.occurredAt)) {
5299
- lines.push(
5300
- "- (no planned tasks or recorded next step \u2014 \u6700\u7D42\u6D3B\u52D5\u306F\u76F4\u8FD1\u306E\u5224\u65AD\u3088\u308A\u5F8C\u3067\u3059\u3002\u7D99\u7D9A\u70B9\u3092\u30E6\u30FC\u30B6\u306B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044)"
5301
- );
5302
- lines.push(` - \u53C2\u8003 (\u53E4\u3044\u53EF\u80FD\u6027\u30FB\u65B9\u91DD\u3067\u306F\u306A\u3044): ${dec.title}`);
5743
+ lines.push(t.orientation.fallbackStaleDirection);
5744
+ lines.push(` - ${t.orientation.fallbackStaleReferenceLabel}: ${dec.title}`);
5303
5745
  } else {
5304
5746
  lines.push("- (no planned tasks \u2014 direction is inferred from recent decisions)");
5305
- lines.push(` - \u76F4\u8FD1\u306E\u5224\u65AD: ${dec.title}`);
5747
+ lines.push(` - ${t.common.latestDecisionLabel}: ${dec.title}`);
5306
5748
  }
5307
5749
  if (dec !== null) {
5308
- lines.push(
5309
- ' - \u6B21\u306B\u4F5C\u308B\u3079\u304D\u672C\u8CEA\u7684\u306A\u65B9\u5411\u6027\u304C\u5B9A\u307E\u3063\u305F\u3089 `basou decision capture` (`"kind":"track"`) / `basou decision record --track` \u3067 track \u5316\u3059\u308B\u3068\u3001close \u307E\u3067\u6BCE session \u3053\u3053\u306B\u7D99\u7D9A\u8868\u793A\u3055\u308C\u307E\u3059\u3002'
5310
- );
5750
+ lines.push(` - ${t.orientation.trackNudge}`);
5311
5751
  }
5312
5752
  }
5313
5753
  lines.push("");
5314
- lines.push("## \u3053\u308C\u306F\u6700\u65B0\u304B");
5754
+ lines.push(t.orientation.headingCurrency);
5315
5755
  lines.push("");
5316
- for (const line of freshnessVerdict(summary, opts.staleness, now)) lines.push(line);
5756
+ for (const line of freshnessVerdict(summary, opts.staleness, now, t)) lines.push(line);
5317
5757
  if (summary.hosts.length > 0) {
5318
5758
  lines.push("");
5319
- lines.push(
5320
- "\u6CE8: \u9BAE\u5EA6\u5224\u5B9A\u306F\u3053\u306E\u30DE\u30B7\u30F3\u306E\u30ED\u30FC\u30AB\u30EB\u30B9\u30C8\u30A2\u306E\u307F\u304C\u5BFE\u8C61\u3067\u3059\u3002\u4ED6\u30DB\u30B9\u30C8\u306E\u53D6\u308A\u3053\u307C\u3057\u306F\u5224\u5B9A\u3067\u304D\u307E\u305B\u3093(\u5404\u30DB\u30B9\u30C8\u3067 basou refresh \u3092\u5B9F\u884C\u3057\u540C\u671F\u3057\u3066\u304F\u3060\u3055\u3044)\u3002"
5321
- );
5759
+ lines.push(t.orientation.federatedFreshnessNote);
5322
5760
  }
5323
5761
  if (opts.verbose) {
5324
5762
  lines.push("");
@@ -5348,7 +5786,7 @@ function formatOrientationBody(summary, opts) {
5348
5786
  }
5349
5787
  return lines.join("\n");
5350
5788
  }
5351
- function toolDisplayName(kind) {
5789
+ function toolDisplayName(kind, t) {
5352
5790
  switch (kind) {
5353
5791
  case "claude-code-import":
5354
5792
  case "claude-code-adapter":
@@ -5356,98 +5794,61 @@ function toolDisplayName(kind) {
5356
5794
  case "codex-import":
5357
5795
  return "Codex";
5358
5796
  case "terminal":
5359
- return "\u30BF\u30FC\u30DF\u30CA\u30EB";
5797
+ return t.orientation.toolTerminal;
5360
5798
  case "human":
5361
- return "\u624B\u52D5\u30E1\u30E2";
5799
+ return t.orientation.toolHuman;
5362
5800
  case "import":
5363
- return "\u4ED6\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9";
5801
+ return t.orientation.toolImport;
5364
5802
  default:
5365
- return kind ?? "\u4E0D\u660E";
5803
+ return kind ?? t.orientation.toolUnknown;
5366
5804
  }
5367
5805
  }
5368
- function stalenessBanner(staleness) {
5806
+ function stalenessBanner(staleness, t) {
5369
5807
  if (staleness === null) return [];
5370
5808
  if ((staleness.unverifiableSessions ?? 0) > 0) {
5371
- return [
5372
- `> \u26A0\uFE0F **\u518D\u53D6\u308A\u8FBC\u307F\u304C\u5FC5\u8981** \u2014 native \u30ED\u30B0\u304C\u5909\u5316\u3057\u305F\u304C\u901A\u5E38\u306E refresh \u3067\u306F\u53D6\u308A\u8FBC\u3081\u306A\u3044\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${staleness.unverifiableSessions} \u4EF6\u3042\u308A\u307E\u3059\u3002\`basou refresh --force\` \u3067\u518D\u53D6\u308A\u8FBC\u307F\u3057\u3066\u304F\u3060\u3055\u3044(\u8A73\u7D30\u306F\u672B\u5C3E\u300C\u3053\u308C\u306F\u6700\u65B0\u304B\u300D)\u3002`
5373
- ];
5809
+ return [t.orientation.bannerUnverifiable(staleness.unverifiableSessions ?? 0)];
5374
5810
  }
5375
5811
  if (staleness.newSessions > 0) {
5376
- const parts = [`\u65B0\u898F ${staleness.newSessions} \u4EF6`];
5377
- if (staleness.updatedSessions > 0) parts.push(`\u66F4\u65B0 ${staleness.updatedSessions} \u4EF6`);
5378
- return [
5379
- `> \u26A0\uFE0F **\u53E4\u3044\u3067\u3059\uFF08\u672A\u53D6\u308A\u8FBC\u307F ${parts.join("\u30FB")}\uFF09** \u2014 \u7740\u624B\u524D\u306B\u5FC5\u305A \`basou refresh\` \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044(\u8A73\u7D30\u306F\u672B\u5C3E\u300C\u3053\u308C\u306F\u6700\u65B0\u304B\u300D)\u3002`
5380
- ];
5812
+ const parts = [t.orientation.partNew(staleness.newSessions)];
5813
+ if (staleness.updatedSessions > 0)
5814
+ parts.push(t.orientation.partUpdated(staleness.updatedSessions));
5815
+ return [t.orientation.bannerStale(parts.join(t.orientation.partsJoiner))];
5381
5816
  }
5382
5817
  return [];
5383
5818
  }
5384
- function freshnessVerdict(summary, staleness, now) {
5819
+ function freshnessVerdict(summary, staleness, now, t) {
5385
5820
  if (staleness !== null && (staleness.unverifiableSessions ?? 0) > 0) {
5386
- return [
5387
- `\u26A0\uFE0F native \u30ED\u30B0\u304C\u5909\u5316\u3057\u307E\u3057\u305F\u304C\u3001\u901A\u5E38\u306E \`basou refresh\` \u3067\u306F\u5B89\u5168\u306B\u518D\u53D6\u308A\u8FBC\u307F\u3067\u304D\u306A\u3044\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${staleness.unverifiableSessions} \u4EF6\u3042\u308A\u307E\u3059(\u975E\u8FFD\u8A18\u5909\u66F4\u30FB\u524D\u30C1\u30A7\u30FC\u30F3\u4E0D\u6574\u5408\u306A\u3069)\u3002`,
5388
- "`basou refresh --force` \u3067\u518D\u53D6\u308A\u8FBC\u307F\u3057\u3066\u304F\u3060\u3055\u3044\u3002(`basou verify` \u306F\u5225\u7269=\u53D6\u308A\u8FBC\u307F\u6E08\u307F\u30C7\u30FC\u30BF\u306E\u6539\u7AC4/\u7834\u640D\u691C\u67FB\u3067\u3001\u30D8\u30C3\u30C0\u306E suspect \u3068\u306F\u5225\u8EF8\u3067\u3059\u3002verify \u304C clean \u3067\u3082\u672A\u53D6\u308A\u8FBC\u307F\u306F\u6B8B\u308A\u5F97\u307E\u3059\u3002)"
5389
- ];
5821
+ return [...t.orientation.verdictUnverifiable(staleness.unverifiableSessions ?? 0)];
5390
5822
  }
5391
5823
  if (staleness !== null && staleness.newSessions > 0) {
5392
- const parts = [`\u65B0\u898F ${staleness.newSessions} \u4EF6`];
5393
- if (staleness.updatedSessions > 0) parts.push(`\u66F4\u65B0 ${staleness.updatedSessions} \u4EF6`);
5394
- return [
5395
- `\u26A0\uFE0F \u53E4\u3044\u3067\u3059\u3002\u6700\u5F8C\u306E\u53D6\u308A\u8FBC\u307F\u4EE5\u964D\u306B\u672A\u53D6\u308A\u8FBC\u307F\u306E\u4F5C\u696D\u304C\u3042\u308A\u307E\u3059(${parts.join("\u30FB")})\u3002`,
5396
- "\u7740\u624B\u524D\u306B\u5FC5\u305A `basou refresh` \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
5397
- ];
5824
+ const parts = [t.orientation.partNew(staleness.newSessions)];
5825
+ if (staleness.updatedSessions > 0)
5826
+ parts.push(t.orientation.partUpdated(staleness.updatedSessions));
5827
+ return [...t.orientation.verdictStale(parts.join(t.orientation.partsJoiner))];
5398
5828
  }
5399
5829
  if (staleness !== null && staleness.updatedSessions > 0) {
5400
- const lines2 = [
5401
- `\u26A0\uFE0F \u66F4\u65B0\u3055\u308C\u305F\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${staleness.updatedSessions} \u4EF6\u3042\u308A\u307E\u3059\u3002\`basou refresh\` \u3067\u53D6\u308A\u8FBC\u3081\u307E\u3059\u3002`,
5402
- "(\u9032\u884C\u4E2D\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u304C\u3042\u308B\u5834\u5408\u3001\u305D\u308C\u81EA\u8EAB\u306F\u53D6\u308A\u8FBC\u307F\u5F8C\u3082\u5897\u3048\u7D9A\u3051\u308B\u305F\u3081\u6B8B\u308A\u307E\u3059\uFF1D\u6B63\u5E38\u3067\u3059\u3002)"
5403
- ];
5830
+ const lines2 = [...t.orientation.verdictUpdatedOnly(staleness.updatedSessions)];
5404
5831
  if (summary.suspects.length > 0) {
5405
- lines2.push(
5406
- `\u307E\u305F\u8981\u6CE8\u610F\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${summary.suspects.length} \u4EF6\u3042\u308A\u307E\u3059(\u4E0A\u8A18\u300C\u8981\u6CE8\u610F session\u300D\u53C2\u7167)\u3002`
5407
- );
5832
+ lines2.push(t.orientation.verdictSuspectsAlso(summary.suspects.length));
5408
5833
  }
5409
5834
  return lines2;
5410
5835
  }
5411
5836
  if (summary.freshness.newestStartedAt === null) {
5412
- return [
5413
- "\u2139\uFE0F \u307E\u3060\u8A18\u9332\u304C\u3042\u308A\u307E\u305B\u3093\u3002",
5414
- "\u3053\u306E\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u3067\u4F5C\u696D\u3059\u308B\u3068\u3001\u3053\u3053\u306B\u73FE\u5728\u5730\u304C\u8868\u793A\u3055\u308C\u307E\u3059\u3002"
5415
- ];
5837
+ return [...t.orientation.verdictEmpty];
5416
5838
  }
5417
- const rel = relativeAgeJa(summary.freshness.newestStartedAt, now);
5418
- const tool = toolDisplayName(summary.freshness.newestSource);
5839
+ const rel = t.relativeAge(summary.freshness.newestStartedAt, now);
5840
+ const tool = toolDisplayName(summary.freshness.newestSource, t);
5419
5841
  const suspectCount = summary.suspects.length;
5420
5842
  if (staleness === null) {
5421
- return [
5422
- `\u2139\uFE0F \u53D6\u308A\u8FBC\u307F\u6E08\u307F\u306E\u72B6\u614B\u3092\u8868\u793A\u3057\u3066\u3044\u307E\u3059\u3002\u6700\u5F8C\u306E\u4F5C\u696D\u306F ${rel}(${tool})\u3002`,
5423
- "\u6700\u65B0\u304B\u78BA\u8A8D\u3059\u308B\u306B\u306F `basou refresh` \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
5424
- ];
5843
+ return [...t.orientation.verdictUnprobed(rel, tool)];
5425
5844
  }
5426
- const localScope = summary.hosts.length > 0 ? "\u3053\u306E\u30DB\u30B9\u30C8(\u30ED\u30FC\u30AB\u30EB)\u306E" : "";
5427
- const lines = [
5428
- `\u2705 ${localScope}\u53D6\u308A\u8FBC\u307F\u306F\u6700\u65B0\u3067\u3059\u3002\u6700\u5F8C\u306E\u4F5C\u696D\u306F ${rel}(${tool})\u3002\u672A\u53D6\u308A\u8FBC\u307F\u306E native \u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u3042\u308A\u307E\u305B\u3093\u3002`
5429
- ];
5845
+ const lines = [t.orientation.verdictCurrent(rel, tool, summary.hosts.length > 0)];
5430
5846
  if (suspectCount > 0) {
5431
- lines.push(`\u305F\u3060\u3057\u8981\u6CE8\u610F\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${suspectCount} \u4EF6\u3042\u308A\u307E\u3059(\u4E0A\u8A18\u300C\u8981\u6CE8\u610F session\u300D\u53C2\u7167)\u3002`);
5847
+ lines.push(t.orientation.verdictSuspectsCaveat(suspectCount));
5432
5848
  }
5433
- lines.push(
5434
- "\u6CE8: \u3053\u306E\u5224\u5B9A\u306F\u53D6\u308A\u8FBC\u307F\u6E08\u307F native \u30BB\u30C3\u30B7\u30E7\u30F3\u306E\u9BAE\u5EA6\u3068 suspect \u306E\u6709\u7121\u3060\u3051\u3092\u898B\u307E\u3059\u3002\u8A08\u753B\u2194\u5B9F\u88C5\u306E\u30C9\u30EA\u30D5\u30C8\u3084\u672A\u8A18\u9332\u306E\u610F\u601D\u6C7A\u5B9A\u307E\u3067\u306F\u691C\u77E5\u3057\u307E\u305B\u3093\u3002"
5435
- );
5849
+ lines.push(t.orientation.verdictScopeDisclaimer);
5436
5850
  return lines;
5437
5851
  }
5438
- function relativeAgeJa(startedAt, now) {
5439
- if (startedAt === null) return "(\u4E0D\u660E)";
5440
- const ms = now.getTime() - Date.parse(startedAt);
5441
- if (!Number.isFinite(ms) || ms < 0) return "\u305F\u3063\u305F\u4ECA";
5442
- if (ms < 6e4) return "\u305F\u3063\u305F\u4ECA";
5443
- const totalMin = Math.floor(ms / 6e4);
5444
- const days = Math.floor(totalMin / 1440);
5445
- const hours = Math.floor(totalMin % 1440 / 60);
5446
- const mins = totalMin % 60;
5447
- if (days > 0) return hours > 0 ? `${days}\u65E5${hours}\u6642\u9593\u524D` : `${days}\u65E5\u524D`;
5448
- if (hours > 0) return mins > 0 ? `${hours}\u6642\u9593${mins}\u5206\u524D` : `${hours}\u6642\u9593\u524D`;
5449
- return `${mins}\u5206\u524D`;
5450
- }
5451
5852
  function relativeAge(startedAt, now) {
5452
5853
  if (startedAt === void 0) return "(unknown)";
5453
5854
  const ms = now.getTime() - Date.parse(startedAt);
@@ -5483,21 +5884,18 @@ function shortId(id) {
5483
5884
 
5484
5885
  // src/project/anchor-starter.ts
5485
5886
  function renderAnchorStarter(input) {
5887
+ const t = presetStrings(resolveAnchorContentLanguage(input.repos)).anchorStarter;
5486
5888
  const lines = [];
5487
5889
  const title = input.projectName ?? input.anchorName;
5488
5890
  lines.push(`# AGENTS.md (${input.anchorName})`);
5489
5891
  lines.push("");
5490
- lines.push(
5491
- `> \u3053\u306E\u30EA\u30DD\u30B8\u30C8\u30EA\u306F **${title} \u306E planning master(anchor)** \u3067\u3059\u3002\u3053\u3053\u3067\u4F5C\u696D\u3059\u308B AI \u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306F\u3001\u307E\u305A\u3053\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u8AAD\u3093\u3067\u304F\u3060\u3055\u3044\u3002`
5492
- );
5892
+ lines.push(t.identityLine(title));
5493
5893
  lines.push(">");
5494
- lines.push(
5495
- "> \u3053\u306E\u30D5\u30A1\u30A4\u30EB\u306F `basou project derive` \u304C greenfield \u7ACB\u3061\u4E0A\u3052\u6642\u306B **\u4E00\u5EA6\u3060\u3051\u751F\u6210\u3057\u305F starter** \u3067\u3059\u3002\u4EE5\u5F8C\u306F\u624B\u7BA1\u7406\u3057\u3066\u304F\u3060\u3055\u3044 \u2014 basou \u306F\u518D\u751F\u6210\u3082\u4E0A\u66F8\u304D\u3082\u3057\u307E\u305B\u3093(BASOU:GENERATED \u30DE\u30FC\u30AB\u30FC\u306F\u7121\u304F\u3001\u81EA\u7531\u306B\u7DE8\u96C6\u3067\u304D\u307E\u3059)\u3002"
5496
- );
5894
+ lines.push(t.starterNote);
5497
5895
  lines.push("");
5498
- lines.push("## \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u57FA\u672C\u60C5\u5831");
5896
+ lines.push(t.basicsHeading);
5499
5897
  lines.push("");
5500
- lines.push("<!-- TODO: manifest \u304B\u3089\u306F\u5C0E\u51FA\u3067\u304D\u306A\u3044\u9805\u76EE\u3067\u3059\u3002\u57CB\u3081\u3066\u304F\u3060\u3055\u3044\u3002 -->");
5898
+ lines.push(t.basicsTodo);
5501
5899
  lines.push("");
5502
5900
  lines.push("```text");
5503
5901
  lines.push(`Product name: ${input.projectName ?? "<!-- TODO -->"}`);
@@ -5508,61 +5906,31 @@ function renderAnchorStarter(input) {
5508
5906
  lines.push("License: <!-- TODO -->");
5509
5907
  lines.push("```");
5510
5908
  lines.push("");
5511
- lines.push("## \u3069\u3053\u3067 commit \u3059\u308B\u304B");
5909
+ lines.push(t.commitHeading);
5512
5910
  lines.push("");
5513
- lines.push("- **\u3053\u306E\u30EA\u30DD\u30B8\u30C8\u30EA(planning master)**: \u69CB\u60F3\u30FB\u8A08\u753B\u30FB\u8A2D\u8A08\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u3002");
5514
- lines.push("- **\u5404\u5B9F\u88C5 repo**: \u5B9F\u88C5\u30B3\u30FC\u30C9\u3002\u5FC5\u305A\u5BFE\u8C61 repo \u306B `cd` \u3057\u3066\u304B\u3089 commit \u3057\u3066\u304F\u3060\u3055\u3044\u3002");
5515
- lines.push("- **workspace view**: git \u7BA1\u7406\u5916\u3002view \u3067\u306F commit \u3067\u304D\u307E\u305B\u3093\u3002");
5911
+ lines.push(t.commitPlanning);
5912
+ lines.push(t.commitImplementation);
5913
+ lines.push(t.commitView);
5516
5914
  lines.push("");
5517
- lines.push("## \u5FC5\u305A\u8AAD\u3080\u3079\u304D\u898F\u7D04");
5915
+ lines.push(t.conventionsHeading);
5518
5916
  lines.push("");
5519
- lines.push("\u4F5C\u696D\u898F\u7D04\u306F\u5404 repo \u306E AGENTS.md \u306B\u3042\u308A\u307E\u3059\u3002\u4EE5\u4E0B\u3092\u8AAD\u3093\u3067\u304B\u3089\u4F5C\u696D\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
5917
+ lines.push(t.conventionsBody);
5520
5918
  lines.push("");
5521
5919
  for (const r of input.repos) {
5522
5920
  if (r.anchor === true) continue;
5523
5921
  lines.push(`- ${r.name}/AGENTS.md`);
5524
5922
  }
5525
5923
  if (input.viewName !== void 0) {
5526
- lines.push(
5527
- `- ${input.viewName}/AGENTS.md(workspace view\u30FBbasou \u304C\u751F\u6210)\u2014 **\u6700\u65B0\u306E repo \u69CB\u6210(roster)\u306F\u3053\u3053\u3092\u6B63\u3068\u3059\u308B**`
5528
- );
5924
+ lines.push(t.viewPointerLine(input.viewName));
5529
5925
  }
5530
5926
  lines.push("");
5531
- lines.push("## \u4F5C\u696D\u65B9\u91DD(\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u56FA\u6709\u4E8B\u9805)");
5927
+ lines.push(t.policyHeading);
5532
5928
  lines.push("");
5533
- lines.push("<!-- TODO: \u4EE5\u4E0B\u3092\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306B\u5408\u308F\u305B\u3066\u8A18\u8FF0\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
5534
- lines.push(" - \u73FE\u5728\u306E\u30D5\u30A7\u30FC\u30BA / \u91CD\u8981\u30C9\u30AD\u30E5\u30E1\u30F3\u30C8");
5535
- lines.push(" - \u6A5F\u5BC6\u60C5\u5831\u306E\u6271\u3044(\u3069\u3053\u306B\u66F8\u304B\u306A\u3044\u304B)");
5536
- lines.push(" - \u8A00\u8A9E\u30DD\u30EA\u30B7\u30FC(commit / \u30B3\u30E1\u30F3\u30C8 / \u30C9\u30AD\u30E5\u30E1\u30F3\u30C8\u306E\u8A00\u8A9E)");
5537
- lines.push(" - commit \u904B\u7528(\u6DF7\u5728\u30B3\u30DF\u30C3\u30C8\u3092\u907F\u3051\u308B \u7B49)");
5538
- lines.push("-->");
5929
+ lines.push(...t.policyTodo);
5539
5930
  return `${lines.join("\n")}
5540
5931
  `;
5541
5932
  }
5542
5933
 
5543
- // src/project/relative-path.ts
5544
- function normalizeRelativePath(p) {
5545
- const trimmed = p.trim();
5546
- const absolute = trimmed.startsWith("/");
5547
- const out = [];
5548
- for (const seg of trimmed.split("/")) {
5549
- if (seg === "" || seg === ".") continue;
5550
- if (seg === "..") {
5551
- const top = out[out.length - 1];
5552
- if (top !== void 0 && top !== "..") {
5553
- out.pop();
5554
- } else if (!absolute) {
5555
- out.push("..");
5556
- }
5557
- continue;
5558
- }
5559
- out.push(seg);
5560
- }
5561
- const joined = out.join("/");
5562
- if (absolute) return `/${joined}`;
5563
- return joined.length === 0 ? "." : joined;
5564
- }
5565
-
5566
5934
  // src/project/archive.ts
5567
5935
  function planArchive(input) {
5568
5936
  const target = normalizeRelativePath(input.target);
@@ -5648,131 +6016,76 @@ function planGitignore(input) {
5648
6016
  }
5649
6017
 
5650
6018
  // src/project/preset.ts
5651
- function visibilityLabel(v) {
5652
- switch (v) {
5653
- case "public":
5654
- return "public(git \u5C65\u6B74\u306F\u516C\u958B)";
5655
- case "private":
5656
- return "private(git \u5C65\u6B74\u306F\u975E\u516C\u958B)";
5657
- case "future-public":
5658
- return "future-public(\u73FE\u5728\u306F\u975E\u516C\u958B\u30FB\u5C06\u6765\u516C\u958B\u4E88\u5B9A)";
5659
- default:
5660
- return "\u672A\u8A2D\u5B9A";
5661
- }
5662
- }
5663
- function sourceLanguageLabel(l) {
5664
- switch (l) {
5665
- case "en":
5666
- return "en(commit\u30FB\u30B3\u30E1\u30F3\u30C8\u30FB\u30B3\u30FC\u30C9\u306F\u82F1\u8A9E)";
5667
- case "ja":
5668
- return "ja(commit\u30FB\u30B3\u30E1\u30F3\u30C8\u30FB\u30B3\u30FC\u30C9\u306F\u65E5\u672C\u8A9E)";
5669
- case "en+ja":
5670
- return "en+ja(commit\u30FB\u30B3\u30E1\u30F3\u30C8\u30FB\u30B3\u30FC\u30C9\u306F\u65E5\u82F1)";
5671
- default:
5672
- return "\u672A\u8A2D\u5B9A";
5673
- }
5674
- }
5675
- function publishKindLabel(k) {
5676
- return k === "web" ? "web(\u30C7\u30D7\u30ED\u30A4)" : "npm(\u30D1\u30C3\u30B1\u30FC\u30B8)";
5677
- }
5678
- function publishVisibilityLabel(v) {
5679
- switch (v) {
5680
- case "public":
5681
- return "\u516C\u958B";
5682
- case "private":
5683
- return "\u975E\u516C\u958B";
5684
- case "future-public":
5685
- return "\u5C06\u6765\u516C\u958B";
5686
- default:
5687
- return "\u53EF\u8996\u6027\u672A\u8A2D\u5B9A";
5688
- }
5689
- }
5690
- function contentLanguageLabel(l) {
5691
- return l ?? "\u8A00\u8A9E\u672A\u8A2D\u5B9A";
5692
- }
5693
6019
  function isRenderable(repo) {
5694
6020
  return repo.visibility !== void 0 || repo.language !== void 0 || repo.publishes !== void 0 && repo.publishes.length > 0;
5695
6021
  }
5696
6022
  function renderPresetBlock(repo) {
6023
+ const t = presetStrings(resolveRepoContentLanguage(repo.language)).repoBlock;
5697
6024
  const lines = [];
5698
- lines.push("## \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u69CB\u6210(basou \u304C\u751F\u6210 \u2014 manifest \u304C\u6B63\u672C)");
6025
+ lines.push(t.heading);
5699
6026
  lines.push("");
5700
- lines.push(
5701
- "\u3053\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u306F `.basou/manifest.yaml` \u306E\u5BA3\u8A00\u304B\u3089 `basou project preset` \u304C\u751F\u6210\u3057\u307E\u3059\u3002\u7DE8\u96C6\u306F manifest \u5074\u3067\u884C\u3063\u3066\u304F\u3060\u3055\u3044(\u30DE\u30FC\u30AB\u30FC\u5916\u306E\u8A18\u8FF0\u306F\u4FDD\u6301\u3055\u308C\u307E\u3059)\u3002"
5702
- );
6027
+ lines.push(t.intro);
5703
6028
  lines.push("");
5704
- lines.push(`- \u30BD\u30FC\u30B9\u53EF\u8996\u6027: ${visibilityLabel(repo.visibility)}`);
5705
- lines.push(`- \u30BD\u30FC\u30B9\u8A00\u8A9E: ${sourceLanguageLabel(repo.language)}`);
6029
+ lines.push(`- ${t.sourceVisibilityLabel}: ${t.visibilityLabel(repo.visibility)}`);
6030
+ lines.push(`- ${t.sourceLanguageLineLabel}: ${t.sourceLanguageLabel(repo.language)}`);
5706
6031
  const publishes = repo.publishes ?? [];
5707
6032
  if (publishes.length === 0) {
5708
- lines.push("- \u914D\u4FE1\u7269: \u306A\u3057");
6033
+ lines.push(t.publishesNone);
5709
6034
  } else {
5710
- lines.push("- \u914D\u4FE1\u7269:");
6035
+ lines.push(t.publishesHeader);
5711
6036
  for (const p of publishes) {
5712
6037
  lines.push(
5713
- ` - ${publishKindLabel(p.kind)} \u2014 ${publishVisibilityLabel(p.visibility)} / ${contentLanguageLabel(p.language)}`
6038
+ ` - ${t.publishKindLabel(p.kind)} \u2014 ${t.publishVisibilityLabel(p.visibility)} / ${t.contentLanguageLabel(p.language)}`
5714
6039
  );
5715
6040
  }
5716
6041
  }
5717
6042
  return lines.join("\n");
5718
6043
  }
5719
- function visibilityShortLabel(v) {
5720
- return v ?? "\u672A\u8A2D\u5B9A";
5721
- }
5722
- function languageShortLabel(l) {
5723
- return l ?? "\u672A\u8A2D\u5B9A";
5724
- }
5725
- function instructionsLabel(repo) {
5726
- if (repo.anchor === true) return "anchor(\u624B\u7BA1\u7406)";
5727
- return repo.self === true ? "self(repo \u304C\u81EA\u5DF1\u7BA1\u7406)" : "hub(basou \u304C\u751F\u6210)";
5728
- }
5729
6044
  function renderViewPresetBlock(input) {
6045
+ const t = presetStrings(resolveAnchorContentLanguage(input.repos)).viewBlock;
6046
+ const shortLabel = (v) => v ?? t.unsetShort;
6047
+ const instructionsLabel = (repo) => {
6048
+ if (repo.anchor === true) return t.instructionsAnchor;
6049
+ return repo.self === true ? t.instructionsSelf : t.instructionsHub;
6050
+ };
5730
6051
  const lines = [];
5731
- lines.push("## workspace view \u69CB\u6210(basou \u304C\u751F\u6210 \u2014 manifest \u304C\u6B63\u672C)");
6052
+ lines.push(t.heading);
5732
6053
  lines.push("");
5733
- lines.push(
5734
- "\u3053\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u306F `.basou/manifest.yaml` \u306E\u5BA3\u8A00\u304B\u3089 `basou project preset` \u304C\u751F\u6210\u3057\u307E\u3059\u3002\u7DE8\u96C6\u306F manifest \u5074\u3067\u884C\u3063\u3066\u304F\u3060\u3055\u3044(\u30DE\u30FC\u30AB\u30FC\u5916\u306E\u8A18\u8FF0\u306F\u4FDD\u6301\u3055\u308C\u307E\u3059)\u3002"
5735
- );
5736
- lines.push(
5737
- `\u3053\u306E AGENTS.md \u81EA\u8EAB\u3082 basou \u306E\u751F\u6210\u7269\u3067\u3059(\u5B9F\u4F53: \`agents/${input.viewName}/AGENTS.md\`\u3001\u30DE\u30FC\u30AB\u30FC\u5916\u306E\u8A18\u8FF0\u306F\u4FDD\u6301\u3055\u308C\u307E\u3059)\u3002`
5738
- );
6054
+ lines.push(t.intro);
6055
+ lines.push(t.selfNote(input.viewName));
5739
6056
  lines.push("");
5740
- lines.push(
5741
- `\u3053\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u3001\u5BA3\u8A00\u3055\u308C\u305F ${input.repos.length} \u500B\u306E repo \u3092 symlink \u3067\u96C6\u7D04\u3059\u308B **view** \u3067\u3059\u3002\u5B9F\u4F53\u3092\u6301\u305F\u305A\u3001git \u7BA1\u7406\u5916\u3067\u3059\u3002`
5742
- );
6057
+ lines.push(t.aggregates(input.repos.length));
5743
6058
  lines.push("");
5744
- lines.push("### \u96C6\u7D04\u3057\u3066\u3044\u308B repo");
6059
+ lines.push(t.reposHeading);
5745
6060
  lines.push("");
5746
- lines.push("| repo | \u53EF\u8996\u6027 | \u8A00\u8A9E | \u6307\u793A\u66F8 |");
6061
+ lines.push(t.tableHeader);
5747
6062
  lines.push("|---|---|---|---|");
5748
6063
  for (const r of input.repos) {
5749
6064
  lines.push(
5750
- `| ${r.name} | ${visibilityShortLabel(r.visibility)} | ${languageShortLabel(r.language)} | ${instructionsLabel(r)} |`
6065
+ `| ${r.name} | ${shortLabel(r.visibility)} | ${shortLabel(r.language)} | ${instructionsLabel(r)} |`
5751
6066
  );
5752
6067
  }
5753
6068
  lines.push("");
5754
- lines.push("### \u3069\u3053\u3067 commit \u3059\u308B\u304B");
6069
+ lines.push(t.commitHeading);
5755
6070
  lines.push("");
5756
- lines.push(
5757
- "view \u3067\u306F commit \u3067\u304D\u307E\u305B\u3093(git \u7BA1\u7406\u5916)\u3002\u5909\u66F4\u306F\u5FC5\u305A\u5B9F\u4F53\u306E repo \u306B `cd` \u3057\u3066\u304B\u3089 commit \u3057\u3066\u304F\u3060\u3055\u3044\u3002"
5758
- );
6071
+ lines.push(t.commitBody);
5759
6072
  lines.push("");
5760
6073
  for (const r of input.repos) {
5761
6074
  lines.push(`- ${r.name} \u2192 \`cd ${r.name}\``);
5762
6075
  }
5763
6076
  lines.push("");
5764
- lines.push("### \u5FC5\u305A\u8AAD\u3080\u3079\u304D\u898F\u7D04");
6077
+ lines.push(t.conventionsHeading);
5765
6078
  lines.push("");
5766
- lines.push("\u4F5C\u696D\u898F\u7D04\u306F\u5404 repo \u306E AGENTS.md \u306B\u3042\u308A\u307E\u3059\u3002\u4EE5\u4E0B\u3092\u8AAD\u3093\u3067\u304B\u3089\u4F5C\u696D\u3057\u3066\u304F\u3060\u3055\u3044\u3002");
6079
+ lines.push(t.conventionsBody);
5767
6080
  lines.push("");
5768
6081
  for (const r of input.repos) {
5769
6082
  lines.push(`- ${r.name}/AGENTS.md`);
5770
6083
  }
5771
6084
  lines.push("");
5772
- lines.push("### \u91CD\u8981\u539F\u5247");
6085
+ lines.push(t.principlesHeading);
5773
6086
  lines.push("");
5774
- lines.push("- \u3053\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u72B6\u614B\u3092\u6301\u305F\u306A\u3044(git \u7BA1\u7406\u5916)");
5775
- lines.push("- \u91CD\u8981\u306A\u30D5\u30A1\u30A4\u30EB\u3092\u3053\u3053\u306B\u76F4\u63A5\u7F6E\u304B\u306A\u3044(\u5B9F\u4F53\u306F\u5404 repo \u306B\u7F6E\u304F)");
6087
+ lines.push(t.principleStateless);
6088
+ lines.push(t.principleNoFiles);
5776
6089
  return lines.join("\n");
5777
6090
  }
5778
6091
  function normalizeBlock(s) {
@@ -6733,7 +7046,8 @@ async function renderReport(input) {
6733
7046
  changedFiles,
6734
7047
  integrity
6735
7048
  };
6736
- return { body: formatReportBody(data), data };
7049
+ const language = input.language ?? await resolveViewLanguageFromPaths(input.paths);
7050
+ return { body: formatReportBody(data, viewStrings(language)), data };
6737
7051
  }
6738
7052
  function computePeriod(entries, nowIso) {
6739
7053
  if (entries.length === 0) return { from: null, to: null };
@@ -6760,7 +7074,7 @@ function tallyTaskStatus(items) {
6760
7074
  count: counts.get(status)
6761
7075
  }));
6762
7076
  }
6763
- function formatReportBody(data) {
7077
+ function formatReportBody(data, t) {
6764
7078
  const lines = [];
6765
7079
  const titleSuffix = data.title !== void 0 ? ` \u2014 ${data.title}` : "";
6766
7080
  lines.push(`# Report${titleSuffix}`);
@@ -6768,14 +7082,14 @@ function formatReportBody(data) {
6768
7082
  const periodSuffix = data.period.from !== null && data.period.to !== null ? ` (${data.period.from.slice(0, 10)}..${data.period.to.slice(0, 10)})` : "";
6769
7083
  lines.push(`> Generated at ${data.generatedAt}${periodSuffix}`);
6770
7084
  lines.push("");
6771
- lines.push("## \u6982\u8981");
7085
+ lines.push(t.report.headingSummary);
6772
7086
  lines.push("");
6773
7087
  lines.push(`- ${formatSessionsLine(data)}`);
6774
7088
  lines.push(
6775
7089
  `- Active time ${formatDurationMs(data.time.activeMs)}, ${formatInt(data.volume.outputTokens)} output tokens`
6776
7090
  );
6777
7091
  lines.push("");
6778
- lines.push("## \u4F5C\u696D\u91CF");
7092
+ lines.push(t.report.headingVolume);
6779
7093
  lines.push("");
6780
7094
  const tokenCaveat = data.volume.tokensAvailable ? "" : " (no token data captured)";
6781
7095
  lines.push(`- Output tokens: ${formatInt(data.volume.outputTokens)}${tokenCaveat}`);
@@ -6795,7 +7109,7 @@ function formatReportBody(data) {
6795
7109
  }
6796
7110
  lines.push(`- Span: ${formatDurationMs(data.time.spanMs)} (total elapsed)`);
6797
7111
  lines.push("");
6798
- lines.push("## \u5224\u65AD");
7112
+ lines.push(t.report.headingDecisions);
6799
7113
  lines.push("");
6800
7114
  if (data.decisions.items.length === 0) {
6801
7115
  lines.push("(no decisions recorded yet)");
@@ -6813,7 +7127,7 @@ function formatReportBody(data) {
6813
7127
  }
6814
7128
  }
6815
7129
  lines.push("");
6816
- lines.push("## \u627F\u8A8D");
7130
+ lines.push(t.report.headingApprovals);
6817
7131
  lines.push("");
6818
7132
  if (data.approvals.items.length === 0) {
6819
7133
  lines.push("(none)");
@@ -6830,7 +7144,7 @@ function formatReportBody(data) {
6830
7144
  if (overflow > 0) lines.push(`- ... +${overflow} more`);
6831
7145
  }
6832
7146
  lines.push("");
6833
- lines.push("## \u30BF\u30B9\u30AF");
7147
+ lines.push(t.report.headingTasks);
6834
7148
  lines.push("");
6835
7149
  if (data.tasks.items.length === 0) {
6836
7150
  lines.push("(no tasks recorded yet)");
@@ -6845,7 +7159,7 @@ function formatReportBody(data) {
6845
7159
  if (overflow > 0) lines.push(`- ... +${overflow} more`);
6846
7160
  }
6847
7161
  lines.push("");
6848
- lines.push("## \u5909\u66F4\u30D5\u30A1\u30A4\u30EB");
7162
+ lines.push(t.report.headingChangedFiles);
6849
7163
  lines.push("");
6850
7164
  if (data.changedFiles.length === 0) {
6851
7165
  lines.push("(no related files recorded)");
@@ -6855,7 +7169,7 @@ function formatReportBody(data) {
6855
7169
  if (overflow > 0) lines.push(`- ... +${overflow} more`);
6856
7170
  }
6857
7171
  lines.push("");
6858
- lines.push("## \u30BB\u30C3\u30B7\u30E7\u30F3\u4E00\u89A7");
7172
+ lines.push(t.report.headingSessions);
6859
7173
  lines.push("");
6860
7174
  if (data.sessions.items.length === 0) {
6861
7175
  lines.push("(no sessions yet)");
@@ -6874,7 +7188,7 @@ function formatReportBody(data) {
6874
7188
  }
6875
7189
  }
6876
7190
  lines.push("");
6877
- lines.push("## \u6574\u5408\u6027");
7191
+ lines.push(t.report.headingIntegrity);
6878
7192
  lines.push("");
6879
7193
  const i = data.integrity;
6880
7194
  lines.push(
@@ -8307,6 +8621,7 @@ export {
8307
8621
  planRosterAdoption,
8308
8622
  planWorkspaceView,
8309
8623
  prefixedUlid,
8624
+ presetStrings,
8310
8625
  readAllEvents,
8311
8626
  readManifest,
8312
8627
  readMarkdownFile,
@@ -8332,12 +8647,16 @@ export {
8332
8647
  renderViewPresetBlock,
8333
8648
  renderWithMarkers,
8334
8649
  replayEvents,
8650
+ resolveAnchorContentLanguage,
8335
8651
  resolveBasouRepositoryRoot,
8336
8652
  resolveClaudeCodeCommand,
8337
8653
  resolveCodexCommand,
8654
+ resolveRepoContentLanguage,
8338
8655
  resolveRepositoryRoot,
8339
8656
  resolveSessionId,
8340
8657
  resolveTaskId,
8658
+ resolveViewLanguage,
8659
+ resolveViewLanguageFromPaths,
8341
8660
  safeSimpleGit,
8342
8661
  sanitizePath,
8343
8662
  sanitizeRelatedFiles,
@@ -8359,6 +8678,7 @@ export {
8359
8678
  updateTaskStatusWithEvent,
8360
8679
  upsertStopHook,
8361
8680
  verifyEventsChain,
8681
+ viewStrings,
8362
8682
  writeEventsBulk,
8363
8683
  writeManifest,
8364
8684
  writeMarkdownFile,