@tekmidian/pai 0.58.0 → 0.60.0

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 (129) hide show
  1. package/.claude-plugin/plugin.json +8 -33
  2. package/PLUGIN-ARCHITECTURE.md +4 -6
  3. package/dist/{aibroker-client-B8Wtasez.mjs → aibroker-client-BYG-GtAg.mjs} +1 -1
  4. package/dist/{chain-Cltx0NjY.mjs → chain-DFlzAfDE.mjs} +4 -4
  5. package/dist/{chain-Cltx0NjY.mjs.map → chain-DFlzAfDE.mjs.map} +1 -1
  6. package/dist/cli/index.mjs +12 -12
  7. package/dist/cli/program.mjs +12 -12
  8. package/dist/{config-DCYWFaEi.mjs → config-GceCMUxa.mjs} +1 -1
  9. package/dist/{config-D1omG_UU.mjs → config-msER8xj_.mjs} +16 -4
  10. package/dist/config-msER8xj_.mjs.map +1 -0
  11. package/dist/{context-handover-cache-Dt4HW1Ro.mjs → context-handover-cache-DF0xWVB0.mjs} +4 -4
  12. package/dist/{context-handover-cache-Dt4HW1Ro.mjs.map → context-handover-cache-DF0xWVB0.mjs.map} +1 -1
  13. package/dist/daemon/index.mjs +12 -12
  14. package/dist/daemon-BppwJtrH.mjs +19 -0
  15. package/dist/{daemon-CmqJfeb9.mjs → daemon-DArRKBlk.mjs} +97 -23
  16. package/dist/daemon-DArRKBlk.mjs.map +1 -0
  17. package/dist/daemon-mcp/index.mjs +9 -9
  18. package/dist/daemon-mcp/index.mjs.map +1 -1
  19. package/dist/{factory-DiuntvwG.mjs → factory-ChfAvCzO.mjs} +7 -7
  20. package/dist/{factory-DiuntvwG.mjs.map → factory-ChfAvCzO.mjs.map} +1 -1
  21. package/dist/{factory-C9wGUe30.mjs → factory-D6I2jmjQ.mjs} +1 -1
  22. package/dist/{fallback-B_2ozQHw.mjs → fallback-B7VEMMRn.mjs} +80 -9
  23. package/dist/fallback-B7VEMMRn.mjs.map +1 -0
  24. package/dist/hooks/block-sleep-poll.mjs +7590 -0
  25. package/dist/hooks/block-sleep-poll.mjs.map +7 -0
  26. package/dist/hooks/capture-all-events.mjs +8 -4
  27. package/dist/hooks/capture-all-events.mjs.map +2 -2
  28. package/dist/hooks/capture-session-summary.mjs.map +1 -1
  29. package/dist/hooks/capture-tool-output.mjs.map +1 -1
  30. package/dist/hooks/cleanup-session-files.mjs.map +1 -1
  31. package/dist/hooks/context-compression-hook.mjs +6 -6
  32. package/dist/hooks/context-compression-hook.mjs.map +3 -3
  33. package/dist/hooks/initialize-session.mjs.map +1 -1
  34. package/dist/hooks/load-core-context.mjs.map +1 -1
  35. package/dist/hooks/load-project-context.mjs.map +2 -2
  36. package/dist/hooks/post-compact-inject.mjs +1 -1
  37. package/dist/hooks/post-compact-inject.mjs.map +2 -2
  38. package/dist/hooks/route-agents-to-worker.mjs +4 -3
  39. package/dist/hooks/route-agents-to-worker.mjs.map +3 -3
  40. package/dist/hooks/security-validator.mjs +4 -3
  41. package/dist/hooks/security-validator.mjs.map +3 -3
  42. package/dist/hooks/stop-hook.mjs.map +1 -1
  43. package/dist/hooks/sync-todo-to-md.mjs.map +1 -1
  44. package/dist/hooks/whisper-rules.mjs +4 -3
  45. package/dist/hooks/whisper-rules.mjs.map +3 -3
  46. package/dist/hooks/worker-proxy.mjs +4 -3
  47. package/dist/hooks/worker-proxy.mjs.map +3 -3
  48. package/dist/hooks/worker-status-line.mjs +9 -6
  49. package/dist/hooks/worker-status-line.mjs.map +3 -3
  50. package/dist/hooks/worker-supervision.mjs +4 -3
  51. package/dist/hooks/worker-supervision.mjs.map +3 -3
  52. package/dist/{main-resolver-DYNxxNwJ.mjs → main-resolver-BqTjt-GL.mjs} +100 -21
  53. package/dist/main-resolver-BqTjt-GL.mjs.map +1 -0
  54. package/dist/main-resolver-CCojcYr2.mjs +7 -0
  55. package/dist/{planner-DsZZEZCA.mjs → planner-DbpsMZqY.mjs} +83 -30
  56. package/dist/planner-DbpsMZqY.mjs.map +1 -0
  57. package/dist/{program-D-WkmvuQ.mjs → program-QSuci3RH.mjs} +325 -93
  58. package/dist/program-QSuci3RH.mjs.map +1 -0
  59. package/dist/router-C5hPk9YP.mjs +3 -0
  60. package/dist/{router-BdxdsAvs.mjs → router-DjFZIoDl.mjs} +2 -1
  61. package/dist/router-DjFZIoDl.mjs.map +1 -0
  62. package/dist/{run-XaA-UPdW.mjs → run-BFxuNnIJ.mjs} +100 -17
  63. package/dist/run-BFxuNnIJ.mjs.map +1 -0
  64. package/dist/{run-env-DyTWgZC9.mjs → run-env-kbWVwUvU.mjs} +2 -2
  65. package/dist/{run-env-DyTWgZC9.mjs.map → run-env-kbWVwUvU.mjs.map} +1 -1
  66. package/dist/{server-CzqHGbKt.mjs → server-B0vBmfJt.mjs} +2 -2
  67. package/dist/{server-CzqHGbKt.mjs.map → server-B0vBmfJt.mjs.map} +1 -1
  68. package/dist/{session-keepalive-DDsEo81Q.mjs → session-keepalive-CUcFa8ld.mjs} +5 -5
  69. package/dist/{session-keepalive-DDsEo81Q.mjs.map → session-keepalive-CUcFa8ld.mjs.map} +1 -1
  70. package/dist/{tools-Diro2s8J.mjs → tools-CaTZnIau.mjs} +2 -2
  71. package/dist/{tools-Diro2s8J.mjs.map → tools-CaTZnIau.mjs.map} +1 -1
  72. package/dist/{tools-KgL1rkPD.mjs → tools-_7MF8Ebb.mjs} +3 -3
  73. package/dist/{wakeup-D0uvqLsE.mjs → wakeup-CTX3rlz-.mjs} +2 -2
  74. package/dist/wakeup-CTX3rlz-.mjs.map +1 -0
  75. package/dist/{work-queue-worker-DL9lu8sN.mjs → work-queue-worker-B4eZGy5n.mjs} +2 -2
  76. package/dist/{work-queue-worker-DL9lu8sN.mjs.map → work-queue-worker-B4eZGy5n.mjs.map} +1 -1
  77. package/dist/work-queue-worker-DuhZmfDB.mjs +11 -0
  78. package/docker/init.sql +1 -1
  79. package/docs/commands/README.md +4 -4
  80. package/docs/commands/daemon.md +6 -6
  81. package/docs/commands/session.md +2 -2
  82. package/docs/commands/worker.md +5 -2
  83. package/docs/workers-config.md +5 -1
  84. package/package.json +1 -1
  85. package/plugins/context-preservation/hooks/hooks.json +38 -31
  86. package/plugins/context-preservation/plugin.json +1 -4
  87. package/plugins/core/hooks/hooks.json +51 -46
  88. package/plugins/core/plugin.json +1 -4
  89. package/plugins/creative/plugin.json +1 -5
  90. package/plugins/observability/hooks/hooks.json +87 -72
  91. package/plugins/observability/plugin.json +1 -5
  92. package/plugins/productivity/hooks/hooks.json +22 -19
  93. package/plugins/productivity/plugin.json +2 -5
  94. package/plugins/semantic-search/plugin.json +1 -16
  95. package/plugins/ui/hooks/hooks.json +17 -14
  96. package/plugins/ui/plugin.json +1 -5
  97. package/plugins/zettelkasten/plugin.json +1 -14
  98. package/src/hooks/session-autosave.sh +4 -2
  99. package/src/hooks/session-stop.sh +2 -2
  100. package/src/hooks/ts/capture-all-events.ts +3 -3
  101. package/src/hooks/ts/lib/metadata-extraction.ts +3 -2
  102. package/src/hooks/ts/lib/pai-paths.test.ts +1 -1
  103. package/src/hooks/ts/lib/sleep-poll-gate.test.ts +62 -0
  104. package/src/hooks/ts/lib/sleep-poll-gate.ts +77 -0
  105. package/src/hooks/ts/post-tool-use/capture-tool-output.ts +1 -1
  106. package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +1 -1
  107. package/src/hooks/ts/pre-compact/context-compression-hook.ts +1 -1
  108. package/src/hooks/ts/pre-tool-use/block-sleep-poll.test.ts +44 -0
  109. package/src/hooks/ts/pre-tool-use/block-sleep-poll.ts +50 -0
  110. package/src/hooks/ts/session-end/capture-session-summary.ts +1 -1
  111. package/src/hooks/ts/session-start/initialize-session.ts +2 -2
  112. package/src/hooks/ts/session-start/load-core-context.ts +2 -2
  113. package/src/hooks/ts/session-start/load-project-context.ts +1 -1
  114. package/src/hooks/ts/stop/stop-hook.ts +1 -1
  115. package/src/hooks/ts/user-prompt/cleanup-session-files.ts +1 -1
  116. package/statusline-command.sh +26 -11
  117. package/dist/config-D1omG_UU.mjs.map +0 -1
  118. package/dist/daemon-B6hx1Ysz.mjs +0 -19
  119. package/dist/daemon-CmqJfeb9.mjs.map +0 -1
  120. package/dist/fallback-B_2ozQHw.mjs.map +0 -1
  121. package/dist/main-resolver-DYNxxNwJ.mjs.map +0 -1
  122. package/dist/main-resolver-Df65bPAi.mjs +0 -7
  123. package/dist/planner-DsZZEZCA.mjs.map +0 -1
  124. package/dist/program-D-WkmvuQ.mjs.map +0 -1
  125. package/dist/router-BXOf_e9e.mjs +0 -3
  126. package/dist/router-BdxdsAvs.mjs.map +0 -1
  127. package/dist/run-XaA-UPdW.mjs.map +0 -1
  128. package/dist/wakeup-D0uvqLsE.mjs.map +0 -1
  129. package/dist/work-queue-worker-BY7xn4pw.mjs +0 -11
