create-anpunkit 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/bin/cli.js +2 -2
  2. package/package.json +2 -3
  3. package/template/.claude/agents/debugger.md +1 -1
  4. package/template/.claude/agents/e2e-runner.md +13 -3
  5. package/template/.claude/agents/implementer.md +1 -1
  6. package/template/.claude/agents/infra-provisioner.md +53 -59
  7. package/template/.claude/agents/planner.md +35 -29
  8. package/template/.claude/agents/researcher.md +16 -1
  9. package/template/.claude/agents/spec-author.md +9 -1
  10. package/template/.claude/agents/synthesizer.md +1 -1
  11. package/template/.claude/agents/test-author.md +9 -1
  12. package/template/.claude/anpunkit-manifest.json +52 -113
  13. package/template/.claude/commands/infra.md +7 -3
  14. package/template/.claude/commands/log-decision.md +1 -1
  15. package/template/.claude/commands/log-issue.md +1 -1
  16. package/template/.claude/commands/overview.md +37 -12
  17. package/template/.claude/commands/phase.md +50 -25
  18. package/template/.claude/commands/quick.md +1 -1
  19. package/template/.claude/commands/replan.md +1 -1
  20. package/template/.claude/commands/store-wisdom.md +1 -1
  21. package/template/.claude/commands/synthesize.md +1 -1
  22. package/template/.claude/commands/unstuck.md +1 -1
  23. package/template/.claude/hooks/session-start.sh +5 -11
  24. package/template/.claude/ref/compression.md +56 -0
  25. package/template/.claude/skills/karpathy-guidelines/SKILL.md +1 -1
  26. package/template/AGENTS.md +115 -84
  27. package/template/CLAUDE.md +4 -9
  28. package/template/README.md +91 -122
  29. package/template/commands.src/infra.md +7 -3
  30. package/template/commands.src/log-decision.md +1 -1
  31. package/template/commands.src/log-issue.md +1 -1
  32. package/template/commands.src/overview.md +37 -12
  33. package/template/commands.src/phase.md +50 -25
  34. package/template/commands.src/quick.md +1 -1
  35. package/template/commands.src/replan.md +1 -1
  36. package/template/commands.src/store-wisdom.md +1 -1
  37. package/template/commands.src/synthesize.md +1 -1
  38. package/template/commands.src/unstuck.md +1 -1
  39. package/template/docs/DESIGN_LOG.md +222 -8
  40. package/template/knowledge/azure.md +161 -0
  41. package/template/knowledge/webapp.md +265 -0
  42. package/template/setup.sh +30 -111
  43. package/template/.claude/hooks/cursor-session-start.sh +0 -17
  44. package/template/.claude/skills/caveman/SKILL.md +0 -39
  45. package/template/.cursor/commands/infra.md +0 -82
  46. package/template/.cursor/commands/log-decision.md +0 -29
  47. package/template/.cursor/commands/log-issue.md +0 -23
  48. package/template/.cursor/commands/overview.md +0 -162
  49. package/template/.cursor/commands/phase.md +0 -296
  50. package/template/.cursor/commands/quick.md +0 -25
  51. package/template/.cursor/commands/replan.md +0 -73
  52. package/template/.cursor/commands/store-wisdom.md +0 -191
  53. package/template/.cursor/commands/synthesize.md +0 -22
  54. package/template/.cursor/commands/unstuck.md +0 -36
  55. package/template/.cursor/hooks.json +0 -14
  56. package/template/.cursor/rules/anpunkit.md +0 -11
  57. package/template/anpunkit.png +0 -0
  58. package/template/docker-compose.test.yml +0 -34
  59. package/template/e2e/global-setup.ts +0 -57
  60. package/template/index.html +0 -339
  61. package/template/playwright.config.ts +0 -28
  62. package/template/scripts/auth-setup.sh +0 -51
  63. package/template/scripts/e2e-stack.sh +0 -65
