@getpawl/setup 1.0.1 → 1.1.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 (2) hide show
  1. package/dist/index.js +439 -13
  2. package/package.json +3 -1
package/dist/index.js CHANGED
@@ -5,13 +5,28 @@
5
5
  var import_node_fs = require("fs");
6
6
  var import_node_path = require("path");
7
7
  function main() {
8
- const encoded = process.argv[2];
9
- if (!encoded) {
10
- console.error(
11
- "Usage: npx @getpawl/setup <PROJECT_KEY>\n\nGet your project key from the Pawl settings page."
12
- );
13
- process.exit(1);
8
+ const arg = process.argv[2];
9
+ if (!arg) {
10
+ printUsage();
11
+ process.exit(0);
12
+ }
13
+ if (arg === "init") {
14
+ const key = process.argv[3];
15
+ pawlInit(key);
16
+ } else {
17
+ legacySetup(arg);
14
18
  }
19
+ }
20
+ function printUsage() {
21
+ console.log(`
22
+ Usage:
23
+ pawl init [PROJECT_KEY] Initialize Pawl in this repo
24
+ pawl-setup <PROJECT_KEY> Legacy setup (still supported)
25
+
26
+ Get your project key at: https://pawl.dev/settings
27
+ `);
28
+ }
29
+ function legacySetup(encoded) {
15
30
  let config;
16
31
  try {
17
32
  const decoded = Buffer.from(encoded, "base64").toString("utf-8");
@@ -38,6 +53,411 @@ function main() {
38
53
  console.log(" Created: .agentmap/.env, .agentmap/sync.sh, .agentmap/parse-cc-session.js");
39
54
  console.log(" Updated: .claude/settings.json, CLAUDE.md, .gitignore\n");
40
55
  }
56
+ function pawlInit(key) {
57
+ const cwd = process.cwd();
58
+ const detected = detectAgents(cwd);
59
+ migrateIfNeeded(cwd, detected.hasAgentMapDir);
60
+ (0, import_node_fs.mkdirSync)((0, import_node_path.join)(cwd, ".pawl"), { recursive: true });
61
+ if (key) {
62
+ let config;
63
+ try {
64
+ const decoded = Buffer.from(key, "base64").toString("utf-8");
65
+ config = JSON.parse(decoded);
66
+ } catch {
67
+ console.error("Error: Invalid project key \u2014 could not decode.");
68
+ process.exit(1);
69
+ }
70
+ writePawlEnvFile(cwd, config);
71
+ }
72
+ writePawlSyncScript(cwd);
73
+ copyParserScript(cwd, ".pawl");
74
+ if (detected.hasCC) {
75
+ mergeClaudeSettings(cwd, ".pawl");
76
+ }
77
+ if (detected.hasGemini) {
78
+ mergeGeminiSettings(cwd);
79
+ }
80
+ writeGitHook(cwd, detected.hasGitDir);
81
+ writePawlClaudeMd(cwd);
82
+ writeAgentsMd(cwd);
83
+ updateGitignore(cwd);
84
+ printSummary(detected, !!key);
85
+ }
86
+ function detectAgents(cwd) {
87
+ return {
88
+ hasCC: (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".claude")),
89
+ hasGemini: (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".gemini")),
90
+ hasAgentMapDir: (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".agentmap")),
91
+ hasGitDir: (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".git"))
92
+ };
93
+ }
94
+ function migrateIfNeeded(cwd, hasAgentMapDir) {
95
+ if (!hasAgentMapDir) return;
96
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".pawl"))) return;
97
+ console.log(" Migrating .agentmap/ \u2192 .pawl/...");
98
+ (0, import_node_fs.mkdirSync)((0, import_node_path.join)(cwd, ".pawl"), { recursive: true });
99
+ const filesToCopy = [".env", "sync.sh", "parse-cc-session.js", "context.md", "progress.md"];
100
+ for (const file of filesToCopy) {
101
+ const src = (0, import_node_path.join)(cwd, ".agentmap", file);
102
+ if ((0, import_node_fs.existsSync)(src)) {
103
+ (0, import_node_fs.copyFileSync)(src, (0, import_node_path.join)(cwd, ".pawl", file));
104
+ }
105
+ }
106
+ const specsDir = (0, import_node_path.join)(cwd, ".agentmap", "specs");
107
+ if ((0, import_node_fs.existsSync)(specsDir)) {
108
+ (0, import_node_fs.cpSync)(specsDir, (0, import_node_path.join)(cwd, ".pawl", "specs"), { recursive: true });
109
+ }
110
+ const envPath = (0, import_node_path.join)(cwd, ".pawl", ".env");
111
+ if ((0, import_node_fs.existsSync)(envPath)) {
112
+ let envContent = (0, import_node_fs.readFileSync)(envPath, "utf-8");
113
+ envContent = envContent.replace(/AGENTMAP_/g, "PAWL_");
114
+ (0, import_node_fs.writeFileSync)(envPath, envContent, "utf-8");
115
+ }
116
+ console.log(" .agentmap/ preserved \u2014 safe to delete manually when ready.\n");
117
+ }
118
+ function writePawlEnvFile(cwd, config) {
119
+ const content = [
120
+ `PAWL_API_KEY=${config.apiKey}`,
121
+ `PAWL_PROJECT_ID=${config.projectId}`,
122
+ `PAWL_API_URL=${config.apiUrl}`,
123
+ ""
124
+ ].join("\n");
125
+ (0, import_node_fs.writeFileSync)((0, import_node_path.join)(cwd, ".pawl", ".env"), content, "utf-8");
126
+ }
127
+ function writePawlSyncScript(cwd) {
128
+ const script = `#!/usr/bin/env bash
129
+ set -euo pipefail
130
+
131
+ # Pawl sync script \u2014 generated by pawl init
132
+ # Do not edit manually; re-run pawl init to update.
133
+
134
+ SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
135
+
136
+ # Source env only if it exists (pawl init without key skips .env)
137
+ if [ -f "$SCRIPT_DIR/.env" ]; then
138
+ source "$SCRIPT_DIR/.env"
139
+ fi
140
+
141
+ BASE_URL="\${PAWL_API_URL:-}/api/projects/\${PAWL_PROJECT_ID:-}"
142
+
143
+ # \u2500\u2500 pull mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
144
+ pull() {
145
+ if [ -z "\${PAWL_API_KEY:-}" ]; then
146
+ echo "Warning: PAWL_API_KEY not set \u2014 sync disabled" >&2
147
+ if [ -f "$SCRIPT_DIR/context.md" ]; then
148
+ cat "$SCRIPT_DIR/context.md"
149
+ fi
150
+ return 0
151
+ fi
152
+
153
+ # Recovery: if tracked-files exist from a previous incomplete session, push first
154
+ if [ -f "$SCRIPT_DIR/.tracked-files" ]; then
155
+ push >/dev/null 2>&1
156
+ fi
157
+
158
+ # Ensure specs directory exists
159
+ mkdir -p "$SCRIPT_DIR/specs"
160
+
161
+ # \u2500\u2500 Level 1: New file-based endpoint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
162
+ RESPONSE=$(curl -sf -X GET "$BASE_URL/context/files" \\
163
+ -H "Authorization: Bearer $PAWL_API_KEY" \\
164
+ -H "Accept: application/json" 2>/dev/null) || true
165
+
166
+ if [ -n "$RESPONSE" ] && echo "$RESPONSE" | jq -e '.files' >/dev/null 2>&1; then
167
+ echo "$RESPONSE" | jq -r '.files[] | @base64' | while read -r encoded; do
168
+ FILE_PATH=$(echo "$encoded" | base64 -d | jq -r '.path')
169
+ FILE_CONTENT=$(echo "$encoded" | base64 -d | jq -r '.content')
170
+
171
+ # Ensure parent directory exists
172
+ mkdir -p "$SCRIPT_DIR/$(dirname "$FILE_PATH")"
173
+ echo "$FILE_CONTENT" > "$SCRIPT_DIR/$FILE_PATH"
174
+ done
175
+
176
+ # Output context.md to stdout (for SessionStart hook)
177
+ if [ -f "$SCRIPT_DIR/context.md" ]; then
178
+ cat "$SCRIPT_DIR/context.md"
179
+ fi
180
+ return 0
181
+ fi
182
+
183
+ # \u2500\u2500 Level 2: Legacy monolithic endpoint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
184
+ RESPONSE=$(curl -sf -X GET "$BASE_URL/context" \\
185
+ -H "Authorization: Bearer $PAWL_API_KEY" \\
186
+ -H "Accept: application/json" 2>/dev/null) || true
187
+
188
+ if [ -n "$RESPONSE" ] && echo "$RESPONSE" | jq -e '.formatted_context' >/dev/null 2>&1; then
189
+ echo "$RESPONSE" | jq -r '.formatted_context // empty' > "$SCRIPT_DIR/context.md"
190
+ cat "$SCRIPT_DIR/context.md"
191
+ return 0
192
+ fi
193
+
194
+ # \u2500\u2500 Level 3: Cached file fallback \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
195
+ if [ -f "$SCRIPT_DIR/context.md" ]; then
196
+ echo "# (cached \u2014 API unreachable)" | cat - "$SCRIPT_DIR/context.md"
197
+ return 0
198
+ fi
199
+
200
+ echo "Warning: Pawl context unavailable" >&2
201
+ return 1
202
+ }
203
+
204
+ # \u2500\u2500 push mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
205
+ push() {
206
+ if [ -z "\${PAWL_API_KEY:-}" ]; then
207
+ return 0
208
+ fi
209
+
210
+ LAST_SHA=""
211
+ if [ -f "$SCRIPT_DIR/.last-sync-sha" ]; then
212
+ LAST_SHA=$(cat "$SCRIPT_DIR/.last-sync-sha")
213
+ fi
214
+
215
+ CURRENT_SHA=$(git rev-parse HEAD 2>/dev/null || echo "")
216
+ BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
217
+
218
+ # Compute diff
219
+ if [ -n "$LAST_SHA" ]; then
220
+ DIFF=$(git diff "$LAST_SHA"..HEAD 2>/dev/null || echo "")
221
+ else
222
+ DIFF=$(git diff HEAD~1..HEAD 2>/dev/null || echo "")
223
+ fi
224
+
225
+ # Collect tracked files (dedup)
226
+ FILES_CHANGED="[]"
227
+ if [ -f "$SCRIPT_DIR/.tracked-files" ]; then
228
+ FILES_CHANGED=$(sort -u "$SCRIPT_DIR/.tracked-files" | jq -R . | jq -s .)
229
+ fi
230
+
231
+ # Commit info
232
+ COMMIT_MSG=$(git log -1 --pretty=format:"%s" 2>/dev/null || echo "")
233
+
234
+ # Session ID from Claude Code (if available)
235
+ SESSION_ID="\${CLAUDE_SESSION_ID:-}"
236
+
237
+ # Repo root for CC session lookup
238
+ REPO_PATH=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
239
+
240
+ # Parse CC session data locally (if node + parser available)
241
+ CC_SESSION="{}"
242
+ if command -v node &> /dev/null; then
243
+ PARSER="$SCRIPT_DIR/parse-cc-session.js"
244
+ if [ -f "$PARSER" ]; then
245
+ CC_SESSION=$(node "$PARSER" "$REPO_PATH" 2>/dev/null || echo "{}")
246
+ fi
247
+ fi
248
+
249
+ # Extract cc_tasks from parser output
250
+ CC_TASKS=$(echo "$CC_SESSION" | jq -c '.tasks // []')
251
+
252
+ PAYLOAD=$(jq -n \\
253
+ --arg diff "$DIFF" \\
254
+ --arg branch "$BRANCH" \\
255
+ --arg commit_sha "$CURRENT_SHA" \\
256
+ --arg commit_message "$COMMIT_MSG" \\
257
+ --arg session_id "$SESSION_ID" \\
258
+ --arg last_sync_sha "$LAST_SHA" \\
259
+ --arg repo_path "$REPO_PATH" \\
260
+ --argjson files_changed "$FILES_CHANGED" \\
261
+ --argjson cc_session "$CC_SESSION" \\
262
+ --argjson cc_tasks "$CC_TASKS" \\
263
+ '{
264
+ diff: $diff,
265
+ branch: $branch,
266
+ commit_sha: $commit_sha,
267
+ commit_message: $commit_message,
268
+ session_id: $session_id,
269
+ last_sync_sha: $last_sync_sha,
270
+ repo_path: $repo_path,
271
+ files_changed: $files_changed,
272
+ cc_session: $cc_session,
273
+ cc_tasks: $cc_tasks
274
+ }')
275
+
276
+ RESPONSE=$(curl -sf -X POST "$BASE_URL/sync" \\
277
+ -H "Authorization: Bearer $PAWL_API_KEY" \\
278
+ -H "Content-Type: application/json" \\
279
+ -d "$PAYLOAD")
280
+
281
+ echo "$RESPONSE" | jq .
282
+
283
+ # Update last sync SHA
284
+ echo "$CURRENT_SHA" > "$SCRIPT_DIR/.last-sync-sha"
285
+
286
+ # Cleanup tracked files
287
+ rm -f "$SCRIPT_DIR/.tracked-files"
288
+ }
289
+
290
+ # \u2500\u2500 dispatch \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
291
+ case "\${1:-}" in
292
+ pull) pull ;;
293
+ push) push ;;
294
+ *)
295
+ echo "Usage: sync.sh [pull|push]"
296
+ exit 1
297
+ ;;
298
+ esac
299
+ `;
300
+ const scriptPath = (0, import_node_path.join)(cwd, ".pawl", "sync.sh");
301
+ (0, import_node_fs.writeFileSync)(scriptPath, script, "utf-8");
302
+ (0, import_node_fs.chmodSync)(scriptPath, 493);
303
+ }
304
+ function mergeGeminiSettings(cwd) {
305
+ const geminiDir = (0, import_node_path.join)(cwd, ".gemini");
306
+ (0, import_node_fs.mkdirSync)(geminiDir, { recursive: true });
307
+ const settingsPath = (0, import_node_path.join)(geminiDir, "settings.json");
308
+ let settings = {};
309
+ if ((0, import_node_fs.existsSync)(settingsPath)) {
310
+ try {
311
+ settings = JSON.parse((0, import_node_fs.readFileSync)(settingsPath, "utf-8"));
312
+ } catch {
313
+ }
314
+ }
315
+ const hooksToInstall = {
316
+ SessionStart: { command: ".pawl/sync.sh pull" },
317
+ AfterAgentLoop: { command: ".pawl/sync.sh push" }
318
+ };
319
+ const hooksObj = settings.hooks || {};
320
+ for (const [event, entry] of Object.entries(hooksToInstall)) {
321
+ const existing = hooksObj[event] || [];
322
+ const alreadyExists = existing.some((e) => e.command === entry.command);
323
+ if (!alreadyExists) {
324
+ existing.push(entry);
325
+ }
326
+ hooksObj[event] = existing;
327
+ }
328
+ settings.hooks = hooksObj;
329
+ (0, import_node_fs.writeFileSync)(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
330
+ }
331
+ function writeGitHook(cwd, hasGitDir) {
332
+ if (!hasGitDir) return;
333
+ const hooksDir = (0, import_node_path.join)(cwd, ".git", "hooks");
334
+ (0, import_node_fs.mkdirSync)(hooksDir, { recursive: true });
335
+ const hookPath = (0, import_node_path.join)(hooksDir, "post-commit");
336
+ const hookBlock = `#!/bin/sh
337
+ # Pawl sync hook \u2014 fires after every commit
338
+ # Provides universal sync fallback for agents without lifecycle hooks (Codex, etc.)
339
+ if [ -f ".pawl/sync.sh" ]; then
340
+ .pawl/sync.sh push
341
+ fi`;
342
+ if (!(0, import_node_fs.existsSync)(hookPath)) {
343
+ (0, import_node_fs.writeFileSync)(hookPath, hookBlock + "\n", "utf-8");
344
+ (0, import_node_fs.chmodSync)(hookPath, 493);
345
+ return;
346
+ }
347
+ const content = (0, import_node_fs.readFileSync)(hookPath, "utf-8");
348
+ if (content.includes("Pawl sync hook")) {
349
+ return;
350
+ }
351
+ (0, import_node_fs.writeFileSync)(hookPath, content.trimEnd() + "\n\n" + hookBlock + "\n", "utf-8");
352
+ (0, import_node_fs.chmodSync)(hookPath, 493);
353
+ }
354
+ function writePawlClaudeMd(cwd) {
355
+ const claudeMdPath = (0, import_node_path.join)(cwd, "CLAUDE.md");
356
+ const PAWL_START = "<!-- pawl:start -->";
357
+ const PAWL_END = "<!-- pawl:end -->";
358
+ const AGENTMAP_START = "<!-- agentmap:start -->";
359
+ const AGENTMAP_END = "<!-- agentmap:end -->";
360
+ const block = [
361
+ PAWL_START,
362
+ "# Pawl Context",
363
+ "",
364
+ "This project uses Pawl for spec management and AI session tracking.",
365
+ "",
366
+ "Context files are in `.pawl/`:",
367
+ "- `.pawl/context.md` \u2014 project index, spec map, health",
368
+ "- `.pawl/specs/` \u2014 individual spec files (one per feature area)",
369
+ "- `.pawl/progress.md` \u2014 last session summary",
370
+ "",
371
+ "Read `.pawl/context.md` at session start, then load relevant specs from `.pawl/specs/` based on the task.",
372
+ PAWL_END
373
+ ].join("\n");
374
+ if (!(0, import_node_fs.existsSync)(claudeMdPath)) {
375
+ (0, import_node_fs.writeFileSync)(claudeMdPath, block + "\n", "utf-8");
376
+ return;
377
+ }
378
+ let content = (0, import_node_fs.readFileSync)(claudeMdPath, "utf-8");
379
+ let startIdx = content.indexOf(PAWL_START);
380
+ let endIdx = content.indexOf(PAWL_END);
381
+ let endMarkerLen = PAWL_END.length;
382
+ if (startIdx === -1 || endIdx === -1) {
383
+ startIdx = content.indexOf(AGENTMAP_START);
384
+ endIdx = content.indexOf(AGENTMAP_END);
385
+ endMarkerLen = AGENTMAP_END.length;
386
+ }
387
+ if (startIdx !== -1 && endIdx !== -1) {
388
+ content = content.slice(0, startIdx) + block + content.slice(endIdx + endMarkerLen);
389
+ } else {
390
+ content = content.trimEnd() + "\n\n" + block + "\n";
391
+ }
392
+ (0, import_node_fs.writeFileSync)(claudeMdPath, content, "utf-8");
393
+ }
394
+ function writeAgentsMd(cwd) {
395
+ const agentsMdPath = (0, import_node_path.join)(cwd, "AGENTS.md");
396
+ const START_MARKER = "<!-- pawl:start -->";
397
+ const END_MARKER = "<!-- pawl:end -->";
398
+ const block = [
399
+ START_MARKER,
400
+ "## Pawl Project Context",
401
+ "",
402
+ "This project uses Pawl for spec management and AI session tracking.",
403
+ "Structured context files are available at:",
404
+ "",
405
+ "- `.pawl/context.md` \u2014 project index, spec map, health overview",
406
+ "- `.pawl/specs/` \u2014 individual spec files (one per feature area)",
407
+ "- `.pawl/progress.md` \u2014 last session summary, recent decisions",
408
+ "",
409
+ "**Start here**: Read `.pawl/context.md` first, then load relevant",
410
+ "spec files from `.pawl/specs/` based on the task at hand.",
411
+ END_MARKER
412
+ ].join("\n");
413
+ if (!(0, import_node_fs.existsSync)(agentsMdPath)) {
414
+ (0, import_node_fs.writeFileSync)(agentsMdPath, block + "\n", "utf-8");
415
+ return;
416
+ }
417
+ let content = (0, import_node_fs.readFileSync)(agentsMdPath, "utf-8");
418
+ const startIdx = content.indexOf(START_MARKER);
419
+ const endIdx = content.indexOf(END_MARKER);
420
+ if (startIdx !== -1 && endIdx !== -1) {
421
+ content = content.slice(0, startIdx) + block + content.slice(endIdx + END_MARKER.length);
422
+ } else {
423
+ content = content.trimEnd() + "\n\n" + block + "\n";
424
+ }
425
+ (0, import_node_fs.writeFileSync)(agentsMdPath, content, "utf-8");
426
+ }
427
+ function printSummary(detected, hasKey) {
428
+ console.log("\n Pawl initialized.\n");
429
+ console.log(" Detected agents:");
430
+ if (detected.hasCC) {
431
+ console.log(" Claude Code \u2713 hooks installed (.claude/settings.json)");
432
+ }
433
+ if (detected.hasGemini) {
434
+ console.log(" Gemini CLI \u2713 hooks installed (.gemini/settings.json)");
435
+ }
436
+ console.log(" Codex / other no lifecycle hooks \u2014 git post-commit fallback active");
437
+ console.log("\n Files written:");
438
+ console.log(" .pawl/sync.sh");
439
+ console.log(" .pawl/parse-cc-session.js");
440
+ console.log(" CLAUDE.md (pawl context block updated)");
441
+ console.log(" AGENTS.md (pawl context block \u2014 readable by Codex, Kiro, and others)");
442
+ if (detected.hasGitDir) {
443
+ console.log(" .git/hooks/post-commit (universal sync fallback)");
444
+ }
445
+ console.log(" .gitignore (updated)");
446
+ if (detected.hasGitDir) {
447
+ console.log("\n Note: .git/hooks/post-commit is per-machine \u2014 not committed to the repo.");
448
+ console.log(" Each team member should run: pawl init");
449
+ } else {
450
+ console.log("\n Note: git repo not detected \u2014 post-commit hook skipped.");
451
+ }
452
+ if (!hasKey) {
453
+ console.log("\n Sync is not yet connected to the Pawl dashboard.");
454
+ console.log(" To enable: pawl init <YOUR_PROJECT_KEY>");
455
+ console.log(" Get your key at: https://pawl.dev/settings");
456
+ } else {
457
+ console.log("\n .pawl/.env written \u2014 sync is connected.");
458
+ }
459
+ console.log("");
460
+ }
41
461
  function writeEnvFile(cwd, config) {
42
462
  const dir = (0, import_node_path.join)(cwd, ".agentmap");
43
463
  (0, import_node_fs.mkdirSync)(dir, { recursive: true });
@@ -212,14 +632,14 @@ esac
212
632
  (0, import_node_fs.writeFileSync)(scriptPath, script, "utf-8");
213
633
  (0, import_node_fs.chmodSync)(scriptPath, 493);
214
634
  }
215
- function copyParserScript(cwd) {
635
+ function copyParserScript(cwd, destDir = ".agentmap") {
216
636
  const src = (0, import_node_path.join)(__dirname, "parse-cc-session.js");
217
- const dest = (0, import_node_path.join)(cwd, ".agentmap", "parse-cc-session.js");
637
+ const dest = (0, import_node_path.join)(cwd, destDir, "parse-cc-session.js");
218
638
  if ((0, import_node_fs.existsSync)(src)) {
219
639
  (0, import_node_fs.copyFileSync)(src, dest);
220
640
  }
221
641
  }
222
- function mergeClaudeSettings(cwd) {
642
+ function mergeClaudeSettings(cwd, basePath = ".agentmap") {
223
643
  const claudeDir = (0, import_node_path.join)(cwd, ".claude");
224
644
  (0, import_node_fs.mkdirSync)(claudeDir, { recursive: true });
225
645
  const settingsPath = (0, import_node_path.join)(claudeDir, "settings.json");
@@ -234,7 +654,7 @@ function mergeClaudeSettings(cwd) {
234
654
  SessionStart: [
235
655
  {
236
656
  hooks: [
237
- { type: "command", command: ".agentmap/sync.sh pull" }
657
+ { type: "command", command: `${basePath}/sync.sh pull` }
238
658
  ]
239
659
  }
240
660
  ],
@@ -244,7 +664,7 @@ function mergeClaudeSettings(cwd) {
244
664
  hooks: [
245
665
  {
246
666
  type: "command",
247
- command: "jq -r '.tool_input.file_path' >> .agentmap/.tracked-files"
667
+ command: `jq -r '.tool_input.file_path' >> ${basePath}/.tracked-files`
248
668
  }
249
669
  ]
250
670
  }
@@ -252,7 +672,7 @@ function mergeClaudeSettings(cwd) {
252
672
  Stop: [
253
673
  {
254
674
  hooks: [
255
- { type: "command", command: ".agentmap/sync.sh push" }
675
+ { type: "command", command: `${basePath}/sync.sh push` }
256
676
  ]
257
677
  }
258
678
  ]
@@ -320,7 +740,13 @@ function updateGitignore(cwd) {
320
740
  ".agentmap/.last-sync-sha",
321
741
  ".agentmap/context.md",
322
742
  ".agentmap/specs/",
323
- ".agentmap/progress.md"
743
+ ".agentmap/progress.md",
744
+ ".pawl/.env",
745
+ ".pawl/.tracked-files",
746
+ ".pawl/.last-sync-sha",
747
+ ".pawl/context.md",
748
+ ".pawl/specs/",
749
+ ".pawl/progress.md"
324
750
  ];
325
751
  const missing = toAdd.filter((line) => !lines.includes(line));
326
752
  if (missing.length > 0) {
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "@getpawl/setup",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "type": "commonjs",
5
5
  "description": "One-shot setup for Pawl + Claude Code hooks",
6
6
  "bin": {
7
+ "pawl": "./dist/index.js",
7
8
  "pawl-setup": "./dist/index.js"
8
9
  },
9
10
  "scripts": {
10
11
  "build": "tsup",
12
+ "test": "node --test test/e2e.test.mjs",
11
13
  "lint": "tsc --noEmit",
12
14
  "prepublishOnly": "pnpm build"
13
15
  },