@@ -1,49 +1,54 @@
1
1
  {
2
- "module": "core",
3
2
  "description": "Essential lifecycle hooks for session management, context loading, and security",
4
- "hooks": [
5
- {
6
- "event": "SessionStart",
7
- "command": "${PAI_DIR}/Hooks/load-core-context.mjs",
8
- "description": "Load PAI skill system and core configuration into session context"
9
- },
10
- {
11
- "event": "SessionStart",
12
- "command": "${PAI_DIR}/Hooks/load-project-context.mjs",
13
- "description": "Detect project from CWD, load notes directory, TODO.md, and session note"
14
- },
15
- {
16
- "event": "SessionStart",
17
- "command": "${PAI_DIR}/Hooks/initialize-session.mjs",
18
- "description": "Create numbered session note and register in PAI registry"
19
- },
20
- {
21
- "event": "SessionStart",
22
- "matcher": "resume|compact",
23
- "command": "${PAI_DIR}/Hooks/mcp-deferred-reminder.mjs",
24
- "description": "Heal stale deferred MCP tool handles on resumed sessions: ToolSearch-and-retry instead of trusting a stale disconnect conclusion"
25
- },
26
- {
27
- "event": "PreToolUse",
28
- "matcher": "Bash",
29
- "command": "${PAI_DIR}/Hooks/security-validator.mjs",
30
- "description": "Validate shell commands against security rules before execution"
31
- },
32
- {
33
- "event": "PreToolUse",
34
- "matcher": "mcp__",
35
- "command": "${PAI_DIR}/Hooks/mcp-deferred-gate.mjs",
36
- "description": "Correct deferred MCP tool calls: ToolSearch remedy instead of a false 'MCP server disconnected' report"
37
- },
38
- {
39
- "event": "Stop",
40
- "command": "${PAI_DIR}/Hooks/stop-hook.mjs",
41
- "description": "Write work items and summary to session note, send notification"
42
- },
43
- {
44
- "event": "Stop",
45
- "command": "${PAI_DIR}/Hooks/pai-session-stop.sh",
46
- "description": "Shell-level session cleanup on stop"
47
- }
48
- ]
3
+ "hooks": {
4
+ "SessionStart": [
5
+ {
6
+ "hooks": [
7
+ { "type": "command", "command": "${PAI_DIR}/Hooks/load-core-context.mjs" }
8
+ ]
9
+ },
10
+ {
11
+ "hooks": [
12
+ { "type": "command", "command": "${PAI_DIR}/Hooks/load-project-context.mjs" }
13
+ ]
14
+ },
15
+ {
16
+ "hooks": [
17
+ { "type": "command", "command": "${PAI_DIR}/Hooks/initialize-session.mjs" }
18
+ ]
19
+ },
20
+ {
21
+ "matcher": "resume|compact",
22
+ "hooks": [
23
+ { "type": "command", "command": "${PAI_DIR}/Hooks/mcp-deferred-reminder.mjs" }
24
+ ]
25
+ }
26
+ ],
27
+ "PreToolUse": [
28
+ {
29
+ "matcher": "Bash",
30
+ "hooks": [
31
+ { "type": "command", "command": "${PAI_DIR}/Hooks/security-validator.mjs" }
32
+ ]
33
+ },
34
+ {
35
+ "matcher": "mcp__",
36
+ "hooks": [
37
+ { "type": "command", "command": "${PAI_DIR}/Hooks/mcp-deferred-gate.mjs" }
38
+ ]
39
+ }
40
+ ],
41
+ "Stop": [
42
+ {
43
+ "hooks": [
44
+ { "type": "command", "command": "${PAI_DIR}/Hooks/stop-hook.mjs" }
45
+ ]
46
+ },
47
+ {
48
+ "hooks": [
49
+ { "type": "command", "command": "${PAI_DIR}/Hooks/pai-session-stop.sh" }
50
+ ]
51
+ }
52
+ ]
53
+ }
49
54
  }
