portable-agent-layer 0.51.1 → 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 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "portable-agent-layer",
3
- "version": "0.51.1",
3
+ "version": "0.51.2",
4
4
  "description": "PAL — Portable Agent Layer: persistent personal context for AI coding assistants",
5
5
  "type": "module",
6
6
  "bin": {
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 { existsSync, mkdirSync, readdirSync, readFileSync, statSync } from "node:fs";
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)
@@ -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 outputPath = pathArg || resolve(palHome(), `pal-export-${timestamp()}.zip`);
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 findLatest(): string | null {
1207
+ function findLatestIn(dirs: string[]): string | null {
1184
1208
  const candidates: string[] = [];
1185
-
1186
- try {
1187
- candidates.push(
1188
- ...readdirSync(home)
1189
- .filter((f) => f.startsWith("pal-export-") && f.endsWith(".zip"))
1190
- .map((f) => resolve(home, f))
1191
- );
1192
- } catch {
1193
- /* empty */
1194
- }
1195
-
1196
- try {
1197
- const backupDir = resolve(home, "backups");
1198
- candidates.push(
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
- if (pathArg) {
1218
- zipPath = resolve(pathArg);
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 latest = findLatest();
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();
@@ -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 PAL_DEBUG=1 or when called via logError (always logged).
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
- /** Log a debug message (only when PAL_DEBUG=1) */
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 (process.env.PAL_DEBUG !== "1") return;
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 PAL_DEBUG) */
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
  };