refactor(packages): extract utils package

This commit is contained in:
YeonGyu-Kim
2026-05-21 00:31:03 +09:00
parent c4fe747e15
commit a5c1d71001
52 changed files with 717 additions and 508 deletions
+2
View File
@@ -0,0 +1,2 @@
# Command: grep -rE "@opencode-ai|from [\"']opencode/|Bun\.|from [\"']bun:" --exclude='*.test.ts' --exclude='*.audit.test.ts' packages/utils/src/
# Result: no matches
+9
View File
@@ -0,0 +1,9 @@
12 files moved (write-file-atomically.ts deferred — see .omo/notepads/package-layering-refactor/decisions.md)
total 16
drwxr-xr-x@ 6 yeongyu staff 192 May 21 00:17 .
drwxr-xr-x@ 19 yeongyu staff 608 May 21 00:12 ..
drwxr-xr-x@ 5 yeongyu staff 160 May 21 00:17 node_modules
-rw-r--r--@ 1 yeongyu staff 487 May 21 00:12 package.json
drwxr-xr-x@ 25 yeongyu staff 800 May 21 00:13 src
-rw-r--r--@ 1 yeongyu staff 312 May 21 00:12 tsconfig.json
+12
View File
@@ -0,0 +1,12 @@
src/shared/deep-merge.ts: export { deepMerge, isPlainObject } from "@oh-my-opencode/utils"
src/shared/snake-case.ts: export { camelToSnake, objectToCamelCase, objectToSnakeCase, snakeToCamel, transformObjectKeys } from "@oh-my-opencode/utils"
src/shared/record-type-guard.ts: export { isRecord } from "@oh-my-opencode/utils"
src/shared/extract-semver.ts: export { extractSemverFromOutput } from "@oh-my-opencode/utils"
src/shared/frontmatter.ts: export { parseFrontmatter, type FrontmatterResult } from "@oh-my-opencode/utils"
src/shared/file-utils.ts: export { isMarkdownFile, isSymbolicLink, resolveSymlink, resolveSymlinkAsync } from "@oh-my-opencode/utils"
src/shared/contains-path.ts: export { containsPath, isWithinProject } from "@oh-my-opencode/utils"
src/shared/port-utils.ts: export {
src/shared/tool-name.ts: export { transformToolName } from "@oh-my-opencode/utils"
src/shared/replace-tool-args.ts: export { replaceToolArgs } from "@oh-my-opencode/utils"
src/shared/jsonc-parser.ts: export {
src/features/boulder-state/format-duration.ts: export { formatDurationHuman } from "@oh-my-opencode/utils"
+1
View File
@@ -0,0 +1 @@
Received: "# Debugging\n\nYou are a hypothesis-driven debugger. Two disciplines apply regardless of language, runtime, or whether you have source:\n\n1. **Runtime truth beats code reading.** Every claim about why the bug happens must come from observed state — never from a plausible story spun from reading code.\n2. **Leave no trace.** Debugging creates artifacts. Every artifact is journaled and removed before you call the task done.\n\nThe rest of this file is a map. **The knowledge is in `references/`.** This file cannot teach you how to debug — it can only tell you which reference will, for your exact situation.\n\n---\n\n# 🚨 READ THE REFERENCES. THIS IS NOT OPTIONAL.\n\n> **This skill is intentionally small.** Ninety percent of what you need to know lives in `references/`. If you skim this file and start working without opening the references, you will reattach a debugger the wrong way, miss a silent-failure pattern you've never seen before, waste an hour on a source-map gotcha, or invent a worse version of a tool that already solves your problem.\n>\n> **Every reference below is mandatory when its scenario applies.** \"I know this language\" is not an exemption. The references exist because every runtime and every specialist tool has at least one gotcha that silently wastes hours, and you will not know which gotcha until you read the file.\n>\n> **The gate rule**: before you run a command from a given reference's domain, you must have read that reference in this session. Re-reading across sessions is cheap. Guessing is expensive.\n\n---\n\n## Runtime Setup — MANDATORY READING BEFORE ATTACHING\n\nThe methodology is language-agnostic. The commands to launch, attach, breakpoint, and inspect are not. **Open the matching reference before Phase 0. Not during. Not after.**\n\n| Your runtime is… | Open this before attaching anything | Non-negotiable because… |\n|---|---|---|\n| Python (CPython, pytest, asyncio, Django, FastAPI) | 📖 **[references/runtimes/python.md](references/runtimes/python.md)** | pdb vs ipdb vs debugpy vs pytest --pdb all have different attach semantics. Async code needs special breakpoint handling. Wrappers like `poetry run` swallow flags. |\n| Node.js / tsx / ts-node / Bun / Deno (running source) | 📖 **[references/runtimes/node.md](references/runtimes/node.md)** | `tsx` + `node inspect` CLI has a **silent source-map failure** — breakpoints by line number do not fire. You will not notice unless you read this first. |\n| Rust (cargo, tokio, panics) | 📖 **[references/runtimes/rust.md](references/runtimes/rust.md)** | Release builds strip symbols. Tokio tasks need `tokio-console`. The borrow checker makes `dbg!` the faster tool most of the time. |\n| Go (goroutines, dlv, pprof, race) | 📖 **[references/runtimes/go.md](references/runtimes/go.md)** | Goroutine leaks and recovered panics are silent by default. `dlv` has a specific port convention. `go test -race` is the first thing to run, not the last. |\n| Native binary / stripped C/C++ / no source | 📖 **[references/runtimes/native-binary.md](references/runtimes/native-binary.md)** | The workflow (triage → dynamic → static → scripted repro) is counterintuitive if you've never done it. `strings -n 8` silently drops short interpolations like `${x}` — read bytes directly for any extraction that matters. macOS adds SIP / Mach-O / lldb specifics that don't apply on Linux. |\n| **Bundled-app binary** (Bun SEA, Node SEA, Deno compile, pkg, nexe, Electron, Tauri, PyInstaller) | 📖 **[references/runtimes/bundled-js-binary.md](references/runtimes/bundled-js-binary.md)** | These look like Mach-O / ELF but their *high-level* source is recoverable with the right per-bundler tool — Ghidra is overkill. Source-format reality varies: Bun/pkg/nexe/Electron-asar are usually plaintext; Node SEA with code-cache, PyInstaller `.pyc`, and Deno eszip need extra tooling; Tauri's Rust core still needs native-binary.md. Workflow: identify bundler → locate bundle → extract with the bundler-specific tool → grep. |\n\n...\n\n 7312 pass\n 1 skip\n 2 fail\n 24 snapshots, 16703 expect() calls\nRan 7315 tests across 744 files. [180.07s]
@@ -0,0 +1,10 @@
## [2026-05-20T15:12:12Z] Task 2 scope reduction
Dropped `write-file-atomically.ts` from utils extraction.
Reason: depends on omo-specific `tolerant-fsync` chain (classify-path-environment, fsync-skip-tracker, logger).
Decision deferred to a future task.
Net effect: utils package ships 12 files instead of 13.
## [2026-05-20T15:12:12Z] Task 2 jsonc-parser decoupling
Choice: Option A
Reason: Small call-site surface; keeps full jsonc-parser API together in the new package and avoids split ownership.
Call sites changed: src/plugin-config.ts, src/cli/config-manager/config-context.ts, src/cli/doctor/checks/config.ts, src/cli/doctor/checks/model-resolution-config.ts, src/cli/doctor/checks/team-mode.ts, src/cli/doctor/checks/tools-lsp.ts, src/shared/project-discovery-dirs.ts, packages/utils/src/jsonc-parser.test.ts, packages/utils/src/jsonc-parser.memoization.test.ts
@@ -0,0 +1,23 @@
## [2026-05-20T15:04:52Z] Task 1 baseline
- Total tests: 7315
- Pass: 7312, Fail: 2, Skip: 1
- Build exit code: 0 (dist/ size: 13M, 1456 files)
- Typecheck exit code: 0
- Packages: 15 package.json files under packages/, 3 private (ast-grep-mcp, rules-core, web)
- Anomalies observed: 2 pre-existing test failures in `src/features/opencode-skill-loader/skill-content.test.ts` — ambiguous short name resolution returns 2 resolved skills instead of 1 when "debugging" + "playwright" are queried together. This is a REAL baseline failure; do not fix as part of this refactor plan unless explicitly directed.
## [2026-05-20T15:12:12Z] Task 2 pre-flight
- `src/shared/deep-merge.ts`: pure, zero imports.
- `src/shared/snake-case.ts`: imports `./deep-merge` only (moved together) — pure after extraction.
- `src/shared/record-type-guard.ts`: pure, zero imports.
- `src/shared/extract-semver.ts`: pure, zero imports.
- `src/shared/frontmatter.ts`: imports `js-yaml` only.
- `src/shared/file-utils.ts`: imports `fs` only.
- `src/shared/contains-path.ts`: imports `fs` and `path` only.
- `src/shared/port-utils.ts`: imports `node:net` only.
- `src/shared/tool-name.ts`: pure, zero imports.
- `src/shared/replace-tool-args.ts`: pure, zero imports.
- `src/features/boulder-state/format-duration.ts`: pure, zero imports.
- `src/shared/jsonc-parser.ts`: coupled to plugin basenames; decoupled via parameterized `detectPluginConfigFile(dir, options)`.
- `src/shared/write-file-atomically.ts`: depends on omo-specific `./tolerant-fsync`; extraction deferred by scope decision (kept in-place).