oh-my-opencode-slim 0.9.8 → 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -371,6 +371,8 @@ If any agent fails to respond, check your provider authentication and config fil
371
371
  | **[Tools](docs/tools.md)** | Background tasks, LSP, code search, formatters |
372
372
  | **[Configuration](docs/configuration.md)** | Config files, prompt overriding, JSONC, full option reference |
373
373
 
374
+ Slim only intercepts `apply_patch` before native execution. It rewrites recoverable stale patches, canonizes safe tolerant matches against the real file when unicode/trim drift is the only mismatch, keeps the authored `new_lines` bytes intact, preserves existing file EOL/final-newline state for updates, validates malformed patches strictly before helper execution, uses a conservative bounded LCS fallback, supports sequential `Update File` hunks on the same path through accumulated helper state, and blocks `apply_patch` before the native tool runs if any patch path falls outside the allowed root/worktree. This rescue does not extend to `edit` or `write`.
375
+
374
376
  ### 💡 Author's Setup
375
377
 
376
378
  | Doc | Contents |
@@ -0,0 +1,7 @@
1
+ import type { ParsedPatch } from './types';
2
+ export declare function normalizeUnicode(text: string): string;
3
+ export declare function stripHeredoc(input: string): string;
4
+ export declare function normalizePatchText(patchText: string): string;
5
+ export declare function parsePatch(patchText: string): ParsedPatch;
6
+ export declare function parsePatchStrict(patchText: string): ParsedPatch;
7
+ export declare function formatPatch(patch: ParsedPatch): string;
@@ -0,0 +1,25 @@
1
+ import type { ApplyPatchErrorCode, ApplyPatchErrorKind } from './types';
2
+ export declare class ApplyPatchError extends Error {
3
+ readonly kind: ApplyPatchErrorKind;
4
+ readonly code: ApplyPatchErrorCode;
5
+ readonly cause?: unknown;
6
+ constructor(kind: ApplyPatchErrorKind, code: ApplyPatchErrorCode, message: string, options?: {
7
+ cause?: unknown;
8
+ });
9
+ }
10
+ export declare function getErrorMessage(error: unknown): string;
11
+ export declare function createApplyPatchBlockedError(message: string, cause?: unknown): ApplyPatchError;
12
+ export declare function createApplyPatchValidationError(message: string, cause?: unknown): ApplyPatchError;
13
+ export declare function createApplyPatchVerificationError(message: string, cause?: unknown): ApplyPatchError;
14
+ export declare function createApplyPatchInternalError(message: string, cause?: unknown): ApplyPatchError;
15
+ export declare function isApplyPatchError(error: unknown): error is ApplyPatchError;
16
+ export declare function isApplyPatchBlockedError(error: unknown): boolean;
17
+ export declare function isApplyPatchValidationError(error: unknown): boolean;
18
+ export declare function isApplyPatchVerificationError(error: unknown): boolean;
19
+ export declare function isApplyPatchInternalError(error: unknown): boolean;
20
+ export declare function getApplyPatchErrorDetails(error: unknown): {
21
+ kind: ApplyPatchErrorKind;
22
+ code: ApplyPatchErrorCode;
23
+ message: string;
24
+ } | undefined;
25
+ export declare function ensureApplyPatchError(error: unknown, context: string): ApplyPatchError;
@@ -0,0 +1,26 @@
1
+ import { resolveUpdateChunksFromText } from './resolution';
2
+ import type { ApplyPatchRuntimeOptions, PatchHunk, UpdatePatchHunk } from './types';
3
+ export type PreparedFileState = {
4
+ exists: false;
5
+ derived: boolean;
6
+ } | {
7
+ exists: true;
8
+ text: string;
9
+ mode?: number;
10
+ derived: boolean;
11
+ };
12
+ export type PatchExecutionContext = {
13
+ hunks: PatchHunk[];
14
+ staged: Map<string, PreparedFileState>;
15
+ getPreparedFileState: (filePath: string, verb: 'update' | 'delete') => Promise<PreparedFileState>;
16
+ assertPreparedPathMissing: (filePath: string, verb: 'add' | 'move') => Promise<void>;
17
+ };
18
+ export type ResolvedPreparedUpdate = {
19
+ resolved: Awaited<ReturnType<typeof resolveUpdateChunksFromText>>['resolved'];
20
+ nextText: string;
21
+ };
22
+ export declare function isMissingPathError(error: unknown): boolean;
23
+ export declare function parseValidatedPatch(patchText: string): PatchHunk[];
24
+ export declare function createPatchExecutionContext(root: string, patchText: string, worktree?: string): Promise<PatchExecutionContext>;
25
+ export declare function resolvePreparedUpdate(filePath: string, currentText: string, hunk: UpdatePatchHunk, cfg: ApplyPatchRuntimeOptions): ResolvedPreparedUpdate;
26
+ export declare function stageAddedText(contents: string): string;
@@ -0,0 +1,15 @@
1
+ import type { PluginInput } from '@opencode-ai/plugin';
2
+ interface ToolExecuteBeforeInput {
3
+ tool: string;
4
+ directory?: string;
5
+ }
6
+ interface ToolExecuteBeforeOutput {
7
+ args?: {
8
+ patchText?: unknown;
9
+ [key: string]: unknown;
10
+ };
11
+ }
12
+ export declare function createApplyPatchHook(ctx: PluginInput): {
13
+ 'tool.execute.before': (input: ToolExecuteBeforeInput, output: ToolExecuteBeforeOutput) => Promise<void>;
14
+ };
15
+ export {};
@@ -0,0 +1,18 @@
1
+ import type { LineComparator, RescueResult, SeekHit } from './types';
2
+ export declare function equalExact(a: string, b: string): boolean;
3
+ export declare function equalUnicodeExact(a: string, b: string): boolean;
4
+ export declare function equalTrimEnd(a: string, b: string): boolean;
5
+ export declare function equalUnicodeTrimEnd(a: string, b: string): boolean;
6
+ export declare function equalTrim(a: string, b: string): boolean;
7
+ export declare function equalUnicodeTrim(a: string, b: string): boolean;
8
+ export declare const autoRescueComparators: LineComparator[];
9
+ export declare const permissiveComparators: LineComparator[];
10
+ export declare function seekMatch(lines: string[], pattern: string[], start: number, eof?: boolean): SeekHit | undefined;
11
+ export declare function seek(lines: string[], pattern: string[], start: number, eof?: boolean): number;
12
+ export declare function list(lines: string[], pattern: string[], start: number, same: LineComparator): number[];
13
+ export declare function sameRescueLine(a: string, b: string): boolean;
14
+ export declare function prefix(old_lines: string[], new_lines: string[]): number;
15
+ export declare function suffix(old_lines: string[], new_lines: string[], prefixLength: number): number;
16
+ export declare function rescueByPrefixSuffix(lines: string[], old_lines: string[], new_lines: string[], start: number): RescueResult;
17
+ export declare function score(a: string[], b: string[]): number;
18
+ export declare function rescueByLcs(lines: string[], old_lines: string[], new_lines: string[], start: number): RescueResult;
@@ -0,0 +1,3 @@
1
+ export { parseValidatedPatch } from './execution-context';
2
+ export { applyPreparedChanges, preparePatchChanges } from './prepared-changes';
3
+ export { rewritePatch, rewritePatchText } from './rewrite';
@@ -0,0 +1,2 @@
1
+ export { parsePatch } from './codec';
2
+ export { preparePatchChanges, rewritePatch, rewritePatchText, } from './operations';
@@ -0,0 +1,17 @@
1
+ import type { ApplyPatchRuntimeOptions, PreparedChange } from './types';
2
+ export declare function preparePatchChanges(root: string, patchText: string, cfg: ApplyPatchRuntimeOptions, worktree?: string): Promise<PreparedChange[]>;
3
+ /**
4
+ * Internal best-effort helper that applies the output of
5
+ * `preparePatchChanges()`: it snapshots all touched paths first and uses
6
+ * temp + rename for writes to regular files. It is not a universal multi-file
7
+ * transaction and is not perfect against concurrent external interference,
8
+ * but it avoids leaving silent partial states on normal apply failures.
9
+ *
10
+ * Contract: although it is exported for local tests/helpers, its expected
11
+ * input is the already prepared output of `preparePatchChanges()`. If it
12
+ * receives manual arrays, it revalidates the basic shape
13
+ * (types/text/normalized absolute paths) and filesystem invariants: it
14
+ * rejects updates/deletes/moves whose source does not exist, and add/move
15
+ * operations whose destination is already occupied.
16
+ */
17
+ export declare function applyPreparedChanges(changes: PreparedChange[]): Promise<void>;
@@ -0,0 +1,19 @@
1
+ import type { ApplyPatchRuntimeOptions, MatchHit, PatchChunk, ResolvedChunk } from './types';
2
+ export declare function readFileLines(file: string): Promise<string[]>;
3
+ export declare function resolveChunkStart(lines: string[], chunk: PatchChunk, start: number): number;
4
+ export declare function locateChunk(lines: string[], file: string, chunk: PatchChunk, start: number, cfg: ApplyPatchRuntimeOptions): ResolvedChunk;
5
+ export declare function applyHits(lines: string[], hits: MatchHit[], eol?: '\n' | '\r\n', hasFinalNewline?: boolean): string;
6
+ export declare function resolveUpdateChunks(file: string, chunks: PatchChunk[], cfg: ApplyPatchRuntimeOptions): Promise<{
7
+ lines: string[];
8
+ resolved: ResolvedChunk[];
9
+ eol: '\n' | '\r\n';
10
+ hasFinalNewline: boolean;
11
+ }>;
12
+ export declare function deriveNewContentFromText(file: string, text: string, chunks: PatchChunk[], cfg: ApplyPatchRuntimeOptions): string;
13
+ export declare function resolveUpdateChunksFromText(file: string, text: string, chunks: PatchChunk[], cfg: ApplyPatchRuntimeOptions): {
14
+ lines: string[];
15
+ resolved: ResolvedChunk[];
16
+ eol: '\n' | '\r\n';
17
+ hasFinalNewline: boolean;
18
+ };
19
+ export declare function deriveNewContent(file: string, chunks: PatchChunk[], cfg: ApplyPatchRuntimeOptions): Promise<string>;
@@ -0,0 +1,10 @@
1
+ import type { ApplyPatchRuntimeOptions } from './types';
2
+ export type RewritePatchResult = {
3
+ patchText: string;
4
+ changed: boolean;
5
+ rewrittenChunks: number;
6
+ totalChunks: number;
7
+ rewriteModes: string[];
8
+ };
9
+ export declare function rewritePatch(root: string, patchText: string, cfg: ApplyPatchRuntimeOptions, worktree?: string): Promise<RewritePatchResult>;
10
+ export declare function rewritePatchText(root: string, patchText: string, cfg: ApplyPatchRuntimeOptions, worktree?: string): Promise<string>;
@@ -0,0 +1,6 @@
1
+ import type { ApplyPatchRuntimeOptions } from './types';
2
+ export declare const DEFAULT_OPTIONS: ApplyPatchRuntimeOptions;
3
+ export declare function createTempDir(prefix?: string): Promise<string>;
4
+ export declare function writeFixture(root: string, relativePath: string, contents: string): Promise<void>;
5
+ export declare function readText(root: string, relativePath: string): Promise<string>;
6
+ export declare function applyPatch(root: string, patchText: string, cfg?: ApplyPatchRuntimeOptions): Promise<void>;
@@ -0,0 +1,80 @@
1
+ export type ApplyPatchRuntimeOptions = {
2
+ prefixSuffix: boolean;
3
+ lcsRescue: boolean;
4
+ };
5
+ export type ApplyPatchErrorKind = 'blocked' | 'validation' | 'verification' | 'internal';
6
+ export type ApplyPatchErrorCode = 'malformed_patch' | 'outside_workspace' | 'verification_failed' | 'internal_unexpected';
7
+ export type ApplyPatchRescueStrategy = 'prefix/suffix' | 'lcs' | 'anchor';
8
+ export type MatchComparatorName = 'exact' | 'unicode' | 'trim-end' | 'unicode-trim-end' | 'trim' | 'unicode-trim';
9
+ export type PatchChunk = {
10
+ old_lines: string[];
11
+ new_lines: string[];
12
+ change_context?: string;
13
+ is_end_of_file?: boolean;
14
+ };
15
+ export type AddPatchHunk = {
16
+ type: 'add';
17
+ path: string;
18
+ contents: string;
19
+ };
20
+ export type DeletePatchHunk = {
21
+ type: 'delete';
22
+ path: string;
23
+ };
24
+ export type UpdatePatchHunk = {
25
+ type: 'update';
26
+ path: string;
27
+ move_path?: string;
28
+ chunks: PatchChunk[];
29
+ };
30
+ export type PatchHunk = AddPatchHunk | DeletePatchHunk | UpdatePatchHunk;
31
+ export type ParsedPatch = {
32
+ hunks: PatchHunk[];
33
+ };
34
+ export type AddPreparedChange = {
35
+ type: 'add';
36
+ file: string;
37
+ text: string;
38
+ };
39
+ export type DeletePreparedChange = {
40
+ type: 'delete';
41
+ file: string;
42
+ };
43
+ export type UpdatePreparedChange = {
44
+ type: 'update';
45
+ file: string;
46
+ move?: string;
47
+ text: string;
48
+ };
49
+ export type PreparedChange = AddPreparedChange | DeletePreparedChange | UpdatePreparedChange;
50
+ export type MatchHit = {
51
+ start: number;
52
+ del: number;
53
+ add: string[];
54
+ };
55
+ export type SeekHit = {
56
+ index: number;
57
+ comparator: MatchComparatorName;
58
+ exact: boolean;
59
+ };
60
+ export type ResolvedChunk = {
61
+ hit: MatchHit;
62
+ old_lines: string[];
63
+ canonical_old_lines: string[];
64
+ canonical_new_lines: string[];
65
+ canonical_change_context?: string;
66
+ resolved_is_end_of_file: boolean;
67
+ rewritten: boolean;
68
+ strategy?: ApplyPatchRescueStrategy;
69
+ matchComparator?: MatchComparatorName;
70
+ };
71
+ export type RescueResult = {
72
+ kind: 'miss';
73
+ } | {
74
+ kind: 'ambiguous';
75
+ phase: 'prefix_suffix' | 'lcs';
76
+ } | {
77
+ kind: 'match';
78
+ hit: MatchHit;
79
+ };
80
+ export type LineComparator = (a: string, b: string) => boolean;
@@ -1,3 +1,4 @@
1
+ export { createApplyPatchHook } from './apply-patch';
1
2
  export type { AutoUpdateCheckerOptions } from './auto-update-checker';