@@ -3,8 +3,5 @@
3
3
  "displayName": "PAI Core",
4
4
  "description": "Core memory engine, session management, and project registry",
5
5
  "version": "0.7.0",
6
- "tier": "free",
7
- "required": true,
8
- "hooks": "hooks/hooks.json",
9
- "skills": ["Sessions", "Route", "Name"]
6
+ "hooks": "./hooks/hooks.json"
10
7
  }
@@ -2,9 +2,5 @@
2
2
  "name": "pai-creative",
3
3
  "displayName": "PAI Creative Studio",
4
4
  "description": "Art direction, story explanation, voice/prosody configuration",
5
- "version": "0.7.0",
6
- "tier": "enterprise",
7
- "required": false,
8
- "depends": ["core"],
9
- "skills": ["Art", "StoryExplanation"]
5
+ "version": "0.7.0"
10
6
  }
@@ -1,75 +1,90 @@
1
1
  {
2
- "module": "observability",
3
2
  "description": "Automatic observation capture, event logging, tool output recording, and session summaries",
4
- "hooks": [
5
- {
6
- "event": "SessionStart",
7
- "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SessionStart",
8
- "description": "Log SessionStart event to session timeline"
9
- },
10
- {
11
- "event": "SessionStart",
12
- "command": "${PAI_DIR}/Hooks/inject-observations.mjs",
13
- "description": "Inject recent observation context (compact index + timeline)"
14
- },
15
- {
16
- "event": "UserPromptSubmit",
17
- "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type UserPromptSubmit",
18
- "description": "Log UserPromptSubmit event to session timeline"
19
- },
20
- {
21
- "event": "PreToolUse",
22
- "matcher": "*",
23
- "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PreToolUse",
24
- "description": "Log PreToolUse event to session timeline"
25
- },
26
- {
27
- "event": "PostToolUse",
28
- "matcher": "*",
29
- "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PostToolUse",
30
- "description": "Log PostToolUse event to session timeline"
31
- },
32
- {
33
- "event": "PostToolUse",
34
- "matcher": "*",
35
- "command": "${PAI_DIR}/Hooks/observe.mjs",
36
- "description": "Classify tool calls into typed observations (decision/bugfix/feature/refactor/discovery/change)"
37
- },
38
- {
39
- "event": "PostToolUse",
40
- "matcher": "*",
41
- "command": "${PAI_DIR}/Hooks/capture-tool-output.mjs",
42
- "description": "Record tool inputs/outputs for observability dashboard"
43
- },
44
- {
45
- "event": "Stop",
46
- "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type Stop",
47
- "description": "Log Stop event to session timeline"
48
- },
49
- {
50
- "event": "SubagentStop",
51
- "command": "${PAI_DIR}/Hooks/subagent-stop-hook.mjs",
52
- "description": "Capture sub-agent completion for observability"
53
- },
54
- {
55
- "event": "SubagentStop",
56
- "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SubagentStop",
57
- "description": "Log SubagentStop event to session timeline"
58
- },
59
- {
60
- "event": "SessionEnd",
61
- "command": "${PAI_DIR}/Hooks/capture-session-summary.mjs",
62
- "description": "Generate final session summary and write to session note"
63
- },
64
- {
65
- "event": "SessionEnd",
66
- "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SessionEnd",
67
- "description": "Log SessionEnd event to session timeline"
68
- },
69
- {
70
- "event": "PreCompact",
71
- "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PreCompact",
72
- "description": "Log PreCompact event to session timeline"
73
- }
74
- ]
3
+ "hooks": {
4
+ "SessionStart": [
5
+ {
6
+ "hooks": [
7
+ { "type": "command", "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SessionStart" }
8
+ ]
9
+ },
10
+ {
11
+ "hooks": [
12
+ { "type": "command", "command": "${PAI_DIR}/Hooks/inject-observations.mjs" }
13
+ ]
14
+ }
15
+ ],
16
+ "UserPromptSubmit": [
17
+ {
18
+ "hooks": [
19
+ { "type": "command", "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type UserPromptSubmit" }
20
+ ]
21
+ }
22
+ ],
23
+ "PreToolUse": [
24
+ {
25
+ "matcher": "*",
26
+ "hooks": [
27
+ { "type": "command", "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PreToolUse" }
28
+ ]
29
+ }
30
+ ],
31
+ "PostToolUse": [
32
+ {
33
+ "matcher": "*",
34
+ "hooks": [
35
+ { "type": "command", "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PostToolUse" }
36
+ ]
37
+ },
38
+ {
39
+ "matcher": "*",
40
+ "hooks": [
41
+ { "type": "command", "command": "${PAI_DIR}/Hooks/observe.mjs" }
42
+ ]
43
+ },
44
+ {
45
+ "matcher": "*",
46
+ "hooks": [
47
+ { "type": "command", "command": "${PAI_DIR}/Hooks/capture-tool-output.mjs" }
48
+ ]
49
+ }
50
+ ],
51
+ "Stop": [
52
+ {
53
+ "hooks": [
54
+ { "type": "command", "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type Stop" }
55
+ ]
56
+ }
57
+ ],
58
+ "SubagentStop": [
59
+ {
60
+ "hooks": [
61
+ { "type": "command", "command": "${PAI_DIR}/Hooks/subagent-stop-hook.mjs" }
62
+ ]
63
+ },
64
+ {
65
+ "hooks": [
66
+ { "type": "command", "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SubagentStop" }
67
+ ]
68
+ }
69
+ ],
70
+ "SessionEnd": [
71
+ {
72
+ "hooks": [
73
+ { "type": "command", "command": "${PAI_DIR}/Hooks/capture-session-summary.mjs" }
74
+ ]
75
+ },
76
+ {
77
+ "hooks": [
78
+ { "type": "command", "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type SessionEnd" }
79
+ ]
80
+ }
81
+ ],
82
+ "PreCompact": [
83
+ {
84
+ "hooks": [
85
+ { "type": "command", "command": "${PAI_DIR}/Hooks/capture-all-events.mjs --event-type PreCompact" }
86
+ ]
87
+ }
88
+ ]
89
+ }
75
90
  }
