@xiaotianxt/skills 0.1.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/EXCLUDED.md +42 -0
- package/LICENSE +21 -0
- package/README.md +165 -0
- package/SECURITY.md +23 -0
- package/SOURCES.md +45 -0
- package/bin/skills.mjs +241 -0
- package/package.json +38 -0
- package/skills/1password/SKILL.md +94 -0
- package/skills/1password/agents/openai.yaml +4 -0
- package/skills/1password/references/item-management.md +80 -0
- package/skills/1password/references/op-cli.md +107 -0
- package/skills/apple-calendar-event/SKILL.md +81 -0
- package/skills/apple-calendar-event/agents/openai.yaml +4 -0
- package/skills/apple-calendar-event/scripts/calendar_audit.py +201 -0
- package/skills/apple-calendar-event/scripts/calendar_event.py +164 -0
- package/skills/bro-browser/SKILL.md +118 -0
- package/skills/bro-browser/agents/openai.yaml +4 -0
- package/skills/bro-browser/references/tool-map.md +102 -0
- package/skills/bro-browser/references/workflows.md +146 -0
- package/skills/bro-browser/scripts/bro-call.mjs +189 -0
- package/skills/calendar/SKILL.md +182 -0
- package/skills/calendar/agents/openai.yaml +4 -0
- package/skills/calendar/references/operations.md +255 -0
- package/skills/calendar/scripts/calendar_list_review.py +157 -0
- package/skills/calendar/scripts/event_dedupe_preview.py +155 -0
- package/skills/canvas/SKILL.md +70 -0
- package/skills/canvas/agents/openai.yaml +4 -0
- package/skills/canvas/references/canvas-api.md +76 -0
- package/skills/course-exam-review-planner/SKILL.md +127 -0
- package/skills/cx/SKILL.md +25 -0
- package/skills/gh-fix-ci/LICENSE.txt +201 -0
- package/skills/gh-fix-ci/SKILL.md +81 -0
- package/skills/gh-fix-ci/agents/openai.yaml +6 -0
- package/skills/gh-fix-ci/assets/github-small.svg +3 -0
- package/skills/gh-fix-ci/assets/github.png +0 -0
- package/skills/gh-fix-ci/scripts/inspect_pr_checks.py +509 -0
- package/skills/gh-review-workflow/SKILL.md +61 -0
- package/skills/gh-review-workflow/agents/openai.yaml +4 -0
- package/skills/gh-review-workflow/references/workflow.md +48 -0
- package/skills/gh-review-workflow/scripts/fetch_review_state.py +222 -0
- package/skills/gh-review-workflow/scripts/resolve_review_threads.py +83 -0
- package/skills/github/SKILL.md +74 -0
- package/skills/github/agents/openai.yaml +6 -0
- package/skills/github/assets/github-small.svg +3 -0
- package/skills/github/assets/github.png +0 -0
- package/skills/gws-calendar/SKILL.md +126 -0
- package/skills/gws-calendar-agenda/SKILL.md +52 -0
- package/skills/gws-calendar-insert/SKILL.md +66 -0
- package/skills/gws-docs/SKILL.md +48 -0
- package/skills/gws-docs-write/SKILL.md +49 -0
- package/skills/gws-drive/SKILL.md +137 -0
- package/skills/gws-drive-upload/SKILL.md +52 -0
- package/skills/gws-gmail/SKILL.md +62 -0
- package/skills/gws-gmail-forward/SKILL.md +55 -0
- package/skills/gws-gmail-reply/SKILL.md +58 -0
- package/skills/gws-gmail-reply-all/SKILL.md +62 -0
- package/skills/gws-gmail-send/SKILL.md +57 -0
- package/skills/gws-gmail-triage/SKILL.md +50 -0
- package/skills/gws-gmail-watch/SKILL.md +58 -0
- package/skills/gws-shared/SKILL.md +27 -0
- package/skills/helium-browser-mcp/SKILL.md +137 -0
- package/skills/helium-browser-mcp/agents/openai.yaml +4 -0
- package/skills/helium-browser-mcp/scripts/obmcp.mjs +92 -0
- package/skills/helium-browser-mcp/scripts/openbrowsermcp-stdio-proxy.mjs +170 -0
- package/skills/learn/SKILL.md +122 -0
- package/skills/learn/agents/openai.yaml +7 -0
- package/skills/learn/assets/AGENTS.template.md +33 -0
- package/skills/learn/assets/errorlog.template.typ +61 -0
- package/skills/learn/assets/reading-sequence.template.md +23 -0
- package/skills/learn/assets/source-index.template.md +17 -0
- package/skills/learn/assets/tasklog.template.typ +57 -0
- package/skills/learn/assets/workbook.template.typ +60 -0
- package/skills/learn/references/learning-science.md +103 -0
- package/skills/learn/scripts/init_learning_workspace.py +70 -0
- package/skills/macos-messages/SKILL.md +258 -0
- package/skills/memory/SKILL.md +33 -0
- package/skills/memory/codex.md +186 -0
- package/skills/memory/opencode.md +164 -0
- package/skills/mimestreamctl/SKILL.md +170 -0
- package/skills/mimestreamctl/agents/openai.yaml +4 -0
- package/skills/mimestreamctl/scripts/mimestreamctl +33 -0
- package/skills/mon/SKILL.md +51 -0
- package/skills/mon/scripts/mon_spend_review.py +458 -0
- package/skills/ocr/SKILL.md +136 -0
- package/skills/ocr/agents/openai.yaml +4 -0
- package/skills/ocr/references/local-ocr-best-practices.md +297 -0
- package/skills/ocr/references/mineru-api.md +159 -0
- package/skills/ocr/scripts/ocr-router +22 -0
- package/skills/ocr/scripts/ocr_router.py +741 -0
- package/skills/panopto-mp4-bulk-download/SKILL.md +57 -0
- package/skills/panopto-mp4-bulk-download/agents/openai.yaml +4 -0
- package/skills/panopto-mp4-bulk-download/references/url-patterns.md +26 -0
- package/skills/panopto-mp4-bulk-download/scripts/panopto_bulk_mp4.sh +213 -0
- package/skills/rust-systems-style/SKILL.md +109 -0
- package/skills/rust-systems-style/agents/openai.yaml +4 -0
- package/skills/rust-systems-style/references/rust-review-checklist.md +77 -0
- package/skills/rust-systems-style/references/style-sources.md +68 -0
- package/skills/ship-ai-native-cli/SKILL.md +76 -0
- package/skills/ship-ai-native-cli/agents/openai.yaml +4 -0
- package/skills/ship-ai-native-cli/references/case-notes.md +83 -0
- package/skills/ship-ai-native-cli/references/product-method.md +82 -0
- package/skills/ship-ai-native-cli/references/release-checklist.md +147 -0
- package/skills/ship-ai-native-cli/references/rust-cli-shape.md +111 -0
- package/skills/telegram-mtproto-session/SKILL.md +125 -0
- package/skills/telegram-mtproto-session/agents/openai.yaml +4 -0
- package/skills/telegram-mtproto-session/scripts/telegram_session.py +687 -0
- package/skills/tg/SKILL.md +173 -0
- package/skills/things3-manager/SKILL.md +116 -0
- package/skills/things3-manager/scripts/things +42 -0
- package/skills/things3-manager/scripts/things_cli.py +514 -0
- package/skills/web-artifacts-builder/LICENSE.txt +202 -0
- package/skills/web-artifacts-builder/SKILL.md +74 -0
- package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/skills/web-artifacts-builder/scripts/init-artifact.sh +379 -0
- package/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/skills/yeet/LICENSE.txt +201 -0
- package/skills/yeet/SKILL.md +71 -0
- package/skills/yeet/agents/openai.yaml +6 -0
- package/skills/yeet/assets/yeet-small.svg +3 -0
- package/skills/yeet/assets/yeet.png +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gws-gmail-watch
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "Gmail: Watch for new emails and stream them as NDJSON."
|
|
5
|
+
metadata:
|
|
6
|
+
openclaw:
|
|
7
|
+
category: "productivity"
|
|
8
|
+
requires:
|
|
9
|
+
bins: ["gws"]
|
|
10
|
+
cliHelp: "gws gmail +watch --help"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# gmail +watch
|
|
14
|
+
|
|
15
|
+
> **PREREQUISITE:** Read `../gws-shared/SKILL.md` for auth, global flags, and security rules. If missing, run `gws generate-skills` to create it.
|
|
16
|
+
|
|
17
|
+
Watch for new emails and stream them as NDJSON
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gws gmail +watch
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Flags
|
|
26
|
+
|
|
27
|
+
| Flag | Required | Default | Description |
|
|
28
|
+
|------|----------|---------|-------------|
|
|
29
|
+
| `--project` | — | — | GCP project ID for Pub/Sub resources |
|
|
30
|
+
| `--subscription` | — | — | Existing Pub/Sub subscription name (skip setup) |
|
|
31
|
+
| `--topic` | — | — | Existing Pub/Sub topic with Gmail push permission already granted |
|
|
32
|
+
| `--label-ids` | — | — | Comma-separated Gmail label IDs to filter (e.g., INBOX,UNREAD) |
|
|
33
|
+
| `--max-messages` | — | 10 | Max messages per pull batch |
|
|
34
|
+
| `--poll-interval` | — | 5 | Seconds between pulls |
|
|
35
|
+
| `--msg-format` | — | full | Gmail message format: full, metadata, minimal, raw |
|
|
36
|
+
| `--once` | — | — | Pull once and exit |
|
|
37
|
+
| `--cleanup` | — | — | Delete created Pub/Sub resources on exit |
|
|
38
|
+
| `--output-dir` | — | — | Write each message to a separate JSON file in this directory |
|
|
39
|
+
|
|
40
|
+
## Examples
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
gws gmail +watch --project my-gcp-project
|
|
44
|
+
gws gmail +watch --project my-project --label-ids INBOX --once
|
|
45
|
+
gws gmail +watch --subscription projects/p/subscriptions/my-sub
|
|
46
|
+
gws gmail +watch --project my-project --cleanup --output-dir ./emails
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Tips
|
|
50
|
+
|
|
51
|
+
- Gmail watch expires after 7 days — re-run to renew.
|
|
52
|
+
- Without --cleanup, Pub/Sub resources persist for reconnection.
|
|
53
|
+
- Press Ctrl-C to stop gracefully.
|
|
54
|
+
|
|
55
|
+
## See Also
|
|
56
|
+
|
|
57
|
+
- [gws-shared](../gws-shared/SKILL.md) — Global flags and auth
|
|
58
|
+
- [gws-gmail](../gws-gmail/SKILL.md) — All send, read, and manage email commands
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gws-shared
|
|
3
|
+
description: Shared authentication, security, and command conventions for locally generated Google Workspace gws skills.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# gws-shared
|
|
7
|
+
|
|
8
|
+
Use this reference before running any `gws-*` skill.
|
|
9
|
+
|
|
10
|
+
## Auth
|
|
11
|
+
|
|
12
|
+
- Use the local `gws` CLI auth state. Do not print tokens, refresh tokens, cookies, or raw credential files.
|
|
13
|
+
- If authentication is missing, run the narrowest relevant login flow, for example `gws auth login --services gmail`.
|
|
14
|
+
- Prefer read-only scopes unless the user explicitly asks to send, create, update, delete, or watch resources.
|
|
15
|
+
|
|
16
|
+
## Command Safety
|
|
17
|
+
|
|
18
|
+
- Inspect command shape with `gws <service> --help` and method schemas with `gws schema <service>.<resource>.<method>`.
|
|
19
|
+
- Prefer `--json` output for agent workflows and summarize only the fields needed for the task.
|
|
20
|
+
- For email and document content, avoid dumping full bodies unless the user asked for exact content.
|
|
21
|
+
- For write operations, state the target account, recipient, calendar, document, or Drive path before executing.
|
|
22
|
+
|
|
23
|
+
## Data Handling
|
|
24
|
+
|
|
25
|
+
- Treat Gmail messages, Calendar events, Docs content, Drive files, and contact-like metadata as private.
|
|
26
|
+
- Keep exports local by default.
|
|
27
|
+
- Do not include user data, OAuth files, cache directories, or raw API responses in commits.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: helium-browser-mcp
|
|
3
|
+
description: Use when Codex needs to control or inspect the user's logged-in Helium browser through the locally installed OpenBrowserMCP extension. Trigger for requests about OpenBrowserMCP, Helium tabs, reading current browser tabs, browser MCP, openbrowsermcp, webpage interaction, or testing this local browser stack.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Helium Browser MCP
|
|
7
|
+
|
|
8
|
+
Use this skill for the local OpenBrowserMCP + Helium setup.
|
|
9
|
+
|
|
10
|
+
## Local Services
|
|
11
|
+
|
|
12
|
+
- OpenBrowserMCP project: `/Users/yupeit/dev/openbrowsermcp`
|
|
13
|
+
- OpenBrowserMCP server: `http://127.0.0.1:3500`
|
|
14
|
+
- OpenBrowserMCP MCP endpoint: `http://127.0.0.1:3500/mcp`
|
|
15
|
+
- OpenBrowserMCP WebSocket bridge: `ws://127.0.0.1:3500/ws`
|
|
16
|
+
- Helium extension id: `ocjmfbmadhimfjoonaljbmpcfnbgiolc`
|
|
17
|
+
- OpenBrowserMCP token: `~/openbrowsermcp/settings.json`
|
|
18
|
+
Important: use `127.0.0.1`, not `localhost`, for OpenBrowserMCP. `localhost`
|
|
19
|
+
can resolve to IPv6 `::1`, while the server listens on IPv4.
|
|
20
|
+
|
|
21
|
+
## Privacy And Safety
|
|
22
|
+
|
|
23
|
+
Helium is the user's real browser profile. Treat tab URLs, page text, screenshots,
|
|
24
|
+
cookies, account state, and extension state as sensitive.
|
|
25
|
+
|
|
26
|
+
- Do not print the OpenBrowserMCP bearer token.
|
|
27
|
+
- Prefer read-only inspection first: `browsers_context`, `tabs_context`,
|
|
28
|
+
`get_page_text`, `read_page`.
|
|
29
|
+
- Before submitting forms, sending messages, uploading files, making purchases,
|
|
30
|
+
changing account settings, or reading highly sensitive pages, ask for explicit
|
|
31
|
+
confirmation.
|
|
32
|
+
- For demos, create a new tab on `https://example.com` and operate only there.
|
|
33
|
+
|
|
34
|
+
## Quick Checks
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
curl -sS http://127.0.0.1:3500/status
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
If OpenBrowserMCP shows no connected extension, open the options page:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
/usr/bin/open -b net.imput.helium chrome-extension://ocjmfbmadhimfjoonaljbmpcfnbgiolc/options.html
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then set:
|
|
47
|
+
|
|
48
|
+
- Server URL: `ws://127.0.0.1:3500/ws`
|
|
49
|
+
- Token: value from `~/openbrowsermcp/settings.json`
|
|
50
|
+
|
|
51
|
+
## Calling OpenBrowserMCP
|
|
52
|
+
|
|
53
|
+
Use the bundled helper so the token is read locally and not printed:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
/Users/yupeit/dev/skills/skills/helium-browser-mcp/scripts/obmcp.mjs browsers_context
|
|
57
|
+
/Users/yupeit/dev/skills/skills/helium-browser-mcp/scripts/obmcp.mjs tabs_context '{"all":true}'
|
|
58
|
+
/Users/yupeit/dev/skills/skills/helium-browser-mcp/scripts/obmcp.mjs get_page_text '{"tabId":123,"browserId":"..."}'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## OpenCode Integration
|
|
62
|
+
|
|
63
|
+
OpenCode is configured globally in `/Users/yupeit/.config/opencode/opencode.json`
|
|
64
|
+
with an `openbrowsermcp` local MCP entry. It runs this stdio proxy:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
/Users/yupeit/dev/skills/skills/helium-browser-mcp/scripts/openbrowsermcp-stdio-proxy.mjs
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The proxy reads `~/openbrowsermcp/settings.json` locally and forwards to
|
|
71
|
+
`http://127.0.0.1:3500/mcp`, so the bearer token is not stored in OpenCode's
|
|
72
|
+
config file.
|
|
73
|
+
|
|
74
|
+
Verify OpenCode sees it:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
opencode mcp list
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Common tools:
|
|
81
|
+
|
|
82
|
+
- `browsers_context` lists connected Helium browser instances.
|
|
83
|
+
- `tabs_context` lists tabs. Pass `{"all":true,"browserId":"..."}`.
|
|
84
|
+
After an agent has chosen a tab, pass `tabId` too so context is anchored to
|
|
85
|
+
that tab's window/group instead of the user's foreground tab.
|
|
86
|
+
- `tabs_create` creates a new background tab by default. Pass
|
|
87
|
+
`{"url":"https://example.com","browserId":"..."}` and keep the returned
|
|
88
|
+
numeric tab ID for all later calls. Use `active:true` only when the user
|
|
89
|
+
explicitly wants the tab brought to the foreground.
|
|
90
|
+
- `navigate` changes an existing tab. Pass
|
|
91
|
+
`{"url":"https://example.com","tabId":123,"browserId":"..."}` when the
|
|
92
|
+
current target tab can be overwritten instead of creating another tab.
|
|
93
|
+
- `tabs_close` closes a tab by ID. Use it for task-owned temporary tabs once
|
|
94
|
+
they are no longer needed.
|
|
95
|
+
- `read_page` reads the accessibility tree. Requires `tabId`.
|
|
96
|
+
- `get_page_text` extracts `document.body.innerText`. Requires `tabId`.
|
|
97
|
+
- `find` searches accessible elements by description. Requires `tabId`.
|
|
98
|
+
- `click_element` clicks a ref from `find`/`read_page`. Requires `tabId`.
|
|
99
|
+
- `javascript_tool` evaluates page JavaScript. Requires `tabId`.
|
|
100
|
+
|
|
101
|
+
## Stable Tab Targeting
|
|
102
|
+
|
|
103
|
+
Do not operate by "whatever tab is active" after the first discovery step.
|
|
104
|
+
Helium is the user's real browser and they may continue using it while Codex is
|
|
105
|
+
working.
|
|
106
|
+
|
|
107
|
+
- Create or identify a target tab first, record both `browserId` and `tabId`,
|
|
108
|
+
and pass them explicitly to every tab-targeted tool.
|
|
109
|
+
- For monitoring, call `read_network_requests`, `read_console_messages`, and
|
|
110
|
+
`get_response_body` with the pinned `tabId`.
|
|
111
|
+
- When refreshing tab context during a task, use
|
|
112
|
+
`tabs_context {"all":true,"tabId":123,"browserId":"..."}` so the listing is
|
|
113
|
+
resolved relative to the pinned tab, not the user's current foreground tab.
|
|
114
|
+
- Avoid `tabs_activate` unless foreground focus is part of the user's request.
|
|
115
|
+
CDP-backed tools such as `javascript_tool`, `read_page`, and monitoring work
|
|
116
|
+
on background tabs.
|
|
117
|
+
|
|
118
|
+
## Tab Lifecycle
|
|
119
|
+
|
|
120
|
+
Keep the user's browser tidy.
|
|
121
|
+
|
|
122
|
+
- Track every tab created for the task. Before finishing, close task-owned tabs
|
|
123
|
+
with `tabs_close` unless the user asked to keep them open or the tab now
|
|
124
|
+
contains useful state the user expects to inspect.
|
|
125
|
+
- Prefer reusing the current target tab with `navigate` when moving to the next
|
|
126
|
+
website and the current page can be safely overwritten. Do this for agent-
|
|
127
|
+
created scratch tabs and for user-approved disposable pages.
|
|
128
|
+
- Do not overwrite or close tabs that existed before the task unless the user
|
|
129
|
+
explicitly says the current page can be reused or closed.
|
|
130
|
+
|
|
131
|
+
For a safe smoke test:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
scripts/obmcp.mjs browsers_context
|
|
135
|
+
scripts/obmcp.mjs tabs_context '{"all":true}'
|
|
136
|
+
scripts/obmcp.mjs tabs_create '{"url":"https://example.com"}'
|
|
137
|
+
```
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const endpoint = 'http://127.0.0.1:3500/mcp';
|
|
5
|
+
const tokenPath = `${process.env.HOME}/openbrowsermcp/settings.json`;
|
|
6
|
+
|
|
7
|
+
function usage() {
|
|
8
|
+
console.error('usage: obmcp.mjs <tool-name> [json-arguments]');
|
|
9
|
+
console.error('example: obmcp.mjs tabs_context \'{"all":true}\'');
|
|
10
|
+
process.exit(2);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const toolName = process.argv[2];
|
|
14
|
+
if (!toolName) usage();
|
|
15
|
+
|
|
16
|
+
let args = {};
|
|
17
|
+
if (process.argv[3]) {
|
|
18
|
+
try {
|
|
19
|
+
args = JSON.parse(process.argv[3]);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
console.error(`invalid JSON arguments: ${err.message}`);
|
|
22
|
+
process.exit(2);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const token = JSON.parse(fs.readFileSync(tokenPath, 'utf8')).token;
|
|
27
|
+
if (!token) {
|
|
28
|
+
console.error(`missing token in ${tokenPath}`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const baseHeaders = {
|
|
33
|
+
'content-type': 'application/json',
|
|
34
|
+
accept: 'application/json, text/event-stream',
|
|
35
|
+
authorization: `Bearer ${token}`,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function parseSseOrJson(text) {
|
|
39
|
+
for (const line of text.split('\n')) {
|
|
40
|
+
if (!line.startsWith('data: ')) continue;
|
|
41
|
+
const payload = line.slice(6).trim();
|
|
42
|
+
if (!payload) continue;
|
|
43
|
+
return JSON.parse(payload);
|
|
44
|
+
}
|
|
45
|
+
return JSON.parse(text);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function rpc(sessionId, id, method, params) {
|
|
49
|
+
const headers = sessionId
|
|
50
|
+
? { ...baseHeaders, 'mcp-session-id': sessionId }
|
|
51
|
+
: baseHeaders;
|
|
52
|
+
const response = await fetch(endpoint, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers,
|
|
55
|
+
body: JSON.stringify({ jsonrpc: '2.0', id, method, params }),
|
|
56
|
+
});
|
|
57
|
+
const text = await response.text();
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error(`HTTP ${response.status}: ${text}`);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
sessionId: response.headers.get('mcp-session-id'),
|
|
63
|
+
body: parseSseOrJson(text),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const init = await rpc(null, 1, 'initialize', {
|
|
68
|
+
protocolVersion: '2024-11-05',
|
|
69
|
+
capabilities: {},
|
|
70
|
+
clientInfo: { name: 'codex-obmcp-helper', version: '0' },
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const sessionId = init.sessionId;
|
|
74
|
+
if (!sessionId) {
|
|
75
|
+
throw new Error('OpenBrowserMCP did not return an MCP session id');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const result = await rpc(sessionId, 2, 'tools/call', {
|
|
79
|
+
name: toolName,
|
|
80
|
+
arguments: args,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const body = result.body;
|
|
84
|
+
const content = body.result?.content;
|
|
85
|
+
if (Array.isArray(content)) {
|
|
86
|
+
for (const item of content) {
|
|
87
|
+
if (item?.type === 'text') console.log(item.text);
|
|
88
|
+
else console.log(JSON.stringify(item));
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
console.log(JSON.stringify(body, null, 2));
|
|
92
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
const endpoint = process.env.OPENBROWSERMCP_MCP_URL || 'http://127.0.0.1:3500/mcp';
|
|
5
|
+
const tokenPath = process.env.OPENBROWSERMCP_SETTINGS || `${process.env.HOME}/openbrowsermcp/settings.json`;
|
|
6
|
+
const logPath = process.env.OPENBROWSERMCP_PROXY_LOG || '';
|
|
7
|
+
|
|
8
|
+
let sessionId = null;
|
|
9
|
+
let input = Buffer.alloc(0);
|
|
10
|
+
let chain = Promise.resolve();
|
|
11
|
+
let framing = null;
|
|
12
|
+
|
|
13
|
+
function log(message) {
|
|
14
|
+
if (!logPath) return;
|
|
15
|
+
fs.appendFileSync(logPath, `${new Date().toISOString()} ${message}\n`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function readToken() {
|
|
19
|
+
const settings = JSON.parse(fs.readFileSync(tokenPath, 'utf8'));
|
|
20
|
+
if (typeof settings.token !== 'string' || settings.token.length === 0) {
|
|
21
|
+
throw new Error(`missing token in ${tokenPath}`);
|
|
22
|
+
}
|
|
23
|
+
return settings.token;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function writeMessage(message) {
|
|
27
|
+
const body = JSON.stringify(message);
|
|
28
|
+
if (framing === 'ndjson') {
|
|
29
|
+
process.stdout.write(`${body}\n`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
process.stdout.write(`Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseSseOrJson(text) {
|
|
36
|
+
const trimmed = text.trim();
|
|
37
|
+
if (!trimmed) return null;
|
|
38
|
+
|
|
39
|
+
const dataLines = [];
|
|
40
|
+
for (const line of trimmed.split(/\r?\n/)) {
|
|
41
|
+
if (line.startsWith('data:')) dataLines.push(line.slice(5).trimStart());
|
|
42
|
+
}
|
|
43
|
+
if (dataLines.length > 0) {
|
|
44
|
+
return JSON.parse(dataLines.join('\n'));
|
|
45
|
+
}
|
|
46
|
+
return JSON.parse(trimmed);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function forward(message) {
|
|
50
|
+
log(`forward ${message.method || '<response>'} id=${Object.prototype.hasOwnProperty.call(message, 'id') ? message.id : '<none>'}`);
|
|
51
|
+
const token = readToken();
|
|
52
|
+
const headers = {
|
|
53
|
+
'content-type': 'application/json',
|
|
54
|
+
accept: 'application/json, text/event-stream',
|
|
55
|
+
authorization: `Bearer ${token}`,
|
|
56
|
+
};
|
|
57
|
+
if (sessionId) headers['mcp-session-id'] = sessionId;
|
|
58
|
+
|
|
59
|
+
const response = await fetch(endpoint, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers,
|
|
62
|
+
body: JSON.stringify(message),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const nextSessionId = response.headers.get('mcp-session-id');
|
|
66
|
+
if (nextSessionId) sessionId = nextSessionId;
|
|
67
|
+
|
|
68
|
+
const text = await response.text();
|
|
69
|
+
log(`response status=${response.status} bytes=${Buffer.byteLength(text)} session=${sessionId ? 'yes' : 'no'}`);
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
throw new Error(`OpenBrowserMCP HTTP ${response.status}: ${text}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const body = parseSseOrJson(text);
|
|
75
|
+
if (body) writeMessage(body);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function jsonRpcError(id, error) {
|
|
79
|
+
writeMessage({
|
|
80
|
+
jsonrpc: '2.0',
|
|
81
|
+
id,
|
|
82
|
+
error: {
|
|
83
|
+
code: -32000,
|
|
84
|
+
message: error instanceof Error ? error.message : String(error),
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function handle(message) {
|
|
90
|
+
chain = chain.then(async () => {
|
|
91
|
+
try {
|
|
92
|
+
await forward(message);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (Object.prototype.hasOwnProperty.call(message, 'id')) {
|
|
95
|
+
jsonRpcError(message.id, error);
|
|
96
|
+
} else {
|
|
97
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function drain() {
|
|
104
|
+
if (framing === null && input.length > 0) {
|
|
105
|
+
const first = input.subarray(0, 1).toString('utf8');
|
|
106
|
+
framing = first === '{' ? 'ndjson' : 'headers';
|
|
107
|
+
log(`framing=${framing}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (framing === 'ndjson') {
|
|
111
|
+
while (true) {
|
|
112
|
+
const lineEnd = input.indexOf('\n');
|
|
113
|
+
if (lineEnd === -1) return;
|
|
114
|
+
|
|
115
|
+
const line = input.subarray(0, lineEnd).toString('utf8').trim();
|
|
116
|
+
input = input.subarray(lineEnd + 1);
|
|
117
|
+
if (!line) continue;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
handle(JSON.parse(line));
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
while (true) {
|
|
128
|
+
let headerEnd = input.indexOf('\r\n\r\n');
|
|
129
|
+
let separatorLength = 4;
|
|
130
|
+
if (headerEnd === -1) {
|
|
131
|
+
headerEnd = input.indexOf('\n\n');
|
|
132
|
+
separatorLength = 2;
|
|
133
|
+
}
|
|
134
|
+
if (headerEnd === -1) return;
|
|
135
|
+
|
|
136
|
+
const header = input.subarray(0, headerEnd).toString('latin1');
|
|
137
|
+
const match = header.match(/content-length:\s*(\d+)/i);
|
|
138
|
+
if (!match) {
|
|
139
|
+
console.error('missing Content-Length header');
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const length = Number(match[1]);
|
|
144
|
+
const bodyStart = headerEnd + separatorLength;
|
|
145
|
+
const bodyEnd = bodyStart + length;
|
|
146
|
+
if (input.length < bodyEnd) return;
|
|
147
|
+
|
|
148
|
+
const body = input.subarray(bodyStart, bodyEnd).toString('utf8');
|
|
149
|
+
input = input.subarray(bodyEnd);
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
handle(JSON.parse(body));
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
process.stdin.on('data', (chunk) => {
|
|
160
|
+
log(`stdin bytes=${chunk.length}`);
|
|
161
|
+
if (process.env.OPENBROWSERMCP_PROXY_PREVIEW === '1') {
|
|
162
|
+
log(`stdin preview=${JSON.stringify(chunk.subarray(0, 200).toString('utf8'))}`);
|
|
163
|
+
}
|
|
164
|
+
input = Buffer.concat([input, chunk]);
|
|
165
|
+
drain();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
process.stdin.on('end', () => {
|
|
169
|
+
chain.finally(() => process.exit(0));
|
|
170
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: learn
|
|
3
|
+
description: Build and run evidence-based learning workflows. Use when Codex needs to create or continue a study workspace, collect and convert learning materials, design goals, teach from sources, quiz the user, score answers, track progress, maintain an error log, or run spaced review for subjects such as courses, books, technical topics, exams, or career prep.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Learn
|
|
7
|
+
|
|
8
|
+
Use this skill to turn a topic into a source-grounded, interactive learning system.
|
|
9
|
+
|
|
10
|
+
## Core Principles
|
|
11
|
+
|
|
12
|
+
- Start from a learning goal, but allow it to change as evidence of the learner's needs emerges.
|
|
13
|
+
- Prefer source-grounded teaching over generic explanation. Identify the exact source chunk before teaching.
|
|
14
|
+
- Teach in small units, then ask retrieval questions before marking progress.
|
|
15
|
+
- Treat mistakes as the main signal. Log wrong, vague, overconfident, or ungrounded answers.
|
|
16
|
+
- Use spaced review, but usually only when the user asks to review or when resuming after a noticeable gap.
|
|
17
|
+
- Avoid material hoarding. Collect enough sources to cover the goal; do not delay learning indefinitely.
|
|
18
|
+
|
|
19
|
+
Read `references/learning-science.md` when designing a new workflow, defending the method, or revising the learning loop.
|
|
20
|
+
|
|
21
|
+
## New Learning Workspace
|
|
22
|
+
|
|
23
|
+
When creating a new study workspace, use:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
python3 /path/to/learn/scripts/init_learning_workspace.py TOPIC --path /target/parent --goal "..."
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The script creates:
|
|
30
|
+
|
|
31
|
+
- `materials/` for source files and web caches
|
|
32
|
+
- `notes/` for lesson plans and ordinary notes
|
|
33
|
+
- `cases/` for worked examples or problem traces
|
|
34
|
+
- `outputs/` for compiled PDFs or exports
|
|
35
|
+
- `AGENTS.md` for local study rules
|
|
36
|
+
- `tasklog.typ` for progress, scores, and next steps
|
|
37
|
+
- `errorlog.typ` for misconceptions and review actions
|
|
38
|
+
- `workbook.typ` for worked examples, section notes, verification questions, and corrections
|
|
39
|
+
- `materials/source-index.md` for collected sources
|
|
40
|
+
- `notes/reading-sequence.md` for the first pass route
|
|
41
|
+
|
|
42
|
+
If the workspace already exists, inspect `AGENTS.md`, then `tasklog.typ`, then `errorlog.typ` before teaching.
|
|
43
|
+
|
|
44
|
+
## Source Collection
|
|
45
|
+
|
|
46
|
+
For each topic, build a source index before deep teaching:
|
|
47
|
+
|
|
48
|
+
1. Gather likely authoritative materials: books, official docs, papers, syllabi, lectures, slides, assignments, transcripts, examples, and prior notes.
|
|
49
|
+
2. Prefer primary sources: official docs, textbook chapters, course pages, papers, source code, specs.
|
|
50
|
+
3. Convert materials into AI-readable form:
|
|
51
|
+
- PDFs/books: text extraction or OCR as needed.
|
|
52
|
+
- Videos/audio: transcripts with timestamps when possible.
|
|
53
|
+
- Slides/images: OCR plus image references.
|
|
54
|
+
- Web pages: URL plus local cache or concise source notes.
|
|
55
|
+
4. Record every source in `materials/source-index.md` with purpose, priority, and local path.
|
|
56
|
+
5. Do not copy large copyrighted texts into notes. Store local references and write original summaries.
|
|
57
|
+
|
|
58
|
+
Use specialized skills when appropriate, for example OCR/document skills for PDFs or screenshots.
|
|
59
|
+
|
|
60
|
+
## Tracking Files
|
|
61
|
+
|
|
62
|
+
Use `tasklog.typ` for:
|
|
63
|
+
|
|
64
|
+
- learning goal and current assumptions
|
|
65
|
+
- scoring rubric
|
|
66
|
+
- lesson/chapter table
|
|
67
|
+
- session log
|
|
68
|
+
- next action
|
|
69
|
+
|
|
70
|
+
Use `errorlog.typ` for:
|
|
71
|
+
|
|
72
|
+
- misconception category
|
|
73
|
+
- original question
|
|
74
|
+
- learner answer, preserving the error
|
|
75
|
+
- correction
|
|
76
|
+
- domain-specific consequence or rule
|
|
77
|
+
- review action and status
|
|
78
|
+
|
|
79
|
+
Use `workbook.typ` for:
|
|
80
|
+
|
|
81
|
+
- section-level notes
|
|
82
|
+
- worked examples
|
|
83
|
+
- verification questions
|
|
84
|
+
- corrected explanations
|
|
85
|
+
- drills or transfer exercises
|
|
86
|
+
|
|
87
|
+
## Teaching Loop
|
|
88
|
+
|
|
89
|
+
For each section, chapter, lecture, or case:
|
|
90
|
+
|
|
91
|
+
1. Select a small unit from the reading sequence.
|
|
92
|
+
2. Read the relevant source material first.
|
|
93
|
+
3. Explain in the learner's language with all essential knowledge points for that unit.
|
|
94
|
+
4. Include at least one concrete example or system mapping.
|
|
95
|
+
5. Name common wrong interpretations before the quiz.
|
|
96
|
+
6. Ask verification questions that require retrieval and transfer, not just recognition.
|
|
97
|
+
7. Stop and wait for the learner's answer.
|
|
98
|
+
8. Score answers using the local rubric.
|
|
99
|
+
9. Update `tasklog.typ`.
|
|
100
|
+
10. Add or update `errorlog.typ` for every weak answer.
|
|
101
|
+
11. Only mark a unit `done` when the learner can explain, apply, and avoid the key trap.
|
|
102
|
+
|
|
103
|
+
## Review Loop
|
|
104
|
+
|
|
105
|
+
When the user asks to review, or resumes after a meaningful gap:
|
|
106
|
+
|
|
107
|
+
1. Read open/review items in `errorlog.typ`.
|
|
108
|
+
2. Ask questions that test the old misconception without showing the correction first.
|
|
109
|
+
3. If answered correctly twice across separate reviews, mark the item `closed`.
|
|
110
|
+
4. If still weak, keep it `review` and add a sharper review action.
|
|
111
|
+
|
|
112
|
+
Avoid unsolicited heavy review sessions; offer a brief review option when stale errors are visible.
|
|
113
|
+
|
|
114
|
+
## Failure Modes To Avoid
|
|
115
|
+
|
|
116
|
+
- Turning learning into passive summaries.
|
|
117
|
+
- Collecting too many materials before starting.
|
|
118
|
+
- Rigid goals that ignore what the learner discovers.
|
|
119
|
+
- Overly broad lessons with no retrieval.
|
|
120
|
+
- Marking progress without questions.
|
|
121
|
+
- Logging every tiny imperfection until the workflow becomes maintenance-heavy.
|
|
122
|
+
- Letting AI answer from memory when source grounding is available.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Learn"
|
|
3
|
+
short_description: "Evidence-based study workflows"
|
|
4
|
+
default_prompt: "Use $learn to create a source-grounded study workspace with progress tracking, retrieval questions, and an error log."
|
|
5
|
+
|
|
6
|
+
policy:
|
|
7
|
+
allow_implicit_invocation: true
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# {TOPIC} Learning Instructions
|
|
2
|
+
|
|
3
|
+
Use this file whenever the user asks to study, review, explain, quiz, or continue `{TOPIC}` in this workspace.
|
|
4
|
+
|
|
5
|
+
## Local Context
|
|
6
|
+
|
|
7
|
+
- Current learning goal: {GOAL}
|
|
8
|
+
- Goals may evolve as weak points are discovered.
|
|
9
|
+
- Prefer source-grounded teaching over generic explanation.
|
|
10
|
+
- Use retrieval questions and error logging before marking progress.
|
|
11
|
+
|
|
12
|
+
## Primary Files
|
|
13
|
+
|
|
14
|
+
- `materials/source-index.md`: collected sources and local paths.
|
|
15
|
+
- `notes/reading-sequence.md`: lesson order.
|
|
16
|
+
- `tasklog.typ`: progress, scores, and next actions.
|
|
17
|
+
- `errorlog.typ`: misconceptions, corrections, and review status.
|
|
18
|
+
- `workbook.typ`: worked examples, section notes, verification questions, and corrections.
|
|
19
|
+
|
|
20
|
+
## Default Study Method
|
|
21
|
+
|
|
22
|
+
1. Inspect `tasklog.typ`, then `errorlog.typ`, before teaching.
|
|
23
|
+
2. Select the next `doing`, `review`, or `todo` item.
|
|
24
|
+
3. Read relevant source material.
|
|
25
|
+
4. Explain the unit with all essential knowledge points.
|
|
26
|
+
5. Ask verification questions.
|
|
27
|
+
6. Wait for the learner answer.
|
|
28
|
+
7. Score answers using `tasklog.typ`.
|
|
29
|
+
8. Update `tasklog.typ`.
|
|
30
|
+
9. Add or update `errorlog.typ` for wrong, vague, or ungrounded answers.
|
|
31
|
+
10. Update `workbook.typ` for worked examples, lesson notes, or corrected explanations.
|
|
32
|
+
|
|
33
|
+
Do not mark a unit `done` until the learner can explain, apply, and avoid the key misconception.
|