Skip to content
Merged
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ All notable changes to cc-settings are documented here.

> **Versioning** — cc-settings uses a single version number matching the installer (`src/setup.ts` `VERSION` constant, written to `~/.claude/.cc-settings-version` sentinel). Historical entries below 10.0 predate this unification; the jump from v8.x to v10.x in April 2026 realigned the product version with the installer version that was already ahead.

## [Unreleased]

### Changed

- Codex bridge hardening: `--force` escape bypasses a sticky rate-limited/no-access verdict (which Codex emits even on auth mismatch); a fresh `available` verdict skips the per-call `codex login status` probe for 60s; timeouts now report partial output + a split/raise hint instead of an opaque exit code; `exec` appends `git status`/`diff --stat` so the changed files are always surfaced; `sanitizeOutput` strips ANSI and redacts secrets (`sk-`/`Bearer`/`Authorization`/`*_API_KEY|TOKEN|SECRET=`) on all returned output.
- Hook tamper-defense: the three divergent "managed `~/.claude/src` hook command" classifiers (settings-merge, light-profile, audit-hooks) are unified into `src/lib/hook-command.ts`; the trusted regex is tightened to `(scripts|hooks)` only (drops `lib/`); hook-block traversal goes through a `HooksBlock`-schema-driven `iterCommandHooks`. `readFingerprint`/`readSrcManifest` now validate through zod, strip control chars from `installedAt`, and reject manifest keys with `..`/absolute paths.
- Installer: `buildInstallPlan` is the single source of truth for install/prune footprint; `main()` split into `runMigrateOnly`/`runFullInstall`; the in-installer skill-prune loop removed. `managed-skills.ts` split into `ACTIVE_SKILLS` (completed — 7 missing current skills added: `codex`, `freeze`, `plan-ceo-review`, `proof-of-work`, `retro`, `review-batch`, `strategist`) + `TOMBSTONE_SKILLS`; `lint:skills` now asserts `ACTIVE_SKILLS` matches `skills/` on disk.
- Settings merger is now pure: MCP-server preservation moved out of `settings-merge.ts` into `mcp.ts` `resolveMcpServers` (behavior unchanged).

### Internal

- `claude-audit.ts` split into `analyzeCommands` (model) + `renderAudit` (render); shared frontmatter-lint core extracted from `lint-skills`/`lint-knowledge`; `writeState` is now atomic (tmp+rename); `tool-cadence` `CounterState` rehydration goes through `normalizeCounterState`.

## [11.27.1] — 2026-06-19

### Fix — installer merger now deep-merges object config defaults
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE-FULL.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,4 @@ Full threat model + remediation: see `SECURITY.md`.

### Skill library soft cap — 40

Anthropic's Skills guide flags 20–50 skills as the point where the Skill selector starts struggling to read every description per turn. We sit at 34 cc-settings skills (Tier P1 cleanup May 2026: retired `audit`, `lenis`; merged `create-handoff`+`resume-handoff` → `handoff`, `discovery`+`prd` → `plan-feature`, `ask`+`premortem`+`compare-approaches` → `oracle`, `tdd` folded into `test`, `cc-sync`+`cc-update` → `cc`; folded `long-task` into `orchestrate`; demoted `write-a-skill` to `bun run new-skill` CLI; `nuclear-review` ported from Cursor team-kit May 2026; `share-learning` revived May 2026; `proof-of-work` + `review-batch` added May 2026 from the Orchestration Tax; `freeze` edit-scope lock ported from gstack June 2026). **Adding a new skill past 40 requires removing one** — re-evaluate `skills/` for consolidation candidates first. Validate the library with `bun run lint:skills`, which enforces the spec (kebab-case folders, frontmatter contract, no angle brackets, …) and surfaces the cap as a warning when crossed. Drift hides easily; let the linter catch it.
Anthropic's Skills guide flags 20–50 skills as the point where the Skill selector starts struggling to read every description per turn. We sit at 35 cc-settings skills (Tier P1 cleanup May 2026: retired `audit`, `lenis`; merged `create-handoff`+`resume-handoff` → `handoff`, `discovery`+`prd` → `plan-feature`, `ask`+`premortem`+`compare-approaches` → `oracle`, `tdd` folded into `test`, `cc-sync`+`cc-update` → `cc`; folded `long-task` into `orchestrate`; demoted `write-a-skill` to `bun run new-skill` CLI; `nuclear-review` ported from Cursor team-kit May 2026; `share-learning` revived May 2026; `proof-of-work` + `review-batch` added May 2026 from the Orchestration Tax; `freeze` edit-scope lock ported from gstack June 2026). **Adding a new skill past 40 requires removing one** — re-evaluate `skills/` for consolidation candidates first. Validate the library with `bun run lint:skills`, which enforces the spec (kebab-case folders, frontmatter contract, no angle brackets, …) and surfaces the cap as a warning when crossed. Drift hides easily; let the linter catch it.
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ for standards.

## About This Repo