@@ -3,9 +3,5 @@
3
3
  "displayName": "PAI Observability Suite",
4
4
  "description": "Automatic observation capture, classification, session summaries, and search history",
5
5
  "version": "0.7.0",
6
- "tier": "pro",
7
- "required": false,
8
- "depends": ["core"],
9
- "hooks": "hooks/hooks.json",
10
- "skills": ["Observability", "SearchHistory"]
6
+ "hooks": "./hooks/hooks.json"
11
7
  }
@@ -1,22 +1,25 @@
1
1
  {
2
- "module": "productivity",
3
2
  "description": "Hooks for TODO sync, session cleanup, and productivity workflows",
4
- "hooks": [
5
- {
6
- "event": "PostToolUse",
7
- "matcher": "TodoWrite",
8
- "command": "${PAI_DIR}/Hooks/sync-todo-to-md.mjs",
9
- "description": "Sync Claude's internal TODO list to Notes/TODO.md on every TodoWrite"
10
- },
11
- {
12
- "event": "UserPromptSubmit",
13
- "command": "${PAI_DIR}/Hooks/cleanup-session-files.mjs",
14
- "description": "Clean up stale temp files between user prompts"
15
- },
16
- {
17
- "event": "UserPromptSubmit",
18
- "command": "${PAI_DIR}/Hooks/worker-supervision.mjs",
19
- "description": "Surface daemon worker supervision events (finished/failed/stalled) for this session's workers"
20
- }
21
- ]
3
+ "hooks": {
4
+ "PostToolUse": [
5
+ {
6
+ "matcher": "TodoWrite",
7
+ "hooks": [
8
+ { "type": "command", "command": "${PAI_DIR}/Hooks/sync-todo-to-md.mjs" }
9
+ ]
10
+ }
11
+ ],
12
+ "UserPromptSubmit": [
13
+ {
14
+ "hooks": [
15
+ { "type": "command", "command": "${PAI_DIR}/Hooks/cleanup-session-files.mjs" }
16
+ ]
17
+ },
18
+ {
19
+ "hooks": [
20
+ { "type": "command", "command": "${PAI_DIR}/Hooks/worker-supervision.mjs" }
21
+ ]
22
+ }
23
+ ]
24
+ }
22
25
  }
