@basou/core 0.39.0 → 0.41.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.d.ts CHANGED
@@ -818,6 +818,110 @@ declare function resolveCodexCommand(lookup?: CommandLookup): Promise<{
818
818
  command: string;
819
819
  }>;
820
820
 
821
+ /**
822
+ * Pure transforms for registering / removing basou's SessionStart hook inside a
823
+ * parsed Codex `hooks.json` object. No disk or environment access: the CLI reads
824
+ * and writes the file, parses the JSON, and passes the object here so the
825
+ * merge/removal logic stays deterministic and unit-testable. The Codex twin of
826
+ * the Claude Code `settings-hook` transforms.
827
+ *
828
+ * Why a hook, and why this shape: `~/.codex/hooks.json` is user-global, but a
829
+ * hook stores nothing — Codex runs it when a session starts and passes the
830
+ * session's own `cwd` on stdin, and whatever the hook prints on stdout becomes
831
+ * developer context for THAT session only. basou resolves the workspace from
832
+ * that `cwd` and prints that workspace's position. So one installed hook serves
833
+ * every workspace on the machine while no workspace's position is ever written
834
+ * where another workspace's session reads it — the property the retired
835
+ * `~/.codex/AGENTS.md` orientation render could not have.
836
+ *
837
+ * `hooks.json` holds other events and other people's hooks; these functions
838
+ * clone the input and touch ONLY the `hooks.SessionStart` handler that basou
839
+ * owns, preserving everything else byte-for-byte through the round-trip.
840
+ */
841
+ /**
842
+ * Seconds before Codex kills the hook. `basou orient` reads the store and
843
+ * probes the native logs for staleness; on a large store that is seconds, not
844
+ * minutes. Codex's own default is 600 — far too long for a session-start
845
+ * handler whose failure mode is "the session waits".
846
+ */
847
+ declare const SESSION_START_HOOK_TIMEOUT_SECONDS = 30;
848
+ /**
849
+ * Which SessionStart sources fire the hook. Codex applies the matcher to the
850
+ * payload's `source`: `startup` (a new session), `resume`, `clear` (context
851
+ * reset). `compact` is left out: the position is already in the context being
852
+ * compacted, and re-injecting ~10 KB after every compaction would crowd the
853
+ * budget the compaction just freed.
854
+ */
855
+ declare const SESSION_START_HOOK_MATCHER = "startup|resume|clear";
856
+ /**
857
+ * Codex caps each hook's model-visible output at roughly 2,500 tokens by default
858
+ * and spills the rest to a temp file, handing the model a head-and-tail preview
859
+ * plus the path. A position is ~10 KB and is useless truncated — the open
860
+ * tracks and the next step sit at the end — so the cap is disabled for this
861
+ * handler (`0` passes the complete output).
862
+ */
863
+ declare const SESSION_START_HOOK_CONTEXT_LIMIT = 0;
864
+ /** Shown in the Codex UI while the hook runs. */
865
+ declare const SESSION_START_HOOK_STATUS_MESSAGE = "basou orient";
866
+ declare function isBasouSessionStartHookCommand(command: string): boolean;
867
+ /**
868
+ * Build the shell command basou registers as a Codex SessionStart hook. Uses
869
+ * the node path (the `basou` alias is often absent from a hook's PATH) and a
870
+ * `2>/dev/null || true` wrapper so a stale dist path or any crash fails open —
871
+ * a session start must never be blocked by its orientation. The handler itself
872
+ * is also fail-open, so the wrapper is belt-and-braces. The entry path is
873
+ * shell-quoted for directories containing spaces or metacharacters.
874
+ */
875
+ declare function buildSessionStartHookCommand(options: {
876
+ cliEntry: string;
877
+ }): string;
878
+ type CodexHooksFile = Record<string, unknown>;
879
+ type SessionStartHookUpsert = {
880
+ hooksFile: CodexHooksFile;
881
+ /** `installed` = a new entry was appended; `updated` = an existing basou entry was rewritten; `unchanged` = already canonical. */
882
+ action: "installed" | "updated" | "unchanged";
883
+ };
884
+ type SessionStartHookRemoval = {
885
+ hooksFile: CodexHooksFile;
886
+ action: "removed" | "absent";
887
+ };
888
+ /**
889
+ * Where basou's handler sits in the file. Codex records trust per handler under
890
+ * `[hooks.state."<hooks.json path>:session_start:<group>:<handler>"]` in
891
+ * `config.toml`, so the two indexes are what `hook status codex` needs to look
892
+ * the trust entry up.
893
+ */
894
+ type SessionStartHookLocation = {
895
+ command: string;
896
+ groupIndex: number;
897
+ handlerIndex: number;
898
+ /** The matcher of the group the handler sits in (undefined = fires on every source). */
899
+ matcher: string | undefined;
900
+ /**
901
+ * The handler object exactly as installed. Codex hashes the installed fields
902
+ * (not basou's canonical ones) when it decides whether the hook is trusted, so
903
+ * a status check must hash what is in the file.
904
+ */
905
+ handler: Record<string, unknown>;
906
+ };
907
+ /**
908
+ * Register (or upgrade in place) basou's SessionStart hook. Idempotent: an
909
+ * existing basou handler is rewritten to the canonical fields; a foreign
910
+ * handler, another event, or any other key is left untouched. A new install
911
+ * appends its own matcher group so it never widens or narrows someone else's
912
+ * matcher.
913
+ */
914
+ declare function upsertSessionStartHook(hooksFile: unknown, command: string): SessionStartHookUpsert;
915
+ /**
916
+ * Remove every basou-owned SessionStart handler. A group emptied by the removal
917
+ * is dropped; a now-empty `hooks.SessionStart` / `hooks` container is deleted so
918
+ * the file does not accumulate empty scaffolding. Foreign handlers and other
919
+ * keys are preserved.
920
+ */
921
+ declare function removeSessionStartHook(hooksFile: unknown): SessionStartHookRemoval;
922
+ /** Locate the installed basou SessionStart handler, or null if none is registered. */
923
+ declare function findBasouSessionStartHook(hooksFile: unknown): SessionStartHookLocation | null;
924
+
821
925
  /**
822
926
  * The `source` string stamped on every event derived from an OpenAI Codex
823
927
  * native rollout log, and the matching session `source.kind`.
@@ -2007,6 +2111,8 @@ type ViewStrings = {
2007
2111
  recentDecisionsLabel: string;
2008
2112
  recentNextStepLabel: string;
2009
2113
  recentChangedLabel: string;
2114
+ /** Trails the recent-files line when scratch paths were left out of it. */
2115
+ scratchOmitted: (count: number) => string;
2010
2116
  trackCloseInstruction: string;
