portable-agent-layer 0.24.0 → 0.24.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli/index.ts +129 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "portable-agent-layer",
3
- "version": "0.24.0",
3
+ "version": "0.24.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
@@ -288,6 +288,38 @@ interface HookHealth {
288
288
  lastError: string | null;
289
289
  }
290
290
 
291
+ function checkClaudeHooksRegistered(): boolean {
292
+ const settingsPath = resolve(platform.claudeDir(), "settings.json");
293
+ if (!existsSync(settingsPath)) return false;
294
+ try {
295
+ const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
296
+ const groups = settings?.hooks?.SessionStart;
297
+ if (!Array.isArray(groups)) return false;
298
+ return groups.some((g: { hooks?: { command?: string }[] }) =>
299
+ g?.hooks?.some((h) => h?.command?.includes("LoadContext"))
300
+ );
301
+ } catch {
302
+ return false;
303
+ }
304
+ }
305
+
306
+ function checkCursorHooksRegistered(): boolean {
307
+ const hooksPath = resolve(platform.cursorDir(), "hooks.json");
308
+ if (!existsSync(hooksPath)) return false;
309
+ try {
310
+ const data = JSON.parse(readFileSync(hooksPath, "utf-8"));
311
+ const hooks = data?.hooks?.sessionStart;
312
+ if (!Array.isArray(hooks)) return false;
313
+ return hooks.some((h: { command?: string }) => h?.command?.includes("LoadContext"));
314
+ } catch {
315
+ return false;
316
+ }
317
+ }
318
+
319
+ function checkOpencodePluginInstalled(): boolean {
320
+ return existsSync(resolve(platform.opencodeDir(), "plugins", "pal-plugin.ts"));
321
+ }
322
+
291
323
  function checkHookHealth(home: string): HookHealth {
292
324
  const logPath = resolve(home, "memory", "state", "debug.log");
293
325
 
@@ -375,6 +407,97 @@ function doctor(silent = false): DoctorResult {
375
407
  ok(`PAL home: ${home}`);
376
408
  telosCount > 0 ? ok(`TELOS: ${telosCount} files`) : fail("TELOS: not scaffolded");
377
409
 
410
+ // Identity
411
+ const palSettingsPath = resolve(home, "memory", "pal-settings.json");
412
+ if (existsSync(palSettingsPath)) {
413
+ try {
414
+ const s = JSON.parse(readFileSync(palSettingsPath, "utf-8"));
415
+ const hasIdentity = s?.identity?.principal?.name && s?.identity?.ai?.name;
416
+ hasIdentity
417
+ ? ok("Identity configured")
418
+ : warn("Identity — incomplete (run 'pal cli install')");
419
+ } catch {
420
+ warn("Identity — could not read pal-settings.json");
421
+ }
422
+ } else {
423
+ warn("Identity — pal-settings.json missing (run 'pal cli install')");
424
+ }
425
+
426
+ // AGENTS.md
427
+ const agentsMdPath = resolve(platform.opencodeDir(), "AGENTS.md");
428
+ existsSync(agentsMdPath)
429
+ ? ok("AGENTS.md present")
430
+ : fail("AGENTS.md — missing (run 'pal cli install')");
431
+
432
+ if (claude.available) {
433
+ const claudeMdPath = resolve(platform.claudeDir(), "CLAUDE.md");
434
+ existsSync(claudeMdPath)
435
+ ? ok("CLAUDE.md present")
436
+ : fail("CLAUDE.md — missing (run 'pal cli install --claude')");
437
+ }
438
+
439
+ // Setup state
440
+ const setupPath = resolve(home, "memory", "state", "setup.json");
441
+ if (existsSync(setupPath)) {
442
+ try {
443
+ const setup = JSON.parse(readFileSync(setupPath, "utf-8"));
444
+ setup?.completed
445
+ ? ok("TELOS setup complete")
446
+ : warn("TELOS setup incomplete — run 'pal cli install' or start a session");
447
+ } catch {
448
+ warn("TELOS setup — could not read setup.json");
449
+ }
450
+ } else {
451
+ warn("TELOS setup — setup.json missing (run 'pal cli install')");
452
+ }
453
+
454
+ // Skills (per installed agent)
455
+ const countSkillsIn = (dir: string) =>
456
+ existsSync(dir)
457
+ ? readdirSync(dir).filter((f) => existsSync(resolve(dir, f, "SKILL.md"))).length
458
+ : 0;
459
+ if (claude.available) {
460
+ const n = countSkillsIn(resolve(platform.claudeDir(), "skills"));
461
+ n > 0
462
+ ? ok(`Claude Code skills: ${n}`)
463
+ : warn("Claude Code skills — none found (run 'pal cli install --claude')");
464
+ }
465
+ if (opencode.available) {
466
+ const n = countSkillsIn(resolve(platform.agentsDir(), "skills"));
467
+ n > 0
468
+ ? ok(`opencode skills: ${n}`)
469
+ : warn("opencode skills — none found (run 'pal cli install --opencode')");
470
+ }
471
+ if (cursor.available) {
472
+ const n = countSkillsIn(resolve(platform.cursorDir(), "skills"));
473
+ n > 0
474
+ ? ok(`Cursor skills: ${n}`)
475
+ : warn("Cursor skills — none found (run 'pal cli install --cursor')");
476
+ }
477
+
478
+ // Dependencies
479
+ const nodeModulesPath = resolve(palPkg(), "node_modules");
480
+ existsSync(nodeModulesPath)
481
+ ? ok("Dependencies installed")
482
+ : fail("Dependencies missing — run 'pal cli install'");
483
+
484
+ // Hook registration (per installed agent)
485
+ if (claude.available) {
486
+ checkClaudeHooksRegistered()
487
+ ? ok("Claude Code hooks registered")
488
+ : fail("Claude Code hooks — not registered (run 'pal cli install --claude')");
489
+ }
490
+ if (opencode.available) {
491
+ checkOpencodePluginInstalled()
492
+ ? ok("opencode plugin installed")
493
+ : fail("opencode plugin — not installed (run 'pal cli install --opencode')");
494
+ }
495
+ if (cursor.available) {
496
+ checkCursorHooksRegistered()
497
+ ? ok("Cursor hooks registered")
498
+ : fail("Cursor hooks — not registered (run 'pal cli install --cursor')");
499
+ }
500
+
378
501
  // API key checks
379
502
  process.env.PAL_ANTHROPIC_API_KEY
380
503
  ? ok("PAL_ANTHROPIC_API_KEY is set")
@@ -382,6 +505,12 @@ function doctor(silent = false): DoctorResult {
382
505
  process.env.PAL_GEMINI_API_KEY
383
506
  ? ok("PAL_GEMINI_API_KEY is set")
384
507
  : warn("PAL_GEMINI_API_KEY — not set (optional, for YouTube analysis)");
508
+ process.env.PAL_XAI_API_KEY
509
+ ? ok("PAL_XAI_API_KEY is set")
510
+ : warn("PAL_XAI_API_KEY — not set (optional, for Grok researcher)");
511
+ process.env.PAL_PERPLEXITY_API_KEY
512
+ ? ok("PAL_PERPLEXITY_API_KEY is set")
513
+ : warn("PAL_PERPLEXITY_API_KEY — not set (optional, for Perplexity researcher)");
385
514
 
386
515
  // Hook health from debug.log
387
516
  const hookHealth = checkHookHealth(home);