predicate-skill 1.2.0 → 1.6.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/README.md +86 -17
- package/cli.bundle.mjs +27912 -248
- package/hooks/codex-cli/README.md +30 -0
- package/hooks/codex-cli/config.toml.template +10 -0
- package/hooks/codex-cli/pre-compact.sh +4 -0
- package/hooks/codex-cli/session-start.sh +7 -0
- package/hooks/codex-cli/stop.sh +4 -0
- package/hooks/cursor/README.md +46 -0
- package/hooks/cursor/mcp.json.template +12 -0
- package/hooks/cursor/pre-compact.sh +7 -0
- package/hooks/cursor/session-start.sh +7 -0
- package/hooks/cursor/stop.sh +5 -0
- package/hooks/gemini-cli/README.md +29 -0
- package/hooks/gemini-cli/pre-compact.sh +5 -0
- package/hooks/gemini-cli/session-start.sh +6 -0
- package/hooks/gemini-cli/settings.json.template +17 -0
- package/hooks/gemini-cli/stop.sh +5 -0
- package/hooks/hooks.json +15 -0
- package/hooks/opencode/README.md +31 -0
- package/hooks/opencode/opencode.json.template +18 -0
- package/hooks/opencode/pre-compact.sh +5 -0
- package/hooks/opencode/session-start.sh +6 -0
- package/hooks/opencode/stop.sh +4 -0
- package/hooks/post-tool-use.sh +10 -0
- package/hooks/pre-tool-use.sh +11 -0
- package/hooks/session-start.sh +7 -19
- package/hooks/stop.sh +20 -0
- package/hooks/vscode-copilot/README.md +43 -0
- package/hooks/vscode-copilot/pre-compact.sh +4 -0
- package/hooks/vscode-copilot/session-start.sh +7 -0
- package/hooks/vscode-copilot/settings.json.template +12 -0
- package/hooks/vscode-copilot/stop.sh +5 -0
- package/package.json +1 -1
- package/server.bundle.mjs +76 -10
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Codex CLI adapter
|
|
2
|
+
|
|
3
|
+
## Install MCP server
|
|
4
|
+
|
|
5
|
+
Merge `config.toml.template` into `~/.codex/config.toml`, replacing
|
|
6
|
+
`__PLUGIN_DIR__` with the absolute path to this package. The 8 `kg_*`
|
|
7
|
+
tools will be available the next time you launch `codex`.
|
|
8
|
+
|
|
9
|
+
## Hooks
|
|
10
|
+
|
|
11
|
+
Codex CLI does not expose SessionStart, PreCompact, or Stop lifecycle
|
|
12
|
+
events as of writing. The three scripts in this directory are provided
|
|
13
|
+
so you can:
|
|
14
|
+
|
|
15
|
+
1. Run `session-start.sh` manually and paste output into your initial
|
|
16
|
+
Codex prompt. Or alias:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
# in ~/.zshrc or ~/.bashrc
|
|
20
|
+
codex() { command codex --context "$(predicate sessionstart 2>/dev/null)" "$@"; }
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
2. Wire `pre-compact.sh` and `stop.sh` to cron for periodic maintenance:
|
|
24
|
+
|
|
25
|
+
```cron
|
|
26
|
+
*/30 * * * * /absolute/path/hooks/codex-cli/pre-compact.sh >/dev/null 2>&1
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If Codex CLI adds lifecycle hooks in the future, this adapter is ready
|
|
30
|
+
to wire them — script logic is unchanged.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Merge into ~/.codex/config.toml — replace __PLUGIN_DIR__ with the
|
|
2
|
+
# absolute path to packages/predicate-skill.
|
|
3
|
+
|
|
4
|
+
[mcp_servers.predicate]
|
|
5
|
+
command = "node"
|
|
6
|
+
args = ["__PLUGIN_DIR__/server.bundle.mjs"]
|
|
7
|
+
|
|
8
|
+
[mcp_servers.predicate.env]
|
|
9
|
+
FUSEKI_URL = "http://localhost:3030"
|
|
10
|
+
PREDICATE_DATASET = "predicate"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Codex CLI session-start adapter. Codex has no native SessionStart event;
|
|
3
|
+
# run this manually before a session and paste the output as initial context,
|
|
4
|
+
# or alias it to `codex` in your shell rc.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
predicate sessionstart 2>/dev/null || \
|
|
7
|
+
echo "Predicate: Fuseki not reachable; run \`predicate up\` first."
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Cursor adapter
|
|
2
|
+
|
|
3
|
+
Predicate exposes its 8 `kg_*` tools to Cursor over MCP, plus three optional
|
|
4
|
+
maintenance scripts you can wire into cron.
|
|
5
|
+
|
|
6
|
+
## 1. MCP server
|
|
7
|
+
|
|
8
|
+
Copy `mcp.json.template` to `.cursor/mcp.json` (project-local) or
|
|
9
|
+
`~/.cursor/mcp.json` (global), replacing `__PLUGIN_DIR__` with the absolute
|
|
10
|
+
path to your local clone, e.g. `/Users/you/code/predicate/packages/predicate-skill`.
|
|
11
|
+
|
|
12
|
+
Then in Cursor restart MCP (Cmd-Shift-P → "Reload MCP servers") and the 8
|
|
13
|
+
`kg_*` tools will be available.
|
|
14
|
+
|
|
15
|
+
## 2. Optional: SessionStart context
|
|
16
|
+
|
|
17
|
+
Cursor has no native SessionStart event. Two options:
|
|
18
|
+
|
|
19
|
+
**a. Manual:** Run `bash session-start.sh` in your terminal; paste the
|
|
20
|
+
output into `.cursor/rules/predicate.md`.
|
|
21
|
+
|
|
22
|
+
**b. Cron:** Refresh the rule file periodically:
|
|
23
|
+
|
|
24
|
+
```cron
|
|
25
|
+
*/10 * * * * bash /absolute/path/hooks/cursor/session-start.sh > /project/.cursor/rules/predicate.md
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 3. Optional: PreCompact maintenance
|
|
29
|
+
|
|
30
|
+
Cursor has no native PreCompact event. Wire `pre-compact.sh` to cron so the
|
|
31
|
+
KG stays tidy between sessions:
|
|
32
|
+
|
|
33
|
+
```cron
|
|
34
|
+
*/30 * * * * /absolute/path/hooks/cursor/pre-compact.sh >/dev/null 2>&1
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 4. Optional: Stop maintenance
|
|
38
|
+
|
|
39
|
+
Run `bash stop.sh` manually after a long session, or wire it into a shell
|
|
40
|
+
shutdown alias.
|
|
41
|
+
|
|
42
|
+
## Notes
|
|
43
|
+
|
|
44
|
+
All scripts require `predicate` on `$PATH`. Install with
|
|
45
|
+
`npm install -g predicate-skill`, or use the absolute path:
|
|
46
|
+
`/abs/path/to/predicate/packages/predicate-skill/cli.bundle.mjs`.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Cursor pre-compact adapter: trims low-confidence stale facts and
|
|
3
|
+
# promotes any matured staged TBox proposals before context compaction.
|
|
4
|
+
# Cursor has no native PreCompact event — run manually or via cron, e.g.:
|
|
5
|
+
# */30 * * * * /path/to/hooks/cursor/pre-compact.sh
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
predicate maintain
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Cursor session-start adapter: emits a plain text status line.
|
|
3
|
+
# Cursor reads stdout when invoked from a custom rule script;
|
|
4
|
+
# can also be run manually and pasted into .cursor/rules/predicate.md.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
predicate sessionstart 2>/dev/null || \
|
|
7
|
+
echo "Predicate: Fuseki not reachable; run \`predicate up\` first."
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Gemini CLI adapter
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
Merge `settings.json.template` into `~/.gemini/settings.json`, replacing
|
|
6
|
+
`__PLUGIN_DIR__` with the absolute path to this package
|
|
7
|
+
(e.g. `/Users/you/code/predicate/packages/predicate-skill`).
|
|
8
|
+
|
|
9
|
+
Restart Gemini CLI. The 8 `kg_*` tools will be available; the three hook
|
|
10
|
+
scripts will fire on `sessionStart`, `preCompress`, and `stop`.
|
|
11
|
+
|
|
12
|
+
## Hooks reference
|
|
13
|
+
|
|
14
|
+
| Event | Script | What it does |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `sessionStart` | `session-start.sh` | Prints KG status line; Gemini reads stdout as context. |
|
|
17
|
+
| `preCompress` | `pre-compact.sh` | Runs `predicate maintain` before context compression. |
|
|
18
|
+
| `stop` | `stop.sh` | Runs `predicate maintain` on session close. |
|
|
19
|
+
|
|
20
|
+
## If your Gemini version doesn't expose hooks
|
|
21
|
+
|
|
22
|
+
The `hooks` block is harmless if unsupported. You can still run each script
|
|
23
|
+
manually or via cron — see `../cursor/README.md` for cron examples; the
|
|
24
|
+
syntax is identical.
|
|
25
|
+
|
|
26
|
+
## Verify wiring
|
|
27
|
+
|
|
28
|
+
Run `gemini --debug` and start a fresh session; you should see Predicate's
|
|
29
|
+
KG status line printed in the debug output before your first prompt.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Gemini CLI session-start adapter. Gemini reads stdout as additional context
|
|
3
|
+
# when wired via the `hooks` block in ~/.gemini/settings.json (event: "sessionStart").
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
predicate sessionstart 2>/dev/null || \
|
|
6
|
+
echo "Predicate: Fuseki not reachable; run \`predicate up\` first."
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"predicate": {
|
|
4
|
+
"command": "node",
|
|
5
|
+
"args": ["__PLUGIN_DIR__/server.bundle.mjs"],
|
|
6
|
+
"env": {
|
|
7
|
+
"FUSEKI_URL": "http://localhost:3030",
|
|
8
|
+
"PREDICATE_DATASET": "predicate"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"hooks": [
|
|
13
|
+
{ "event": "sessionStart", "command": "bash __PLUGIN_DIR__/hooks/gemini-cli/session-start.sh" },
|
|
14
|
+
{ "event": "preCompress", "command": "bash __PLUGIN_DIR__/hooks/gemini-cli/pre-compact.sh" },
|
|
15
|
+
{ "event": "stop", "command": "bash __PLUGIN_DIR__/hooks/gemini-cli/stop.sh" }
|
|
16
|
+
]
|
|
17
|
+
}
|
package/hooks/hooks.json
CHANGED
|
@@ -4,6 +4,21 @@
|
|
|
4
4
|
"event": "SessionStart",
|
|
5
5
|
"matcher": "startup|clear|compact",
|
|
6
6
|
"command": "bash ${PLUGIN_DIR}/hooks/session-start.sh"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"event": "PreToolUse",
|
|
10
|
+
"matcher": "*",
|
|
11
|
+
"command": "bash ${PLUGIN_DIR}/hooks/pre-tool-use.sh"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"event": "PostToolUse",
|
|
15
|
+
"matcher": "*",
|
|
16
|
+
"command": "bash ${PLUGIN_DIR}/hooks/post-tool-use.sh"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"event": "Stop",
|
|
20
|
+
"matcher": "*",
|
|
21
|
+
"command": "bash ${PLUGIN_DIR}/hooks/stop.sh"
|
|
7
22
|
}
|
|
8
23
|
]
|
|
9
24
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# OpenCode adapter
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
Merge `opencode.json.template` into `~/.config/opencode/opencode.json`
|
|
6
|
+
(or your project-local `opencode.json`), replacing `__PLUGIN_DIR__` with
|
|
7
|
+
the absolute path to this package.
|
|
8
|
+
|
|
9
|
+
Restart OpenCode. The 8 `kg_*` tools will be available, and the three
|
|
10
|
+
hook scripts will fire on `session.started`, `session.compacted`, and
|
|
11
|
+
`session.stopped`.
|
|
12
|
+
|
|
13
|
+
## Hooks reference
|
|
14
|
+
|
|
15
|
+
| Event | Script | What it does |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| `session.started` | `session-start.sh` | Prints KG status line; OpenCode reads stdout as context. |
|
|
18
|
+
| `session.compacted` | `pre-compact.sh` | Runs `predicate maintain` before context compression. |
|
|
19
|
+
| `session.stopped` | `stop.sh` | Runs `predicate maintain` on session close. |
|
|
20
|
+
|
|
21
|
+
## Verify wiring
|
|
22
|
+
|
|
23
|
+
Start an OpenCode session and check the debug log; you should see
|
|
24
|
+
Predicate's KG status line in the initial context, and `predicate maintain`
|
|
25
|
+
output when the session compacts or stops.
|
|
26
|
+
|
|
27
|
+
## If event names changed in your OpenCode version
|
|
28
|
+
|
|
29
|
+
Consult `opencode --help events` (or the OpenCode docs) for the current
|
|
30
|
+
event names. The scripts are event-agnostic — only the template's `on:`
|
|
31
|
+
keys need to match.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"mcp": {
|
|
4
|
+
"predicate": {
|
|
5
|
+
"type": "local",
|
|
6
|
+
"command": ["node", "__PLUGIN_DIR__/server.bundle.mjs"],
|
|
7
|
+
"environment": {
|
|
8
|
+
"FUSEKI_URL": "http://localhost:3030",
|
|
9
|
+
"PREDICATE_DATASET": "predicate"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"events": [
|
|
14
|
+
{ "on": "session.started", "run": "bash __PLUGIN_DIR__/hooks/opencode/session-start.sh" },
|
|
15
|
+
{ "on": "session.compacted", "run": "bash __PLUGIN_DIR__/hooks/opencode/pre-compact.sh" },
|
|
16
|
+
{ "on": "session.stopped", "run": "bash __PLUGIN_DIR__/hooks/opencode/stop.sh" }
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# OpenCode session-start adapter. OpenCode reads stdout as additional context
|
|
3
|
+
# when wired to the session.started event.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
predicate sessionstart 2>/dev/null || \
|
|
6
|
+
echo "Predicate: Fuseki not reachable; run \`predicate up\` first."
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Claude Code PostToolUse hook: records {toolName, input, output, sessionId,
|
|
3
|
+
# phase:"post"} in kg:usage. Reads Claude Code's hook payload JSON from stdin
|
|
4
|
+
# and delegates to `predicate capture --from-stdin`. Fails open.
|
|
5
|
+
set -uo pipefail
|
|
6
|
+
|
|
7
|
+
if command -v predicate >/dev/null 2>&1; then
|
|
8
|
+
predicate capture --from-stdin --phase post >/dev/null 2>&1 || true
|
|
9
|
+
fi
|
|
10
|
+
exit 0
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Claude Code PreToolUse hook: records {toolName, input, sessionId, phase:"pre"}
|
|
3
|
+
# in kg:usage. Reads Claude Code's hook payload JSON from stdin and delegates
|
|
4
|
+
# to `predicate capture --from-stdin`. Fails open: any error returns exit 0
|
|
5
|
+
# so the user's tool invocation is never blocked by capture logic.
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
if command -v predicate >/dev/null 2>&1; then
|
|
9
|
+
predicate capture --from-stdin --phase pre >/dev/null 2>&1 || true
|
|
10
|
+
fi
|
|
11
|
+
exit 0
|
package/hooks/session-start.sh
CHANGED
|
@@ -1,25 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# SessionStart hook: emits a short context block telling
|
|
3
|
-
#
|
|
2
|
+
# SessionStart hook for Claude Code: emits a short context block telling
|
|
3
|
+
# the agent what's in the KG. Delegates to `predicate sessionstart` so the
|
|
4
|
+
# message format stays in one place.
|
|
4
5
|
set -euo pipefail
|
|
5
|
-
FUSEKI="${FUSEKI_URL:-http://localhost:3030}"
|
|
6
|
-
DS="${PREDICATE_DATASET:-predicate}"
|
|
7
6
|
|
|
8
|
-
if
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
if MSG="$(predicate sessionstart 2>/dev/null)"; then
|
|
8
|
+
:
|
|
9
|
+
else
|
|
10
|
+
MSG="Predicate: Fuseki not reachable; KG tools may fail. Start it with \`predicate up\`."
|
|
11
11
|
fi
|
|
12
12
|
|
|
13
|
-
GOALS=$(curl -fsS "$FUSEKI/$DS/query" \
|
|
14
|
-
--data-urlencode "query=PREFIX pred: <https://predicate.dev/meta#>
|
|
15
|
-
SELECT (COUNT(*) AS ?n) WHERE { GRAPH <kg:goals> { ?g pred:status \"active\" } }" \
|
|
16
|
-
--header "Accept: application/sparql-results+json" \
|
|
17
|
-
| jq -r '.results.bindings[0].n.value // "0"')
|
|
18
|
-
|
|
19
|
-
CONCEPTS=$(curl -fsS "$FUSEKI/$DS/query" \
|
|
20
|
-
--data-urlencode "query=SELECT (COUNT(DISTINCT ?c) AS ?n) WHERE { GRAPH <kg:tbox> { ?c a <http://www.w3.org/2002/07/owl#Class> } }" \
|
|
21
|
-
--header "Accept: application/sparql-results+json" \
|
|
22
|
-
| jq -r '.results.bindings[0].n.value // "0"')
|
|
23
|
-
|
|
24
|
-
MSG="Predicate ready: ${GOALS} active goals, ${CONCEPTS} TBox classes. Use kg_explore_schema before drafting SPARQL."
|
|
25
13
|
jq -n --arg m "$MSG" '{ additional_context: $m }'
|
package/hooks/stop.sh
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Claude Code Stop hook: reads the Stop-hook JSON payload from stdin,
|
|
3
|
+
# runs structured turn extraction (predicate extract), then a
|
|
4
|
+
# maintenance sweep. Fail-open: any error returns exit 0 so capture
|
|
5
|
+
# never blocks the user's next prompt.
|
|
6
|
+
set -uo pipefail
|
|
7
|
+
|
|
8
|
+
if ! command -v predicate >/dev/null 2>&1; then
|
|
9
|
+
exit 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
# Buffer stdin so we can tee it into extract.
|
|
13
|
+
payload="$(cat || true)"
|
|
14
|
+
|
|
15
|
+
if [ -n "$payload" ]; then
|
|
16
|
+
printf '%s' "$payload" | predicate extract --from-stdin >/dev/null 2>&1 || true
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
predicate maintain >/dev/null 2>&1 || true
|
|
20
|
+
exit 0
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# VS Code Copilot adapter
|
|
2
|
+
|
|
3
|
+
## Install MCP server
|
|
4
|
+
|
|
5
|
+
Merge `settings.json.template` into your VS Code `settings.json`
|
|
6
|
+
(User or Workspace), replacing `__PLUGIN_DIR__` with the absolute path
|
|
7
|
+
to this package. Restart VS Code. The 8 `kg_*` tools will be available
|
|
8
|
+
to Copilot Chat.
|
|
9
|
+
|
|
10
|
+
## Hooks
|
|
11
|
+
|
|
12
|
+
VS Code Copilot does not expose SessionStart, PreCompact, or Stop
|
|
13
|
+
lifecycle events as of writing. The three scripts in this directory
|
|
14
|
+
are provided so you can:
|
|
15
|
+
|
|
16
|
+
1. Run `session-start.sh` manually before opening Copilot Chat and
|
|
17
|
+
paste the output into a prompt as initial context.
|
|
18
|
+
|
|
19
|
+
2. Wire `pre-compact.sh` and `stop.sh` to cron for periodic KG
|
|
20
|
+
maintenance — see `../cursor/README.md` for cron examples.
|
|
21
|
+
|
|
22
|
+
3. Use them in VS Code tasks (`.vscode/tasks.json`):
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"version": "2.0.0",
|
|
27
|
+
"tasks": [
|
|
28
|
+
{
|
|
29
|
+
"label": "predicate: session start",
|
|
30
|
+
"type": "shell",
|
|
31
|
+
"command": "bash ${workspaceFolder}/packages/predicate-skill/hooks/vscode-copilot/session-start.sh"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"label": "predicate: maintain",
|
|
35
|
+
"type": "shell",
|
|
36
|
+
"command": "bash ${workspaceFolder}/packages/predicate-skill/hooks/vscode-copilot/pre-compact.sh"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If VS Code adds lifecycle hooks for Copilot Chat in the future, this
|
|
43
|
+
adapter is ready to wire them — script logic is unchanged.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# VS Code Copilot session-start adapter. VS Code has no native SessionStart
|
|
3
|
+
# hook today — run this manually before invoking Copilot Chat, or wire it
|
|
4
|
+
# to a VS Code task in tasks.json.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
predicate sessionstart 2>/dev/null || \
|
|
7
|
+
echo "Predicate: Fuseki not reachable; run \`predicate up\` first."
|
package/package.json
CHANGED
package/server.bundle.mjs
CHANGED
|
@@ -3643,7 +3643,7 @@ var require_fast_uri = __commonJS({
|
|
|
3643
3643
|
normalizeString(uri, options);
|
|
3644
3644
|
} else if (typeof uri === "object") {
|
|
3645
3645
|
uri = /** @type {T} */
|
|
3646
|
-
parse3(
|
|
3646
|
+
parse3(serialize2(uri, options), options);
|
|
3647
3647
|
}
|
|
3648
3648
|
return uri;
|
|
3649
3649
|
}
|
|
@@ -3651,13 +3651,13 @@ var require_fast_uri = __commonJS({
|
|
|
3651
3651
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3652
3652
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
3653
3653
|
schemelessOptions.skipEscape = true;
|
|
3654
|
-
return
|
|
3654
|
+
return serialize2(resolved, schemelessOptions);
|
|
3655
3655
|
}
|
|
3656
3656
|
function resolveComponent(base, relative, options, skipNormalization) {
|
|
3657
3657
|
const target = {};
|
|
3658
3658
|
if (!skipNormalization) {
|
|
3659
|
-
base = parse3(
|
|
3660
|
-
relative = parse3(
|
|
3659
|
+
base = parse3(serialize2(base, options), options);
|
|
3660
|
+
relative = parse3(serialize2(relative, options), options);
|
|
3661
3661
|
}
|
|
3662
3662
|
options = options || {};
|
|
3663
3663
|
if (!options.tolerant && relative.scheme) {
|
|
@@ -3711,7 +3711,7 @@ var require_fast_uri = __commonJS({
|
|
|
3711
3711
|
const normalizedB = normalizeComparableURI(uriB, options);
|
|
3712
3712
|
return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA.toLowerCase() === normalizedB.toLowerCase();
|
|
3713
3713
|
}
|
|
3714
|
-
function
|
|
3714
|
+
function serialize2(cmpts, opts) {
|
|
3715
3715
|
const component = {
|
|
3716
3716
|
host: cmpts.host,
|
|
3717
3717
|
scheme: cmpts.scheme,
|
|
@@ -3889,7 +3889,7 @@ var require_fast_uri = __commonJS({
|
|
|
3889
3889
|
function normalizeStringWithStatus(uri, opts) {
|
|
3890
3890
|
const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
|
|
3891
3891
|
return {
|
|
3892
|
-
normalized: malformedAuthorityOrPort ? uri :
|
|
3892
|
+
normalized: malformedAuthorityOrPort ? uri : serialize2(parsed, opts),
|
|
3893
3893
|
malformedAuthorityOrPort
|
|
3894
3894
|
};
|
|
3895
3895
|
}
|
|
@@ -3899,7 +3899,7 @@ var require_fast_uri = __commonJS({
|
|
|
3899
3899
|
return malformedAuthorityOrPort ? void 0 : normalized;
|
|
3900
3900
|
}
|
|
3901
3901
|
if (typeof uri === "object") {
|
|
3902
|
-
return
|
|
3902
|
+
return serialize2(uri, opts);
|
|
3903
3903
|
}
|
|
3904
3904
|
}
|
|
3905
3905
|
var fastUri = {
|
|
@@ -3908,7 +3908,7 @@ var require_fast_uri = __commonJS({
|
|
|
3908
3908
|
resolve: resolve2,
|
|
3909
3909
|
resolveComponent,
|
|
3910
3910
|
equal,
|
|
3911
|
-
serialize,
|
|
3911
|
+
serialize: serialize2,
|
|
3912
3912
|
parse: parse3
|
|
3913
3913
|
};
|
|
3914
3914
|
module.exports = fastUri;
|
|
@@ -30990,9 +30990,9 @@ async function kgAsk(client, input) {
|
|
|
30990
30990
|
async function logUsage(client, question, sparql, rowCount, elapsedMs) {
|
|
30991
30991
|
const usage = escapeIRI(GRAPH.usage);
|
|
30992
30992
|
const id = `urn:predicate:usage:${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
30993
|
-
const
|
|
30993
|
+
const META7 = "https://predicate.dev/meta#";
|
|
30994
30994
|
await client.update(`
|
|
30995
|
-
PREFIX pred: <${
|
|
30995
|
+
PREFIX pred: <${META7}>
|
|
30996
30996
|
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
|
|
30997
30997
|
INSERT DATA { GRAPH ${usage} {
|
|
30998
30998
|
<${id}> a pred:Query ;
|
|
@@ -37832,6 +37832,51 @@ async function kgStats(client) {
|
|
|
37832
37832
|
};
|
|
37833
37833
|
}
|
|
37834
37834
|
|
|
37835
|
+
// ../predicate-mcp/src/tools/kg-capture.ts
|
|
37836
|
+
var META6 = "https://predicate.dev/meta#";
|
|
37837
|
+
function truncate(s, max) {
|
|
37838
|
+
if (s.length <= max) return s;
|
|
37839
|
+
const extra = s.length - max;
|
|
37840
|
+
return `${s.slice(0, max)} \u2026 [truncated, ${extra} more chars]`;
|
|
37841
|
+
}
|
|
37842
|
+
function serialize(value, max) {
|
|
37843
|
+
let s;
|
|
37844
|
+
if (value === void 0 || value === null) s = "";
|
|
37845
|
+
else if (typeof value === "string") s = value;
|
|
37846
|
+
else {
|
|
37847
|
+
try {
|
|
37848
|
+
s = JSON.stringify(value);
|
|
37849
|
+
} catch {
|
|
37850
|
+
s = String(value);
|
|
37851
|
+
}
|
|
37852
|
+
}
|
|
37853
|
+
return truncate(s, max);
|
|
37854
|
+
}
|
|
37855
|
+
async function kgCapture(client, input) {
|
|
37856
|
+
const t0 = Date.now();
|
|
37857
|
+
const maxChars = parseInt(process.env["PREDICATE_CAPTURE_TRUNCATE"] ?? "500", 10);
|
|
37858
|
+
const captureId = `urn:predicate:capture:${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
37859
|
+
const inputStr = serialize(input.input, maxChars);
|
|
37860
|
+
const hasOutput = input.output !== void 0 && input.output !== null;
|
|
37861
|
+
const outputStr = hasOutput ? serialize(input.output, maxChars) : "";
|
|
37862
|
+
const lines = [
|
|
37863
|
+
`${escapeIRI(captureId)} a <${META6}ToolCall> ;`,
|
|
37864
|
+
` <${META6}toolName> ${escapeLiteral(input.toolName)} ;`,
|
|
37865
|
+
` <${META6}phase> ${escapeLiteral(input.phase)} ;`,
|
|
37866
|
+
` <${META6}at> "${(/* @__PURE__ */ new Date()).toISOString()}"^^<http://www.w3.org/2001/XMLSchema#dateTime>`
|
|
37867
|
+
];
|
|
37868
|
+
if (inputStr.length > 0) lines.push(` ; <${META6}toolInput> ${escapeLiteral(inputStr)}`);
|
|
37869
|
+
if (hasOutput) lines.push(` ; <${META6}toolOutput> ${escapeLiteral(outputStr)}`);
|
|
37870
|
+
if (input.sessionId) lines.push(` ; <${META6}sessionId> ${escapeLiteral(input.sessionId)}`);
|
|
37871
|
+
lines.push(" .");
|
|
37872
|
+
await client.update(`
|
|
37873
|
+
INSERT DATA { GRAPH ${escapeIRI(GRAPH.usage)} {
|
|
37874
|
+
${lines.join("\n ")}
|
|
37875
|
+
} }
|
|
37876
|
+
`);
|
|
37877
|
+
return { captureId, elapsedMs: Date.now() - t0 };
|
|
37878
|
+
}
|
|
37879
|
+
|
|
37835
37880
|
// ../predicate-mcp/src/tools/registry.ts
|
|
37836
37881
|
var deltaQuadSchema = external_exports.object({
|
|
37837
37882
|
s: external_exports.string(),
|
|
@@ -37996,6 +38041,27 @@ function buildTools(client) {
|
|
|
37996
38041
|
inputSchema: external_exports.object({}),
|
|
37997
38042
|
handler: async () => kgStats(client)
|
|
37998
38043
|
},
|
|
38044
|
+
{
|
|
38045
|
+
name: "kg_capture",
|
|
38046
|
+
description: "Record a tool invocation (toolName, input, output, sessionId, phase) into kg:usage. Used by per-platform PreToolUse/PostToolUse hooks; safe to call directly. Returns {captureId, elapsedMs}.",
|
|
38047
|
+
inputSchema: external_exports.object({
|
|
38048
|
+
toolName: external_exports.string().min(1),
|
|
38049
|
+
input: external_exports.unknown().optional(),
|
|
38050
|
+
output: external_exports.unknown().optional(),
|
|
38051
|
+
sessionId: external_exports.string().optional(),
|
|
38052
|
+
phase: external_exports.enum(["pre", "post"])
|
|
38053
|
+
}),
|
|
38054
|
+
handler: async (raw) => {
|
|
38055
|
+
const args = external_exports.object({
|
|
38056
|
+
toolName: external_exports.string().min(1),
|
|
38057
|
+
input: external_exports.unknown().optional(),
|
|
38058
|
+
output: external_exports.unknown().optional(),
|
|
38059
|
+
sessionId: external_exports.string().optional(),
|
|
38060
|
+
phase: external_exports.enum(["pre", "post"])
|
|
38061
|
+
}).parse(raw);
|
|
38062
|
+
return kgCapture(client, args);
|
|
38063
|
+
}
|
|
38064
|
+
},
|
|
37999
38065
|
...stubs()
|
|
38000
38066
|
];
|
|
38001
38067
|
}
|