@softspark/ai-toolkit 4.9.0 → 4.10.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/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v4.10.0 — Per-repo session storage outside the repo + drop dead session-context hook (2026-06-17)
11
+
12
+ Minor release. Session lifecycle hooks stop writing auto-generated files into each project's `.claude/` directory and store them per-repo under `~/.softspark/ai-toolkit/`. The dead `session-context.sh` hook (write-only, no consumer) is removed across all editor generators. Test count: 1197 → 1195.
13
+
14
+ ### Changed
15
+ - **Session lifecycle hooks store artifacts outside the repo.** `save-session.sh`, `session-end.sh`, `session-start.sh`, and `pre-compact.sh` now read/write the auto-generated session files (`session-context.md`, `session-end.md`, `session-context.md.checkpoints`, `decisions.md`) under `~/.softspark/ai-toolkit/sessions/<repo-key>/` instead of the project's `.claude/` directory. `<repo-key>` is the git work-tree root path (fallback: cwd) with `/` replaced by `-`, keyed per repo so projects stay isolated. Stops these files from piling up in every repository. New shared helper `app/hooks/_session-paths.sh` is the single source of path resolution. No migration of pre-existing in-repo files: the new store starts fresh, old `.claude/session-*.md` left for the user to delete.
16
+ - **Constitution Art. I §5** updated to point proactive checkpointing at the per-repo session store.
17
+
18
+ ### Removed
19
+ - **Dropped the dead `session-context.sh` hook.** It fired on `SessionStart` and wrote an environment snapshot (`~/.softspark/ai-toolkit/sessions/<id>.json`: pwd, git branch/status, node/python versions) that no hook, script, or tool ever read — pure write-only dead code (Constitution Art. VI.1). Removed the script, its `app/hooks.json` wiring, its entries across all 7 editor hook generators (cursor, windsurf, codex, devin, augment, gemini, opencode), its tests, and its catalog/overview docs. Editors that had it as the sole entry for an event (windsurf `post_setup_worktree`, codex `PostCompact`, gemini `BeforeModel`, devin `SessionStart`) drop that event. Not related to the session-storage change above — it never wrote to the repo.
20
+
21
+ ---
22
+
10
23
  ## v4.9.0 - Epistemic integrity rules + deep-research skill + custom-rule leak fix (2026-06-15)
11
24
 
12
25
  Minor release. Adds a constitution article on injection resistance and grounding, a new web-research methodology skill, and an anti-sycophancy/formatting pass across the voice layer. Also fixes a leak where a maintainer's personal registered rules were baked into the toolkit's own committed editor files. Skill count: 107 → 108. Constitution: 6 → 7 articles. Test count: 1196 → 1197.
