Skip to content

feat(skill): ck — persistent per-project memory for Claude Code#959

Merged
affaan-m merged 3 commits intoaffaan-m:mainfrom
sreedhargs89:feat/skill-context-keeper
Mar 29, 2026
Merged

feat(skill): ck — persistent per-project memory for Claude Code#959
affaan-m merged 3 commits intoaffaan-m:mainfrom
sreedhargs89:feat/skill-context-keeper

Conversation

@sreedhargs89
Copy link
Copy Markdown
Contributor

@sreedhargs89 sreedhargs89 commented Mar 27, 2026

Summary

Adds ck (Context Keeper) v2 — a skill that gives Claude Code persistent, per-project memory across sessions using deterministic Node.js scripts.

  • Commands are real scriptscommands/*.mjs handle all logic; Claude calls them and displays output. Behavior is consistent across model versions (no LLM-interpreted prose instructions).
  • SessionStart hook — injects a compact ~100-token summary per session (not kilobytes). Detects unsaved sessions, git activity since last save, goal mismatch vs CLAUDE.md.
  • context.json as source of truth — structured JSON with full session history, session IDs, git activity per save. CONTEXT.md is generated from it.
  • Native memory integration/ck:save writes a memory entry to ~/.claude/projects/*/memory/ so decisions surface across sessions even without /ck:resume.
  • /ck:migrate — converts v1 CONTEXT.md + meta.json data to v2 context.json; supports --dry-run; backs up originals.

Commands

