portable-agent-layer 0.51.0 → 0.51.2
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 +8 -1
- package/assets/statusline.ps1 +5 -1
- package/assets/statusline.sh +5 -1
- package/package.json +1 -1
- package/src/cli/index.ts +79 -34
- package/src/hooks/lib/log.ts +9 -5
- package/src/targets/opencode/plugin.ts +0 -3
package/README.md
CHANGED
|
@@ -142,9 +142,16 @@ PAL routes inference through the host agent's subscription CLI by default. API k
|
|
|
142
142
|
|
|
143
143
|
### Debug / test
|
|
144
144
|
|
|
145
|
+
Enable verbose hook logging with:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
pal cli debug on # enable → logs to memory/state/debug.log
|
|
149
|
+
pal cli debug off # disable
|
|
150
|
+
pal cli debug # show current status and log path
|
|
151
|
+
```
|
|
152
|
+
|
|
145
153
|
| Variable | Description |
|
|
146
154
|
|----------|-------------|
|
|
147
|
-
| `PAL_DEBUG` | Set to `1` to emit verbose hook logs to `memory/state/debug.log` |
|
|
148
155
|
| `PAL_INFERENCE_DISABLED` | Set to `1` to disable all inference (used by the test suite to prevent real CLI spawns) |
|
|
149
156
|
| `PAL_NOTIFICATIONS_DISABLED` | Set to `1` to suppress desktop notifications (used by the test suite) |
|
|
150
157
|
|
package/assets/statusline.ps1
CHANGED
|
@@ -172,11 +172,15 @@ $QUOTES = @(
|
|
|
172
172
|
"In the middle of difficulty lies opportunity.|Albert Einstein"
|
|
173
173
|
"A person who never made a mistake never tried anything new.|Albert Einstein"
|
|
174
174
|
"The journey of a thousand miles begins with one step.|Lao Tzu"
|
|
175
|
+
"The obstacle is the way.|Marcus Aurelius"
|
|
176
|
+
"Whether you think you can or you think you can't — you are right.|Henry Ford"
|
|
177
|
+
"Everything should be made as simple as possible, but not simpler.|Albert Einstein"
|
|
178
|
+
"You miss 100% of the shots you don't take.|Wayne Gretzky"
|
|
175
179
|
)
|
|
176
180
|
$EPOCH = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
|
|
177
181
|
$SLOT = [int]([math]::Floor($EPOCH / 5400))
|
|
178
182
|
$SLOT_OFFSET = $EPOCH % 5400
|
|
179
|
-
if ($SLOT_OFFSET -lt
|
|
183
|
+
if ($SLOT_OFFSET -lt 1800) {
|
|
180
184
|
$QUOTE_IDX = $SLOT % $QUOTES.Count
|
|
181
185
|
$QUOTE_PARTS = $QUOTES[$QUOTE_IDX] -split '\|'
|
|
182
186
|
Write-Host ($DIM + $ITALIC + '"' + $QUOTE_PARTS[0] + '" - ' + $QUOTE_PARTS[1] + $RESET)
|
package/assets/statusline.sh
CHANGED
|
@@ -208,10 +208,14 @@ QUOTES=(
|
|
|
208
208
|
"In the middle of difficulty lies opportunity.|Albert Einstein"
|
|
209
209
|
"A person who never made a mistake never tried anything new.|Albert Einstein"
|
|
210
210
|
"The journey of a thousand miles begins with one step.|Lao Tzu"
|
|
211
|
+
"The obstacle is the way.|Marcus Aurelius"
|
|
212
|
+
"Whether you think you can or you think you can't — you are right.|Henry Ford"
|
|
213
|
+
"Everything should be made as simple as possible, but not simpler.|Albert Einstein"
|
|
214
|
+
"You miss 100% of the shots you don't take.|Wayne Gretzky"
|
|
211
215
|
)
|
|
212
216
|
SLOT=$(( $(date +%s) / 5400 ))
|
|
213
217
|
SLOT_OFFSET=$(( $(date +%s) % 5400 ))
|
|
214
|
-
if [ $SLOT_OFFSET -lt
|
|
218
|
+
if [ $SLOT_OFFSET -lt 1800 ]; then
|
|
215
219
|
QUOTE_IDX=$(( SLOT % ${#QUOTES[@]} ))
|
|
216
220
|
QUOTE_ENTRY="${QUOTES[$QUOTE_IDX]}"
|
|
217
221
|
QUOTE_TEXT="${QUOTE_ENTRY%|*}"
|
package/package.json
CHANGED
package/src/cli/index.ts
CHANGED
|
@@ -18,14 +18,23 @@
|
|
|
18
18
|
* usage Summarize token usage and cost
|
|
19
19
|
* skill link <name> Link a personal ~/.pal/skills/<name>/ into installed agents
|
|
20
20
|
* skill doctor <name> Evaluate a skill against the authoring best practices
|
|
21
|
+
* debug [on|off] Enable / disable verbose hook debug logging
|
|
21
22
|
*/
|
|
22
23
|
|
|
23
24
|
import { spawnSync } from "node:child_process";
|
|
24
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
existsSync,
|
|
27
|
+
mkdirSync,
|
|
28
|
+
readdirSync,
|
|
29
|
+
readFileSync,
|
|
30
|
+
rmSync,
|
|
31
|
+
statSync,
|
|
32
|
+
writeFileSync,
|
|
33
|
+
} from "node:fs";
|
|
25
34
|
import { homedir } from "node:os";
|
|
26
35
|
import { resolve } from "node:path";
|
|
27
36
|
import { inference, previewInferenceRoute } from "../hooks/lib/inference";
|
|
28
|
-
import { DEBUG_LOG_MAX_ROTATED } from "../hooks/lib/log";
|
|
37
|
+
import { DEBUG_LOG_MAX_ROTATED, logDebug } from "../hooks/lib/log";
|
|
29
38
|
import { palHome, palPkg, platform } from "../hooks/lib/paths";
|
|
30
39
|
import { hasRealContent, SETUP_STEPS, STEP_ORDER } from "../hooks/lib/setup";
|
|
31
40
|
import { log } from "../targets/lib";
|
|
@@ -200,6 +209,9 @@ async function runCli(command: string | undefined, args: string[]) {
|
|
|
200
209
|
if (code !== 0) process.exit(code);
|
|
201
210
|
break;
|
|
202
211
|
}
|
|
212
|
+
case "debug":
|
|
213
|
+
cliDebug(args);
|
|
214
|
+
break;
|
|
203
215
|
case "--help":
|
|
204
216
|
case "-h":
|
|
205
217
|
case "help":
|
|
@@ -244,6 +256,7 @@ function showHelp() {
|
|
|
244
256
|
(search · graph · stats · hubs · find · show · add · ls)
|
|
245
257
|
pal cli skill link <name> Link a personal ~/.pal/skills/<name>/ into installed agents
|
|
246
258
|
pal cli skill doctor <name> Evaluate a skill against the authoring best practices
|
|
259
|
+
pal cli debug [on|off] Enable/disable verbose hook debug logging (persisted)
|
|
247
260
|
|
|
248
261
|
Environment:
|
|
249
262
|
PAL_HOME Override user state directory (default: ~/.pal or repo root)
|
|
@@ -439,7 +452,7 @@ function checkAgentHookPrefix(filePath: string, agentName: string): HookPrefixCh
|
|
|
439
452
|
if (!existsSync(filePath)) return { ok: false, total: 0, missing: 0 };
|
|
440
453
|
try {
|
|
441
454
|
const data = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
442
|
-
const commands = extractAllHookCommands(data);
|
|
455
|
+
const commands = extractAllHookCommands(data.hooks ?? data);
|
|
443
456
|
const prefix = `PAL_AGENT=${agentName} `;
|
|
444
457
|
const missing = commands.filter((c) => !c.startsWith(prefix));
|
|
445
458
|
return {
|
|
@@ -1151,10 +1164,19 @@ async function exportState(args: string[]) {
|
|
|
1151
1164
|
|
|
1152
1165
|
const dryRun = args.includes("--dry-run");
|
|
1153
1166
|
const pathArg = args.find((a) => !a.startsWith("-"));
|
|
1154
|
-
const
|
|
1155
|
-
|
|
1167
|
+
const resolvedArg = pathArg ? resolve(pathArg) : null;
|
|
1168
|
+
const argIsDir =
|
|
1169
|
+
resolvedArg !== null &&
|
|
1170
|
+
existsSync(resolvedArg) &&
|
|
1171
|
+
statSync(resolvedArg).isDirectory();
|
|
1172
|
+
const outputPath = argIsDir
|
|
1173
|
+
? resolve(resolvedArg, `pal-export-${timestamp()}.zip`)
|
|
1174
|
+
: (resolvedArg ?? resolve(palHome(), `pal-export-${timestamp()}.zip`));
|
|
1175
|
+
|
|
1176
|
+
logDebug("export", `start dryRun=${dryRun} outputPath=${outputPath}`);
|
|
1156
1177
|
if (dryRun) {
|
|
1157
1178
|
const files = collectExportFiles();
|
|
1179
|
+
logDebug("export", `dry-run collected ${files.length} files`);
|
|
1158
1180
|
if (files.length === 0) {
|
|
1159
1181
|
console.log("Nothing to export.");
|
|
1160
1182
|
} else {
|
|
@@ -1163,6 +1185,7 @@ async function exportState(args: string[]) {
|
|
|
1163
1185
|
}
|
|
1164
1186
|
} else {
|
|
1165
1187
|
const count = exportZip(outputPath);
|
|
1188
|
+
logDebug("export", `done count=${count} path=${outputPath}`);
|
|
1166
1189
|
if (count === 0) {
|
|
1167
1190
|
console.log("Nothing to export.");
|
|
1168
1191
|
} else {
|
|
@@ -1179,49 +1202,50 @@ async function importState(args: string[]) {
|
|
|
1179
1202
|
const home = palHome();
|
|
1180
1203
|
const dryRun = args.includes("--dry-run");
|
|
1181
1204
|
const pathArg = args.find((a) => !a.startsWith("-"));
|
|
1205
|
+
logDebug("import", `start dryRun=${dryRun} pathArg=${pathArg ?? "(auto)"}`);
|
|
1182
1206
|
|
|
1183
|
-
function
|
|
1207
|
+
function findLatestIn(dirs: string[]): string | null {
|
|
1184
1208
|
const candidates: string[] = [];
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
...readdirSync(backupDir)
|
|
1200
|
-
.filter(
|
|
1201
|
-
(f) =>
|
|
1202
|
-
(f.startsWith("pal-export-") || f.startsWith("pal-backup-")) &&
|
|
1203
|
-
f.endsWith(".zip")
|
|
1204
|
-
)
|
|
1205
|
-
.map((f) => resolve(backupDir, f))
|
|
1206
|
-
);
|
|
1207
|
-
} catch {
|
|
1208
|
-
/* empty */
|
|
1209
|
+
for (const dir of dirs) {
|
|
1210
|
+
try {
|
|
1211
|
+
candidates.push(
|
|
1212
|
+
...readdirSync(dir)
|
|
1213
|
+
.filter(
|
|
1214
|
+
(f) =>
|
|
1215
|
+
(f.startsWith("pal-export-") || f.startsWith("pal-backup-")) &&
|
|
1216
|
+
f.endsWith(".zip")
|
|
1217
|
+
)
|
|
1218
|
+
.map((f) => resolve(dir, f))
|
|
1219
|
+
);
|
|
1220
|
+
} catch {
|
|
1221
|
+
/* empty */
|
|
1222
|
+
}
|
|
1209
1223
|
}
|
|
1210
|
-
|
|
1211
1224
|
if (candidates.length === 0) return null;
|
|
1212
1225
|
return candidates.sort((a, b) => statSync(b).mtimeMs - statSync(a).mtimeMs)[0];
|
|
1213
1226
|
}
|
|
1214
1227
|
|
|
1215
1228
|
let zipPath: string;
|
|
1216
1229
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1230
|
+
const resolvedArg = pathArg ? resolve(pathArg) : null;
|
|
1231
|
+
const argIsDir =
|
|
1232
|
+
resolvedArg !== null &&
|
|
1233
|
+
existsSync(resolvedArg) &&
|
|
1234
|
+
statSync(resolvedArg).isDirectory();
|
|
1235
|
+
|
|
1236
|
+
if (resolvedArg && !argIsDir) {
|
|
1237
|
+
zipPath = resolvedArg;
|
|
1238
|
+
logDebug("import", `using provided path=${zipPath}`);
|
|
1219
1239
|
} else {
|
|
1220
|
-
const
|
|
1240
|
+
const searchDirs = argIsDir ? [resolvedArg] : [home, resolve(home, "backups")];
|
|
1241
|
+
logDebug("import", `searching dirs=${searchDirs.join(",")}`);
|
|
1242
|
+
const latest = findLatestIn(searchDirs);
|
|
1221
1243
|
if (!latest) {
|
|
1244
|
+
logDebug("import", "no export/backup files found");
|
|
1222
1245
|
log.error("No export or backup files found. Provide a path: pal cli import <path>");
|
|
1223
1246
|
process.exit(1);
|
|
1224
1247
|
}
|
|
1248
|
+
logDebug("import", `auto-selected latest=${latest}`);
|
|
1225
1249
|
console.log(`Found: ${latest}`);
|
|
1226
1250
|
const zip = new AdmZip(latest);
|
|
1227
1251
|
console.log(
|
|
@@ -1253,11 +1277,13 @@ async function importState(args: string[]) {
|
|
|
1253
1277
|
process.exit(0);
|
|
1254
1278
|
}
|
|
1255
1279
|
|
|
1280
|
+
logDebug("import", `zip=${zipPath} entries=${entries.length} dryRun=${dryRun}`);
|
|
1256
1281
|
if (dryRun) {
|
|
1257
1282
|
console.log(`Would import ${entries.length} files → ${home}\n`);
|
|
1258
1283
|
for (const e of entries) console.log(` ${e.entryName}`);
|
|
1259
1284
|
} else {
|
|
1260
1285
|
zip.extractAllTo(home, true);
|
|
1286
|
+
logDebug("import", `done extracted=${entries.length} to=${home}`);
|
|
1261
1287
|
console.log(`Imported ${entries.length} files → ${home}`);
|
|
1262
1288
|
log.info("Run 'pal cli install' to re-register hooks.");
|
|
1263
1289
|
}
|
|
@@ -1309,6 +1335,25 @@ async function update() {
|
|
|
1309
1335
|
await install(resolveTargets([]));
|
|
1310
1336
|
}
|
|
1311
1337
|
|
|
1338
|
+
function cliDebug(args: string[]) {
|
|
1339
|
+
const stateDir = resolve(palHome(), "memory", "state");
|
|
1340
|
+
const flagFile = resolve(stateDir, "debug-enabled");
|
|
1341
|
+
const logFile = resolve(stateDir, "debug.log");
|
|
1342
|
+
const sub = args[0];
|
|
1343
|
+
if (sub === "on") {
|
|
1344
|
+
mkdirSync(stateDir, { recursive: true });
|
|
1345
|
+
writeFileSync(flagFile, "");
|
|
1346
|
+
log.success(`Debug logging enabled → ${logFile}`);
|
|
1347
|
+
} else if (sub === "off") {
|
|
1348
|
+
rmSync(flagFile, { force: true });
|
|
1349
|
+
log.success("Debug logging disabled");
|
|
1350
|
+
} else {
|
|
1351
|
+
const on = existsSync(flagFile);
|
|
1352
|
+
console.log(` Debug: ${on ? "ON" : "OFF"}`);
|
|
1353
|
+
console.log(` Log: ${logFile}`);
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1312
1357
|
async function status() {
|
|
1313
1358
|
const home = palHome();
|
|
1314
1359
|
const pkg = palPkg();
|
package/src/hooks/lib/log.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Simple file-based debug logger for PAL hooks.
|
|
3
3
|
* Writes to memory/state/debug.log — rotated on each session start.
|
|
4
4
|
*
|
|
5
|
-
* Only writes when
|
|
5
|
+
* Only writes when debug is enabled (`pal cli debug on`) or when called via logError (always logged).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { appendFileSync, existsSync, renameSync, statSync, unlinkSync } from "node:fs";
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
|
-
import { paths } from "./paths";
|
|
10
|
+
import { palHome, paths } from "./paths";
|
|
11
11
|
|
|
12
12
|
const MAX_LOG_SIZE = 50_000; // ~50KB per file
|
|
13
13
|
const MAX_ROTATED = 5; // keep up to 5 rotated files (.1 newest → .5 oldest)
|
|
@@ -74,9 +74,13 @@ function rotateIfNeeded(path: string): void {
|
|
|
74
74
|
/** Test-only: max rotated count for callers that need to enumerate. */
|
|
75
75
|
export const DEBUG_LOG_MAX_ROTATED = MAX_ROTATED;
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
function isDebugEnabled(): boolean {
|
|
78
|
+
return existsSync(resolve(palHome(), "memory", "state", "debug-enabled"));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Log a debug message (only when debug is enabled via `pal cli debug on`) */
|
|
78
82
|
export function logDebug(source: string, message: string): void {
|
|
79
|
-
if (
|
|
83
|
+
if (!isDebugEnabled()) return;
|
|
80
84
|
const path = logFile();
|
|
81
85
|
rotateIfNeeded(path);
|
|
82
86
|
try {
|
|
@@ -86,7 +90,7 @@ export function logDebug(source: string, message: string): void {
|
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
/** Log an error (always written, regardless of
|
|
93
|
+
/** Log an error (always written, regardless of debug mode) */
|
|
90
94
|
export function logError(source: string, error: unknown): void {
|
|
91
95
|
const path = logFile();
|
|
92
96
|
rotateIfNeeded(path);
|
|
@@ -192,9 +192,6 @@ const PALPlugin: Plugin = async ({ directory, client }: PluginInput) => {
|
|
|
192
192
|
output: { env: Record<string, string> }
|
|
193
193
|
) => {
|
|
194
194
|
output.env.PAL_DIR = PAL_DIR;
|
|
195
|
-
if (process.env.PAL_DEBUG) {
|
|
196
|
-
output.env.PAL_DEBUG = process.env.PAL_DEBUG;
|
|
197
|
-
}
|
|
198
195
|
},
|
|
199
196
|
};
|
|
200
197
|
};
|