portable-agent-layer 0.24.1 → 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 +123 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "portable-agent-layer",
3
- "version": "0.24.1",
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")