geobuke-code 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -11,7 +11,7 @@ import { loadState, resetGate } from "./state.js";
11
11
  import { addDefer, loadDefers, resolveDefer, startDefer, reopenDefer } from "./defer.js";
12
12
  import { isStopHintMuted, setStopHintMuted } from "./config.js";
13
13
  import { selectedTransport } from "./judge.js";
14
- import { buildPreCommand, normalizeHooks, ensureSessionStartHook } from "./install.js";
14
+ import { buildPreCommand, normalizeHooks, ensureSessionStartHook, DEV_PLACEHOLDER } from "./install.js";
15
15
  import { isCacheStale, readVersionCache, refreshVersionCache, } from "./version.js";
16
16
  import { logEvent, parseEvents, computeMetrics } from "./metrics.js";
17
17
  const CLI_PATH = fileURLToPath(import.meta.url);
@@ -30,8 +30,8 @@ const PKG_VERSION = readPkgVersion();
30
30
  function hasApiKey() {
31
31
  return existsSync(join(homedir(), ".gbc", "api-key"));
32
32
  }
33
- function stopCommand() {
34
- return `node "${CLI_PATH}" hook stop`;
33
+ function stopCommand(hookPath) {
34
+ return `node "${hookPath}" hook stop`;
35
35
  }
36
36
  function nowIso() {
37
37
  try {
@@ -65,6 +65,10 @@ function nowStamp() {
65
65
  async function cmdInit(args) {
66
66
  const cwd = process.cwd();
67
67
  const yes = args.includes("--yes") || args.includes("-y");
68
+ // --dev: hook 명령에 절대경로(CLI_PATH) 대신 ${CLAUDE_PROJECT_DIR} placeholder를 굽는다.
69
+ // geobuke-code 자기 repo 도그푸딩 전용(dist 위치가 옮겨다녀도 안 깨짐). 기본(false)은 절대경로.
70
+ const dev = args.includes("--dev");
71
+ const hookPath = dev ? DEV_PLACEHOLDER : CLI_PATH;
68
72
  const claudeDir = join(cwd, ".claude");
69
73
  const settingsPath = join(claudeDir, "settings.json");
70
74
  // 설치 대상 스킬들(제품소스 skills/<name>/SKILL.md → .claude/skills/<name>/SKILL.md).
@@ -76,7 +80,7 @@ async function cmdInit(args) {
76
80
  1) ${settingsPath} 에 PreToolUse(Edit|Write) + Stop + SessionStart hook 추가 (머지·멱등)
77
81
  - 기존 settings.json 있으면 백업: settings.json.bak-<시각>
78
82
  2) ${join(claudeDir, "skills")} 에 ${skillNames.map((n) => `/${n}`).join(", ")} 스킬 설치
79
- 3) hook 명령: ${buildPreCommand(CLI_PATH)}
83
+ 3) hook 명령: ${buildPreCommand(hookPath)}${dev ? " (--dev: ${CLAUDE_PROJECT_DIR} placeholder)" : ""}
80
84
  ${hasApiKey()
81
85
  ? " (~/.gbc/api-key 감지됨 → 빠른 haiku API 경로로 동작)"
82
86
  : " (~/.gbc/api-key 없음 → claude -p 폴백. 빠른 경로 원하면 키 파일 생성)"}
@@ -105,7 +109,7 @@ ${hasApiKey()
105
109
  if (!serialized.includes("hook pre-tool-use")) {
106
110
  (hooks.PreToolUse ??= []).push({
107
111
  matcher: "Edit|Write|MultiEdit",
108
- hooks: [{ type: "command", command: buildPreCommand(CLI_PATH) }],
112
+ hooks: [{ type: "command", command: buildPreCommand(hookPath) }],
109
113
  });
110
114
  console.log(` + PreToolUse hook 추가`);
111
115
  }
@@ -119,7 +123,7 @@ ${hasApiKey()
119
123
  // Stop (멱등)
120
124
  if (!serialized.includes("hook stop")) {
121
125
  (hooks.Stop ??= []).push({
122
- hooks: [{ type: "command", command: stopCommand() }],
126
+ hooks: [{ type: "command", command: stopCommand(hookPath) }],
123
127
  });
124
128
  console.log(` + Stop hook 추가`);
125
129
  }
@@ -127,7 +131,7 @@ ${hasApiKey()
127
131
  console.log(` = Stop hook 이미 존재 (skip)`);
128
132
  }
129
133
  // SessionStart (멱등) — 세션 진입(startup|resume) 시 미해결 defer 알림
130
- if (ensureSessionStartHook(settings, CLI_PATH)) {
134
+ if (ensureSessionStartHook(settings, hookPath)) {
131
135
  console.log(` + SessionStart hook 추가`);
132
136
  }
133
137
  else {
package/dist/install.js CHANGED
@@ -1,6 +1,22 @@
1
1
  // gbc init 설치 로직 (순수함수 — cli.ts main() 부작용 없이 단위테스트 가능).
2
2
  // 키 주입은 셸이 아니라 gbc 코드(judge.ts resolveApiKey)가 처리한다 → hook 명령은
3
3
  // 셸 무관 순수 형태라 native Windows(cmd.exe)/bash/zsh/Mac에서 동일하게 동작한다.
4
+ /**
5
+ * dev(도그푸딩) 설치용 hook 경로 placeholder. `gbc init --dev`가 절대경로(CLI_PATH) 대신 이걸
6
+ * 구워, geobuke-code 자기 repo처럼 dist 위치가 옮겨다니는 클론에서도 hook이 깨지지 않게 한다
7
+ * (CC 런타임이 ${CLAUDE_PROJECT_DIR}를 프로젝트 루트로 치환). npm 전역·외부 4곳 도그푸딩은 절대경로
8
+ * 유지(기본동작 불변) — 이 placeholder는 명시 opt-in일 때만 쓰인다.
9
+ */
10
+ export const DEV_PLACEHOLDER = "${CLAUDE_PROJECT_DIR}/dist/cli.js";
11
+ /**
12
+ * PreToolUse hook의 *정식* 명령 집합(절대경로 + dev placeholder). stale/normalize 판정의 공통 기준.
13
+ * read-time(hasStalePreToolUse)은 런타임 cliPath=절대경로뿐이라 이 repo가 dev인지 모른다 → 두 정식
14
+ * 형태 중 하나면 stale 아님으로 봐야 placeholder를 구식으로 오판하지 않는다. substring이 아니라
15
+ * 완전일치 집합이라, 서브명령명이 바뀌면 placeholder 형태도 함께 갱신돼 진짜 구식 감지는 유지된다.
16
+ */
17
+ function canonicalPreCommands(cliPath) {
18
+ return [buildPreCommand(cliPath), buildPreCommand(DEV_PLACEHOLDER)];
19
+ }
4
20
  /**
5
21
  * PreToolUse hook 명령 생성 — 셸 무관 순수 명령.
6
22
  * `node "<cliPath>" hook pre-tool-use` 형태만 생성한다. 키 주입(셸 prefix)·셸 확장 없음.
@@ -19,12 +35,14 @@ export function buildPreCommand(cliPath) {
19
35
  * settings를 제자리 수정하고 변경 건수를 반환한다(멱등: 이미 표준이면 0건).
20
36
  */
21
37
  export function normalizeHooks(settings, cliPath) {
22
- const target = buildPreCommand(cliPath);
38
+ const canon = canonicalPreCommands(cliPath);
23
39
  let changed = 0;
24
40
  for (const entry of settings.hooks?.PreToolUse ?? []) {
25
41
  for (const h of entry.hooks ?? []) {
26
- if (h.command.includes("hook pre-tool-use") && h.command !== target) {
27
- h.command = target;
42
+ // 이미 정식(절대 or placeholder)이면 건드리지 않는다 dev placeholder를 절대경로로 덮어
43
+ // 도그푸딩 설치를 깨뜨리지 않게. 진짜 구식(옛 bash 키주입 등)만 절대경로로 교체.
44
+ if (h.command.includes("hook pre-tool-use") && !canon.includes(h.command)) {
45
+ h.command = buildPreCommand(cliPath);
28
46
  changed++;
29
47
  }
30
48
  }
@@ -40,10 +58,12 @@ export function buildSessionStartCommand(cliPath) {
40
58
  * 감지부만 떼어낸 비파괴 술어 — ②init-staleness 안내가 settings를 수정하지 않고 판단하게 한다.
41
59
  */
42
60
  export function hasStalePreToolUse(settings, cliPath) {
43
- const target = buildPreCommand(cliPath);
61
+ const canon = canonicalPreCommands(cliPath);
44
62
  for (const entry of settings.hooks?.PreToolUse ?? []) {
45
63
  for (const h of entry.hooks ?? []) {
46
- if (h.command.includes("hook pre-tool-use") && h.command !== target)
64
+ // dev placeholder도 정식이므로 stale 아님 — 절대경로 런타임에서 placeholder를 구식으로 오판해
65
+ // 'gbc init' 재실행을 헛권하던 false-positive 차단(B-잔여 #3의 실제 증상).
66
+ if (h.command.includes("hook pre-tool-use") && !canon.includes(h.command))
47
67
  return true;
48
68
  }
49
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "geobuke-code",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "거북이코드 — 구현 직전 강제 게이트. Claude Code PreToolUse hook으로 코드 변경 전 계획 케이스 누락·시나리오 미지정을 차단한다.",
5
5
  "type": "module",
6
6
  "bin": {