@basou/core 0.28.0 → 0.29.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
@@ -45,6 +45,65 @@ declare function resolveClaudeCodeCommand(lookup?: CommandLookup): Promise<{
45
45
  */
46
46
  declare function summarizeAdapterOutput(_stream: "stdout" | "stderr", _raw: string): string;
47
47
 
48
+ /**
49
+ * Pure transforms for registering / removing basou's Stop hook inside a parsed
50
+ * Claude Code settings.json object. No disk or environment access: the CLI reads
51
+ * and writes the file, parses the JSON, and passes the object here so the
52
+ * merge/removal logic stays deterministic and unit-testable.
53
+ *
54
+ * settings.json holds many unrelated keys (permissions, model, other hooks);
55
+ * these functions clone the input and touch ONLY the `hooks.Stop` entry that
56
+ * basou owns, preserving everything else byte-for-byte through the round-trip.
57
+ */
58
+ /** Seconds before Claude Code kills the hook process. Matches the documented orient (SessionStart) hook. */
59
+ declare const STOP_HOOK_TIMEOUT_SECONDS = 20;
60
+ declare function isBasouStopHookCommand(command: string): boolean;
61
+ type BuildStopHookCommandOptions = {
62
+ /** Absolute path to the CLI entry to invoke (the running dist/index.js). */
63
+ cliEntry: string;
64
+ /** Register the blocking (opt-in enforcement) form. */
65
+ block?: boolean;
66
+ /** Override the file-edit threshold passed to `hook stop`. */
67
+ minEdits?: number;
68
+ };
69
+ /**
70
+ * Build the shell command basou registers as a Stop hook. Uses the node path
71
+ * (not the `basou` alias, which is often absent from a non-interactive hook's
72
+ * PATH) and a `2>/dev/null || true` wrapper so a stale/incorrect dist path or
73
+ * any crash fails open — no per-turn error noise. The wrapper is safe for the
74
+ * blocking form because that emits `decision:"block"` on stdout with exit 0;
75
+ * `|| true` would defeat an exit-2 block but leaves the JSON form intact.
76
+ *
77
+ * The entry path is shell-quoted so a home/project directory containing spaces
78
+ * or shell metacharacters still invokes correctly (an unquoted path with a
79
+ * space would split into the wrong argv and the hook would silently no-op).
80
+ */
81
+ declare function buildStopHookCommand(options: BuildStopHookCommandOptions): string;
82
+ type ClaudeSettings = Record<string, unknown>;
83
+ type StopHookUpsert = {
84
+ settings: ClaudeSettings;
85
+ /** `installed` = a new entry was appended; `updated` = an existing basou entry was rewritten; `unchanged` = already canonical. */
86
+ action: "installed" | "updated" | "unchanged";
87
+ };
88
+ type StopHookRemoval = {
89
+ settings: ClaudeSettings;
90
+ action: "removed" | "absent";
91
+ };
92
+ /**
93
+ * Register (or upgrade in place) basou's Stop hook. Idempotent: an existing
94
+ * basou Stop hook is rewritten to the canonical command + timeout; a foreign
95
+ * Stop hook or any other settings key is left untouched.
96
+ */
97
+ declare function upsertStopHook(settings: unknown, command: string): StopHookUpsert;
98
+ /**
99
+ * Remove every basou-owned Stop hook. A group emptied by the removal is dropped;
100
+ * a now-empty `hooks.Stop` / `hooks` container is deleted so the file does not
101
+ * accumulate empty scaffolding. Foreign hooks and other keys are preserved.
102
+ */
103
+ declare function removeStopHook(settings: unknown): StopHookRemoval;
104
+ /** Return the installed basou Stop hook command, or null if none is registered. */
105
+ declare function findBasouStopHookCommand(settings: unknown): string | null;
106
+
48
107
  /**
49
108
  * Schema for `.basou/manifest.yaml`. The minimal manifest carries
50
109
  * schema_version, basou_version, workspace metadata, project info, enabled
@@ -5827,4 +5886,4 @@ declare function overwriteYamlFile(filePath: string, value: unknown): Promise<vo
5827
5886
  */
5828
5887
  declare const BASOU_CORE_VERSION = "0.1.0";
5829
5888
 
5830
- export { ACTIVE_GAP_CAP_MS, AGENT_INFRA_DIRS, type ActiveTimeBasis, type AdapterOutputEvent, type AdoptCandidate, type AdoptCandidateKind, 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 BulkChainResult, CLAUDE_IMPORT_SOURCE, CODEX_IMPORT_SOURCE, type CaptureMode, type ChainBreakReason, type ChainTailState, type ChainVerdict, type ChainVerdictStatus, type ChainedEvents, ChildProcessRunner, type CitedReview, type ClaudeTranscriptRecord, type ClaudeTranscriptToPayloadOptions, 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 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 NoteAddedEvent, 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 ProcessRunner, type PublishKind, type PublishTarget, 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 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 ReviewGapRepoSummary, type ReviewGapUnit, type ReviewGapVerdict, type ReviewGapsInput, type ReviewGapsSummary, type RiskLevel, RiskLevelSchema, type RosterAdoptionPlan, type RosterDriftSummary, type RunOptions, type RunResult, STUCK_THRESHOLD_MS, type SanitizePathOptions, type SanitizeRelatedFilesResult, SchemaVersionSchema, 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 StopHookSilentReason, 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 UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type ViewCollision, type ViewConflict, type ViewLinkState, type ViewRepoFact, type ViewStrayUnknown, 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, buildStatusSnapshot, chainEvents, chainRawJsonLines, classifyFilesBySourceRoot, classifyRetrofit, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, evaluateStopHook, finalizeSessionYaml, findErrorCode, findReviewGaps, formatDurationMs, genesisHash, getDiff, getSnapshot, importSessionFromJson, inspectChainTail, instructionMode, isGitNotFound, isImportDerivedSource, isLazyExpired, isRenderable, isValidPrefixedId, lineHash, linkYamlFile, loadApproval, loadFederatedSessionEntries, loadSessionEntries, loadTaskEntries, normalizeRepoKey, normalizeRepoPath, overwriteYamlFile, parseDuration, parseMarkers, pathBasename, planArchive, planGitignore, planRename, planRosterAdoption, planWorkspaceView, prefixedUlid, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, rechainSessionInPlace, reconcileAllTasks, reconcileSourceRoots, reconcileTask, refreshTaskLinkedSessions, reimportPreservingId, removeMarkerSection, renderDecisions, renderHandoff, renderOrientation, renderPresetBlock, renderReport, renderWithMarkers, replayEvents, resolveBasouRepositoryRoot, resolveClaudeCodeCommand, resolveRepositoryRoot, resolveSessionId, resolveTaskId, safeSimpleGit, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, serializeEventLine, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, summarizeOrientation, summarizePresetPlan, summarizeRosterDrift, summarizeSymlinkPlan, summarizeWiring, tryRemoteUrl, ulid, unknownManifestKeys, updateTaskStatusWithEvent, verifyEventsChain, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
5889
+ export { ACTIVE_GAP_CAP_MS, AGENT_INFRA_DIRS, type ActiveTimeBasis, type AdapterOutputEvent, type AdoptCandidate, type AdoptCandidateKind, 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 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 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 NoteAddedEvent, 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 ProcessRunner, type PublishKind, type PublishTarget, 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 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 ReviewGapRepoSummary, type ReviewGapUnit, type ReviewGapVerdict, type ReviewGapsInput, type ReviewGapsSummary, type RiskLevel, RiskLevelSchema, type RosterAdoptionPlan, type RosterDriftSummary, type RunOptions, type RunResult, STOP_HOOK_TIMEOUT_SECONDS, STUCK_THRESHOLD_MS, type SanitizePathOptions, type SanitizeRelatedFilesResult, SchemaVersionSchema, 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 UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type ViewCollision, type ViewConflict, type ViewLinkState, type ViewRepoFact, type ViewStrayUnknown, 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, buildStatusSnapshot, buildStopHookCommand, chainEvents, chainRawJsonLines, classifyFilesBySourceRoot, classifyRetrofit, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, evaluateStopHook, finalizeSessionYaml, findBasouStopHookCommand, findErrorCode, findReviewGaps, formatDurationMs, genesisHash, getDiff, getSnapshot, importSessionFromJson, inspectChainTail, instructionMode, isBasouStopHookCommand, isGitNotFound, isImportDerivedSource, isLazyExpired, isRenderable, isValidPrefixedId, lineHash, linkYamlFile, loadApproval, loadFederatedSessionEntries, loadSessionEntries, loadTaskEntries, normalizeRepoKey, normalizeRepoPath, overwriteYamlFile, parseDuration, parseMarkers, pathBasename, planArchive, planGitignore, planRename, planRosterAdoption, planWorkspaceView, prefixedUlid, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, rechainSessionInPlace, reconcileAllTasks, reconcileSourceRoots, reconcileTask, refreshTaskLinkedSessions, reimportPreservingId, removeMarkerSection, removeStopHook, renderDecisions, renderHandoff, renderOrientation, renderPresetBlock, renderReport, renderWithMarkers, replayEvents, resolveBasouRepositoryRoot, resolveClaudeCodeCommand, resolveRepositoryRoot, resolveSessionId, resolveTaskId, safeSimpleGit, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, serializeEventLine, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, summarizeOrientation, summarizePresetPlan, summarizeRosterDrift, summarizeSymlinkPlan, summarizeWiring, tryRemoteUrl, ulid, unknownManifestKeys, updateTaskStatusWithEvent, upsertStopHook, verifyEventsChain, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
package/dist/index.js CHANGED
@@ -21,6 +21,118 @@ function summarizeAdapterOutput(_stream, _raw) {
21
21
  throw new Error("adapter_output summary is not implemented in this release");
22
22
  }
23
23
 
24
+ // src/adapters/claude-code/settings-hook.ts
25
+ var STOP_HOOK_TIMEOUT_SECONDS = 20;
26
+ var BASOU_STOP_HOOK = /(?:\bbasou|(?:@basou|packages)\/cli\/dist\/index\.js['"]?)\s+hook\s+stop\b/;
27
+ function isBasouStopHookCommand(command) {
28
+ return BASOU_STOP_HOOK.test(command);
29
+ }
30
+ function shellQuote(value) {
31
+ return `'${value.replace(/'/g, "'\\''")}'`;
32
+ }
33
+ function buildStopHookCommand(options) {
34
+ const flags = [];
35
+ if (options.block === true) flags.push("--block");
36
+ if (options.minEdits !== void 0) flags.push(`--min-edits ${options.minEdits}`);
37
+ const suffix = flags.length > 0 ? ` ${flags.join(" ")}` : "";
38
+ return `node ${shellQuote(options.cliEntry)} hook stop${suffix} 2>/dev/null || true`;
39
+ }
40
+ function isRecord(value) {
41
+ return typeof value === "object" && value !== null && !Array.isArray(value);
42
+ }
43
+ function cloneSettings(settings) {
44
+ if (settings === void 0 || settings === null) return {};
45
+ if (!isRecord(settings)) {
46
+ throw new Error("Claude settings is not a JSON object.");
47
+ }
48
+ return structuredClone(settings);
49
+ }
50
+ function upsertStopHook(settings, command) {
51
+ const root = cloneSettings(settings);
52
+ if (root.hooks === void 0) {
53
+ root.hooks = {};
54
+ } else if (!isRecord(root.hooks)) {
55
+ throw new Error("The 'hooks' key in Claude settings is not an object.");
56
+ }
57
+ const hooks = root.hooks;
58
+ if (hooks.Stop === void 0) {
59
+ hooks.Stop = [];
60
+ } else if (!Array.isArray(hooks.Stop)) {
61
+ throw new Error("The 'hooks.Stop' key in Claude settings is not an array.");
62
+ }
63
+ const stop = hooks.Stop;
64
+ for (const group of stop) {
65
+ if (!isRecord(group) || !Array.isArray(group.hooks)) continue;
66
+ for (const entry of group.hooks) {
67
+ if (!isRecord(entry)) continue;
68
+ if (typeof entry.command === "string" && isBasouStopHookCommand(entry.command)) {
69
+ const unchanged = entry.type === "command" && entry.command === command && entry.timeout === STOP_HOOK_TIMEOUT_SECONDS;
70
+ entry.type = "command";
71
+ entry.command = command;
72
+ entry.timeout = STOP_HOOK_TIMEOUT_SECONDS;
73
+ return { settings: root, action: unchanged ? "unchanged" : "updated" };
74
+ }
75
+ }
76
+ }
77
+ stop.push({ hooks: [{ type: "command", command, timeout: STOP_HOOK_TIMEOUT_SECONDS }] });
78
+ return { settings: root, action: "installed" };
79
+ }
80
+ function removeStopHook(settings) {
81
+ const root = cloneSettings(settings);
82
+ if (!isRecord(root.hooks) || !Array.isArray(root.hooks.Stop)) {
83
+ return { settings: root, action: "absent" };
84
+ }
85
+ const hooks = root.hooks;
86
+ const stop = hooks.Stop;
87
+ let removed = false;
88
+ const newStop = [];
89
+ for (const group of stop) {
90
+ if (!isRecord(group) || !Array.isArray(group.hooks)) {
91
+ newStop.push(group);
92
+ continue;
93
+ }
94
+ const keptHooks = group.hooks.filter((entry) => {
95
+ if (isRecord(entry) && typeof entry.command === "string" && isBasouStopHookCommand(entry.command)) {
96
+ removed = true;
97
+ return false;
98
+ }
99
+ return true;
100
+ });
101
+ if (keptHooks.length === group.hooks.length) {
102
+ newStop.push(group);
103
+ } else if (keptHooks.length > 0) {
104
+ group.hooks = keptHooks;
105
+ newStop.push(group);
106
+ }
107
+ }
108
+ if (!removed) {
109
+ return { settings: root, action: "absent" };
110
+ }
111
+ if (newStop.length === 0) {
112
+ delete hooks.Stop;
113
+ } else {
114
+ hooks.Stop = newStop;
115
+ }
116
+ if (Object.keys(hooks).length === 0) {
117
+ delete root.hooks;
118
+ }
119
+ return { settings: root, action: "removed" };
120
+ }
121
+ function findBasouStopHookCommand(settings) {
122
+ if (!isRecord(settings) || !isRecord(settings.hooks) || !Array.isArray(settings.hooks.Stop)) {
123
+ return null;
124
+ }
125
+ for (const group of settings.hooks.Stop) {
126
+ if (!isRecord(group) || !Array.isArray(group.hooks)) continue;
127
+ for (const entry of group.hooks) {
128
+ if (isRecord(entry) && typeof entry.command === "string" && isBasouStopHookCommand(entry.command)) {
129
+ return entry.command;
130
+ }
131
+ }
132
+ }
133
+ return null;
134
+ }
135
+
24
136
  // src/adapters/claude-code/ask-user-question.ts
25
137
  function readString(value) {
26
138
  return typeof value === "string" && value.length > 0 ? value : void 0;
@@ -7664,6 +7776,7 @@ export {
7664
7776
  PROTOCOL_END,
7665
7777
  PROTOCOL_START,
7666
7778
  RiskLevelSchema,
7779
+ STOP_HOOK_TIMEOUT_SECONDS,
7667
7780
  STUCK_THRESHOLD_MS,
7668
7781
  SchemaVersionSchema,
7669
7782
  SessionIdSchema,
@@ -7691,6 +7804,7 @@ export {
7691
7804
  basouPaths,
7692
7805
  buildJsonSchemas,
7693
7806
  buildStatusSnapshot,
7807
+ buildStopHookCommand,
7694
7808
  chainEvents,
7695
7809
  chainRawJsonLines,
7696
7810
  classifyFilesBySourceRoot,
@@ -7712,6 +7826,7 @@ export {
7712
7826
  enumerateTaskIds,
7713
7827
  evaluateStopHook,
7714
7828
  finalizeSessionYaml,
7829
+ findBasouStopHookCommand,
7715
7830
  findErrorCode,
7716
7831
  findReviewGaps,
7717
7832
  formatDurationMs,
@@ -7721,6 +7836,7 @@ export {
7721
7836
  importSessionFromJson,
7722
7837
  inspectChainTail,
7723
7838
  instructionMode,
7839
+ isBasouStopHookCommand,
7724
7840
  isGitNotFound,
7725
7841
  isImportDerivedSource,
7726
7842
  isLazyExpired,
@@ -7759,6 +7875,7 @@ export {
7759
7875
  refreshTaskLinkedSessions,
7760
7876
  reimportPreservingId,
7761
7877
  removeMarkerSection,
7878
+ removeStopHook,
7762
7879
  renderDecisions,
7763
7880
  renderHandoff,
7764
7881
  renderOrientation,
@@ -7788,6 +7905,7 @@ export {
7788
7905
  ulid,
7789
7906
  unknownManifestKeys,
7790
7907
  updateTaskStatusWithEvent,
7908
+ upsertStopHook,
7791
7909
  verifyEventsChain,
7792
7910
  writeEventsBulk,
7793
7911
  writeManifest,