@@ -3,9 +3,6 @@
3
3
  "displayName": "PAI Productivity",
4
4
  "description": "Planning, journaling, reviewing, research, sharing, and cross-session task routing",
5
5
  "version": "0.7.0",
6
- "tier": "free",
7
- "required": false,
8
- "depends": ["core"],
9
- "hooks": "hooks/hooks.json",
10
- "skills": ["Plan", "Review", "Journal", "Research", "Share", "Createskill", "Reconstruct", "Tasks"]
6
+ "hooks": "./hooks/hooks.json",
7
+ "skills": ["./skills/"]
11
8
  }
@@ -2,20 +2,5 @@
2
2
  "name": "pai-semantic-search",
3
3
  "displayName": "PAI Semantic Search",
4
4
  "description": "Vector search with pgvector, cross-encoder reranking, hybrid mode, recency boost",
5
- "version": "0.7.0",
6
- "tier": "pro",
7
- "required": false,
8
- "depends": ["core"],
9
- "features": [
10
- "semantic_search_mode",
11
- "hybrid_search_mode",
12
- "cross_encoder_reranking",
13
- "recency_boost",
14
- "pgvector_storage"
15
- ],
16
- "requirements": {
17
- "docker": true,
18
- "postgresql": ">=15",
19
- "pgvector": ">=0.5.0"
20
- }
5
+ "version": "0.7.0"
21
6
  }
