@polygraph/opencode-plugin 0.4.32 → 0.4.34
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 +13 -65
- package/agent-capture-mapping.mjs +62 -3
- package/package.json +1 -1
- package/server.js +24 -12
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<h1 align="center">Polygraph Skills</h1>
|
|
9
9
|
|
|
10
10
|
<p align="center">
|
|
11
|
-
AI agent skills and subagents for <a href="https://
|
|
11
|
+
AI agent skills and subagents for <a href="https://trypolygraph.com/">Polygraph</a> — the meta-harness for maximum agentic autonomy, giving agents visibility across every repo and memory that survives every session.
|
|
12
12
|
</p>
|
|
13
13
|
|
|
14
14
|
<p align="center">
|
|
@@ -23,7 +23,17 @@
|
|
|
23
23
|
|
|
24
24
|
## What is Polygraph?
|
|
25
25
|
|
|
26
|
-
Polygraph is a
|
|
26
|
+
Polygraph is a meta-harness for maximum agentic autonomy. It works with the agents you already use and gives them what they're missing: visibility across every repo boundary, and memory that survives every session. Agents discover how repositories relate, coordinate changes across them, and hand off or resume work later with repos, branches, PRs, and logs all preserved.
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
Run the interactive setup and follow the prompts:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
polygraph config
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
It detects your AI agent — Claude Code, Codex, OpenCode, and more — and installs the Polygraph skills and subagents for it. Re-run it any time to add another agent or update an existing install.
|
|
27
37
|
|
|
28
38
|
## Skills
|
|
29
39
|
|
|
@@ -36,66 +46,6 @@ Polygraph is a standalone product for coordinating changes across multiple repos
|
|
|
36
46
|
- **polygraph-init-subagent** — Discovers candidate repositories and initializes a Polygraph session
|
|
37
47
|
- **polygraph-delegate-subagent** — Delegates work to a child agent in another repository, polls for completion
|
|
38
48
|
|
|
39
|
-
## Codex Installer
|
|
40
|
-
|
|
41
|
-
The publishable Codex package now exposes an explicit installer CLI:
|
|
42
|
-
|
|
43
|
-
```sh
|
|
44
|
-
npx @polygraph/codex-plugin
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
That command copies the packaged Codex plugin into:
|
|
48
|
-
|
|
49
|
-
```text
|
|
50
|
-
~/.agents/plugins/polygraph
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
installs the packaged custom Codex subagents into:
|
|
54
|
-
|
|
55
|
-
```text
|
|
56
|
-
$CODEX_HOME/agents
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
updates the personal Codex marketplace at:
|
|
60
|
-
|
|
61
|
-
```text
|
|
62
|
-
~/.agents/plugins/marketplace.json
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
so the `polygraph` plugin points at `./.agents/plugins/polygraph`, and enables the plugin in:
|
|
66
|
-
|
|
67
|
-
```text
|
|
68
|
-
$CODEX_HOME/config.toml
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
`CODEX_HOME` defaults to `~/.codex` when unset.
|
|
72
|
-
|
|
73
|
-
To verify an install, run:
|
|
74
|
-
|
|
75
|
-
```sh
|
|
76
|
-
npx @polygraph/codex-plugin check
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## OpenCode Plugin
|
|
80
|
-
|
|
81
|
-
The publishable OpenCode package exposes the skills and subagents through OpenCode's native plugin system. Add it to `opencode.json`:
|
|
82
|
-
|
|
83
|
-
```json
|
|
84
|
-
{
|
|
85
|
-
"plugin": ["@polygraph/opencode-plugin"]
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
For repeatable installs, pin the npm version:
|
|
90
|
-
|
|
91
|
-
```json
|
|
92
|
-
{
|
|
93
|
-
"plugin": ["@polygraph/opencode-plugin@0.4.18"]
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
The plugin adds its packaged `skills/` directory to OpenCode's skill paths and registers the packaged Markdown agents as `subagent` entries in OpenCode config during startup.
|
|
98
|
-
|
|
99
49
|
## Development
|
|
100
50
|
|
|
101
51
|
```sh
|
|
@@ -118,10 +68,8 @@ For the strictest release flow, do not allow direct `npm publish` for the truste
|
|
|
118
68
|
|
|
119
69
|
## Learn More
|
|
120
70
|
|
|
121
|
-
- **[Polygraph](https://
|
|
71
|
+
- **[Polygraph](https://trypolygraph.com/)** — The meta-harness for maximum agentic autonomy
|
|
122
72
|
- **[@polygraph/mcp](https://www.npmjs.com/package/@polygraph/mcp)** — The MCP server that powers Polygraph tools
|
|
123
|
-
- **[Nx AI Agent Skills](https://github.com/nrwl/nx-ai-agents-config)** — The main Nx AI agent skills repo
|
|
124
|
-
|
|
125
73
|
## License
|
|
126
74
|
|
|
127
75
|
License information is defined in the package metadata.
|
|
@@ -17,7 +17,15 @@
|
|
|
17
17
|
// - Refresh: preserves firstSeenAt when a valid prior mapping exists.
|
|
18
18
|
// - All failures are silently swallowed.
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
appendFileSync,
|
|
22
|
+
existsSync,
|
|
23
|
+
mkdirSync,
|
|
24
|
+
readFileSync,
|
|
25
|
+
renameSync,
|
|
26
|
+
statSync,
|
|
27
|
+
writeFileSync,
|
|
28
|
+
} from 'node:fs';
|
|
21
29
|
import { homedir } from 'node:os';
|
|
22
30
|
import path from 'node:path';
|
|
23
31
|
|
|
@@ -25,6 +33,55 @@ function sanitizeMappingFilename(str) {
|
|
|
25
33
|
return str.replace(/[^A-Za-z0-9._-]/g, '_');
|
|
26
34
|
}
|
|
27
35
|
|
|
36
|
+
const HOOK_LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Append a one-line JSON record of a hook failure to ~/.polygraph/logs/hooks.log.
|
|
40
|
+
*
|
|
41
|
+
* Hooks otherwise swallow their errors silently (a broken hook must never break
|
|
42
|
+
* the agent session) and must never write to stdout — so this on-disk log is the
|
|
43
|
+
* only record that something went wrong. The logger is itself failure-proof:
|
|
44
|
+
* any error here is swallowed so a logging bug can never break a hook.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} hook Identifier for the failing hook.
|
|
47
|
+
* @param {unknown} error The thrown value.
|
|
48
|
+
* @param {object} [meta] Extra context to record (sessionID, etc.).
|
|
49
|
+
* @param {string} [home] Override HOME for testing.
|
|
50
|
+
*/
|
|
51
|
+
export function logHookFailure(
|
|
52
|
+
hook,
|
|
53
|
+
error,
|
|
54
|
+
meta = {},
|
|
55
|
+
home = process.env.HOME?.trim() || homedir()
|
|
56
|
+
) {
|
|
57
|
+
try {
|
|
58
|
+
const logsDir = path.join(home, '.polygraph', 'logs');
|
|
59
|
+
mkdirSync(logsDir, { recursive: true });
|
|
60
|
+
const logFile = path.join(logsDir, 'hooks.log');
|
|
61
|
+
|
|
62
|
+
// Best-effort rotation so the log can't grow unbounded.
|
|
63
|
+
try {
|
|
64
|
+
if (statSync(logFile).size > HOOK_LOG_MAX_BYTES) {
|
|
65
|
+
renameSync(logFile, `${logFile}.1`);
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
// no prior log, or rotation failed — ignore
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const entry = {
|
|
72
|
+
time: new Date().toISOString(),
|
|
73
|
+
hook,
|
|
74
|
+
pid: process.pid,
|
|
75
|
+
...meta,
|
|
76
|
+
error: error instanceof Error ? error.message : String(error),
|
|
77
|
+
...(error instanceof Error && error.stack ? { stack: error.stack } : {}),
|
|
78
|
+
};
|
|
79
|
+
appendFileSync(logFile, JSON.stringify(entry) + '\n');
|
|
80
|
+
} catch {
|
|
81
|
+
// Logging must never throw — a failing logger must not break the hook.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
28
85
|
/**
|
|
29
86
|
* Write (or refresh) the agent-capture mapping for an OpenCode session.
|
|
30
87
|
* Reads POLYGRAPH_SESSION_ID and POLYGRAPH_CHILD_AGENT from process.env.
|
|
@@ -84,7 +141,9 @@ export function writeAgentCaptureMapping(
|
|
|
84
141
|
|
|
85
142
|
writeFileSync(tmpPath, JSON.stringify(mapping, null, 2) + '\n');
|
|
86
143
|
renameSync(tmpPath, finalPath);
|
|
87
|
-
} catch {
|
|
88
|
-
// Silent — a broken plugin hook must never break the
|
|
144
|
+
} catch (error) {
|
|
145
|
+
// Silent toward the agent — a broken plugin hook must never break the
|
|
146
|
+
// session — but record it so failures are not invisible.
|
|
147
|
+
logHookFailure('opencode:writeAgentCaptureMapping', error, { agentSessionId }, home);
|
|
89
148
|
}
|
|
90
149
|
}
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -14,7 +14,7 @@ import path from 'node:path';
|
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
import yaml from 'js-yaml';
|
|
16
16
|
|
|
17
|
-
import { writeAgentCaptureMapping } from './agent-capture-mapping.mjs';
|
|
17
|
+
import { writeAgentCaptureMapping, logHookFailure } from './agent-capture-mapping.mjs';
|
|
18
18
|
|
|
19
19
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
20
20
|
const packageRoot = __dirname;
|
|
@@ -39,11 +39,16 @@ export const PolygraphPlugin = async () => {
|
|
|
39
39
|
},
|
|
40
40
|
|
|
41
41
|
'shell.env': async (input, output) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
try {
|
|
43
|
+
output.env.POLYGRAPH_AGENT_SESSION_ID = input.sessionID;
|
|
44
|
+
output.env.POLYGRAPH_AGENT_TYPE = 'opencode';
|
|
45
|
+
// Record the agent-capture mapping so the Polygraph CLI can bind
|
|
46
|
+
// parent-log capture deterministically for this session.
|
|
47
|
+
writeAgentCaptureMapping(input.sessionID);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// Never let a hook failure break the OpenCode session; just record it.
|
|
50
|
+
logHookFailure('opencode:shell.env', error, { sessionID: input?.sessionID });
|
|
51
|
+
}
|
|
47
52
|
},
|
|
48
53
|
|
|
49
54
|
// OpenCode has no SessionStart hook, but the Polygraph CLI already seeds the
|
|
@@ -52,13 +57,20 @@ export const PolygraphPlugin = async () => {
|
|
|
52
57
|
// before each compaction; the note is appended to the summarization prompt
|
|
53
58
|
// (best-effort — we trust the model to keep the id + repos in the summary).
|
|
54
59
|
'experimental.session.compacting': async (input, output) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
try {
|
|
61
|
+
const note = polygraphCompactionNote(input.sessionID);
|
|
62
|
+
if (note) {
|
|
63
|
+
output.context.push(note);
|
|
64
|
+
}
|
|
65
|
+
// Refresh the mapping on compaction (same refresh semantics as Claude/Codex
|
|
66
|
+
// SessionStart hooks firing on 'compact').
|
|
67
|
+
writeAgentCaptureMapping(input.sessionID);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
// Never let a hook failure break the OpenCode session; just record it.
|
|
70
|
+
logHookFailure('opencode:session.compacting', error, {
|
|
71
|
+
sessionID: input?.sessionID,
|
|
72
|
+
});
|
|
58
73
|
}
|
|
59
|
-
// Refresh the mapping on compaction (same refresh semantics as Claude/Codex
|
|
60
|
-
// SessionStart hooks firing on 'compact').
|
|
61
|
-
writeAgentCaptureMapping(input.sessionID);
|
|
62
74
|
},
|
|
63
75
|
};
|
|
64
76
|
};
|