@@ -0,0 +1,265 @@
1
+ # knowledge/webapp.md — matured web-app practice (preinstalled)
2
+
3
+ > **What this file is.** Use-case knowledge for browser-facing web apps,
4
+ > consulted by `researcher` (STEP 0.5) when `knowledge/webapp.md` appears in
5
+ > the `knowledge_docs:` list of `docs/OVERVIEW.md`. Selection is deterministic
6
+ > — declared at `/overview`, never inferred (hard rule 10).
7
+ >
8
+ > **Templates below are materialized per-project** — copied to the stated path
9
+ > the first time the phase work needs them. Once materialized they are
10
+ > user-owned project files: adjust freely, the kit never overwrites them.
11
+
12
+ ## Browser boundary practice (E2E_KIND: browser)
13
+
14
+ - `ui` spec cases carry a `fixtures/<case-id>-ui.json` descriptor with a
15
+ closed, kit-versioned assert vocabulary: `visible`, `text-equals`,
16
+ `enabled`, `count`. `e2e-runner` EMITS Playwright specs from descriptors —
17
+ it never authors blind from prose.
18
+ - Each emitted spec block carries a `// spec: <case-id>` citation
19
+ (`spec-conformance.sh` checks it).
20
+ - EVIDENCE (hard rule 13): at each UI-existence assertion, capture a
21
+ screenshot regardless of pass/fail to
22
+ `docs/evidence/e2e-phase-<n>/<case-id>-<element-slug>.png` — one shot per
23
+ asserted element, on green as well as red.
24
+ - `ui` cases have NO mock mirror — the browser IS the boundary.
25
+
26
+ ## E2E stack ritual
27
+
28
+ Two target modes, recorded in `docs/INFRA.md`:
29
+
30
+ - **azure-deployed** (`E2E_STACK_EXTERNAL=1` in `.env.test`): tests hit the
31
+ deployed app; `e2e-stack.sh up` only health-checks reachability.
32
+ - **local-docker**: `e2e-stack.sh up` builds + starts the compose stack and
33
+ polls health.
34
+
35
+ Sequence, always: `bash scripts/e2e-stack.sh up` -> `npx playwright test` ->
36
+ `bash scripts/e2e-stack.sh down`. Stack didn't come up -> classify
37
+ **STACK NOT READY** (not a bug; no debug budget).
38
+
39
+ ## Web deploy ritual (DEPLOY_KIND: cloud-deploy)
40
+
41
+ - Deploy task lives in the final phase (DEPLOY_KIND completion, hard rules).
42
+ - After deploy: record the deployed base URL in `docs/INFRA.md`
43
+ (`## Deployed base URL`) and confirm `docs/ENDPOINTS.md` entries resolve
44
+ against it; run a `/health` (or equivalent) check before calling it done.
45
+
46
+ ## .gitignore lines a materialized stack needs
47
+
48
+ ```
49
+ e2e/.auth/
50
+ playwright-report/
51
+ test-results/
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Templates
57
+
58
+ ### `playwright.config.ts` — materialize to project root
59
+
60
+ ```ts
61
+ // playwright.config.ts
62
+ import { defineConfig, devices } from '@playwright/test';
63
+
64
+ export default defineConfig({
65
+ testDir: './e2e',
66
+ globalSetup: './e2e/global-setup.ts',
67
+ fullyParallel: false,
68
+ forbidOnly: !!process.env.CI,
69
+ retries: process.env.CI ? 1 : 0,
70
+ workers: 1,
71
+ reporter: [['list'], ['html', { open: 'never' }]],
72
+ use: {
73
+ baseURL: process.env.E2E_BASE_URL || 'http://localhost:8080',
74
+ storageState: 'e2e/.auth/state.json',
75
+ trace: 'retain-on-failure',
76
+ screenshot: 'only-on-failure',
77
+ actionTimeout: 15_000,
78
+ },
79
+ projects: [
80
+ { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
81
+ ],
82
+ webServer: process.env.E2E_STACK_EXTERNAL ? undefined : {
83
+ command: 'bash scripts/e2e-stack.sh up',
84
+ url: process.env.E2E_BASE_URL || 'http://localhost:8080',
85
+ timeout: 180_000,
86
+ reuseExistingServer: !process.env.CI,
87
+ },
88
+ });
89
+ ```
90
+
91
+ ### `e2e/global-setup.ts` — materialize to `e2e/`
92
+
93
+ Entra ID ROPC token injection (Azure/MSAL apps — see `knowledge/azure.md` for
94
+ the auth practice). Known gap: the localStorage keys (`e2e.access_token`,
95
+ `e2e.id_token`) are a stub convention — align them with your app's real MSAL
96
+ cache keys when materializing.
97
+
98
+ ```ts
99
+ // e2e/global-setup.ts
100
+ import { chromium } from '@playwright/test';
101
+ import * as fs from 'fs';
102
+ import * as path from 'path';
103
+
104
+ const AUTH_DIR = path.join(__dirname, '.auth');
105
+ const STATE = path.join(AUTH_DIR, 'state.json');
106
+
107
+ async function fetchRopcToken() {
108
+ const tenant = reqEnv('E2E_TENANT_ID');
109
+ const body = new URLSearchParams({
110
+ grant_type: 'password',
111
+ client_id: reqEnv('E2E_CLIENT_ID'),
112
+ username: reqEnv('E2E_TEST_USER'),
113
+ password: reqEnv('E2E_TEST_PASSWORD'),
114
+ scope: `openid profile ${reqEnv('E2E_SCOPE')}`,
115
+ });
116
+ const url = `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`;
117
+ const res = await fetch(url, {
118
+ method: 'POST',
119
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
120
+ body,
121
+ });
122
+ if (!res.ok) {
123
+ const txt = await res.text();
124
+ throw new Error(
125
+ `ROPC token fetch failed (${res.status}). ` +
126
+ `Check: ROPC enabled on app registration, test user exists, ` +
127
+ `MFA excluded via Conditional Access. Response: ${txt}`
128
+ );
129
+ }
130
+ return res.json() as Promise<{ access_token: string; id_token: string; expires_in: number }>;
131
+ }
132
+
133
+ function reqEnv(k: string): string {
134
+ const v = process.env[k];
135
+ if (!v) throw new Error(`Missing env ${k} — run /infra to regenerate .env.test`);
136
+ return v;
137
+ }
138
+
139
+ async function globalSetup() {
140
+ fs.mkdirSync(AUTH_DIR, { recursive: true });
141
+ const token = await fetchRopcToken();
142
+ const baseURL = reqEnv('E2E_BASE_URL');
143
+ const browser = await chromium.launch();
144
+ const page = await browser.newPage();
145
+ await page.goto(baseURL);
146
+ await page.evaluate((t) => {
147
+ localStorage.setItem('e2e.access_token', t.access_token);
148
+ localStorage.setItem('e2e.id_token', t.id_token);
149
+ }, token);
150
+ await page.context().storageState({ path: STATE });
151
+ await browser.close();
152
+ console.log('E2E auth ready — real Entra token injected, MFA UI bypassed.');
153
+ }
154
+
155
+ export default globalSetup;
156
+ ```
157
+
158
+ ### `docker-compose.test.yml` — materialize to project root
159
+
160
+ ```yaml
161
+ # docker-compose.test.yml — E2E test stack (local-docker mode).
162
+ # TEMPLATE — adjust build contexts and ports for your project layout.
163
+ services:
164
+ backend:
165
+ build:
166
+ context: ./backend # <-- adjust
167
+ environment:
168
+ SQL_SERVER: ${E2E_SQL_SERVER}
169
+ SQL_DB: ${E2E_SQL_DB}
170
+ SQL_USER: ${E2E_SQL_USER}
171
+ SQL_PASSWORD: ${E2E_SQL_PASSWORD}
172
+ TENANT_ID: ${E2E_TENANT_ID}
173
+ CLIENT_ID: ${E2E_CLIENT_ID}
174
+ STORAGE_CONN: ${E2E_STORAGE_CONN:-}
175
+ ports:
176
+ - "8081:8081"
177
+ healthcheck:
178
+ test: ["CMD", "curl", "-fs", "http://localhost:8081/health"]
179
+ interval: 5s
180
+ timeout: 3s
181
+ retries: 12
182
+
183
+ webapp:
184
+ build:
185
+ context: ./frontend # <-- adjust
186
+ environment:
187
+ API_BASE_URL: http://backend:8081
188
+ TENANT_ID: ${E2E_TENANT_ID}
189
+ CLIENT_ID: ${E2E_CLIENT_ID}
190
+ ports:
191
+ - "8080:80"
192
+ depends_on:
193
+ backend:
194
+ condition: service_healthy
195
+ ```
196
+
197
+ ### `scripts/e2e-stack.sh` — materialize to `scripts/`
198
+
199
+ ```bash
200
+ #!/usr/bin/env bash
201
+ # scripts/e2e-stack.sh — E2E test stack lifecycle.
202
+ set -euo pipefail
203
+ cd "$(dirname "$0")/.."
204
+
205
+ CMD="${1:-}"
206
+ COMPOSE="docker-compose.test.yml"
207
+ ENVFILE=".env.test"
208
+
209
+ [ -f "$ENVFILE" ] || { echo "missing $ENVFILE — run /infra to generate it"; exit 1; }
210
+ set -a; . "./$ENVFILE"; set +a
211
+
212
+ if [ "${E2E_STACK_EXTERNAL:-}" = "1" ]; then
213
+ echo "[e2e-stack] E2E_STACK_EXTERNAL=1 — targeting deployed Azure app at ${E2E_BASE_URL:-<unset>}"
214
+ case "$CMD" in
215
+ up)
216
+ echo "[e2e-stack] Verifying Azure app is reachable..."
217
+ for i in $(seq 1 10); do
218
+ if curl -fs "${E2E_BASE_URL:-http://localhost:8080}/health" >/dev/null 2>&1 \
219
+ || curl -fs "${E2E_BASE_URL:-http://localhost:8080}" >/dev/null 2>&1; then
220
+ echo "[e2e-stack] Azure app reachable."
221
+ exit 0
222
+ fi
223
+ sleep 3
224
+ done
225
+ echo "[e2e-stack] WARNING: Azure app not reachable. Classify as AZURE UNAVAILABLE."
226
+ exit 1
227
+ ;;
228
+ down)
229
+ echo "[e2e-stack] External mode — nothing to stop."
230
+ exit 0
231
+ ;;
232
+ *)
233
+ echo "usage: e2e-stack.sh up|down"
234
+ exit 1
235
+ ;;
236
+ esac
237
+ fi
238
+
239
+ if docker compose version >/dev/null 2>&1; then DC="docker compose"; else DC="docker-compose"; fi
240
+
241
+ case "$CMD" in
242
+ up)
243
+ echo "[e2e-stack] building + starting local app containers..."
244
+ $DC -f "$COMPOSE" --env-file "$ENVFILE" up -d --build
245
+ for i in $(seq 1 30); do
246
+ if curl -fs "${E2E_BASE_URL:-http://localhost:8080}" >/dev/null 2>&1; then
247
+ echo "[e2e-stack] up."
248
+ exit 0
249
+ fi
250
+ sleep 3
251
+ done
252
+ echo "[e2e-stack] ERROR: app did not become healthy — STACK NOT READY"
253
+ exit 1
254
+ ;;
255
+ down)
256
+ echo "[e2e-stack] stopping local containers..."
257
+ $DC -f "$COMPOSE" --env-file "$ENVFILE" down
258
+ echo "[e2e-stack] down."
259
+ ;;
260
+ *)
261
+ echo "usage: e2e-stack.sh up|down"
262
+ exit 1
263
+ ;;
264
+ esac
265
+ ```
package/template/setup.sh CHANGED
@@ -11,9 +11,8 @@
11
11
  # user-owned -> NEVER overwritten
12
12
  # hybrid -> merged idempotently, never replaced
13
13
  #
14
+ # Claude Code only (v2.3 — Cursor support dropped, §5.69).
14
15
  # Flags: --src DIR --kb-path P --kb-remote URL --no-kb --force --dry-run
15
- # --tools LIST (comma/space list from: claude cursor; default both)
16
- # --add-tool T (additive: install T's files into an existing project)
17
16
  set -euo pipefail
18
17
 
19
18
  SRC="."
@@ -22,9 +21,6 @@ KB_REMOTE="${ANPUNKIT_KB_REMOTE:-}"
22
21
  NO_KB=0
23
22
  FORCE=0
24
23
  DRY=0
25
- TOOLS_FLAG=""
26
- ADD_TOOL=""
27
- AVAILABLE_TOOLS="claude cursor" # tools with real adapters; extend here
28
24
  while [ $# -gt 0 ]; do
29
25
  case "$1" in
30
26
  --src) SRC="$2"; shift 2;;
@@ -33,8 +29,6 @@ while [ $# -gt 0 ]; do
33
29
  --no-kb) NO_KB=1; shift;;
34
30
  --force) FORCE=1; shift;;
35
31
  --dry-run) DRY=1; shift;;
36
- --tools) TOOLS_FLAG="$2"; shift 2;;
37
- --add-tool) ADD_TOOL="$2"; shift 2;;
38
32
  *) echo "unknown flag: $1" >&2; exit 2;;
39
33
  esac
40
34
  done
@@ -70,36 +64,6 @@ fi
70
64
  say "mode: $MODE (installed: ${OLD_VER:-none} -> new: $NEW_VER)"
71
65
  [ "$MODE" = "newer-installed" ] && say " ! installed version is newer than this package — proceeding as repair, review carefully."
72
66
 
73
- # ---- tool selection (v2.1): install only the selected tools' adapters/hooks/files
74
- TOOLS_FILE="$DEST/.claude/anpunkit-tools.json"
75
- norm_tools() { printf '%s' "$1" | tr ',' ' ' | tr -s ' ' | sed 's/^ //; s/ $//'; }
76
- read_recorded_tools() {
77
- [ -f "$TOOLS_FILE" ] || { printf ''; return; }
78
- node -e "try{console.log((JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')).tools||[]).join(' '))}catch(e){console.log('')}" "$TOOLS_FILE" 2>/dev/null || printf ''
79
- }
80
- valid_tool() { case " $AVAILABLE_TOOLS " in *" $1 "*) return 0;; *) return 1;; esac; }
81
-
82
- RECORDED="$(read_recorded_tools)"
83
- if [ -n "$TOOLS_FLAG" ]; then
84
- TOOLS="$(norm_tools "$TOOLS_FLAG")"
85
- elif [ -n "$RECORDED" ]; then
86
- TOOLS="$RECORDED" # re-run: preserve prior selection
87
- elif [ "$MODE" = "fresh" ] && [ -t 0 ]; then
88
- printf " Select tools to install (space-separated from: %s) [claude cursor]: " "$AVAILABLE_TOOLS"
89
- read -r _sel || true
90
- TOOLS="$(norm_tools "${_sel:-claude cursor}")"
91
- else
92
- TOOLS="claude cursor" # non-interactive default = both (no regression)
93
- fi
94
- # additive: --add-tool unions into the selection (and is recorded)
95
- [ -n "$ADD_TOOL" ] && TOOLS="$(norm_tools "$TOOLS $(norm_tools "$ADD_TOOL")" | tr ' ' '\n' | sort -u | tr '\n' ' ')"
96
- TOOLS="$(norm_tools "$TOOLS")"
97
- # validate
98
- for t in $TOOLS; do valid_tool "$t" || { echo "FATAL: unknown tool '$t' (available: $AVAILABLE_TOOLS)"; exit 2; }; done
99
- [ -z "$TOOLS" ] && { echo "FATAL: no tools selected"; exit 2; }
100
- tool_selected() { case " $TOOLS " in *" $1 "*) return 0;; *) return 1;; esac; }
101
- say "tools: $TOOLS$([ -n "$ADD_TOOL" ] && echo ' (+'"$ADD_TOOL"' added)')"
102
-
103
67
  BACKUP="$DEST/.anpunkit-backup-$(date +%Y%m%d-%H%M%S)"
104
68
  backup_one() {
105
69
  [ "$MODE" = "fresh" ] && return 0
@@ -114,11 +78,6 @@ backup_one() {
114
78
  install_kit_file() {
115
79
  local rel="$1" s="$SRC/$1" d="$DEST/$1"
116
80
  [ -f "$s" ] || return 0
117
- # v2.1 tool gating: cursor-only files skipped unless cursor selected.
118
- # (.claude/* is shared infra — Cursor reads .claude/agents + .claude/hooks too.)
119
- case "$rel" in
120
- .cursor/*) tool_selected cursor || { act "skip $rel (cursor not selected)"; return 0; };;
121
- esac
122
81
  if [ ! -f "$d" ]; then
123
82
  act "write $rel"; [ "$DRY" = 1 ] && return 0
124
83
  mkdir -p "$(dirname "$d")"; cp -p "$s" "$d"; return 0
@@ -156,76 +115,44 @@ else
156
115
  fi
157
116
 
158
117
  # ---- generate command adapters from commands.src/ (manifest-owned output)
159
- say ""; say "[2/6] generate command adapters (tools: $TOOLS):"
118
+ say ""; say "[2/6] generate command adapters (.claude/commands/):"
160
119
  gen_adapters() {
161
- tool_selected claude && mkdir -p "$DEST/.claude/commands"
162
- tool_selected cursor && mkdir -p "$DEST/.cursor/commands" "$DEST/.cursor/rules"
120
+ mkdir -p "$DEST/.claude/commands"
163
121
  for f in "$DEST"/commands.src/*.md; do
164
122
  [ -f "$f" ] || continue
165
123
  n=$(basename "$f")
166
- if tool_selected claude; then
167
- act "claude .claude/commands/$n"
168
- [ "$DRY" = 1 ] || cp -p "$f" "$DEST/.claude/commands/$n"
169
- fi
170
- if tool_selected cursor; then
171
- act "cursor .cursor/commands/$n"
172
- [ "$DRY" = 1 ] || awk 'BEGIN{fm=0} NR==1 && $0=="---"{fm=1; next} fm==1 && $0=="---"{fm=0; next} fm==0{print}' "$f" \
173
- | sed '/^$/{1d}' > "$DEST/.cursor/commands/$n"
174
- fi
124
+ act "claude .claude/commands/$n"
125
+ [ "$DRY" = 1 ] || cp -p "$f" "$DEST/.claude/commands/$n"
175
126
  done
176
127
  }
177
128
  gen_adapters
178
129
 
179
- # ---- hybrid JSON merge: ensure the hook entries for selected tools, never drop user keys
180
- say ""; say "[3/6] wire hooks (idempotent merge, tools: $TOOLS):"
130
+ # ---- hybrid JSON merge: ensure the Claude hook entries, never drop user keys
131
+ say ""; say "[3/6] wire hooks (idempotent merge, .claude/settings.json):"
181
132
  merge_settings() {
182
- [ "$DRY" = 1 ] && { act "merge $(tool_selected claude && echo -n '.claude/settings.json ')$(tool_selected cursor && echo -n '.cursor/hooks.json')"; return 0; }
183
- tool_selected claude && backup_one "$DEST/.claude/settings.json"
184
- tool_selected cursor && backup_one "$DEST/.cursor/hooks.json"
185
- CLAUDE_SEL=$(tool_selected claude && echo 1 || echo 0) \
186
- CURSOR_SEL=$(tool_selected cursor && echo 1 || echo 0) \
133
+ [ "$DRY" = 1 ] && { act "merge .claude/settings.json"; return 0; }
134
+ backup_one "$DEST/.claude/settings.json"
187
135
  node - "$DEST" <<'NODE'
188
136
  const fs=require('fs'),path=require('path');
189
137
  const dest=process.argv[2];
190
- const claudeSel=process.env.CLAUDE_SEL==='1', cursorSel=process.env.CURSOR_SEL==='1';
191
138
  const load=(p,d)=>{try{return JSON.parse(fs.readFileSync(p,'utf8'))}catch(e){return d}};
192
139
  const save=(p,o)=>{fs.mkdirSync(path.dirname(p),{recursive:true});fs.writeFileSync(p,JSON.stringify(o,null,2)+"\n")};
193
- const out=[];
194
- // Claude settings.json
195
- if(claudeSel){
196
- const sp=path.join(dest,'.claude/settings.json');
197
- const s=load(sp,{});
198
- if(!s.$schema)s.$schema='https://json.schemastore.org/claude-code-settings.json';
199
- s.hooks=s.hooks||{};
200
- const ensure=(event,matcher,cmd)=>{
201
- const arr=s.hooks[event]=s.hooks[event]||[];
202
- for(const blk of arr)for(const h of (blk.hooks||[]))if(h.command===cmd)return;
203
- const blk={hooks:[{type:'command',command:cmd}]};
204
- if(matcher)blk.matcher=matcher;
205
- arr.push(blk);
206
- };
207
- ensure('SessionStart','startup|clear|compact','bash .claude/hooks/session-start.sh');
208
- ensure('PreCompact','auto|manual','bash .claude/hooks/pre-compact.sh');
209
- ensure('SubagentStop','','bash .claude/hooks/subagent-stop.sh');
210
- save(sp,s);
211
- out.push('.claude/settings.json');
212
- }
213
- // Cursor hooks.json
214
- if(cursorSel){
215
- const cp=path.join(dest,'.cursor/hooks.json');
216
- const c=load(cp,{version:1,hooks:{}});
217
- c.hooks=c.hooks||{};
218
- const cursorEnsure=(event,cmd)=>{
219
- const arr=c.hooks[event]=c.hooks[event]||[];
220
- if(!arr.some(h=>h.command===cmd))arr.push({command:cmd});
221
- };
222
- cursorEnsure('sessionStart','bash .claude/hooks/cursor-session-start.sh');
223
- cursorEnsure('preCompact','bash .claude/hooks/pre-compact.sh');
224
- cursorEnsure('subagentStop','bash .claude/hooks/subagent-stop.sh');
225
- save(cp,c);
226
- out.push('.cursor/hooks.json');
227
- }
228
- console.log(' merged '+(out.join(' + ')||'(nothing — no tool selected hooks)'));
140
+ const sp=path.join(dest,'.claude/settings.json');
141
+ const s=load(sp,{});
142
+ if(!s.$schema)s.$schema='https://json.schemastore.org/claude-code-settings.json';
143
+ s.hooks=s.hooks||{};
144
+ const ensure=(event,matcher,cmd)=>{
145
+ const arr=s.hooks[event]=s.hooks[event]||[];
146
+ for(const blk of arr)for(const h of (blk.hooks||[]))if(h.command===cmd)return;
147
+ const blk={hooks:[{type:'command',command:cmd}]};
148
+ if(matcher)blk.matcher=matcher;
149
+ arr.push(blk);
150
+ };
151
+ ensure('SessionStart','startup|clear|compact','bash .claude/hooks/session-start.sh');
152
+ ensure('PreCompact','auto|manual','bash .claude/hooks/pre-compact.sh');
153
+ ensure('SubagentStop','','bash .claude/hooks/subagent-stop.sh');
154
+ save(sp,s);
155
+ console.log(' merged .claude/settings.json');
229
156
  NODE
230
157
  }
231
158
  merge_settings
@@ -237,17 +164,11 @@ say ""; say "[4/6] AGENTS.md / CLAUDE.md verify:"
237
164
  verify_import() {
238
165
  local CL="$DEST/CLAUDE.md" AG="$DEST/AGENTS.md"
239
166
  [ -f "$AG" ] || { echo "FATAL VERIFY: AGENTS.md not on disk"; exit 1; }
240
- # sentinel present in AGENTS.md (shared — both tools read AGENTS.md)
241
167
  if ! grep -q 'ANPUNKIT-AGENTS-SENTINEL' "$AG"; then
242
168
  echo "FATAL VERIFY: AGENTS.md sentinel missing (file clobbered or empty?)"; exit 1; fi
243
- # CLAUDE.md bare @AGENTS.md import is Claude-specific — only verify if claude selected
244
- if tool_selected claude; then
245
- if ! grep -qE '^@AGENTS\.md[[:space:]]*$' "$CL"; then
246
- echo "FATAL VERIFY: CLAUDE.md is missing a bare top-level '@AGENTS.md' import line"; exit 1; fi
247
- say " VERIFY ok: bare @AGENTS.md import + AGENTS.md on disk + sentinel present."
248
- else
249
- say " VERIFY ok: AGENTS.md on disk + sentinel present (claude not selected — CLAUDE.md import not required)."
250
- fi
169
+ if ! grep -qE '^@AGENTS\.md[[:space:]]*$' "$CL"; then
170
+ echo "FATAL VERIFY: CLAUDE.md is missing a bare top-level '@AGENTS.md' import line"; exit 1; fi
171
+ say " VERIFY ok: bare @AGENTS.md import + AGENTS.md on disk + sentinel present."
251
172
  }
252
173
  verify_import
253
174
 
@@ -305,14 +226,12 @@ configure_kb() {
305
226
 
306
227
  configure_kb
307
228
 
308
- # ---- finalize: stamp the installed manifest (copy the new one) + record tools
229
+ # ---- finalize: stamp the installed manifest (copy the new one)
309
230
  if [ "$DRY" != 1 ]; then
310
231
  cp -p "$MAN_SRC" "$MAN_DEST" 2>/dev/null || true
311
- mkdir -p "$DEST/.claude"
312
- node -e "require('fs').writeFileSync(process.argv[1],JSON.stringify({tools:process.argv[2].split(' ').filter(Boolean)},null,2)+'\n')" "$TOOLS_FILE" "$TOOLS" 2>/dev/null || true
313
232
  [ -d "$BACKUP" ] && say "" && say "backup written: ${BACKUP#$DEST/}"
314
233
  fi
315
234
 
316
- say ""; say "anpunkit setup complete ($MODE, tools: $TOOLS). Open Claude Code or Cursor — the SessionStart/sessionStart hook fires automatically."
235
+ say ""; say "anpunkit setup complete ($MODE). Open Claude Code — the SessionStart hook fires automatically."
317
236
  if [ "$DRY" = 1 ]; then say "(dry-run: no files were written.)"; fi
318
237
  exit 0
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env bash
2
- # cursor-session-start.sh — Cursor wiring for the SESSION-OPEN ritual.
3
- # Cursor's sessionStart hook expects JSON on stdout ({"additional_context": ...}),
4
- # unlike Claude Code, which injects raw stdout. This wrapper runs the SHARED
5
- # session-start.sh body (single copy — anti-drift) and wraps its output in the
6
- # JSON envelope Cursor requires. Cursor sets CLAUDE_PROJECT_DIR (compat alias),
7
- # so the shared script needs no changes.
8
- # Uses node for JSON (the kit assumes Node; python3 is a fallback only).
9
- # Verified against cursor.com/docs/hooks (sessionStart output schema), 2026-06.
10
- set -euo pipefail
11
- HERE="$(cd "$(dirname "$0")" && pwd)"
12
- OUT="$(bash "$HERE/session-start.sh" 2>/dev/null || true)"
13
- if command -v node >/dev/null 2>&1; then
14
- printf '%s' "$OUT" | node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>console.log(JSON.stringify({additional_context:d})))'
15
- else
16
- printf '%s' "$OUT" | python3 -c 'import json,sys; print(json.dumps({"additional_context": sys.stdin.read()}))'
17
- fi
@@ -1,39 +0,0 @@
1
- ---
2
- name: caveman
3
- description: >
4
- Ultra-compressed communication mode. Cuts token usage ~75% by dropping
5
- filler, articles, and pleasantries while keeping full technical accuracy.
6
- Use when user says "caveman mode", "talk like caveman", "use caveman",
7
- "less tokens", "be brief", or invokes /caveman.
8
- ---
9
-
10
- Respond terse like smart caveman. All technical substance stay. Only fluff die.
11
-
12
- ## Persistence
13
-
14
- ACTIVE EVERY RESPONSE once triggered. No revert after many turns. No filler drift. Still active if unsure. Off only when user says "stop caveman" or "normal mode".
15
-
16
- ## Rules
17
-
18
- Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Fragments OK. Short synonyms (big not extensive, fix not "implement a solution for"). Abbreviate common terms (DB/auth/config/req/res/fn/impl). Strip conjunctions. Use arrows for causality (X -> Y). One word when one word enough.
19
-
20
- Technical terms stay exact. Code blocks unchanged. Errors quoted exact.
21
-
22
- Pattern: `[thing] [action] [reason]. [next step].`
23
-
24
- Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
25
- Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:"
26
-
27
- ### Examples
28
-
29
- **"Why React component re-render?"**
30
-
31
- > Inline obj prop -> new ref -> re-render. `useMemo`.
32
-
33
- **"Explain database connection pooling."**
34
-
35
- > Pool = reuse DB conn. Skip handshake -> fast under load.
36
-
37
- ## Auto-Clarity Exception
38
-
39
- Drop caveman temporarily for: security warnings, irreversible action confirmations, multi-step sequences where fragment order risks misread, user asks to clarify or repeats question. Resume caveman after.
@@ -1,82 +0,0 @@
1
- Caveman ULTRA mode. You are the ORCHESTRATOR.
2
-
3
- Action: $ARGUMENTS (default: "provision" if INFRA.md missing, "verify" if present)
4
-
5
- ---
6
-
7
- ## PRE-FLIGHT
8
-
9
- 1. Check `az account show` — if it fails, STOP.
10
- Tell me: "Run `scripts/auth-setup.sh` first."
11
-
12
- 2. Determine mode from $ARGUMENTS or default logic.
13
-
14
- ---
15
-
16
- ## PROVISION / UPDATE flow
17
-
18
- 1. Dispatch `infra-provisioner` with the mode.
19
-
20
- 2. It returns WHAT-IF READY. Show me:
21
- - resource list with SKUs and costs
22
- - total monthly cost estimate
23
- - what-if detail file path
24
- Then ask:
25
- > "Review docs/research/infra-whatif-<timestamp>.md. Type 'go' to apply,
26
- > or describe changes to make first."
27
- WAIT. Do not proceed until I say "go".
28
-
29
- 3. On "go": pass APPROVED to `infra-provisioner`.
30
-
31
- 4. On "make changes": dispatch UPDATE with feedback. Loop to step 2.
32
-
33
- 5. On APPLIED: tell me INFRA.md ✓, .env.test ✓, and any manual steps remaining.
34
- Then run AUTH PROOF (below) before declaring Phase 0 complete.
35
-
36
- ---
37
-
38
- ## AUTH PROOF (one-time, hard rule 16)
39
-
40
- Runs after a successful PROVISION (and on demand via `/infra auth-proof`). Proves
41
- every credential the project's real tests will use is reusable WITHOUT interaction.
42
- "Reusable" is defined falsifiably: obtainable headlessly TWICE IN A ROW.
43
-
44
- 1. Enumerate the credentials in scope: the Entra/MSAL app login, plus every
45
- external datasource credential referenced in docs/DATAFLOW.md (external rows)
46
- and docs/ENDPOINTS.md (auth column) — Azure SQL, Tableau, etc.
47
-
48
- 2. Dispatch `infra-provisioner` to run, for EACH credential, a headless obtain
49
- twice in a row: first call primes (token fetch / connect), second call must
50
- succeed from cache/refresh with ZERO prompts. The Microsoft login UI is never
51
- driven (ROPC + dedicated MFA-excluded test account; hard rule 8).
52
-
53
- 3. Result:
54
- - All credentials pass twice headlessly -> write `AUTH PROOF: PASS <timestamp>`
55
- to docs/INFRA.md with the per-credential list. Phase 0 may complete.
56
- - Any credential prompts or fails the second obtain -> NOT reusable. Write
57
- `AUTH PROOF: FAIL` + which credential, STOP, tell me what to fix. Phase 0 is
58
- not complete until the proof passes.
59
-
60
- ---
61
-
62
- ## VERIFY flow
63
-
64
- 1. Check INFRA.md exists. If missing: tell me to run `/infra provision` first.
65
- 2. Dispatch `infra-provisioner` VERIFY mode.
66
- 3. Return drift report. If the AUTH PROOF marker is missing or stale, re-run AUTH PROOF.
67
-
68
- ---
69
-
70
- ## REGENERATE-ENV flow
71
-
72
- 1. Check INFRA.md exists.
73
- 2. Dispatch `infra-provisioner` REGENERATE-ENV.
74
- 3. Confirm .env.test rewritten.
75
-
76
- ---
77
-
78
- ## Notes
79
-
80
- - /infra never touches PLAN.md, STATE.md, ISSUES.md, or DESIGN_LOG.md.
81
- - Infra errors are AZURE UNAVAILABLE or CONFIG ERROR — do NOT route to debugger.
82
- - If UNDERSPEC: list missing decisions, ask me to fill them, re-dispatch.
@@ -1,29 +0,0 @@
1
- Caveman ULTRA mode.
2
-
3
- Append an architectural decision to docs/DESIGN_LOG.md. Change: $ARGUMENTS
4
-
5
- WHEN this applies — the change is ARCHITECTURAL:
6
- - new / removed / renamed agent, hook, or command
7
- - changed workflow rule (escalation, phase gate, test layering)
8
- - design decision where alternatives were weighed
9
-
10
- WHEN it does NOT apply:
11
- - code changes, features, phase progress -> HISTORY.md
12
- - bugs + fixes -> ISSUES.md
13
- - infra changes -> INFRA.md
14
-
15
- Steps:
16
- 1. Read docs/DESIGN_LOG.md.
17
- 2. Append a dated line to `## 0. Changelog`, newest first.
18
- 3. If alternatives were weighed, ALSO append a full entry to §5:
19
- ```
20
-
21
- ### 5.x <decision title>
22
-
23
- Options: <a, b, c>. **Chosen: <x>.** <why it won.>
24
-
25
- ```
26
- 4. If agent/hook/command/file added or removed, update the file inventory (§6).
27
- 5. NEVER rewrite or delete existing entries — append only.
28
-
29
- Confirm back in 2-3 lines: what was logged and where.
@@ -1,23 +0,0 @@
1
- Caveman ULTRA mode.
2
-
3
- Append an entry to docs/ISSUES.md. Error: $ARGUMENTS
4
-
5
- Canonical format:
6
- ```
7
-
8
- ### <short error title — searchable, literal error keywords>
9
-
10
- - [x] open (or “- [x] resolved”)
11
- - symptom: <what was observed>
12
- - root cause: <the REAL underlying cause>
13
- - solution: <exact fix, or “pending”>
14
- - failed attempts: <approaches that did NOT work>
15
-
16
- ```
17
- Rules:
18
- - title must contain literal error keywords -> grep finds it.
19
- - root cause is the real cause, not "the line threw an error".
20
- - always fill "failed attempts" — stops repeated dead ends.
21
- - open issues go at the TOP; resolved below; archived oldest at bottom.
22
-
23
- Confirm the entry back in 2 lines.