@@ -1,17 +1,20 @@
1
1
  {
2
- "module": "ui",
3
2
  "description": "Terminal tab titles, statusline updates, and visual feedback hooks",
4
- "hooks": [
5
- {
6
- "event": "UserPromptSubmit",
7
- "command": "${PAI_DIR}/Hooks/update-tab-titles.mjs",
8
- "description": "Set terminal tab title from session context"
9
- },
10
- {
11
- "event": "PostToolUse",
12
- "matcher": "*",
13
- "command": "${PAI_DIR}/Hooks/update-tab-on-action.mjs",
14
- "description": "Update terminal tab title based on current tool activity"
15
- }
16
- ]
3
+ "hooks": {
4
+ "UserPromptSubmit": [
5
+ {
6
+ "hooks": [
7
+ { "type": "command", "command": "${PAI_DIR}/Hooks/update-tab-titles.mjs" }
8
+ ]
9
+ }
10
+ ],
11
+ "PostToolUse": [
12
+ {
13
+ "matcher": "*",
14
+ "hooks": [
15
+ { "type": "command", "command": "${PAI_DIR}/Hooks/update-tab-on-action.mjs" }
16
+ ]
17
+ }
18
+ ]
19
+ }
17
20
  }
@@ -3,9 +3,5 @@
3
3
  "displayName": "PAI UI Customization",
4
4
  "description": "Terminal tab titles, statusline, tab coloring, and visual customization",
5
5
  "version": "0.7.0",
6
- "tier": "free",
7
- "required": false,
8
- "depends": ["core"],
9
- "hooks": "hooks/hooks.json",
10
- "scripts": ["statusline-command.sh", "tab-color-command.sh"]
6
+ "hooks": "./hooks/hooks.json"
11
7
  }
@@ -2,18 +2,5 @@
2
2
  "name": "pai-zettelkasten",
3
3
  "displayName": "PAI Zettelkasten Intelligence",
4
4
  "description": "Luhmann-inspired vault intelligence with graph operations and Obsidian integration",
5
- "version": "0.7.0",
6
- "tier": "enterprise",
7
- "required": false,
8
- "depends": ["core", "semantic-search"],
9
- "skills": ["VaultConnect", "VaultContext", "VaultEmerge", "VaultOrphans", "VaultTrace"],
10
- "features": [
11
- "vault_indexer",
12
- "zettel_explore",
13
- "zettel_surprise",
14
- "zettel_converse",
15
- "zettel_themes",
16
- "zettel_health",
17
- "zettel_suggest"
18
- ]
5
+ "version": "0.7.0"
19
6
  }
