@yugabytedb/meko-mcp 1.2.0 → 2.0.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 +28 -19
- package/assets/hooks-handlers/before-submit-prompt.sh +2 -2
- package/assets/hooks-handlers/lib/capture.js +209 -144
- package/assets/hooks-handlers/lib/checkpoint-timer.js +23 -11
- package/assets/hooks-handlers/session-start.sh +6 -2
- package/assets/scripts/meko-statusline.sh +119 -0
- package/assets/scripts/package-desktop-skill.sh +27 -24
- package/assets/skills/meko-mcp-tools/SKILL.md +42 -50
- package/assets/skills/meko-mcp-tools/references/_sections.md +1 -1
- package/assets/skills/meko-mcp-tools/references/tools-cookbook.md +75 -150
- package/assets/skills/meko-mcp-tools/references/tools-datapack-workflow.md +30 -36
- package/assets/skills/meko-mcp-tools/references/tools-known-limitations.md +6 -22
- package/assets/skills/meko-mcp-tools/references/tools-memory-vs-conversation.md +9 -8
- package/assets/skills/meko-mcp-tools/references/tools-overview.md +35 -42
- package/assets/skills/meko-mcp-tools/references/tools-rag-workflow.md +28 -92
- package/assets/skills/meko-mcp-tools/references/tools-scope-permissions.md +5 -9
- package/assets/skills/meko-mcp-tools/references/tools-troubleshooting.md +1 -81
- package/assets/skills/meko-mcp-tools-desktop/SKILL.md +100 -107
- package/assets/skills/meko-mcp-tools-desktop/desktop/README.md +41 -11
- package/assets/skills/meko-mcp-tools-desktop/references/_sections.md +1 -1
- package/assets/skills/meko-mcp-tools-desktop/references/tools-cookbook.md +71 -150
- package/assets/skills/meko-mcp-tools-desktop/references/tools-known-limitations.md +12 -18
- package/assets/skills/meko-mcp-tools-desktop/references/tools-memory-vs-conversation.md +12 -15
- package/assets/skills/meko-mcp-tools-desktop/references/tools-overview.md +35 -42
- package/assets/skills/meko-mcp-tools-desktop/references/tools-rag-workflow.md +28 -92
- package/assets/skills/meko-mcp-tools-desktop/references/tools-scope-permissions.md +5 -9
- package/assets/skills/meko-mcp-tools-desktop/references/tools-troubleshooting.md +1 -81
- package/assets/skills/meko-select-datapack-desktop/SKILL.md +198 -0
- package/assets/skills/meko-select-datapack-desktop/references/cookbook.md +121 -0
- package/assets/skills/meko-select-datapack-desktop/references/troubleshooting.md +66 -0
- package/bin/create-meko-setup.mjs +29 -0
- package/lib/clients/claude-code.mjs +133 -6
- package/lib/clients/claude-desktop.mjs +37 -12
- package/lib/clients/codex.mjs +306 -5
- package/lib/clients/common.mjs +41 -1
- package/lib/clients/cursor.mjs +75 -19
- package/lib/config.mjs +26 -0
- package/lib/installer.mjs +24 -0
- package/lib/mcp-transport.mjs +5 -0
- package/lib/validate.mjs +0 -1
- package/package.json +1 -1
- package/scripts/bundle-assets.mjs +8 -0
- package/assets/skills/meko-mcp-tools/references/tools-db-query-patterns.md +0 -38
- package/assets/skills/meko-mcp-tools/references/tools-write-guardrails.md +0 -30
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ npx @yugabytedb/meko-mcp --client cursor --local --scope project --yes
|
|
|
42
42
|
|
|
43
43
|
**Claude Desktop install:**
|
|
44
44
|
|
|
45
|
-
Claude Desktop has two install paths — pick based on whether you want the agent skill (the behavioral guide that teaches the agent *when* to call Meko tools,
|
|
45
|
+
Claude Desktop has two install paths — pick based on whether you want the agent skill (the behavioral guide that teaches the agent *when* to call Meko tools — how to capture turns via per-turn `conversation_add_message` posting so the server can extract memories, and when the narrow explicit `memory_add` cases apply):
|
|
46
46
|
|
|
47
47
|
| | Option A: `.mcpb` Desktop Extension | Option B: `create-meko-setup` installer |
|
|
48
48
|
|---|---|---|
|
|
@@ -50,7 +50,7 @@ Claude Desktop has two install paths — pick based on whether you want the agen
|
|
|
50
50
|
| Installs the agent skill (behavioral guide) | **No** | **Yes** |
|
|
51
51
|
| Repo clone needed | No | No (works via `npx`); clones also work |
|
|
52
52
|
| Best for | Non-technical users; quickest path to a working MCP connection | Anyone who wants the skill that teaches the agent *when* to call Meko tools |
|
|
53
|
-
| Auto-capture of conversations/memories | No (see [issue #8](https://github.com/amiramm/meko-mcp-server/issues/8)) | Skill-driven
|
|
53
|
+
| Auto-capture of conversations/memories | No (see [issue #8](https://github.com/amiramm/meko-mcp-server/issues/8)) | Skill-driven — the agent posts each substantive turn via `conversation_add_message` and the server extracts memories from those posts. Still less reliable than Claude Code hooks because it's agent-driven, not lifecycle-driven. |
|
|
54
54
|
|
|
55
55
|
You can also combine both: install via `.mcpb` for the MCP wiring, then run Option B to add the skill bundle. If you re-run the Option B installer on top of an `.mcpb` install, it detects the existing entry and prompts before overwriting `mcpServers.meko`.
|
|
56
56
|
|
|
@@ -81,16 +81,16 @@ npx @yugabytedb/meko-mcp --client claude-desktop --local --yes
|
|
|
81
81
|
This does two things:
|
|
82
82
|
|
|
83
83
|
1. Merges `mcpServers.meko` into `claude_desktop_config.json` (backed up to `.bak`).
|
|
84
|
-
2. Builds
|
|
84
|
+
2. Builds two bundles — `meko-mcp-tools-desktop.skill` (memory doctrine) and `meko-select-datapack-desktop.skill` (datapack selection/pinning) — and prints their absolute paths. The location depends on how you invoked the installer — under the npm package's asset root for `npx`, or under `skills/` in your clone. **Import both**; the selector bundle is required for pinned-datapack capture routing.
|
|
85
85
|
|
|
86
86
|
Then, **once per machine**:
|
|
87
87
|
|
|
88
|
-
1. Open the path that the installer printed (look for the `Built Meko skill bundle: ...` log
|
|
89
|
-
2. Drag `meko-mcp-tools-desktop.skill` onto the Claude Desktop window — or import
|
|
88
|
+
1. Open the path that the installer printed (look for the `Built Meko skill bundle: ...` log lines — there are two).
|
|
89
|
+
2. Drag **both** `meko-mcp-tools-desktop.skill` and `meko-select-datapack-desktop.skill` onto the Claude Desktop window — or import each via **Settings > Extensions > Install from file**.
|
|
90
90
|
3. Quit and relaunch Claude Desktop.
|
|
91
|
-
4. Verify
|
|
91
|
+
4. Verify both loaded: ask *"Which Meko skills do you have available?"* — the agent should mention `meko-mcp-tools-desktop` and `meko-select-datapack-desktop`.
|
|
92
92
|
|
|
93
|
-
Re-run the installer to refresh the
|
|
93
|
+
Re-run the installer to refresh the bundles when skill content updates; you'll need to re-drag the rebuilt `.skill` files to pick up changes.
|
|
94
94
|
|
|
95
95
|
**Install on multiple clients at once:**
|
|
96
96
|
|
|
@@ -145,15 +145,22 @@ The canary used to do a `memory_add` → `memory_get_by_id` → `memory_delete_b
|
|
|
145
145
|
- **Cursor**
|
|
146
146
|
- MCP config: `~/.cursor/mcp.json` or `.cursor/mcp.json`
|
|
147
147
|
- Hooks config: `~/.cursor/hooks.json` or `.cursor/hooks.json` (native mode)
|
|
148
|
-
-
|
|
148
|
+
- Stable hook scripts: `~/.cursor/meko-hooks/hooks-handlers` or `.cursor/meko-hooks/hooks-handlers`. The installer copies the shipped tree here so an npx cache cleanup cannot break configured hooks.
|
|
149
|
+
- Skills path: `~/.cursor/skills` or `.cursor/skills`. Packaged installs copy skills instead of symlinking back into the ephemeral npm package cache.
|
|
149
150
|
- **OpenAI Codex (CLI + Desktop App)** — one install configures both surfaces (they share `~/.codex/`).
|
|
150
151
|
- MCP config: `~/.codex/config.toml` (TOML; `[mcp_servers.<name>]` block).
|
|
151
152
|
- Skills path: `~/.agents/skills/meko-mcp-tools/` (the shared agent-skills convention).
|
|
152
153
|
- System prompt: `~/.codex/AGENTS.md` (a Meko-managed sentinel block is upserted; user content outside the fence is preserved).
|
|
153
154
|
- **Cloud auth (MCP server)**: Codex's TOML stores `bearer_token_env_var = "MEKO_API_KEY"` rather than the key itself. You must export `MEKO_API_KEY` in your shell before launching Codex. The installer offers (interactively, never with `--yes`) to append the export line to your shell rc (`~/.zshrc` / `~/.bashrc` / `~/.config/fish/config.fish`). On macOS, when the desktop app is detected and an API key is supplied, the installer runs `launchctl setenv MEKO_API_KEY ...` so Codex.app launched from Finder/Dock can authenticate in the current login session.
|
|
155
|
+
- **Cloud WAF User-Agent** (cloud installs only): Cloud Meko sits behind a WAF that returns an HTML `403 Forbidden` for any HTTP request with no `User-Agent`. Codex's native MCP client sends none by default, so its `initialize` handshake would 403 and the `meko` server would fail to start with `MCP client for 'meko' failed to start ... HTTP 403`. `codex mcp add` exposes no header flag, so the installer writes a per-server header into `~/.codex/config.toml`:
|
|
156
|
+
```toml
|
|
157
|
+
[mcp_servers.meko]
|
|
158
|
+
http_headers = { "User-Agent" = "codex-mcp/1.0 (+https://github.com/yugabyte/meko-mcp-server)" }
|
|
159
|
+
```
|
|
160
|
+
This is **scoped to the Meko server block only** — it affects requests Codex makes to the Meko MCP server and nothing else (other MCP servers, other Codex traffic are untouched). The installer **merges** rather than clobbers: it reads whatever headers you already have — in either the inline `http_headers = { … }` form or the table form `[mcp_servers.meko.http_headers]` — keeps every one of them, and only *adds* a `User-Agent` if you don't already have one (a custom User-Agent is left exactly as you set it). If you had the table form, it's folded into the single inline line so there's never a conflicting duplicate definition. This holds **across a reinstall** too: re-registration deletes and rewrites the whole server block, so the installer snapshots your headers beforehand and merges them back. It **backs up `config.toml` to `config.toml.bak`** before writing. Local installs skip this (no WAF in front of `localhost`). Related: the capture-hook side of the same WAF issue is [#174](https://github.com/yugabyte/meko-mcp-server/issues/174).
|
|
154
161
|
- **No project scope**: Codex has no per-project config. `--scope project --client codex` warns and degrades to user scope.
|
|
155
|
-
- **Auto-capture hooks**: when Codex hooks are available, the installer writes Meko lifecycle hooks to `~/.codex/hooks.json` and prunes stale Meko hook entries from deleted install paths.
|
|
156
|
-
- **API key storage** (security note): Codex's hooks.json schema rejects a top-level `env` block and provides no per-command env field. The installer writes `MEKO_MCP_URL` and (when supplied) `MEKO_API_KEY` to `~/.codex/meko-env.sh` at mode 0600 (user read/write only); each hook's `command` field sources that file before invoking bash, e.g. `. '/Users/you/.codex/meko-env.sh' && bash '
|
|
162
|
+
- **Auto-capture hooks**: when Codex hooks are available, the installer copies the Meko hook scripts to `~/.codex/meko-hooks/hooks-handlers/`, writes Meko lifecycle hooks to `~/.codex/hooks.json` pointing at that copy, and prunes stale Meko hook entries from deleted install paths. The scripts live under `$CODEX_HOME` (not the shipped package) on purpose: under `npx @yugabytedb/meko-mcp` the package unpacks into an ephemeral `~/.npm/_npx/<hash>/` cache dir that npx later garbage-collects, so a `hooks.json` pinned there would become a dangling `bash '<missing>'` and Codex would report `SessionStart hook (failed) — hook exited with code 127`. Copying under `$CODEX_HOME` gives the hook a home-dir-stable target; re-running the installer refreshes the copy and auto-heals any stale npx-pinned entry. `memory_search` still handles recall; capture is driven by the hooks (and, where hooks are unavailable, by AGENTS.md-prompted per-turn `conversation_add_message` posting that the server extracts memories from). Explicit `memory_add` is reserved for the narrow cases — the user says "remember this", a fact lives only in the assistant's output or a tool result, or a negated/corrected fact needs overwriting.
|
|
163
|
+
- **API key storage** (security note): Codex's hooks.json schema rejects a top-level `env` block and provides no per-command env field. The installer writes `MEKO_MCP_URL` and (when supplied) `MEKO_API_KEY` to `~/.codex/meko-env.sh` at mode 0600 (user read/write only); each hook's `command` field sources that file before invoking bash, e.g. `. '/Users/you/.codex/meko-env.sh' && bash '/Users/you/.codex/meko-hooks/hooks-handlers/session-start.sh'`. The key never appears in process arguments, so `ps -ef` from another user can't lift it while a hook is running. Trust boundary is the same as `~/.aws/credentials` or `~/.netrc` — file-system permissions on the user's home directory. If your threat model precludes even that, install for the local Meko URL (no API key prompt), or skip hook installation by deleting the Meko entries from `~/.codex/hooks.json` and removing `~/.codex/meko-env.sh` and `~/.codex/meko-hooks/` after install.
|
|
157
164
|
- **Restart Codex.app** after install so it picks up the new MCP server registration.
|
|
158
165
|
|
|
159
166
|
## CLI Reference
|
|
@@ -325,7 +332,7 @@ memory errs: 0 (failed memory_add calls; re-run with --force to
|
|
|
325
332
|
### What does NOT get migrated (v1)
|
|
326
333
|
|
|
327
334
|
- Slash-command skills, hooks, and `.claude/settings.json` — those remain in Claude Code's own config; Meko has no equivalent tool.
|
|
328
|
-
- Knowledge-base documents —
|
|
335
|
+
- Knowledge-base documents — KB ingestion is UI-only (Datapack → Actions → Add Knowledge); not exposed via MCP.
|
|
329
336
|
- Transcripts from other coding agents (Aider, Continue.dev, Windsurf, Codex). Claude Code + Cursor only.
|
|
330
337
|
- `user_id` is left null on every call (the server fills it from the API key). Multi-user routing is a follow-up.
|
|
331
338
|
|
|
@@ -336,11 +343,13 @@ memory errs: 0 (failed memory_add calls; re-run with --force to
|
|
|
336
343
|
| One-command install | Yes | Yes | Yes | Yes |
|
|
337
344
|
| MCP server registration | via `claude mcp add` | via `claude_desktop_config.json` merge using `mcp-remote` stdio bridge | via `.cursor/mcp.json` merge | via `codex mcp add` (CLI) or direct TOML write (App-only) |
|
|
338
345
|
| Backup before config write | `.bak` on `~/.claude/settings.json` | `.bak` on `claude_desktop_config.json` | `.bak` on Cursor config | `.bak` on `~/.codex/config.toml` and `~/.codex/AGENTS.md` |
|
|
339
|
-
| Automatic conversation capture | Yes (hooks) | **No** — see [issue #8](https://github.com/amiramm/meko-mcp-server/issues/8) | Yes (native hooks) |
|
|
340
|
-
| Automatic memory persistence | Yes (hooks + skill) | Skill only (proactive `memory_add`) | Yes (hooks + skill) |
|
|
346
|
+
| Automatic conversation capture | Yes (hooks) | **No** — see [issue #8](https://github.com/amiramm/meko-mcp-server/issues/8) | Yes (native hooks; first SessionStart may pause briefly while creating the Meko conversation) | **No** — Codex has no user-installable hooks; AGENTS.md prompts proactive memory tool calls instead |
|
|
347
|
+
| Automatic memory persistence | Yes (hooks + skill) | Skill only (proactive `memory_add`) | Yes (hooks + skill) | Skill + AGENTS.md (proactive `memory_add`) |
|
|
341
348
|
| Skill install | Plugin marketplace (automatic) | `.skill` bundle auto-built, user drags into app | Copy into `.cursor/skills` | Copy into `~/.agents/skills/meko-mcp-tools/` |
|
|
342
349
|
| Uninstall | Full automatic | MCP entry removed; skill must be removed manually from app | MCP + hooks + skills cleaned up | MCP entry, skill dir, and AGENTS.md block all cleaned up |
|
|
343
|
-
|
|
350
|
+
|
|
351
|
+
Cursor previously had a hybrid hook mode that also wrote Claude-compatible hooks into `~/.claude/settings.json`. If you used that mode and now see duplicate `claude_code:*` Meko instructions in Cursor, remove the old Meko hook entries from `~/.claude/settings.json` unless you still use them for Claude Code.
|
|
352
|
+
| `agent_id` used by skill | `claude_code:<repo>` per project (via SessionStart hook); cross-project facts go to `meko_agent` | `claude_desktop` | `cursor:<repo>` per project (via Cursor hook); cross-project facts go to `meko_agent` | Whatever the agent derives by following the meko-mcp-tools skill (no hook injects an agent_id; the AGENTS.md block defers to the skill) |
|
|
344
353
|
| Transport | HTTP only | HTTP (or stdio via hand-edited config) | HTTP only | HTTP (Codex's `bearer_token_env_var` model) |
|
|
345
354
|
| Auth model | inline `Authorization: Bearer ...` header | inline header via `mcp-remote` | inline header | MCP server: env-var name reference (`MEKO_API_KEY` exported in shell or via `launchctl setenv`). Hooks: key written to `~/.codex/meko-env.sh` at mode 0600; each hook command sources the file before bash (no env block in Codex's hooks schema; see [Security](#security)). |
|
|
346
355
|
| Project scope | Yes (`--scope project`) | No | Yes (`--scope project`) | No (warns and degrades to user scope) |
|
|
@@ -374,14 +383,14 @@ The installer auto-detects cloud vs local based on the URL: any URL containing `
|
|
|
374
383
|
- `MEKO_API_KEY` env var — written to `~/.cursor/hooks.json` (native hook mode) and/or `~/.claude/settings.json` (claude-compat hook mode) under `env` so lifecycle hooks can authenticate.
|
|
375
384
|
- **OpenAI Codex (CLI + Desktop App)**
|
|
376
385
|
- MCP-server bearer reference — `~/.codex/config.toml` stores `bearer_token_env_var = "MEKO_API_KEY"`, **not** the key itself. The runtime env (shell rc or `launchctl setenv`) supplies the value.
|
|
377
|
-
- Hook commands — Codex's `~/.codex/hooks.json` schema rejects a top-level `env` block and provides no per-command env field, so the installer writes `MEKO_API_KEY` to `~/.codex/meko-env.sh` at **mode 0600** (user read/write only). Each Meko hook's `command` field sources that file before invoking bash, so the key never appears in process arguments — `ps -ef` from another user can't lift it while the hook runs. Same trust boundary as `~/.aws/credentials` or `~/.netrc` (file-system permissions on the user's home directory). To opt out: install for the local Meko URL (no key prompt), or skip Codex hook installation by deleting the Meko entries from `~/.codex/hooks.json` and removing `~/.codex/meko-env.sh` after install.
|
|
386
|
+
- Hook commands — Codex's `~/.codex/hooks.json` schema rejects a top-level `env` block and provides no per-command env field, so the installer writes `MEKO_API_KEY` to `~/.codex/meko-env.sh` at **mode 0600** (user read/write only). Each Meko hook's `command` field sources that file before invoking bash, so the key never appears in process arguments — `ps -ef` from another user can't lift it while the hook runs. Same trust boundary as `~/.aws/credentials` or `~/.netrc` (file-system permissions on the user's home directory). To opt out: install for the local Meko URL (no key prompt), or skip Codex hook installation by deleting the Meko entries from `~/.codex/hooks.json` and removing `~/.codex/meko-env.sh` and `~/.codex/meko-hooks/` after install. The hook scripts themselves are copied to `~/.codex/meko-hooks/hooks-handlers/` (a home-dir-stable location) rather than referenced from the shipped package, so `npx`'s cache garbage-collection can't leave the hook pointing at a deleted script (which would fail with `code 127`).
|
|
378
387
|
|
|
379
388
|
**How credentials are protected:**
|
|
380
389
|
|
|
381
390
|
- The `--verbose` flag never prints the actual API key. It always appears as `<set>` or `<not set>`.
|
|
382
391
|
- `--dry-run` mode redacts the key in displayed commands (shown as `<set>`).
|
|
383
392
|
- The API key is never logged to stdout or stderr during normal operation.
|
|
384
|
-
- Interactive mode uses
|
|
393
|
+
- Interactive mode uses silent input (sudo/ssh-style — no characters echoed) when prompting for the API key to prevent cleartext leaks during paste or with specific terminal configurations.
|
|
385
394
|
- The `MEKO_API_KEY` env var fallback means the key does not need to appear in shell history — set it in your shell profile or CI secrets.
|
|
386
395
|
- During Codex installs, the installer emits a `warn`-level log line confirming where the API key has been written.
|
|
387
396
|
|
|
@@ -561,7 +570,7 @@ cp ~/.claude/settings.json.bak ~/.claude/settings.json
|
|
|
561
570
|
Missing core tools: memory_search, memory_get_by_id, conversation_create. Found 0 tool(s) total.
|
|
562
571
|
```
|
|
563
572
|
|
|
564
|
-
The MCP server responded but does not expose the expected tools (`memory_search`, `memory_get_by_id`, `conversation_create`, `
|
|
573
|
+
The MCP server responded but does not expose the expected tools (`memory_search`, `memory_get_by_id`, `conversation_create`, `datapack_list`). This usually means a server version mismatch. Update the MCP server or check which datapack is connected. `memory_get_by_id` is used by the canary round-trip for direct-row verification (see [Post-install validation](#post-install-validation-canary-round-trip) above), so an older server that exposes `memory_search` but not `memory_get_by_id` will fail the canary check.
|
|
565
574
|
|
|
566
575
|
### DNS lookup failed
|
|
567
576
|
|
|
@@ -587,7 +596,7 @@ After the installer writes `claude_desktop_config.json`, **quit and relaunch** C
|
|
|
587
596
|
```json
|
|
588
597
|
"meko": {
|
|
589
598
|
"command": "npx",
|
|
590
|
-
"args": ["-y", "mcp-remote", "<url>", "--transport", "http-only"]
|
|
599
|
+
"args": ["-y", "mcp-remote", "<url>", "--transport", "http-only", "--header", "User-Agent:meko-mcp-installer/1.0"]
|
|
591
600
|
}
|
|
592
601
|
```
|
|
593
602
|
If you see a `"url"` field at the top level instead, re-run the installer to rewrite it.
|
|
@@ -606,7 +615,7 @@ The installer runs `skills/scripts/package-desktop-skill.sh` via `bash`. If pack
|
|
|
606
615
|
|
|
607
616
|
### Claude Desktop capture doesn't happen automatically
|
|
608
617
|
|
|
609
|
-
Expected: Claude Desktop has no SessionStart / PreCompact / SessionEnd hook API, so
|
|
618
|
+
Expected: Claude Desktop has no SessionStart / PreCompact / SessionEnd hook API, so there is no lifecycle-driven transcript capture. Instead, the skill tells the agent to post each substantive turn via `conversation_add_message` (creating a conversation first with `conversation_create`); the server then extracts durable memories from those posts. This per-turn posting replaces the old proactive-`memory_add` model and addresses the proactive-save unreliability tracked in [issue #8](https://github.com/amiramm/meko-mcp-server/issues/8), but it still relies on agent behavior rather than a hook.
|
|
610
619
|
|
|
611
620
|
## Uninstalling
|
|
612
621
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
|
|
3
|
-
# Cursor native beforeSubmitPrompt hook.
|
|
4
|
-
#
|
|
3
|
+
# Legacy Cursor native beforeSubmitPrompt hook. Cursor does not support context
|
|
4
|
+
# injection from this event; native installs use SessionStart for context.
|
|
5
5
|
|
|
6
6
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
7
|
|