@getpawl/setup 1.4.3 → 1.4.5
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/README.md +15 -1
- package/dist/index.js +228 -14
- package/dist/parse-cc-session.js +84 -39
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Then the `pawl` command will be available globally (`pawl sync`, `pawl connect`,
|
|
|
18
18
|
npx @getpawl/setup init
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Creates `.pawl/` directory with sync scripts, configures Claude Code hooks,
|
|
21
|
+
Sets up Pawl in this repo — works for both new and existing projects. Creates `.pawl/` directory with sync scripts, configures Claude Code hooks, sets up git post-commit hook, and pulls existing context if available.
|
|
22
22
|
|
|
23
23
|
### 2. Connect
|
|
24
24
|
|
|
@@ -37,6 +37,20 @@ pawl sync --pull # pull latest context from dashboard
|
|
|
37
37
|
|
|
38
38
|
Sync happens automatically via hooks. Use these commands for manual sync or agents without lifecycle hooks (Codex, etc.).
|
|
39
39
|
|
|
40
|
+
## Joining an existing Pawl project
|
|
41
|
+
|
|
42
|
+
If your team already uses Pawl, you don't need to bootstrap — just pull:
|
|
43
|
+
|
|
44
|
+
1. Clone the repo
|
|
45
|
+
2. Copy `.env.example` to `.pawl/.env` and add your API key and project ID
|
|
46
|
+
— get these from [app.getpawl.dev](https://app.getpawl.dev) → your project → Settings
|
|
47
|
+
3. Run `npx @getpawl/setup init`
|
|
48
|
+
4. Run `npx @getpawl/setup status` to confirm everything is connected
|
|
49
|
+
5. Start a Claude Code session — full context is ready
|
|
50
|
+
|
|
51
|
+
> `pawl init` detects existing specs and skips bootstrap automatically.
|
|
52
|
+
> Pawl currently supports one project per repo. Multi-project monorepo support is planned.
|
|
53
|
+
|
|
40
54
|
## Legacy Setup
|
|
41
55
|
|
|
42
56
|
Existing users can still use the one-step flow:
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,8 @@ async function main() {
|
|
|
22
22
|
const noConnect = rest.includes("--no-connect");
|
|
23
23
|
const key = rest.find((a) => !a.startsWith("--"));
|
|
24
24
|
await pawlInit(key, { noConnect });
|
|
25
|
+
} else if (arg === "status") {
|
|
26
|
+
await pawlStatus();
|
|
25
27
|
} else if (arg === "migrate") {
|
|
26
28
|
pawlMigrate();
|
|
27
29
|
} else {
|
|
@@ -33,9 +35,10 @@ function printUsage() {
|
|
|
33
35
|
Pawl \u2014 project visibility layer for AI-native development
|
|
34
36
|
|
|
35
37
|
Usage:
|
|
36
|
-
pawl init Set up Pawl in this repo
|
|
38
|
+
pawl init Set up Pawl in this repo (new or existing project)
|
|
37
39
|
pawl connect Link this repo to your Pawl dashboard
|
|
38
40
|
pawl sync [--pull] Push session data to dashboard (or pull context)
|
|
41
|
+
pawl status Check project health and connectivity
|
|
39
42
|
`);
|
|
40
43
|
}
|
|
41
44
|
function pawlSync(flag) {
|
|
@@ -60,19 +63,21 @@ function pawlSync(flag) {
|
|
|
60
63
|
}
|
|
61
64
|
function pawlMigrate() {
|
|
62
65
|
const cwd = process.cwd();
|
|
63
|
-
const
|
|
64
|
-
if (!(0, import_node_fs.existsSync)(
|
|
66
|
+
const pawlDir = (0, import_node_path.join)(cwd, ".pawl");
|
|
67
|
+
if (!(0, import_node_fs.existsSync)((0, import_node_path.join)(pawlDir, "sync.sh")) && !(0, import_node_fs.existsSync)((0, import_node_path.join)(pawlDir, "sync.mjs"))) {
|
|
65
68
|
console.log(" Nothing to migrate \u2014 run `pawl init` first.");
|
|
66
69
|
process.exit(0);
|
|
67
70
|
}
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
const syncPath = (0, import_node_path.join)(pawlDir, "sync.sh");
|
|
72
|
+
if ((0, import_node_fs.existsSync)(syncPath)) {
|
|
73
|
+
let content = (0, import_node_fs.readFileSync)(syncPath, "utf-8");
|
|
74
|
+
if (content.includes("AGENTMAP_")) {
|
|
75
|
+
content = content.replace(/AGENTMAP_API_URL/g, "PAWL_API_URL").replace(/AGENTMAP_PROJECT_ID/g, "PAWL_PROJECT_ID").replace(/AGENTMAP_API_KEY/g, "PAWL_API_KEY");
|
|
76
|
+
(0, import_node_fs.writeFileSync)(syncPath, content, "utf-8");
|
|
77
|
+
console.log(" .pawl/sync.sh migrated to PAWL_* vars.");
|
|
78
|
+
}
|
|
72
79
|
}
|
|
73
|
-
|
|
74
|
-
(0, import_node_fs.writeFileSync)(syncPath, content, "utf-8");
|
|
75
|
-
const envPath = (0, import_node_path.join)(cwd, ".pawl", ".env");
|
|
80
|
+
const envPath = (0, import_node_path.join)(pawlDir, ".env");
|
|
76
81
|
if ((0, import_node_fs.existsSync)(envPath)) {
|
|
77
82
|
let envContent = (0, import_node_fs.readFileSync)(envPath, "utf-8");
|
|
78
83
|
if (envContent.includes("AGENTMAP_")) {
|
|
@@ -81,14 +86,21 @@ function pawlMigrate() {
|
|
|
81
86
|
console.log(" .pawl/.env also migrated.");
|
|
82
87
|
}
|
|
83
88
|
}
|
|
84
|
-
if (!(0, import_node_fs.existsSync)((0, import_node_path.join)(
|
|
89
|
+
if (!(0, import_node_fs.existsSync)((0, import_node_path.join)(pawlDir, "sync.mjs"))) {
|
|
85
90
|
writePawlSyncMjs(cwd);
|
|
86
91
|
}
|
|
87
|
-
if (!(0, import_node_fs.existsSync)((0, import_node_path.join)(
|
|
92
|
+
if (!(0, import_node_fs.existsSync)((0, import_node_path.join)(pawlDir, "track-file.mjs"))) {
|
|
88
93
|
writeTrackFileScript(cwd);
|
|
89
94
|
}
|
|
90
95
|
mergeClaudeSettings(cwd, ".pawl");
|
|
91
|
-
|
|
96
|
+
writePawlClaudeMd(cwd);
|
|
97
|
+
writeAgentsMd(cwd);
|
|
98
|
+
const oldDecisions = (0, import_node_path.join)(pawlDir, "decisions.json");
|
|
99
|
+
if ((0, import_node_fs.existsSync)(oldDecisions)) {
|
|
100
|
+
console.log(" \u26A0 Found .pawl/decisions.json (old format). Per-decision files are now in .pawl/decisions/.");
|
|
101
|
+
console.log(" Safe to delete .pawl/decisions.json after confirming decisions/ is populated.");
|
|
102
|
+
}
|
|
103
|
+
console.log(" Migration complete.");
|
|
92
104
|
}
|
|
93
105
|
function generateCode() {
|
|
94
106
|
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
@@ -340,12 +352,20 @@ async function pawlInit(key, opts) {
|
|
|
340
352
|
writeAgentsMd(cwd);
|
|
341
353
|
updateGitignore(cwd);
|
|
342
354
|
printSummary(detected, !!key, !!opts?.noConnect);
|
|
355
|
+
if (opts?.noConnect && !(0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".pawl", ".env"))) {
|
|
356
|
+
console.log("\n No API key found. Add PAWL_API_KEY to .pawl/.env (see .env.example) then run pawl init again.");
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
343
359
|
let config = null;
|
|
344
360
|
if (!key && !opts?.noConnect) {
|
|
345
361
|
console.log("\nConnecting to Pawl dashboard...\n");
|
|
346
362
|
config = await pawlConnect();
|
|
347
363
|
}
|
|
348
|
-
|
|
364
|
+
const specsDir = (0, import_node_path.join)(cwd, ".pawl", "specs");
|
|
365
|
+
const hasExistingSpecs = (0, import_node_fs.existsSync)(specsDir) && (0, import_node_fs.readdirSync)(specsDir).some((f) => f.endsWith(".md"));
|
|
366
|
+
if (hasExistingSpecs) {
|
|
367
|
+
console.log(" Existing specs found \u2014 skipping bootstrap");
|
|
368
|
+
} else if (config) {
|
|
349
369
|
const repoUrl = getRepoUrl(cwd);
|
|
350
370
|
if (repoUrl) {
|
|
351
371
|
console.log("Scanning your repo to generate specs...");
|
|
@@ -358,6 +378,164 @@ async function pawlInit(key, opts) {
|
|
|
358
378
|
}
|
|
359
379
|
}
|
|
360
380
|
}
|
|
381
|
+
const envPath = (0, import_node_path.join)(cwd, ".pawl", ".env");
|
|
382
|
+
if ((0, import_node_fs.existsSync)(envPath)) {
|
|
383
|
+
console.log(" Pulling latest context from API...");
|
|
384
|
+
try {
|
|
385
|
+
pawlSync("--pull");
|
|
386
|
+
console.log(" \u2713 Context ready");
|
|
387
|
+
} catch {
|
|
388
|
+
console.warn(" \u26A0 Could not pull context \u2014 run `pawl sync --pull` manually");
|
|
389
|
+
}
|
|
390
|
+
try {
|
|
391
|
+
const sha = (0, import_node_child_process.execSync)("git rev-parse HEAD", { cwd, encoding: "utf-8" }).trim();
|
|
392
|
+
(0, import_node_fs.writeFileSync)((0, import_node_path.join)(cwd, ".pawl", ".last-sync-sha"), sha, "utf-8");
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
try {
|
|
397
|
+
const pawlEnv = readPawlEnvFromFile(cwd);
|
|
398
|
+
if (pawlEnv.apiKey && pawlEnv.projectId) {
|
|
399
|
+
const baseUrl = `${pawlEnv.apiUrl}/api/projects/${pawlEnv.projectId}`;
|
|
400
|
+
const res = await fetch(`${baseUrl}/decisions/export`, {
|
|
401
|
+
headers: { Authorization: `Bearer ${pawlEnv.apiKey}`, Accept: "application/json" }
|
|
402
|
+
});
|
|
403
|
+
if (res.ok) {
|
|
404
|
+
const data = await res.json();
|
|
405
|
+
if (data.decisions?.length) {
|
|
406
|
+
const decisionsDir = (0, import_node_path.join)(cwd, ".pawl", "decisions");
|
|
407
|
+
(0, import_node_fs.mkdirSync)(decisionsDir, { recursive: true });
|
|
408
|
+
let written = 0;
|
|
409
|
+
for (const d of data.decisions) {
|
|
410
|
+
const fp = (0, import_node_path.join)(decisionsDir, `${d.id}.json`);
|
|
411
|
+
if ((0, import_node_fs.existsSync)(fp)) continue;
|
|
412
|
+
(0, import_node_fs.writeFileSync)(fp, JSON.stringify(d, null, 2) + "\n", "utf-8");
|
|
413
|
+
written++;
|
|
414
|
+
}
|
|
415
|
+
if (written > 0) console.log(` \u2713 Seeded ${written} decision${written > 1 ? "s" : ""}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
} catch {
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function readPawlEnvFromFile(cwd) {
|
|
423
|
+
const envPath = (0, import_node_path.join)(cwd, ".pawl", ".env");
|
|
424
|
+
if (!(0, import_node_fs.existsSync)(envPath)) return {};
|
|
425
|
+
const vars = {};
|
|
426
|
+
for (const line of (0, import_node_fs.readFileSync)(envPath, "utf-8").split("\n")) {
|
|
427
|
+
const trimmed = line.trim();
|
|
428
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
429
|
+
const eq = trimmed.indexOf("=");
|
|
430
|
+
if (eq === -1) continue;
|
|
431
|
+
vars[trimmed.slice(0, eq)] = trimmed.slice(eq + 1);
|
|
432
|
+
}
|
|
433
|
+
return {
|
|
434
|
+
apiKey: vars.PAWL_API_KEY || "",
|
|
435
|
+
projectId: vars.PAWL_PROJECT_ID || "",
|
|
436
|
+
apiUrl: vars.PAWL_API_URL || DEFAULT_API_URL
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
async function pawlStatus() {
|
|
440
|
+
const cwd = process.cwd();
|
|
441
|
+
const pawlEnv = readPawlEnvFromFile(cwd);
|
|
442
|
+
const apiKey = pawlEnv.apiKey || "";
|
|
443
|
+
const projectId = pawlEnv.projectId || "";
|
|
444
|
+
const apiUrl = pawlEnv.apiUrl || DEFAULT_API_URL;
|
|
445
|
+
console.log("\n pawl status\n");
|
|
446
|
+
if (!apiKey) {
|
|
447
|
+
console.log(" \u2717 PAWL_API_KEY not set");
|
|
448
|
+
console.log(" Fix: pawl connect\n");
|
|
449
|
+
process.exit(1);
|
|
450
|
+
}
|
|
451
|
+
console.log(" \u2713 PAWL_API_KEY set");
|
|
452
|
+
if (!projectId) {
|
|
453
|
+
console.log(" \u2717 PAWL_PROJECT_ID not set");
|
|
454
|
+
console.log(" Fix: pawl connect\n");
|
|
455
|
+
process.exit(1);
|
|
456
|
+
}
|
|
457
|
+
console.log(" \u2713 PAWL_PROJECT_ID set");
|
|
458
|
+
try {
|
|
459
|
+
const res = await fetch(`${apiUrl}/api/projects/${projectId}/context`, {
|
|
460
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
461
|
+
});
|
|
462
|
+
if (res.ok) {
|
|
463
|
+
console.log(` \u2713 API connected (${apiUrl})`);
|
|
464
|
+
} else {
|
|
465
|
+
console.log(` \u2717 API returned ${res.status}`);
|
|
466
|
+
process.exit(1);
|
|
467
|
+
}
|
|
468
|
+
} catch {
|
|
469
|
+
console.log(" \u2717 API unreachable");
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
const lastSyncPath = (0, import_node_path.join)(cwd, ".pawl", ".last-sync-sha");
|
|
473
|
+
if (!(0, import_node_fs.existsSync)(lastSyncPath)) {
|
|
474
|
+
console.log(" \u25CB Never synced");
|
|
475
|
+
console.log(" Fix: pawl sync --pull");
|
|
476
|
+
} else {
|
|
477
|
+
const stat = (0, import_node_fs.statSync)(lastSyncPath);
|
|
478
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
479
|
+
const ageHrs = Math.floor(ageMs / 36e5);
|
|
480
|
+
const sha = (0, import_node_fs.readFileSync)(lastSyncPath, "utf-8").trim().slice(0, 7);
|
|
481
|
+
if (ageHrs > 24) {
|
|
482
|
+
console.log(` \u26A0 Last sync ${ageHrs}h ago (commit ${sha})`);
|
|
483
|
+
console.log(" Fix: pawl sync --pull");
|
|
484
|
+
} else {
|
|
485
|
+
const ago = ageHrs > 0 ? `${ageHrs}h ago` : `${Math.floor(ageMs / 6e4)}m ago`;
|
|
486
|
+
console.log(` \u2713 Last sync ${ago} (commit ${sha})`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
console.log("");
|
|
490
|
+
const specsDir = (0, import_node_path.join)(cwd, ".pawl", "specs");
|
|
491
|
+
if ((0, import_node_fs.existsSync)(specsDir)) {
|
|
492
|
+
const specFiles = (0, import_node_fs.readdirSync)(specsDir).filter((f) => f.endsWith(".md"));
|
|
493
|
+
console.log(` ${specFiles.length > 0 ? "\u2713" : "\u26A0"} specs/ ${specFiles.length} files`);
|
|
494
|
+
} else {
|
|
495
|
+
console.log(" \u26A0 specs/ missing");
|
|
496
|
+
console.log(" Fix: pawl sync --pull");
|
|
497
|
+
}
|
|
498
|
+
const decisionsDir = (0, import_node_path.join)(cwd, ".pawl", "decisions");
|
|
499
|
+
if ((0, import_node_fs.existsSync)(decisionsDir)) {
|
|
500
|
+
const decFiles = (0, import_node_fs.readdirSync)(decisionsDir).filter((f) => f.endsWith(".json"));
|
|
501
|
+
console.log(` ${decFiles.length > 0 ? "\u2713" : "\u26A0"} decisions/ ${decFiles.length} files`);
|
|
502
|
+
} else {
|
|
503
|
+
console.log(" \u26A0 decisions/ missing");
|
|
504
|
+
console.log(" Fix: pawl sync --pull");
|
|
505
|
+
}
|
|
506
|
+
console.log("");
|
|
507
|
+
const ccSettingsPath = (0, import_node_path.join)(cwd, ".claude", "settings.json");
|
|
508
|
+
if ((0, import_node_fs.existsSync)(ccSettingsPath)) {
|
|
509
|
+
const ccSettings = (0, import_node_fs.readFileSync)(ccSettingsPath, "utf-8");
|
|
510
|
+
const hasStopHook = ccSettings.includes("sync.mjs") && ccSettings.includes("Stop");
|
|
511
|
+
console.log(` ${hasStopHook ? "\u2713" : "\u2717"} CC stop hook ${hasStopHook ? "installed" : "not installed"}`);
|
|
512
|
+
if (!hasStopHook) console.log(" Fix: pawl init");
|
|
513
|
+
} else {
|
|
514
|
+
console.log(" \u2717 CC stop hook not installed");
|
|
515
|
+
console.log(" Fix: pawl init");
|
|
516
|
+
}
|
|
517
|
+
const hookPath = (0, import_node_path.join)(cwd, ".git", "hooks", "post-commit");
|
|
518
|
+
const hasGitHook = (0, import_node_fs.existsSync)(hookPath);
|
|
519
|
+
console.log(` ${hasGitHook ? "\u2713" : "\u2717"} git post-commit ${hasGitHook ? "installed" : "not installed"}`);
|
|
520
|
+
if (!hasGitHook) console.log(" Fix: pawl init");
|
|
521
|
+
const hasSyncMjs = (0, import_node_fs.existsSync)((0, import_node_path.join)(cwd, ".pawl", "sync.mjs"));
|
|
522
|
+
console.log(` ${hasSyncMjs ? "\u2713" : "\u2717"} sync.mjs ${hasSyncMjs ? "present" : "missing"}`);
|
|
523
|
+
if (!hasSyncMjs) console.log(" Fix: pawl init");
|
|
524
|
+
console.log("");
|
|
525
|
+
try {
|
|
526
|
+
const pkgPath = (0, import_node_path.join)(__dirname, "..", "package.json");
|
|
527
|
+
const localVer = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf-8")).version;
|
|
528
|
+
let latestVer = localVer;
|
|
529
|
+
try {
|
|
530
|
+
latestVer = (0, import_node_child_process.execSync)("npm view @getpawl/setup version", { encoding: "utf-8" }).trim();
|
|
531
|
+
} catch {
|
|
532
|
+
}
|
|
533
|
+
const upToDate = localVer === latestVer;
|
|
534
|
+
console.log(` Package @getpawl/setup v${localVer} (latest: v${latestVer} ${upToDate ? "\u2713" : "\u26A0"})`);
|
|
535
|
+
if (!upToDate) console.log(" Fix: npm install -g @getpawl/setup@latest");
|
|
536
|
+
} catch {
|
|
537
|
+
}
|
|
538
|
+
console.log("");
|
|
361
539
|
}
|
|
362
540
|
function detectAgents(cwd) {
|
|
363
541
|
return {
|
|
@@ -390,6 +568,11 @@ function migrateIfNeeded(cwd, hasAgentMapDir) {
|
|
|
390
568
|
(0, import_node_fs.writeFileSync)(envPath, envContent, "utf-8");
|
|
391
569
|
}
|
|
392
570
|
console.log(" .agentmap/ preserved \u2014 safe to delete manually when ready.\n");
|
|
571
|
+
const oldDecisions = (0, import_node_path.join)(cwd, ".pawl", "decisions.json");
|
|
572
|
+
if ((0, import_node_fs.existsSync)(oldDecisions)) {
|
|
573
|
+
console.log(" \u26A0 Found .pawl/decisions.json (old format). Per-decision files are now in .pawl/decisions/.");
|
|
574
|
+
console.log(" Safe to delete .pawl/decisions.json after confirming decisions/ is populated.");
|
|
575
|
+
}
|
|
393
576
|
}
|
|
394
577
|
function writePawlEnvFile(cwd, config) {
|
|
395
578
|
const content = [
|
|
@@ -636,6 +819,7 @@ async function pull() {
|
|
|
636
819
|
for (const file of data.files) {
|
|
637
820
|
const filePath = join(SCRIPT_DIR, file.path);
|
|
638
821
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
822
|
+
if (file.path.startsWith('decisions/') && existsSync(filePath)) continue;
|
|
639
823
|
writeFileSync(filePath, file.content, 'utf-8');
|
|
640
824
|
}
|
|
641
825
|
const ctxPath = join(SCRIPT_DIR, 'context.md');
|
|
@@ -872,6 +1056,21 @@ function writePawlClaudeMd(cwd) {
|
|
|
872
1056
|
"- `.pawl/decisions.json` \u2014 architectural decisions log (read before starting work)",
|
|
873
1057
|
"",
|
|
874
1058
|
"Read `.pawl/context.md` at session start, then load relevant specs from `.pawl/specs/` based on the task.",
|
|
1059
|
+
"",
|
|
1060
|
+
"## Decision Capture",
|
|
1061
|
+
"",
|
|
1062
|
+
"When you make a non-obvious architectural choice \u2014 picking one approach over alternatives,",
|
|
1063
|
+
"deciding on a data model, choosing a library, or changing direction mid-task \u2014 output this",
|
|
1064
|
+
"block before writing code:",
|
|
1065
|
+
"",
|
|
1066
|
+
"PAWL_INTENT:",
|
|
1067
|
+
"context: <what situation triggered this decision>",
|
|
1068
|
+
"approach: <what was chosen and why>",
|
|
1069
|
+
"alternatives_rejected: <what else was considered>",
|
|
1070
|
+
"constraints: <what limited the options>",
|
|
1071
|
+
"END_PAWL_INTENT",
|
|
1072
|
+
"",
|
|
1073
|
+
"Only use this for significant decisions. Skip for routine implementation, bug fixes, and style changes.",
|
|
875
1074
|
PAWL_END
|
|
876
1075
|
].join("\n");
|
|
877
1076
|
if (!(0, import_node_fs.existsSync)(claudeMdPath)) {
|
|
@@ -912,6 +1111,21 @@ function writeAgentsMd(cwd) {
|
|
|
912
1111
|
"",
|
|
913
1112
|
"**Start here**: Read `.pawl/context.md` first, then load relevant",
|
|
914
1113
|
"spec files from `.pawl/specs/` based on the task at hand.",
|
|
1114
|
+
"",
|
|
1115
|
+
"## Decision Capture",
|
|
1116
|
+
"",
|
|
1117
|
+
"When you make a non-obvious architectural choice \u2014 picking one approach over alternatives,",
|
|
1118
|
+
"deciding on a data model, choosing a library, or changing direction mid-task \u2014 output this",
|
|
1119
|
+
"block before writing code:",
|
|
1120
|
+
"",
|
|
1121
|
+
"PAWL_INTENT:",
|
|
1122
|
+
"context: <what situation triggered this decision>",
|
|
1123
|
+
"approach: <what was chosen and why>",
|
|
1124
|
+
"alternatives_rejected: <what else was considered>",
|
|
1125
|
+
"constraints: <what limited the options>",
|
|
1126
|
+
"END_PAWL_INTENT",
|
|
1127
|
+
"",
|
|
1128
|
+
"Only use this for significant decisions. Skip for routine implementation, bug fixes, and style changes.",
|
|
915
1129
|
END_MARKER
|
|
916
1130
|
].join("\n");
|
|
917
1131
|
if (!(0, import_node_fs.existsSync)(agentsMdPath)) {
|
package/dist/parse-cc-session.js
CHANGED
|
@@ -61,7 +61,10 @@ async function parseSessionFile(filePath) {
|
|
|
61
61
|
let totalOutputTokens = 0;
|
|
62
62
|
let cacheReadTokens = 0;
|
|
63
63
|
let cacheWriteTokens = 0;
|
|
64
|
+
const reasoningDecisions = [];
|
|
65
|
+
let currentMessageIndex = 0;
|
|
64
66
|
for await (const line of rl) {
|
|
67
|
+
currentMessageIndex++;
|
|
65
68
|
let parsed;
|
|
66
69
|
try {
|
|
67
70
|
parsed = JSON.parse(line);
|
|
@@ -88,52 +91,93 @@ async function parseSessionFile(filePath) {
|
|
|
88
91
|
const content = msg.content;
|
|
89
92
|
if (Array.isArray(content)) {
|
|
90
93
|
for (const block of content) {
|
|
91
|
-
if (block.type
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
if (block.type === "tool_use") {
|
|
95
|
+
const fp = block.input?.file_path;
|
|
96
|
+
if (fp) {
|
|
97
|
+
if (block.name === "Read") {
|
|
98
|
+
filesRead.add(fp);
|
|
99
|
+
} else if (block.name === "Write" || block.name === "Edit") {
|
|
100
|
+
filesWritten.add(fp);
|
|
101
|
+
}
|
|
98
102
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
103
|
+
if (block.name === "TaskCreate") {
|
|
104
|
+
pendingCreates.set(block.id, {
|
|
105
|
+
subject: block.input?.subject || "",
|
|
106
|
+
description: block.input?.description || ""
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (block.name === "TaskUpdate") {
|
|
110
|
+
const taskId = block.input?.taskId;
|
|
111
|
+
const task = taskId ? tasks.get(taskId) : void 0;
|
|
112
|
+
if (task && block.input?.status) {
|
|
113
|
+
task.status = block.input.status;
|
|
114
|
+
if (block.input.status === "in_progress")
|
|
115
|
+
currentTaskId = taskId;
|
|
116
|
+
if (block.input.status === "completed" || block.input.status === "deleted") {
|
|
117
|
+
if (currentTaskId === taskId) currentTaskId = null;
|
|
118
|
+
}
|
|
115
119
|
}
|
|
116
120
|
}
|
|
121
|
+
if (currentTaskId && (block.name === "Write" || block.name === "Edit") && fp) {
|
|
122
|
+
tasks.get(currentTaskId)?.filesAssociated.add(fp);
|
|
123
|
+
}
|
|
117
124
|
}
|
|
118
|
-
if (
|
|
119
|
-
|
|
125
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
126
|
+
const text = block.text;
|
|
127
|
+
const intentStart = text.indexOf("PAWL_INTENT:");
|
|
128
|
+
const intentEnd = text.indexOf("END_PAWL_INTENT");
|
|
129
|
+
if (intentStart !== -1 && intentEnd !== -1 && intentEnd > intentStart) {
|
|
130
|
+
const rawBlock = text.slice(intentStart, intentEnd + "END_PAWL_INTENT".length);
|
|
131
|
+
const lines = rawBlock.split("\n");
|
|
132
|
+
const fields = {};
|
|
133
|
+
for (const l of lines) {
|
|
134
|
+
const match = l.match(/^(\w+):\s*(.+)/);
|
|
135
|
+
if (match && match[1] !== "PAWL_INTENT") {
|
|
136
|
+
fields[match[1]] = match[2].trim();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
reasoningDecisions.push({
|
|
140
|
+
type: "pawl_intent",
|
|
141
|
+
rawBlock,
|
|
142
|
+
context: fields.context,
|
|
143
|
+
approach: fields.approach,
|
|
144
|
+
alternativesRejected: fields.alternatives_rejected,
|
|
145
|
+
constraints: fields.constraints,
|
|
146
|
+
messageIndex: currentMessageIndex
|
|
147
|
+
});
|
|
148
|
+
} else if (intentStart !== -1 && intentEnd === -1) {
|
|
149
|
+
process.stderr.write("Warning: PAWL_INTENT block without END marker \u2014 skipped\n");
|
|
150
|
+
}
|
|
120
151
|
}
|
|
121
152
|
}
|
|
122
153
|
}
|
|
123
154
|
}
|
|
124
|
-
if (parsed.type === "user"
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
if (
|
|
155
|
+
if (parsed.type === "user") {
|
|
156
|
+
const userContent = parsed.message?.content;
|
|
157
|
+
const messageText = typeof userContent === "string" ? userContent : Array.isArray(userContent) ? userContent.filter((b) => b.type === "text").map((b) => b.text).join("\n") : "";
|
|
158
|
+
const DECIDE_PREFIX = /^#decide\s+/i;
|
|
159
|
+
if (DECIDE_PREFIX.test(messageText)) {
|
|
160
|
+
reasoningDecisions.push({
|
|
161
|
+
type: "decide_tag",
|
|
162
|
+
rawBlock: messageText,
|
|
163
|
+
text: messageText.replace(DECIDE_PREFIX, "").trim(),
|
|
164
|
+
messageIndex: currentMessageIndex
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (parsed.toolUseResult?.task?.id) {
|
|
168
|
+
const tr = parsed.toolUseResult;
|
|
169
|
+
const content = parsed.message?.content;
|
|
170
|
+
const toolUseId = Array.isArray(content) ? content.find((b) => b.type === "tool_result")?.tool_use_id : void 0;
|
|
171
|
+
const pending = toolUseId ? pendingCreates.get(toolUseId) : void 0;
|
|
172
|
+
tasks.set(tr.task.id, {
|
|
173
|
+
id: tr.task.id,
|
|
174
|
+
subject: tr.task.subject || pending?.subject || "",
|
|
175
|
+
description: pending?.description || "",
|
|
176
|
+
status: "pending",
|
|
177
|
+
filesAssociated: /* @__PURE__ */ new Set()
|
|
178
|
+
});
|
|
179
|
+
if (toolUseId) pendingCreates.delete(toolUseId);
|
|
180
|
+
}
|
|
137
181
|
}
|
|
138
182
|
}
|
|
139
183
|
if (totalInputTokens === 0 && totalOutputTokens === 0) return null;
|
|
@@ -168,7 +212,8 @@ async function parseSessionFile(filePath) {
|
|
|
168
212
|
description: t.description,
|
|
169
213
|
status: t.status,
|
|
170
214
|
filesAssociated: [...t.filesAssociated]
|
|
171
|
-
}))
|
|
215
|
+
})),
|
|
216
|
+
reasoningDecisions
|
|
172
217
|
};
|
|
173
218
|
} catch {
|
|
174
219
|
return null;
|