@@ -31,8 +31,10 @@ SENTINEL="${TMPDIR:-/tmp}/pai-autosave-hook-${SENTINEL_KEY}"
31
31
 
32
32
  if [ -f "$SENTINEL" ]; then
33
33
  NOW=$(date +%s)
34
- # stat is not portable: BSD/macOS uses -f %m, GNU/Linux uses -c %Y.
35
- LAST=$(stat -f %m "$SENTINEL" 2>/dev/null || stat -c %Y "$SENTINEL" 2>/dev/null || echo 0)
34
+ # stat is not portable: GNU/Linux uses -c %Y, BSD/macOS uses -f %m. GNU
35
+ # first — BSD's `-f %m` reads as "filesystem status" on GNU and silently
36
+ # succeeds on the wrong thing instead of failing over.
37
+ LAST=$(stat -c %Y "$SENTINEL" 2>/dev/null || stat -f %m "$SENTINEL" 2>/dev/null || echo 0)
36
38
  if [ $((NOW - LAST)) -lt "$MIN_GAP_SECONDS" ]; then
37
39
  exit 0
38
40
  fi
@@ -68,7 +68,7 @@ OLD_LOCK_DIR="$HOME/.config/pai/.session-stop.lock"
68
68
  mkdir -p "$(dirname "$LOCK_DIR")" 2>/dev/null || true
69
69
 
70
70
  if [ -d "$OLD_LOCK_DIR" ] && [ ! -d "$LOCK_DIR" ]; then
71
- OLD_LOCK_AGE=$(( $(date +%s) - $(stat -f %m "$OLD_LOCK_DIR" 2>/dev/null || echo 0) ))
71
+ OLD_LOCK_AGE=$(( $(date +%s) - $(stat -c %Y "$OLD_LOCK_DIR" 2>/dev/null || stat -f %m "$OLD_LOCK_DIR" 2>/dev/null || echo 0) ))
72
72
  if [ "$OLD_LOCK_AGE" -le 600 ]; then
73
73
  echo "pai: honoring in-flight lock at old location $OLD_LOCK_DIR" >&2
74
74
  LOCK_DIR="$OLD_LOCK_DIR"
@@ -78,7 +78,7 @@ fi
78
78
  # A crashed run must not wedge this forever: treat a lock older than 10 minutes
79
79
  # as abandoned. Longer than any observed tail, shorter than a working session.
80
80
  if [ -d "$LOCK_DIR" ]; then
81
- LOCK_AGE=$(( $(date +%s) - $(stat -f %m "$LOCK_DIR" 2>/dev/null || echo 0) ))
81
+ LOCK_AGE=$(( $(date +%s) - $(stat -c %Y "$LOCK_DIR" 2>/dev/null || stat -f %m "$LOCK_DIR" 2>/dev/null || echo 0) ))
82
82
  [ "$LOCK_AGE" -gt 600 ] && rmdir "$LOCK_DIR" 2>/dev/null
83
83
  fi
84
84
 
@@ -8,9 +8,9 @@
8
8
 
9
9
  import { readFileSync, appendFileSync, mkdirSync, existsSync } from 'fs';
10
10
  import { join } from 'path';
11
- import { historyDir, agentSessionsPath } from './lib/pai-paths';
12
- import { enrichEventWithAgentMetadata, isAgentSpawningCall } from './lib/metadata-extraction';
13
- import { isProbeSession } from './lib/project-utils';
11
+ import { historyDir, agentSessionsPath } from './lib/pai-paths.js';
12
+ import { enrichEventWithAgentMetadata, isAgentSpawningCall } from './lib/metadata-extraction.js';
13
+ import { isProbeSession } from './lib/project-utils.js';
14
14
  import { writeJsonAtomic } from '../../config/json-store.js';
15
15
 
