@pnds/pond 1.5.0 → 1.6.1
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/openclaw.plugin.json +5 -0
- package/package.json +4 -5
- package/skills/pond-platform/SKILL.md +50 -0
- package/skills/pond-tasks/SKILL.md +44 -0
- package/skills/pond-wiki/SKILL.md +97 -0
- package/src/config-manager.ts +2 -15
- package/src/hooks.ts +11 -73
- package/src/index.ts +0 -2
- package/TOOLS.md +0 -62
- package/src/tool-helpers.ts +0 -11
- package/src/tools-md.ts +0 -7
- package/src/wiki-tools.ts +0 -361
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnds/pond",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "OpenClaw channel plugin for Pond IM",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -11,13 +11,12 @@
|
|
|
11
11
|
"type": "module",
|
|
12
12
|
"files": [
|
|
13
13
|
"src",
|
|
14
|
-
"
|
|
14
|
+
"skills",
|
|
15
15
|
"openclaw.plugin.json"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@
|
|
19
|
-
"@pnds/
|
|
20
|
-
"@pnds/sdk": "1.5.0"
|
|
18
|
+
"@pnds/cli": "1.6.1",
|
|
19
|
+
"@pnds/sdk": "1.6.1"
|
|
21
20
|
},
|
|
22
21
|
"devDependencies": {
|
|
23
22
|
"@types/node": "^22.0.0",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pond-platform
|
|
3
|
+
description: "Pond platform queries: list org members, agents, chats, read message history, check identity. Use when: user asks about org members, who's online, chat history, agent list, or identity/auth questions."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pond Platform
|
|
7
|
+
|
|
8
|
+
Query organization data via the Pond CLI. Auth is pre-configured.
|
|
9
|
+
|
|
10
|
+
## Identity
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx @pnds/cli@latest whoami
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Members & Agents
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx @pnds/cli@latest members list # All org members (humans + agents)
|
|
20
|
+
npx @pnds/cli@latest agents list # Agents only
|
|
21
|
+
npx @pnds/cli@latest users get usr_xxx # Get user details by ID
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Chats & Messages
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx @pnds/cli@latest chats list # List all chats
|
|
28
|
+
npx @pnds/cli@latest messages list cht_xxx # Read chat message history
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Sending Messages
|
|
32
|
+
|
|
33
|
+
Each `[Event: message.new]` includes `[Chat: ... (cht_xxx)]` — always use that chat ID explicitly.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Reply to a chat (use the chat ID from the event)
|
|
37
|
+
npx @pnds/cli@latest messages send cht_xxx --text "Your reply"
|
|
38
|
+
|
|
39
|
+
# Direct message by user ID or display name
|
|
40
|
+
npx @pnds/cli@latest dm usr_xxx --text "Hello"
|
|
41
|
+
npx @pnds/cli@latest dm "Alice" --text "Hello"
|
|
42
|
+
|
|
43
|
+
# Thread reply
|
|
44
|
+
npx @pnds/cli@latest messages send cht_xxx --text "Reply" --thread-root-id 01JWC...
|
|
45
|
+
|
|
46
|
+
# FYI message (no response expected)
|
|
47
|
+
npx @pnds/cli@latest messages send cht_xxx --text "FYI: done" --no-reply
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
All CLI output is JSON.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pond-tasks
|
|
3
|
+
description: "Pond task operations: create, update, comment on, and query tasks and projects. Use when: user asks to create a task, update task status, add comments, list tasks, or manage projects."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pond Tasks
|
|
7
|
+
|
|
8
|
+
Manage tasks and projects via the Pond CLI. Auth and runtime context are pre-configured.
|
|
9
|
+
|
|
10
|
+
## Task Commands
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# List tasks (filterable by status)
|
|
14
|
+
npx @pnds/cli@latest tasks list
|
|
15
|
+
npx @pnds/cli@latest tasks list --status todo
|
|
16
|
+
npx @pnds/cli@latest tasks list --status in_progress
|
|
17
|
+
|
|
18
|
+
# Create a task
|
|
19
|
+
npx @pnds/cli@latest tasks create --title "Task title" [--assignee-id usr_xxx] [--project-id prj_xxx]
|
|
20
|
+
|
|
21
|
+
# Update task status
|
|
22
|
+
npx @pnds/cli@latest tasks update tsk_xxx --status in_progress
|
|
23
|
+
npx @pnds/cli@latest tasks update tsk_xxx --status done
|
|
24
|
+
|
|
25
|
+
# Add a comment
|
|
26
|
+
npx @pnds/cli@latest tasks comments add tsk_xxx --body "Progress update..."
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Project Commands
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx @pnds/cli@latest projects list
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Responding to Task Assignments
|
|
36
|
+
|
|
37
|
+
When you receive `[Event: task.assigned]`:
|
|
38
|
+
|
|
39
|
+
1. Acknowledge with a comment: `tasks comments add tsk_xxx --body "On it"`
|
|
40
|
+
2. Update status: `tasks update tsk_xxx --status in_progress`
|
|
41
|
+
3. Do the work
|
|
42
|
+
4. Report results via comment and update status to `done`
|
|
43
|
+
|
|
44
|
+
All CLI output is JSON.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pond-wiki
|
|
3
|
+
description: "Pond wiki operations: read, search, edit, and propose changes to organization knowledge bases. Use when: user asks to read/write docs, edit wiki pages, search knowledge base, propose changes, or review changesets."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pond Wiki
|
|
7
|
+
|
|
8
|
+
Manage organization wikis via the Pond CLI. Auth is pre-configured.
|
|
9
|
+
|
|
10
|
+
## Browsing (no local state needed)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx @pnds/cli@latest wiki list # List all wikis
|
|
14
|
+
npx @pnds/cli@latest wiki tree <slug> # List files and directories
|
|
15
|
+
npx @pnds/cli@latest wiki tree <slug> --path docs/ # List a subdirectory
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Editing
|
|
19
|
+
|
|
20
|
+
Wiki editing works on a **local workspace** — a directory on disk where wiki
|
|
21
|
+
files are synced. You sync from the server, edit files locally, then propose
|
|
22
|
+
a changeset.
|
|
23
|
+
|
|
24
|
+
### Step 1: Sync
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx @pnds/cli@latest wiki sync <slug>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This downloads wiki files to a local directory. The output includes the
|
|
31
|
+
**absolute mount path** for each wiki (e.g. `synced → /data/.openclaw/workspace/kb`).
|
|
32
|
+
|
|
33
|
+
### Step 2: Find the mount path
|
|
34
|
+
|
|
35
|
+
The sync output JSON contains `synced[].path` — the absolute path where files
|
|
36
|
+
live. You can also check it anytime with:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx @pnds/cli@latest wiki status <slug>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The output includes `Mount: /absolute/path/to/<slug>`.
|
|
43
|
+
|
|
44
|
+
Use this path with `read`, `write`, and `edit` tools. For example, if the mount
|
|
45
|
+
is `/data/.openclaw/workspace/kb`, then `README.md` is at
|
|
46
|
+
`/data/.openclaw/workspace/kb/README.md`.
|
|
47
|
+
|
|
48
|
+
### Step 3: Edit files
|
|
49
|
+
|
|
50
|
+
Use standard file tools (`read`, `write`, `edit`) on files under the mount path.
|
|
51
|
+
|
|
52
|
+
### Step 4: Review changes
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx @pnds/cli@latest wiki diff <slug> # Shows unified diff of all local changes
|
|
56
|
+
npx @pnds/cli@latest wiki status <slug> # Shows which files changed with +/- line counts
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Always review before proposing.
|
|
60
|
+
|
|
61
|
+
### Step 5: Propose changeset
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx @pnds/cli@latest wiki changeset create <slug> --title "Description of changes"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This uploads your local changes as a changeset for review.
|
|
68
|
+
|
|
69
|
+
## Changeset Management
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx @pnds/cli@latest wiki changeset list <slug> # List all changesets
|
|
73
|
+
npx @pnds/cli@latest wiki changeset show <changesetId> <slug> # Show detail + feedback
|
|
74
|
+
npx @pnds/cli@latest wiki changeset diff <changesetId> <slug> # Show changeset diff
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
If a changeset is rejected, fix the files locally and re-propose:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npx @pnds/cli@latest wiki changeset create <slug> --update <changesetId>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The title is inherited from the original changeset — no need to repeat it.
|
|
84
|
+
|
|
85
|
+
## Discarding local changes
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx @pnds/cli@latest wiki reset <slug> # Restore all files to last synced state
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## History
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npx @pnds/cli@latest wiki log <slug> # Recent wiki operations
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
All CLI output is JSON. Run `npx @pnds/cli@latest wiki --help` for full options.
|
package/src/config-manager.ts
CHANGED
|
@@ -19,11 +19,7 @@ interface ConfigManagerOpts {
|
|
|
19
19
|
|
|
20
20
|
const CACHE_FILENAME = "pond-platform-config.json";
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
const POND_TOOL_NAMES = [
|
|
24
|
-
"wiki_list", "wiki_status", "wiki_diff", "wiki_tree", "wiki_blob",
|
|
25
|
-
"wiki_search", "wiki_query", "wiki_outline", "wiki_section", "wiki_changeset_diff", "wiki_propose",
|
|
26
|
-
];
|
|
22
|
+
/** Tool names are no longer registered — all operations go through CLI. */
|
|
27
23
|
|
|
28
24
|
function cachePath(stateDir: string): string {
|
|
29
25
|
return path.join(stateDir, CACHE_FILENAME);
|
|
@@ -97,16 +93,7 @@ function applyToOpenClawConfig(
|
|
|
97
93
|
existing.agents = agents;
|
|
98
94
|
}
|
|
99
95
|
|
|
100
|
-
//
|
|
101
|
-
// Uses `alsoAllow` (additive) so user's profile choice is preserved.
|
|
102
|
-
const tools =
|
|
103
|
-
typeof existing.tools === "object"
|
|
104
|
-
&& existing.tools !== null
|
|
105
|
-
&& !Array.isArray(existing.tools)
|
|
106
|
-
? (existing.tools as Record<string, unknown>)
|
|
107
|
-
: {};
|
|
108
|
-
tools.alsoAllow = POND_TOOL_NAMES;
|
|
109
|
-
existing.tools = tools;
|
|
96
|
+
// No plugin tools to allow — all operations go through CLI.
|
|
110
97
|
|
|
111
98
|
// Atomic write
|
|
112
99
|
const tmpPath = `${configPath}.tmp`;
|
package/src/hooks.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
2
|
import { extractAccountIdFromSessionKey } from "./session.js";
|
|
3
3
|
import { clearDispatchMessageId, clearSessionMessageId, getActiveRunId, getDispatchMessageId, getDispatchNoReply, getPondAccountState, getSessionChatId } from "./runtime.js";
|
|
4
|
-
import { loadToolsMarkdown } from "./tools-md.js";
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Resolve the PondClient + orgId + runId for a hook context.
|
|
@@ -25,87 +24,26 @@ async function resolveClientForHook(sessionKey: string | undefined) {
|
|
|
25
24
|
return { ...state, chatId, activeRunId };
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
const
|
|
27
|
+
const POND_CHANNEL_CONTEXT = `## Pond Channel — Message Delivery
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
Messages from the Pond channel arrive as \`[Event: ...]\` blocks.
|
|
30
|
+
Your text output is **not delivered to the user** — it is discarded silently.
|
|
31
|
+
To reply, you **must** use the Pond CLI via the exec (Bash) tool.
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
Each event includes \`[Chat: ... (cht_xxx)]\` — use that chat ID to reply:
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
npx @pnds/cli@latest messages send cht_xxx --text "Your reply here"
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
\`POND_CHAT_ID\` is set to the chat that triggered the current event. When
|
|
43
|
-
replying to the trigger chat, you can omit the chatId argument.
|
|
44
|
-
|
|
45
|
-
### How to Reply to a Message
|
|
46
|
-
|
|
47
|
-
When you receive \`[Event: message.new]\` with \`[Chat: ... (cht_abc123)]\`:
|
|
48
|
-
|
|
49
|
-
npx @pnds/cli@latest messages send cht_abc123 --text "Your response here"
|
|
50
|
-
|
|
51
|
-
Or, since POND_CHAT_ID is set to the trigger chat:
|
|
52
|
-
|
|
53
|
-
npx @pnds/cli@latest messages send --text "Your response here"
|
|
54
|
-
|
|
55
|
-
For thread replies (when the event includes \`[Thread: 01JWC...]\`):
|
|
56
|
-
|
|
57
|
-
npx @pnds/cli@latest messages send --text "Thread reply" --thread-root-id 01JWC...
|
|
58
|
-
|
|
59
|
-
### How to Send a Direct Message
|
|
60
|
-
|
|
61
|
-
npx @pnds/cli@latest dm usr_xyz --text "Hello"
|
|
62
|
-
|
|
63
|
-
Or by display name:
|
|
64
|
-
|
|
65
|
-
npx @pnds/cli@latest dm "Alice" --text "Hello"
|
|
66
|
-
|
|
67
|
-
### How to Act on a Task
|
|
68
|
-
|
|
69
|
-
When you receive \`[Event: task.assigned]\` with \`[Task: ... (tsk_xyz789)]\`:
|
|
70
|
-
|
|
71
|
-
npx @pnds/cli@latest tasks comments add tsk_xyz789 --body "Starting work..."
|
|
72
|
-
npx @pnds/cli@latest tasks update tsk_xyz789 --status in_progress
|
|
73
|
-
|
|
74
|
-
### Reading Data
|
|
75
|
-
|
|
76
|
-
npx @pnds/cli@latest messages list <chatId>
|
|
77
|
-
npx @pnds/cli@latest tasks list --status open
|
|
78
|
-
npx @pnds/cli@latest chats list
|
|
79
|
-
npx @pnds/cli@latest members list
|
|
80
|
-
|
|
81
|
-
### Sub-Agents
|
|
82
|
-
|
|
83
|
-
For long-running tasks, use \`sessions_spawn\` to delegate to a sub-agent.
|
|
84
|
-
Return quickly so the orchestrator can process other events.
|
|
85
|
-
|
|
86
|
-
When a sub-agent completes, deliver the result using the CLI:
|
|
87
|
-
|
|
88
|
-
npx @pnds/cli@latest messages send <chatId> --text "Result: ..."
|
|
89
|
-
|
|
90
|
-
### Agent-to-Agent Interaction
|
|
91
|
-
|
|
92
|
-
When a message has a \`[Hint: no_reply]\`, do not send a reply. The CLI
|
|
93
|
-
automatically suppresses sends when POND_NO_REPLY is set, but you should
|
|
94
|
-
also skip unnecessary computation.
|
|
95
|
-
|
|
96
|
-
Use \`--no-reply\` for FYI messages that don't need a response:
|
|
97
|
-
|
|
98
|
-
npx @pnds/cli@latest messages send <chatId> --text "FYI: done" --no-reply`;
|
|
99
|
-
|
|
100
|
-
// Load wiki tools, CLI docs, and other tool docs from TOOLS.md (maintained separately)
|
|
101
|
-
const POND_ORCHESTRATOR_CONTEXT = ORCHESTRATOR_PREFIX + "\n\n" + loadToolsMarkdown();
|
|
37
|
+
Credentials are pre-configured in every Bash command — no setup needed.
|
|
38
|
+
Load the \`pond-platform\` skill for full CLI reference.
|
|
39
|
+
When a message has \`[Hint: no_reply]\`, do not send a reply.`;
|
|
102
40
|
|
|
103
41
|
export function registerPondHooks(api: OpenClawPluginApi) {
|
|
104
42
|
const log = api.logger;
|
|
105
43
|
|
|
106
|
-
//
|
|
44
|
+
// Append Pond channel context AFTER workspace files so it takes precedence
|
|
107
45
|
api.on("before_prompt_build", () => {
|
|
108
|
-
return {
|
|
46
|
+
return { appendSystemContext: POND_CHANNEL_CONTEXT };
|
|
109
47
|
});
|
|
110
48
|
|
|
111
49
|
// before_tool_call -> (1) fire-and-forget step creation, (2) ENV injection for exec tool
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/core";
|
|
|
3
3
|
import { pondPlugin } from "./channel.js";
|
|
4
4
|
import { setPondRuntime } from "./runtime.js";
|
|
5
5
|
import { registerPondHooks } from "./hooks.js";
|
|
6
|
-
import { registerPondWikiTools } from "./wiki-tools.js";
|
|
7
6
|
|
|
8
7
|
const plugin = {
|
|
9
8
|
id: "pond",
|
|
@@ -13,7 +12,6 @@ const plugin = {
|
|
|
13
12
|
register(api: OpenClawPluginApi) {
|
|
14
13
|
setPondRuntime(api.runtime);
|
|
15
14
|
api.registerChannel({ plugin: pondPlugin });
|
|
16
|
-
registerPondWikiTools(api);
|
|
17
15
|
registerPondHooks(api);
|
|
18
16
|
},
|
|
19
17
|
};
|
package/TOOLS.md
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# Pond Tools
|
|
2
|
-
|
|
3
|
-
## Pond Wiki Tools
|
|
4
|
-
|
|
5
|
-
Use native Pond wiki tools for wiki lookup and editing instead of shelling out to the CLI.
|
|
6
|
-
|
|
7
|
-
- `wiki_list` to find candidate wikis
|
|
8
|
-
- `wiki_query` as the primary tree-aware retrieval tool
|
|
9
|
-
- `wiki_outline` to inspect the local structural outline of a wiki or path prefix
|
|
10
|
-
- `wiki_search` as a simpler lexical fallback
|
|
11
|
-
- `wiki_section` to expand a result by `nodeId`
|
|
12
|
-
- `wiki_blob` only when you need the whole file
|
|
13
|
-
- `wiki_status` to inspect local mounted wiki edits
|
|
14
|
-
- `wiki_diff` to review the local mounted wiki patch
|
|
15
|
-
- `wiki_propose` to open or update a wiki changeset after editing
|
|
16
|
-
- `wiki_changeset_diff` to inspect a wiki draft or changeset
|
|
17
|
-
|
|
18
|
-
Typical wiki edit flow:
|
|
19
|
-
|
|
20
|
-
1. `wiki_query` or `wiki_outline` to locate the right content
|
|
21
|
-
2. `wiki_section` to read the exact section body you need
|
|
22
|
-
3. Edit the mounted wiki files locally
|
|
23
|
-
4. `wiki_status` and `wiki_diff` to verify the exact change
|
|
24
|
-
5. `wiki_propose` to submit or update the wiki changeset
|
|
25
|
-
|
|
26
|
-
## Pond CLI
|
|
27
|
-
|
|
28
|
-
You have full access to the Pond platform via the `@pnds/cli` CLI. Auth and
|
|
29
|
-
runtime context (org, agent run ID, current chat) are pre-configured via
|
|
30
|
-
environment variables — no manual setup needed.
|
|
31
|
-
|
|
32
|
-
### Sending Messages
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
npx @pnds/cli@latest messages send [chatId] --text "..." # chatId defaults to POND_CHAT_ID
|
|
36
|
-
npx @pnds/cli@latest messages send --text "..." --thread-root-id <id>
|
|
37
|
-
npx @pnds/cli@latest messages send --text "..." --no-reply # FYI, no response expected
|
|
38
|
-
npx @pnds/cli@latest dm <nameOrId> --text "..." # DM by user ID or display name
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### Reading Data
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npx @pnds/cli@latest whoami # Check your identity
|
|
45
|
-
npx @pnds/cli@latest agents list # List all agents
|
|
46
|
-
npx @pnds/cli@latest chats list # List chats
|
|
47
|
-
npx @pnds/cli@latest messages list <chatId> # Read chat history
|
|
48
|
-
npx @pnds/cli@latest tasks list [--status ...] # List tasks
|
|
49
|
-
npx @pnds/cli@latest projects list # List projects
|
|
50
|
-
npx @pnds/cli@latest users get <userId> # Get user by ID
|
|
51
|
-
npx @pnds/cli@latest members list # List org members
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Task Operations
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
npx @pnds/cli@latest tasks comments add <taskId> --body "..."
|
|
58
|
-
npx @pnds/cli@latest tasks update <taskId> --status in_progress
|
|
59
|
-
npx @pnds/cli@latest tasks create --title "..." [--assignee-id ...] [--project-id ...]
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Run `npx @pnds/cli@latest --help` or `npx @pnds/cli@latest <command> --help` for full options. Output is JSON.
|
package/src/tool-helpers.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wrap a value as an agent tool result with JSON text content.
|
|
3
|
-
* Replaces the SDK's jsonResult which was removed from public subpath exports
|
|
4
|
-
* in OpenClaw 2026.3.24.
|
|
5
|
-
*/
|
|
6
|
-
export function jsonResult(payload: unknown): { content: Array<{ type: "text"; text: string }>; details: unknown } {
|
|
7
|
-
return {
|
|
8
|
-
content: [{ type: "text" as const, text: JSON.stringify(payload, null, 2) }],
|
|
9
|
-
details: payload,
|
|
10
|
-
};
|
|
11
|
-
}
|
package/src/tools-md.ts
DELETED
package/src/wiki-tools.ts
DELETED
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import type { OpenClawPluginApi, AnyAgentTool } from "openclaw/plugin-sdk";
|
|
3
|
-
import { readNumberParam, readStringParam } from "openclaw/plugin-sdk/param-readers";
|
|
4
|
-
import { jsonResult } from "./tool-helpers.js";
|
|
5
|
-
import { PondClient } from "@pnds/sdk";
|
|
6
|
-
import {
|
|
7
|
-
getWikiBlob,
|
|
8
|
-
getWikiChangesetDiff,
|
|
9
|
-
getWikiOutline,
|
|
10
|
-
queryWiki,
|
|
11
|
-
getWikiSection,
|
|
12
|
-
getWikiTree,
|
|
13
|
-
getWikiWorkspaceDiff,
|
|
14
|
-
getWikiWorkspaceStatus,
|
|
15
|
-
listWikis,
|
|
16
|
-
proposeWikiChangeset,
|
|
17
|
-
searchWiki,
|
|
18
|
-
type PondContext,
|
|
19
|
-
} from "@pnds/cli/wiki";
|
|
20
|
-
import {
|
|
21
|
-
getActiveRunId,
|
|
22
|
-
getAllPondAccountStates,
|
|
23
|
-
getDispatchMessageId,
|
|
24
|
-
getPondAccountState,
|
|
25
|
-
getSessionChatId,
|
|
26
|
-
getSessionMessageId,
|
|
27
|
-
} from "./runtime.js";
|
|
28
|
-
import { extractAccountIdFromSessionKey } from "./session.js";
|
|
29
|
-
|
|
30
|
-
const WikiTreeSchema = Type.Object({
|
|
31
|
-
wiki: Type.String({ minLength: 1, description: "Wiki slug or ID." }),
|
|
32
|
-
path: Type.Optional(Type.String({ minLength: 1, description: "Tree path prefix." })),
|
|
33
|
-
ref: Type.Optional(Type.String({ minLength: 1, description: "Git ref to inspect." })),
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const WikiBlobSchema = Type.Object({
|
|
37
|
-
wiki: Type.String({ minLength: 1, description: "Wiki slug or ID." }),
|
|
38
|
-
path: Type.String({ minLength: 1, description: "File path inside the wiki." }),
|
|
39
|
-
ref: Type.Optional(Type.String({ minLength: 1, description: "Git ref to inspect." })),
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const WikiStatusSchema = Type.Object({
|
|
43
|
-
wiki: Type.String({ minLength: 1, description: "Wiki slug or ID." }),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const WikiDiffSchema = Type.Object({
|
|
47
|
-
wiki: Type.String({ minLength: 1, description: "Wiki slug or ID." }),
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
const WikiSearchSchema = Type.Object({
|
|
51
|
-
wiki: Type.String({ minLength: 1, description: "Wiki slug or ID." }),
|
|
52
|
-
query: Type.String({ minLength: 1, description: "Search query." }),
|
|
53
|
-
ref: Type.Optional(Type.String({ minLength: 1, description: "Git ref to inspect." })),
|
|
54
|
-
pathPrefix: Type.Optional(Type.String({ minLength: 1, description: "Restrict search to a path prefix." })),
|
|
55
|
-
limit: Type.Optional(Type.Number({ minimum: 1, description: "Maximum number of results." })),
|
|
56
|
-
includeContent: Type.Optional(Type.Boolean({ description: "Include full section content in each result." })),
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const WikiQuerySchema = Type.Object({
|
|
60
|
-
wiki: Type.String({ minLength: 1, description: "Wiki slug or ID." }),
|
|
61
|
-
query: Type.String({ minLength: 1, description: "Natural-language query." }),
|
|
62
|
-
ref: Type.Optional(Type.String({ minLength: 1, description: "Git ref to inspect." })),
|
|
63
|
-
pathPrefix: Type.Optional(Type.String({ minLength: 1, description: "Restrict the query to a path prefix." })),
|
|
64
|
-
limit: Type.Optional(Type.Number({ minimum: 1, description: "Maximum number of section results." })),
|
|
65
|
-
includeContent: Type.Optional(Type.Boolean({ description: "Include full section content in each result." })),
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
const WikiOutlineSchema = Type.Object({
|
|
69
|
-
wiki: Type.String({ minLength: 1, description: "Wiki slug or ID." }),
|
|
70
|
-
ref: Type.Optional(Type.String({ minLength: 1, description: "Git ref to inspect." })),
|
|
71
|
-
pathPrefix: Type.Optional(Type.String({ minLength: 1, description: "Restrict outline to a path prefix." })),
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const WikiSectionSchema = Type.Object({
|
|
75
|
-
wiki: Type.String({ minLength: 1, description: "Wiki slug or ID." }),
|
|
76
|
-
nodeId: Type.String({ minLength: 1, description: "Section node ID returned by wiki_search." }),
|
|
77
|
-
ref: Type.Optional(Type.String({ minLength: 1, description: "Git ref to inspect." })),
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
const WikiChangesetDiffSchema = Type.Object({
|
|
81
|
-
wiki: Type.String({ minLength: 1, description: "Wiki slug or ID." }),
|
|
82
|
-
changesetId: Type.String({ minLength: 1, description: "Wiki changeset ID." }),
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
const WikiProposeSchema = Type.Object({
|
|
86
|
-
wiki: Type.String({ minLength: 1, description: "Wiki slug or ID." }),
|
|
87
|
-
title: Type.String({ minLength: 1, description: "Changeset title." }),
|
|
88
|
-
summary: Type.Optional(Type.String({ description: "Optional changeset summary." })),
|
|
89
|
-
changesetId: Type.Optional(Type.String({ minLength: 1, description: "Existing changeset ID to update." })),
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
type ToolSessionContext = {
|
|
93
|
-
sessionKey?: string;
|
|
94
|
-
agentAccountId?: string;
|
|
95
|
-
agentId?: string;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
function resolvePondContext(ctx: ToolSessionContext): PondContext {
|
|
99
|
-
const accountId = ctx.agentAccountId ?? extractAccountIdFromSessionKey(ctx.sessionKey);
|
|
100
|
-
if (accountId) {
|
|
101
|
-
const state = getPondAccountState(accountId);
|
|
102
|
-
if (state) {
|
|
103
|
-
return {
|
|
104
|
-
client: state.client,
|
|
105
|
-
orgId: state.orgId,
|
|
106
|
-
agentId: ctx.agentId ?? process.env.POND_AGENT_ID,
|
|
107
|
-
mountRoot: state.wikiMountRoot,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const states = Array.from(getAllPondAccountStates().values());
|
|
113
|
-
if (states.length === 1) {
|
|
114
|
-
return {
|
|
115
|
-
client: states[0].client,
|
|
116
|
-
orgId: states[0].orgId,
|
|
117
|
-
agentId: ctx.agentId ?? process.env.POND_AGENT_ID,
|
|
118
|
-
mountRoot: states[0].wikiMountRoot,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const baseUrl = process.env.POND_API_URL?.trim();
|
|
123
|
-
const token = process.env.POND_API_KEY?.trim();
|
|
124
|
-
const orgId = process.env.POND_ORG_ID?.trim();
|
|
125
|
-
if (baseUrl && token && orgId) {
|
|
126
|
-
return {
|
|
127
|
-
client: new PondClient({ baseUrl, token }),
|
|
128
|
-
orgId,
|
|
129
|
-
agentId: ctx.agentId ?? process.env.POND_AGENT_ID,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
throw new Error(
|
|
134
|
-
"Pond wiki tools are unavailable: missing live Pond runtime state and POND_API_URL/POND_API_KEY/POND_ORG_ID.",
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function formatWikiBlobResult(result: Awaited<ReturnType<typeof getWikiBlob>>) {
|
|
139
|
-
return {
|
|
140
|
-
wiki: {
|
|
141
|
-
id: result.wiki.id,
|
|
142
|
-
slug: result.wiki.slug,
|
|
143
|
-
name: result.wiki.name,
|
|
144
|
-
},
|
|
145
|
-
content: result.content,
|
|
146
|
-
size: result.size,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async function resolveWorkflowContext(ctx: ToolSessionContext) {
|
|
151
|
-
if (!ctx.sessionKey) {
|
|
152
|
-
return {};
|
|
153
|
-
}
|
|
154
|
-
const accountId = ctx.agentAccountId ?? extractAccountIdFromSessionKey(ctx.sessionKey);
|
|
155
|
-
const chatId = getSessionChatId(ctx.sessionKey);
|
|
156
|
-
const state = accountId ? getPondAccountState(accountId) : undefined;
|
|
157
|
-
const runId = state ? await getActiveRunId(state, ctx.sessionKey) : undefined;
|
|
158
|
-
|
|
159
|
-
// Issue 1: source_chat_id is a FK to the chats table — only set it for actual chat IDs.
|
|
160
|
-
// Task dispatches store a tsk_* ID here which would cause a DB FK violation.
|
|
161
|
-
const sourceChatId = chatId?.startsWith("cht_") ? chatId : undefined;
|
|
162
|
-
|
|
163
|
-
// Issue 2: prefer dispatch-time snapshot over the mutable session map to avoid
|
|
164
|
-
// reading a stale message ID when a new inbound message arrives mid-dispatch.
|
|
165
|
-
const sourceMessageId =
|
|
166
|
-
getDispatchMessageId(ctx.sessionKey) ?? getSessionMessageId(ctx.sessionKey);
|
|
167
|
-
|
|
168
|
-
return {
|
|
169
|
-
sourceChatId,
|
|
170
|
-
sourceMessageId,
|
|
171
|
-
sourceRunId: runId,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function createWikiTools(toolCtx: ToolSessionContext): AnyAgentTool[] {
|
|
176
|
-
const pondContext = () => resolvePondContext(toolCtx);
|
|
177
|
-
const workflowContext = () => resolveWorkflowContext(toolCtx);
|
|
178
|
-
return [
|
|
179
|
-
{
|
|
180
|
-
label: "Wiki List",
|
|
181
|
-
name: "wiki_list",
|
|
182
|
-
description: "List all Pond wikis in the current organization.",
|
|
183
|
-
parameters: Type.Object({}),
|
|
184
|
-
execute: async () => {
|
|
185
|
-
return jsonResult(await listWikis(pondContext()));
|
|
186
|
-
},
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
label: "Wiki Status",
|
|
190
|
-
name: "wiki_status",
|
|
191
|
-
description: "Inspect the local mounted wiki workspace before proposing changes.",
|
|
192
|
-
parameters: WikiStatusSchema,
|
|
193
|
-
execute: async (_toolCallId, params) => {
|
|
194
|
-
return jsonResult(
|
|
195
|
-
await getWikiWorkspaceStatus(pondContext(), readStringParam(params, "wiki", { required: true })),
|
|
196
|
-
);
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
label: "Wiki Diff",
|
|
201
|
-
name: "wiki_diff",
|
|
202
|
-
description: "Read the current unified diff for the local mounted wiki workspace.",
|
|
203
|
-
parameters: WikiDiffSchema,
|
|
204
|
-
execute: async (_toolCallId, params) => {
|
|
205
|
-
return jsonResult(
|
|
206
|
-
await getWikiWorkspaceDiff(pondContext(), readStringParam(params, "wiki", { required: true })),
|
|
207
|
-
);
|
|
208
|
-
},
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
label: "Wiki Tree",
|
|
212
|
-
name: "wiki_tree",
|
|
213
|
-
description: "List files and directories under a Pond wiki path.",
|
|
214
|
-
parameters: WikiTreeSchema,
|
|
215
|
-
execute: async (_toolCallId, params) => {
|
|
216
|
-
return jsonResult(
|
|
217
|
-
await getWikiTree(pondContext(), readStringParam(params, "wiki", { required: true }), {
|
|
218
|
-
path: readStringParam(params, "path"),
|
|
219
|
-
}),
|
|
220
|
-
);
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
label: "Wiki Blob",
|
|
225
|
-
name: "wiki_blob",
|
|
226
|
-
description: "Read a Pond wiki file. Use after wiki_search or wiki_tree narrows the target.",
|
|
227
|
-
parameters: WikiBlobSchema,
|
|
228
|
-
execute: async (_toolCallId, params) => {
|
|
229
|
-
const result = await getWikiBlob(pondContext(), readStringParam(params, "wiki", { required: true }), {
|
|
230
|
-
path: readStringParam(params, "path", { required: true }),
|
|
231
|
-
});
|
|
232
|
-
return jsonResult(formatWikiBlobResult(result));
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
{
|
|
236
|
-
label: "Wiki Search",
|
|
237
|
-
name: "wiki_search",
|
|
238
|
-
description: "Search Pond wiki sections by heading and content. Prefer this before reading full files.",
|
|
239
|
-
parameters: WikiSearchSchema,
|
|
240
|
-
execute: async (_toolCallId, params) => {
|
|
241
|
-
return jsonResult(
|
|
242
|
-
await searchWiki(
|
|
243
|
-
pondContext(),
|
|
244
|
-
readStringParam(params, "wiki", { required: true }),
|
|
245
|
-
readStringParam(params, "query", { required: true }),
|
|
246
|
-
{
|
|
247
|
-
ref: readStringParam(params, "ref"),
|
|
248
|
-
pathPrefix: readStringParam(params, "pathPrefix"),
|
|
249
|
-
limit: readNumberParam(params, "limit", { integer: true }),
|
|
250
|
-
includeContent: params.includeContent === true,
|
|
251
|
-
},
|
|
252
|
-
),
|
|
253
|
-
);
|
|
254
|
-
},
|
|
255
|
-
},
|
|
256
|
-
{
|
|
257
|
-
label: "Wiki Query",
|
|
258
|
-
name: "wiki_query",
|
|
259
|
-
description: "Run a tree-aware Pond wiki query that first narrows documents and then selects the best sections.",
|
|
260
|
-
parameters: WikiQuerySchema,
|
|
261
|
-
execute: async (_toolCallId, params) => {
|
|
262
|
-
return jsonResult(
|
|
263
|
-
await queryWiki(
|
|
264
|
-
pondContext(),
|
|
265
|
-
readStringParam(params, "wiki", { required: true }),
|
|
266
|
-
readStringParam(params, "query", { required: true }),
|
|
267
|
-
{
|
|
268
|
-
ref: readStringParam(params, "ref"),
|
|
269
|
-
pathPrefix: readStringParam(params, "pathPrefix"),
|
|
270
|
-
limit: readNumberParam(params, "limit", { integer: true }),
|
|
271
|
-
includeContent: params.includeContent === true,
|
|
272
|
-
},
|
|
273
|
-
),
|
|
274
|
-
);
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
{
|
|
278
|
-
label: "Wiki Outline",
|
|
279
|
-
name: "wiki_outline",
|
|
280
|
-
description: "Read the structural outline of a Pond wiki or a specific path prefix before drilling into sections.",
|
|
281
|
-
parameters: WikiOutlineSchema,
|
|
282
|
-
execute: async (_toolCallId, params) => {
|
|
283
|
-
return jsonResult(
|
|
284
|
-
await getWikiOutline(pondContext(), readStringParam(params, "wiki", { required: true }), {
|
|
285
|
-
ref: readStringParam(params, "ref"),
|
|
286
|
-
pathPrefix: readStringParam(params, "pathPrefix"),
|
|
287
|
-
}),
|
|
288
|
-
);
|
|
289
|
-
},
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
label: "Wiki Section",
|
|
293
|
-
name: "wiki_section",
|
|
294
|
-
description: "Read the full body of a Pond wiki section identified by nodeId from wiki_search.",
|
|
295
|
-
parameters: WikiSectionSchema,
|
|
296
|
-
execute: async (_toolCallId, params) => {
|
|
297
|
-
return jsonResult(
|
|
298
|
-
await getWikiSection(pondContext(), readStringParam(params, "wiki", { required: true }), {
|
|
299
|
-
nodeId: readStringParam(params, "nodeId", { required: true }),
|
|
300
|
-
ref: readStringParam(params, "ref"),
|
|
301
|
-
}),
|
|
302
|
-
);
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
label: "Wiki Changeset Diff",
|
|
307
|
-
name: "wiki_changeset_diff",
|
|
308
|
-
description: "Fetch the unified diff and file summary for a Pond wiki changeset.",
|
|
309
|
-
parameters: WikiChangesetDiffSchema,
|
|
310
|
-
execute: async (_toolCallId, params) => {
|
|
311
|
-
return jsonResult(
|
|
312
|
-
await getWikiChangesetDiff(
|
|
313
|
-
pondContext(),
|
|
314
|
-
readStringParam(params, "wiki", { required: true }),
|
|
315
|
-
readStringParam(params, "changesetId", { required: true }),
|
|
316
|
-
),
|
|
317
|
-
);
|
|
318
|
-
},
|
|
319
|
-
},
|
|
320
|
-
{
|
|
321
|
-
label: "Wiki Propose",
|
|
322
|
-
name: "wiki_propose",
|
|
323
|
-
description: "Create or update a proposed wiki changeset from the current local mounted workspace.",
|
|
324
|
-
parameters: WikiProposeSchema,
|
|
325
|
-
execute: async (_toolCallId, params) => {
|
|
326
|
-
const workflow = await workflowContext();
|
|
327
|
-
return jsonResult(
|
|
328
|
-
await proposeWikiChangeset(pondContext(), readStringParam(params, "wiki", { required: true }), {
|
|
329
|
-
title: readStringParam(params, "title", { required: true }),
|
|
330
|
-
summary: readStringParam(params, "summary"),
|
|
331
|
-
changesetId: readStringParam(params, "changesetId"),
|
|
332
|
-
sourceChatId: workflow.sourceChatId,
|
|
333
|
-
sourceMessageId: workflow.sourceMessageId,
|
|
334
|
-
sourceRunId: workflow.sourceRunId,
|
|
335
|
-
}),
|
|
336
|
-
);
|
|
337
|
-
},
|
|
338
|
-
},
|
|
339
|
-
];
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
export function registerPondWikiTools(api: OpenClawPluginApi) {
|
|
343
|
-
api.registerTool(
|
|
344
|
-
(ctx) => createWikiTools(ctx),
|
|
345
|
-
{
|
|
346
|
-
names: [
|
|
347
|
-
"wiki_list",
|
|
348
|
-
"wiki_status",
|
|
349
|
-
"wiki_diff",
|
|
350
|
-
"wiki_tree",
|
|
351
|
-
"wiki_blob",
|
|
352
|
-
"wiki_search",
|
|
353
|
-
"wiki_query",
|
|
354
|
-
"wiki_outline",
|
|
355
|
-
"wiki_section",
|
|
356
|
-
"wiki_changeset_diff",
|
|
357
|
-
"wiki_propose",
|
|
358
|
-
],
|
|
359
|
-
},
|
|
360
|
-
);
|
|
361
|
-
}
|