Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 234 additions & 0 deletions docs/audit-cafekit-vs-claude-code-2026-07.md

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions docs/installer-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ bin/lib/
4. **Snapshot** — back up platform folders + root `CLAUDE.md`/`.gitignore` (skipped in dry-run).
5. **Per platform** — read ownership baseline, start a tracker, then: copy payload →
claude-runtime *or* opencode-runtime → write metadata + manifest.
6. **Root config** — ensure `.gitignore` patterns (incl. `.cafekit-backup/`, `.cafekit.lock`).
6. **Root config** — ensure `.gitignore` patterns (incl. `.claude/`, `.opencode/`,
`.cafekit-backup/`, `.cafekit.lock`).
7. **Post-install** — OpenCode model, Gemini, addressing (re-records CLAUDE.md baseline).
8. **Skills setup** — opt-in: Python venv, pip deps, skill npm, Chromium; detect system tools.
9. **rtk setup** — opt-in: rtk binary + hook registration for token-saving on Bash commands.
Expand Down Expand Up @@ -75,9 +76,16 @@ A file written earlier in the same run (e.g. spec templates copied by the `specs
tree, then revisited by the template-sync loop) is treated as pristine via the
tracker's in-run record, avoiding false "user-created" classification.

The ownership manifest should be **committed** in the consumer project so a teammate's
clone shares the baseline; otherwise their first install would treat committed
`.claude/` files as user-created and never update them.
By default the installer gitignores the runtime folders (`.claude/`, `.opencode/`)
at project root — reinstall with `npx @haposoft/cafekit` on each machine. The
ownership manifest therefore lives only on disk as a local re-install baseline.
If a team deliberately force-adds and commits the runtime folder, they should
also commit the ownership manifest so teammates share the baseline; otherwise
their first install would treat those files as user-created and never update them.

A second layer lives inside the runtime: `.claude/.gitignore` /
`.opencode/.gitignore` (from `src/claude/gitignore`) ignore secrets, skill
deps, session state, and logs so force-adds stay safe.

## Safety properties

Expand Down
17 changes: 17 additions & 0 deletions docs/project-changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ All notable changes to CafeKit are documented here, following
- **`session.cjs` env escaping**: `writeEnv` now escapes `\ $ \` "` (was `"` only), so an attacker-influenced value flowing into `CLAUDE_ENV_FILE` (e.g. a git branch named `` evil$HOME-x`pwd` ``) can no longer expand or execute when the env file is sourced. Matches the canonical `escapeShellValue` semantics in `lib/config.cjs`.
- **`privacy-block.cjs` symlink bypass**: the sensitive-file gate now resolves symlink targets via `fs.realpathSync` and checks both the requested name and its real target, closing the bypass where a harmless-looking symlink (e.g. `notes.txt` → `.env`) slipped past the basename check. Exemptions (`.env.example`) still win on either name; fail-open when the path cannot be resolved.

## [0.13.4] - 2026-07-15

### Added
- **`hapo:delegate`**: offload a scoped coding task to Codex or Grok CLI from Claude Code (brief file, sandbox, monitor/resume, independent verify). Wired into skill routing + migration manifest.
- Installer dual-layer gitignore: root ignores `.claude/` and `.opencode/`; in-folder `.gitignore` covers secrets, skill deps, session state, and logs.

### Docs
- CafeKit vs Claude Code audit (2026-07); installer architecture + README gitignore policy.

## [0.13.3] - 2026-06-22

### Added
- Validator placeholder gate for unfilled scaffold stubs (hard-fail on leftover `{{...}}` in task files).

### Changed
- Specs self-tests / Step 7 wording realigned to process-discipline (not token-cut) framing.

## [0.13.2] - 2026-06-21

### Added — Enforce scaffold on task creation
Expand Down
26 changes: 25 additions & 1 deletion packages/spec/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **`spec-state.cjs` tollgate reminder slimmed**: state-change path is now a compact English block (≤7 lines: feature, phase, task counts, next unblocked task, sync/validate rule, Stop-gate note). Removed the red ALL-CAPS / bilingual MANDATORY wall (`URGENT`, `BẮT BUỘC`, `CẤM`). One-line unchanged-fingerprint path is unchanged. Completion enforcement moved to `spec-gate.cjs` on Stop.
- Installer obsolete mechanism removes directories recursively and prunes ownership-manifest entries by prefix (`tracker.prunePrefix`), enabling skill-level cleanup on upgrade.

## [0.13.4] - 2026-07-15

### Added
- **`hapo:delegate` skill**: dispatch a scoped implementation task from Claude Code to an external agent CLI (**Codex** or **Grok**). Covers file-based task briefs, non-interactive dispatch, minimum permissions, background monitoring/resume, and **independent verification** of the returned work (agent claims are not evidence).
- Codex / Grok reference guides under `skills/delegate/references/` with verified CLI flags and pitfalls.
- Workflow routing: assign/offload intents map to `/hapo:delegate` in `skill-workflow-routing.md`.
- `migration-manifest.json`: `delegate` added to required skills so the installer ships it.

### Changed — Installer gitignore (runtime out of git by default)
- **Root `.gitignore`**: `ensureGitignore` now also adds `.claude/` and `.opencode/` so the local CafeKit payload is not committed. Reinstall with `npx @haposoft/cafekit` on each machine. Existing equivalent forms (`.claude` without trailing slash) are treated as already present.
- **In-folder `.claude/.gitignore` / `.opencode/.gitignore`**: expanded template (`src/claude/gitignore`) with layered ignores for secrets, skill venvs/`node_modules`, session state, hook/plugin logs, and update cache — defense in depth for force-adds and partial un-ignores.

### Docs
- Audit note: `docs/audit-cafekit-vs-claude-code-2026-07.md` (full package inventory vs Claude Code practices as of v0.13.2).
- Installer architecture + package README document the dual-layer gitignore policy.

## [0.13.3] - 2026-06-22

### Added
- **Validator placeholder gate (`validate-spec-output.cjs`)**: a task file that still carries an unfilled `{{...}}` scaffold placeholder now hard-fails (previously a prompt-only DoCT rule); a leftover `.../` path fragment warns. Fill-side complement to the scaffold-guard hook — it proves every scaffolded stub was actually completed, closing the "stub created but not filled" gap.

### Changed
- Self-tests realigned to the Specs-v2 `SKILL.md` wording (`validate guardrail`, `init-is-never-a-stop-point`): the invariants are unchanged, only the asserted phrasing.
- `skills/specs/SKILL.md` Step 7: dropped the misleading "scaffold cuts output tokens" claim — scaffold enforces process discipline, not a token cut.

## [0.13.2] - 2026-06-21

### Added — Enforce scaffold on task creation
- **`task-scaffold-guard.cjs` (PreToolUse hook)**: hard-blocks any `Write` whose path matches `specs/<feature>/tasks/task-*.md`, so task files can only be created via `spec-scaffold.cjs` and then `Edit`-filled. Closes the dodge where the model hand-`Write`s task files and bypasses the (previously opt-in) scaffold step. Narrow scope: only the `Write` tool on a task-file path is blocked; `Edit`/`MultiEdit` and `Write` to any other file are untouched, and the scaffold script (writing via Node fs through Bash) is never blocked.
- **Three safety valves**: fail-open when `spec-scaffold.cjs` is absent (a hook shipped without its script must not deadlock task creation); actionable block message carrying the exact scaffold command; escape hatch via `"spec": { "scaffold_guard": false }` in `.claude/runtime.json`.
- **Validator placeholder gate (`validate-spec-output.cjs`)**: a task file that still carries an unfilled `{{...}}` scaffold placeholder now hard-fails (previously a prompt-only DoCT rule). A leftover `.../` path fragment warns. This is the fill-side complement to the guard — the hook forces task files through the scaffold; this proves the resulting stubs were actually completed, closing the "stub created but not filled" gap.

### Changed
- `settings/settings.json`: registered the guard under a dedicated `Write` matcher in `PreToolUse` (separate entry so the settings-merge dedupe does not swallow it).
Expand Down
22 changes: 21 additions & 1 deletion packages/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> Claude Code-first spec-driven workflow and runtime bundle for AI coding assistants.

[![Version](https://img.shields.io/badge/version-0.9.3-blue.svg)](https://github.com/haposoft/cafekit)
[![Version](https://img.shields.io/badge/version-0.13.4-blue.svg)](https://github.com/haposoft/cafekit)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Claude%20Code](https://img.shields.io/badge/Claude%20Code-Primary-orange.svg)](https://claude.ai/code)

Expand Down Expand Up @@ -46,6 +46,26 @@ Requirements:
- Node.js 18+
- Claude Code project with `.claude/`, OpenCode project with `.opencode/` or `opencode.json`, or choose a runtime when prompted

## Git ignore policy

On install, CafeKit updates the **project-root** `.gitignore` with:

```text
# CafeKit / Ecosystem
specs/_shared/
plans/
!plans/templates/
.cafekit-backup/
.cafekit.lock
.claude/
.opencode/
```

Runtime folders are local — reinstall with `npx @haposoft/cafekit` rather than
committing them. Inside the runtime, CafeKit also installs `.claude/.gitignore`
(or `.opencode/.gitignore`) so secrets, skill dependencies, session state, and
hook logs stay out of git even if someone force-adds the folder.

## What Gets Installed

Claude Code targets:
Expand Down
29 changes: 24 additions & 5 deletions packages/spec/bin/phases/root-config.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
/**
* Phase: root project configuration (.gitignore).
*
* Ensures the root .gitignore carries CafeKit-managed patterns, including the
* new install backup dir and lock file so they never get committed. Honors dry-run.
* Ensures the root .gitignore carries CafeKit-managed patterns:
* - workflow dirs (plans, shared research)
* - install safety artifacts (backup, lock)
* - runtime folders (.claude/, .opencode/) — reinstall via
* `npx @haposoft/cafekit`; do not commit the local payload
*
* Layered with the in-folder `.claude/.gitignore` /
* `.opencode/.gitignore` templates (secrets, skill deps, session
* state) so force-adds and partial un-ignores stay safe.
*
* Honors dry-run.
*/

const fs = require('fs');
const path = require('path');

/** True if `lines` already carries `pattern` with or without a trailing slash. */
function hasPattern(lines, pattern) {
const bare = pattern.replace(/\/$/, '');
const withSlash = bare + '/';
return lines.includes(pattern) || lines.includes(bare) || lines.includes(withSlash);
}

function ensureGitignore(ctx) {
const gitignorePath = path.join(process.cwd(), '.gitignore');
const header = '# CafeKit / Ecosystem';
Expand All @@ -16,7 +32,10 @@ function ensureGitignore(ctx) {
'plans/',
'!plans/templates/',
'.cafekit-backup/',
'.cafekit.lock'
'.cafekit.lock',
// Local runtime payload — reinstall with npx; keep out of git
'.claude/',
'.opencode/'
];
const prefix = ctx.dryRun ? '[dry-run] ' : '';

Expand All @@ -30,7 +49,7 @@ function ensureGitignore(ctx) {

const content = fs.readFileSync(gitignorePath, 'utf8');
const lines = content.split('\n').map((l) => l.trim());
const missing = patterns.filter((p) => !lines.includes(p));
const missing = patterns.filter((p) => !hasPattern(lines, p));

if (missing.length > 0) {
let newContent = content;
Expand All @@ -46,4 +65,4 @@ function ensureGitignore(ctx) {
}
}

module.exports = { ensureGitignore };
module.exports = { ensureGitignore, hasPattern };
15 changes: 14 additions & 1 deletion packages/spec/scripts/run-skill-self-tests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,20 @@ async function runStaticSemanticTests() {
assert: (content) =>
content.includes("session-state/") &&
content.includes("hooks/.logs/") &&
content.includes("skills/**/node_modules/"),
content.includes("plugins/.logs/") &&
content.includes("skills/**/node_modules/") &&
content.includes("skills/**/.venv/") &&
content.includes(".cafekit-update-cache.json"),
},
{
label: "installer root gitignore ignores runtime folders",
file: "bin/phases/root-config.js",
assert: (content) =>
content.includes("'.claude/'") &&
content.includes("'.opencode/'") &&
content.includes("'.cafekit-backup/'") &&
content.includes("'.cafekit.lock'") &&
content.includes("function hasPattern"),
},
{
label: "hapo:specs handoff block points to hapo:develop",
Expand Down
15 changes: 11 additions & 4 deletions packages/spec/src/claude/gitignore
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
# Environment and Sandbox credentials within .claude
# CafeKit local runtime (.claude/ or .opencode/)
# Also ignored at project root by the installer. This file protects
# force-adds (`git add -f`) and partial un-ignores of the runtime folder.

# Secrets and local credentials
.env
.env.*
!.env.example

# Virtual environments for python scripts (skills)
# Skill dependency installs (Python venv + npm)
skills/.venv/
skills/**/.venv/
skills/**/node_modules/
.venv/
venv/
skills/**/node_modules/
__pycache__/
**/__pycache__/
*.pyc

# System generated state, caches, and logs
# Generated session state, caches, and logs
session-state/
hooks/.logs/
plugins/.logs/
agent-memory/
settings.bak.json
.mcp.json
Expand Down
1 change: 1 addition & 0 deletions packages/spec/src/claude/migration-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"chrome-devtools",
"code-review",
"debug",
"delegate",
"develop",
"devops",
"docs",
Expand Down
8 changes: 8 additions & 0 deletions packages/spec/src/claude/rules/skill-workflow-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ Do not turn reconstructed docs directly into implementation. Human review must a

Use `/hapo:research` for external knowledge. Use `/hapo:inspect` for local source truth.

## Delegation Workflow

| User intent | Suggested skill |
|---|---|
| assign/offload an implementation task to an external agent CLI (codex, grok) | `/hapo:delegate` |

Claude Code remains the orchestrator: it writes the task brief, dispatches, monitors, and independently verifies the returned work.

## Output Artifact Workflow

| User intent | Suggested skill |
Expand Down
Loading