package/README.md CHANGED
@@ -6,16 +6,16 @@
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
7
  [![Skills](https://img.shields.io/badge/skills-108-brightgreen)](app/skills/)
8
8
  [![Agents](https://img.shields.io/badge/agents-44-blue)](app/agents/)
9
- [![Tests](https://img.shields.io/badge/tests-1197%20passing-success)](tests/)
9
+ [![Tests](https://img.shields.io/badge/tests-1195%20passing-success)](tests/)
10
10
 
11
- ## What's New in v4.9.0
11
+ ## What's New in v4.10.0
12
12
 
13
- v4.9.0 hardens the toolkit's epistemics and fixes a leak where a maintainer's personal rules were baked into the toolkit's own committed editor files.
13
+ v4.10.0 gets auto-generated session files out of your repositories and removes a dead lifecycle hook.
14
14
 
15
- - **Constitution Article VII Epistemic & Injection Integrity**: embedded or untrusted text is data, not commands (no privilege escalation or exfiltration); never fabricate files, APIs, or citations; declare ungrounded when sources come back empty. Constitution: 67 articles.
16
- - **`deep-research` skill**: multi-source web research methodology retrieve-vs-answer gate, complexity-scaled search budget, source-conflict skepticism, adversarial verification, attribution-without-reproduction. Skill count: 107 → 108.
17
- - **Anti-sycophancy + formatting discipline**: `golden-rules` gains "Honesty Over Agreeableness"; `brand-voice` gains list-gating, minimum-substance bullets, and mode carve-outs (code quality never reduced, auto-escalation, disclosure-on-pushback).
18
- - **Custom-rule leak fixed**: `AGENTS.md`, `GEMINI.md`, and `.github/copilot-instructions.md` no longer embed a maintainer's personal `~/.softspark/ai-toolkit/rules/` content, gated behind the new `AI_TOOLKIT_NO_CUSTOM_RULES` flag with a regression test.
15
+ - **Per-repo session storage outside the repo**: `save-session.sh`, `session-end.sh`, `session-start.sh`, and `pre-compact.sh` now keep `session-context.md`, `session-end.md`, checkpoints, and `decisions.md` under `~/.softspark/ai-toolkit/sessions/<repo-key>/` instead of each project's `.claude/` — no more generated files piling up in every repo. Keyed per repo (git root with `/``-`) so projects stay isolated.
16
+ - **No silent migration**: the new store starts fresh; pre-existing in-repo `.claude/session-*.md` are left for you to delete.
17
+ - **Dropped the dead `session-context.sh` hook**: it wrote a `${SESSION}.json` environment snapshot nobody ever read (Constitution Art. VI.1). Removed across `app/hooks.json` and all 7 editor hook generators (cursor, windsurf, codex, devin, augment, gemini, opencode).
18
+ - **Test count**: 1197 1195.
19
19
 
20
20
  See [CHANGELOG.md](CHANGELOG.md) for full history.
21
21
 
@@ -148,7 +148,7 @@ ai-toolkit/
148
148
  │ └── ARCHITECTURE.md # Full system design
149
149
  ├── kb/ # Reference docs, procedures, plans
150
150
  ├── scripts/ # Validation, install, evaluation scripts
151
- ├── tests/ # Bats test suite (1197 tests)
151
+ ├── tests/ # Bats test suite (1195 tests)
152
152
  └── CHANGELOG.md
153
153
  ```
154
154
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ai-toolkit",
3
3
  "description": "Professional-grade Claude Code toolkit with persona presets, skill security auditor, expanded lifecycle hooks, experimental opt-in plugin packs, benchmark harvesting, and multi-tool support.",
4
- "version": "4.9.0",
4
+ "version": "4.10.0",
5
5
  "author": {
6
6
  "name": "SoftSpark",
7
7
  "url": "https://github.com/softspark"
@@ -21,7 +21,7 @@ status: IMMUTABLE
21
21
  - Exceeding limits requires explicit user override
22
22
 
23
23
  ### Section 5: Proactive Context Checkpointing
24
- - During multi-step tasks (>5 tool calls), update `.claude/session-context.md` after each major milestone
24
+ - During multi-step tasks (>5 tool calls), append milestones to the per-repo session-context store after each major milestone. Auto-generated session files live under `~/.softspark/ai-toolkit/sessions/<repo-root-with-/-as->/` (the repo work-tree root path with `/` replaced by `-`), NOT inside the project repo. Append agent checkpoints to `session-context.md.checkpoints` in that directory; the Stop hook folds them into the session summary
25
25
  - Checkpoint MUST include: current objective, completed steps, pending steps, files modified, key decisions
26
26
  - Minimum frequency: after every completed task phase, workflow stage, or subagent handoff
27
27
  - Agent SHOULD checkpoint before any risky or destructive operation
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env bash
2
+ # _session-paths.sh — shared per-repo session storage paths for lifecycle hooks.
3
+ #
4
+ # All auto-generated session artifacts live OUTSIDE the project repo, under a
5
+ # per-repo subdirectory of ~/.softspark/ai-toolkit/sessions/. This keeps generated
6
+ # files (session context, handoff note, checkpoints, decisions) from polluting
7
+ # every project's .claude/ directory.
8
+ #
9
+ # Usage (in a hook script):
10
+ # source "$(dirname "$0")/_session-paths.sh"
11
+ # Then use the exported SESSION_* paths.
12
+ #
13
+ # Repo key: the git work-tree root (fallback: cwd) with "/" replaced by "-",
14
+ # mirroring Claude Code's own ~/.claude/projects/<encoded-path> convention so the
15
+ # location is derivable by an agent, not an opaque hash.
16
+
17
+ _session_repo_root() {
18
+ if command -v git >/dev/null 2>&1 && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
19
+ local top
20
+ top="$(git rev-parse --show-toplevel 2>/dev/null)"
21
+ if [ -n "$top" ]; then
22
+ printf '%s' "$top"
23
+ return 0
24
+ fi
25
+ fi
26
+ pwd
27
+ }
28
+
29
+ SESSION_REPO_ROOT="$(_session_repo_root)"
30
+ SESSION_REPO_KEY="${SESSION_REPO_ROOT//\//-}"
31
+ SESSION_DIR="$HOME/.softspark/ai-toolkit/sessions/$SESSION_REPO_KEY"
32
+ SESSION_CONTEXT_FILE="$SESSION_DIR/session-context.md"
33
+ SESSION_CHECKPOINTS_FILE="$SESSION_DIR/session-context.md.checkpoints"
34
+ SESSION_END_FILE="$SESSION_DIR/session-end.md"
35
+ SESSION_DECISIONS_FILE="$SESSION_DIR/decisions.md"
36
+
37
+ export SESSION_REPO_ROOT SESSION_REPO_KEY SESSION_DIR
38
+ export SESSION_CONTEXT_FILE SESSION_CHECKPOINTS_FILE SESSION_END_FILE SESSION_DECISIONS_FILE
39
+
40
+ unset -f _session_repo_root
@@ -8,6 +8,8 @@
8
8
 
9
9
  # shellcheck source=_profile-check.sh
10
10
  source "$(dirname "$0")/_profile-check.sh"
11
+ # shellcheck source=_session-paths.sh
12
+ source "$(dirname "$0")/_session-paths.sh"
11
13
 
12
14
  # ---------------------------------------------------------------------------
13
15
  # 1. Mandatory reload reminder (highest priority — always survives)
@@ -32,9 +34,9 @@ fi
32
34
  # ---------------------------------------------------------------------------
33
35
  # 3. Current task state (medium priority — what we're doing)
34
36
  # ---------------------------------------------------------------------------
35
- if [ -f ".claude/session-context.md" ]; then
37
+ if [ -f "$SESSION_CONTEXT_FILE" ]; then
36
38
  echo "=== Session Context ==="
37
- cat ".claude/session-context.md"
39
+ cat "$SESSION_CONTEXT_FILE"
38
40
  echo "======================="
39
41
  fi
40
42
 
@@ -55,10 +57,10 @@ fi
55
57
  # ---------------------------------------------------------------------------
56
58
  # 5. Key decisions file (if user has been noting decisions this session)
57
59
  # ---------------------------------------------------------------------------
58
- if [ -f ".claude/decisions.md" ]; then
60
+ if [ -f "$SESSION_DECISIONS_FILE" ]; then
59
61
  echo "=== Key Decisions This Session ==="
60
62
  # Only show last 10 lines to keep token budget tight
61
- tail -10 ".claude/decisions.md"
63
+ tail -10 "$SESSION_DECISIONS_FILE"
62
64
  echo "=================================="
63
65
  fi
64
66
 
@@ -6,16 +6,18 @@
6
6
 
7
7
  # shellcheck source=_profile-check.sh
8
8
  source "$(dirname "$0")/_profile-check.sh"
9
+ # shellcheck source=_session-paths.sh
10
+ source "$(dirname "$0")/_session-paths.sh"
9
11
 
10
12
  # Read from stdin (Claude Code passes JSON with .session_id, .last_assistant_message)
11
13
  INPUT=$(cat)
12
14
  SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
13
15
  LAST_MSG=$(echo "$INPUT" | jq -r '.last_assistant_message // "No summary available"' 2>/dev/null | head -5)
14
16
 
15
- SESSION_FILE=".claude/session-context.md"
17
+ SESSION_FILE="$SESSION_CONTEXT_FILE"
16
18
 
17
19
  if [ -n "$SESSION_ID" ]; then
18
- mkdir -p .claude
20
+ mkdir -p "$SESSION_DIR"
19
21
 
20
22
  # Gather git state for richer context
21
23
  GIT_BRANCH=""
@@ -7,13 +7,14 @@
7
7
 
8
8
  # shellcheck source=_profile-check.sh
9
9
  source "$(dirname "$0")/_profile-check.sh"
10
+ # shellcheck source=_session-paths.sh
11
+ source "$(dirname "$0")/_session-paths.sh"
10
12
 
11
- STATE_DIR=".claude"
12
- SESSION_FILE="$STATE_DIR/session-context.md"
13
- HANDOFF_FILE="$STATE_DIR/session-end.md"
13
+ SESSION_FILE="$SESSION_CONTEXT_FILE"
14
+ HANDOFF_FILE="$SESSION_END_FILE"
14
15
  STAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date)"
15
16
 
16
- mkdir -p "$STATE_DIR"
17
+ mkdir -p "$SESSION_DIR"
17
18
 
18
19
  {
19
20
  echo "# Session End Snapshot"
@@ -28,9 +29,9 @@ mkdir -p "$STATE_DIR"
28
29
  } > "$HANDOFF_FILE"
29
30
 
30
31
  if [ -f "$SESSION_FILE" ]; then
31
- echo "SessionEnd: wrote .claude/session-end.md and preserved existing .claude/session-context.md for the next session."
32
+ echo "SessionEnd: wrote $HANDOFF_FILE and preserved existing session-context for the next session."
32
33
  else
33
- echo "SessionEnd: wrote .claude/session-end.md. Consider persisting open tasks in .claude/session-context.md before ending long workstreams."
34
+ echo "SessionEnd: wrote $HANDOFF_FILE. Consider persisting open tasks in the session-context file before ending long workstreams."
34
35
  fi
35
36
 
36
37
  exit 0
@@ -6,6 +6,8 @@
6
6
 
7
7
  # shellcheck source=_locate-toolkit.sh
8
8
  source "$(dirname "$0")/_locate-toolkit.sh"
9
+ # shellcheck source=_session-paths.sh
10
+ source "$(dirname "$0")/_session-paths.sh"
9
11
 
10
12
  emit_context() {
11
13
  [ "${AI_TOOLKIT_HOOK_QUIET:-0}" = "1" ] && return 0
@@ -51,7 +53,7 @@ if [ -n "$VERSION_MSG" ]; then
51
53
  fi
52
54
 
53
55
  # 3. Load session context (if available)
54
- SESSION_FILE=".claude/session-context.md"
56
+ SESSION_FILE="$SESSION_CONTEXT_FILE"
55
57
  if [ -f "$SESSION_FILE" ] && [ "${AI_TOOLKIT_HOOK_QUIET:-0}" != "1" ] && [ "${AI_TOOLKIT_HOOK_VERBOSE:-0}" = "1" ]; then
56
58
  printf '%s\n' "=== Session Context ==="
57
59
  cat "$SESSION_FILE"
package/app/hooks.json CHANGED
@@ -20,16 +20,6 @@
20
20
  "command": "\"$HOME/.softspark/ai-toolkit/hooks/mcp-health.sh\""
21
21
  }
22
22
  ]
23
- },
24
- {
25
- "_source": "ai-toolkit",
26
- "matcher": "startup|compact",
27
- "hooks": [
28
- {
29
- "type": "command",
30
- "command": "\"$HOME/.softspark/ai-toolkit/hooks/session-context.sh\""
31
- }
32
- ]
33
23
  }
34
24
  ],
35
25
  "Notification": [
@@ -269,7 +269,6 @@ Agents (code-reviewer, debugger, devops-implementer, ...)
269
269
  |------|---------|--------|--------|
270
270
  | SessionStart | Session start + compact | `session-start.sh` | MANDATORY rules reminder + session context + instincts |
271
271
  | SessionStart | Session start | `mcp-health.sh` | Check MCP runtime availability |
272
- | SessionStart | Session start | `session-context.sh` | Capture environment snapshot |
273
272
  | Notification | Claude waiting for input | *(inline)* | macOS desktop notification |
274
273
  | PreToolUse | Before Bash | `guard-destructive.sh` | Block destructive commands |
275
274
  | PreToolUse | Before file ops (Bash, Read, Edit, Write, MultiEdit, Glob, Grep, NotebookEdit, mcp\_filesystem) | `guard-path.sh` | Block wrong-user path hallucination |
@@ -47,13 +47,21 @@ ai-toolkit update # re-copies scripts, re-merges (idempotent)
47
47
  **Actions:**
48
48
  1. Injects MANDATORY reminder to follow CLAUDE.md rules
49
49
  2. Injects REMINDER about tests and documentation
50
- 3. Loads session context from `.claude/session-context.md` (if exists)
50
+ 3. Loads session context from the per-repo session store (if exists)
51
51
  4. Loads active instincts from `.claude/instincts/*.md` (if any)
52
52
 
53
53
  By default the hook performs session-state reset, stale search-flag cleanup, and
54
54
  update notification side effects without printing informational stdout. Set
55
- `AI_TOOLKIT_HOOK_VERBOSE=1` to print the startup reminders and loaded context
56
- for debugging; `AI_TOOLKIT_HOOK_QUIET=1` keeps it silent explicitly.
55
+ `AI_TOOLKIT_HOOK_VERBOSE=1` to print the startup reminders and loaded context for
56
+ debugging; `AI_TOOLKIT_HOOK_QUIET=1` keeps it silent explicitly.
57
+
58
+ > **Session storage:** auto-generated session artifacts (context, handoff note,
59
+ > checkpoints, decisions) are stored **outside the project repo** under
60
+ > `~/.softspark/ai-toolkit/sessions/<repo-key>/`, where `<repo-key>` is the git
61
+ > work-tree root path (fallback: cwd) with `/` replaced by `-` (mirrors Claude
62
+ > Code's own `~/.claude/projects/` convention). This keeps generated files from
63
+ > piling up in every project's `.claude/` directory. Path resolution lives in the
64
+ > shared helper `app/hooks/_session-paths.sh`.
57
65
 
58
66
  ### Notification — `notify-waiting.sh`
59
67
 
@@ -192,10 +200,10 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
192
200
  | Script | `~/.softspark/ai-toolkit/hooks/save-session.sh` |
193
201
  | Fires | After every Claude response |
194
202
 
195
- **Action:** Writes enriched session context to `.claude/session-context.md` for cross-session persistence. Captures:
203
+ **Action:** Writes enriched session context to the per-repo session store (`~/.softspark/ai-toolkit/sessions/<repo-key>/session-context.md`) for cross-session persistence. Captures:
196
204
  - Session ID and last assistant message (first 5 lines)
197
205
  - Git branch, uncommitted change count, and diff stat (last 5 lines)
198
- - Agent-written checkpoints from `.claude/session-context.md.checkpoints` (if present — written by proactive checkpointing per Constitution Art. I §5)
206
+ - Agent-written checkpoints from `session-context.md.checkpoints` in the same store (if present — written by proactive checkpointing per Constitution Art. I §5)
199
207
 
200
208
  Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
201
209
 
@@ -263,9 +271,9 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
263
271
  **Actions (prioritized — higher priority items survive tighter token budgets):**
264
272
  1. **Mandatory reload reminder** — always emitted, instructs Claude to re-read CLAUDE.md and active tasks
265
273
  2. **Active instincts** — lists each instinct with confidence score and pattern name from `.claude/instincts/*.md`
266
- 3. **Session context** — preserves task state from `.claude/session-context.md` (if exists)
274
+ 3. **Session context** — preserves task state from the per-repo session store (if exists)
267
275
  4. **Git working state** — branch name, uncommitted change count, last commit (if inside a git repo)
268
- 5. **Key decisions** — last 10 lines from `.claude/decisions.md` (if exists)
276
+ 5. **Key decisions** — last 10 lines from `decisions.md` in the per-repo session store (if exists)
269
277
 
270
278
  Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
271
279
 
@@ -278,7 +286,7 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
278
286
  | Script | `~/.softspark/ai-toolkit/hooks/session-end.sh` |
279
287
  | Fires | When a Claude session ends |
280
288
 
281
- **Action:** Writes `.claude/session-end.md` with a lightweight handoff note for the next session and reminds the next session to review preserved context.
289
+ **Action:** Writes `session-end.md` to the per-repo session store (`~/.softspark/ai-toolkit/sessions/<repo-key>/`) with a lightweight handoff note for the next session and reminds the next session to review preserved context.
282
290
 
283
291
  Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
284
292
 
@@ -351,17 +359,6 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
351
359
 
352
360
  **Action:** Non-blocking (always exits 0). Inspects Bash commands containing `git commit`. Extracts the commit message from the `-m` flag and checks it against Conventional Commits format (`type: description`, where type is one of feat/fix/docs/refactor/test/chore/ci/perf/style/revert). Emits an advisory warning if the message does not match — the commit is not blocked, only nudged. Commands without `git commit` or without a `-m` message (e.g. interactive commits) are ignored.
353
361
 
354
- ### SessionStart — `session-context.sh`
355
-
356
- | Field | Value |
357
- |-------|-------|
358
- | Event | `SessionStart` |
359
- | Matcher | *(all)* |
360
- | Script | `~/.softspark/ai-toolkit/hooks/session-context.sh` |
361
- | Fires | Session start |
362
-
363
- **Action:** Captures an environment snapshot to `~/.softspark/ai-toolkit/sessions/current-context.json`. Records working directory, git branch, git status summary, Node.js version, Python version, and timestamp. Used by other hooks and tools to access session metadata without re-running discovery commands. Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
364
-
365
362
  ---
366
363
 
367
364
  ## New Hooks (Constitution Art. VI Enforcement)
@@ -520,8 +517,8 @@ commands explicitly silent, and Codex-generated hooks plus Claude's bundled
520
517
  ├── _locate-toolkit.sh # NEW: shared $TOOLKIT_DIR locator
521
518
  ├── _hook-io.sh # NEW: shared multi-editor payload/output adapter
522
519
  ├── _search-capability.sh # NEW: capability-aware search-first enforcement
520
+ ├── _session-paths.sh # NEW: per-repo session storage path resolver
523
521
  ├── session-start.sh
524
- ├── session-context.sh
525
522
  ├── guard-destructive.sh
526
523
  ├── guard-path.sh
527
524
  ├── guard-config.sh
@@ -549,7 +546,7 @@ commands explicitly silent, and Codex-generated hooks plus Claude's bundled
549
546
 
550
547
  ~/.claude/settings.json
551
548
  └── hooks: # Hook definitions referencing ~/.softspark/ai-toolkit/hooks/
552
- ├── SessionStart → session-start.sh, mcp-health.sh, session-context.sh
549
+ ├── SessionStart → session-start.sh, mcp-health.sh
553
550
  ├── Notification → notify-waiting.sh
554
551
  ├── PreToolUse → guard-destructive.sh, guard-path.sh, guard-config.sh, commit-quality.sh, revert-guard.sh
555
552
  ├── UserPromptSubmit → user-prompt-submit.sh, track-usage.sh
@@ -590,7 +587,7 @@ Windsurf rebranded to Devin Desktop (2026-06-02); the Cascade agent — and its
590
587
  Devin CLI uses a **Claude-compatible** hook format (docs.devin.ai/cli/extensibility/hooks). Key facts driving the generator:
591
588
 
592
589
  - **Standalone file shape:** in `.devin/hooks.v1.json` the entire file IS the hooks object — no top-level `"hooks"` wrapper key (unlike `.claude/settings.json`).
593
- - **Events:** Claude-style PascalCase — `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, `SessionStart`. `post_setup_worktree` has no Devin equivalent (`session-context.sh` moves to `SessionStart`); `post_cascade_response` maps to `Stop` (which carries no response text on stdin).
590
+ - **Events:** Claude-style PascalCase — `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`. `post_cascade_response` maps to `Stop` (which carries no response text on stdin).
594
591
  - **Matchers:** regex against the Devin **tool name** (`read`, `edit`, `exec`, `grep`, `glob`, `mcp__<server>__<tool>`) — NOT Claude's `Bash`/`Edit`, so the shared guards reliably fire.
595
592
  - **Block contract:** the guard scripts emit `{"decision":"block","reason":...}` on stdout (plain mode) AND exit 2 — Devin honors both. Hooks run **without** `AI_TOOLKIT_HOOK_FORMAT=json` because Devin expects the flat `{"decision","reason"}` shape, not Claude's `hookSpecificOutput` envelope.
596
593
  - **Stdin payload:** flat `{ "hook_event_name", "tool_name", "tool_input" }` — already handled by `_hook-io.sh` via its `.tool_name` / `.tool_input.*` branches, so no normalizer change was needed.
@@ -79,7 +79,7 @@ Each command file carries the prompt in its markdown body (built from the SKILL.
79
79
 
80
80
  | opencode event | Bash hook(s) |
81
81
  |----------------------------|--------------------------------------------------------------------|
82
- | `session.created` | `session-start.sh` + `session-context.sh` + `mcp-health.sh` |
82
+ | `session.created` | `session-start.sh` + `mcp-health.sh` |
83
83
  | `session.compacted` | `pre-compact.sh` + `pre-compact-save.sh` (PreCompact equivalent) |
84
84
  | `session.deleted` | `session-end.sh` + `save-session.sh` |
85
85
  | `message.updated` | `user-prompt-submit.sh` + `track-usage.sh` |
@@ -170,7 +170,7 @@ The canonical data lives in **`scripts/ecosystem_tools.json`** and is consumed b
170
170
  | Config paths | `AGENTS.md`, `.agents/skills/*/SKILL.md`, `.codex/hooks.json`, `.codex/config.toml` (project layers, root→cwd, closest wins, trusted projects only), `~/.codex/config.toml` |
171
171
  | Our generators | `scripts/generate_codex.py`, `scripts/generate_codex_hooks.py`, `scripts/generate_codex_skills.py` (opt-in via `--codex-skills`) |
172
172
  | Rules delivery | Universal coding rules are inlined into `AGENTS.md` (Codex reads instructions only from AGENTS.md, not `.agents/rules/`); language rules ship as `<lang>-rules` skills under `.agents/skills/`. |
173
- | Tracked hook events | Upstream canonical (codex-rs `HookEventName` enum): `PreToolUse`, `PostToolUse`, `PermissionRequest`, `PreCompact`, `PostCompact`, `SessionStart`, `UserPromptSubmit`, `SubagentStart`, `SubagentStop`, `Stop` (10 events). We wire all 10 to shared toolkit hook scripts, mirroring the Claude Code mapping in `app/hooks.json`. |
173
+ | Tracked hook events | Upstream canonical (codex-rs `HookEventName` enum): `PreToolUse`, `PostToolUse`, `PermissionRequest`, `PreCompact`, `PostCompact`, `SessionStart`, `UserPromptSubmit`, `SubagentStart`, `SubagentStop`, `Stop` (10 events). We wire 9 of these to shared toolkit hook scripts, mirroring the Claude Code mapping in `app/hooks.json`. `PostCompact` is not wired (its only hook was the removed environment-snapshot probe). |
174
174
  | Tracked handler types | `command` (emitted by default); `prompt` and `agent` available upstream but authored by hand |
175
175
  | Other capabilities | `AGENTS.md`, `config.toml`, `mcp_servers`, sandbox policies, `.agents/skills/*/SKILL.md` (native Codex skill discovery path) |
176
176
  | Version probe | `codex --version` |
@@ -27,7 +27,6 @@ Hook logic lives in `app/hooks/*.sh` — not inline JSON one-liners. Scripts are
27
27
  |-------|--------|--------|
28
28
  | SessionStart | `session-start.sh` | MANDATORY rules reminder + session context + instincts + reset session-edit state |
29
29
  | SessionStart | `mcp-health.sh` | Check MCP server command availability (non-blocking warning) |
30
- | SessionStart | `session-context.sh` | Capture environment snapshot to `~/.softspark/ai-toolkit/sessions/current-context.json` |
31
30
  | Notification | `notify-waiting.sh` | Cross-platform desktop notification |
32
31
  | PreToolUse | `guard-destructive.sh` | Block `rm -rf`, `DROP TABLE`, etc. |
33
32
  | PreToolUse | `guard-path.sh` | Block wrong-user path hallucination |
package/llms-full.txt CHANGED
@@ -6501,7 +6501,6 @@ Agents (code-reviewer, debugger, devops-implementer, ...)
6501
6501
  |------|---------|--------|--------|
6502
6502
  | SessionStart | Session start + compact | `session-start.sh` | MANDATORY rules reminder + session context + instincts |
6503
6503
  | SessionStart | Session start | `mcp-health.sh` | Check MCP runtime availability |
6504
- | SessionStart | Session start | `session-context.sh` | Capture environment snapshot |
6505
6504
  | Notification | Claude waiting for input | *(inline)* | macOS desktop notification |
6506
6505
  | PreToolUse | Before Bash | `guard-destructive.sh` | Block destructive commands |
6507
6506
  | PreToolUse | Before file ops (Bash, Read, Edit, Write, MultiEdit, Glob, Grep, NotebookEdit, mcp\_filesystem) | `guard-path.sh` | Block wrong-user path hallucination |
@@ -9043,13 +9042,21 @@ ai-toolkit update # re-copies scripts, re-merges (idempotent)
9043
9042
  **Actions:**
9044
9043
  1. Injects MANDATORY reminder to follow CLAUDE.md rules
9045
9044
  2. Injects REMINDER about tests and documentation
9046
- 3. Loads session context from `.claude/session-context.md` (if exists)
9045
+ 3. Loads session context from the per-repo session store (if exists)
9047
9046
  4. Loads active instincts from `.claude/instincts/*.md` (if any)
9048
9047
 
9049
9048
  By default the hook performs session-state reset, stale search-flag cleanup, and
9050
9049
  update notification side effects without printing informational stdout. Set
9051
- `AI_TOOLKIT_HOOK_VERBOSE=1` to print the startup reminders and loaded context
9052
- for debugging; `AI_TOOLKIT_HOOK_QUIET=1` keeps it silent explicitly.
9050
+ `AI_TOOLKIT_HOOK_VERBOSE=1` to print the startup reminders and loaded context for
9051
+ debugging; `AI_TOOLKIT_HOOK_QUIET=1` keeps it silent explicitly.
9052
+
9053
+ > **Session storage:** auto-generated session artifacts (context, handoff note,
9054
+ > checkpoints, decisions) are stored **outside the project repo** under
9055
+ > `~/.softspark/ai-toolkit/sessions/<repo-key>/`, where `<repo-key>` is the git
9056
+ > work-tree root path (fallback: cwd) with `/` replaced by `-` (mirrors Claude
9057
+ > Code's own `~/.claude/projects/` convention). This keeps generated files from
9058
+ > piling up in every project's `.claude/` directory. Path resolution lives in the
9059
+ > shared helper `app/hooks/_session-paths.sh`.
9053
9060
 
9054
9061
  ### Notification — `notify-waiting.sh`
9055
9062
 
@@ -9188,10 +9195,10 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
9188
9195
  | Script | `~/.softspark/ai-toolkit/hooks/save-session.sh` |
9189
9196
  | Fires | After every Claude response |
9190
9197
 
9191
- **Action:** Writes enriched session context to `.claude/session-context.md` for cross-session persistence. Captures:
9198
+ **Action:** Writes enriched session context to the per-repo session store (`~/.softspark/ai-toolkit/sessions/<repo-key>/session-context.md`) for cross-session persistence. Captures:
9192
9199
  - Session ID and last assistant message (first 5 lines)
9193
9200
  - Git branch, uncommitted change count, and diff stat (last 5 lines)
9194
- - Agent-written checkpoints from `.claude/session-context.md.checkpoints` (if present — written by proactive checkpointing per Constitution Art. I §5)
9201
+ - Agent-written checkpoints from `session-context.md.checkpoints` in the same store (if present — written by proactive checkpointing per Constitution Art. I §5)
9195
9202
 
9196
9203
  Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
9197
9204
 
@@ -9259,9 +9266,9 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
9259
9266
  **Actions (prioritized — higher priority items survive tighter token budgets):**
9260
9267
  1. **Mandatory reload reminder** — always emitted, instructs Claude to re-read CLAUDE.md and active tasks
9261
9268
  2. **Active instincts** — lists each instinct with confidence score and pattern name from `.claude/instincts/*.md`
9262
- 3. **Session context** — preserves task state from `.claude/session-context.md` (if exists)
9269
+ 3. **Session context** — preserves task state from the per-repo session store (if exists)
9263
9270
  4. **Git working state** — branch name, uncommitted change count, last commit (if inside a git repo)
9264
- 5. **Key decisions** — last 10 lines from `.claude/decisions.md` (if exists)
9271
+ 5. **Key decisions** — last 10 lines from `decisions.md` in the per-repo session store (if exists)
9265
9272
 
9266
9273
  Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
9267
9274
 
@@ -9274,7 +9281,7 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
9274
9281
  | Script | `~/.softspark/ai-toolkit/hooks/session-end.sh` |
9275
9282
  | Fires | When a Claude session ends |
9276
9283
 
9277
- **Action:** Writes `.claude/session-end.md` with a lightweight handoff note for the next session and reminds the next session to review preserved context.
9284
+ **Action:** Writes `session-end.md` to the per-repo session store (`~/.softspark/ai-toolkit/sessions/<repo-key>/`) with a lightweight handoff note for the next session and reminds the next session to review preserved context.
9278
9285
 
9279
9286
  Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
9280
9287
 
@@ -9347,17 +9354,6 @@ Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
9347
9354
 
9348
9355
  **Action:** Non-blocking (always exits 0). Inspects Bash commands containing `git commit`. Extracts the commit message from the `-m` flag and checks it against Conventional Commits format (`type: description`, where type is one of feat/fix/docs/refactor/test/chore/ci/perf/style/revert). Emits an advisory warning if the message does not match — the commit is not blocked, only nudged. Commands without `git commit` or without a `-m` message (e.g. interactive commits) are ignored.
9349
9356
 
9350
- ### SessionStart — `session-context.sh`
9351
-
9352
- | Field | Value |
9353
- |-------|-------|
9354
- | Event | `SessionStart` |
9355
- | Matcher | *(all)* |
9356
- | Script | `~/.softspark/ai-toolkit/hooks/session-context.sh` |
9357
- | Fires | Session start |
9358
-
9359
- **Action:** Captures an environment snapshot to `~/.softspark/ai-toolkit/sessions/current-context.json`. Records working directory, git branch, git status summary, Node.js version, Python version, and timestamp. Used by other hooks and tools to access session metadata without re-running discovery commands. Skipped when `TOOLKIT_HOOK_PROFILE=minimal`.
9360
-
9361
9357
  ---
9362
9358
 
9363
9359
  ## New Hooks (Constitution Art. VI Enforcement)
@@ -9516,8 +9512,8 @@ commands explicitly silent, and Codex-generated hooks plus Claude's bundled
9516
9512
  ├── _locate-toolkit.sh # NEW: shared $TOOLKIT_DIR locator
9517
9513
  ├── _hook-io.sh # NEW: shared multi-editor payload/output adapter
9518
9514
  ├── _search-capability.sh # NEW: capability-aware search-first enforcement
9515
+ ├── _session-paths.sh # NEW: per-repo session storage path resolver
9519
9516
  ├── session-start.sh
9520
- ├── session-context.sh
9521
9517
  ├── guard-destructive.sh
9522
9518
  ├── guard-path.sh
9523
9519
  ├── guard-config.sh
@@ -9545,7 +9541,7 @@ commands explicitly silent, and Codex-generated hooks plus Claude's bundled
9545
9541
 
9546
9542
  ~/.claude/settings.json
9547
9543
  └── hooks: # Hook definitions referencing ~/.softspark/ai-toolkit/hooks/
9548
- ├── SessionStart → session-start.sh, mcp-health.sh, session-context.sh
9544
+ ├── SessionStart → session-start.sh, mcp-health.sh
9549
9545
  ├── Notification → notify-waiting.sh
9550
9546
  ├── PreToolUse → guard-destructive.sh, guard-path.sh, guard-config.sh, commit-quality.sh, revert-guard.sh
9551
9547
  ├── UserPromptSubmit → user-prompt-submit.sh, track-usage.sh
@@ -9586,7 +9582,7 @@ Windsurf rebranded to Devin Desktop (2026-06-02); the Cascade agent — and its
9586
9582
  Devin CLI uses a **Claude-compatible** hook format (docs.devin.ai/cli/extensibility/hooks). Key facts driving the generator:
9587
9583
 
9588
9584
  - **Standalone file shape:** in `.devin/hooks.v1.json` the entire file IS the hooks object — no top-level `"hooks"` wrapper key (unlike `.claude/settings.json`).
9589
- - **Events:** Claude-style PascalCase — `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, `SessionStart`. `post_setup_worktree` has no Devin equivalent (`session-context.sh` moves to `SessionStart`); `post_cascade_response` maps to `Stop` (which carries no response text on stdin).
9585
+ - **Events:** Claude-style PascalCase — `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`. `post_cascade_response` maps to `Stop` (which carries no response text on stdin).
9590
9586
  - **Matchers:** regex against the Devin **tool name** (`read`, `edit`, `exec`, `grep`, `glob`, `mcp__<server>__<tool>`) — NOT Claude's `Bash`/`Edit`, so the shared guards reliably fire.
9591
9587
  - **Block contract:** the guard scripts emit `{"decision":"block","reason":...}` on stdout (plain mode) AND exit 2 — Devin honors both. Hooks run **without** `AI_TOOLKIT_HOOK_FORMAT=json` because Devin expects the flat `{"decision","reason"}` shape, not Claude's `hookSpecificOutput` envelope.
9592
9588
  - **Stdin payload:** flat `{ "hook_event_name", "tool_name", "tool_input" }` — already handled by `_hook-io.sh` via its `.tool_name` / `.tool_input.*` branches, so no normalizer change was needed.
@@ -11057,7 +11053,7 @@ Each command file carries the prompt in its markdown body (built from the SKILL.
11057
11053
 
11058
11054
  | opencode event | Bash hook(s) |
11059
11055
  |----------------------------|--------------------------------------------------------------------|
11060
- | `session.created` | `session-start.sh` + `session-context.sh` + `mcp-health.sh` |
11056
+ | `session.created` | `session-start.sh` + `mcp-health.sh` |
11061
11057
  | `session.compacted` | `pre-compact.sh` + `pre-compact-save.sh` (PreCompact equivalent) |
11062
11058
  | `session.deleted` | `session-end.sh` + `save-session.sh` |
11063
11059
  | `message.updated` | `user-prompt-submit.sh` + `track-usage.sh` |
@@ -12062,7 +12058,7 @@ The canonical data lives in **`scripts/ecosystem_tools.json`** and is consumed b
12062
12058
  | Config paths | `AGENTS.md`, `.agents/skills/*/SKILL.md`, `.codex/hooks.json`, `.codex/config.toml` (project layers, root→cwd, closest wins, trusted projects only), `~/.codex/config.toml` |
12063
12059
  | Our generators | `scripts/generate_codex.py`, `scripts/generate_codex_hooks.py`, `scripts/generate_codex_skills.py` (opt-in via `--codex-skills`) |
12064
12060
  | Rules delivery | Universal coding rules are inlined into `AGENTS.md` (Codex reads instructions only from AGENTS.md, not `.agents/rules/`); language rules ship as `<lang>-rules` skills under `.agents/skills/`. |
12065
- | Tracked hook events | Upstream canonical (codex-rs `HookEventName` enum): `PreToolUse`, `PostToolUse`, `PermissionRequest`, `PreCompact`, `PostCompact`, `SessionStart`, `UserPromptSubmit`, `SubagentStart`, `SubagentStop`, `Stop` (10 events). We wire all 10 to shared toolkit hook scripts, mirroring the Claude Code mapping in `app/hooks.json`. |
12061
+ | Tracked hook events | Upstream canonical (codex-rs `HookEventName` enum): `PreToolUse`, `PostToolUse`, `PermissionRequest`, `PreCompact`, `PostCompact`, `SessionStart`, `UserPromptSubmit`, `SubagentStart`, `SubagentStop`, `Stop` (10 events). We wire 9 of these to shared toolkit hook scripts, mirroring the Claude Code mapping in `app/hooks.json`. `PostCompact` is not wired (its only hook was the removed environment-snapshot probe). |
12066
12062
  | Tracked handler types | `command` (emitted by default); `prompt` and `agent` available upstream but authored by hand |
12067
12063
  | Other capabilities | `AGENTS.md`, `config.toml`, `mcp_servers`, sandbox policies, `.agents/skills/*/SKILL.md` (native Codex skill discovery path) |
12068
12064
  | Version probe | `codex --version` |
@@ -12244,7 +12240,6 @@ Hook logic lives in `app/hooks/*.sh` — not inline JSON one-liners. Scripts are
12244
12240
  |-------|--------|--------|
12245
12241
  | SessionStart | `session-start.sh` | MANDATORY rules reminder + session context + instincts + reset session-edit state |
12246
12242
  | SessionStart | `mcp-health.sh` | Check MCP server command availability (non-blocking warning) |
12247
- | SessionStart | `session-context.sh` | Capture environment snapshot to `~/.softspark/ai-toolkit/sessions/current-context.json` |
12248
12243
  | Notification | `notify-waiting.sh` | Cross-platform desktop notification |
12249
12244
  | PreToolUse | `guard-destructive.sh` | Block `rm -rf`, `DROP TABLE`, etc. |
12250
12245
  | PreToolUse | `guard-path.sh` | Block wrong-user path hallucination |
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.9.0",
2
+ "version": "4.10.0",
3
3
  "components": {
4
4
  "agents": {
5
5
  "description": "44 specialized agents (orchestrator, backend, frontend, security, devops, etc.)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softspark/ai-toolkit",
3
- "version": "4.9.0",
3
+ "version": "4.10.0",
4
4
  "description": "AI coding toolkit: 108 skills, 44 agents, 12-editor write-through (Claude, Cursor, Windsurf, Copilot, Gemini, Cline, Roo, Aider, Augment, Antigravity, Codex, opencode), machine-enforced safety constitution, SARIF audit, signed npm provenance.",
5
5
  "keywords": [
6
6
  "claude",
@@ -42,7 +42,6 @@ AUGMENT_HOOKS: dict[str, list[tuple[str, str]]] = {
42
42
  "SessionStart": [
43
43
  ("", "session-start.sh"),
44
44
  ("", "mcp-health.sh"),
45
- ("", "session-context.sh"),
46
45
  ],
47
46
  "PreToolUse": [
48
47
  ("launch-process", "guard-destructive.sh"),
@@ -33,7 +33,6 @@ CODEX_HOOKS: dict[str, list[tuple[str, str]]] = {
33
33
  "SessionStart": [
34
34
  ("startup|resume", "session-start.sh"),
35
35
  ("startup|resume", "mcp-health.sh"),
36
- ("startup|resume", "session-context.sh"),
37
36
  ],
38
37
  "PreToolUse": [
39
38
  ("Bash", "guard-destructive.sh"),
@@ -67,10 +66,6 @@ CODEX_HOOKS: dict[str, list[tuple[str, str]]] = {
67
66
  ("", "pre-compact.sh"),
68
67
  ("", "pre-compact-save.sh"),
69
68
  ],
70
- "PostCompact": [
71
- # Re-establish working context after compaction.
72
- ("", "session-context.sh"),
73
- ],
74
69
  "Stop": [
75
70
  ("", "quality-check.sh"),
76
71
  ("", "save-session.sh"),
@@ -38,7 +38,6 @@ CURSOR_HOOKS: dict[str, list[tuple[str, str]]] = {
38
38
  "sessionStart": [
39
39
  ("", "session-start.sh"),
40
40
  ("", "mcp-health.sh"),
41
- ("", "session-context.sh"),
42
41
  ],
43
42
  "beforeShellExecution": [
44
43
  ("", "guard-destructive.sh"),
@@ -61,11 +61,6 @@ DEVIN_HOOKS: dict[str, list[tuple[str, list[str]]]] = {
61
61
  "Stop": [
62
62
  ("", ["quality-check.sh", "save-session.sh", "stop-search-check.sh"]),
63
63
  ],
64
- # Cascade's post_setup_worktree has no Devin equivalent; session-context
65
- # moves to SessionStart (Devin fires SessionStart when a session begins).
66
- "SessionStart": [
67
- ("", ["session-context.sh"]),
68
- ],
69
64
  }
70
65
 
71
66
 
@@ -40,7 +40,6 @@ GEMINI_HOOKS: dict[str, list[tuple[str, str]]] = {
40
40
  "SessionStart": [
41
41
  ("", "session-start.sh"),
42
42
  ("", "mcp-health.sh"),
43
- ("", "session-context.sh"),
44
43
  ],
45
44
  "BeforeTool": [
46
45
  # run_shell_command covers Bash-equivalent destructive patterns
@@ -67,10 +66,6 @@ GEMINI_HOOKS: dict[str, list[tuple[str, str]]] = {
67
66
  ("", "save-session.sh"),
68
67
  ("", "stop-search-check.sh"),
69
68
  ],
70
- "BeforeModel": [
71
- # Light-weight observability slot. Reuses the session-context probe.
72
- ("", "session-context.sh"),
73
- ],
74
69
  "SessionEnd": [
75
70
  ("", "session-end.sh"),
76
71
  ],
@@ -11,7 +11,7 @@ opencode lifecycle events. Coverage map:
11
11
 
12
12
  opencode event -> ai-toolkit Bash hook(s)
13
13
  ---------------------------------------------------------------
14
- session.created -> session-start.sh + session-context.sh + mcp-health.sh
14
+ session.created -> session-start.sh + mcp-health.sh
15
15
  session.compacted -> pre-compact.sh + pre-compact-save.sh
16
16
  session.deleted -> session-end.sh + save-session.sh
17
17
  message.updated -> user-prompt-submit.sh + track-usage.sh
@@ -79,7 +79,6 @@ export const AiToolkitHooks = async ({ $, project, directory, worktree }) => ({
79
79
  switch (event.type) {
80
80
  case "session.created":
81
81
  await runHook($, "session-start.sh", payload);
82
- await runHook($, "session-context.sh", payload);
83
82
  await runHook($, "mcp-health.sh", payload);
84
83
  break;
85
84
  case "session.compacted":
@@ -72,9 +72,6 @@ WINDSURF_HOOKS: dict[str, list[str]] = {
72
72
  "save-session.sh",
73
73
  "stop-search-check.sh",
74
74
  ],
75
- "post_setup_worktree": [
76
- "session-context.sh",
77
- ],
78
75
  }
79
76
 
80
77
 
@@ -1,63 +0,0 @@
1
- #!/usr/bin/env bash
2
- # session-context.sh — Capture session context at start for later reference.
3
- #
4
- # Fires on: SessionStart
5
- # Saves environment snapshot to JSON for cross-hook and cross-session use.
6
- # Skipped when TOOLKIT_HOOK_PROFILE=minimal.
7
-
8
- # shellcheck source=_profile-check.sh
9
- source "$(dirname "$0")/_profile-check.sh"
10
-
11
- # Read from stdin (Claude Code passes JSON with .session_id)
12
- INPUT=$(cat)
13
- SESSION=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
14
- [ -z "$SESSION" ] && SESSION="$$"
15
- SESSIONS_DIR="$HOME/.softspark/ai-toolkit/sessions"
16
- mkdir -p "$SESSIONS_DIR"
17
-
18
- CONTEXT_FILE="$SESSIONS_DIR/${SESSION}.json"
19
-
20
- # Gather context
21
- WORKDIR="$(pwd)"
22
- GIT_BRANCH=""
23
- GIT_STATUS=""
24
- if command -v git >/dev/null 2>&1 && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
25
- GIT_BRANCH=$(git branch --show-current 2>/dev/null || echo "detached")
26
- GIT_STATUS=$(git status --short 2>/dev/null | head -20)
27
- fi
28
-
29
- NODE_VERSION=""
30
- if command -v node >/dev/null 2>&1; then
31
- NODE_VERSION=$(node --version 2>/dev/null)
32
- fi
33
-
34
- PYTHON_VERSION=""
35
- if command -v python3 >/dev/null 2>&1; then
36
- PYTHON_VERSION=$(python3 --version 2>/dev/null | awk '{print $2}')
37
- fi
38
-
39
- CURRENT_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
40
-
41
- # Write JSON using python3 for proper escaping
42
- python3 - "$CONTEXT_FILE" "$WORKDIR" "$GIT_BRANCH" "$GIT_STATUS" "$CURRENT_DATE" "$NODE_VERSION" "$PYTHON_VERSION" <<'PY'
43
- import json
44
- import sys
45
- import os
46
-
47
- context_file = sys.argv[1]
48
- data = {
49
- "pwd": sys.argv[2],
50
- "git_branch": sys.argv[3],
51
- "git_status": sys.argv[4],
52
- "date": sys.argv[5],
53
- "node_version": sys.argv[6],
54
- "python_version": sys.argv[7],
55
- }
56
-
57
- tmp = context_file + ".tmp"
58
- with open(tmp, "w") as f:
59
- json.dump(data, f, indent=2)
60
- os.replace(tmp, context_file)
61
- PY
62
-
63
- exit 0