create-anpunkit 2.0.0 → 2.0.3
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/bin/cli.js
CHANGED
|
@@ -16,10 +16,56 @@ if (!fs.existsSync(SETUP)) {
|
|
|
16
16
|
process.exit(1);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
// --- locate a REAL bash on Windows (never the System32 WSL relay).
|
|
20
|
+
// In PowerShell, bare `bash` resolves to C:\Windows\System32\bash.exe, which
|
|
21
|
+
// relays into WSL and fails with "execvpe(/bin/bash) failed" when no distro
|
|
22
|
+
// is installed. We must find Git Bash explicitly.
|
|
23
|
+
function findBash() {
|
|
24
|
+
if (process.platform !== 'win32') return 'bash';
|
|
25
|
+
const candidates = [];
|
|
26
|
+
if (process.env.ANPUNKIT_BASH) candidates.push(process.env.ANPUNKIT_BASH);
|
|
27
|
+
// Derive from git on PATH: <Git>\cmd\git.exe or <Git>\mingw64\bin\git.exe -> <Git>\bin\bash.exe
|
|
28
|
+
try {
|
|
29
|
+
const r = spawnSync('where', ['git'], { encoding: 'utf8' });
|
|
30
|
+
if (r.status === 0) {
|
|
31
|
+
for (const line of r.stdout.split(/\r?\n/).map(s => s.trim()).filter(Boolean)) {
|
|
32
|
+
const d1 = path.resolve(path.dirname(line), '..');
|
|
33
|
+
const d2 = path.resolve(d1, '..');
|
|
34
|
+
candidates.push(path.join(d1, 'bin', 'bash.exe'), path.join(d2, 'bin', 'bash.exe'));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch (_) { /* where.exe missing — fall through */ }
|
|
38
|
+
// Standard Git for Windows install locations
|
|
39
|
+
for (const base of [
|
|
40
|
+
process.env.ProgramFiles,
|
|
41
|
+
process.env['ProgramFiles(x86)'],
|
|
42
|
+
process.env.LocalAppData ? path.join(process.env.LocalAppData, 'Programs') : null
|
|
43
|
+
]) {
|
|
44
|
+
if (base) candidates.push(path.join(base, 'Git', 'bin', 'bash.exe'));
|
|
45
|
+
}
|
|
46
|
+
for (const c of candidates) {
|
|
47
|
+
if (c && !/system32/i.test(c) && fs.existsSync(c)) return c;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const bash = findBash();
|
|
53
|
+
if (!bash) {
|
|
54
|
+
console.error('create-anpunkit: could not find Git Bash.');
|
|
55
|
+
console.error('The Windows `bash` on PATH is the WSL relay (System32), which is not usable here.');
|
|
56
|
+
console.error('Fix one of:');
|
|
57
|
+
console.error(' 1. Install Git for Windows (includes Git Bash): https://git-scm.com/download/win');
|
|
58
|
+
console.error(' 2. Run `npx create-anpunkit` from a Git Bash terminal instead of PowerShell.');
|
|
59
|
+
console.error(' 3. Set ANPUNKIT_BASH to the full path of a bash.exe.');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
19
63
|
// Pass through recognised flags only; setup.sh validates the rest.
|
|
20
64
|
const passthrough = ['--kb-path', '--kb-remote', '--no-kb', '--force', '--dry-run'];
|
|
21
65
|
const argv = process.argv.slice(2);
|
|
22
|
-
|
|
66
|
+
// Git Bash is happiest with forward slashes; Windows APIs accept them too.
|
|
67
|
+
const fwd = p => p.split(path.sep).join('/');
|
|
68
|
+
const args = ['--src', fwd(TEMPLATE)];
|
|
23
69
|
for (let i = 0; i < argv.length; i++) {
|
|
24
70
|
const a = argv[i];
|
|
25
71
|
if (passthrough.includes(a)) {
|
|
@@ -31,11 +77,10 @@ for (let i = 0; i < argv.length; i++) {
|
|
|
31
77
|
}
|
|
32
78
|
}
|
|
33
79
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const r = spawnSync(bash, [SETUP, ...args], { stdio: 'inherit', cwd: process.cwd() });
|
|
80
|
+
console.log(`create-anpunkit -> running setup.sh (bash: ${bash})`);
|
|
81
|
+
const r = spawnSync(bash, [fwd(SETUP), ...args], { stdio: 'inherit', cwd: process.cwd() });
|
|
37
82
|
if (r.error) {
|
|
38
|
-
console.error(
|
|
83
|
+
console.error(`create-anpunkit: failed to run ${bash}: ${r.error.message}`);
|
|
39
84
|
process.exit(1);
|
|
40
85
|
}
|
|
41
86
|
process.exit(r.status === null ? 1 : r.status);
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.0.
|
|
2
|
+
"version": "2.0.3",
|
|
3
3
|
"files": [
|
|
4
4
|
{
|
|
5
5
|
"path": "AGENTS.md",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"path": "README.md",
|
|
14
|
-
"sha256": "
|
|
14
|
+
"sha256": "0335a0cecb86b57d01e63f564206cdf6d189ac873fce5c69d0f5081facfec964"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"path": ".gitattributes",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"path": "setup.sh",
|
|
22
|
-
"sha256": "
|
|
22
|
+
"sha256": "85c4aab58c83bf7b62344bbc04b94a4b49f90df0840b5e20660546b540889d6e"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"path": ".claude/agents/debugger.md",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
"path": ".claude/hooks/cursor-session-start.sh",
|
|
58
|
-
"sha256": "
|
|
58
|
+
"sha256": "e08bcd14fbcfe1831b64e7d8f181b57b02c561cd8d0de6eb16fe06db259be6af"
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
"path": ".claude/hooks/pre-compact.sh",
|
|
@@ -231,11 +231,11 @@
|
|
|
231
231
|
},
|
|
232
232
|
{
|
|
233
233
|
"path": "create-anpunkit/package.json",
|
|
234
|
-
"sha256": "
|
|
234
|
+
"sha256": "4b16cdfd4d9a22379147338664530fd7ce7f0e7566bf63d097f7b8f8f76dcdd8"
|
|
235
235
|
},
|
|
236
236
|
{
|
|
237
237
|
"path": "create-anpunkit/bin/cli.js",
|
|
238
|
-
"sha256": "
|
|
238
|
+
"sha256": "129dae04923b6d380d3ca4601b5dcd2baa6ad4c51c827dddae3e43ea6a9c50e2"
|
|
239
239
|
},
|
|
240
240
|
{
|
|
241
241
|
"path": "create-anpunkit/build.sh",
|
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
# session-start.sh body (single copy — anti-drift) and wraps its output in the
|
|
6
6
|
# JSON envelope Cursor requires. Cursor sets CLAUDE_PROJECT_DIR (compat alias),
|
|
7
7
|
# so the shared script needs no changes.
|
|
8
|
+
# Uses node for JSON (the kit assumes Node; python3 is a fallback only).
|
|
8
9
|
# Verified against cursor.com/docs/hooks (sessionStart output schema), 2026-06.
|
|
9
10
|
set -euo pipefail
|
|
10
11
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
11
12
|
OUT="$(bash "$HERE/session-start.sh" 2>/dev/null || true)"
|
|
12
|
-
|
|
13
|
-
$OUT
|
|
14
|
-
|
|
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
|
package/template/README.md
CHANGED
|
@@ -26,7 +26,7 @@ npx create-anpunkit # non-destructive: never clobbers your files
|
|
|
26
26
|
/overview # bootstrap the project (includes Phase 0 infra)
|
|
27
27
|
```
|
|
28
28
|
Useful flags: `--dry-run` (print the plan, write nothing), `--force` (overwrite
|
|
29
|
-
user-modified kit files), `--kb-path <dir>`
|
|
29
|
+
user-modified kit files), `--kb-path <dir>` (your pre-cloned KB repo; remote auto-recorded from its origin) / `--no-kb`.
|
|
30
30
|
|
|
31
31
|
Upgrading an existing anpunkit project? Re-run `npx create-anpunkit`. It refreshes
|
|
32
32
|
kit-owned files, preserves anything you modified as `<file>.anpunkit-new`, merges
|
|
@@ -157,14 +157,20 @@ re-discovering the same Azure/Databricks gotchas project after project.
|
|
|
157
157
|
**Setup (one-time per machine):**
|
|
158
158
|
|
|
159
159
|
```bash
|
|
160
|
-
# 1. Create a GitHub repo: anpunkit-kb (empty)
|
|
161
|
-
# 2. Clone it somewhere permanent
|
|
162
|
-
git clone git@github.com
|
|
160
|
+
# 1. Create a GitHub repo: anpunkit-kb (empty is fine)
|
|
161
|
+
# 2. Clone it yourself, somewhere permanent — you own the git auth
|
|
162
|
+
git clone git@github.com:<you>/anpunkit-kb.git ~/anpunkit-kb
|
|
163
163
|
|
|
164
|
-
# 3.
|
|
165
|
-
|
|
164
|
+
# 3. Point anpunkit at it
|
|
165
|
+
npx create-anpunkit --kb-path ~/anpunkit-kb
|
|
166
|
+
# (or paste the path at the interactive prompt)
|
|
166
167
|
```
|
|
167
168
|
|
|
169
|
+
That's the last manual git you'll run against the KB. From then on the
|
|
170
|
+
**session-start hook pulls** each session and **`/store-wisdom` commits and
|
|
171
|
+
pushes** (always with your approval). The remote URL is recorded automatically
|
|
172
|
+
from the clone's `origin`; `--kb-remote <url>` overrides it if needed.
|
|
173
|
+
|
|
168
174
|
**Usage:**
|
|
169
175
|
|
|
170
176
|
```
|
|
@@ -17,6 +17,27 @@
|
|
|
17
17
|
|
|
18
18
|
Newest first. One entry per architectural change. Appended by `/log-decision`.
|
|
19
19
|
|
|
20
|
+
- **(v2.0.2)** — KB setup stays PATH-FIRST: the user clones the KB repo
|
|
21
|
+
themselves (`git clone ... ~/anpunkit-kb`) and passes `--kb-path` (or the
|
|
22
|
+
interactive prompt). New: the remote URL is auto-recorded from the clone's
|
|
23
|
+
`origin`, so `--kb-remote` is optional metadata. After setup, no manual git:
|
|
24
|
+
the session-start hook pulls; `/store-wisdom` commits and pushes (human-gated).
|
|
25
|
+
REJECTED: URL-first setup where anpunkit runs `git clone` itself — it was built
|
|
26
|
+
and tested, then dropped: it entangles a fresh install with the user's GitHub
|
|
27
|
+
authentication (SSH keys / gh auth), and a clone failure mid-setup is a worse
|
|
28
|
+
failure mode than asking a developer to run one clone command they already
|
|
29
|
+
understand. Setup records state; it does not acquire credentials.
|
|
30
|
+
|
|
31
|
+
- **(v2.0.1)** — Windows portability fix for the npx installer. (1) `cli.js` no
|
|
32
|
+
longer trusts bare `bash` on win32: PowerShell resolves it to the System32 WSL
|
|
33
|
+
relay, which fails with `execvpe(/bin/bash)` when no distro is installed. The
|
|
34
|
+
bin now locates Git Bash explicitly (ANPUNKIT_BASH override -> derive from
|
|
35
|
+
`where git` -> standard install paths), refuses System32, and fails loudly with
|
|
36
|
+
install guidance. (2) `setup.sh` and `cursor-session-start.sh` no longer depend
|
|
37
|
+
on `python3` (absent or a Store stub on many Windows machines): all JSON and
|
|
38
|
+
checksum work now uses Node, which the kit already requires. Paths passed into
|
|
39
|
+
Git Bash are normalized to forward slashes.
|
|
40
|
+
|
|
20
41
|
- **(v2.0)** — Major release. Project name: **anpunkit**; distributed
|
|
21
42
|
as `npx create-anpunkit`. Five locked features: (1) **TDD-first** via a SCAFFOLD
|
|
22
43
|
step (implementer writes stubs only; test-author writes the suite blind against
|
package/template/setup.sh
CHANGED
|
@@ -39,7 +39,7 @@ act() { if [ "$DRY" = 1 ]; then say " [dry-run] $*"; else say " $*"; fi; }
|
|
|
39
39
|
sha() {
|
|
40
40
|
if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1
|
|
41
41
|
elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1
|
|
42
|
-
else
|
|
42
|
+
else node -e "console.log(require('crypto').createHash('sha256').update(require('fs').readFileSync(process.argv[1])).digest('hex'))" "$1"; fi
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
say "anpunkit setup — dest: $DEST src: $SRC $([ "$DRY" = 1 ] && echo '(DRY-RUN)')"
|
|
@@ -47,12 +47,12 @@ say "anpunkit setup — dest: $DEST src: $SRC $([ "$DRY" = 1 ] && echo '(DRY-R
|
|
|
47
47
|
MAN_SRC="$SRC/.claude/anpunkit-manifest.json"
|
|
48
48
|
MAN_DEST="$DEST/.claude/anpunkit-manifest.json"
|
|
49
49
|
[ -f "$MAN_SRC" ] || { echo "FATAL: missing $MAN_SRC (corrupt kit)"; exit 1; }
|
|
50
|
-
NEW_VER=$(
|
|
50
|
+
NEW_VER=$(node -e "console.log(JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')).version)" "$MAN_SRC")
|
|
51
51
|
|
|
52
52
|
# ---- determine mode from installed manifest
|
|
53
53
|
MODE="fresh"
|
|
54
54
|
if [ -f "$MAN_DEST" ]; then
|
|
55
|
-
OLD_VER=$(
|
|
55
|
+
OLD_VER=$(node -e "try{console.log(JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')).version||'')}catch(e){console.log('')}" "$MAN_DEST" 2>/dev/null || echo "")
|
|
56
56
|
if [ "$OLD_VER" = "$NEW_VER" ]; then MODE="repair"
|
|
57
57
|
elif [ -z "$OLD_VER" ]; then MODE="upgrade"
|
|
58
58
|
else
|
|
@@ -82,19 +82,23 @@ install_kit_file() {
|
|
|
82
82
|
mkdir -p "$(dirname "$d")"; cp -p "$s" "$d"; return 0
|
|
83
83
|
fi
|
|
84
84
|
# present — compare against the INSTALLED manifest's recorded checksum
|
|
85
|
-
local recorded; recorded=$(
|
|
86
|
-
|
|
87
|
-
try
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
local recorded; recorded=$(node - "$MAN_DEST" "$rel" <<'NODE' 2>/dev/null || true
|
|
86
|
+
const fs=require('fs');
|
|
87
|
+
try{
|
|
88
|
+
const m=JSON.parse(fs.readFileSync(process.argv[2],'utf8'));
|
|
89
|
+
const f=(m.files||[]).find(f=>f.path===process.argv[3]);
|
|
90
|
+
console.log(f?f.sha256:'');
|
|
91
|
+
}catch(e){console.log('');}
|
|
92
|
+
NODE
|
|
92
93
|
)
|
|
93
94
|
local cur; cur=$(sha "$d")
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
local new; new=$(sha "$s")
|
|
96
|
+
# already identical to the new version -> nothing to do (regardless of manifest)
|
|
97
|
+
if [ "$cur" = "$new" ]; then act "ok $rel (identical)"; return 0; fi
|
|
98
|
+
# NOTE: bash gives || and && EQUAL precedence (left-assoc) — group explicitly.
|
|
99
|
+
if [ "$cur" = "$recorded" ] || { [ -z "$recorded" ] && [ "$SRC" = "." ]; }; then
|
|
100
|
+
# unmodified since last install (or clone flow) -> refresh to the new version
|
|
101
|
+
backup_one "$d"; act "refresh $rel"; [ "$DRY" = 1 ] || cp -p "$s" "$d"
|
|
98
102
|
else
|
|
99
103
|
# user-modified kit file
|
|
100
104
|
if [ "$FORCE" = 1 ]; then backup_one "$d"; act "force $rel (overwritten)"; [ "$DRY" = 1 ] || cp -p "$s" "$d"
|
|
@@ -104,7 +108,7 @@ PY
|
|
|
104
108
|
|
|
105
109
|
say ""; say "[1/6] kit-owned files (taxonomy):"
|
|
106
110
|
if [ "$SRC" != "." ]; then
|
|
107
|
-
while IFS= read -r rel; do install_kit_file "$rel"; done < <(
|
|
111
|
+
while IFS= read -r rel; do install_kit_file "$rel"; done < <(node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')).files.forEach(f=>console.log(f.path))" "$MAN_SRC")
|
|
108
112
|
else
|
|
109
113
|
say " clone flow (src=.) — kit files already in place; verifying + generating only."
|
|
110
114
|
fi
|
|
@@ -131,44 +135,41 @@ say ""; say "[3/6] wire hooks (idempotent merge):"
|
|
|
131
135
|
merge_settings() {
|
|
132
136
|
[ "$DRY" = 1 ] && { act "merge .claude/settings.json + .cursor/hooks.json"; return 0; }
|
|
133
137
|
backup_one "$DEST/.claude/settings.json"; backup_one "$DEST/.cursor/hooks.json"
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
dest=
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
s=
|
|
143
|
-
s.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
ensure(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
c=
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
json.dump(c,open(cp,"w"),indent=2); open(cp,"a").write("\n")
|
|
170
|
-
print(" merged .claude/settings.json + .cursor/hooks.json")
|
|
171
|
-
PY
|
|
138
|
+
node - "$DEST" <<'NODE'
|
|
139
|
+
const fs=require('fs'),path=require('path');
|
|
140
|
+
const dest=process.argv[2];
|
|
141
|
+
const load=(p,d)=>{try{return JSON.parse(fs.readFileSync(p,'utf8'))}catch(e){return d}};
|
|
142
|
+
const save=(p,o)=>{fs.mkdirSync(path.dirname(p),{recursive:true});fs.writeFileSync(p,JSON.stringify(o,null,2)+"\n")};
|
|
143
|
+
// Claude settings.json
|
|
144
|
+
const sp=path.join(dest,'.claude/settings.json');
|
|
145
|
+
const s=load(sp,{});
|
|
146
|
+
if(!s.$schema)s.$schema='https://json.schemastore.org/claude-code-settings.json';
|
|
147
|
+
s.hooks=s.hooks||{};
|
|
148
|
+
const ensure=(event,matcher,cmd)=>{
|
|
149
|
+
const arr=s.hooks[event]=s.hooks[event]||[];
|
|
150
|
+
for(const blk of arr)for(const h of (blk.hooks||[]))if(h.command===cmd)return;
|
|
151
|
+
const blk={hooks:[{type:'command',command:cmd}]};
|
|
152
|
+
if(matcher)blk.matcher=matcher;
|
|
153
|
+
arr.push(blk);
|
|
154
|
+
};
|
|
155
|
+
ensure('SessionStart','startup|clear|compact','bash .claude/hooks/session-start.sh');
|
|
156
|
+
ensure('PreCompact','auto|manual','bash .claude/hooks/pre-compact.sh');
|
|
157
|
+
ensure('SubagentStop','','bash .claude/hooks/subagent-stop.sh');
|
|
158
|
+
save(sp,s);
|
|
159
|
+
// Cursor hooks.json
|
|
160
|
+
const cp=path.join(dest,'.cursor/hooks.json');
|
|
161
|
+
const c=load(cp,{version:1,hooks:{}});
|
|
162
|
+
c.hooks=c.hooks||{};
|
|
163
|
+
const cursorEnsure=(event,cmd)=>{
|
|
164
|
+
const arr=c.hooks[event]=c.hooks[event]||[];
|
|
165
|
+
if(!arr.some(h=>h.command===cmd))arr.push({command:cmd});
|
|
166
|
+
};
|
|
167
|
+
cursorEnsure('sessionStart','bash .claude/hooks/cursor-session-start.sh');
|
|
168
|
+
cursorEnsure('preCompact','bash .claude/hooks/pre-compact.sh');
|
|
169
|
+
cursorEnsure('subagentStop','bash .claude/hooks/subagent-stop.sh');
|
|
170
|
+
save(cp,c);
|
|
171
|
+
console.log(' merged .claude/settings.json + .cursor/hooks.json');
|
|
172
|
+
NODE
|
|
172
173
|
}
|
|
173
174
|
merge_settings
|
|
174
175
|
|
|
@@ -206,23 +207,41 @@ say ""; say "[6/6] shared KB:"
|
|
|
206
207
|
configure_kb() {
|
|
207
208
|
local cfg="$DEST/.claude/kb-config.json"
|
|
208
209
|
if [ "$NO_KB" = 1 ]; then say " --no-kb: skipping KB."; return 0; fi
|
|
209
|
-
|
|
210
|
+
|
|
211
|
+
# Path-first by design: YOU clone the KB repo (you own the git auth);
|
|
212
|
+
# anpunkit only records where it lives. After that, the session-start hook
|
|
213
|
+
# pulls and /store-wisdom commits+pushes (human-gated) — no manual git needed.
|
|
214
|
+
if [ -z "$KB_PATH" ]; then
|
|
210
215
|
if [ -t 0 ]; then
|
|
211
|
-
printf " Local path to cloned anpunkit-kb repo (blank to skip): "
|
|
212
|
-
|
|
216
|
+
printf " Local path to your cloned anpunkit-kb repo (blank to skip): "
|
|
217
|
+
read -r KB_PATH || true
|
|
218
|
+
[ -z "$KB_PATH" ] && { say " skipped KB."; return 0; }
|
|
213
219
|
else
|
|
214
|
-
say " no KB flags + no TTY -> skipping KB (re-run with --kb-path
|
|
220
|
+
say " no KB flags + no TTY -> skipping KB (re-run with --kb-path <dir>)."; return 0
|
|
215
221
|
fi
|
|
216
222
|
fi
|
|
217
|
-
if [
|
|
218
|
-
echo " ! KB path '$KB_PATH' not a directory. Clone your
|
|
223
|
+
if [ ! -d "$KB_PATH" ]; then
|
|
224
|
+
echo " ! KB path '$KB_PATH' is not a directory. Clone your KB repo first, e.g.:"
|
|
225
|
+
echo " git clone git@github.com:<you>/anpunkit-kb.git ~/anpunkit-kb"
|
|
226
|
+
echo " then re-run with --kb-path ~/anpunkit-kb"
|
|
227
|
+
return 0
|
|
228
|
+
fi
|
|
229
|
+
|
|
230
|
+
# remote is metadata: explicit --kb-remote wins, else read the clone's origin
|
|
231
|
+
if [ -z "$KB_REMOTE" ] && [ -d "$KB_PATH/.git" ]; then
|
|
232
|
+
KB_REMOTE=$(git -C "$KB_PATH" remote get-url origin 2>/dev/null || echo "")
|
|
233
|
+
fi
|
|
219
234
|
if [ -n "$KB_REMOTE" ]; then
|
|
220
|
-
git ls-remote "$KB_REMOTE" >/dev/null 2>&1
|
|
221
|
-
|
|
235
|
+
git ls-remote "$KB_REMOTE" >/dev/null 2>&1 \
|
|
236
|
+
|| say " ! warning: cannot reach KB remote '$KB_REMOTE' right now (recording anyway; pull/push will need it)."
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
[ "$DRY" = 1 ] && { act "write .claude/kb-config.json (path: $KB_PATH)"; return 0; }
|
|
222
240
|
mkdir -p "$DEST/.claude"
|
|
223
|
-
|
|
224
|
-
say " wrote .claude/kb-config.json"
|
|
241
|
+
node -e "require('fs').writeFileSync(process.argv[1],JSON.stringify({path:process.argv[2],remote:process.argv[3]},null,2)+'\n')" "$cfg" "$KB_PATH" "$KB_REMOTE"
|
|
242
|
+
say " wrote .claude/kb-config.json (path: $KB_PATH${KB_REMOTE:+, remote: $KB_REMOTE})"
|
|
225
243
|
}
|
|
244
|
+
|
|
226
245
|
configure_kb
|
|
227
246
|
|
|
228
247
|
# ---- finalize: stamp the installed manifest (copy the new one)
|