Command What It Does
/ck:init Register project — auto-detects stack, goal, repo
/ck:save Snapshot session: decisions, next steps, blockers, git activity
/ck:resume [name|#] Full briefing box
/ck:info [name|#] Quick read-only snapshot
/ck:list Portfolio table — reply with number to jump in
/ck:forget Remove project context
/ck:migrate Convert v1 data to v2

Structure

skills/ck/
├── SKILL.md              ← ~50 lines: maps commands to script calls
├── commands/             ← Deterministic Node.js scripts (no external deps)
│   ├── shared.mjs        ← Shared utilities, renderers, resolvers
│   ├── init.mjs          ← Auto-detect project info → JSON for Claude to confirm
│   ├── save.mjs          ← Write session to context.json + native memory
│   ├── resume.mjs        ← Render full briefing box
│   ├── info.mjs          ← Render compact snapshot
│   ├── list.mjs          ← Render portfolio ASCII table
│   ├── forget.mjs        ← Remove project context
│   └── migrate.mjs       ← v1 → v2 migration with --dry-run
└── hooks/
    └── session-start.mjs ← Compact context injection on session start

Source

Full repo with install script, changelog, and releases:
https://github.com/sreedhargs89/context-keeper

Test plan

  • Install via ECC, open Claude Code in a new project → /ck:init auto-detects info
  • /ck:save after a session → context.json written, CONTEXT.md regenerated
  • Close and reopen Claude Code → hook shows compact 5-line briefing automatically
  • /ck:resume, /ck:info, /ck:list render correctly
  • /ck:migrate --dry-run on v1 data → preview without writing
  • Unsaved session warning appears when reopening without saving

🤖 Generated with Claude Code


Summary by cubic

Adds ck (Context Keeper) v2 — deterministic Node.js scripts that give Claude Code persistent, per‑project memory and a compact session‑start briefing. Uses context.json as the source of truth, generates CONTEXT.md, writes native memory, and supports v1 → v2 via /ck:migrate.

  • New Features

    • Script-backed commands: /ck:init, /ck:save, /ck:resume, /ck:info, /ck:list, /ck:forget, /ck:migrate.
    • SessionStart hook injects a ~100‑token summary; flags unsaved sessions, recent git activity, and goal mismatches vs CLAUDE.md.
    • Structured per‑project history with session IDs, decisions, next steps, blockers; native memory at ~/.claude/projects/*/memory/.
  • Bug Fixes

    • Fixed unsaved‑session detection by reading the previous session before writing current-session.json.
    • Preserve displayName in briefings and lists (fallback to slug only if missing).
    • Hardened git and shell usage: .git checks and 3s timeouts; runGit() replaces subshells; resume.mjs uses fs.existsSync.
    • Improved naming: init.mjs uses path.basename() for fallbacks; save confirmation shows the original name, not the slug.

Written for commit 00787d6. Summary will update on new commits.

Summary by CodeRabbit

  • New Features
    • Context Keeper skill: persistent per-project memory with init, save, resume, info, list, and forget commands
    • Auto-detection on init for name, stack, repo, goal, and description; guided drafting flow when saving sessions
    • Session-start hook auto-injects compact session context and warns about unsaved sessions or goal mismatches
    • Shows recent git activity when saving/resuming; optional native-memory markdown entries
    • Migration tool to convert legacy Context Keeper data to the new format

Adds the ck (Context Keeper) skill — deterministic Node.js scripts
that give Claude Code persistent, per-project memory across sessions.

Architecture:
- commands/ — 8 Node.js scripts handle all command logic (init, save,
  resume, info, list, forget, migrate, shared). Claude calls scripts
  and displays output — no LLM interpretation of command logic.
- hooks/session-start.mjs — injects ~100 token compact summary on
  session start (not kilobytes). Detects unsaved sessions, git
  activity since last save, goal mismatch vs CLAUDE.md.
- context.json as source of truth — CONTEXT.md is generated from it.
  Full session history, session IDs, git activity per save.

Commands: /ck:init /ck:save /ck:resume /ck:info /ck:list /ck:forget /ck:migrate
Source: https://github.com/sreedhargs89/context-keeper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ecc-tools
Copy link
Copy Markdown
Contributor

ecc-tools bot commented Mar 27, 2026

Analyzing 5000 commits...

@ecc-tools
Copy link
Copy Markdown
Contributor

ecc-tools bot commented Mar 27, 2026

Analysis Failed

Not Found - https://docs.github.com/rest/git/refs#get-a-reference

Troubleshooting
Cause Resolution
Large repository Analysis may timeout on repos with extensive history
API rate limits Wait 15 minutes before retrying
Network issues Queue timeout is 15 minutes; retry may succeed
Permissions Verify app has Contents: Read access

Retry: /ecc-tools analyze


Report Issue | ECC Tools

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 27, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c2b92017-7d74-4179-9792-ff930237a06d

📥 Commits

Reviewing files that changed from the base of the PR and between 17f6f95 and 00787d6.

📒 Files selected for processing (3)
  • skills/ck/commands/save.mjs
  • skills/ck/commands/shared.mjs
  • skills/ck/hooks/session-start.mjs

📝 Walkthrough

Walkthrough

Adds "Context Keeper v2" (ck): a per-project persistent memory system. Introduces CLI scripts (init, save, resume, info, list, forget, migrate), shared utilities, a SessionStart hook, and an on-disk layout under ~/.claude/ck/ with projects.json and per-project contexts/<name>/context.json + generated CONTEXT.md.

Changes

Cohort / File(s) Summary
Skill Documentation
skills/ck/SKILL.md
New spec for ck: on-disk layout, command behaviors, LLM-driven draft/save flow, SessionStart hook registration, and operational rules.
Shared Library & Rendering
skills/ck/commands/shared.mjs
New stdlib: CK paths/constants, robust JSON I/O, context load/save, resolveContext (cwd/number/name), git helpers with timeout, ID/date helpers, path encoding, and renderers for CONTEXT.md, briefings, info blocks, and list tables.
Init & Metadata Detection
skills/ck/commands/init.mjs
New init CLI: inspects cwd (package.json, go.mod, Cargo.toml, pyproject.toml, .git/config, CLAUDE.md, README.md) and emits structured JSON metadata for registration.
Save & Session Persistence
skills/ck/commands/save.mjs
New save CLI: stdin JSON input, --init registration mode, session create/upsert, git activity detection, writes context.json/CONTEXT.md, updates projects.json, optional native memory markdown. Strict validation and exit codes.
Resume & Info
skills/ck/commands/resume.mjs, skills/ck/commands/info.mjs
New resume/info CLIs: resolve by name/number/cwd, verify path, render bordered briefing or compact info snapshot; exit code 1 when unresolved.
List, Forget, Migrate
skills/ck/commands/list.mjs, skills/ck/commands/forget.mjs, skills/ck/commands/migrate.mjs
List: ASCII portfolio table. Forget: non-interactive removal of context dir and registry entry. Migrate: v1→v2 conversion of CONTEXT.md/meta.json into context.json sessions, supports --dry-run and backups.
SessionStart Hook
skills/ck/hooks/session-start.mjs
Hook reads stdin session_id, updates current-session.json, emits compact per-project summary or recent-projects block (unsaved-session warnings, goal-mismatch detection via CLAUDE.md, git activity), and outputs verbatim SESSION START blocks.

Sequence Diagram(s)

sequenceDiagram
    participant User as Claude/User
    participant CLI as ck CLI (node scripts)
    participant FS as File System (~/.claude/ck/)
    participant Git as Git Repo

    rect rgba(100, 150, 255, 0.5)
    Note over User,CLI: /ck:init
    User->>CLI: run /ck:init
    CLI->>FS: read project files (package.json, CLAUDE.md, README, .git)
    CLI->>FS: read/write projects.json
    CLI-->>User: JSON metadata output
    end

    rect rgba(100, 200, 100, 0.5)
    Note over User,Git: /ck:save
    User->>CLI: /ck:save (LLM draft -> JSON stdin)
    CLI->>FS: load projects.json & context.json
    CLI->>Git: gitSummary(since lastSession)
    CLI->>FS: write context.json, CONTEXT.md, native memory
    CLI-->>User: success confirmation (session id, git summary)
    end

    rect rgba(200, 100, 100, 0.5)
    Note over User,CLI: SessionStart Hook
    User->>CLI: session-start hook (stdin session_id)
    CLI->>FS: read projects.json, current-session.json, context.json, CLAUDE.md
    CLI->>Git: git log since lastSessionDate
    CLI-->>User: compact project summary / SESSION START block
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I nibble logs and tuck them tight,

Init a den, then save by night.
Resume the tale where last I hopped,
Forget the weeds that time has stopped.
Context kept — hop on, all right!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(skill): ck — persistent per-project memory for Claude Code' clearly and specifically summarizes the main change: introducing a new skill (ck) that provides persistent per-project memory functionality. It is concise, uses standard commit convention, and directly reflects the core contribution of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 27, 2026

Greptile Summary

This PR introduces ck (Context Keeper) v2 — a new community skill that gives Claude Code persistent, per-project memory across sessions using deterministic Node.js scripts with no external dependencies. It fits cleanly into the skills/ structure, registering projects in ~/.claude/ck/, writing native Claude memory entries, and injecting a compact ~100-token briefing via a SessionStart hook.

All previously-reported issues (read-after-write race in session-start.mjs, shell injection in resume.mjs and shared.mjs, displayName rendering) have been addressed. A few new concerns remain:

  • SKILL.md hook registration uses ~ inside double quotes — tilde expansion is suppressed by the shell in this position, so the SessionStart hook silently fails on installation for most users. Replacing ~ with $HOME fixes it.
  • encodeProjectPath can collide — paths like /home/user/my-app and /home/user-my-app both encode to -home-user-my-app, pointing native-memory writes from two different projects at the same directory.
  • Empty contextDir guard missing in save.mjs — an all-special-character project name produces an empty slug, causing context.json to be written at the wrong path.
  • list.mjs has a redundant duplicate import from shared.mjs and imports CONTEXTS_DIR without using it.

Confidence Score: 4/5

Safe to merge after fixing the tilde-expansion bug in the hook registration command — without it the core SessionStart feature is silently broken for all users

All previously flagged P0/P1 issues are resolved. One new P1 remains: the hook command in SKILL.md uses ~ inside double quotes which bash does not expand, meaning the SessionStart hook never fires on any standard installation. The remaining findings are P2 and do not block merge.

skills/ck/SKILL.md — hook registration command must use $HOME instead of ~

Important Files Changed

Filename Overview
skills/ck/SKILL.md Instruction manifest for the ck skill; the SessionStart hook registration example uses ~ inside double quotes which won't expand in bash, silently breaking the core hook feature
skills/ck/commands/shared.mjs Core utility module; git helpers now use spawnSync (safe, no shell injection); encodeProjectPath can produce collisions between paths that differ only in / vs - characters
skills/ck/hooks/session-start.mjs SessionStart hook; previously flagged read-after-write race fixed; spawnSync used for git calls; compact ~100-token context injection looks correct
skills/ck/commands/save.mjs Session persistence and init registration; displayName/name separation is correct; contextDir sanitizer can produce an empty string for all-special-char names, causing files to land at wrong path
skills/ck/commands/resume.mjs Full briefing renderer; previously flagged execSync shell injection replaced with safe existsSync check; logic is correct
skills/ck/commands/migrate.mjs v1 to v2 migration with --dry-run support; migrated session IDs truncated to 8 chars while live saves store full UUIDs — harmless in practice; backs up originals safely
skills/ck/commands/list.mjs Portfolio listing; redundant duplicate import from shared.mjs and unused CONTEXTS_DIR import; functional behavior is correct
skills/ck/commands/init.mjs Project auto-detection across package.json, go.mod, Cargo.toml, pyproject.toml, .git/config, CLAUDE.md, README; directory basename used as fallback name; correct
skills/ck/commands/info.mjs Compact read-only project snapshot; thin wrapper around resolveContext + renderInfoBlock; no issues found
skills/ck/commands/forget.mjs Project removal via rmSync + projects.json update; confirmation prompt delegated to Claude per SKILL.md; correct

Sequence Diagram

sequenceDiagram
    participant U as User
    participant CC as Claude Code
    participant SS as session-start.mjs
    participant S as save.mjs
    participant R as resume.mjs
    participant FS as ~/.claude/ck/

    CC->>SS: SessionStart hook fires
    SS->>FS: readJson(current-session.json) [prev]
    SS->>FS: writeFileSync(current-session.json) [new]
    SS->>FS: readJson(context.json)
    SS-->>CC: additionalContext (compact ~100 token briefing)

    U->>CC: /ck:init
    CC->>CC: node init.mjs → JSON
    CC->>U: Confirm project details?
    U->>CC: Confirmed
    CC->>S: echo JSON | node save.mjs --init
    S->>FS: writeJson(context.json + CONTEXT.md + projects.json)

    U->>CC: /ck:save
    CC->>U: Draft summary — save this?
    U->>CC: yes
    CC->>S: echo JSON | node save.mjs
    S->>FS: update context.json (append session)
    S->>FS: writeFileSync(~/.claude/projects/<encoded>/memory/ck_*.md)

    U->>CC: /ck:resume
    CC->>R: node resume.mjs [arg]
    R->>FS: readJson(context.json)
    R-->>CC: Bordered briefing box
Loading

Reviews (3): Last reviewed commit: "fix(ck): preserve display names and hard..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (3)
skills/ck/commands/init.mjs (1)

138-141: Use path.basename() for cross-platform directory name extraction.

Splitting by / won't work correctly on Windows paths.

♻️ Proposed fix
+import { resolve, basename } from 'path';
...
 // ── Name fallback: directory name ─────────────────────────────────────────
 if (!output.name) {
-  output.name = cwd.split('/').pop().toLowerCase().replace(/\s+/g, '-');
+  output.name = basename(cwd).toLowerCase().replace(/\s+/g, '-');
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/commands/init.mjs` around lines 138 - 141, The code sets
output.name using cwd.split('/').pop(), which breaks on Windows paths; replace
that expression with path.basename(cwd) and ensure the module imports/uses the
Node path API (e.g., require('path') or import path from 'path') so
cross-platform directory name extraction is used, then continue to call
.toLowerCase().replace(/\s+/g, '-') on the basename as before to preserve the
existing normalization.
skills/ck/commands/list.mjs (1)

11-12: Consolidate duplicate imports.

The two import statements from ./shared.mjs can be combined into one.

♻️ Proposed fix
-import { readProjects, loadContext, today, CONTEXTS_DIR } from './shared.mjs';
-import { renderListTable } from './shared.mjs';
+import { readProjects, loadContext, today, CONTEXTS_DIR, renderListTable } from './shared.mjs';

Note: CONTEXTS_DIR appears to be unused in this file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/commands/list.mjs` around lines 11 - 12, The file has two separate
imports from './shared.mjs' — combine them into a single import statement that
imports readProjects, loadContext, today, renderListTable (and drop CONTEXTS_DIR
if it truly isn't used in this module) to eliminate duplication; update the
import to reference the unique symbols readProjects, loadContext, today,
renderListTable (and remove CONTEXTS_DIR) so the module uses one consolidated
import line.
skills/ck/commands/shared.mjs (1)

182-185: Windows path encoding is incomplete.

encodeProjectPath only replaces forward slashes. On Windows, paths like C:\Users\foo would become C:\Users\foo (unchanged), potentially causing issues with native memory directory creation.

♻️ Proposed fix for cross-platform support
 export function encodeProjectPath(absolutePath) {
   // "/Users/sree/dev/app" -> "-Users-sree-dev-app"
-  return absolutePath.replace(/\//g, '-');
+  return absolutePath.replace(/[/\\:]/g, '-');
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/commands/shared.mjs` around lines 182 - 185, The encodeProjectPath
function currently only replaces forward slashes; update it to normalize Windows
paths too by replacing backslashes and drive-letter colons so paths like
"C:\Users\foo" are encoded safely; specifically change the replacement to target
both '/' and '\' and ':' (e.g. use a regex like /[\/\\:]/g) in encodeProjectPath
and keep the same replacement character '-' so all platforms produce a
consistent encoded string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@skills/ck/commands/forget.mjs`:
- Around line 39-44: The code deletes the context directory and then updates
projects.json via readProjects()/writeProjects(), which can leave projects.json
pointing to a removed context if writeProjects fails; wrap the remove-and-write
sequence in a try/catch around the operations that touch projects (use
readProjects, delete projects[projectPath], writeProjects) and on error restore
the deleted entry (re-add projects[projectPath] = originalValue) or recreate the
directory as appropriate, then log a clear error including the project name
variable (name) and the error so the caller can retry or clean up; ensure the
catch does not swallow errors and exits with a non-zero status if unrecoverable.

In `@skills/ck/commands/migrate.mjs`:
- Around line 44-47: The parser in parseDecisionsTable is treating aligned
markdown separator rows (e.g. "|:---|---:|") as data because it only checks for
lines matching /^[|\s-]+$/, so modify the logic that processes each line (the
block using line, cols and rows) to detect and skip markdown separator rows:
after splitting into cols, add a guard that skips the line when all relevant
cols match a separator pattern (e.g. /^:?-+:?$/ or generally /^[\s:-]+$/),
ensuring only real content rows are pushed into rows (keep references to line,
cols, rows and the function parseDecisionsTable).
- Around line 35-37: The parseBullets chain is using a regex that fails to match
multi-digit or dotted numbered lists (e.g. "1. item", "12. item"), causing
numbered items to be dropped; update the first .filter and the .map replace to
accept optional leading whitespace and either a dash/star or one-or-more digits
followed by a dot (e.g. use /^\s*(?:[-*]|\d+\.)\s*/ in the test/replace logic)
so parseBullets correctly retains and strips list markers for numbered and
bulleted items.

In `@skills/ck/commands/resume.mjs`:
- Around line 26-38: The current directory-existence check uses execSync with
projectPath interpolated into a shell command (in the block that references
projectPath, cwd, and execSync), which is a command-injection risk; replace that
whole execSync try/catch with a safe Node fs check: normalize/resolve
projectPath (use path.resolve) and then use fs.existsSync or fs.statSync to
verify the path exists and is a directory (e.g., check stat.isDirectory()), and
then log the same messages (→ cd ... or ⚠ Path not found ...) — remove any shell
interpolation and keep the logic that skips if projectPath is falsy or equals
cwd.

In `@skills/ck/commands/save.mjs`:
- Around line 121-128: When re-saving a session (existingIdx from
context.sessions.findIndex) the code always uses the last session's date for
gitSummary; change the logic so lastSessionDate uses the date of the session
being updated when existingIdx >= 0 (e.g., context.sessions[existingIdx]?.date)
and only fall back to the last session's date when creating a new session
(existingIdx < 0), then pass that corrected lastSessionDate into gitSummary(cwd,
lastSessionDate).

In `@skills/ck/commands/shared.mjs`:
- Around line 244-251: The table rows are corrupted if decision fields contain
pipe characters; before pushing rows for each decision in the loop (where
allDecisions.forEach and the template `| ${d.what} | ${d.why || ''} | ${d.date
|| ''} |` is built), sanitize each cell value (d.what, d.why, d.date) by
escaping or HTML-encoding pipe characters (e.g., replace '|' with '&#124;' or
prepend a backslash) and normalizing newlines so the Markdown table stays
intact; apply this sanitization where the template is constructed.
- Around line 145-155: The runGit helper currently interpolates cwd into a shell
string causing command injection risk; update runGit to call execSync without
embedding cwd into the command (remove `-C "${cwd}"`) and instead pass cwd via
the execSync options object (use the cwd property), keep args as a separate
string or ideally as an array passed safely, preserve timeout/stdio/encoding and
return trimmed output or null on error; refer to the runGit function and its use
of execSync so you replace the `git -C "${cwd}" ${args}` invocation with
execSync('git ' + args, { cwd, timeout, stdio, encoding }) (or equivalent safe
invocation).

In `@skills/ck/hooks/session-start.mjs`:
- Line 216: The current console.log emits { additionalContext } directly, but
SessionStart hooks must output a hook envelope; replace the single top-level
additionalContext emission with a hookSpecificOutput object containing
hookEventName: "SessionStart" and the additionalContext payload (i.e., wrap the
existing parts.join(...) string under additionalContext inside
hookSpecificOutput with hookEventName "SessionStart"); update the console.log
call that currently references parts.join(...) so the platform recognizes the
hook output.
- Around line 89-97: You write current-session.json before checking the previous
session so the mismatch check never triggers; change the flow in the
session-start logic to first attempt to read/parse the existing CURRENT_SESSION
(wrap in try/catch and parse to prevSession), perform the mismatch check
comparing prevSession.sessionId (and any relevant fields) against the new
sessionId/project info, handle the warning if they differ, and only then
serialize/write the new session object to CURRENT_SESSION; reference
CURRENT_SESSION, sessionId, entry?.name, and the existing mismatch-check block
so you update the read/write order without changing the comparison logic.
- Around line 20-21: The git command built in session-start.mjs currently
interpolates projectPath and sinceDate into a shell string passed to execSync,
allowing shell injection; replace the use of execSync with execFileSync
(imported from 'child_process') and call git as an executable with an argument
array that includes projectPath and sinceDate as separate arguments (e.g., use
['-C', projectPath, 'log', `--since=${sinceDate}`, ...] or pass '--since' and
sinceDate separately) so no user data is passed through a shell string and avoid
shell:true.

In `@skills/ck/SKILL.md`:
- Around line 10-147: The SKILL.md is missing the required documentation
sections; update SKILL.md by adding three markdown sections: "When to Use"
(bullet scenarios for using /ck:init, /ck:save, /ck:resume, /ck:list,
/ck:forget, /ck:migrate and the session-start hook), "How It Works" (brief
numbered steps describing Registration (/ck:init → save.mjs), Saving (/ck:save
analysis → save.mjs), Resuming (/ck:resume and session-start hook injection),
and Migration (/ck:migrate)), and "Examples" (short sample invocations and
expected outputs for /ck:init, /ck:save, /ck:resume), ensuring wording matches
the repository skill formatting guidelines and that the examples reference the
actual command names used in the file.

---

Nitpick comments:
In `@skills/ck/commands/init.mjs`:
- Around line 138-141: The code sets output.name using cwd.split('/').pop(),
which breaks on Windows paths; replace that expression with path.basename(cwd)
and ensure the module imports/uses the Node path API (e.g., require('path') or
import path from 'path') so cross-platform directory name extraction is used,
then continue to call .toLowerCase().replace(/\s+/g, '-') on the basename as
before to preserve the existing normalization.

In `@skills/ck/commands/list.mjs`:
- Around line 11-12: The file has two separate imports from './shared.mjs' —
combine them into a single import statement that imports readProjects,
loadContext, today, renderListTable (and drop CONTEXTS_DIR if it truly isn't
used in this module) to eliminate duplication; update the import to reference
the unique symbols readProjects, loadContext, today, renderListTable (and remove
CONTEXTS_DIR) so the module uses one consolidated import line.

In `@skills/ck/commands/shared.mjs`:
- Around line 182-185: The encodeProjectPath function currently only replaces
forward slashes; update it to normalize Windows paths too by replacing
backslashes and drive-letter colons so paths like "C:\Users\foo" are encoded
safely; specifically change the replacement to target both '/' and '\' and ':'
(e.g. use a regex like /[\/\\:]/g) in encodeProjectPath and keep the same
replacement character '-' so all platforms produce a consistent encoded string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9bb042ee-b7ff-4a1a-9f4c-3957772ee2c8

📥 Commits

Reviewing files that changed from the base of the PR and between cc60bf6 and 1e226ba.

📒 Files selected for processing (10)
  • skills/ck/SKILL.md
  • skills/ck/commands/forget.mjs
  • skills/ck/commands/info.mjs
  • skills/ck/commands/init.mjs
  • skills/ck/commands/list.mjs
  • skills/ck/commands/migrate.mjs
  • skills/ck/commands/resume.mjs
  • skills/ck/commands/save.mjs
  • skills/ck/commands/shared.mjs
  • skills/ck/hooks/session-start.mjs

Comment on lines +39 to +44
// Remove from projects.json
const projects = readProjects();
delete projects[projectPath];
writeProjects(projects);

console.log(`✓ Context for '${name}' removed.`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential state inconsistency if writeProjects fails.

If the context directory is deleted but writeProjects throws (e.g., disk full, permissions), projects.json will still reference the deleted context. Consider wrapping in try/catch to handle this edge case gracefully.

🛡️ Proposed fix
 // Remove from projects.json
-const projects = readProjects();
-delete projects[projectPath];
-writeProjects(projects);
+try {
+  const projects = readProjects();
+  delete projects[projectPath];
+  writeProjects(projects);
+} catch (e) {
+  console.log(`⚠  Context directory removed but projects.json update failed: ${e.message}`);
+  process.exit(1);
+}

 console.log(`✓ Context for '${name}' removed.`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/commands/forget.mjs` around lines 39 - 44, The code deletes the
context directory and then updates projects.json via
readProjects()/writeProjects(), which can leave projects.json pointing to a
removed context if writeProjects fails; wrap the remove-and-write sequence in a
try/catch around the operations that touch projects (use readProjects, delete
projects[projectPath], writeProjects) and on error restore the deleted entry
(re-add projects[projectPath] = originalValue) or recreate the directory as
appropriate, then log a clear error including the project name variable (name)
and the error so the caller can retry or clean up; ensure the catch does not
swallow errors and exits with a non-zero status if unrecoverable.

Comment on lines +35 to +37
.filter(l => /^[-*\d]\s/.test(l.trim()))
.map(l => l.replace(/^[-*\d]+\.?\s+/, '').trim())
.filter(Boolean);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Numbered bullet items are silently dropped during migration.

parseBullets currently misses common markdown list items like 1. item and multi-digit numbering, so nextSteps, constraints, blockers, and leftOff can lose data during v1→v2 conversion.

Proposed fix
 function parseBullets(text) {
   if (!text) return [];
   return text.split('\n')
-    .filter(l => /^[-*\d]\s/.test(l.trim()))
-    .map(l => l.replace(/^[-*\d]+\.?\s+/, '').trim())
+    .filter(l => /^([-*]\s+|\d+\.?\s+)/.test(l.trim()))
+    .map(l => l.replace(/^([-*]|\d+\.?)\s+/, '').trim())
     .filter(Boolean);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/commands/migrate.mjs` around lines 35 - 37, The parseBullets chain
is using a regex that fails to match multi-digit or dotted numbered lists (e.g.
"1. item", "12. item"), causing numbered items to be dropped; update the first
.filter and the .map replace to accept optional leading whitespace and either a
dash/star or one-or-more digits followed by a dot (e.g. use
/^\s*(?:[-*]|\d+\.)\s*/ in the test/replace logic) so parseBullets correctly
retains and strips list markers for numbered and bulleted items.

Comment on lines +44 to +47
if (!line.startsWith('|') || line.match(/^[|\s-]+$/)) continue;
const cols = line.split('|').map(c => c.trim()).filter((c, i) => i > 0 && i < 4);
if (cols.length >= 1 && !cols[0].startsWith('Decision') && !cols[0].startsWith('_')) {
rows.push({ what: cols[0] || '', why: cols[1] || '', date: cols[2] || '' });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Aligned markdown separator rows can be misparsed as decisions.

parseDecisionsTable does not skip separator rows containing :, so tables like |:---|---:| may produce fake decision records.

Proposed fix
-    if (!line.startsWith('|') || line.match(/^[|\s-]+$/)) continue;
+    if (!line.startsWith('|') || line.match(/^[|\s:-]+$/)) continue;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/commands/migrate.mjs` around lines 44 - 47, The parser in
parseDecisionsTable is treating aligned markdown separator rows (e.g.
"|:---|---:|") as data because it only checks for lines matching /^[|\s-]+$/, so
modify the logic that processes each line (the block using line, cols and rows)
to detect and skip markdown separator rows: after splitting into cols, add a
guard that skips the line when all relevant cols match a separator pattern (e.g.
/^:?-+:?$/ or generally /^[\s:-]+$/), ensuring only real content rows are pushed
into rows (keep references to line, cols, rows and the function
parseDecisionsTable).

Comment on lines +121 to +128
// Check for duplicate (re-save of same session)
const existingIdx = context.sessions.findIndex(s => s.id === sessionId);

const { summary, leftOff, nextSteps, decisions, blockers, goal } = input;

// Capture git activity since the last session
const lastSessionDate = context.sessions?.[context.sessions.length - 1]?.date;
const gitActivity = gitSummary(cwd, lastSessionDate);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Git activity calculation may be incorrect on session re-save.

When re-saving an existing session (existingIdx >= 0), lastSessionDate is taken from the last session in the array (line 127), not from the session being updated. If the session being re-saved isn't the last one, gitSummary will use the wrong baseline date.

This is an edge case (re-saving a non-latest session is uncommon), but worth noting.

💡 Proposed fix
 // Check for duplicate (re-save of same session)
 const existingIdx = context.sessions.findIndex(s => s.id === sessionId);

 const { summary, leftOff, nextSteps, decisions, blockers, goal } = input;

 // Capture git activity since the last session
-const lastSessionDate = context.sessions?.[context.sessions.length - 1]?.date;
+const lastSessionDate = existingIdx >= 0
+  ? context.sessions[existingIdx]?.date  // Use the session's own date on re-save
+  : context.sessions?.[context.sessions.length - 1]?.date;
 const gitActivity = gitSummary(cwd, lastSessionDate);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Check for duplicate (re-save of same session)
const existingIdx = context.sessions.findIndex(s => s.id === sessionId);
const { summary, leftOff, nextSteps, decisions, blockers, goal } = input;
// Capture git activity since the last session
const lastSessionDate = context.sessions?.[context.sessions.length - 1]?.date;
const gitActivity = gitSummary(cwd, lastSessionDate);
// Check for duplicate (re-save of same session)
const existingIdx = context.sessions.findIndex(s => s.id === sessionId);
const { summary, leftOff, nextSteps, decisions, blockers, goal } = input;
// Capture git activity since the last session
const lastSessionDate = existingIdx >= 0
? context.sessions[existingIdx]?.date // Use the session's own date on re-save
: context.sessions?.[context.sessions.length - 1]?.date;
const gitActivity = gitSummary(cwd, lastSessionDate);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/commands/save.mjs` around lines 121 - 128, When re-saving a session
(existingIdx from context.sessions.findIndex) the code always uses the last
session's date for gitSummary; change the logic so lastSessionDate uses the date
of the session being updated when existingIdx >= 0 (e.g.,
context.sessions[existingIdx]?.date) and only fall back to the last session's
date when creating a new session (existingIdx < 0), then pass that corrected
lastSessionDate into gitSummary(cwd, lastSessionDate).

Comment on lines +244 to +251
lines.push(`## Decisions Made`);
lines.push(`| Decision | Why | Date |`);
lines.push(`|----------|-----|------|`);
if (allDecisions.length) {
allDecisions.forEach(d => lines.push(`| ${d.what} | ${d.why || ''} | ${d.date || ''} |`));
} else {
lines.push(`| _(none yet)_ | | |`);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Pipe characters in decision text will break the Markdown table.

If d.what or d.why contains | characters, the table structure will be corrupted.

💡 Proposed fix
+const escapeCell = (s) => String(s || '').replace(/\|/g, '\\|');
+
 lines.push(`## Decisions Made`);
 lines.push(`| Decision | Why | Date |`);
 lines.push(`|----------|-----|------|`);
 if (allDecisions.length) {
-  allDecisions.forEach(d => lines.push(`| ${d.what} | ${d.why || ''} | ${d.date || ''} |`));
+  allDecisions.forEach(d => lines.push(`| ${escapeCell(d.what)} | ${escapeCell(d.why)} | ${d.date || ''} |`));
 } else {
   lines.push(`| _(none yet)_ | | |`);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
lines.push(`## Decisions Made`);
lines.push(`| Decision | Why | Date |`);
lines.push(`|----------|-----|------|`);
if (allDecisions.length) {
allDecisions.forEach(d => lines.push(`| ${d.what} | ${d.why || ''} | ${d.date || ''} |`));
} else {
lines.push(`| _(none yet)_ | | |`);
}
const escapeCell = (s) => String(s || '').replace(/\|/g, '\\|');
lines.push(`## Decisions Made`);
lines.push(`| Decision | Why | Date |`);
lines.push(`|----------|-----|------|`);
if (allDecisions.length) {
allDecisions.forEach(d => lines.push(`| ${escapeCell(d.what)} | ${escapeCell(d.why)} | ${d.date || ''} |`));
} else {
lines.push(`| _(none yet)_ | | |`);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/commands/shared.mjs` around lines 244 - 251, The table rows are
corrupted if decision fields contain pipe characters; before pushing rows for
each decision in the loop (where allDecisions.forEach and the template `|
${d.what} | ${d.why || ''} | ${d.date || ''} |` is built), sanitize each cell
value (d.what, d.why, d.date) by escaping or HTML-encoding pipe characters
(e.g., replace '|' with '&#124;' or prepend a backslash) and normalizing
newlines so the Markdown table stays intact; apply this sanitization where the
template is constructed.


const parts = main();
if (parts.length > 0) {
console.log(JSON.stringify({ additionalContext: parts.join('\n\n---\n\n') }));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

SessionStart payload shape does not follow the expected hook envelope.

Line 216 emits { additionalContext }, but SessionStart hooks should emit hookSpecificOutput with hookEventName: "SessionStart" so the platform treats it as hook context.

Proposed fix
 if (parts.length > 0) {
-  console.log(JSON.stringify({ additionalContext: parts.join('\n\n---\n\n') }));
+  console.log(JSON.stringify({
+    hookSpecificOutput: {
+      hookEventName: 'SessionStart',
+      additionalContext: parts.join('\n\n---\n\n'),
+    },
+  }));
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(JSON.stringify({ additionalContext: parts.join('\n\n---\n\n') }));
if (parts.length > 0) {
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: 'SessionStart',
additionalContext: parts.join('\n\n---\n\n'),
},
}));
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/hooks/session-start.mjs` at line 216, The current console.log emits
{ additionalContext } directly, but SessionStart hooks must output a hook
envelope; replace the single top-level additionalContext emission with a
hookSpecificOutput object containing hookEventName: "SessionStart" and the
additionalContext payload (i.e., wrap the existing parts.join(...) string under
additionalContext inside hookSpecificOutput with hookEventName "SessionStart");
update the console.log call that currently references parts.join(...) so the
platform recognizes the hook output.

Comment on lines +10 to +147
# ck — Context Keeper

You are the **Context Keeper** assistant. When the user invokes any `/ck:*` command,
run the corresponding Node.js script and present its stdout to the user verbatim.
Scripts live at: `~/.claude/skills/ck/commands/` (expand `~` with `$HOME`).

---

## Data Layout

```
~/.claude/ck/
├── projects.json ← path → {name, contextDir, lastUpdated}
└── contexts/<name>/
├── context.json ← SOURCE OF TRUTH (structured JSON, v2)
└── CONTEXT.md ← generated view — do not hand-edit
```

---

## Commands

### `/ck:init` — Register a Project
```bash
node "$HOME/.claude/skills/ck/commands/init.mjs"
```
The script outputs JSON with auto-detected info. Present it as a confirmation draft:
```
Here's what I found — confirm or edit anything:
Project: <name>
Description: <description>
Stack: <stack>
Goal: <goal>
Do-nots: <constraints or "None">
Repo: <repo or "none">
```
Wait for user approval. Apply any edits. Then pipe confirmed JSON to save.mjs --init:
```bash
echo '<confirmed-json>' | node "$HOME/.claude/skills/ck/commands/save.mjs" --init
```
Confirmed JSON schema: `{"name":"...","path":"...","description":"...","stack":["..."],"goal":"...","constraints":["..."],"repo":"..." }`

---

### `/ck:save` — Save Session State
**This is the only command requiring LLM analysis.** Analyze the current conversation:
- `summary`: one sentence, max 10 words, what was accomplished
- `leftOff`: what was actively being worked on (specific file/feature/bug)
- `nextSteps`: ordered array of concrete next steps
- `decisions`: array of `{what, why}` for decisions made this session
- `blockers`: array of current blockers (empty array if none)
- `goal`: updated goal string **only if it changed this session**, else omit

Show a draft summary to the user: `"Session: '<summary>' — save this? (yes / edit)"`
Wait for confirmation. Then pipe to save.mjs:
```bash
echo '<json>' | node "$HOME/.claude/skills/ck/commands/save.mjs"
```
JSON schema (exact): `{"summary":"...","leftOff":"...","nextSteps":["..."],"decisions":[{"what":"...","why":"..."}],"blockers":["..."]}`
Display the script's stdout confirmation verbatim.

---

### `/ck:resume [name|number]` — Full Briefing
```bash
node "$HOME/.claude/skills/ck/commands/resume.mjs" [arg]
```
Display output verbatim. Then ask: "Continue from here? Or has anything changed?"
If user reports changes → run `/ck:save` immediately.

---

### `/ck:info [name|number]` — Quick Snapshot
```bash
node "$HOME/.claude/skills/ck/commands/info.mjs" [arg]
```
Display output verbatim. No follow-up question.

---

### `/ck:list` — Portfolio View
```bash
node "$HOME/.claude/skills/ck/commands/list.mjs"
```
Display output verbatim. If user replies with a number or name → run `/ck:resume`.

---

### `/ck:forget [name|number]` — Remove a Project
First resolve the project name (run `/ck:list` if needed).
Ask: `"This will permanently delete context for '<name>'. Are you sure? (yes/no)"`
If yes:
```bash
node "$HOME/.claude/skills/ck/commands/forget.mjs" [name]
```
Display confirmation verbatim.

---

### `/ck:migrate` — Convert v1 Data to v2
```bash
node "$HOME/.claude/skills/ck/commands/migrate.mjs"
```
For a dry run first:
```bash
node "$HOME/.claude/skills/ck/commands/migrate.mjs" --dry-run
```
Display output verbatim. Migrates all v1 CONTEXT.md + meta.json files to v2 context.json.
Originals are backed up as `meta.json.v1-backup` — nothing is deleted.

---

## SessionStart Hook

The hook at `~/.claude/skills/ck/hooks/session-start.mjs` must be registered in
`~/.claude/settings.json` to auto-load project context on session start:

```json
{
"hooks": {
"SessionStart": [
{ "hooks": [{ "type": "command", "command": "node \"~/.claude/skills/ck/hooks/session-start.mjs\"" }] }
]
}
}
```

The hook injects ~100 tokens per session (compact 5-line summary). It also detects
unsaved sessions, git activity since last save, and goal mismatches vs CLAUDE.md.

---

## Rules
- Always expand `~` as `$HOME` in Bash calls.
- Commands are case-insensitive: `/CK:SAVE`, `/ck:save`, `/Ck:Save` all work.
- If a script exits with code 1, display its stdout as an error message.
- Never edit `context.json` or `CONTEXT.md` directly — always use the scripts.
- If `projects.json` is malformed, tell the user and offer to reset it to `{}`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Missing required skill documentation sections.

Per coding guidelines, skills under skills/**/*.md must include 'When to Use', 'How It Works', and 'Examples' sections. This SKILL.md is missing all three.

Consider adding these sections to comply with the repository's skill formatting standards:

📝 Suggested structure to add after line 9
## When to Use

- When starting a new Claude Code session and you want to quickly resume context from your last session
- When working across multiple projects and need to track session state for each
- When you want decisions, blockers, and next steps to persist between sessions
- When onboarding to a project and need a quick briefing on current state

## How It Works

1. **Registration**: `/ck:init` detects project metadata (stack, goal, constraints) from config files and CLAUDE.md
2. **Saving**: `/ck:save` captures session state (decisions, blockers, next steps) to `context.json` and writes to native memory
3. **Resuming**: `/ck:resume` loads the full briefing box; the session-start hook auto-injects a compact summary (~100 tokens)
4. **Portfolio**: `/ck:list` shows all registered projects with staleness indicators

## Examples

### Initialize a new project

/ck:init

Claude auto-detects project info and asks for confirmation before saving.

### Save session state

/ck:save

Claude analyzes the conversation and proposes a summary to save.

### Resume a project by name

/ck:resume my-app

/ck:resume 2


As per coding guidelines: "Skills must be formatted as Markdown files with clear sections including 'When to Use', 'How It Works', and 'Examples'".
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @skills/ck/SKILL.md around lines 10 - 147, The SKILL.md is missing the
required documentation sections; update SKILL.md by adding three markdown
sections: "When to Use" (bullet scenarios for using /ck:init, /ck:save,
/ck:resume, /ck:list, /ck:forget, /ck:migrate and the session-start hook), "How
It Works" (brief numbered steps describing Registration (/ck:init → save.mjs),
Saving (/ck:save analysis → save.mjs), Resuming (/ck:resume and session-start
hook injection), and Migration (/ck:migrate)), and "Examples" (short sample
invocations and expected outputs for /ck:init, /ck:save, /ck:resume), ensuring
wording matches the repository skill formatting guidelines and that the examples
reference the actual command names used in the file.


</details>

<!-- fingerprinting:phantom:medusa:ocelot:57dcdb90-4846-489b-bd12-c20feb894a8a -->

<!-- This is an auto-generated comment by CodeRabbit -->

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

22 issues found across 10 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="skills/ck/commands/init.mjs">

<violation number="1" location="skills/ck/commands/init.mjs:36">
P2: Section extraction regex is newline-fragile; it won’t match CRLF (`\r\n`) headings, causing CLAUDE.md sections to be skipped on Windows.</violation>

<violation number="2" location="skills/ck/commands/init.mjs:97">
P2: Repo URL from .git/config is emitted verbatim; if the remote contains embedded credentials (https://token@host/...), they will be exposed in output and persisted. Sanitize userinfo before saving.</violation>

<violation number="3" location="skills/ck/commands/init.mjs:140">
P2: Name fallback splits `cwd` only on '/', which breaks on Windows paths and yields a full path instead of the directory name.</violation>
</file>

<file name="skills/ck/SKILL.md">

<violation number="1" location="skills/ck/SKILL.md:7">
P2: User-facing skill metadata links to an external GitHub repo, which violates the supply-chain hardening guidance to avoid unvetted external repositories.</violation>

<violation number="2" location="skills/ck/SKILL.md:48">
P2: Using single-quoted `echo '<confirmed-json>'` is unsafe for JSON that may contain apostrophes; it can break the shell string or allow unintended shell interpretation. Use a quoted heredoc or printf-safe piping instead.</violation>

<violation number="3" location="skills/ck/SKILL.md:66">
P2: Using single-quoted `echo '<json>'` is unsafe for JSON that may contain apostrophes; it can break the shell string or allow unintended shell interpretation. Use a quoted heredoc or printf-safe piping instead.</violation>

<violation number="4" location="skills/ck/SKILL.md:131">
P2: SessionStart hook example quotes `~`, preventing tilde expansion and likely breaking the hook command.</violation>
</file>

<file name="skills/ck/commands/resume.mjs">

<violation number="1" location="skills/ck/commands/resume.mjs:29">
P2: Shell-based `test -d` directory check is non-portable (fails on Windows) and errors are swallowed, so `/ck:resume` can silently skip the path message. Prefer a cross-platform fs check (fs.stat/access) with explicit error handling.</violation>

<violation number="2" location="skills/ck/commands/resume.mjs:29">
P2: `execSync` shells out with an unsanitized `projectPath`. Because the value is interpolated into a shell command, a crafted project path (from persisted projects.json) can trigger command injection. Use fs APIs instead of a shell command to check for directory existence.</violation>
</file>

<file name="skills/ck/commands/migrate.mjs">

<violation number="1" location="skills/ck/commands/migrate.mjs:27">
P2: extractSection only matches LF (`\n`) line endings, so CRLF Markdown headings/sections won't parse and migrated fields can be dropped.</violation>

<violation number="2" location="skills/ck/commands/migrate.mjs:35">
P2: parseBullets filters out standard numbered list items like `1. item`, causing those entries to be dropped during migration.</violation>

<violation number="3" location="skills/ck/commands/migrate.mjs:44">
P2: Skip alignment separator rows that contain `:` in the decisions table parser; otherwise `|:---|---:|` lines can be migrated as bogus decision records.</violation>

<violation number="4" location="skills/ck/commands/migrate.mjs:175">
P2: Migration renames meta.json before saveContext; a failure leaves the project in a partially migrated state with meta.json missing, so retries lose v1 metadata unless the user restores the backup manually.</violation>
</file>

<file name="skills/ck/hooks/session-start.mjs">

<violation number="1" location="skills/ck/hooks/session-start.mjs:50">
P1: Shell command injection risk: execSync is called with a string that interpolates projectPath and sinceDate, allowing crafted values to break out of quotes and execute arbitrary commands.</violation>

<violation number="2" location="skills/ck/hooks/session-start.mjs:91">
P2: Unsaved-session detection reads CURRENT_SESSION after it has already been overwritten with the new session, so prevSession usually equals sessionId and the warning never triggers.</violation>
</file>

<file name="skills/ck/commands/save.mjs">

<violation number="1" location="skills/ck/commands/save.mjs:57">
P2: `contextDir` can be empty after sanitization, but it is still used to save/register a project. An empty `contextDir` writes context.json/CONTEXT.md directly into the base contexts directory, causing collisions/overwrites and ambiguous lookups for projects whose names sanitize to empty.</violation>

<violation number="2" location="skills/ck/commands/save.mjs:178">
P2: Unescaped `session.summary` is inserted directly into YAML frontmatter, so summaries containing newlines or YAML-significant characters can corrupt the metadata and break native memory parsing.</violation>
</file>

<file name="skills/ck/commands/shared.mjs">

<violation number="1" location="skills/ck/commands/shared.mjs:147">
P1: Dynamic shell command construction in `runGit` allows command injection via unescaped `cwd`/`args` interpolation.</violation>

<violation number="2" location="skills/ck/commands/shared.mjs:169">
P2: `gitSummary` incorrectly uses reflog index syntax (`HEAD@{count}`) where commit ancestry was intended, causing inaccurate or brittle diff-based file-change summaries.</violation>

<violation number="3" location="skills/ck/commands/shared.mjs:184">
P1: Project-path encoding is lossy and Unix-only, which can collide distinct projects and misroute native memory storage.</violation>

<violation number="4" location="skills/ck/commands/shared.mjs:248">
P3: Escape `|` characters in decision fields before writing markdown table cells; otherwise `CONTEXT.md` tables break when decision text includes pipes.</violation>

<violation number="5" location="skills/ck/commands/shared.mjs:285">
P3: Use `displayName` (with `name` as fallback) in user-facing render output. Right now saved projects always display the slug instead of the original project name.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


export function encodeProjectPath(absolutePath) {
// "/Users/sree/dev/app" -> "-Users-sree-dev-app"
return absolutePath.replace(/\//g, '-');
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Project-path encoding is lossy and Unix-only, which can collide distinct projects and misroute native memory storage.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/ck/commands/shared.mjs, line 184:

<comment>Project-path encoding is lossy and Unix-only, which can collide distinct projects and misroute native memory storage.</comment>

<file context>
@@ -0,0 +1,384 @@
+
+export function encodeProjectPath(absolutePath) {
+  // "/Users/sree/dev/app" -> "-Users-sree-dev-app"
+  return absolutePath.replace(/\//g, '-');
+}
+
</file context>
Fix with Cubic

const gitConfig = readFile('.git/config');
if (gitConfig) {
const repoMatch = gitConfig.match(/url\s*=\s*(.+)/);
if (repoMatch) output.repo = repoMatch[1].trim();
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Repo URL from .git/config is emitted verbatim; if the remote contains embedded credentials (https://token@host/...), they will be exposed in output and persisted. Sanitize userinfo before saving.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/ck/commands/init.mjs, line 97:

<comment>Repo URL from .git/config is emitted verbatim; if the remote contains embedded credentials (https://token@host/...), they will be exposed in output and persisted. Sanitize userinfo before saving.</comment>

<file context>
@@ -0,0 +1,143 @@
+const gitConfig = readFile('.git/config');
+if (gitConfig) {
+  const repoMatch = gitConfig.match(/url\s*=\s*(.+)/);
+  if (repoMatch) output.repo = repoMatch[1].trim();
+}
+
</file context>
Fix with Cubic

const projects = readProjects();

// Derive contextDir (lowercase, spaces→dashes, deduplicate)
let contextDir = name.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: contextDir can be empty after sanitization, but it is still used to save/register a project. An empty contextDir writes context.json/CONTEXT.md directly into the base contexts directory, causing collisions/overwrites and ambiguous lookups for projects whose names sanitize to empty.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/ck/commands/save.mjs, line 57:

<comment>`contextDir` can be empty after sanitization, but it is still used to save/register a project. An empty `contextDir` writes context.json/CONTEXT.md directly into the base contexts directory, causing collisions/overwrites and ambiguous lookups for projects whose names sanitize to empty.</comment>

<file context>
@@ -0,0 +1,210 @@
+  const projects = readProjects();
+
+  // Derive contextDir (lowercase, spaces→dashes, deduplicate)
+  let contextDir = name.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
+  let suffix = 2;
+  const existingDirs = Object.values(projects).map(p => p.contextDir);
</file context>
Fix with Cubic

if (!text) return [];
const rows = [];
for (const line of text.split('\n')) {
if (!line.startsWith('|') || line.match(/^[|\s-]+$/)) continue;
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Skip alignment separator rows that contain : in the decisions table parser; otherwise |:---|---:| lines can be migrated as bogus decision records.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/ck/commands/migrate.mjs, line 44:

<comment>Skip alignment separator rows that contain `:` in the decisions table parser; otherwise `|:---|---:|` lines can be migrated as bogus decision records.</comment>

<file context>
@@ -0,0 +1,198 @@
+  if (!text) return [];
+  const rows = [];
+  for (const line of text.split('\n')) {
+    if (!line.startsWith('|') || line.match(/^[|\s-]+$/)) continue;
+    const cols = line.split('|').map(c => c.trim()).filter((c, i) => i > 0 && i < 4);
+    if (cols.length >= 1 && !cols[0].startsWith('Decision') && !cols[0].startsWith('_')) {
</file context>
Fix with Cubic

lines.push(`| Decision | Why | Date |`);
lines.push(`|----------|-----|------|`);
if (allDecisions.length) {
allDecisions.forEach(d => lines.push(`| ${d.what} | ${d.why || ''} | ${d.date || ''} |`));
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Escape | characters in decision fields before writing markdown table cells; otherwise CONTEXT.md tables break when decision text includes pipes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/ck/commands/shared.mjs, line 248:

<comment>Escape `|` characters in decision fields before writing markdown table cells; otherwise `CONTEXT.md` tables break when decision text includes pipes.</comment>

<file context>
@@ -0,0 +1,384 @@
+  lines.push(`| Decision | Why | Date |`);
+  lines.push(`|----------|-----|------|`);
+  if (allDecisions.length) {
+    allDecisions.forEach(d => lines.push(`| ${d.what} | ${d.why || ''} | ${d.date || ''} |`));
+  } else {
+    lines.push(`| _(none yet)_ | | |`);
</file context>
Fix with Cubic

- Fix read-after-write in session-start.mjs: read prevSession BEFORE
  overwriting current-session.json so unsaved-session detection fires
- Fix shell injection in resume.mjs: replace execSync shell string with
  fs.existsSync for directory existence check
- Fix shell injection in shared.mjs gitSummary: replace nested \$(git ...)
  subshell with a separate runGit() call to get rev count
- Fix displayName never shown: render functions now use ctx.displayName
  ?? ctx.name so user-supplied names show instead of the slug
- Fix renderListTable: uses context.displayName ?? entry.name
- Fix init.mjs: use path.basename() instead of cwd.split('/').pop()
- Fix save.mjs confirmation: show original name, not contextDir slug

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ecc-tools
Copy link
Copy Markdown
Contributor

ecc-tools bot commented Mar 27, 2026

Analyzing 5000 commits...

@ecc-tools
Copy link
Copy Markdown
Contributor

ecc-tools bot commented Mar 27, 2026

Analysis Failed

Not Found - https://docs.github.com/rest/git/refs#get-a-reference

Troubleshooting
Cause Resolution
Large repository Analysis may timeout on repos with extensive history
API rate limits Wait 15 minutes before retrying
Network issues Queue timeout is 15 minutes; retry may succeed
Permissions Verify app has Contents: Read access

Retry: /ecc-tools analyze


Report Issue | ECC Tools

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="skills/ck/commands/resume.mjs">

<violation number="1" location="skills/ck/commands/resume.mjs:28">
P2: Directory check was weakened to existence-only, so `/ck:resume` may suggest `cd` into a non-directory path.</violation>
</file>

<file name="skills/ck/commands/shared.mjs">

<violation number="1" location="skills/ck/commands/shared.mjs:171">
P2: `gitSummary` can silently lose file-change statistics because it now relies on a single brittle `HEAD~revCount..HEAD` range with no fallback when the range is invalid.</violation>

<violation number="2" location="skills/ck/commands/shared.mjs:356">
P2: `/ck:list` now shows `displayName`, but name lookup for `/ck:resume` and `/ck:info` still matches only stored `name` (slug), so visible project names may not resolve.</violation>
</file>

<file name="skills/ck/hooks/session-start.mjs">

<violation number="1" location="skills/ck/hooks/session-start.mjs:90">
P2: Unsaved-session warning is not scoped to the previous session’s project, so it can fire incorrectly after switching projects.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


// Attempt to cd to the project path
if (projectPath && projectPath !== cwd) {
if (existsSync(projectPath)) {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Directory check was weakened to existence-only, so /ck:resume may suggest cd into a non-directory path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/ck/commands/resume.mjs, line 28:

<comment>Directory check was weakened to existence-only, so `/ck:resume` may suggest `cd` into a non-directory path.</comment>

<file context>
@@ -25,16 +25,11 @@ const { context, projectPath } = resolved;
-      console.log(`⚠  Path not found: ${projectPath}`);
-    }
-  } catch { /* non-fatal */ }
+  if (existsSync(projectPath)) {
+    console.log(`→ cd ${projectPath}`);
+  } else {
</file context>
Fix with Cubic

const statusLabel = icon === '●' ? '● Active' : icon === '◐' ? '◐ Warm' : '○ Stale';
const sessId = latest.id ? latest.id.slice(0, 8) : '—';
const summary = (latest.summary || '—').slice(0, 34);
const displayName = ((e.context?.displayName ?? e.name) + (isHere ? ' <-' : '')).slice(0, 18);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: /ck:list now shows displayName, but name lookup for /ck:resume and /ck:info still matches only stored name (slug), so visible project names may not resolve.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/ck/commands/shared.mjs, line 356:

<comment>`/ck:list` now shows `displayName`, but name lookup for `/ck:resume` and `/ck:info` still matches only stored `name` (slug), so visible project names may not resolve.</comment>

<file context>
@@ -352,7 +353,7 @@ export function renderListTable(entries, cwd, todayStr) {
     const sessId = latest.id ? latest.id.slice(0, 8) : '—';
     const summary = (latest.summary || '—').slice(0, 34);
-    const displayName = (e.name + (isHere ? ' <-' : '')).slice(0, 18);
+    const displayName = ((e.context?.displayName ?? e.name) + (isHere ? ' <-' : '')).slice(0, 18);
     return {
       num: String(i + 1),
</file context>
Fix with Cubic

// Count unique files changed: use a separate runGit call to avoid nested shell substitution
const countStr = runGit(`rev-list --count HEAD --since="${sinceDate}"`, projectPath);
const revCount = countStr ? parseInt(countStr, 10) : commits;
const diff = runGit(`diff --shortstat HEAD~${Math.min(revCount, 50)}..HEAD`, projectPath);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: gitSummary can silently lose file-change statistics because it now relies on a single brittle HEAD~revCount..HEAD range with no fallback when the range is invalid.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/ck/commands/shared.mjs, line 171:

<comment>`gitSummary` can silently lose file-change statistics because it now relies on a single brittle `HEAD~revCount..HEAD` range with no fallback when the range is invalid.</comment>

<file context>
@@ -165,9 +165,10 @@ export function gitSummary(projectPath, sinceDate) {
+  // Count unique files changed: use a separate runGit call to avoid nested shell substitution
+  const countStr = runGit(`rev-list --count HEAD --since="${sinceDate}"`, projectPath);
+  const revCount = countStr ? parseInt(countStr, 10) : commits;
+  const diff = runGit(`diff --shortstat HEAD~${Math.min(revCount, 50)}..HEAD`, projectPath);
 
   if (diff) {
</file context>
Fix with Cubic

const entry = projects[cwd];

// Read previous session BEFORE overwriting current-session.json
const prevSession = readJson(CURRENT_SESSION);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Unsaved-session warning is not scoped to the previous session’s project, so it can fire incorrectly after switching projects.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At skills/ck/hooks/session-start.mjs, line 90:

<comment>Unsaved-session warning is not scoped to the previous session’s project, so it can fire incorrectly after switching projects.</comment>

<file context>
@@ -86,6 +86,9 @@ function main() {
   const entry = projects[cwd];
 
+  // Read previous session BEFORE overwriting current-session.json
+  const prevSession = readJson(CURRENT_SESSION);
+
   // Write current-session.json
</file context>
Fix with Cubic

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
skills/ck/hooks/session-start.mjs (1)

29-45: Duplicated helpers could be imported from shared.mjs.

readJson, daysAgo/daysAgoLabel, and stalenessIcon are reimplemented here with minor format differences. Consider importing from shared.mjs for consistency and maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/hooks/session-start.mjs` around lines 29 - 45, This file
reimplements utility helpers that live in shared.mjs—replace the local
implementations of readJson, daysAgo (or daysAgoLabel), and stalenessIcon with
imports from shared.mjs: remove the local functions readJson, daysAgo, and
stalenessIcon, add imports for the canonical symbols (e.g., readJson,
daysAgoLabel/daysAgo, stalenessIcon) from shared.mjs, and update any call sites
in this module to use those imported names so formatting/semantics match the
shared utilities.
skills/ck/commands/shared.mjs (1)

183-186: Windows path separators are not encoded.

The function only replaces forward slashes. On Windows, backslashes (\) would remain, potentially causing filesystem issues or collisions.

 export function encodeProjectPath(absolutePath) {
   // "/Users/sree/dev/app" -> "-Users-sree-dev-app"
-  return absolutePath.replace(/\//g, '-');
+  return absolutePath.replace(/[\\/]/g, '-');
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/commands/shared.mjs` around lines 183 - 186, The encodeProjectPath
function only replaces forward slashes and misses Windows backslashes; update
encodeProjectPath to replace both '/' and '\' (e.g. using a regex character
class like [\/\\]) so Windows paths are normalized the same way as POSIX paths,
and ensure the replacement preserves the existing behavior of turning separators
into hyphens.
skills/ck/commands/resume.mjs (1)

26-33: Directory check is incomplete.

The previous shell injection was fixed, but existsSync returns true for files too. The original suggestion included a directory check.

-import { existsSync } from 'fs';
+import { existsSync, statSync } from 'fs';
 if (projectPath && projectPath !== cwd) {
-  if (existsSync(projectPath)) {
+  if (existsSync(projectPath) && statSync(projectPath).isDirectory()) {
     console.log(`→ cd ${projectPath}`);
   } else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/commands/resume.mjs` around lines 26 - 33, The directory existence
check using existsSync(projectPath) is insufficient because it returns true for
files too; update the conditional that checks projectPath (the block that
compares projectPath !== cwd and currently uses existsSync(projectPath)) to
verify the path is a directory e.g. use fs.statSync(projectPath).isDirectory()
(or fs.lstatSync with isDirectory()) combined with existsSync inside a try/catch
to avoid throwing on broken symlinks; keep the same console messages but only
print "→ cd ..." when the path exists and is a directory, otherwise print the "⚠
Path not found" or an appropriate "not a directory" warning.
skills/ck/commands/save.mjs (1)

130-165: today() called multiple times may produce inconsistent dates.

If the save spans midnight, session.date (line 132), the memory filename (line 165), and content (line 178) could have different dates. Consider capturing the date once.

+const todayStr = today();
+
 const session = {
   id: sessionId,
-  date: today(),
+  date: todayStr,
   summary: summary || 'Session saved',
   // ...
 };

Then use todayStr at lines 165 and 178.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/ck/commands/save.mjs` around lines 130 - 165, The code calls today()
multiple times causing potential inconsistencies across session.date, the memory
filename (memFile) and memory content; capture the date once into a variable
(e.g., todayStr) at the start of save logic, use that variable for session.date
instead of today(), use it when constructing memFile (resolve(memDir,
`ck_${todayStr}_${sessionId.slice(0,8)}.md`)) and anywhere else the date is
needed (e.g., memory content or projects[cwd].lastUpdated) so all saved
artifacts share the same date; update references in the session creation,
memFile construction, and any subsequent writes (functions/variables: session,
sessionId, memFile, nativeMemoryDir, projects[cwd].lastUpdated, writeProjects)
to use todayStr.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@skills/ck/commands/resume.mjs`:
- Around line 26-33: The directory existence check using existsSync(projectPath)
is insufficient because it returns true for files too; update the conditional
that checks projectPath (the block that compares projectPath !== cwd and
currently uses existsSync(projectPath)) to verify the path is a directory e.g.
use fs.statSync(projectPath).isDirectory() (or fs.lstatSync with isDirectory())
combined with existsSync inside a try/catch to avoid throwing on broken
symlinks; keep the same console messages but only print "→ cd ..." when the path
exists and is a directory, otherwise print the "⚠ Path not found" or an
appropriate "not a directory" warning.

In `@skills/ck/commands/save.mjs`:
- Around line 130-165: The code calls today() multiple times causing potential
inconsistencies across session.date, the memory filename (memFile) and memory
content; capture the date once into a variable (e.g., todayStr) at the start of
save logic, use that variable for session.date instead of today(), use it when
constructing memFile (resolve(memDir,
`ck_${todayStr}_${sessionId.slice(0,8)}.md`)) and anywhere else the date is
needed (e.g., memory content or projects[cwd].lastUpdated) so all saved
artifacts share the same date; update references in the session creation,
memFile construction, and any subsequent writes (functions/variables: session,
sessionId, memFile, nativeMemoryDir, projects[cwd].lastUpdated, writeProjects)
to use todayStr.

In `@skills/ck/commands/shared.mjs`:
- Around line 183-186: The encodeProjectPath function only replaces forward
slashes and misses Windows backslashes; update encodeProjectPath to replace both
'/' and '\' (e.g. using a regex character class like [\/\\]) so Windows paths
are normalized the same way as POSIX paths, and ensure the replacement preserves
the existing behavior of turning separators into hyphens.

In `@skills/ck/hooks/session-start.mjs`:
- Around line 29-45: This file reimplements utility helpers that live in
shared.mjs—replace the local implementations of readJson, daysAgo (or
daysAgoLabel), and stalenessIcon with imports from shared.mjs: remove the local
functions readJson, daysAgo, and stalenessIcon, add imports for the canonical
symbols (e.g., readJson, daysAgoLabel/daysAgo, stalenessIcon) from shared.mjs,
and update any call sites in this module to use those imported names so
formatting/semantics match the shared utilities.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5c3418f0-cbb1-4881-b745-c56a9d4f2c80

📥 Commits

Reviewing files that changed from the base of the PR and between 1e226ba and 17f6f95.

📒 Files selected for processing (5)
  • skills/ck/commands/init.mjs
  • skills/ck/commands/resume.mjs
  • skills/ck/commands/save.mjs
  • skills/ck/commands/shared.mjs
  • skills/ck/hooks/session-start.mjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • skills/ck/commands/init.mjs

@ecc-tools
Copy link
Copy Markdown
Contributor

ecc-tools bot commented Mar 29, 2026

Analyzing 5000 commits...

@ecc-tools
Copy link
Copy Markdown
Contributor

ecc-tools bot commented Mar 29, 2026

Analysis Failed

Not Found - https://docs.github.com/rest/git/refs#get-a-reference

Troubleshooting
Cause Resolution
Large repository Analysis may timeout on repos with extensive history
API rate limits Wait 15 minutes before retrying
Network issues Queue timeout is 15 minutes; retry may succeed
Permissions Verify app has Contents: Read access

Retry: /ecc-tools analyze


Report Issue | ECC Tools

@affaan-m affaan-m merged commit eeeea50 into affaan-m:main Mar 29, 2026
2 of 3 checks passed
@sreedhargs89 sreedhargs89 deleted the feat/skill-context-keeper branch March 29, 2026 04:55
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