**TypeScript on Bun** (migrated from bash in April 2026; see git log for history). Runtime: `bun >=1.1.30`. Skills: 34 cc-settings skills (Tier P1 cleanup May 2026: retired 3, merged 7 pairs into 4, demoted 1 to CLI; `nuclear-review` ported from Cursor team-kit May 2026; share-learning revived May 2026; `freeze` edit-scope lock ported from gstack June 2026).
**TypeScript on Bun** (migrated from bash in April 2026; see git log for history). Runtime: `bun >=1.1.30`. Skills: 35 cc-settings skills (Tier P1 cleanup May 2026: retired 3, merged 7 pairs into 4, demoted 1 to CLI; `nuclear-review` ported from Cursor team-kit May 2026; share-learning revived May 2026; `freeze` edit-scope lock ported from gstack June 2026).
Deps: `zod`, `@inquirer/confirm`, `yaml`. Dev: `@biomejs/biome`, `typescript`,
`@types/bun`.

Expand Down
2 changes: 1 addition & 1 deletion MANUAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ You'll be asked to pick a starter — **satus** (Next.js, content sites) or **no
| *"review my changes"* | `/review` — checks against TypeScript / React / a11y / performance rules |
| *"audit my recent activity"* | `bun run claude-audit` — analyzes Bash command logs |

**4. Don't memorize skills.** Just talk normally. The `Skill` tool auto-matches the right one. To see all 34 cc-settings skills, scroll to [All Skills](#all-skills) below. (Native Claude Code skills like `/loop`, `/schedule`, `/code-review`, `/review`, `/init`, `/security-review`, and any plugins like `sanity:*` or `vercel:*` load in addition — your session typically sees 60–80 skills total.)
**4. Don't memorize skills.** Just talk normally. The `Skill` tool auto-matches the right one. To see all 35 cc-settings skills, scroll to [All Skills](#all-skills) below. (Native Claude Code skills like `/loop`, `/schedule`, `/code-review`, `/review`, `/init`, `/security-review`, and any plugins like `sanity:*` or `vercel:*` load in addition — your session typically sees 60–80 skills total.)

**5. When something is unclear**, ask Claude directly: *"what skill handles X?"* or *"what just changed in cc-settings?"*. The setup is self-describing.

Expand Down
53 changes: 36 additions & 17 deletions src/hooks/tool-cadence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@ const FILE_EDIT_TOOLS: Record<string, string> = {
interface CounterState {
count: number;
lastTool: string;
/** Debounce timestamp; genuinely absent until the first fire. */
firedAt?: number;
files?: string[];
nudged?: boolean;
countAtNudge?: number;
filesAtNudge?: number;
escalated?: boolean;
// The rest are always present once normalizeCounterState() has run.
files: string[];
nudged: boolean;
countAtNudge: number;
filesAtNudge: number;
escalated: boolean;
}

type Payload = {
Expand Down Expand Up @@ -100,24 +102,41 @@ async function currentHead(cwd: string): Promise<string | undefined> {
return out || undefined;
}

/** Validate and default every field of CounterState from an unknown raw value.
* Replaces the previous inline re-hydration + `as number | undefined` cast. */
function normalizeCounterState(raw: unknown): CounterState {
if (raw === null || typeof raw !== "object") {
return {
count: 0,
lastTool: "",
files: [],
nudged: false,
countAtNudge: 0,
filesAtNudge: 0,
escalated: false,
};
}
const r = raw as Record<string, unknown>;
return {
count: typeof r.count === "number" ? r.count : 0,
lastTool: typeof r.lastTool === "string" ? r.lastTool : "",
firedAt: typeof r.firedAt === "number" ? r.firedAt : undefined,
files: Array.isArray(r.files) ? (r.files as string[]) : [],
nudged: typeof r.nudged === "boolean" ? r.nudged : false,
countAtNudge: typeof r.countAtNudge === "number" ? r.countAtNudge : 0,
filesAtNudge: typeof r.filesAtNudge === "number" ? r.filesAtNudge : 0,
escalated: typeof r.escalated === "boolean" ? r.escalated : false,
};
}

// --- Branch 1: consecutive non-Agent call counter --------------------------

async function parallelmaxBranch(
toolName: string,
toolInput: Payload["tool_input"],
): Promise<void> {
// Defensive defaults for old-shape state files.
const raw = await readState<CounterState>(COUNTER_STATE, { count: 0, lastTool: "" });
const state = {
count: raw.count ?? 0,
lastTool: raw.lastTool ?? "",
firedAt: raw.firedAt as number | undefined,
files: raw.files ?? ([] as string[]),
nudged: raw.nudged ?? false,
countAtNudge: raw.countAtNudge ?? 0,
filesAtNudge: raw.filesAtNudge ?? 0,
escalated: raw.escalated ?? false,
};
const raw = await readState<unknown>(COUNTER_STATE, null);
const state = normalizeCounterState(raw);

if (toolName === "Agent") {
// Reset the whole streak on delegation; preserve firedAt for debounce.
Expand Down
Loading
Loading