2011
2117
  nextStepRecordedLabel: (age: string) => string;
2012
2118
  noteStaleNote: (activityAge: string) => string;
@@ -4224,6 +4330,8 @@ type OrientationSummary = {
4224
4330
  displayed: string[];
4225
4331
  overflow: number;
4226
4332
  outOfRoot: string[];
4333
+ /** Scratch paths left out of `displayed` (see `isTransientToolPath`). */
4334
+ omitted: number;
4227
4335
  };
4228
4336
  /** Tasks whose status is `planned` or `in_progress`. */
4229
4337
  inFlightTasks: InFlightTask[];
@@ -6877,4 +6985,4 @@ declare function overwriteYamlFile(filePath: string, value: unknown): Promise<vo
6877
6985
  */
6878
6986
  declare const BASOU_CORE_VERSION = "0.1.0";
6879
6987
 
6880
- export { ACTIVE_GAP_CAP_MS, AGENT_INFRA_DIRS, type ActiveTimeBasis, type AdapterOutputEvent, type AdoptCandidate, type AdoptCandidateKind, type AnchorStarterInput, type AnchorStarterRepo, type AppendBasouGitignoreOptions, type AppendBasouGitignoreResult, type AppendEventToExistingInput, type AppendEventToExistingResult, type Approval, type ApprovalApprovedEvent, type ApprovalExpiredEvent, ApprovalIdSchema, type ApprovalLocation, type ApprovalRejectedEvent, type ApprovalRequestedEvent, ApprovalSchema, type ApprovalStatus, ApprovalStatusSchema, type ArchivePlan, type ArchiveTaskInput, type ArchiveTaskResult, type AttachTaskInput, type AttachUpdateTaskStatusInput, type AttachableStatus, BASOU_CORE_VERSION, type BasouPaths, type BuildStopHookCommandOptions, type BulkChainResult, CLAUDE_IMPORT_SOURCE, CODEX_IMPORT_SOURCE, type CaptureMode, type ChainBreakReason, type ChainTailState, type ChainVerdict, type ChainVerdictStatus, type ChainedEvents, ChildProcessRunner, type CitedReview, type ClaudeSettings, type ClaudeTranscriptRecord, type ClaudeTranscriptToPayloadOptions, type CodexCommandLookup, type CodexRolloutRecord, type CodexRolloutToPayloadOptions, type CommandExecutedEvent, type CommandLookup, type CreateAdHocSessionInput, type CreateAdHocSessionResult, type CreateAdHocTaskInput, type CreateManifestInput, type CreateTaskInput, type CreateTaskResult, DEFAULT_STOP_HOOK_MIN_EDITS, type DayWorkStats, DecisionIdSchema, type DecisionRecordedEvent, type DecisionsRendererInput, type DecisionsRendererResult, type DeleteTaskInput, type DeleteTaskResult, type DiffResult, type EditTaskInput, type EditTaskResult, type Event, EventIdSchema, EventSchema, EventSourceSchema, type ExistingViewLink, FailedToFinalizeError, type FederatedRoot, type FileChange, type FileChangeStatus, type FileChangedEvent, GENERATED_END, GENERATED_START, type GitSnapshot, type GitSnapshotEvent, type GitignorePlanSummary, type HandoffRendererInput, type HandoffRendererResult, ID_PREFIXES, type IdPrefix, type ImportSessionOptions, type ImportSessionResult, type IncompleteWiring, type InstructionFileFact, type InstructionSymlinkFact, type InstructionSymlinkState, IsoTimestampSchema, JSON_SCHEMA_VERSION, type JsonSchemaArtifact, type LoadFederatedOptions, type LoadSessionEntriesOptions, type LoadTaskEntriesOptions, type LoadedApproval, type LockHandle, type LockScope, type Manifest, ManifestSchema, type MarkerSection, type Markers, type MeasureAvailability, type MissingCanonical, type NoteAddedEvent, ORIENTATION_END, ORIENTATION_START, type OrientationRendererInput, type OrientationRendererResult, type OrientationSummary, PROTOCOL_END, PROTOCOL_START, type PrefixedId, type PresetAction, type PresetCollision, type PresetMarkerConflict, type PresetMarkerKind, type PresetPlanSummary, type PresetRepo, type PresetStrings, type ProcessRunner, type PublishKind, type PublishTarget, REVIEW_RECORD_NO_INPUT_HINT, type RechainOptions, type RechainResult, type ReconcileAllResult, type ReconcileAllTasksInput, type ReconcileAllTasksOptions, type ReconcileFailure, type ReconcileResult, type ReconcileTaskInput, type RefreshLinkageInput, type RefreshLinkageResult, type ReimportOptions, type ReimportResult, type RenamePlan, type ReplayOptions, type ReplayWarning, type RepoEntry, type RepoGitignoreFacts, type RepoGitignorePlan, type RepoInstructions, type RepoLanguage, type RepoPathProblem, type RepoPresetFacts, type RepoPresetPlan, type RepoSymlinkFacts, type RepoSymlinkPlan, type RepoVisibility, type RepoWiringFacts, type ReportApprovalItem, type ReportData, type ReportDecisionItem, type ReportRendererInput, type ReportRendererResult, type ReportSessionItem, type ReportTaskItem, type RetrofitAction, type RetrofitAgentsState, type RetrofitFacts, type RetrofitPlan, type RetrofitReason, type ReviewBlocked, type ReviewFinding, type ReviewGapRepoSummary, type ReviewGapUnit, type ReviewGapVerdict, type ReviewGapsInput, type ReviewGapsSummary, type ReviewGateResult, type ReviewGateSilentReason, type ReviewRecordBlockedInput, type ReviewRecordFindingInput, type ReviewRecordInput, type ReviewRecordedEvent, type RiskLevel, RiskLevelSchema, type RosterAdoptionPlan, type RosterDriftSummary, type RunOptions, type RunResult, STOP_HOOK_TIMEOUT_SECONDS, STUCK_THRESHOLD_MS, type SanitizePathOptions, type SanitizeRelatedFilesResult, SchemaVersionSchema, type SelfReportedReview, type Session, type SessionEndedEvent, type SessionEntry, SessionIdSchema, type SessionImportPayload, SessionImportPayloadSchema, type SessionInnerImportInput, SessionInnerImportSchema, type SessionIntegrity, SessionIntegritySchema, type SessionMetrics, SessionMetricsSchema, SessionSchema, type SessionSkipReason, type SessionSourceKind, SessionSourceKindSchema, type SessionStartedEvent, type SessionStatus, type SessionStatusChangedEvent, SessionStatusSchema, type SessionWorkStats, type SourceRootScope, type SourceRootsReconcile, type SourceWorkStats, type StatusCount, StatusSchema, type StatusSnapshot, type StopHookEvaluation, type StopHookEvaluationInput, type StopHookRemoval, type StopHookSilentReason, type StopHookUpsert, type SuspectReason, type SymlinkCollision, type SymlinkConflict, type SymlinkPlanSummary, type Task, type TaskArchivedEvent, type TaskCreatedEvent, type TaskDeletedEvent, type TaskDocument, TaskIdSchema, type TaskLinkageRefreshedEvent, type TaskReconciledEvent, TaskSchema, type TaskSkipReason, type TaskStatus, type TaskStatusChangedEvent, type TaskStatusCount, TaskStatusSchema, TaskWriteAfterEventError, type TaskWriteAfterEventPhase, type TokenTotals, type UnattachedSelfReports, type UnbindableRepo, type UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type ViewCollision, type ViewConflict, type ViewLanguage, type ViewLinkState, type ViewPresetInput, type ViewPresetRepo, type ViewRepoFact, type ViewStrayUnknown, type ViewStrings, type ViewWiringFacts, type WiringCollision, type WiringConflict, type WiringDriftSummary, type WiringRisk, type WiringSummary, type WorkStatsInput, type WorkStatsResult, type WorkStatsTotals, WorkspaceIdSchema, type WorkspaceViewPlan, type WriteEventsBulkOptions, type WriteTaskFileMode, acquireLock, appendBasouGitignore, appendChainedEvent, appendChainedEventLocked, appendEvent, appendEventToExistingSession, archiveTask, assertBasouRootSafe, basouPaths, buildJsonSchemas, buildReviewRecordLabel, buildReviewRecordedEvent, buildStatusSnapshot, buildStopHookCommand, chainEvents, chainRawJsonLines, classifyFilesBySourceRoot, classifyRetrofit, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexAdapterMetadata, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, evaluateStopHook, finalizeSessionYaml, findBasouStopHookCommand, findErrorCode, findReviewGaps, findUnbindableRepos, formatDurationMs, genesisHash, getDiff, getSnapshot, importSessionFromJson, inspectChainTail, instructionMode, isBasouStopHookCommand, isGitNotFound, isImportDerivedSource, isLazyExpired, isRenderable, isValidPrefixedId, lineHash, linkYamlFile, loadApproval, loadFederatedSessionEntries, loadSessionEntries, loadTaskEntries, normalizeRepoKey, normalizeRepoPath, overwriteYamlFile, parseDuration, parseMarkers, parseReviewRecordInput, pathBasename, planArchive, planGitignore, planRename, planRosterAdoption, planWorkspaceView, prefixedUlid, presetStrings, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, rechainSessionInPlace, reconcileAllTasks, reconcileSourceRoots, reconcileTask, refreshTaskLinkedSessions, reimportPreservingId, removeMarkerSection, removeStopHook, renderAnchorStarter, renderDecisions, renderHandoff, renderOrientation, renderPresetBlock, renderReport, renderViewPresetBlock, renderWithMarkers, replayEvents, resolveAnchorContentLanguage, resolveBasouRepositoryRoot, resolveClaudeCodeCommand, resolveCodexCommand, resolveRepoContentLanguage, resolveRepoRoot, resolveRepositoryRoot, resolveSessionId, resolveTaskId, resolveViewLanguage, resolveViewLanguageFromPaths, safeSimpleGit, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, seedMarkers, serializeEventLine, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, summarizeOrientation, summarizePresetPlan, summarizeRosterDrift, summarizeSymlinkPlan, summarizeWiring, summarizeWiringDrift, tryRemoteUrl, ulid, unknownManifestKeys, updateTaskStatusWithEvent, upsertStopHook, verifyEventsChain, viewStrings, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
6988
+ export { ACTIVE_GAP_CAP_MS, AGENT_INFRA_DIRS, type ActiveTimeBasis, type AdapterOutputEvent, type AdoptCandidate, type AdoptCandidateKind, type AnchorStarterInput, type AnchorStarterRepo, type AppendBasouGitignoreOptions, type AppendBasouGitignoreResult, type AppendEventToExistingInput, type AppendEventToExistingResult, type Approval, type ApprovalApprovedEvent, type ApprovalExpiredEvent, ApprovalIdSchema, type ApprovalLocation, type ApprovalRejectedEvent, type ApprovalRequestedEvent, ApprovalSchema, type ApprovalStatus, ApprovalStatusSchema, type ArchivePlan, type ArchiveTaskInput, type ArchiveTaskResult, type AttachTaskInput, type AttachUpdateTaskStatusInput, type AttachableStatus, BASOU_CORE_VERSION, type BasouPaths, type BuildStopHookCommandOptions, type BulkChainResult, CLAUDE_IMPORT_SOURCE, CODEX_IMPORT_SOURCE, type CaptureMode, type ChainBreakReason, type ChainTailState, type ChainVerdict, type ChainVerdictStatus, type ChainedEvents, ChildProcessRunner, type CitedReview, type ClaudeSettings, type ClaudeTranscriptRecord, type ClaudeTranscriptToPayloadOptions, type CodexCommandLookup, type CodexHooksFile, type CodexRolloutRecord, type CodexRolloutToPayloadOptions, type CommandExecutedEvent, type CommandLookup, type CreateAdHocSessionInput, type CreateAdHocSessionResult, type CreateAdHocTaskInput, type CreateManifestInput, type CreateTaskInput, type CreateTaskResult, DEFAULT_STOP_HOOK_MIN_EDITS, type DayWorkStats, DecisionIdSchema, type DecisionRecordedEvent, type DecisionsRendererInput, type DecisionsRendererResult, type DeleteTaskInput, type DeleteTaskResult, type DiffResult, type EditTaskInput, type EditTaskResult, type Event, EventIdSchema, EventSchema, EventSourceSchema, type ExistingViewLink, FailedToFinalizeError, type FederatedRoot, type FileChange, type FileChangeStatus, type FileChangedEvent, GENERATED_END, GENERATED_START, type GitSnapshot, type GitSnapshotEvent, type GitignorePlanSummary, type HandoffRendererInput, type HandoffRendererResult, ID_PREFIXES, type IdPrefix, type ImportSessionOptions, type ImportSessionResult, type IncompleteWiring, type InstructionFileFact, type InstructionSymlinkFact, type InstructionSymlinkState, IsoTimestampSchema, JSON_SCHEMA_VERSION, type JsonSchemaArtifact, type LoadFederatedOptions, type LoadSessionEntriesOptions, type LoadTaskEntriesOptions, type LoadedApproval, type LockHandle, type LockScope, type Manifest, ManifestSchema, type MarkerSection, type Markers, type MeasureAvailability, type MissingCanonical, type NoteAddedEvent, ORIENTATION_END, ORIENTATION_START, type OrientationRendererInput, type OrientationRendererResult, type OrientationSummary, PROTOCOL_END, PROTOCOL_START, type PrefixedId, type PresetAction, type PresetCollision, type PresetMarkerConflict, type PresetMarkerKind, type PresetPlanSummary, type PresetRepo, type PresetStrings, type ProcessRunner, type PublishKind, type PublishTarget, REVIEW_RECORD_NO_INPUT_HINT, type RechainOptions, type RechainResult, type ReconcileAllResult, type ReconcileAllTasksInput, type ReconcileAllTasksOptions, type ReconcileFailure, type ReconcileResult, type ReconcileTaskInput, type RefreshLinkageInput, type RefreshLinkageResult, type ReimportOptions, type ReimportResult, type RenamePlan, type ReplayOptions, type ReplayWarning, type RepoEntry, type RepoGitignoreFacts, type RepoGitignorePlan, type RepoInstructions, type RepoLanguage, type RepoPathProblem, type RepoPresetFacts, type RepoPresetPlan, type RepoSymlinkFacts, type RepoSymlinkPlan, type RepoVisibility, type RepoWiringFacts, type ReportApprovalItem, type ReportData, type ReportDecisionItem, type ReportRendererInput, type ReportRendererResult, type ReportSessionItem, type ReportTaskItem, type RetrofitAction, type RetrofitAgentsState, type RetrofitFacts, type RetrofitPlan, type RetrofitReason, type ReviewBlocked, type ReviewFinding, type ReviewGapRepoSummary, type ReviewGapUnit, type ReviewGapVerdict, type ReviewGapsInput, type ReviewGapsSummary, type ReviewGateResult, type ReviewGateSilentReason, type ReviewRecordBlockedInput, type ReviewRecordFindingInput, type ReviewRecordInput, type ReviewRecordedEvent, type RiskLevel, RiskLevelSchema, type RosterAdoptionPlan, type RosterDriftSummary, type RunOptions, type RunResult, SESSION_START_HOOK_CONTEXT_LIMIT, SESSION_START_HOOK_MATCHER, SESSION_START_HOOK_STATUS_MESSAGE, SESSION_START_HOOK_TIMEOUT_SECONDS, STOP_HOOK_TIMEOUT_SECONDS, STUCK_THRESHOLD_MS, type SanitizePathOptions, type SanitizeRelatedFilesResult, SchemaVersionSchema, type SelfReportedReview, type Session, type SessionEndedEvent, type SessionEntry, SessionIdSchema, type SessionImportPayload, SessionImportPayloadSchema, type SessionInnerImportInput, SessionInnerImportSchema, type SessionIntegrity, SessionIntegritySchema, type SessionMetrics, SessionMetricsSchema, SessionSchema, type SessionSkipReason, type SessionSourceKind, SessionSourceKindSchema, type SessionStartHookLocation, type SessionStartHookRemoval, type SessionStartHookUpsert, type SessionStartedEvent, type SessionStatus, type SessionStatusChangedEvent, SessionStatusSchema, type SessionWorkStats, type SourceRootScope, type SourceRootsReconcile, type SourceWorkStats, type StatusCount, StatusSchema, type StatusSnapshot, type StopHookEvaluation, type StopHookEvaluationInput, type StopHookRemoval, type StopHookSilentReason, type StopHookUpsert, type SuspectReason, type SymlinkCollision, type SymlinkConflict, type SymlinkPlanSummary, type Task, type TaskArchivedEvent, type TaskCreatedEvent, type TaskDeletedEvent, type TaskDocument, TaskIdSchema, type TaskLinkageRefreshedEvent, type TaskReconciledEvent, TaskSchema, type TaskSkipReason, type TaskStatus, type TaskStatusChangedEvent, type TaskStatusCount, TaskStatusSchema, TaskWriteAfterEventError, type TaskWriteAfterEventPhase, type TokenTotals, type UnattachedSelfReports, type UnbindableRepo, type UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type ViewCollision, type ViewConflict, type ViewLanguage, type ViewLinkState, type ViewPresetInput, type ViewPresetRepo, type ViewRepoFact, type ViewStrayUnknown, type ViewStrings, type ViewWiringFacts, type WiringCollision, type WiringConflict, type WiringDriftSummary, type WiringRisk, type WiringSummary, type WorkStatsInput, type WorkStatsResult, type WorkStatsTotals, WorkspaceIdSchema, type WorkspaceViewPlan, type WriteEventsBulkOptions, type WriteTaskFileMode, acquireLock, appendBasouGitignore, appendChainedEvent, appendChainedEventLocked, appendEvent, appendEventToExistingSession, archiveTask, assertBasouRootSafe, basouPaths, buildJsonSchemas, buildReviewRecordLabel, buildReviewRecordedEvent, buildSessionStartHookCommand, buildStatusSnapshot, buildStopHookCommand, chainEvents, chainRawJsonLines, classifyFilesBySourceRoot, classifyRetrofit, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexAdapterMetadata, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, evaluateStopHook, finalizeSessionYaml, findBasouSessionStartHook, findBasouStopHookCommand, findErrorCode, findReviewGaps, findUnbindableRepos, formatDurationMs, genesisHash, getDiff, getSnapshot, importSessionFromJson, inspectChainTail, instructionMode, isBasouSessionStartHookCommand, isBasouStopHookCommand, isGitNotFound, isImportDerivedSource, isLazyExpired, isRenderable, isValidPrefixedId, lineHash, linkYamlFile, loadApproval, loadFederatedSessionEntries, loadSessionEntries, loadTaskEntries, normalizeRepoKey, normalizeRepoPath, overwriteYamlFile, parseDuration, parseMarkers, parseReviewRecordInput, pathBasename, planArchive, planGitignore, planRename, planRosterAdoption, planWorkspaceView, prefixedUlid, presetStrings, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, rechainSessionInPlace, reconcileAllTasks, reconcileSourceRoots, reconcileTask, refreshTaskLinkedSessions, reimportPreservingId, removeMarkerSection, removeSessionStartHook, removeStopHook, renderAnchorStarter, renderDecisions, renderHandoff, renderOrientation, renderPresetBlock, renderReport, renderViewPresetBlock, renderWithMarkers, replayEvents, resolveAnchorContentLanguage, resolveBasouRepositoryRoot, resolveClaudeCodeCommand, resolveCodexCommand, resolveRepoContentLanguage, resolveRepoRoot, resolveRepositoryRoot, resolveSessionId, resolveTaskId, resolveViewLanguage, resolveViewLanguageFromPaths, safeSimpleGit, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, seedMarkers, serializeEventLine, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, summarizeOrientation, summarizePresetPlan, summarizeRosterDrift, summarizeSymlinkPlan, summarizeWiring, summarizeWiringDrift, tryRemoteUrl, ulid, unknownManifestKeys, updateTaskStatusWithEvent, upsertSessionStartHook, upsertStopHook, verifyEventsChain, viewStrings, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
package/dist/index.js CHANGED
@@ -660,6 +660,135 @@ async function resolveCodexCommand(lookup = isOnPath) {
660
660
  throw new Error("Codex CLI not found in PATH. Install codex first.");
661
661
  }
662
662
 
663
+ // src/adapters/codex/hooks-json.ts
664
+ var SESSION_START_HOOK_TIMEOUT_SECONDS = 30;
665
+ var SESSION_START_HOOK_MATCHER = "startup|resume|clear";
666
+ var SESSION_START_HOOK_CONTEXT_LIMIT = 0;
667
+ var SESSION_START_HOOK_STATUS_MESSAGE = "basou orient";
668
+ var BASOU_SESSION_START_HOOK = /(?:\bbasou|(?:@basou|packages)\/cli\/dist\/index\.js['"]?)\s+hook\s+session-start\b/;
669
+ function isBasouSessionStartHookCommand(command) {
670
+ return BASOU_SESSION_START_HOOK.test(command);
671
+ }
672
+ function shellQuote2(value) {
673
+ return `'${value.replace(/'/g, "'\\''")}'`;
674
+ }
675
+ function buildSessionStartHookCommand(options) {
676
+ return `node ${shellQuote2(options.cliEntry)} hook session-start 2>/dev/null || true`;
677
+ }
678
+ function isRecord2(value) {
679
+ return typeof value === "object" && value !== null && !Array.isArray(value);
680
+ }
681
+ function cloneHooksFile(hooksFile) {
682
+ if (hooksFile === void 0 || hooksFile === null) return {};
683
+ if (!isRecord2(hooksFile)) {
684
+ throw new Error("The Codex hooks.json is not a JSON object.");
685
+ }
686
+ return structuredClone(hooksFile);
687
+ }
688
+ function canonicalHandler(command) {
689
+ return {
690
+ type: "command",
691
+ command,
692
+ timeout: SESSION_START_HOOK_TIMEOUT_SECONDS,
693
+ statusMessage: SESSION_START_HOOK_STATUS_MESSAGE,
694
+ additionalContextLimit: SESSION_START_HOOK_CONTEXT_LIMIT
695
+ };
696
+ }
697
+ function handlerIsCanonical(entry, command) {
698
+ const want = canonicalHandler(command);
699
+ return Object.keys(want).every((k) => entry[k] === want[k]);
700
+ }
701
+ function upsertSessionStartHook(hooksFile, command) {
702
+ const root = cloneHooksFile(hooksFile);
703
+ if (root.hooks === void 0) {
704
+ root.hooks = {};
705
+ } else if (!isRecord2(root.hooks)) {
706
+ throw new Error("The 'hooks' key in the Codex hooks.json is not an object.");
707
+ }
708
+ const hooks = root.hooks;
709
+ if (hooks.SessionStart === void 0) {
710
+ hooks.SessionStart = [];
711
+ } else if (!Array.isArray(hooks.SessionStart)) {
712
+ throw new Error("The 'hooks.SessionStart' key in the Codex hooks.json is not an array.");
713
+ }
714
+ const groups = hooks.SessionStart;
715
+ for (const group of groups) {
716
+ if (!isRecord2(group) || !Array.isArray(group.hooks)) continue;
717
+ for (const entry of group.hooks) {
718
+ if (!isRecord2(entry)) continue;
719
+ if (typeof entry.command === "string" && isBasouSessionStartHookCommand(entry.command)) {
720
+ const unchanged = handlerIsCanonical(entry, command);
721
+ Object.assign(entry, canonicalHandler(command));
722
+ return { hooksFile: root, action: unchanged ? "unchanged" : "updated" };
723
+ }
724
+ }
725
+ }
726
+ groups.push({ matcher: SESSION_START_HOOK_MATCHER, hooks: [canonicalHandler(command)] });
727
+ return { hooksFile: root, action: "installed" };
728
+ }
729
+ function removeSessionStartHook(hooksFile) {
730
+ const root = cloneHooksFile(hooksFile);
731
+ if (!isRecord2(root.hooks) || !Array.isArray(root.hooks.SessionStart)) {
732
+ return { hooksFile: root, action: "absent" };
733
+ }
734
+ const hooks = root.hooks;
735
+ const groups = hooks.SessionStart;
736
+ let removed = false;
737
+ const kept = [];
738
+ for (const group of groups) {
739
+ if (!isRecord2(group) || !Array.isArray(group.hooks)) {
740
+ kept.push(group);
741
+ continue;
742
+ }
743
+ const keptHandlers = group.hooks.filter((entry) => {
744
+ if (isRecord2(entry) && typeof entry.command === "string" && isBasouSessionStartHookCommand(entry.command)) {
745
+ removed = true;
746
+ return false;
747
+ }
748
+ return true;
749
+ });
750
+ if (keptHandlers.length === group.hooks.length) {
751
+ kept.push(group);
752
+ } else if (keptHandlers.length > 0) {
753
+ group.hooks = keptHandlers;
754
+ kept.push(group);
755
+ }
756
+ }
757
+ if (!removed) return { hooksFile: root, action: "absent" };
758
+ if (kept.length === 0) {
759
+ delete hooks.SessionStart;
760
+ } else {
761
+ hooks.SessionStart = kept;
762
+ }
763
+ if (Object.keys(hooks).length === 0) {
764
+ delete root.hooks;
765
+ }
766
+ return { hooksFile: root, action: "removed" };
767
+ }
768
+ function findBasouSessionStartHook(hooksFile) {
769
+ if (!isRecord2(hooksFile) || !isRecord2(hooksFile.hooks) || !Array.isArray(hooksFile.hooks.SessionStart)) {
770
+ return null;
771
+ }
772
+ const groups = hooksFile.hooks.SessionStart;
773
+ for (let g = 0; g < groups.length; g++) {
774
+ const group = groups[g];
775
+ if (!isRecord2(group) || !Array.isArray(group.hooks)) continue;
776
+ for (let h = 0; h < group.hooks.length; h++) {
777
+ const entry = group.hooks[h];
778
+ if (isRecord2(entry) && typeof entry.command === "string" && isBasouSessionStartHookCommand(entry.command)) {
779
+ return {
780
+ command: entry.command,
781
+ groupIndex: g,
782
+ handlerIndex: h,
783
+ matcher: typeof group.matcher === "string" ? group.matcher : void 0,
784
+ handler: structuredClone(entry)
785
+ };
786
+ }
787
+ }
788
+ }
789
+ return null;
790
+ }
791
+
663
792
  // src/adapters/codex/rollout-importer.ts
664
793
  var CODEX_IMPORT_SOURCE = "codex-import";
665
794
  function codexRolloutToImportPayload(records, options) {
@@ -1783,31 +1912,26 @@ var WorkspaceMetaSchema = z4.looseObject({
1783
1912
  view: SourceRootSchema.optional()
1784
1913
  });
1785
1914
  var ChannelsSchema = z4.looseObject({
1786
- /**
1787
- * Render this workspace's orientation into `~/.codex/AGENTS.md` on `basou
1788
- * refresh` / `basou run codex`. A boolean today; if a face ever needs
1789
- * per-block control this widens to `boolean | object`, which existing
1790
- * manifests keep parsing — so the boolean does not foreclose that.
1791
- */
1915
+ /** Retired (see above). Parsed for compatibility; has no effect. */
1792
1916
  codex: z4.boolean().optional().meta({
1793
- description: "Opt in to rendering this workspace's orientation into the user-global ~/.codex/AGENTS.md on `basou refresh` and `basou run codex`. Off when absent: that file is auto-loaded by Codex for every project on the machine, so nothing is written there unless the workspace says so."
1917
+ description: "Retired and ignored. It used to opt this workspace into rendering its orientation into the user-global ~/.codex/AGENTS.md; nothing is written there any more \u2014 a Codex session receives the workspace's position from the SessionStart hook (`basou hook install codex`)."
1794
1918
  })
1795
1919
  });
1796
1920
  var PoliciesSchema = z4.looseObject({
1797
1921
  /**
1798
1922
  * This workspace's provenance must not persist where another workspace's
1799
- * tool reads it. That is the GOAL the key states; what it gates today is one
1800
- * thing: rendering the orientation into the user-global `~/.codex/AGENTS.md`
1801
- * (`basou refresh` / `basou run codex`) never, regardless of `channels`, so
1802
- * a confidential workspace cannot be re-enabled by a second declaration in
1803
- * the same file. It does not yet govern `basou protocol sync` (a global
1804
- * render, not a per-workspace one), and it gates writing only: it cannot keep
1805
- * another workspace's block out of this workspace's tool (see `basou channel
1806
- * clear`). Stated as a goal rather than as "never write" so that a transient
1807
- * render removed before anyone else can read it remains permissible.
1923
+ * tool reads it. That is the GOAL the key states. The one writer it used to
1924
+ * gate the orientation render into the user-global `~/.codex/AGENTS.md`
1925
+ * is retired, so today the key gates nothing: basou has no per-workspace path
1926
+ * that writes a position anywhere another workspace's tool reads. It is kept,
1927
+ * parsed, and honoured as a declaration, so a future writer to a shared
1928
+ * surface must consult it before it ships (`basou protocol sync`, a global
1929
+ * render rather than a per-workspace one, does not consult it). A top-level
1930
+ * `confidential` is still refused rather than ignored, for the same reason it
1931
+ * always was: a safety key that is not honoured must fail loudly.
1808
1932
  */
1809
1933
  confidential: z4.boolean().optional().meta({
1810
- description: "This workspace's provenance must not persist where another workspace's tool reads it. Today this means its orientation is never rendered into the user-global ~/.codex/AGENTS.md by `basou refresh` or `basou run codex`, regardless of `channels`. It does not yet govern `basou protocol sync`, and it gates writing only \u2014 it cannot keep another workspace's block out of this workspace's tool."
1934
+ description: "Posture: this workspace's provenance must not persist where another workspace's tool reads it. The one writer this used to gate \u2014 rendering the orientation into the user-global ~/.codex/AGENTS.md \u2014 is retired, so today the key gates nothing; it stays declared so a future writer to a shared surface must consult it. A top-level `confidential` is rejected rather than ignored."
1811
1935
  })
1812
1936
  });
1813
1937
  var ManifestObjectSchema = z4.looseObject({
@@ -1974,6 +2098,7 @@ var EN = {
1974
2098
  recentDecisionsLabel: "Decisions",
1975
2099
  recentNextStepLabel: "Next step",
1976
2100
  recentChangedLabel: "Changed",
2101
+ scratchOmitted: (count) => `(+${count} scratch omitted)`,
1977
2102
  trackCloseInstruction: "When finished, close it with `basou decision void <decision_id>`. It stays listed here every time until closed.",
1978
2103
  nextStepRecordedLabel: (age) => `Next step (recorded, ${age})`,
1979
2104
  noteStaleNote: (age) => `Note: work continued after this was recorded (latest activity ${age}), so this starting point may be stale.`,
@@ -2069,6 +2194,7 @@ var JA = {
2069
2194
  recentDecisionsLabel: "\u5224\u65AD",
2070
2195
  recentNextStepLabel: "\u6B21\u306E\u8D77\u70B9",
2071
2196
  recentChangedLabel: "\u5909\u66F4",
2197
+ scratchOmitted: (count) => `(\u4F5C\u696D\u7528\u4E00\u6642\u30D5\u30A1\u30A4\u30EB ${count} \u4EF6\u306F\u9664\u5916)`,
2072
2198
  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",
2073
2199
  nextStepRecordedLabel: (age) => `\u6B21\u306E\u8D77\u70B9 (\u8A18\u9332\u6E08\u307F, ${age})`,
2074
2200
  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`,
@@ -2409,8 +2535,8 @@ async function isStaleLock(lockPath) {
2409
2535
  }
2410
2536
  }
2411
2537
  function lockfilePath(paths, scope, resourceId) {
2412
- const sep = resourceId.indexOf("_");
2413
- const ulid2 = sep >= 0 ? resourceId.slice(sep + 1) : resourceId;
2538
+ const sep2 = resourceId.indexOf("_");
2539
+ const ulid2 = sep2 >= 0 ? resourceId.slice(sep2 + 1) : resourceId;
2414
2540
  return join3(paths.locks, `${scope}_${ulid2}.lock`);
2415
2541
  }
2416
2542
 
@@ -3473,6 +3599,29 @@ function pickLatestSubstantiveEntry(entries) {
3473
3599
  })[0];
3474
3600
  }
3475
3601
 
3602
+ // src/lib/transient-paths.ts
3603
+ import { tmpdir } from "os";
3604
+ import { normalize, sep } from "path";
3605
+ var TEMP_ROOTS = ["/tmp", "/var/tmp"];
3606
+ var MIN_ENCODED_DASHES = 3;
3607
+ function isEncodedWorkingDirectory(segment) {
3608
+ return segment.startsWith("-") && segment.split("-").length - 1 >= MIN_ENCODED_DASHES;
3609
+ }
3610
+ function withPrivateAlias(root) {
3611
+ const normalized = normalize(root);
3612
+ if (normalized.startsWith(`${sep}private${sep}`)) {
3613
+ return [normalized, normalized.slice(`${sep}private`.length)];
3614
+ }
3615
+ return [normalized, `${sep}private${normalized}`];
3616
+ }
3617
+ function isTransientToolPath(filePath, temp = tmpdir()) {
3618
+ const candidate = normalize(filePath);
3619
+ if (!candidate.startsWith(sep)) return false;
3620
+ const root = [...TEMP_ROOTS, temp].flatMap(withPrivateAlias).find((r) => candidate === r || candidate.startsWith(r + sep));
3621
+ if (root === void 0) return false;
3622
+ return candidate.slice(root.length).split(sep).some(isEncodedWorkingDirectory);
3623
+ }
3624
+
3476
3625
  // src/storage/tasks.ts
3477
3626
  import { createHash as createHash2 } from "crypto";
3478
3627
  import { mkdir as mkdir3, readdir as readdir4, readFile as readFile7, rename as rename2, stat as stat3, unlink as unlink3 } from "fs/promises";
@@ -5292,7 +5441,9 @@ async function renderHandoff(input) {
5292
5441
  (e) => e.session.session.status !== "archived" && e.session.session.source.kind !== "import"
5293
5442
  );
5294
5443
  const latestSession = pickLatestSubstantiveEntry(liveEntries);
5295
- const latestFiles = latestSession?.session.session.related_files ?? [];
5444
+ const latestFiles = (latestSession?.session.session.related_files ?? []).filter(
5445
+ (file) => !isTransientToolPath(file)
5446
+ );
5296
5447
  const sortedFiles = [...new Set(latestFiles)].sort();
5297
5448
  const displayedFiles = sortedFiles.slice(0, limit);
5298
5449
  const overflow = Math.max(0, sortedFiles.length - limit);
@@ -5510,9 +5661,9 @@ function shortHandoffId(sessionId) {
5510
5661
  return sessionId.slice(0, 10);
5511
5662
  }
5512
5663
  function shortIdWithPrefix(id) {
5513
- const sep = id.indexOf("_");
5514
- if (sep === -1) return id.slice(0, 10);
5515
- return id.slice(0, sep + 1) + id.slice(sep + 1, sep + 1 + 10);
5664
+ const sep2 = id.indexOf("_");
5665
+ if (sep2 === -1) return id.slice(0, 10);
5666
+ return id.slice(0, sep2 + 1) + id.slice(sep2 + 1, sep2 + 1 + 10);
5516
5667
  }
5517
5668
 
5518
5669
  // src/lib/duration.ts
@@ -5619,10 +5770,10 @@ async function resolveIdInternal(paths, input, kind, options = {}) {
5619
5770
  // src/lib/source-root-scope.ts
5620
5771
  import { promises as fs } from "fs";
5621
5772
  import { homedir as osHomedir } from "os";
5622
- import { basename as basename2, dirname as dirname3, isAbsolute, join as join14, normalize, relative, resolve as resolve2 } from "path";
5773
+ import { basename as basename2, dirname as dirname3, isAbsolute, join as join14, normalize as normalize2, relative, resolve as resolve2 } from "path";
5623
5774
  var AGENT_INFRA_DIRS = ["~/.claude", "~/.codex", "~/.basou"];
5624
5775
  async function realpathBestEffort(absPath) {
5625
- let current = normalize(absPath);
5776
+ let current = normalize2(absPath);
5626
5777
  const tail = [];
5627
5778
  for (let guard = 0; guard < 4096; guard += 1) {
5628
5779
  try {
@@ -5631,15 +5782,15 @@ async function realpathBestEffort(absPath) {
5631
5782
  } catch (error) {
5632
5783
  const code = error?.code;
5633
5784
  if (code !== "ENOENT" && code !== "ENOTDIR") {
5634
- return normalize(absPath);
5785
+ return normalize2(absPath);
5635
5786
  }
5636
5787
  const parent = dirname3(current);
5637
- if (parent === current) return normalize(absPath);
5788
+ if (parent === current) return normalize2(absPath);
5638
5789
  tail.push(basename2(current));
5639
5790
  current = parent;
5640
5791
  }
5641
5792
  }
5642
- return normalize(absPath);
5793
+ return normalize2(absPath);
5643
5794
  }
5644
5795
  function expandTilde(p, homedir5) {
5645
5796
  if (p === "~") return homedir5;
@@ -5648,8 +5799,8 @@ function expandTilde(p, homedir5) {
5648
5799
  }
5649
5800
  function toAbsolute(p, workingDirAbs, homedir5) {
5650
5801
  const expanded = expandTilde(p, homedir5);
5651
- if (isAbsolute(expanded)) return normalize(expanded);
5652
- return normalize(resolve2(workingDirAbs, expanded));
5802
+ if (isAbsolute(expanded)) return normalize2(expanded);
5803
+ return normalize2(resolve2(workingDirAbs, expanded));
5653
5804
  }
5654
5805
  function isUnder(child, parent) {
5655
5806
  if (child === parent) return true;
@@ -5666,12 +5817,12 @@ async function classifyFilesBySourceRoot(input) {
5666
5817
  const rootsAbs = [];
5667
5818
  for (const r of declared) {
5668
5819
  const expanded = expandTilde(r, homedir5);
5669
- const abs = isAbsolute(expanded) ? normalize(expanded) : normalize(resolve2(input.masterRoot, expanded));
5820
+ const abs = isAbsolute(expanded) ? normalize2(expanded) : normalize2(resolve2(input.masterRoot, expanded));
5670
5821
  rootsAbs.push(await realpathBestEffort(abs));
5671
5822
  }
5672
5823
  for (const e of input.extraInRoot ?? []) {
5673
5824
  const expanded = expandTilde(e, homedir5);
5674
- const abs = isAbsolute(expanded) ? normalize(expanded) : normalize(resolve2(homedir5, expanded));
5825
+ const abs = isAbsolute(expanded) ? normalize2(expanded) : normalize2(resolve2(homedir5, expanded));
5675
5826
  rootsAbs.push(await realpathBestEffort(abs));
5676
5827
  }
5677
5828
  if (rootsAbs.length === 0) {
@@ -5804,7 +5955,13 @@ async function summarizeOrientation(input) {
5804
5955
  const decisionTitles = (bucket?.decisions ?? []).filter((d) => !voidedDecisionIds.has(d.decisionId)).map((d) => d.title);
5805
5956
  const notes = bucket?.notes ?? [];
5806
5957
  const hasIntent = decisionTitles.length > 0 || notes.length > 0;
5807
- const files = hasIntent ? [] : [...new Set(entry.session.session.related_files ?? [])].sort().slice(0, FILES_PER_DIGEST);
5958
+ const files = hasIntent ? [] : [
5959
+ ...new Set(
5960
+ (entry.session.session.related_files ?? []).filter(
5961
+ (file) => !isTransientToolPath(file)
5962
+ )
5963
+ )
5964
+ ].sort().slice(0, FILES_PER_DIGEST);
5808
5965
  return {
5809
5966
  sessionId: entry.sessionId,
5810
5967
  label: entry.session.session.label ?? null,
@@ -5875,8 +6032,10 @@ async function summarizeOrientation(input) {
5875
6032
  } catch {
5876
6033
  sourceRoots = null;
5877
6034
  }
5878
- const latestFiles = latestEntry?.session.session.related_files ?? [];
6035
+ const recordedFiles = latestEntry?.session.session.related_files ?? [];
6036
+ const latestFiles = recordedFiles.filter((file) => !isTransientToolPath(file));
5879
6037
  const uniqueFiles = new Set(latestFiles);
6038
+ const omittedFiles = new Set(recordedFiles).size - uniqueFiles.size;
5880
6039
  const sortedFiles = [...uniqueFiles].sort();
5881
6040
  const displayed = sortedFiles.slice(0, limit);
5882
6041
  const overflow = Math.max(0, uniqueFiles.size - limit);
@@ -5907,7 +6066,7 @@ async function summarizeOrientation(input) {
5907
6066
  openTracks,
5908
6067
  latestNote,
5909
6068
  recentDirection,
5910
- relatedFiles: { displayed, overflow, outOfRoot },
6069
+ relatedFiles: { displayed, overflow, outOfRoot, omitted: omittedFiles },
5911
6070
  inFlightTasks,
5912
6071
  plannedTasks,
5913
6072
  pendingApprovals,
@@ -5995,10 +6154,16 @@ function formatOrientationBody(summary, opts) {
5995
6154
  `- ${t.common.latestDecisionLabel}: (no decisions recorded yet; capture with \`basou decision capture\`)`
5996
6155
  );
5997
6156
  }
5998
- if (summary.relatedFiles.displayed.length > 0) {
5999
- const shown = summary.relatedFiles.displayed.join(", ");
6000
- const more = summary.relatedFiles.overflow > 0 ? ` (... +${summary.relatedFiles.overflow} more)` : "";
6001
- lines.push(`- ${t.common.recentFilesLabel}: ${shown}${more}`);
6157
+ if (summary.relatedFiles.displayed.length > 0 || summary.relatedFiles.omitted > 0) {
6158
+ const parts = [];
6159
+ if (summary.relatedFiles.displayed.length > 0) {
6160
+ const more = summary.relatedFiles.overflow > 0 ? ` (... +${summary.relatedFiles.overflow} more)` : "";
6161
+ parts.push(`${summary.relatedFiles.displayed.join(", ")}${more}`);
6162
+ }
6163
+ if (summary.relatedFiles.omitted > 0) {
6164
+ parts.push(t.orientation.scratchOmitted(summary.relatedFiles.omitted));
6165
+ }
6166
+ lines.push(`- ${t.common.recentFilesLabel}: ${parts.join(" ")}`);
6002
6167
  if (summary.relatedFiles.outOfRoot.length > 0) {
6003
6168
  const OUT_OF_ROOT_DISPLAY = 10;
6004
6169
  const out = summary.relatedFiles.outOfRoot;
@@ -6247,9 +6412,9 @@ function suspectText(reason) {
6247
6412
  return "suspect";
6248
6413
  }
6249
6414
  function shortId(id) {
6250
- const sep = id.indexOf("_");
6251
- if (sep === -1) return id.slice(0, 10);
6252
- return id.slice(0, sep + 1) + id.slice(sep + 1, sep + 1 + 10);
6415
+ const sep2 = id.indexOf("_");
6416
+ if (sep2 === -1) return id.slice(0, 10);
6417
+ return id.slice(0, sep2 + 1) + id.slice(sep2 + 1, sep2 + 1 + 10);
6253
6418
  }
6254
6419
 
6255
6420
  // src/project/anchor-starter.ts
@@ -9451,6 +9616,10 @@ export {
9451
9616
  PROTOCOL_START,
9452
9617
  REVIEW_RECORD_NO_INPUT_HINT,
9453
9618
  RiskLevelSchema,
9619
+ SESSION_START_HOOK_CONTEXT_LIMIT,
9620
+ SESSION_START_HOOK_MATCHER,
9621
+ SESSION_START_HOOK_STATUS_MESSAGE,
9622
+ SESSION_START_HOOK_TIMEOUT_SECONDS,
9454
9623
  STOP_HOOK_TIMEOUT_SECONDS,
9455
9624
  STUCK_THRESHOLD_MS,
9456
9625
  SchemaVersionSchema,
@@ -9480,6 +9649,7 @@ export {
9480
9649
  buildJsonSchemas,
9481
9650
  buildReviewRecordLabel,
9482
9651
  buildReviewRecordedEvent,
9652
+ buildSessionStartHookCommand,
9483
9653
  buildStatusSnapshot,
9484
9654
  buildStopHookCommand,
9485
9655
  chainEvents,
@@ -9504,6 +9674,7 @@ export {
9504
9674
  enumerateTaskIds,
9505
9675
  evaluateStopHook,
9506
9676
  finalizeSessionYaml,
9677
+ findBasouSessionStartHook,
9507
9678
  findBasouStopHookCommand,
9508
9679
  findErrorCode,
9509
9680
  findReviewGaps,
@@ -9515,6 +9686,7 @@ export {
9515
9686
  importSessionFromJson,
9516
9687
  inspectChainTail,
9517
9688
  instructionMode,
9689
+ isBasouSessionStartHookCommand,
9518
9690
  isBasouStopHookCommand,
9519
9691
  isGitNotFound,
9520
9692
  isImportDerivedSource,
@@ -9556,6 +9728,7 @@ export {
9556
9728
  refreshTaskLinkedSessions,
9557
9729
  reimportPreservingId,
9558
9730
  removeMarkerSection,
9731
+ removeSessionStartHook,
9559
9732
  removeStopHook,
9560
9733
  renderAnchorStarter,
9561
9734
  renderDecisions,
@@ -9596,6 +9769,7 @@ export {
9596
9769
  ulid,
9597
9770
  unknownManifestKeys,
9598
9771
  updateTaskStatusWithEvent,
9772
+ upsertSessionStartHook,
9599
9773
  upsertStopHook,
9600
9774
  verifyEventsChain,
9601
9775
  viewStrings,