@agimon-ai/po-hook 0.0.0-alpha.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.
- package/LICENSE +21 -0
- package/README.md +177 -0
- package/dist/apiContracts.cjs +17 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +53 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +13 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/authoring.cjs +10 -0
- package/dist/authoring.cjs.map +1 -0
- package/dist/authoring.d.mts +7 -0
- package/dist/authoring.d.mts.map +1 -0
- package/dist/authoring.mjs +9 -0
- package/dist/authoring.mjs.map +1 -0
- package/dist/extensions/mcp.cjs +26 -0
- package/dist/extensions/mcp.cjs.map +1 -0
- package/dist/extensions/mcp.d.mts +5 -0
- package/dist/extensions/mcp.d.mts.map +1 -0
- package/dist/extensions/mcp.mjs +21 -0
- package/dist/extensions/mcp.mjs.map +1 -0
- package/dist/extensions/server.cjs +611 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +606 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/hookModule-f-HwSbmi.d.mts +54 -0
- package/dist/hookModule-f-HwSbmi.d.mts.map +1 -0
- package/dist/hookModules-DSheklXp.cjs +899 -0
- package/dist/hookModules-DSheklXp.cjs.map +1 -0
- package/dist/hookModules-GoRDG8mg.mjs +749 -0
- package/dist/hookModules-GoRDG8mg.mjs.map +1 -0
- package/dist/index.cjs +23 -0
- package/dist/index.d.mts +362 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/llms.txt +11 -0
- package/package.json +122 -0
- package/src/prompts/po-author-hook/SKILL.md +128 -0
package/package.json
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agimon-ai/po-hook",
|
|
3
|
+
"version": "0.0.0-alpha.0",
|
|
4
|
+
"description": "Claude-Code-compatible repository and plugin hook runner for Po sessions.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"claude-code-hooks",
|
|
8
|
+
"coding-agent",
|
|
9
|
+
"developer-tools",
|
|
10
|
+
"guardrails",
|
|
11
|
+
"hooks",
|
|
12
|
+
"pi-coding-agent",
|
|
13
|
+
"pi-extension",
|
|
14
|
+
"pi-package",
|
|
15
|
+
"po",
|
|
16
|
+
"repository-hooks"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://agimon.ai",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/AgiFlow/po/issues"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Vuong Ngo",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/AgiFlow/po.git",
|
|
27
|
+
"directory": "packages/default/po-hook"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"llms.txt",
|
|
32
|
+
"README.md",
|
|
33
|
+
"src/prompts",
|
|
34
|
+
"LICENSE",
|
|
35
|
+
"package.json"
|
|
36
|
+
],
|
|
37
|
+
"type": "module",
|
|
38
|
+
"main": "./dist/index.cjs",
|
|
39
|
+
"module": "./dist/index.mjs",
|
|
40
|
+
"types": "./dist/index.d.mts",
|
|
41
|
+
"jsnext:main": "./dist/index.mjs",
|
|
42
|
+
"exports": {
|
|
43
|
+
".": {
|
|
44
|
+
"types": "./dist/index.d.mts",
|
|
45
|
+
"import": "./dist/index.mjs",
|
|
46
|
+
"require": "./dist/index.cjs"
|
|
47
|
+
},
|
|
48
|
+
"./apiContracts": {
|
|
49
|
+
"types": "./dist/apiContracts.d.mts",
|
|
50
|
+
"import": "./dist/apiContracts.mjs",
|
|
51
|
+
"require": "./dist/apiContracts.cjs"
|
|
52
|
+
},
|
|
53
|
+
"./authoring": {
|
|
54
|
+
"types": "./dist/authoring.d.mts",
|
|
55
|
+
"import": "./dist/authoring.mjs",
|
|
56
|
+
"require": "./dist/authoring.cjs"
|
|
57
|
+
},
|
|
58
|
+
"./extensions/server": {
|
|
59
|
+
"types": "./dist/extensions/server.d.mts",
|
|
60
|
+
"import": "./dist/extensions/server.mjs",
|
|
61
|
+
"require": "./dist/extensions/server.cjs"
|
|
62
|
+
},
|
|
63
|
+
"./package.json": "./package.json"
|
|
64
|
+
},
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public"
|
|
67
|
+
},
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"@agimon-ai/po-config": "0.0.0-alpha.0",
|
|
70
|
+
"@agimon-ai/po-core": "0.0.0-alpha.0",
|
|
71
|
+
"@agimon-ai/po-telemetry": "0.0.0-alpha.0",
|
|
72
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
73
|
+
"yaml": "2.9.1"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@agimon-ai/po-build": "0.0.0-alpha.0",
|
|
77
|
+
"@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.0-alpha.0",
|
|
78
|
+
"@earendil-works/pi-coding-agent": "1.0.2",
|
|
79
|
+
"@types/node": "26.6.4",
|
|
80
|
+
"@vitest/coverage-v8": "5.0.3",
|
|
81
|
+
"tsdown": "0.23.0",
|
|
82
|
+
"typescript": "7.0.2",
|
|
83
|
+
"vitest": "5.0.3"
|
|
84
|
+
},
|
|
85
|
+
"peerDependencies": {
|
|
86
|
+
"@earendil-works/pi-coding-agent": "1.0.2"
|
|
87
|
+
},
|
|
88
|
+
"peerDependenciesMeta": {
|
|
89
|
+
"@earendil-works/pi-coding-agent": {
|
|
90
|
+
"optional": true
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"engines": {
|
|
94
|
+
"node": ">=24"
|
|
95
|
+
},
|
|
96
|
+
"poMcp": {
|
|
97
|
+
"entry": "./generated/mcp.ts",
|
|
98
|
+
"dist": "./dist/extensions/mcp.mjs",
|
|
99
|
+
"scopes": [
|
|
100
|
+
"session"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"poServer": {
|
|
104
|
+
"entry": "./generated/server.ts",
|
|
105
|
+
"dist": "./dist/extensions/server.mjs",
|
|
106
|
+
"scopes": [
|
|
107
|
+
"session"
|
|
108
|
+
],
|
|
109
|
+
"contracts": {
|
|
110
|
+
"entry": "./src/exports/apiContracts.ts",
|
|
111
|
+
"dist": "./dist/apiContracts.mjs"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"scripts": {
|
|
115
|
+
"build": "tsdown && pnpm build:mcp",
|
|
116
|
+
"build:mcp": "tsdown --config tsdown.mcp.config.ts",
|
|
117
|
+
"test": "vitest --run",
|
|
118
|
+
"typecheck": "tsc --noEmit",
|
|
119
|
+
"lint": "oxlint . && oxfmt . --check",
|
|
120
|
+
"fixcode": "oxlint . --fix && oxfmt ."
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: po-author-hook
|
|
3
|
+
description: Author Po repository or plugin hooks. Use when creating or changing .po/hooks.yaml, selecting hook groups from modes.yaml, writing hook commands, or adapting Claude Code hook payloads and decisions to Po.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Author Po hooks
|
|
7
|
+
|
|
8
|
+
Write the smallest hook that enforces the requested policy. Hooks are trusted executable code, not sandboxed guardrails.
|
|
9
|
+
|
|
10
|
+
## Registry and selection
|
|
11
|
+
|
|
12
|
+
Define personal hooks in `~/.po/hooks.yaml` and repository hooks in `<repo>/.po/hooks.yaml`:
|
|
13
|
+
|
|
14
|
+
```yaml
|
|
15
|
+
groups:
|
|
16
|
+
safety:
|
|
17
|
+
core: true
|
|
18
|
+
hooks:
|
|
19
|
+
- event: PreToolUse
|
|
20
|
+
pi:
|
|
21
|
+
matcher: Bash
|
|
22
|
+
module: .po/hooks/guard.ts
|
|
23
|
+
timeout: 10
|
|
24
|
+
skipInSubagent: true
|
|
25
|
+
order: 0
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- Each `pi` row declares exactly one of `command` or `module`. Module paths resolve relative to the declaring repository root or `~/.po` root.
|
|
29
|
+
- A repository group replaces a personal group of the same ID completely.
|
|
30
|
+
- `core: true` always runs. Unset `hookGroups` means all groups; explicit `hookGroups: []` means core only. An explicit list selects those groups plus core.
|
|
31
|
+
- Keep `@agimon-ai/po-hook` in active packages. Layers select groups through `hookGroups` in `modes.yaml`.
|
|
32
|
+
- `matcher` is a regular expression over Claude tool names such as `Bash` and `Write`, including for module rows.
|
|
33
|
+
- Lower `order` runs first, then declaration order. Registry commands/modules interleave before plugin commands. Explicit denial ends the chain.
|
|
34
|
+
- `skipInSubagent: true` excludes a row from child tool dispatch. Use it for parent-owned effects, not to control lifecycle ownership.
|
|
35
|
+
|
|
36
|
+
## Lightweight module API
|
|
37
|
+
|
|
38
|
+
Import from the dependency-free authoring subpath:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { defineDoomHook } from '@agimon-ai/po-hook/authoring';
|
|
42
|
+
|
|
43
|
+
export default defineDoomHook({
|
|
44
|
+
setup(ctx) {
|
|
45
|
+
let calls = 0;
|
|
46
|
+
return {
|
|
47
|
+
tool_call(event, ctx) {
|
|
48
|
+
calls += 1;
|
|
49
|
+
if (event.toolName === 'bash' && String(event.input.command).includes('rm -rf')) {
|
|
50
|
+
return { block: true, reason: 'Use a non-destructive command.' };
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
async dispose() {
|
|
54
|
+
// Release session-owned resources.
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The default export must expose `setup(ctx)`, returning handlers synchronously or asynchronously. Registry events select native handlers:
|
|
62
|
+
|
|
63
|
+
- `SessionStart`: `session_start(event, ctx)`, returns void.
|
|
64
|
+
- `PreToolUse`: `tool_call(event, ctx)`, returns void or `{ block?: boolean, reason?: string }`.
|
|
65
|
+
- `PostToolUse`: `tool_result(event, ctx)`, returns void or `{ content?, details?, isError? }`.
|
|
66
|
+
- `Stop`: `agent_settled(event, ctx)`, returns void and cannot send messages.
|
|
67
|
+
- Optional `dispose()` returns void or a promise. No module `SessionEnd` exists.
|
|
68
|
+
|
|
69
|
+
Native events use Pi tool names such as `bash`. Tool-call handlers may mutate `event.input`, which must remain a JSON object. Accepted changes reach later rows and the tool. Tool-result patches support text/image content, JSON-compatible details, and a boolean `isError`. Failed, invalid, or late calls cannot publish mutations or patches. Lifecycle handlers cannot return decisions.
|
|
70
|
+
|
|
71
|
+
## Full HookContext contract
|
|
72
|
+
|
|
73
|
+
The authoring API exports `HookContext`, `HookHandlers`, `HookModule`, `HookModuleDescriptor`, `AgentSettledEvent`, `Awaitable`, and `JsonValue` types:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
interface HookContext {
|
|
77
|
+
readonly sessionId: string;
|
|
78
|
+
readonly parentSessionId?: string;
|
|
79
|
+
readonly agent?: string;
|
|
80
|
+
readonly isSubagent: boolean;
|
|
81
|
+
readonly cwd: string;
|
|
82
|
+
readonly repoRoot: string;
|
|
83
|
+
readonly model?: { readonly provider: string; readonly id: string };
|
|
84
|
+
readonly signal: AbortSignal;
|
|
85
|
+
sendMessage(text: string, delivery: 'steer' | 'followUp'): Promise<void>;
|
|
86
|
+
appendCustomEntry(type: string, data: JsonValue): Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Optional identity/model fields may be absent, including model in native children. `cwd` is the session working directory; `repoRoot` is its resolved repository root. Await context effects. Messages use host prompt admission; admission failures propagate rather than becoming advisory hook failures. Custom entries persist JSON-compatible data in session history.
|
|
91
|
+
|
|
92
|
+
Setup and row calls for one module/session are serialized. Multiple rows share one setup instance. Store mutable session state in the setup closure, never module globals, which may be shared across sessions. Closure state is not durable across reloads; record custom entries explicitly when needed.
|
|
93
|
+
|
|
94
|
+
Setup gets a lifetime context; handlers get invocation contexts reflecting cancellation. Invocation effects are revoked after completion. Setup failure, setup/handler timeout, or cancellation quarantines the instance and revokes its effects. Honor `signal`; revocation cannot terminate arbitrary JavaScript or undo completed external effects.
|
|
95
|
+
|
|
96
|
+
## Command payloads and decisions
|
|
97
|
+
|
|
98
|
+
Commands run through `/bin/bash -c` from the repository root with Claude-compatible JSON on stdin. Environment fields include `CLAUDE_PROJECT_DIR`, `CODEX_REPO_ROOT`, `ORIGINAL_REPO_PATH`, and `CLAUDE_PLUGIN_ROOT`, the declaring root. Native child payloads use the child's own `session_id`, plus `parent_session_id` and `agent_type`.
|
|
99
|
+
|
|
100
|
+
The final stdout line beginning with `{` is parsed. Denial is `decision: "block"` or `hookSpecificOutput.permissionDecision: "deny"`. Reasons use top-level `reason` or `hookSpecificOutput.reason`; context uses `hookSpecificOutput.additionalContext`.
|
|
101
|
+
|
|
102
|
+
- `SessionStart` adds additional context to the conversation.
|
|
103
|
+
- `PreToolUse` denial blocks. Additional context also blocks the current call with that text as its reason so the agent can reconsider, rather than silently steering execution.
|
|
104
|
+
- `PostToolUse` appends additional context or reason; denial marks the result as an error.
|
|
105
|
+
- `Stop` denial with a reason requests a follow-up turn. At most five consecutive refusals schedule follow-ups; a further refusal is reported and stops without another turn. A stop without a refusal reason resets the count. `stop_hook_active` indicates a prior active refusal.
|
|
106
|
+
- `SessionEnd` is plugin-command-only and runs for side effects.
|
|
107
|
+
|
|
108
|
+
Module `agent_settled` cannot implement command Stop continuation by returning a decision or sending a message.
|
|
109
|
+
|
|
110
|
+
## Ownership, sync, and failures
|
|
111
|
+
|
|
112
|
+
Terminal parents use Pi tool and lifecycle handlers. For headless parents, the Pi bridge owns `SessionStart`, `Stop`, and plugin `SessionEnd`; server facets alone own tool dispatch. Lifecycle/tool paths share session module setup. Session-start readiness gates dependent turns/tools. Shutdown runs session-end once before disposal.
|
|
113
|
+
|
|
114
|
+
Native children in both adapters run only `PreToolUse` and `PostToolUse`, never lifecycle rows, regardless of `skipInSubagent`. Children have separate setup/state and retain their launch selection and compiled modules after parent reload/disposal.
|
|
115
|
+
|
|
116
|
+
After editing modules/imports, registry, or selection, restart or reopen the session. Each new session reads the registry live: TypeScript hook modules compile into content-addressed artifacts and built JavaScript loads in place, for core plus the union of mode-selected groups, or all groups if any mode leaves selection unset. A module that cannot be compiled is left out with a notice and its rows fail closed. Runtime imports only descriptor-mapped artifacts. A running session keeps the descriptor it started with.
|
|
117
|
+
|
|
118
|
+
`timeout` is positive finite seconds, default 10. A module's first invocation budget includes import/setup and handler execution. Execution/setup errors, invalid results, and timeouts are advisory missed checks, not automatic denials. Quarantined instances do not resume checks in that session. Host readiness, cancellation, and context effect/admission failures are not advisory execution failures. Command nonzero exits, invalid JSON, spawn failures, and timeouts are advisory; timeout terminates the process group with SIGTERM then SIGKILL two seconds later.
|
|
119
|
+
|
|
120
|
+
Compilation/pinning ensure consistency, not isolation or authorization. Modules run in the host Node.js process with host privileges. Synchronous infinite loops, `process.exit`, and out-of-memory failures cannot be contained in-process. Prefer `command:` when process isolation and hard termination matter. Serialization and session-local setup do not isolate globals, filesystem/network access, or external effects. Context revocation gates only the exposed context methods. Do not run untrusted hooks or promise guaranteed enforcement from advisory checks.
|
|
121
|
+
|
|
122
|
+
## Verification
|
|
123
|
+
|
|
124
|
+
1. Run commands directly with representative stdin JSON; sync module sources and their imports.
|
|
125
|
+
2. Confirm activation with the intended mode and `po --explain`.
|
|
126
|
+
3. Reload/restart and exercise exact events in a disposable session, including child tool calls, cancellation, timeout, and `skipInSubagent` behavior.
|
|
127
|
+
4. Check failure reporting, shared lifecycle/tool setup, result/input changes, and once-only shutdown.
|
|
128
|
+
5. Distinguish runtime proof from mocks: verified functional proof covers real Pi bridge dispatch, shared setup, admission markers, cancellation, native command rows, cold descriptor pinning, and both real native child adapters. Direct/MCP command-row proof explicitly mocks tool bodies. Do not claim full intercom plugin integration from that evidence.
|