mirror of
https://github.com/Yeachan-Heo/oh-my-claudecode.git
synced 2026-10-19 01:39:52 -05:00
main
7
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d893c59e92 |
feat(installer): add claudeMdEnabled opt-out for the global CLAUDE.md (#4383)
* feat(installer): add claudeMdEnabled opt-out for the global CLAUDE.md Some users maintain their own global CLAUDE.md and do not want every install or update to merge the OMC block into it. Add a claudeMdEnabled key to .omc-config.json, following the hudEnabled pattern: it defaults to true and only an explicit false disables it. When it is false, the global CLAUDE.md and CLAUDE-omc.md are neither written nor merged by the installer (install, update reconcile) or by the global modes of the CLAUDE.md coordinator used by omc-setup. Each skip is logged. Project-scoped (local) CLAUDE.md handling is unchanged. Doctor and SessionStart respect the flag as well: doctor reports the missing markers as informational instead of asking for omc-setup, and SessionStart no longer reports CLAUDE.md version drift. The key is added to the doctor known-keys list and the auto-update config type, and is documented in docs/REFERENCE.md. * fix(skills): respect claudeMdEnabled in omc-doctor and omc-setup; test config passthrough * fix(doctor): report the claudeMdEnabled opt-out when no CLAUDE.md exists * fix(omc-doctor): expand ~ in CLAUDE_CONFIG_DIR before reading the opt-out The Step 4 opt-out one-liner passed CLAUDE_CONFIG_DIR to path.join as given, so a value like ~/.claude-alt was read relative to the current directory. Expand it the way getClaudeConfigDir() does: trim, empty falls back to ~/.claude, a bare ~ is the home directory, and a leading ~/ or ~\ is joined onto it. * docs(omc-setup): clarify claudeMdEnabled skip * chore(inventory): refresh graph for PR #4383 * fix(omc-doctor): fail closed when the claudeMdEnabled probe cannot run Step 4 treated any outcome other than the opt-out line as "enabled". When node is not on PATH (the case Step 5 diagnoses) the probe prints nothing, so the Missing/Outdated CLAUDE.md fix could overwrite a file the user opted out of managing. Continue with the CLAUDE.md checks only when the probe printed exactly "CLAUDE.md management enabled". If it printed neither line or failed, report a WARN, skip the rest of Step 4 and the CLAUDE.md fix, and never create or overwrite CLAUDE.md or CLAUDE-omc.md. Add tests pinning the rule and running the extracted probe with and without node on PATH. Refresh the inventory graph. * fix(doctor): report disabled global CLAUDE.md management * fix(doctor): do not recommend omc-setup cleanup while CLAUDE.md management is off With claudeMdEnabled set to false, omc-setup skips every global CLAUDE.md write, so the exact-legacy-guide cleanup hint it printed could not work. The hint was still shown for a markerless file that matches a known legacy guide. When management is disabled, say that setup cleanup is skipped and that the user can re-enable claudeMdEnabled or remove the legacy content manually. The enabled path keeps its existing output. Add a test for the disabled case and refresh the inventory graph. * fix: honor CLAUDE.md opt-out on committed setup path * chore(inventory): refresh graph after latest dev merge * test(installer): cover coordinator CLAUDE opt-out * chore(inventory): refresh graph after latest dev merge * fix(omc-setup): skip install style prompt when disabled * chore(inventory): refresh graph after latest dev merge --------- Co-authored-by: Bellman <54757707+Yeachan-Heo@users.noreply.github.com> |
||
|
|
edb331a451 |
fix(session-start): bound the stabilization helper lifetime and batch the darwin identity probe (#4384)
* fix(session-start): bound the stabilization helper lifetime and batch the darwin identity probe The detached --stabilize-legacy-tool-failure-state helper only left its loop when the quiet window had passed and no other plugin version was live. Sessions on an older version can stay open for days, so the helper (one per .omc root) never exited and kept rescanning the occupancy registry every 5 s. - Give the helper a hard lifetime: max(10 x stabilization window, 60 s), at most 5 min with the 30 s production window. At the deadline the helper stops waiting and returns. The loop still purges legacy artifacts on every iteration whenever any exist, as before; the deadline adds no extra purge. Trade-off: an older-version session that keeps writing legacy artifacts after the deadline leaves them on disk until the next SessionStart in that root starts a new helper, whereas before the helper scrubbed them within about 250 ms for as long as that session lived. An optional fifth argument can shorten the cap; the cap also defaults when the function is called without it. - On darwin, resolve process start identities with one `ps -p pid1,pid2,... -o pid=,lstart=` per scan instead of one spawn per registry record, like the win32 batch. ps exits 1 when none of the pids exist (some BSD builds also when only some are gone), so status 0 and 1 are both parsed; any other failure keeps the records. The Linux /proc path and the rescan intervals are unchanged. Applied to both the plugin scripts and the standalone hook templates. Inventory regenerated for the changed sources. Fixes #4382 * fix(session-start): stop the stabilization helper from purging after its lifetime ends The helper checked its hard lifetime only at the top of the loop. The cleanup it ran inside an iteration waited up to 2.5 s for the PostToolUseFailure state lock on its own, so a lock that freed up after the deadline but before that wait ran out still let the helper delete legacy artifacts past its lifetime. - Bound the cleanup lock wait by the time left in the lifetime. - Check the deadline again once the lock is held: past it, release the lock and leave the artifacts for the next SessionStart in that root. - Callers without a lifetime (SessionStart and the deferred purge child) keep the 2.5 s wait and purge whenever they hold the lock. Applied to both the plugin script and the standalone hook template, with a regression test for each that holds the lock past a shortened lifetime. Inventory regenerated for the changed sources. * fix(session-start): bound the stabilization helper's worker lock wait by its lifetime The helper took its cleanup-worker lock with the lock library's default wait of about 3 s and no overall timeout. When the state directory already existed, or appeared at the deadline, the loop left the state-directory wait without checking the lifetime, so a held worker lock kept the helper alive past it. - Check the deadline at the top of the state-directory wait, before leaving it. - Pass the time left in the lifetime as the acquisition's overall timeout; a spent lifetime fails the acquisition at once and the helper exits. - Check the deadline again once the worker lock is held and release it without doing any work when it has passed. - Other parameters of the lock acquisition keep their defaults. Applied to both the plugin script and the standalone hook template, with a regression test for each that holds the worker lock past a 200 ms lifetime. Inventory regenerated for the changed sources. * fix(session-start): bound the stabilization helper's plugin occupancy probes by its lifetime The helper scanned for older plugin versions without a probe timeout, so a slow Darwin batched ps (2 s) or PowerShell host (3 s) could run past the helper's hard lifetime, before the wait loop and again while the cleanup-worker lock was held. - Pass the time left in the lifetime as the probe timeout of every older-plugin scan on the helper's path. The probe never exceeds its platform default, and a spent lifetime skips it. - A probe that times out or has no time left identifies nothing, so every registry record is kept and its root counts as occupied: nothing is purged, the worker lock is released and the helper exits. - Callers without a lifetime keep the default probe timeout. The darwin scan stays a single batched ps. Applied to both the plugin script and the standalone hook template, with a regression test for each that delays the post-lock darwin ps under a shortened lifetime and asserts a timely exit with the worker lock released. Inventory regenerated for the changed sources. * chore(inventory): refresh graph for current dev * chore(inventory): refresh graph for current dev --------- Co-authored-by: Bellman <54757707+Yeachan-Heo@users.noreply.github.com> |
||
|
|
0b07c4d71c |
test(atomic-write): give SQLite contention timing bounds a runner margin (#4385)
The two SQLite contention tests asserted that a contended acquire with a 75 ms busy timeout returns in under 500 ms. On macOS CI runners the "PreCompact restore" job failed on that bound intermittently (575 and 533 ms). The lock code issues a single busy wait for this call: attempts is 1, only BEGIN IMMEDIATE blocks, and the open, pragma and CREATE TABLE IF NOT EXISTS statements take well under 10 ms. SQLite's busy handler sums the sleeps it requests rather than watching a clock, so the real wait is a little over the nominal timeout locally (about 85 to 120 ms for 75 ms, roughly 1.1 times the timeout plus a small constant at 300 and 1000 ms) and grows with scheduler and VM stalls on a shared runner. The wait is bounded as intended; the wall-clock ceiling was too tight for a CI runner. Measure with the monotonic clock, allow the nominal timeout plus a 1000 ms margin (still under the 2000 ms default, so a call that ignores the caller's timeout fails), and add a 50 ms floor so a call that does not wait at all fails too. Both bounds were checked against a lock helper that ignores the caller's timeout and one that does not wait. |
||
|
|
51bec397e7 |
fix(installer): log CLAUDE.md as already up to date when install leaves it unchanged (#4377)
executeClaudeMdTransaction drops write operations whose planned bytes equal the existing file, so a repeated install over an up-to-date CLAUDE.md returns no CLAUDE.md write. install() chose 'Created CLAUDE.md' whenever no write with existedBefore was present, so the second run logged 'Created CLAUDE.md' for a file that already existed and was not modified. Look up the CLAUDE.md write operation first. No operation logs 'CLAUDE.md already up to date'; an operation with existedBefore keeps 'Updated CLAUDE.md (merged with existing content)'; otherwise 'Created CLAUDE.md' is unchanged. Adds a test that installs twice into one temp config dir and checks the second run logs the new message, not 'Created CLAUDE.md', and leaves the file byte-identical. Co-authored-by: gaebal-gajae <clawdbot@users.noreply.github.com> Co-authored-by: gaebal-gajae <gaebal-gajae@users.noreply.github.com> |
||
|
|
410f424c7e |
fix(plugin-cache): link stale plugin roots only to a real cache directory (#4375)
* fix(session-start): link stale plugin roots only to a real cache directory The keep-latest-2 pass and the stale CLAUDE_PLUGIN_ROOT guard both used versions[0] as the link target, and versions keeps symlink entries. When the newest entry was a symlink (dangling after its directory was removed, or created for a downgrade) the guard linked the stale path to that symlink, e.g. 4.14.5 -> 4.14.2 next to 4.14.2 -> 4.14.5, so every later hook failed with ELOOP. The keep-2 pass could also turn the only real directory into a symlink to a newer-named symlink. Pick the target from the newest entry that is a real directory, keep symlink entries in the list so they can still be re-pointed, and skip both passes when no real directory exists. * fix(repair-plugin-cache): link stale cache versions only to a real directory latestValidCacheRoot kept symlink entries in its candidate list, so a valid-looking symlink such as 5.0.0 -> 4.4.3 was chosen as the latest root. The fallback pass then replaced the real 4.4.3 directory with 4.4.3 -> 5.0.0, a two-link cycle that destroys the only real copy and fails every later lookup with ELOOP. Choose the latest root only from real directories. Symlink entries are still scanned by the fallback pass, so they are re-pointed at the real latest directory as before, but they never serve as the link target. --------- Co-authored-by: gaebal-gajae <clawdbot@users.noreply.github.com> |
||
|
|
1c5a42d37f |
test(run-cjs): blank user-level OMC env in the worker skip-guard hook spawns (#4376)
test(run-cjs): blank user-level OMC env in the worker skip-guard hook spawns run-cjs-worker-skip-guard.test.ts spawned hooks with the developer's full environment. With OMC_QUIET=1 exported, pre-tool-enforcer emits no hookSpecificOutput and "still delivers the payload to a hook that is not skipped" fails; an exported DISABLE_OMC=1 or OMC_SKIP_HOOKS=pre-tool-use fails it the same way. Blank the same variables pre-tool-enforcer.test.ts blanks, plus the other variables pre-tool-enforcer and its libs read (read-budget bytes, advisory cooldown/clock, OMC_JEV). Values a test passes to runHook still win, so the OMC_SKIP_HOOKS and DISABLE_OMC cases keep working. |
||
|
|
e087c3562f |
perf(hooks): launch the file-context injectors only for the tools they act on (#4373)
post-tool-rules-injector and post-tool-directory-context-injector were
registered under the PostToolUse "*" matcher, so Claude Code started two
Node launches after every tool call. Both hooks return '' from
processToolExecution() for any tool outside TRACKED_TOOLS (read, write, edit,
multiedit) before they touch storage, so for Bash, Grep, Glob, Agent, Skill,
TodoWrite, ToolSearch, WebFetch, MCP tools, etc. the launch only ever printed
{"continue":true}.
- hooks.json: move both hooks to a "Read|Write|Edit|MultiEdit" PostToolUse
group. post-tool-verifier and project-memory-posttool stay on "*".
- run.cjs: add post-tool-directory-context-injector.mjs to
TRUSTED_WORKER_HOOKS. It was registered after the #3928 Worker conversion
and still took the generic path, which starts a second Node process (the
same shape as #4221).
- registry.ts: selectApplicableEntries() now treats a matcher as a
"|"-separated list of tool names, so the shadow registry keeps selecting
these two entries for Read/Write/Edit/MultiEdit.
- Tests pin the matcher to the TRACKED_TOOLS of both hooks, extend the Worker
matrices, and cover the matcher list in the registry.
|