2
3
  export { createAutoUpdateCheckerHook } from './auto-update-checker';
3
4
  export { createChatHeadersHook } from './chat-headers';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Post-tool nudge - appends a delegation reminder after file reads/writes.
2
+ * Post-tool nudge - queues a delegation reminder after file reads/writes.
3
3
  * Catches the "inspect/edit files → implement myself" anti-pattern.
4
4
  */
5
5
  interface ToolExecuteAfterInput {
@@ -7,12 +7,29 @@ interface ToolExecuteAfterInput {
7
7
  sessionID?: string;
8
8
  callID?: string;
9
9
  }
10
- interface ToolExecuteAfterOutput {
11
- title: string;
12
- output: string;
13
- metadata: Record<string, unknown>;
10
+ interface ChatSystemTransformInput {
11
+ sessionID?: string;
12
+ }
13
+ interface ChatSystemTransformOutput {
14
+ system: string[];
15
+ }
16
+ interface EventInput {
17
+ event: {
18
+ type: string;
19
+ properties?: {
20
+ info?: {
21
+ id?: string;
22
+ };
23
+ sessionID?: string;
24
+ };
25
+ };
26
+ }
27
+ interface PostFileToolNudgeOptions {
28
+ shouldInject?: (sessionID: string) => boolean;
14
29
  }
15
- export declare function createPostFileToolNudgeHook(): {
16
- 'tool.execute.after': (input: ToolExecuteAfterInput, output: ToolExecuteAfterOutput) => Promise<void>;
30
+ export declare function createPostFileToolNudgeHook(options?: PostFileToolNudgeOptions): {
31
+ 'tool.execute.after': (input: ToolExecuteAfterInput, _output: unknown) => Promise<void>;
32
+ 'experimental.chat.system.transform': (input: ChatSystemTransformInput, output: ChatSystemTransformOutput) => Promise<void>;
33
+ event: (input: EventInput) => Promise<void>;
17
34
  };
18
35
  export {};
@@ -12,6 +12,10 @@ export declare function createTodoContinuationHook(ctx: PluginInput, config?: {
12
12
  properties?: Record<string, unknown>;
13
13
  };
14
14
  }) => Promise<void>;
15
+ handleChatMessage: (input: {
16
+ sessionID: string;
17
+ agent?: string;
18
+ }) => void;
15
19
  handleCommandExecuteBefore: (input: {
16
20
  command: string;
17
21
  sessionID: string;