16
16
  interface HookEvent {
@@ -52,10 +52,11 @@ export function extractAgentInstanceId(
52
52
  if (!result.agent_instance_id && toolInput?.prompt && typeof toolInput.prompt === 'string') {
53
53
  const promptMatch = toolInput.prompt.match(/\[AGENT_INSTANCE:\s*([^\]]+)\]/);
54
54
  if (promptMatch) {
55
- result.agent_instance_id = promptMatch[1].trim();
55
+ const agentInstanceId = promptMatch[1].trim();
56
+ result.agent_instance_id = agentInstanceId;
56
57
 
57
58
  // Parse agent type and instance number from ID
58
- const parts = result.agent_instance_id.match(/^([a-z-]+)-(\d+)$/);
59
+ const parts = agentInstanceId.match(/^([a-z-]+)-(\d+)$/);
59
60
  if (parts) {
60
61
  result.agent_type = parts[1];
61
62
  result.instance_number = parseInt(parts[2], 10);
@@ -28,7 +28,7 @@ function resetNoticeFlag(): void {
28
28
  }
29
29
 
30
30
  function deprecationLines(writeSpy: ReturnType<typeof vi.spyOn>): string[] {
31
- return writeSpy.mock.calls.map((c) => String(c[0])).filter((line) => line.includes("PAI_DIR is deprecated"));
31
+ return writeSpy.mock.calls.map((c: unknown[]) => String(c[0])).filter((line: string) => line.includes("PAI_DIR is deprecated"));
32
32
  }
33
33
 
34
34
  beforeEach(() => {
@@ -0,0 +1,62 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { decideSleepPollGate, renderDecision, type SleepPollGateInput } from "./sleep-poll-gate.js";
3
+
4
+ function decisionOf(input: SleepPollGateInput, env: Record<string, string | undefined> = {}) {
5
+ return JSON.parse(renderDecision(decideSleepPollGate(input, env))).hookSpecificOutput;
6
+ }
7
+
8
+ function bash(command: string, run_in_background?: boolean): SleepPollGateInput {
9
+ return { tool_name: "Bash", tool_input: { command, ...(run_in_background !== undefined ? { run_in_background } : {}) } };
10
+ }
11
+
12
+ describe("sleep-poll gate", () => {
13
+ it("denies a long sleep, standalone or buried in a compound command", () => {
14
+ for (const cmd of [
15
+ "sleep 590",
16
+ "sleep 2m",
17
+ "cd x && sleep 300",
18
+ "sleep 120; tail -5 f",
19
+ "while true; do sleep 90; done",
20
+ "for i in 1 2; do sleep 61; done",
21
+ ]) {
22
+ const out = decisionOf(bash(cmd));
23
+ expect(out.permissionDecision).toBe("deny");
24
+ expect(out.hookEventName).toBe("PreToolUse");
25
+ expect(out.permissionDecisionReason).toContain("do not sleep-poll");
26
+ }
27
+ });
28
+
29
+ it("allows a short sleep or a sleep lookalike", () => {
30
+ for (const cmd of ["sleep 30", "sleep 59; tail f", "echo sleep 500", "grep sleep file", "timeout 600 make"]) {
31
+ expect(decisionOf(bash(cmd)).permissionDecision).toBe("allow");
32
+ }
33
+ });
34
+
35
+ it("allows any command when run_in_background is true — a backgrounded sleep blocks nothing", () => {
36
+ expect(decisionOf(bash("sleep 590", true)).permissionDecision).toBe("allow");
37
+ });
38
+
39
+ it("lets a non-Bash tool through untouched", () => {
40
+ expect(
41
+ decisionOf({ tool_name: "Write", tool_input: { command: "sleep 590" } } as unknown as SleepPollGateInput)
42
+ .permissionDecision
43
+ ).toBe("allow");
44
+ });
45
+
46
+ it("interactive session: deny reason points at run_in_background and a bounded loop", () => {
47
+ const out = decisionOf(bash("sleep 590"), {});
48
+ expect(out.permissionDecisionReason).toContain("run_in_background: true");
49
+ expect(out.permissionDecisionReason).toContain("bounded loop under 60s");
50
+ });
51
+
52
+ it("worker session (PAI_WORKER=1): deny reason points at a foreground timeout and a kill -0 loop instead", () => {
53
+ const out = decisionOf(bash("sleep 590"), { PAI_WORKER: "1" });
54
+ expect(out.permissionDecisionReason).toContain("FOREGROUND with a Bash timeout up to 600000 ms");
55
+ expect(out.permissionDecisionReason).toContain("while kill -0 <pid> 2>/dev/null; do sleep 15; done");
56
+ expect(out.permissionDecisionReason).not.toContain("run_in_background: true");
57
+ });
58
+
59
+ it("names the exact offending duration in the reason", () => {
60
+ expect(decisionOf(bash("sleep 2m")).permissionDecisionReason).toContain("sleep 120s blocked");
61
+ });
62
+ });