Skip to content

fix: port getPaiDir() CLAUDE_CONFIG_DIR portability to v4.0.3#873

Open
raychaser wants to merge 14 commits intodanielmiessler:mainfrom
raychaser:raychaser/getPaiDir-v4.0.3
Open

fix: port getPaiDir() CLAUDE_CONFIG_DIR portability to v4.0.3#873
raychaser wants to merge 14 commits intodanielmiessler:mainfrom
raychaser:raychaser/getPaiDir-v4.0.3

Conversation

@raychaser
Copy link

Summary

  • Ports the getPaiDir() centralized path resolution from v4.0.1 (fix: make PAI v4.0.1 portable for non-default CLAUDE_CONFIG_DIR installs #838) to the v4.0.3 release
  • Same migration: replaces ~15 inline patterns across 49 files with getPaiDir() from hooks/lib/paths.ts
  • Handles $HOME, ${HOME}, and ~ expansion (Claude Code doesn't expand shell variables in settings.json env values)
  • When PAI_DIR is unset, behavior is byte-identical to before this PR
  • Includes the same error handling improvements and ancestor traversal blocker

This is the v4.0.3 counterpart to #838 (v4.0.1). See #838 for the full design rationale, or issue #830 for the original problem description.

What changed (51 files, +546 −135)

Identical migration pattern as #838, applied to Releases/v4.0.3/:

  • 44 filesgetPaiDir() import replacing inline process.env.HOME! + '/.claude', join(homedir(), '.claude'), etc.
  • 1 filehooks/lib/paths.ts — removed 3 unused convenience wrappers
  • 1 new file.claude/.claude/settings.json — ancestor traversal blocker
  • 1 new filetest-nondefault-install.sh — validation script
  • Error handling — LoadContext, notifications, AlgorithmPhaseReport, ActivityParser, SessionHarvester, LastResponseCache

Intentionally NOT migrated: skills/Telos/DashboardTemplate/ (4 Next.js route handlers) and PAI-Install/ (3 installer files) — both run in separate bundling contexts.

Test plan

🤖 Generated with Claude Code

raychaser and others added 10 commits February 28, 2026 14:03
* fix: make PAI v4.0.1 portable for non-default CLAUDE_CONFIG_DIR installs

Replace hardcoded ~/.claude paths with process.env.PAI_DIR fallback pattern
across 31 TypeScript files. Add .claude/settings.json traversal blocker to
prevent Claude Code from loading ancestor ~/.claude/settings.json as project
settings when PAI is installed at a custom CLAUDE_CONFIG_DIR location.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review findings — missed hardcoded paths, PROJECTS_DIR slug, mkdir safety

- chat/route.ts: PAI_DIR fallback for Inference.ts spawn path
- AlgorithmBridge.ts: PAI_DIR fallback for ISCManager.ts shell command
- BugBountyTool/config.ts: replaced 4 hardcoded ~/.claude paths with join(paiDir, ...)
- ActivityParser.ts: dynamic CWD_SLUG (was hardcoded -Users-${USERNAME}--claude), replaced
  5 hardcoded /.claude/ string matches with CLAUDE_DIR_MARKER for non-default installs
- LastResponseCache.hook.ts: mkdirSync before writeFileSync to prevent ENOENT on fresh installs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: harden error handling and centralize PAI_DIR resolution

- ActivityParser.ts & SessionHarvester.ts: use getPaiDir() from hooks/lib/paths
  instead of inline process.env.PAI_DIR || process.env.HOME! pattern (throws
  explicitly when HOME is undefined instead of producing "undefined/.claude")
- ActivityParser.ts: add PROJECTS_DIR diagnostic (lists available slugs when
  computed slug doesn't match), JSONL parse counter (warns when all lines fail),
  CLAUDE_DIR_MARKER miss counter (warns when no file paths match marker)
- SessionHarvester.ts: same PROJECTS_DIR diagnostic and JSONL parse counter
- AlgorithmBridge.ts: use homedir() instead of process.env.HOME, add existsSync
  check before shell exec, wrap in try-catch (was silently swallowed by .quiet())
- chat/route.ts: use homedir()/join() instead of template literal with HOME,
  surface actual error message in 500 response instead of generic text
- LastResponseCache.hook.ts: separate mkdir and write error paths for clearer
  diagnostics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: eliminate silent failures and complete getPaiDir() migration

- AlgorithmBridge: migrate to getPaiDir() for $HOME expansion
- chat/route.ts: fix variable shadowing, prevent info disclosure in 500 response
- file/save/route.ts, upload/route.ts: log actual error messages server-side
- AlgorithmPhaseReport: add console.error to 3 empty catch blocks
- LoadContext.hook.ts: add error logging to 7 empty catch blocks
- notifications.ts: add error logging to 3 empty catch blocks
- ActivityParser, SessionHarvester: guard diagnostic readdirSync with try-catch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: complete getPaiDir() migration and restore stack trace logging

- AlgorithmPhaseReport: migrate to getPaiDir() (was missed in prior commit)
- notifications.ts: log sendPush() failures instead of silent catch
- Telos routes: restore full error object in console.error (stack traces)
- chat/route.ts: remove unused errorMessage variable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: AlgorithmPhaseReport exits non-zero on error, preserve stack traces in notifications

- AlgorithmPhaseReport: writeState() re-throws on failure, top-level catch exits with code 1
- notifications.ts: pass full error objects to console.error instead of string interpolation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: migrate remaining 35 files from inline PAI_DIR to getPaiDir()

Replace all remaining `process.env.PAI_DIR || join(homedir(), '.claude')`
inline patterns with the centralized `getPaiDir()` helper from
hooks/lib/paths.ts, which provides $HOME/~/~ expansion, safe homedir()
fallback, and a single source of truth for PAI directory resolution.

Files intentionally kept as-is: VoiceServer/server.ts and 4 Telos
DashboardTemplate routes (standalone deployable contexts).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: migrate VoiceServer to getPaiDir() for expandPath() support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: preserve stack traces in LoadContext, suppress ENOENT noise, guard NaN

- LoadContext.hook.ts: Change 12 catch blocks from `${err}` template
  interpolation to `, err` argument passing to preserve stack traces.
  Replace emoji prefixes with consistent [LoadContext] tag.
- AlgorithmPhaseReport.ts: Suppress expected ENOENT errors on first run
  (state file doesn't exist yet) alongside the existing 'empty' sentinel.
- notifications.ts: Guard against NaN propagation when session start file
  contains corrupted/non-numeric data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: restore AlgorithmPhaseReport silent error handling

Revert writeState re-throw and top-level process.exit(1) back to
original non-blocking catch blocks. A reporting tool should not be
capable of killing the process on transient filesystem errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* cleanup: remove unused getHooksDir, getSkillsDir, getMemoryDir exports

These three functions have zero consumers. paiPath and getSettingsPath
are kept — they have active consumers across the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* add non-default CLAUDE_CONFIG_DIR test script and README note

Temporary manual deployment script for testing PAI at paths other than
~/.claude. Copies release files, patches settings.json paths, manages
shell aliases, and cleans up stale caches. Will be superseded once the
installer handles non-default config directories natively.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…em state

21 stale tips fixed (wrong counts, removed effort tiers, non-existent
agents, outdated ISC scales). 14 new tips added for v4.0 features
(Components, LoadContext, hierarchical skills, PRD format, voice
announcements, context compaction). Algorithm version bumped to v3.6.0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added section 4 documenting spinner tips audit (21 fixed, 14 added)
- Updated badges (workflows 180→338, added Tips badge)
- Created RELEASE.json with version metadata and change highlights
- Updated files-changed table to reflect settings.json scope

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes applied to Releases/v4.0.1/.claude/ (matching live ~/.claude/):

- danielmiessler#825: Fix wrong pai alias path (skills/PAI → PAI)
- danielmiessler#757: Detect user shell for alias (.zshrc/.bashrc/.config/fish)
- danielmiessler#819: Write algorithmVersion to settings.json from CLAUDE.md
- danielmiessler#772: Add 'explicit' to rating source union type
- danielmiessler#768: LoadContext exits 0 on error (non-fatal)
- danielmiessler#766: DocCrossRefIntegrity uses configured voice, not hardcoded ID
- danielmiessler#747: Persistent terminal width cache prevents mini mode on re-render
- danielmiessler#755: Auto-detect headless/SSH, fall back to CLI installer
- danielmiessler#832: Skip inference when 0 drift detected (saves ~15s/response)
- danielmiessler#767: Track lineageSubKey instead of fragile .pop() in YAML parser
- danielmiessler#769: Remove dead TrendingAnalysis.ts code and all call sites

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make PAI v4.0.1 portable for non-default CLAUDE_CONFIG_DIR installs

Replace hardcoded ~/.claude paths with process.env.PAI_DIR fallback pattern
across 31 TypeScript files. Add .claude/settings.json traversal blocker to
prevent Claude Code from loading ancestor ~/.claude/settings.json as project
settings when PAI is installed at a custom CLAUDE_CONFIG_DIR location.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR review findings — missed hardcoded paths, PROJECTS_DIR slug, mkdir safety

- chat/route.ts: PAI_DIR fallback for Inference.ts spawn path
- AlgorithmBridge.ts: PAI_DIR fallback for ISCManager.ts shell command
- BugBountyTool/config.ts: replaced 4 hardcoded ~/.claude paths with join(paiDir, ...)
- ActivityParser.ts: dynamic CWD_SLUG (was hardcoded -Users-${USERNAME}--claude), replaced
  5 hardcoded /.claude/ string matches with CLAUDE_DIR_MARKER for non-default installs
- LastResponseCache.hook.ts: mkdirSync before writeFileSync to prevent ENOENT on fresh installs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: harden error handling and centralize PAI_DIR resolution

- ActivityParser.ts & SessionHarvester.ts: use getPaiDir() from hooks/lib/paths
  instead of inline process.env.PAI_DIR || process.env.HOME! pattern (throws
  explicitly when HOME is undefined instead of producing "undefined/.claude")
- ActivityParser.ts: add PROJECTS_DIR diagnostic (lists available slugs when
  computed slug doesn't match), JSONL parse counter (warns when all lines fail),
  CLAUDE_DIR_MARKER miss counter (warns when no file paths match marker)
- SessionHarvester.ts: same PROJECTS_DIR diagnostic and JSONL parse counter
- AlgorithmBridge.ts: use homedir() instead of process.env.HOME, add existsSync
  check before shell exec, wrap in try-catch (was silently swallowed by .quiet())
- chat/route.ts: use homedir()/join() instead of template literal with HOME,
  surface actual error message in 500 response instead of generic text
- LastResponseCache.hook.ts: separate mkdir and write error paths for clearer
  diagnostics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: eliminate silent failures and complete getPaiDir() migration

- AlgorithmBridge: migrate to getPaiDir() for $HOME expansion
- chat/route.ts: fix variable shadowing, prevent info disclosure in 500 response
- file/save/route.ts, upload/route.ts: log actual error messages server-side
- AlgorithmPhaseReport: add console.error to 3 empty catch blocks
- LoadContext.hook.ts: add error logging to 7 empty catch blocks
- notifications.ts: add error logging to 3 empty catch blocks
- ActivityParser, SessionHarvester: guard diagnostic readdirSync with try-catch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: complete getPaiDir() migration and restore stack trace logging

- AlgorithmPhaseReport: migrate to getPaiDir() (was missed in prior commit)
- notifications.ts: log sendPush() failures instead of silent catch
- Telos routes: restore full error object in console.error (stack traces)
- chat/route.ts: remove unused errorMessage variable

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: AlgorithmPhaseReport exits non-zero on error, preserve stack traces in notifications

- AlgorithmPhaseReport: writeState() re-throws on failure, top-level catch exits with code 1
- notifications.ts: pass full error objects to console.error instead of string interpolation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: migrate remaining 35 files from inline PAI_DIR to getPaiDir()

Replace all remaining `process.env.PAI_DIR || join(homedir(), '.claude')`
inline patterns with the centralized `getPaiDir()` helper from
hooks/lib/paths.ts, which provides $HOME/~/~ expansion, safe homedir()
fallback, and a single source of truth for PAI directory resolution.

Files intentionally kept as-is: VoiceServer/server.ts and 4 Telos
DashboardTemplate routes (standalone deployable contexts).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: migrate VoiceServer to getPaiDir() for expandPath() support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: preserve stack traces in LoadContext, suppress ENOENT noise, guard NaN

- LoadContext.hook.ts: Change 12 catch blocks from `${err}` template
  interpolation to `, err` argument passing to preserve stack traces.
  Replace emoji prefixes with consistent [LoadContext] tag.
- AlgorithmPhaseReport.ts: Suppress expected ENOENT errors on first run
  (state file doesn't exist yet) alongside the existing 'empty' sentinel.
- notifications.ts: Guard against NaN propagation when session start file
  contains corrupted/non-numeric data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: restore AlgorithmPhaseReport silent error handling

Revert writeState re-throw and top-level process.exit(1) back to
original non-blocking catch blocks. A reporting tool should not be
capable of killing the process on transient filesystem errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* cleanup: remove unused getHooksDir, getSkillsDir, getMemoryDir exports

These three functions have zero consumers. paiPath and getSettingsPath
are kept — they have active consumers across the codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* add non-default CLAUDE_CONFIG_DIR test script and README note

Temporary manual deployment script for testing PAI at paths other than
~/.claude. Copies release files, patches settings.json paths, manages
shell aliases, and cleans up stale caches. Will be superseded once the
installer handles non-default config directories natively.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* sync: port upstream v4.0.2 bugfixes onto getPaiDir() branch

Merges upstream's 5 hook bugfixes while preserving our getPaiDir() portability
migration. Changes: remove dead triggerTrending code, add explicit rating source
field, fix YAML lineage parsing, non-fatal LoadContext exit, dynamic voice ID
via getIdentity(), algorithm version extraction in UpdateCounts, and conditional
inference skip in DocCrossRefIntegrity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Same migration as v4.0.1 (danielmiessler#838): centralizes all PAI directory
resolution into getPaiDir() in hooks/lib/paths.ts, replacing ~15
different inline patterns across 49 files. Handles $HOME, ${HOME},
and ~ expansion. Byte-identical behavior when PAI_DIR is unset.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
raychaser and others added 4 commits March 2, 2026 15:10
Verifies that BuildCLAUDE.ts can find CLAUDE.md.template via
getPaiDir() when running from a non-default CLAUDE_CONFIG_DIR.
Without PAI_DIR set, getPaiDir() falls back to ~/.claude where
the template doesn't exist — this test catches that.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Follows the upstream upgrade flow (backup → copy → rebuild) but
adapted for non-default target directories. Does not use the
installer. Preserves user data (MEMORY, .env, agents), removes
stale files, updates settings.json paths, and rebuilds CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add file-history, shell-snapshots, commands, and .DS_Store to
skip list. Add skills to preserve list. These are Claude Code
internals or user-generated content that don't come from the
release and should never be deleted during upgrades.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The upstream upgrade flow (cp -r .claude ~/) overlays new files
without deleting anything. The stale file cleanup step had no
upstream precedent and was deleting Claude Code runtime data
(file-history, commands, skills). Removed entirely — upgrades
should only add/overwrite, never delete.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DevenDucommun added a commit to DevenDucommun/Personal_AI_Infrastructure that referenced this pull request Mar 6, 2026
Algorithm v3.8.0: merge v3.6.0 cognitive scaffolding (self-interrogation,
constraint extraction, confidence tags, priority classification, coverage
map, QG2-QG7 gates) into v3.7.0. Split ISC methodology, capability
selection, and examples into dedicated files. Standard PRD skip. Session-
safe context recovery. Voice curls fire-and-forget.

Hooks: consolidate 4 terminal hooks → TerminalState.hook.ts. Add
ModeClassifier (deterministic mode pre-classification, PR danielmiessler#840) and
PostCompactRecovery (context re-injection after compaction, PR danielmiessler#799).

Portability: replace hardcoded join(homedir(),'.claude') with paiPath()
across 22 TS files (PR danielmiessler#873).

Docs: slim SKILLSYSTEM.md and THEHOOKSYSTEM.md to ~80 lines each; move
full content to PAI/dev/. Move MEMORYSYSTEM migration history to
MEMORY-CHANGELOG.md. Add TELOS/DIGEST.md template to loadAtStartup.
Clean CLAUDE.md MINIMAL format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants