From 925eec1c955db82c494b9168c215c0c3666f9e5e Mon Sep 17 00:00:00 2001 From: Gowtham Date: Sat, 30 May 2026 22:49:55 -0600 Subject: [PATCH 1/4] Prefer lnk CLI command --- README.md | 63 +++---- docs/cli.html | 204 +++++++++++----------- docs/concepts.html | 10 +- docs/getting-started.html | 48 ++--- docs/index.html | 14 +- docs/mcp.html | 24 +-- docs/memory-contract.html | 4 +- docs/obsidian.html | 16 +- docs/scale.html | 12 +- docs/security.html | 18 +- docs/team-security.html | 46 ++--- docs/troubleshooting.html | 32 ++-- docs/ui.html | 4 +- integrations/README.md | 2 +- integrations/_shared/instructions.ps1 | 4 +- integrations/_shared/instructions.sh | 4 +- integrations/_shared/link-instructions.md | 18 +- integrations/_shared/scaffold.ps1 | 19 +- integrations/_shared/scaffold.sh | 26 +-- mcp_package/link_core/benchmark.py | 2 +- mcp_package/link_core/ingest.py | 54 +++--- mcp_package/link_core/mcp_connect.py | 14 +- mcp_package/link_core/mcp_verify.py | 13 ++ mcp_package/link_core/memory_log.py | 4 +- mcp_package/link_core/memory_wins.py | 10 +- mcp_package/link_core/obsidian.py | 4 +- mcp_package/link_core/share.py | 2 +- mcp_package/link_core/snapshot.py | 4 +- mcp_package/link_core/team_sync.py | 10 +- mcp_package/link_core/web_health.py | 2 +- mcp_package/link_core/web_propose.py | 2 +- mcp_package/link_core/wiki.py | 2 +- mcp_package/link_mcp/server.py | 2 +- packaging/homebrew/Formula/link.rb | 16 +- packaging/homebrew/README.md | 4 +- scripts/check_tool_contract.py | 2 + tests/test_benchmark_core.py | 2 +- tests/test_cli_admin_core.py | 4 +- tests/test_cli_memory_core.py | 4 +- tests/test_cli_query_core.py | 8 +- tests/test_cli_runtime_core.py | 38 ++-- tests/test_ingest_core.py | 10 +- tests/test_installers.py | 12 +- tests/test_link_cli.py | 20 +-- tests/test_mcp_connect_core.py | 4 +- tests/test_mcp_contract.py | 6 +- tests/test_operations_core.py | 2 +- tests/test_prompts_core.py | 6 +- tests/test_serve.py | 6 +- tests/test_share_core.py | 2 +- tests/test_tool_contract.py | 2 +- tests/test_web_health_core.py | 10 +- tests/test_web_ingest_core.py | 16 +- tests/test_web_memory_core.py | 14 +- tests/test_web_memory_pages_core.py | 16 +- tests/test_web_prompts_core.py | 16 +- 56 files changed, 475 insertions(+), 438 deletions(-) diff --git a/README.md b/README.md index 0c1a025..6431b1a 100644 --- a/README.md +++ b/README.md @@ -73,10 +73,13 @@ macOS with Homebrew: ```bash brew install gowtham0992/link/link -link try -link serve link-demo +lnk try +lnk serve link-demo ``` +The installed command is `lnk` because `link` is already a POSIX/macOS system +utility. From a source checkout, use `python3 link.py ...` instead. + Windows or source checkout: ```powershell @@ -97,7 +100,7 @@ python3 link.py next link-demo python3 link.py serve link-demo ``` -Use `link try` for the shortest Homebrew proof loop. It creates the demo, +Use `lnk try` for the shortest Homebrew proof loop. It creates the demo, checks readiness, runs a compact query/brief proof, and prints the agent prompts and viewer command. From source, use `python3 link.py try`. @@ -116,15 +119,15 @@ The web viewer is for local use only. It binds to `127.0.0.1`, has no user accounts or authentication, and should not be exposed to the internet unless you add your own auth layer. -For the shortest guided proof path, run `link welcome link-demo`. +For the shortest guided proof path, run `lnk welcome link-demo`. Try the value loop: ```bash -link query "why does Link help agents?" link-demo --budget small -link brief "working on agent memory" link-demo -link benchmark "agent memory" link-demo -link health link-demo +lnk query "why does Link help agents?" link-demo --budget small +lnk brief "working on agent memory" link-demo +lnk benchmark "agent memory" link-demo +lnk health link-demo ``` The `/health` page mirrors the readiness loop in the browser: validation state, @@ -168,7 +171,7 @@ limits are. Pick the surface that matches how you work. They all read and write the same local Markdown wiki. -These surfaces are independent. `link serve` / `serve.py` is only the local web +These surfaces are independent. `lnk serve` / `serve.py` is only the local web viewer. CLI commands and MCP tools read the same `wiki/` files directly, so Claude, Codex, Kiro, Cursor, or another MCP client can use Link even when the web viewer is not running. @@ -239,10 +242,10 @@ connection helper. It previews the exact config first; add `--write` when you want Link to update the agent config file. ```bash -link connect codex ~/link -link connect codex ~/link --write -link connect kiro ~/link --write -link verify-mcp ~/link +lnk connect codex ~/link +lnk connect codex ~/link --write +lnk connect kiro ~/link --write +lnk verify-mcp ~/link ```
@@ -279,8 +282,8 @@ Obsidian users can import an existing vault into `raw/` for agent ingest, or open `~/link/wiki` directly as a vault for editing Link pages: ```bash -link init ~/link -link import-obsidian ~/Documents/ObsidianVault ~/link +lnk init ~/link +lnk import-obsidian ~/Documents/ObsidianVault ~/link ``` See the [Obsidian guide](https://gowtham0992.github.io/link/obsidian.html) for @@ -307,7 +310,7 @@ The storage model is plain and inspectable: | `wiki/` | Source-backed pages, concepts, entities, explorations, comparisons, and memories. | | MCP tools | Compact packets agents can use without dumping the whole wiki into context. | -If a raw file was already ingested and later edited, `link ingest-status` marks it +If a raw file was already ingested and later edited, `lnk ingest-status` marks it as stale and tells your agent to refresh the existing source page instead of creating a duplicate. @@ -346,42 +349,42 @@ Use `visibility` to separate where a memory applies from who should see it: `private` stays personal, `project` is intended for a project workspace, and `team` means the user explicitly approved sharing it with a team. -For team handoff or security review, `link compliance-export --output audit.json` +For team handoff or security review, `lnk compliance-export --output audit.json` writes a redacted JSON packet with readiness, validation, memory review status, operation markers, and recent audit log entries. Raw source contents and memory bodies are not included. -For day-to-day auditability, `link memory-log ~/link` shows what Link recently +For day-to-day auditability, `lnk memory-log ~/link` shows what Link recently remembered, updated, reviewed, archived, restored, forgot, or accepted from raw captures. -For recovery, `link backup ~/link` creates a local archive and `link +For recovery, `lnk backup ~/link` creates a local archive and `lnk restore-backup ~/link` previews what would be restored. Passing `--confirm` replaces local files after creating a safety backup when possible; `raw/` is still excluded unless `--include-raw` is explicit. -For local proof of value, `link wins ~/link` shows reusable memories, reviewed +For local proof of value, `lnk wins ~/link` shows reusable memories, reviewed memory, provenance, project continuity, freshness guardrails, and copyable prompts without tracking user behavior. -For Git-backed team memory, `link team-sync ~/link` checks whether the workspace +For Git-backed team memory, `lnk team-sync ~/link` checks whether the workspace is ready to share reviewed `wiki/` pages while keeping `raw/`, caches, backups, and local MCP Python markers private by default. It also blocks "ready" status when the memory inbox is not clear or active `visibility: private` memories would be included by a broad `git add wiki`. ```bash -link team-sync ~/link --remote git@example.com:team/link-memory.git +lnk team-sync ~/link --remote git@example.com:team/link-memory.git ``` -For a teammate, reviewer, or another agent, `link share` resolves a page, +For a teammate, reviewer, or another agent, `lnk share` resolves a page, memory, title, alias, or search phrase into a local viewer URL: ```bash -link share "Prefer local memory" ~/link +lnk share "Prefer local memory" ~/link ``` -For a static, read-only review packet, `link snapshot` exports rendered wiki +For a static, read-only review packet, `lnk snapshot` exports rendered wiki HTML without `raw/`, captures, operation markers, live MCP state, or memory pages by default. `--include-memories` exports only non-private memories; use `--include-private-memories` only for a personal archive or an explicitly @@ -389,9 +392,9 @@ approved review. It blocks export if wiki pages contain secret-looking values unless you explicitly override it. ```bash -link snapshot ~/link --output link-snapshot -link snapshot ~/link --output link-snapshot --include-memories --force -link snapshot ~/link --output personal-snapshot --include-memories --include-private-memories --force +lnk snapshot ~/link --output link-snapshot +lnk snapshot ~/link --output link-snapshot --include-memories --force +lnk snapshot ~/link --output personal-snapshot --include-memories --include-private-memories --force ``` ## Agent Contract @@ -417,10 +420,10 @@ Link itself is local-first: - No hosted backend. - No external API calls from `serve.py` or `link-mcp`. - Raw sources and generated wiki pages are ignored by git by default. -- `link backup` excludes `raw/` unless you explicitly pass `--include-raw`. +- `lnk backup` excludes `raw/` unless you explicitly pass `--include-raw`. - Secret-looking API keys, provider tokens, JWTs, registry credentials, and private key blocks are detected in raw sources, captures, and release hygiene - checks. `link validate` and `link doctor` also fail if secret-looking values + checks. `lnk validate` and `lnk doctor` also fail if secret-looking values are found inside wiki pages before they can be served through the local UI or returned through MCP context. - The local web server binds to `127.0.0.1` and is not meant to be exposed to diff --git a/docs/cli.html b/docs/cli.html index 16c38d0..5576cf6 100644 --- a/docs/cli.html +++ b/docs/cli.html @@ -38,7 +38,7 @@
Command reference

Local commands for daily memory work.

-

Install the CLI with brew install gowtham0992/link/link on macOS, or use link <command> after a global agent installer run. From a source checkout, use python3 link.py <command> on macOS/Linux or py link.py <command> on Windows.

+

Install the CLI with brew install gowtham0992/link/link on macOS, or use lnk <command> after a global agent installer run. Link uses lnk because link is already a POSIX/macOS system utility. From a source checkout, use python3 link.py <command> on macOS/Linux or py link.py <command> on Windows.

@@ -59,123 +59,123 @@

CLI Tour

Animated Link CLI walkthrough
Status, query, brief, and benchmark are the core terminal loop.
-

The CLI is independent of the web viewer. Use link query, link brief, link health, and maintenance commands even when link serve is not running.

-

Use link try when you want the shortest proof loop: demo creation, readiness, query proof, brief proof, viewer command, and first agent prompts in one command.

+

The CLI is independent of the web viewer. Use lnk query, lnk brief, lnk health, and maintenance commands even when lnk serve is not running.

+

Use lnk try when you want the shortest proof loop: demo creation, readiness, query proof, brief proof, viewer command, and first agent prompts in one command.

Daily Loop

-
link serve
-link welcome
-link next
-link health
-link ingest-status
-link remember "User prefers feature branches for Link work." --type preference --scope project --project link --visibility project --review-after 2026-08-01
-link remember "Temporary launch branch is release/one-off." --type project --project link --expires-at 2026-09-01
-link share "Prefer local memory"
-link snapshot ~/link --output link-snapshot
-link brief "working on Link release" --project link
-link query "what should I know before changing the MCP tools?" --budget small --project link
-link validate
+
lnk serve
+lnk welcome
+lnk next
+lnk health
+lnk ingest-status
+lnk remember "User prefers feature branches for Link work." --type preference --scope project --project link --visibility project --review-after 2026-08-01
+lnk remember "Temporary launch branch is release/one-off." --type project --project link --expires-at 2026-09-01
+lnk share "Prefer local memory"
+lnk snapshot ~/link --output link-snapshot
+lnk brief "working on Link release" --project link
+lnk query "what should I know before changing the MCP tools?" --budget small --project link
+lnk validate

Memory Commands

-

link remember

Save a local agent memory. Strong duplicates and likely conflicts are refused unless explicitly allowed.

-

link recall

Search local memories with recall readiness and project-aware filtering.

-

link explain-memory

Show provenance, lifecycle, graph links, review issues, and recall readiness.

-

link update-memory

Merge new text into an existing memory and reset review state.

-

link archive-memory

Reversibly hide a stale or wrong memory from default recall.

-

link forget-memory

Permanently delete a memory after explicit confirmation.

+

lnk remember

Save a local agent memory. Strong duplicates and likely conflicts are refused unless explicitly allowed.

+

lnk recall

Search local memories with recall readiness and project-aware filtering.

+

lnk explain-memory

Show provenance, lifecycle, graph links, review issues, and recall readiness.

+

lnk update-memory

Merge new text into an existing memory and reset review state.

+

lnk archive-memory

Reversibly hide a stale or wrong memory from default recall.

+

lnk forget-memory

Permanently delete a memory after explicit confirmation.

Capture Workflow

Use captures for longer chat notes or session summaries that should be reviewed before becoming durable memory.

-
link capture-session session-notes.md --project link
-link capture-inbox --project link
-link accept-capture raw/memory-captures/<capture>.md --index 1
-link redact-capture raw/memory-captures/<capture>.md
-link delete-capture raw/memory-captures/<capture>.md --confirm
+
lnk capture-session session-notes.md --project link
+lnk capture-inbox --project link
+lnk accept-capture raw/memory-captures/<capture>.md --index 1
+lnk redact-capture raw/memory-captures/<capture>.md
+lnk delete-capture raw/memory-captures/<capture>.md --confirm

Maintenance

-
link backup
-link doctor --fix
-link health
-link status --validate
-link memory-audit
-link compliance-export --output link-audit.json
-link team-sync ~/link
-link snapshot ~/link --output link-snapshot
-link operations
-link benchmark "agent memory"
-link rebuild-index
-link rebuild-backlinks
-link validate
-link verify-mcp
-

Use link backup before broad repair work. Use link benchmark when a wiki starts to feel slow. link status --validate and link benchmark both show persistent-cache reuse so you can tell whether Link is rereading every page or reusing unchanged records.

-

Use link team-sync before sharing Link through Git. It is read-only: it checks Git state, verifies that raw/ is protected, checks whether review or visibility: private memories would make sharing unsafe, and prints paste-safe setup/sync commands without pushing private source material for you.

-

Use link snapshot when you need a static, read-only HTML export for a teammate, maintainer, security reviewer, or demo. It excludes raw/, captures, local operation state, and memory pages by default. With --include-memories, it still excludes visibility: private memories unless --include-private-memories is explicitly passed.

-

Use link connect <agent> when an agent already has Link instructions but still needs MCP wiring. It previews the config before writing.

-
link connect codex ~/link
-link connect codex ~/link --write
-link connect kiro ~/link --write
-link verify-mcp ~/link
-

From a source checkout, use the synthetic large-wiki smoke when you want local scale evidence without touching your real wiki. The script prints the exact link serve command and graph URL for the generated fixture.

+
lnk backup
+lnk doctor --fix
+lnk health
+lnk status --validate
+lnk memory-audit
+lnk compliance-export --output link-audit.json
+lnk team-sync ~/link
+lnk snapshot ~/link --output link-snapshot
+lnk operations
+lnk benchmark "agent memory"
+lnk rebuild-index
+lnk rebuild-backlinks
+lnk validate
+lnk verify-mcp
+

Use lnk backup before broad repair work. Use lnk benchmark when a wiki starts to feel slow. lnk status --validate and lnk benchmark both show persistent-cache reuse so you can tell whether Link is rereading every page or reusing unchanged records.

+

Use lnk team-sync before sharing Link through Git. It is read-only: it checks Git state, verifies that raw/ is protected, checks whether review or visibility: private memories would make sharing unsafe, and prints paste-safe setup/sync commands without pushing private source material for you.

+

Use lnk snapshot when you need a static, read-only HTML export for a teammate, maintainer, security reviewer, or demo. It excludes raw/, captures, local operation state, and memory pages by default. With --include-memories, it still excludes visibility: private memories unless --include-private-memories is explicitly passed.

+

Use lnk connect <agent> when an agent already has Link instructions but still needs MCP wiring. It previews the config before writing.

+
lnk connect codex ~/link
+lnk connect codex ~/link --write
+lnk connect kiro ~/link --write
+lnk verify-mcp ~/link
+

From a source checkout, use the synthetic large-wiki smoke when you want local scale evidence without touching your real wiki. The script prints the exact lnk serve command and graph URL for the generated fixture.

python3 scripts/smoke_large_wiki.py --pages 10000

All Commands

-
link --version
-link version
-link init [dir]
-link serve [dir] [--port 3000]
-link try [dir] [--force] [--serve] [--port 3000]
-link welcome [dir] [--project slug]
-link prompts [dir] [--project slug]
-link next [dir] [--project slug]
-link health [dir] [--json]
-link status [--validate]
-link operations [--limit 20]
-link backup [--label name] [--include-raw]
-link compliance-export [dir] [--output audit.json] [--project slug]
-link team-sync [dir] [--remote git-url]
-link share <page-or-memory> [dir] [--port 3000]
-link snapshot [dir] [--output link-snapshot] [--include-memories] [--include-private-memories] [--force]
-link ingest-status
-link import-obsidian <vault> [dir] [--dry-run] [--overwrite]
-link remember "text" [--project slug] [--visibility private|project|team] [--review-after YYYY-MM-DD] [--expires-at YYYY-MM-DD]
-link propose-memories <file-or-text> [--project slug]
-link capture-session <file-or-text> [--project slug]
-link capture-inbox [--project slug]
-link accept-capture <capture> [--index N] [--visibility private|project|team]
-link redact-capture <capture>
-link delete-capture <capture> --confirm
-link query "task" [--budget small|medium|large] [--project slug]
-link graph-summary ["topic"] [--limit 40] [--depth 1]
-link benchmark ["query"] [--budget small|medium|large] [--project slug]
-link brief "task" [--project slug]
-link memory-audit [--project slug]
-link recall "query" [--project slug]
-link profile [--project slug]
-link wins [--project slug]
-link memory-inbox [--project slug]
-link memory-log [--limit N]
-link review-memory <name>
-link explain-memory <name>
-link update-memory <name> "text" [--project slug]
-link set-memory-visibility <name> private|project|team
-link archive-memory <name>
-link restore-memory <name>
-link forget-memory <name> --confirm
-link doctor
-link doctor --fix
-link migrate
-link validate [--strict]
-link rebuild-index
-link rebuild-backlinks
-link verify-mcp [--json]
-link connect <agent> [dir] [--write] [--config path] [--python python]
-link backup [--list] [--include-raw]
-link restore-backup <backup.tar.gz> [--include-raw] --confirm
+        
lnk --version
+lnk version
+lnk init [dir]
+lnk serve [dir] [--port 3000]
+lnk try [dir] [--force] [--serve] [--port 3000]
+lnk welcome [dir] [--project slug]
+lnk prompts [dir] [--project slug]
+lnk next [dir] [--project slug]
+lnk health [dir] [--json]
+lnk status [--validate]
+lnk operations [--limit 20]
+lnk backup [--label name] [--include-raw]
+lnk compliance-export [dir] [--output audit.json] [--project slug]
+lnk team-sync [dir] [--remote git-url]
+lnk share <page-or-memory> [dir] [--port 3000]
+lnk snapshot [dir] [--output link-snapshot] [--include-memories] [--include-private-memories] [--force]
+lnk ingest-status
+lnk import-obsidian <vault> [dir] [--dry-run] [--overwrite]
+lnk remember "text" [--project slug] [--visibility private|project|team] [--review-after YYYY-MM-DD] [--expires-at YYYY-MM-DD]
+lnk propose-memories <file-or-text> [--project slug]
+lnk capture-session <file-or-text> [--project slug]
+lnk capture-inbox [--project slug]
+lnk accept-capture <capture> [--index N] [--visibility private|project|team]
+lnk redact-capture <capture>
+lnk delete-capture <capture> --confirm
+lnk query "task" [--budget small|medium|large] [--project slug]
+lnk graph-summary ["topic"] [--limit 40] [--depth 1]
+lnk benchmark ["query"] [--budget small|medium|large] [--project slug]
+lnk brief "task" [--project slug]
+lnk memory-audit [--project slug]
+lnk recall "query" [--project slug]
+lnk profile [--project slug]
+lnk wins [--project slug]
+lnk memory-inbox [--project slug]
+lnk memory-log [--limit N]
+lnk review-memory <name>
+lnk explain-memory <name>
+lnk update-memory <name> "text" [--project slug]
+lnk set-memory-visibility <name> private|project|team
+lnk archive-memory <name>
+lnk restore-memory <name>
+lnk forget-memory <name> --confirm
+lnk doctor
+lnk doctor --fix
+lnk migrate
+lnk validate [--strict]
+lnk rebuild-index
+lnk rebuild-backlinks
+lnk verify-mcp [--json]
+lnk connect <agent> [dir] [--write] [--config path] [--python python]
+lnk backup [--list] [--include-raw]
+lnk restore-backup <backup.tar.gz> [--include-raw] --confirm
 python3 link.py demo
 python3 link.py query-link "task" [dir]
-

query-link is kept as an internal/backward-compatible alias. Prefer link query in user-facing docs.

+

query-link is kept as an internal/backward-compatible alias. Prefer lnk query in user-facing docs.

diff --git a/docs/concepts.html b/docs/concepts.html index f388dce..65c4a3d 100644 --- a/docs/concepts.html +++ b/docs/concepts.html @@ -113,11 +113,11 @@

Graph Context

Scale Model

Link uses local caching, token indexes, bounded page APIs, bounded graph summaries, and optional in-memory SQLite FTS5 for fast search. If SQLite is unavailable, Link falls back to the token index.

-
link benchmark "agent memory"
-link graph-summary "local memory" --limit 40 --depth 1
-link validate
-link rebuild-backlinks
-

Use link benchmark when a wiki starts to feel slow. It reports cache time, persistent-cache reuse, search, query, graph payloads, backend, and readiness recommendations.

+
lnk benchmark "agent memory"
+lnk graph-summary "local memory" --limit 40 --depth 1
+lnk validate
+lnk rebuild-backlinks
+

Use lnk benchmark when a wiki starts to feel slow. It reports cache time, persistent-cache reuse, search, query, graph payloads, backend, and readiness recommendations.

diff --git a/docs/getting-started.html b/docs/getting-started.html index 436ac37..58f7d45 100644 --- a/docs/getting-started.html +++ b/docs/getting-started.html @@ -58,8 +58,8 @@

1. Run The Demo

The demo is the fastest proof of value. It already has raw sources, wiki pages, memories, backlinks, and graph data.

macOS with Homebrew:

brew install gowtham0992/link/link
-link try
-link serve link-demo
+lnk try +lnk serve link-demo

The Homebrew formula is maintained in the public gowtham0992/homebrew-link tap.

Or from source:

git clone https://github.com/gowtham0992/link.git
@@ -73,21 +73,21 @@ 

1. Run The Demo

py link.py demo py link.py next link-demo py link.py serve link-demo
-

Use link try for the shortest Homebrew proof loop. It creates the demo, checks readiness, runs a compact query and brief, and prints the viewer command plus the first agent prompts. From source, use python3 link.py try or py link.py try.

+

Use lnk try for the shortest Homebrew proof loop. It creates the demo, checks readiness, runs a compact query and brief, and prints the viewer command plus the first agent prompts. From source, use python3 link.py try or py link.py try.

Judge the generated demo The repo's root wiki/ is only a scaffold for local development and personal testing. Generated content in wiki/, raw/, and link-demo/ is ignored by git so private memory is not published by accident.
Viewer is optional - link serve is only for browsing Link in a local web UI. CLI commands and MCP-enabled agents work without it because they read the same local wiki/ files directly. + lnk serve is only for browsing Link in a local web UI. CLI commands and MCP-enabled agents work without it because they read the same local wiki/ files directly.
-

The demo includes one pending memory intentionally, so the review inbox and explain-memory workflow are visible. Run link review-memory prefer-local-personal-memory link-demo if you want memory audit to be fully clear.

+

The demo includes one pending memory intentionally, so the review inbox and explain-memory workflow are visible. Run lnk review-memory prefer-local-personal-memory link-demo if you want memory audit to be fully clear.

Open http://127.0.0.1:3000, then inspect /brief, /memory, /ingest, /graph, and /health. Open more for prompts, proposal review, audit, captures, profile, log, and all pages. Link accepts localhost too, but the numeric loopback address avoids slow IPv6 fallback in some Safari setups.

-
link query "why does Link help agents?" link-demo --budget small
-link brief "working on agent memory" link-demo
-link benchmark "agent memory" link-demo
-link health link-demo
+
lnk query "why does Link help agents?" link-demo --budget small
+lnk brief "working on agent memory" link-demo
+lnk benchmark "agent memory" link-demo
+lnk health link-demo

2. Install Link For Your Agent

From the cloned checkout, run the installer for the agent you use. Re-running the same installer updates code and instructions without replacing existing wiki data.

@@ -124,14 +124,14 @@

3. Add One Source

Raw notes stay local. The agent turns them into source-cited wiki pages. EOF

Check pending work:

-
link ingest-status
+
lnk ingest-status
Safety gate Link blocks normal ingest guidance when raw files contain secret-looking values or cannot be read safely. Redact or fix those local files first.
Existing Link data - If you already have files in ~/link/raw/, link ingest-status may point to a different pending file first. If first-memory.md was already ingested and you overwrite it, Link marks that raw file as stale and asks the agent to refresh the existing source page. + If you already have files in ~/link/raw/, lnk ingest-status may point to a different pending file first. If first-memory.md was already ingested and you overwrite it, Link marks that raw file as stale and asks the agent to refresh the existing source page.

4. Save One Direct Memory

@@ -140,11 +140,11 @@

4. Save One Direct Memory

brief me from Link before we continue what does Link remember about local personal memory?

Or use the CLI:

-
link remember "I am testing Link as local personal memory for agents." --type preference --scope user --tags onboarding
-link brief "local personal memory"
-link recall "local personal memory"
-link profile
-link memory-audit
+
lnk remember "I am testing Link as local personal memory for agents." --type preference --scope user --tags onboarding
+lnk brief "local personal memory"
+lnk recall "local personal memory"
+lnk profile
+lnk memory-audit

5. Ask The Agent To Ingest

In your agent chat, ask:

@@ -153,14 +153,14 @@

5. Ask The Agent To Ingest

Return to /ingest after the agent finishes. Link shows which raw files are represented and gives follow-up prompts for proposals or retrieval checks.

6. Verify The Loop

-
link doctor --fix
-link health
-link ingest-status
-link validate
-link memory-audit
-link operations
-link verify-mcp
-

link verify-mcp should report Result: ready. Then ask your MCP-enabled agent:

+
lnk doctor --fix
+lnk health
+lnk ingest-status
+lnk validate
+lnk memory-audit
+lnk operations
+lnk verify-mcp
+

lnk verify-mcp should report Result: ready. Then ask your MCP-enabled agent:

query Link for first Link memory

If the answer comes from Link, local agent memory is working.

diff --git a/docs/index.html b/docs/index.html index 64b0cc2..0d45351 100644 --- a/docs/index.html +++ b/docs/index.html @@ -130,9 +130,9 @@

Run a finished memory wiki locally.

# macOS
 brew install gowtham0992/link/link
-link demo
-link next link-demo
-link serve link-demo
+lnk demo
+lnk next link-demo
+lnk serve link-demo
 
 # or from source
 git clone https://github.com/gowtham0992/link.git
@@ -142,9 +142,9 @@ 

Run a finished memory wiki locally.

python3 link.py serve link-demo # then try -link query "why does Link help agents?" link-demo --budget small -link brief "working on agent memory" link-demo -link benchmark "agent memory" link-demo
+lnk query "why does Link help agents?" link-demo --budget small +lnk brief "working on agent memory" link-demo +lnk benchmark "agent memory" link-demo @@ -155,7 +155,7 @@

Review it, script it, or let an agent call it.

The web UI, CLI, and MCP server all operate on the same local Markdown wiki. Read it like a local document, script it from a terminal, or let an agent query the same memory through MCP.

No background web server required - link serve only starts the human web viewer. The CLI and MCP server read the same local files directly, so agents can query Link when the viewer is closed. + lnk serve only starts the human web viewer. The CLI and MCP server read the same local files directly, so agents can query Link when the viewer is closed.
diff --git a/docs/mcp.html b/docs/mcp.html index 41f894e..cd7d637 100644 --- a/docs/mcp.html +++ b/docs/mcp.html @@ -83,14 +83,14 @@

Agent Installers

.\integrations\copilot\install.ps1 .\integrations\vscode\install.ps1 .\integrations\antigravity\install.ps1 -

If you already have agent instructions and only need the MCP config, use link connect. It previews the target file and config snippet first; add --write for an explicit update.

-
link connect codex ~/link
-link connect codex ~/link --write
-link connect kiro ~/link --write
-link connect claude-code ~/link --write
-link connect cursor ~/link --write
-link connect antigravity ~/link --write
-link verify-mcp ~/link
+

If you already have agent instructions and only need the MCP config, use lnk connect. It previews the target file and config snippet first; add --write for an explicit update.

+
lnk connect codex ~/link
+lnk connect codex ~/link --write
+lnk connect kiro ~/link --write
+lnk connect claude-code ~/link --write
+lnk connect cursor ~/link --write
+lnk connect antigravity ~/link --write
+lnk verify-mcp ~/link

MCP Only

python3 -m pip install --upgrade link-mcp
@@ -174,10 +174,10 @@ 

MCP Tools

Project-aware tools accept an optional project argument. When set, Link returns broad user/global memory plus memories for that project, while excluding memories from other explicit projects.

Verify Setup

-
link verify-mcp
-link health
-link next
-

link verify-mcp --json is useful when an agent or script should read structured issues and next actions.

+
lnk verify-mcp
+lnk health
+lnk next
+

lnk verify-mcp --json is useful when an agent or script should read structured issues and next actions.

Natural prompts to try

is Link ready?

diff --git a/docs/memory-contract.html b/docs/memory-contract.html index 23913cf..08e52f6 100644 --- a/docs/memory-contract.html +++ b/docs/memory-contract.html @@ -114,8 +114,8 @@

Sharing Semantics

visibilityprivate, project, teamControls sharing intent for Git sync and snapshots. -

By default, user/global memories are private and project memories are project-visible. link team-sync blocks ready status if private memories would be included in a broad Git share. link snapshot --include-memories still excludes private memories unless --include-private-memories is explicitly passed.

-

Use set_memory_visibility or link set-memory-visibility only after explicit user approval when an existing memory should move between private, project, and team sharing intent.

+

By default, user/global memories are private and project memories are project-visible. lnk team-sync blocks ready status if private memories would be included in a broad Git share. lnk snapshot --include-memories still excludes private memories unless --include-private-memories is explicitly passed.

+

Use set_memory_visibility or lnk set-memory-visibility only after explicit user approval when an existing memory should move between private, project, and team sharing intent.

Budgets

query_link supports small, medium, and large budgets. Each packet includes why an item was selected, estimated tokens, has_more flags, and follow-up actions. This keeps agents from reading a 500-page or 10,000-page wiki just to answer one task.

diff --git a/docs/obsidian.html b/docs/obsidian.html index dd27b80..2d5f960 100644 --- a/docs/obsidian.html +++ b/docs/obsidian.html @@ -53,10 +53,10 @@

Open Link in Obsidian when you want a richer notes UI.

Import An Existing Obsidian Vault

-

Use link import-obsidian when you already have project notes in Obsidian and want Link agents to ingest them as source-backed knowledge. Link copies Markdown notes into raw/obsidian/<vault>/, skips Obsidian plugin state, and blocks notes with secret-looking values before writing them.

-
link init ~/link
-link import-obsidian ~/Documents/ObsidianVault ~/link
-link ingest-status ~/link
+

Use lnk import-obsidian when you already have project notes in Obsidian and want Link agents to ingest them as source-backed knowledge. Link copies Markdown notes into raw/obsidian/<vault>/, skips Obsidian plugin state, and blocks notes with secret-looking values before writing them.

+
lnk init ~/link
+lnk import-obsidian ~/Documents/ObsidianVault ~/link
+lnk ingest-status ~/link

The import does not silently create durable memories. It stages raw source notes, then the normal ingest and proposal workflow decides what becomes wiki knowledge or reviewed memory.

Open The Link Wiki

@@ -73,10 +73,10 @@

Edit Safely

Keep Link Indexes Current

After manual Obsidian edits, rebuild derived indexes and validate the wiki before asking agents to rely on the new content:

-
link rebuild-index ~/link
-link rebuild-backlinks ~/link
-link validate ~/link
-link health ~/link
+
lnk rebuild-index ~/link
+lnk rebuild-backlinks ~/link
+lnk validate ~/link
+lnk health ~/link

The local web viewer is still useful for Link-specific views like health, ingest, memory review, captures, and MCP-ready query context. Obsidian is an editor/viewer for the same underlying files, not a replacement for Link's validation and memory lifecycle.

diff --git a/docs/scale.html b/docs/scale.html index e582961..c8e233b 100644 --- a/docs/scale.html +++ b/docs/scale.html @@ -74,10 +74,10 @@

Bounded Surfaces

Measure Locally

-

Use link benchmark on your real wiki. It reports cache time, persistent-cache reuse, search backend, search/query timing, graph payload shape, and recommendations.

-
link benchmark "agent memory"
-link health
-link status --validate
+

Use lnk benchmark on your real wiki. It reports cache time, persistent-cache reuse, search backend, search/query timing, graph payload shape, and recommendations.

+
lnk benchmark "agent memory"
+lnk health
+lnk status --validate

From a source checkout, use a synthetic 10k-page check without touching your real wiki:

python3 scripts/smoke_large_wiki.py --pages 10000

The smoke script prints the generated wiki path, the local viewer command, and graph URLs so you can inspect browser behavior manually.

@@ -86,8 +86,8 @@

Large Wiki Habits

  • Prefer brief, query, and graph-summary over full exports.
  • Use page-type filters and search before opening full graph data.
  • -
  • Run link health after ingest or broad manual edits.
  • -
  • Run link doctor --fix only when health or validation points to a repairable issue.
  • +
  • Run lnk health after ingest or broad manual edits.
  • +
  • Run lnk doctor --fix only when health or validation points to a repairable issue.
  • Keep private raw sources out of Git; use snapshot, team-sync, and compliance-export for reviewable sharing paths.
diff --git a/docs/security.html b/docs/security.html index 784fa09..b459a2c 100644 --- a/docs/security.html +++ b/docs/security.html @@ -67,11 +67,11 @@

Privacy Model

Secret Handling

Link scans raw sources, captures, wiki pages, release files, and public artifacts for secret-looking values. It detects common API keys, provider tokens, JWTs, private key blocks, and registry credentials, warns without logging secret values, and refuses normal ingest guidance when raw safety cannot be established. Validation and doctor checks also fail if a secret-looking value is already present in a wiki page before the local UI or MCP tools can serve it as context.

-
link ingest-status
-link capture-inbox
-link redact-capture raw/memory-captures/<capture>.md
-link validate
-link doctor
+        
lnk ingest-status
+lnk capture-inbox
+lnk redact-capture raw/memory-captures/<capture>.md
+lnk validate
+lnk doctor
 python3 scripts/check_release_hygiene.py
Rule @@ -83,10 +83,10 @@

HTTP Boundary

HTTP write actions require X-Link-Local-Action: true. Responses include X-Link-API-Version. Proposal analysis does not write pages.

Backups

-

link backup and MCP backup_wiki write local .link-backups/ archives. Raw sources are excluded unless explicitly requested.

-
link backup
-link backup --include-raw
-link doctor --fix
+

lnk backup and MCP backup_wiki write local .link-backups/ archives. Raw sources are excluded unless explicitly requested.

+
lnk backup
+lnk backup --include-raw
+lnk doctor --fix

Run a backup before broad repair work or large generated changes.

Team Review

diff --git a/docs/team-security.html b/docs/team-security.html index 1be9acb..b83b9ea 100644 --- a/docs/team-security.html +++ b/docs/team-security.html @@ -59,12 +59,12 @@

Deployment Model

Link is designed as a local personal or repo-local memory layer. Each developer runs the CLI and MCP server on their own machine. The web viewer is optional and only serves the local UI.

No server dependency - link serve is only the human web viewer. CLI and MCP access work directly against local Markdown files when the viewer is closed. + lnk serve is only the human web viewer. CLI and MCP access work directly against local Markdown files when the viewer is closed.
brew install gowtham0992/link/link
-link init ~/link
-link health ~/link
-link connect codex ~/link
+lnk init ~/link +lnk health ~/link +lnk connect codex ~/link

Data Boundaries

    @@ -74,30 +74,30 @@

    Data Boundaries

  • The installed product has no telemetry, hosted backend, or outbound API calls.
  • Secret-looking values are scanned before capture, ingest, Obsidian import, and doctor checks.
-
link ingest-status ~/link
-link doctor ~/link
-link validate ~/link
+
lnk ingest-status ~/link
+lnk doctor ~/link
+lnk validate ~/link

Memory Approval Gates

Agents can propose memories, but durable memory should be explicit and reviewable. Link keeps memory as Markdown with type, scope, visibility, project, source, review status, optional review dates, and optional expiry dates.

-
link propose-memories raw/notes.md ~/link
-link memory-inbox ~/link
-link review-memory memory-name ~/link
-link archive-memory memory-name ~/link --reason stale
+
lnk propose-memories raw/notes.md ~/link
+lnk memory-inbox ~/link
+lnk review-memory memory-name ~/link
+lnk archive-memory memory-name ~/link --reason stale

For temporary context, use expires_at. For decisions that should be re-confirmed, use review_after. For team handoff, keep personal context at visibility: private and only mark memories project or team after the user explicitly approves sharing them.

Team Sharing Pattern

The safest early team workflow is Git-backed sharing of reviewed wiki pages. Keep raw sources local unless the team explicitly decides to share them.

-
link team-sync ~/link --remote git@example.com:team/link-memory.git
-link compliance-export ~/link --output link-audit.json
-link backup ~/link
-

link team-sync is read-only. It checks Git state, raw-source protection, review readiness, and whether active visibility: private memories would be swept into a broad git add wiki. It prints paste-safe commands instead of pushing data for you.

+
lnk team-sync ~/link --remote git@example.com:team/link-memory.git
+lnk compliance-export ~/link --output link-audit.json
+lnk backup ~/link
+

lnk team-sync is read-only. It checks Git state, raw-source protection, review readiness, and whether active visibility: private memories would be swept into a broad git add wiki. It prints paste-safe commands instead of pushing data for you.

Audit Packet

-

link compliance-export creates a redacted JSON packet for review. It includes readiness, validation status, memory review counts, operation markers, recent audit log metadata, and safe next actions. Raw source contents and memory bodies are excluded.

-
link compliance-export ~/link --output link-audit.json
-link wins ~/link
-link memory-log ~/link
+

lnk compliance-export creates a redacted JSON packet for review. It includes readiness, validation status, memory review counts, operation markers, recent audit log metadata, and safe next actions. Raw source contents and memory bodies are excluded.

+
lnk compliance-export ~/link --output link-audit.json
+lnk wins ~/link
+lnk memory-log ~/link

Current Limits

    @@ -109,11 +109,11 @@

    Current Limits

    Security Review Checklist

      -
    1. Run link health ~/link and verify readiness is green.
    2. -
    3. Run link doctor ~/link and resolve secret or validation warnings.
    4. -
    5. Run link compliance-export ~/link --output link-audit.json.
    6. +
    7. Run lnk health ~/link and verify readiness is green.
    8. +
    9. Run lnk doctor ~/link and resolve secret or validation warnings.
    10. +
    11. Run lnk compliance-export ~/link --output link-audit.json.
    12. Confirm raw/, backups, caches, and local MCP Python markers are ignored by Git.
    13. -
    14. Review wiki/log.md, link memory-log ~/link, and link wins ~/link.
    15. +
    16. Review wiki/log.md, lnk memory-log ~/link, and lnk wins ~/link.
    17. Only share reviewed wiki/ pages whose memories are marked visibility: project or visibility: team.
    diff --git a/docs/troubleshooting.html b/docs/troubleshooting.html index ad90975..9c28456 100644 --- a/docs/troubleshooting.html +++ b/docs/troubleshooting.html @@ -57,32 +57,32 @@

    Start with status, then repair deliberately.

    Is Link Ready?

    -
    link health
    -link status --validate
    -link doctor
    -link next
    +
    lnk health
    +lnk status --validate
    +lnk doctor
    +lnk next

    If link is not on your PATH, run from the source checkout with python3 link.py, or add ~/.local/bin to your shell path.

    MCP Is Not Visible

    -
    link verify-mcp
    +        
    lnk verify-mcp
     python3 -m pip index versions link-mcp

    Restart the MCP client after changing its config. If your installer printed a venv Python path, use that exact path in the MCP config.

    -

    You do not need link serve or serve.py running for MCP. The web viewer is separate from the MCP server.

    +

    You do not need lnk serve or serve.py running for MCP. The web viewer is separate from the MCP server.

    Ingest Is Blocked

    -
    link ingest-status
    +
    lnk ingest-status

    Blocked ingest usually means a raw source has secret-looking values, cannot be read safely, or source representation counts may be incomplete. Redact or fix the local file, then ask the agent to ingest again.

    Interrupted Writes

    -
    link operations
    -link validate
    -link doctor --fix
    +
    lnk operations
    +lnk validate
    +lnk doctor --fix

    If a memory write was interrupted, Link leaves a marker under wiki/.link-operations/. Inspect it before deleting anything manually, then validate the wiki and repair generated indexes if needed.

    Graph Is Stale

    -
    link rebuild-index
    -link rebuild-backlinks
    -link validate
    +
    lnk rebuild-index
    +lnk rebuild-backlinks
    +lnk validate

    Run this after manual Obsidian edits, hand-written wikilinks, or a failed ingest.

    Demo Looks Stale

    @@ -93,10 +93,10 @@

    Demo Looks Stale

    The current generated demo should include three raw sources, source-backed wiki pages, one starter memory, one exploration, current backlinks, and schema v1.

    The Wiki Feels Slow

    -
    link benchmark "agent memory"
    -link graph-summary "agent memory" --limit 40 --depth 1
    +
    lnk benchmark "agent memory"
    +lnk graph-summary "agent memory" --limit 40 --depth 1

    Large graph views intentionally open bounded first. Use type filters, node search, depth controls, and explicit all-data loading only when you need search or filtering across every page.

    -

    If link health or link benchmark shows low persistent-cache reuse after repeated runs, inspect recently edited pages or generated files that are changing on every request.

    +

    If lnk health or lnk benchmark shows low persistent-cache reuse after repeated runs, inspect recently edited pages or generated files that are changing on every request.

    From a source checkout, use the synthetic smoke script when you want local scale evidence without touching your real wiki:

    python3 scripts/smoke_large_wiki.py --pages 10000
    diff --git a/docs/ui.html b/docs/ui.html index 4baca4d..fa15e61 100644 --- a/docs/ui.html +++ b/docs/ui.html @@ -84,10 +84,10 @@

    Graph At Scale

    Health Page

    -

    Open /health when the wiki feels wrong. It gathers the same readiness signals as link status --validate, plus persistent-cache reuse, interrupted operation markers, and repair commands you can copy back into your terminal or agent chat.

    +

    Open /health when the wiki feels wrong. It gathers the same readiness signals as lnk status --validate, plus persistent-cache reuse, interrupted operation markers, and repair commands you can copy back into your terminal or agent chat.

    Start It

    -
    link serve
    +        
    lnk serve
     
     # from a source checkout
     python3 link.py serve link-demo
    diff --git a/integrations/README.md b/integrations/README.md index 09454cc..1e09c8b 100644 --- a/integrations/README.md +++ b/integrations/README.md @@ -49,7 +49,7 @@ query Link for what you know about this project 2. Scaffolds wiki structure at `~/link/` or the current directory with `--project`. 3. Installs or upgrades `link-mcp`, using `~/.link-mcp-venv` when system Python is externally managed. 4. Writes `.link-mcp-python` so clients can use the Python that actually has `link-mcp`. -5. Adds a short `link` command wrapper for global installs, so checks are short: `link health`. +5. Adds a short `lnk` command wrapper for global installs, so checks are short: `lnk health`. 6. Prints next prompts and verification commands for your install mode. The instruction file is intentionally small. It tells the agent to check diff --git a/integrations/_shared/instructions.ps1 b/integrations/_shared/instructions.ps1 index 3cbcd75..a33dc52 100644 --- a/integrations/_shared/instructions.ps1 +++ b/integrations/_shared/instructions.ps1 @@ -147,8 +147,8 @@ function Link-PrintNextSteps { Write-Host " ingest raw/ into Link" } else { Write-Host " Drop sources into ~/link/raw/." - Write-Host " View wiki: link serve" - Write-Host " Print starter prompts: link next" + Write-Host " View wiki: lnk serve" + Write-Host " Print starter prompts: lnk next" Write-Host " Try in your agent:" Write-Host " is Link ready?" Write-Host " brief me from Link before we continue" diff --git a/integrations/_shared/instructions.sh b/integrations/_shared/instructions.sh index 4ef4c7d..4c63f00 100644 --- a/integrations/_shared/instructions.sh +++ b/integrations/_shared/instructions.sh @@ -52,8 +52,8 @@ link_print_next_steps() { echo " ingest raw/ into Link" else echo " Drop sources into ~/link/raw/." - echo " View wiki: link serve" - echo " Print starter prompts: link next" + echo " View wiki: lnk serve" + echo " Print starter prompts: lnk next" echo " Try in your agent:" echo " is Link ready?" echo " brief me from Link before we continue" diff --git a/integrations/_shared/link-instructions.md b/integrations/_shared/link-instructions.md index b86e837..e90a856 100644 --- a/integrations/_shared/link-instructions.md +++ b/integrations/_shared/link-instructions.md @@ -2,23 +2,23 @@ Local agent memory lives at `~/link/`. It has raw sources in `~/link/raw/`, compiled wiki pages in `~/link/wiki/`, and direct memories in `~/link/wiki/memories/`. -If you are unsure whether Link is ready, use MCP `link_status` when available, or run `link health`. +If you are unsure whether Link is ready, use MCP `link_status` when available, or run `lnk health`. -If the user asks what to try after installing Link, use MCP `starter_prompts` when available, or run `link next`. +If the user asks what to try after installing Link, use MCP `starter_prompts` when available, or run `lnk next`. -If status reports a missing or old schema marker, use MCP `migrate_wiki` when available, or run `link migrate`, before other writes. +If status reports a missing or old schema marker, use MCP `migrate_wiki` when available, or run `lnk migrate`, before other writes. -When the user asks to ingest or drops files into `raw/`, use MCP `ingest_status` when available, or run `link ingest-status`, then follow its guided plan before deciding what to process. If it reports `blocked_secrets` or secret warnings, do not read or ingest flagged raw files until the user redacts them. +When the user asks to ingest or drops files into `raw/`, use MCP `ingest_status` when available, or run `lnk ingest-status`, then follow its guided plan before deciding what to process. If it reports `blocked_secrets` or secret warnings, do not read or ingest flagged raw files until the user redacts them. -When answering a substantive question that may need local memory or wiki context, start with MCP `query_link` when available, or run `link query ""`. +When answering a substantive question that may need local memory or wiki context, start with MCP `query_link` when available, or run `lnk query ""`. -When you only need graph orientation, especially for a large wiki, prefer MCP `get_graph_summary` or `link graph-summary ""` before requesting the full graph. +When you only need graph orientation, especially for a large wiki, prefer MCP `get_graph_summary` or `lnk graph-summary ""` before requesting the full graph. -When starting personalized or project-specific work, prime yourself with Link first: use MCP `memory_brief` when available, or run `link brief ""`. +When starting personalized or project-specific work, prime yourself with Link first: use MCP `memory_brief` when available, or run `lnk brief ""`. -Before broad repairs or risky local wiki edits, create a local backup with MCP `backup_wiki` when available, or run `link backup`. Do not include `raw/` unless the user explicitly asks. +Before broad repairs or risky local wiki edits, create a local backup with MCP `backup_wiki` when available, or run `lnk backup`. Do not include `raw/` unless the user explicitly asks. -After ingesting raw sources or making substantial wiki edits, use MCP `rebuild_index`, `rebuild_backlinks`, and `validate_wiki` when available, or run `link rebuild-index`, `link rebuild-backlinks`, and `link validate`, before saying the wiki is updated. +After ingesting raw sources or making substantial wiki edits, use MCP `rebuild_index`, `rebuild_backlinks`, and `validate_wiki` when available, or run `lnk rebuild-index`, `lnk rebuild-backlinks`, and `lnk validate`, before saying the wiki is updated. When the user says **"remember"**, **"recall"**, **"ingest"**, **"query"**, **"lint"**, or **"research"**, read `~/link/LINK.md` for instructions and follow the protocol. Use terminal commands to access `~/link/` since it's outside the workspace. diff --git a/integrations/_shared/scaffold.ps1 b/integrations/_shared/scaffold.ps1 index 9ce64cf..66dc728 100644 --- a/integrations/_shared/scaffold.ps1 +++ b/integrations/_shared/scaffold.ps1 @@ -43,13 +43,22 @@ function Install-LinkCommandWrapper { } $cliDir = if ($env:LINK_CLI_DIR) { $env:LINK_CLI_DIR } else { Join-Path $HOME ".local\bin" } - $cmdPath = Join-Path $cliDir "link.cmd" - $psPath = Join-Path $cliDir "link.ps1" + $cmdPath = Join-Path $cliDir "lnk.cmd" + $psPath = Join-Path $cliDir "lnk.ps1" + $legacyCmdPath = Join-Path $cliDir "link.cmd" + $legacyPsPath = Join-Path $cliDir "link.ps1" $marker = "Link command wrapper" $linkPy = Join-Path $TargetDir "link.py" New-Item -ItemType Directory -Force -Path $cliDir | Out-Null + foreach ($legacyPath in @($legacyCmdPath, $legacyPsPath)) { + if ((Test-Path $legacyPath) -and (Select-String -Quiet -SimpleMatch $marker $legacyPath)) { + Remove-Item -Force $legacyPath + Write-Host " Removed old Link wrapper: $legacyPath" + } + } + if ((Test-Path $cmdPath) -and -not (Select-String -Quiet -SimpleMatch $marker $cmdPath)) { Write-Host " · $cmdPath already exists and is not a Link wrapper; not overwriting." Write-Host " Fallback: $BasePython `"$linkPy`" health" @@ -73,7 +82,7 @@ exit `$LASTEXITCODE Write-Host " ✓ Link command: $cmdPath" $pathParts = ($env:PATH -split [IO.Path]::PathSeparator) if ($pathParts -notcontains $cliDir) { - Write-Host " · Add $cliDir to PATH to run: link health" + Write-Host " · Add $cliDir to PATH to run: lnk health" } } @@ -220,8 +229,8 @@ if (Test-Path (Join-Path $TargetDir "link.py")) { Write-Host " py link.py verify-mcp" } else { Write-Host " Check Link readiness:" - Write-Host " link health" + Write-Host " lnk health" Write-Host " Verify MCP setup:" - Write-Host " link verify-mcp" + Write-Host " lnk verify-mcp" } } diff --git a/integrations/_shared/scaffold.sh b/integrations/_shared/scaffold.sh index aa3f9c0..1fc9eb3 100755 --- a/integrations/_shared/scaffold.sh +++ b/integrations/_shared/scaffold.sh @@ -31,11 +31,17 @@ install_link_cli_wrapper() { fi LINK_CLI_DIR="${LINK_CLI_DIR:-$HOME/.local/bin}" - LINK_CLI_BIN="$LINK_CLI_DIR/link" + LINK_CLI_BIN="$LINK_CLI_DIR/lnk" + LEGACY_LINK_CLI_BIN="$LINK_CLI_DIR/link" LINK_CLI_MARKER="# Link command wrapper" mkdir -p "$LINK_CLI_DIR" + if [ -e "$LEGACY_LINK_CLI_BIN" ] && grep -q "$LINK_CLI_MARKER" "$LEGACY_LINK_CLI_BIN" 2>/dev/null; then + rm -f "$LEGACY_LINK_CLI_BIN" + echo " Removed old Link wrapper: $LEGACY_LINK_CLI_BIN" + fi + if [ -e "$LINK_CLI_BIN" ] && ! grep -q "$LINK_CLI_MARKER" "$LINK_CLI_BIN" 2>/dev/null; then echo " · $LINK_CLI_BIN already exists and is not a Link wrapper; not overwriting." echo " Fallback: cd \"$TARGET_DIR\" && python3 link.py health" @@ -54,9 +60,9 @@ EOF echo " ✓ Link command: $LINK_CLI_BIN" - RESOLVED_LINK="$(command -v link 2>/dev/null || true)" + RESOLVED_LINK="$(command -v lnk 2>/dev/null || true)" if [ "$RESOLVED_LINK" != "$LINK_CLI_BIN" ]; then - echo " · Add $LINK_CLI_DIR to the front of PATH to run: link health" + echo " · Add $LINK_CLI_DIR to the front of PATH to run: lnk health" fi } @@ -230,18 +236,18 @@ if [ -f "$TARGET_DIR/link.py" ]; then echo " python3 link.py rebuild-backlinks" else echo " Check Link readiness:" - echo " link health" + echo " lnk health" echo " Print starter prompts:" - echo " link next" + echo " lnk next" echo " Check wiki health:" - echo " link doctor" + echo " lnk doctor" echo " Create a local backup:" - echo " link backup" + echo " lnk backup" echo " Validate ingest output:" - echo " link validate" + echo " lnk validate" echo " Verify MCP setup:" - echo " link verify-mcp" + echo " lnk verify-mcp" echo " Repair stale graph index:" - echo " link rebuild-backlinks" + echo " lnk rebuild-backlinks" fi fi diff --git a/mcp_package/link_core/benchmark.py b/mcp_package/link_core/benchmark.py index d456044..351532f 100644 --- a/mcp_package/link_core/benchmark.py +++ b/mcp_package/link_core/benchmark.py @@ -184,7 +184,7 @@ def benchmark_health(payload: Mapping[str, object]) -> dict[str, object]: if warnings: summary = "Review recommended before relying on this wiki for interactive agent work." recommendations = [ - "Run link doctor --fix and link benchmark again after repairing wiki/index state.", + "Run lnk doctor --fix and lnk benchmark again after repairing wiki/index state.", ] if large_token_fallback or "search" in slow_paths or "query" in slow_paths: recommendations.append("Use a Python build with sqlite3/FTS5 enabled for large local wikis.") diff --git a/mcp_package/link_core/ingest.py b/mcp_package/link_core/ingest.py index 7c7bc42..cc260fc 100644 --- a/mcp_package/link_core/ingest.py +++ b/mcp_package/link_core/ingest.py @@ -156,7 +156,7 @@ def _target_command(command: str, target: str) -> str: if target and target in command: return command parts = command.split() - if not parts or parts[0] != "link" or not target: + if not parts or parts[0] not in {"link", "lnk"} or not target: return command if target in parts[1:]: return display_command(parts) @@ -268,10 +268,10 @@ def build_ingest_plan(status: dict[str, object], limit: int = 5) -> dict[str, ob "agent_prompt": guidance.get("agent_prompt"), "memory_prompt": f"propose memories from {first['raw']}", "post_checks": [ - "link rebuild-index", - "link rebuild-backlinks", - "link validate", - "link health", + "lnk rebuild-index", + "lnk rebuild-backlinks", + "lnk validate", + "lnk health", ], } @@ -294,10 +294,10 @@ def build_ingest_plan(status: dict[str, object], limit: int = 5) -> dict[str, ob "agent_prompt": guidance.get("agent_prompt"), "memory_prompt": f"propose memories from {first['raw']}", "post_checks": [ - "link rebuild-index", - "link rebuild-backlinks", - "link validate", - "link health", + "lnk rebuild-index", + "lnk rebuild-backlinks", + "lnk validate", + "lnk health", ], } @@ -325,7 +325,7 @@ def build_ingest_plan(status: dict[str, object], limit: int = 5) -> dict[str, ob ], "agent_prompt": None, "memory_prompt": None, - "post_checks": ["link ingest-status", "link health"], + "post_checks": ["lnk ingest-status", "lnk health"], } if state == "blocked_raw_access": @@ -352,7 +352,7 @@ def build_ingest_plan(status: dict[str, object], limit: int = 5) -> dict[str, ob ], "agent_prompt": None, "memory_prompt": None, - "post_checks": ["link ingest-status", "link health"], + "post_checks": ["lnk ingest-status", "lnk health"], } if state == "blocked_source_access": @@ -378,7 +378,7 @@ def build_ingest_plan(status: dict[str, object], limit: int = 5) -> dict[str, ob ], "agent_prompt": None, "memory_prompt": None, - "post_checks": ["link ingest-status", "link validate", "link health"], + "post_checks": ["lnk ingest-status", "lnk validate", "lnk health"], } if state == "stale_graph": @@ -392,7 +392,7 @@ def build_ingest_plan(status: dict[str, object], limit: int = 5) -> dict[str, ob "Validate the wiki after rebuilding backlinks.", ], "agent_prompt": guidance.get("agent_prompt"), - "post_checks": ["link rebuild-backlinks", "link validate", "link health"], + "post_checks": ["lnk rebuild-backlinks", "lnk validate", "lnk health"], } if state == "empty": @@ -407,7 +407,7 @@ def build_ingest_plan(status: dict[str, object], limit: int = 5) -> dict[str, ob "Review generated pages before relying on them as memory.", ], "agent_prompt": None, - "post_checks": ["link ingest-status", "link health"], + "post_checks": ["lnk ingest-status", "lnk health"], } if state == "ready": @@ -421,7 +421,7 @@ def build_ingest_plan(status: dict[str, object], limit: int = 5) -> dict[str, ob "Add new files to raw/ when Link should learn new source-backed context.", ], "agent_prompt": None, - "post_checks": ["link doctor", "link health"], + "post_checks": ["lnk doctor", "lnk health"], } return { @@ -430,11 +430,11 @@ def build_ingest_plan(status: dict[str, object], limit: int = 5) -> dict[str, ob "summary": "Link needs its raw/ and wiki/ structure before ingest can start.", "batch": [], "steps": [ - "Run link init or rerun an installer.", + "Run lnk init or rerun an installer.", "Check readiness before adding sources.", ], "agent_prompt": None, - "post_checks": ["link init", "link health"], + "post_checks": ["lnk init", "lnk health"], } @@ -497,7 +497,7 @@ def render_ingest_status_text(target: str, status: dict[str, object]) -> str: if not status["has_wiki_dir"]: lines.append("Missing wiki/ directory") if not status["has_raw_dir"] or not status["has_wiki_dir"]: - lines.extend(["", "Next:", f" Run an installer or initialize this directory: {_target_command('link init', target)}"]) + lines.extend(["", "Next:", f" Run an installer or initialize this directory: {_target_command('lnk init', target)}"]) return "\n".join(lines) lines.append(f"Raw files: {status['raw_count']}") @@ -626,7 +626,7 @@ def build_ingest_guidance(status: dict[str, object]) -> dict[str, object]: "state": "missing_structure", "summary": "Link is not initialized here yet.", "agent_prompt": None, - "commands": ["link init", "link health"], + "commands": ["lnk init", "lnk health"], "notes": ["Run the installer or initialize this directory before ingesting sources."], } @@ -635,7 +635,7 @@ def build_ingest_guidance(status: dict[str, object]) -> dict[str, object]: "state": "blocked_source_access", "summary": f"{source_read_warning_count} source page could not be inspected. Fix source page access before ingest.", "agent_prompt": None, - "commands": ["link ingest-status", "link validate", "link health"], + "commands": ["lnk ingest-status", "lnk validate", "lnk health"], "notes": [ "Represented and pending raw counts may be incomplete while source pages cannot be read.", "Fix permissions or repair the page, then refresh ingest status.", @@ -652,7 +652,7 @@ def build_ingest_guidance(status: dict[str, object]) -> dict[str, object]: "state": "blocked_raw_access", "summary": summary + f" Fix access for {first} before ingest.", "agent_prompt": None, - "commands": ["link ingest-status", "link health"], + "commands": ["lnk ingest-status", "lnk health"], "notes": [ "Do not ask an agent to ingest raw files that Link cannot read and scan for secret-looking values.", "Fix permissions or replace the file, then refresh ingest status.", @@ -669,7 +669,7 @@ def build_ingest_guidance(status: dict[str, object]) -> dict[str, object]: "state": "blocked_secrets", "summary": summary + f" Redact {first} before ingest.", "agent_prompt": None, - "commands": ["link ingest-status", "link health"], + "commands": ["lnk ingest-status", "lnk health"], "notes": [ "Do not ask an agent to ingest flagged raw files until the secret-looking values are removed or redacted.", "After redaction, refresh ingest status and continue with the normal ingest prompt.", @@ -686,7 +686,7 @@ def build_ingest_guidance(status: dict[str, object]) -> dict[str, object]: "state": "stale_raw", "summary": summary, "agent_prompt": f"re-ingest {first} into Link", - "commands": ["link rebuild-index", "link rebuild-backlinks", "link validate", "link health"], + "commands": ["lnk rebuild-index", "lnk rebuild-backlinks", "lnk validate", "lnk health"], "notes": [ "The raw file is represented, but it is newer than the linked source page.", "Ask the agent to refresh the existing source page before relying on retrieval.", @@ -705,7 +705,7 @@ def build_ingest_guidance(status: dict[str, object]) -> dict[str, object]: "state": "pending_raw", "summary": summary, "agent_prompt": f"ingest {first} into Link", - "commands": ["link rebuild-index", "link rebuild-backlinks", "link validate", "link health"], + "commands": ["lnk rebuild-index", "lnk rebuild-backlinks", "lnk validate", "lnk health"], "notes": [ "If the source contains user preferences, decisions, or project context, ask for memory proposals before saving durable memories.", "After ingest, rebuild index/backlinks if your agent did not already do it.", @@ -717,7 +717,7 @@ def build_ingest_guidance(status: dict[str, object]) -> dict[str, object]: "state": "stale_graph", "summary": "Raw files are represented, but the graph index needs repair.", "agent_prompt": "rebuild Link backlinks and validate the wiki", - "commands": ["link rebuild-backlinks", "link validate", "link doctor"], + "commands": ["lnk rebuild-backlinks", "lnk validate", "lnk doctor"], "notes": ["Run the graph repair before relying on context or graph views."], } @@ -726,7 +726,7 @@ def build_ingest_guidance(status: dict[str, object]) -> dict[str, object]: "state": "empty", "summary": "Link is ready, but raw/ has no source files yet.", "agent_prompt": None, - "commands": ["link health", "link serve"], + "commands": ["lnk health", "lnk serve"], "notes": ["Drop notes, articles, transcripts, or project files into raw/, then ask your agent to ingest them into Link."], } @@ -734,7 +734,7 @@ def build_ingest_guidance(status: dict[str, object]) -> dict[str, object]: "state": "ready", "summary": "All raw files are represented in wiki/sources and the graph index is current.", "agent_prompt": None, - "commands": ["link doctor", "link health"], + "commands": ["lnk doctor", "lnk health"], "notes": ["Add new files to raw/ when you want Link to learn new source-backed knowledge."], } diff --git a/mcp_package/link_core/mcp_connect.py b/mcp_package/link_core/mcp_connect.py index 7b24bac..bf22456 100644 --- a/mcp_package/link_core/mcp_connect.py +++ b/mcp_package/link_core/mcp_connect.py @@ -8,7 +8,7 @@ from typing import Any, Mapping from .files import atomic_write_json, atomic_write_text -from .mcp_verify import display_command, resolve_mcp_python +from .mcp_verify import display_command, normalize_command_parts, resolve_mcp_python @dataclass(frozen=True) @@ -83,7 +83,7 @@ class AgentMcpConfig: def supported_agents() -> tuple[str, ...]: - """Return canonical agent names supported by `link connect`.""" + """Return canonical agent names supported by `lnk connect`.""" return tuple(config.name for config in AGENT_CONFIGS) @@ -93,7 +93,7 @@ def _agent_by_name(agent: str) -> AgentMcpConfig: if normalized == config.name or normalized in config.aliases: return config choices = ", ".join(supported_agents()) - raise ValueError(f"unsupported agent for link connect: {agent}. Try one of: {choices}") + raise ValueError(f"unsupported agent for lnk connect: {agent}. Try one of: {choices}") def _config_path(default_config: str, override: str | None) -> Path: @@ -196,7 +196,7 @@ def build_mcp_connect_payload( except Exception as exc: write_status = {"requested": True, "ok": False, "message": str(exc)} - connect_command = ["link", "connect", config.name, str(target)] + connect_command = ["lnk", "connect", config.name, str(target)] if config_path: connect_command.extend(["--config", str(path)]) if python_cmd: @@ -223,12 +223,12 @@ def build_mcp_connect_payload( }, { "label": "verify MCP runtime", - "command": ["link", "verify-mcp", str(target), "--python", resolved_python], - "command_text": display_command(["link", "verify-mcp", str(target), "--python", resolved_python]), + "command": ["lnk", "verify-mcp", str(target), "--python", resolved_python], + "command_text": display_command(["lnk", "verify-mcp", str(target), "--python", resolved_python]), }, { "label": "create wiki if missing", - "command": init_command, + "command": normalize_command_parts(init_command), "command_text": display_command(init_command), }, ], diff --git a/mcp_package/link_core/mcp_verify.py b/mcp_package/link_core/mcp_verify.py index dda4d74..0a6562c 100644 --- a/mcp_package/link_core/mcp_verify.py +++ b/mcp_package/link_core/mcp_verify.py @@ -9,14 +9,27 @@ from typing import Callable, Mapping +PREFERRED_LINK_COMMAND = "lnk" +LEGACY_LINK_COMMAND = "link" + + +def normalize_command_parts(parts: list[str]) -> list[str]: + """Use Link's non-conflicting CLI command name in generated user commands.""" + if parts and parts[0] == LEGACY_LINK_COMMAND: + return [PREFERRED_LINK_COMMAND, *parts[1:]] + return list(parts) + + def display_command(parts: list[str]) -> str: """Return a shell-safe command for the current platform.""" + parts = normalize_command_parts(parts) if os.name == "nt": return subprocess.list2cmdline(parts) return shlex.join(parts) def mcp_verify_action(tool: str, label: str, command: list[str]) -> dict[str, object]: + command = normalize_command_parts(command) return { "tool": tool, "label": label, diff --git a/mcp_package/link_core/memory_log.py b/mcp_package/link_core/memory_log.py index 12d1b3e..7cdcfa3 100644 --- a/mcp_package/link_core/memory_log.py +++ b/mcp_package/link_core/memory_log.py @@ -59,12 +59,12 @@ def memory_log_payload( "next_actions": [ { "label": "Inspect memory review queue", - "command": "link memory-inbox", + "command": "lnk memory-inbox", "reason": "Review pending, stale, expired, or underspecified memories before relying on them.", }, { "label": "Explain a changed memory", - "command": "link explain-memory ", + "command": "lnk explain-memory ", "reason": "Open provenance, review status, graph links, and matching log entries for one memory.", }, ], diff --git a/mcp_package/link_core/memory_wins.py b/mcp_package/link_core/memory_wins.py index 1ac135c..32cb52b 100644 --- a/mcp_package/link_core/memory_wins.py +++ b/mcp_package/link_core/memory_wins.py @@ -211,12 +211,12 @@ def _next_actions( { "label": "Create first memory", "reason": "A wins report becomes useful after Link has at least one durable memory.", - "command": f'link remember "I prefer ..." {target} --type preference --scope user', + "command": f'lnk remember "I prefer ..." {target} --type preference --scope user', }, { "label": "Propose from sources", "reason": "Use source-backed proposals when raw notes contain preferences or decisions.", - "command": f"link propose-memories raw/.md {target}", + "command": f"lnk propose-memories raw/.md {target}", }, ] if review_count: @@ -224,7 +224,7 @@ def _next_actions( { "label": "Review memory inbox", "reason": "Reviewed memory is safer to reuse across agents.", - "command": f"link memory-inbox {target}{project_arg}", + "command": f"lnk memory-inbox {target}{project_arg}", } ] if active_count: @@ -232,13 +232,13 @@ def _next_actions( { "label": "Use the memory", "reason": "Ask an agent for a brief before work to see the value loop.", - "command": f"link brief \"current task\" {target}{project_arg}", + "command": f"lnk brief \"current task\" {target}{project_arg}", } ] return [ { "label": "Restore or create memory", "reason": "No active memories are currently available for default recall.", - "command": f"link profile {target}{project_arg}", + "command": f"lnk profile {target}{project_arg}", } ] diff --git a/mcp_package/link_core/obsidian.py b/mcp_package/link_core/obsidian.py index 7011dcb..28783ea 100644 --- a/mcp_package/link_core/obsidian.py +++ b/mcp_package/link_core/obsidian.py @@ -101,9 +101,9 @@ def import_obsidian_vault( elif skipped_large: status = "partial" has_wiki_dir = (root / "wiki").is_dir() - next_commands = [f"link ingest-status {root}", f"link validate {root}", f"link health {root}"] + next_commands = [f"lnk ingest-status {root}", f"lnk validate {root}", f"lnk health {root}"] if not has_wiki_dir: - next_commands = [f"link init {root}", *next_commands] + next_commands = [f"lnk init {root}", *next_commands] return { "status": status, "target": str(root), diff --git a/mcp_package/link_core/share.py b/mcp_package/link_core/share.py index ce6c783..a5a8f32 100644 --- a/mcp_package/link_core/share.py +++ b/mcp_package/link_core/share.py @@ -95,7 +95,7 @@ def share_page_payload( summary = _page_summary(page) page_name = str(summary["name"]) root = wiki_dir.parent if wiki_dir.name == "wiki" else wiki_dir - serve_command = ["link", "serve", str(root), "--port", str(port)] + serve_command = ["lnk", "serve", str(root), "--port", str(port)] return { "found": True, "query": query, diff --git a/mcp_package/link_core/snapshot.py b/mcp_package/link_core/snapshot.py index 480765e..1af6254 100644 --- a/mcp_package/link_core/snapshot.py +++ b/mcp_package/link_core/snapshot.py @@ -283,7 +283,7 @@ def export_snapshot( if sensitive_values or sensitive_read_errors: return { "created": False, - "error": "wiki contains secret-looking values or unreadable files; run link doctor before exporting", + "error": "wiki contains secret-looking values or unreadable files; run lnk doctor before exporting", "output": str(output_dir), "sensitive_values": sensitive_values, "read_errors": sensitive_read_errors, @@ -389,7 +389,7 @@ def render_snapshot_text(payload: Mapping[str, object]) -> tuple[int, str]: lines.append("") lines.append("Unreadable files:") lines.extend(f"- {item}" for item in read_errors[:8]) - lines.extend(["", "Next:", " link doctor"]) + lines.extend(["", "Next:", " lnk doctor"]) return 1, "\n".join(lines) lines = [ diff --git a/mcp_package/link_core/team_sync.py b/mcp_package/link_core/team_sync.py index 7f2cab5..43df1fb 100644 --- a/mcp_package/link_core/team_sync.py +++ b/mcp_package/link_core/team_sync.py @@ -148,7 +148,7 @@ def build_team_sync_payload(target: Path, *, remote: str | None = None) -> dict[ warnings: list[str] = [] if not wiki_dir.exists(): - warnings.append("Link wiki is missing. Run link init before preparing team sync.") + warnings.append("Link wiki is missing. Run lnk init before preparing team sync.") if git_root and not bool(gitignore.get("protects_raw")): warnings.append("raw/ is not protected by the workspace .gitignore; do not push until raw sources are intentionally handled.") if git_root and not remotes and not remote_clean: @@ -160,10 +160,10 @@ def build_team_sync_payload(target: Path, *, remote: str | None = None) -> dict[ setup_actions: list[dict[str, str]] = [] sync_actions: list[dict[str, str]] = [ - _action("check Link health", ["link", "health", str(root)]), - _action("review pending memories", ["link", "memory-inbox", str(root)]), - _action("validate before sharing", ["link", "validate", str(root)]), - _action("backup before sharing", ["link", "backup", str(root)]), + _action("check Link health", ["lnk", "health", str(root)]), + _action("review pending memories", ["lnk", "memory-inbox", str(root)]), + _action("validate before sharing", ["lnk", "validate", str(root)]), + _action("backup before sharing", ["lnk", "backup", str(root)]), ] if git_root is None: setup_actions.extend([ diff --git a/mcp_package/link_core/web_health.py b/mcp_package/link_core/web_health.py index 1aa1e65..ec23fb5 100644 --- a/mcp_package/link_core/web_health.py +++ b/mcp_package/link_core/web_health.py @@ -193,7 +193,7 @@ def _render_health_cards(status: Mapping[str, object], operations: Mapping[str, validation_detail = ( f"{int(validation.get('error_count') or 0)} errors · {int(validation.get('warning_count') or 0)} warnings" if validation_checked - else "run link health" + else "run lnk health" ) cards = [ ( diff --git a/mcp_package/link_core/web_propose.py b/mcp_package/link_core/web_propose.py index 237cd87..93a3e34 100644 --- a/mcp_package/link_core/web_propose.py +++ b/mcp_package/link_core/web_propose.py @@ -58,7 +58,7 @@ def _render_proposal_path() -> str: 'remember that ...
    ' '
    4' '

    Review later

    Use the inbox and explain views to review, archive, update, or forget memories.

    ' - 'link memory-inbox
    ' + 'lnk memory-inbox' '' ) diff --git a/mcp_package/link_core/wiki.py b/mcp_package/link_core/wiki.py index f50e25e..69f5ecb 100644 --- a/mcp_package/link_core/wiki.py +++ b/mcp_package/link_core/wiki.py @@ -1040,7 +1040,7 @@ def rebuild_index( "next_actions": [ { "tool": "rebuild_backlinks", - "command": "link rebuild-backlinks", + "command": "lnk rebuild-backlinks", "reason": "Regenerated index links change graph edges; rebuild backlinks before validation.", } ], diff --git a/mcp_package/link_mcp/server.py b/mcp_package/link_mcp/server.py index 4d59ed9..5f9f46f 100644 --- a/mcp_package/link_mcp/server.py +++ b/mcp_package/link_mcp/server.py @@ -50,7 +50,7 @@ if not WIKI_DIR.exists(): print( f"[link-mcp] Wiki not found at {WIKI_DIR}. " - "Initialize Link first with `link init` or `python3 link.py init`, " + "Initialize Link first with `lnk init` or `python3 link.py init`, " "run an integration installer under integrations/*/install.sh, " "or pass --wiki /path/to/wiki.", file=sys.stderr, diff --git a/packaging/homebrew/Formula/link.rb b/packaging/homebrew/Formula/link.rb index 797e258..bc19dd4 100644 --- a/packaging/homebrew/Formula/link.rb +++ b/packaging/homebrew/Formula/link.rb @@ -21,7 +21,7 @@ def install (libexec/"mcp_package").mkpath (libexec/"mcp_package").install "mcp_package/link_core" - (bin/"link").write <<~SH + (bin/"lnk").write <<~SH #!/bin/sh exec "#{python3}" "#{libexec}/link.py" "$@" SH @@ -30,15 +30,15 @@ def install def caveats <<~EOS Try Link: - link demo - link serve link-demo + lnk demo + lnk serve link-demo Then open: http://127.0.0.1:3000 http://127.0.0.1:3000/graph To create a personal wiki: - link init ~/link + lnk init ~/link For MCP clients, install link-mcp with the agent installer or a venv: python3 -m venv ~/.link-mcp-venv @@ -47,9 +47,9 @@ def caveats end test do - system bin/"link", "--version" - system bin/"link", "demo", testpath/"link-demo", "--force" - system bin/"link", "validate", testpath/"link-demo" - system bin/"link", "status", "--validate", testpath/"link-demo" + system bin/"lnk", "--version" + system bin/"lnk", "demo", testpath/"link-demo", "--force" + system bin/"lnk", "validate", testpath/"link-demo" + system bin/"lnk", "status", "--validate", testpath/"link-demo" end end diff --git a/packaging/homebrew/README.md b/packaging/homebrew/README.md index 05ecb2e..eae3cef 100644 --- a/packaging/homebrew/README.md +++ b/packaging/homebrew/README.md @@ -32,8 +32,8 @@ Validate locally: brew audit --strict --online gowtham0992/link/link brew install --build-from-source gowtham0992/link/link brew test gowtham0992/link/link -link --version -link demo +lnk --version +lnk demo ``` Then push the tap repo: diff --git a/scripts/check_tool_contract.py b/scripts/check_tool_contract.py index 5afd355..a7a4064 100644 --- a/scripts/check_tool_contract.py +++ b/scripts/check_tool_contract.py @@ -179,8 +179,10 @@ def _missing_cli_reference(path: Path = ROOT / CLI_DOC_PATH) -> list[str]: missing: list[str] = [] for command in sorted(DOCS_CLI_COMMANDS): command_tokens = ( + f"`lnk {command}", f"`link {command}", f"`python3 link.py {command}", + f"lnk {command}", f"link {command}", f"python3 link.py {command}", ) diff --git a/tests/test_benchmark_core.py b/tests/test_benchmark_core.py index 40aecef..5284a50 100644 --- a/tests/test_benchmark_core.py +++ b/tests/test_benchmark_core.py @@ -81,7 +81,7 @@ def test_benchmark_health_warns_on_slow_paths(self): self.assertEqual(health["label"], "review") self.assertIn("search took 1.5000s", health["warnings"][0]) self.assertIn("Review recommended", health["summary"]) - self.assertIn("Run link doctor --fix", health["recommendations"][0]) + self.assertIn("Run lnk doctor --fix", health["recommendations"][0]) self.assertIn("sqlite3/FTS5", health["recommendations"][1]) def test_benchmark_health_warns_on_large_token_fallback(self): diff --git a/tests/test_cli_admin_core.py b/tests/test_cli_admin_core.py index 0507ab3..940453f 100644 --- a/tests/test_cli_admin_core.py +++ b/tests/test_cli_admin_core.py @@ -73,7 +73,7 @@ def test_render_status_not_ready(self): self.assertIn("Ready: no", text) self.assertIn("Missing: wiki/index.md", text) self.assertIn("migrate_wiki: migrate schema", text) - self.assertIn("Run: link migrate /tmp/link", text) + self.assertIn("Run: lnk migrate /tmp/link", text) def test_render_status_ready_includes_human_query_command(self): code, text = render_status_text({ @@ -94,7 +94,7 @@ def test_render_status_ready_includes_human_query_command(self): self.assertEqual(code, 0) self.assertIn("query_link: answer with compact local context", text) - self.assertIn("Run: link query", text) + self.assertIn("Run: lnk query", text) self.assertIn("what should I know before continuing?", text) self.assertIn("/tmp/link", text) diff --git a/tests/test_cli_memory_core.py b/tests/test_cli_memory_core.py index 1732aec..4776515 100644 --- a/tests/test_cli_memory_core.py +++ b/tests/test_cli_memory_core.py @@ -269,7 +269,7 @@ def test_render_memory_inbox(self): "kind": "review", "label": "Review", "description": "Mark memory reviewed", - "command": "link review-memory prefer-local-memory", + "command": "lnk review-memory prefer-local-memory", }, "actions": [ {"kind": "review", "label": "Review"}, @@ -463,7 +463,7 @@ def test_render_memory_audit_text(self): "next_actions": [{ "label": "Review memory inbox", "recommended": True, - "command": "link memory-inbox", + "command": "lnk memory-inbox", }], }, target="/tmp/link") diff --git a/tests/test_cli_query_core.py b/tests/test_cli_query_core.py index 9990dde..e6b3290 100644 --- a/tests/test_cli_query_core.py +++ b/tests/test_cli_query_core.py @@ -14,15 +14,15 @@ def test_render_query_not_found(self): self.assertIn("No Link context found for: missing", text) self.assertIn("Next:", text) self.assertIn("ingest the new raw Link files", text) - self.assertIn("Run: link ingest-status", text) - self.assertIn("Then rerun: link query missing", text) + self.assertIn("Run: lnk ingest-status", text) + self.assertIn("Then rerun: lnk query missing", text) def test_render_query_not_found_can_include_explicit_target(self): code, text = render_query_text({"found": False}, query_text="missing topic", command_target="/tmp/Link Demo") self.assertEqual(code, 0) - self.assertIn("Run: link ingest-status", text) - self.assertIn("Then rerun: link query", text) + self.assertIn("Run: lnk ingest-status", text) + self.assertIn("Then rerun: lnk query", text) self.assertIn("missing topic", text) self.assertIn("/tmp/Link Demo", text) diff --git a/tests/test_cli_runtime_core.py b/tests/test_cli_runtime_core.py index 0341a00..84ad040 100644 --- a/tests/test_cli_runtime_core.py +++ b/tests/test_cli_runtime_core.py @@ -17,28 +17,28 @@ def test_render_init_text(self): self.assertEqual(code, 0) self.assertIn("Link wiki ready at /tmp/link", text) self.assertIn("Initialized:", text) - self.assertIn("link health /tmp/link", text) - self.assertIn("link serve /tmp/link", text) + self.assertIn("lnk health /tmp/link", text) + self.assertIn("lnk serve /tmp/link", text) def test_render_starter_prompts_text(self): code, text = render_starter_prompts_text({ "target": "/tmp/link", "project": "link", - "shortcut": "link next /tmp/link", + "shortcut": "lnk next /tmp/link", "prompts": [{ "prompt": "is Link ready?", "when": "first run", }], - "commands": ["link health"], + "commands": ["lnk health"], }) self.assertEqual(code, 0) self.assertIn("Link starter prompts: /tmp/link", text) self.assertIn("Project: link", text) self.assertIn("Shortcut", text) - self.assertIn("- link next /tmp/link", text) + self.assertIn("- lnk next /tmp/link", text) self.assertIn("- is Link ready?", text) - self.assertIn("- link health", text) + self.assertIn("- lnk health", text) def test_render_welcome_text(self): code, text = render_welcome_text({ @@ -49,7 +49,7 @@ def test_render_welcome_text(self): "prompt": "is Link ready?", "proves": "Agent can find Link.", }], - "commands": ["link health"], + "commands": ["lnk health"], "urls": ["http://127.0.0.1:3000/health"], }) @@ -58,7 +58,7 @@ def test_render_welcome_text(self): self.assertIn("Project: link", text) self.assertIn("1. is Link ready?", text) self.assertIn("Proves: Agent can find Link.", text) - self.assertIn("- link health", text) + self.assertIn("- lnk health", text) self.assertIn("- http://127.0.0.1:3000/health", text) def test_render_demo_text(self): @@ -89,12 +89,12 @@ def test_render_try_text(self): search_backend="sqlite-fts", query_summary="agent-memory · 1 memories · 3 context items", brief_summary="1 relevant memories · 1 review items", - serve_command="link serve /tmp/link-demo", - next_command="link next /tmp/link-demo", - health_command="link health /tmp/link-demo", - query_command="link query 'why does Link help agents?' /tmp/link-demo --budget small", - brief_command="link brief 'working on agent memory' /tmp/link-demo", - benchmark_command="link benchmark 'agent memory' /tmp/link-demo", + serve_command="lnk serve /tmp/link-demo", + next_command="lnk next /tmp/link-demo", + health_command="lnk health /tmp/link-demo", + query_command="lnk query 'why does Link help agents?' /tmp/link-demo --budget small", + brief_command="lnk brief 'working on agent memory' /tmp/link-demo", + benchmark_command="lnk benchmark 'agent memory' /tmp/link-demo", url="http://127.0.0.1:3000", ) @@ -103,7 +103,7 @@ def test_render_try_text(self): self.assertIn("Demo: ready", text) self.assertIn("Query proof:", text) self.assertIn("Ask an agent:", text) - self.assertIn("link next /tmp/link-demo", text) + self.assertIn("lnk next /tmp/link-demo", text) def test_render_mcp_connect_text_preview(self): code, text = render_mcp_connect_text({ @@ -114,8 +114,8 @@ def test_render_mcp_connect_text_preview(self): "snippet": "[mcp_servers.link]\ncommand = \"/tmp/python\"", "write": {"requested": False, "ok": False, "message": "preview only"}, "next_actions": [ - {"label": "write config", "command_text": "link connect codex /tmp/link --write"}, - {"label": "verify MCP runtime", "command_text": "link verify-mcp /tmp/link --python /tmp/python"}, + {"label": "write config", "command_text": "lnk connect codex /tmp/link --write"}, + {"label": "verify MCP runtime", "command_text": "lnk verify-mcp /tmp/link --python /tmp/python"}, ], "restart_hint": "Restart the agent, then ask: is Link ready?", }) @@ -123,9 +123,9 @@ def test_render_mcp_connect_text_preview(self): self.assertEqual(code, 0) self.assertIn("Link connect: Codex", text) self.assertIn("Preview only", text) - self.assertIn("link connect codex /tmp/link --write", text) + self.assertIn("lnk connect codex /tmp/link --write", text) self.assertIn("[mcp_servers.link]", text) - self.assertIn("link verify-mcp /tmp/link --python /tmp/python", text) + self.assertIn("lnk verify-mcp /tmp/link --python /tmp/python", text) if __name__ == "__main__": diff --git a/tests/test_ingest_core.py b/tests/test_ingest_core.py index 21b9dad..04b4044 100644 --- a/tests/test_ingest_core.py +++ b/tests/test_ingest_core.py @@ -32,7 +32,7 @@ def test_collect_ingest_status_reports_missing_structure(self): self.assertFalse(payload["has_wiki_dir"]) self.assertEqual(payload["guidance"]["state"], "missing_structure") self.assertIn("Missing raw/ directory", text) - self.assertIn("Run an installer or initialize this directory: link init", text) + self.assertIn("Run an installer or initialize this directory: lnk init", text) def test_collect_ingest_status_reports_pending_raw(self): root = Path(tempfile.mkdtemp(prefix="link-ingest-core-")) @@ -56,16 +56,16 @@ def test_collect_ingest_status_reports_pending_raw(self): self.assertEqual(payload["plan"]["title"], "Ingest pending raw sources") self.assertEqual(payload["plan"]["batch"][0]["suggested_source_page"], "wiki/sources/new-note.md") self.assertEqual(payload["plan"]["memory_prompt"], "propose memories from raw/new-note.md") - self.assertTrue(any(command.startswith("link rebuild-index ") for command in payload["plan"]["post_checks"])) - self.assertIn(resolved_root, "\n".join(payload["plan"]["post_checks"])) + self.assertTrue(any(command.startswith("lnk rebuild-index ") for command in payload["plan"]["post_checks"])) text = render_ingest_status_text(str(root), payload) + self.assertIn(f"lnk rebuild-index {resolved_root}", text) self.assertIn(f"Link ingest status: {root}", text) self.assertIn("Raw files: 1", text) self.assertIn("Pending raw files:\n- raw/new-note.md", text) self.assertIn("Ask your agent: ingest raw/new-note.md into Link", text) - self.assertIn(f"Run: link rebuild-index {resolved_root}", text) - self.assertIn(f"- link health {resolved_root}", text) + self.assertIn(f"Run: lnk rebuild-index {resolved_root}", text) + self.assertIn(f"- lnk health {resolved_root}", text) self.assertIn("Suggested workflow: Ingest pending raw sources", text) self.assertIn("Memory review: propose memories from raw/new-note.md", text) diff --git a/tests/test_installers.py b/tests/test_installers.py index 1678505..06376ab 100644 --- a/tests/test_installers.py +++ b/tests/test_installers.py @@ -38,10 +38,12 @@ def test_scaffold_does_not_use_break_system_packages(self): def test_scaffold_installs_short_global_link_command(self): scaffold = (ROOT / "integrations/_shared/scaffold.sh").read_text(encoding="utf-8") - self.assertIn('LINK_CLI_BIN="$LINK_CLI_DIR/link"', scaffold) + self.assertIn('LINK_CLI_BIN="$LINK_CLI_DIR/lnk"', scaffold) + self.assertIn('LEGACY_LINK_CLI_BIN="$LINK_CLI_DIR/link"', scaffold) + self.assertIn("Removed old Link wrapper", scaffold) self.assertIn("Link command wrapper", scaffold) self.assertIn("not overwriting", scaffold) - self.assertIn("link health", scaffold) + self.assertIn("lnk health", scaffold) self.assertIn('if [ "$MODE" = "--project" ]', scaffold) def test_scaffold_project_mode_uses_absolute_target(self): @@ -56,7 +58,9 @@ def test_powershell_scaffold_uses_venv_and_short_link_command(self): self.assertNotIn("--break-system-packages", scaffold) self.assertIn(".link-mcp-venv", scaffold) self.assertIn(".link-mcp-python", scaffold) + self.assertIn("lnk.cmd", scaffold) self.assertIn("link.cmd", scaffold) + self.assertIn("Removed old Link wrapper", scaffold) self.assertIn("Link command wrapper", scaffold) self.assertIn("Get-Command py", scaffold) self.assertIn("-m venv", scaffold) @@ -80,9 +84,9 @@ def test_installers_print_mode_specific_next_steps(self): self.assertIn("link_print_next_steps()", instructions) self.assertIn('if [ "$mode" = "--project" ]; then', instructions) self.assertIn("View wiki: python3 link.py serve", instructions) - self.assertIn("View wiki: link serve", instructions) + self.assertIn("View wiki: lnk serve", instructions) self.assertIn("Print starter prompts: python3 link.py next", instructions) - self.assertIn("Print starter prompts: link next", instructions) + self.assertIn("Print starter prompts: lnk next", instructions) self.assertIn("Try in your agent:", instructions) self.assertIn("is Link ready?", instructions) self.assertIn("brief me from Link before we continue", instructions) diff --git a/tests/test_link_cli.py b/tests/test_link_cli.py index 2e4f1e0..e798d61 100644 --- a/tests/test_link_cli.py +++ b/tests/test_link_cli.py @@ -54,8 +54,8 @@ def test_init_creates_empty_wiki(self): backlinks = json.loads((target / "wiki/_backlinks.json").read_text(encoding="utf-8")) self.assertIn("backlinks", backlinks) self.assertIn("forward", backlinks) - self.assertIn("link health", out.getvalue()) - self.assertIn("link serve", out.getvalue()) + self.assertIn("lnk health", out.getvalue()) + self.assertIn("lnk serve", out.getvalue()) def test_init_preserves_existing_pages(self): tmp = Path(tempfile.mkdtemp(prefix="link-init-test-")) @@ -101,7 +101,7 @@ def test_prompts_prints_first_run_agent_prompts(self): self.assertIn("remember that I prefer local-first agent memory", out.getvalue()) self.assertIn("query Link for what you know about me", out.getvalue()) self.assertIn("propose memories from raw/", out.getvalue()) - self.assertIn("link health", out.getvalue()) + self.assertIn("lnk health", out.getvalue()) def test_prompts_json_supports_project_examples(self): tmp = Path(tempfile.mkdtemp(prefix="link-prompts-test-")) @@ -130,7 +130,7 @@ def test_welcome_prints_short_first_use_path(self): self.assertIn("Link welcome:", text) self.assertIn("1. is Link ready?", text) self.assertIn("Proves: Agent can find Link", text) - self.assertIn("link health", text) + self.assertIn("lnk health", text) self.assertIn("http://127.0.0.1:3000/health", text) def test_welcome_json_supports_project_examples(self): @@ -175,7 +175,7 @@ def test_serve_reports_missing_wiki(self): self.assertEqual(code, 1) self.assertIn("Link wiki missing", out.getvalue()) - self.assertIn("link init", out.getvalue()) + self.assertIn("lnk init", out.getvalue()) def test_serve_validates_port_before_spawning_viewer(self): tmp = Path(tempfile.mkdtemp(prefix="link-serve-test-")) @@ -373,12 +373,12 @@ def test_ingest_status_reports_pending_raw_file(self): self.assertIn("raw/new-source.md", out.getvalue()) self.assertIn("Guidance: 1 raw file needs ingest.", out.getvalue()) self.assertIn("Ask your agent: ingest raw/new-source.md into Link", out.getvalue()) - self.assertIn("Run: link validate", out.getvalue()) + self.assertIn("Run: lnk validate", out.getvalue()) self.assertIn("Suggested workflow: Ingest pending raw sources", out.getvalue()) self.assertIn("Memory review: propose memories from raw/new-source.md", out.getvalue()) self.assertIn("raw/new-source.md -> wiki/sources/new-source.md", out.getvalue()) self.assertIn("Post-ingest checks:", out.getvalue()) - self.assertIn("link health", out.getvalue()) + self.assertIn("lnk health", out.getvalue()) def test_ingest_status_reports_represented_completion(self): tmp = Path(tempfile.mkdtemp(prefix="link-ingest-test-")) @@ -530,7 +530,7 @@ def test_ingest_status_reports_stale_backlinks(self): self.assertEqual(code, 0) self.assertIn("Backlinks: stale", out.getvalue()) self.assertIn("Guidance: Raw files are represented, but the graph index needs repair.", out.getvalue()) - self.assertIn("Run: link rebuild-backlinks", out.getvalue()) + self.assertIn("Run: lnk rebuild-backlinks", out.getvalue()) def test_status_reports_demo_readiness(self): tmp = Path(tempfile.mkdtemp(prefix="link-status-test-")) @@ -629,7 +629,7 @@ def test_operations_reports_interrupted_write_markers(self): self.assertEqual(code, 1) self.assertIn("Link operations:", out.getvalue()) self.assertIn("remember | pending | stale", out.getvalue()) - self.assertIn("link validate", out.getvalue()) + self.assertIn("lnk validate", out.getvalue()) json_out = StringIO() with redirect_stdout(json_out): @@ -658,7 +658,7 @@ def test_health_reports_interrupted_write_markers(self): self.assertEqual(code, 1) self.assertIn("Ready: no", out.getvalue()) self.assertIn("Operations: 1 total", out.getvalue()) - self.assertIn("link operations", out.getvalue()) + self.assertIn("lnk operations", out.getvalue()) def test_status_prints_readiness_warnings(self): tmp = Path(tempfile.mkdtemp(prefix="link-status-test-")) diff --git a/tests/test_mcp_connect_core.py b/tests/test_mcp_connect_core.py index 6f644d5..5323ac4 100644 --- a/tests/test_mcp_connect_core.py +++ b/tests/test_mcp_connect_core.py @@ -23,7 +23,7 @@ def test_build_codex_preview_uses_marker_python(self): root = Path(temp) wiki = root / "wiki" wiki.mkdir() - (root / ".link-mcp-python").write_text("/tmp/link python/bin/python\n", encoding="utf-8") + (root / ".link-mcp-python").write_text("/tmp/Link Python/bin/python\n", encoding="utf-8") payload = build_mcp_connect_payload( target=root, @@ -35,7 +35,7 @@ def test_build_codex_preview_uses_marker_python(self): ) self.assertEqual(payload["agent"], "codex") - self.assertEqual(payload["python"], "/tmp/link python/bin/python") + self.assertEqual(payload["python"], "/tmp/Link Python/bin/python") self.assertIn("[mcp_servers.link]", str(payload["snippet"])) self.assertIn(str(wiki), str(payload["snippet"])) diff --git a/tests/test_mcp_contract.py b/tests/test_mcp_contract.py index 8e7179f..48cda1b 100644 --- a/tests/test_mcp_contract.py +++ b/tests/test_mcp_contract.py @@ -190,7 +190,7 @@ def test_link_operations_contract(self): self.assertEqual(payload["stale_count"], 1) self.assertEqual(payload["operations"][0]["operation"], "remember") self.assertEqual(payload["operations"][0]["description"], "Save memory") - self.assertIn("link operations", payload["next_actions"][0]["command"]) + self.assertIn("lnk operations", payload["next_actions"][0]["command"]) def test_starter_prompts_contract(self): payload = json.loads(self.server.starter_prompts(project="Client Launch")) @@ -198,7 +198,7 @@ def test_starter_prompts_contract(self): self.assertEqual(payload["project"], "client-launch") self.assertEqual(payload["prompts"][0]["prompt"], "is Link ready?") self.assertIn("this project uses Link", payload["prompts"][2]["prompt"]) - self.assertTrue(any(command.startswith("link health ") for command in payload["commands"])) + self.assertTrue(any(command.startswith("lnk health ") for command in payload["commands"])) def test_missing_wiki_message_points_to_current_setup_paths(self): previous_argv = sys.argv[:] @@ -216,7 +216,7 @@ def test_missing_wiki_message_points_to_current_setup_paths(self): self.assertEqual(cm.exception.code, 1) text = err.getvalue() self.assertIn("Wiki not found", text) - self.assertIn("link init", text) + self.assertIn("lnk init", text) self.assertIn("python3 link.py init", text) self.assertIn("integrations/*/install.sh", text) self.assertIn("--wiki /path/to/wiki", text) diff --git a/tests/test_operations_core.py b/tests/test_operations_core.py index 3705b4d..67fbfd0 100644 --- a/tests/test_operations_core.py +++ b/tests/test_operations_core.py @@ -84,7 +84,7 @@ def test_operation_report_renders_stale_marker_guidance(self): self.assertIn("remember | pending | stale", text) self.assertIn("Description: Save memory", text) self.assertIn("Touched: wiki/memories/prefer-local.md, wiki/log.md", text) - self.assertIn("link validate", text) + self.assertIn("lnk validate", text) self.assertIn(str(wiki.parent), text) self.assertIn("Result: needs attention", text) diff --git a/tests/test_prompts_core.py b/tests/test_prompts_core.py index 1310e66..940a2e9 100644 --- a/tests/test_prompts_core.py +++ b/tests/test_prompts_core.py @@ -28,8 +28,8 @@ def test_global_wiki_gets_personal_memory_prompts(self): self.assertIn("remember that I prefer local-first agent memory", prompts) self.assertIn("query Link for what you know about me", prompts) self.assertIn("propose memories from raw/", prompts) - self.assertTrue(str(payload["shortcut"]).startswith("link next ")) - self.assertTrue(any(command.startswith("link health ") for command in payload["commands"])) + self.assertTrue(str(payload["shortcut"]).startswith("lnk next ")) + self.assertTrue(any(command.startswith("lnk health ") for command in payload["commands"])) self.assertTrue(any(str(root) in command for command in payload["commands"])) def test_git_project_gets_project_memory_prompts(self): @@ -63,7 +63,7 @@ def test_welcome_payload_returns_short_proof_path(self): self.assertEqual(len(payload["steps"]), 3) self.assertEqual(payload["steps"][0]["prompt"], "is Link ready?") self.assertIn("Agent can find Link", payload["steps"][0]["proves"]) - self.assertTrue(any(command.startswith("link serve ") for command in payload["commands"])) + self.assertTrue(any(command.startswith("lnk serve ") for command in payload["commands"])) self.assertTrue(any(str(root) in command for command in payload["commands"])) self.assertIn("http://127.0.0.1:3000/health", payload["urls"]) diff --git a/tests/test_serve.py b/tests/test_serve.py index afee881..e6438be 100644 --- a/tests/test_serve.py +++ b/tests/test_serve.py @@ -298,7 +298,7 @@ def test_health_page_and_operations_api_show_interrupted_writes(self): self.assertEqual(page_status, 200) self.assertIn(b"Health", body) self.assertIn(b"Interrupted Operations", body) - self.assertIn(b"link operations", body) + self.assertIn(b"lnk operations", body) self.assertEqual(api_status, 200) self.assertEqual(payload["api_version"], serve.API_VERSION) self.assertEqual(payload["stale_count"], 1) @@ -548,7 +548,7 @@ def test_prompts_page_and_api_share_starter_prompts(self): self.assertIn("Ask Your Agent", html) self.assertIn("Local Checks", html) self.assertIn("Project examples are scoped to client-launch", html) - self.assertIn("link health", html) + self.assertIn("lnk health", html) def test_css_has_explicit_black_dark_theme(self): self.assertIn(':root[data-theme="dark"]', serve.CSS) @@ -1930,7 +1930,7 @@ def test_ingest_page_and_api_show_pending_raw(self): self.assertIn('data-copy-text="ingest raw/new-source.md into Link"', html) self.assertIn("Copy prompt", html) self.assertIn("Copy command", html) - self.assertIn('data-copy-text="link validate ', html) + self.assertIn('data-copy-text="lnk validate ', html) self.assertIn(str(wiki.parent), html) self.assertIn("ingest raw/new-source.md into Link", html) self.assertIn("open memory proposals first", html) diff --git a/tests/test_share_core.py b/tests/test_share_core.py index b56c2dc..888e93a 100644 --- a/tests/test_share_core.py +++ b/tests/test_share_core.py @@ -51,7 +51,7 @@ def test_share_resolves_exact_memory_title(self): self.assertEqual(payload["resolution"], "exact") self.assertEqual(payload["page"]["name"], "prefer-local-memory") self.assertEqual(payload["url"], "http://127.0.0.1:3456/page/prefer-local-memory") - self.assertIn("link serve", payload["serve_command_text"]) + self.assertIn("lnk serve", payload["serve_command_text"]) def test_share_resolves_path_alias_and_search(self): wiki = self.make_wiki() diff --git a/tests/test_tool_contract.py b/tests/test_tool_contract.py index 9914862..dea5ea6 100644 --- a/tests/test_tool_contract.py +++ b/tests/test_tool_contract.py @@ -33,7 +33,7 @@ def test_contract_reports_missing_mcp_docs(self): shutil.copy2(ROOT / "mcp_package/link_mcp/server.py", tmp / "mcp_package/link_mcp/server.py") (tmp / "docs").mkdir() - cli_reference = "\n".join(f"`link {command}`" for command in sorted(contract.DOCS_CLI_COMMANDS)) + cli_reference = "\n".join(f"`lnk {command}`" for command in sorted(contract.DOCS_CLI_COMMANDS)) mcp_reference = "\n".join( tool for tool in sorted(contract.EXPECTED_MCP_TOOLS) if tool != "query_link" ) diff --git a/tests/test_web_health_core.py b/tests/test_web_health_core.py index fc19e42..f865ac1 100644 --- a/tests/test_web_health_core.py +++ b/tests/test_web_health_core.py @@ -37,7 +37,7 @@ def test_render_health_page_shows_readiness_operations_and_commands(tmp_path): "next_actions": [ { "label": "inspect operation marker files before deleting them", - "command": f"link operations {tmp_path}", + "command": f"lnk operations {tmp_path}", } ], "operations": [{"operation": "remember", "description": "Save memory", "marker": "remember-1.json"}], @@ -60,8 +60,8 @@ def test_render_health_page_shows_readiness_operations_and_commands(tmp_path): assert "remember-1.json" in html assert "Operation Next Actions" in html assert str(tmp_path) in html - assert "link operations" in html - assert "link benchmark" in html + assert "lnk operations" in html + assert "lnk benchmark" in html assert "agent memory" in html @@ -93,7 +93,7 @@ def test_render_health_page_maps_ready_actions_to_targeted_commands(tmp_path): ) assert "Next Safe Action" in html - assert "link query" in html + assert "lnk query" in html assert "what should I know before continuing?" in html assert str(tmp_path) in html @@ -126,5 +126,5 @@ def test_render_health_page_targets_memory_review_command(tmp_path): ) assert "Review pending memories" in html - assert "link memory-inbox" in html + assert "lnk memory-inbox" in html assert str(tmp_path) in html diff --git a/tests/test_web_ingest_core.py b/tests/test_web_ingest_core.py index 72e8ca4..b21530c 100644 --- a/tests/test_web_ingest_core.py +++ b/tests/test_web_ingest_core.py @@ -17,9 +17,9 @@ def _page_href(name: str) -> str: def test_copy_button_escapes_text_and_label(): - html = copy_button('link ""', "") + html = copy_button('lnk ""', "") - assert 'data-copy-text="link "<raw>""' in html + assert 'data-copy-text="lnk "<raw>""' in html assert "<Copy>" in html assert "" not in html @@ -35,7 +35,7 @@ def test_render_ingest_page_shows_pending_workflow(): "state": "pending_raw", "summary": "1 raw file needs ingest.", "agent_prompt": "ingest raw/new-source.md into Link", - "commands": ["link validate"], + "commands": ["lnk validate"], "notes": ["After ingest, validate."], }, "safety": {"status": "clear", "summary": "No secret-looking values detected in raw sources.", "labels": []}, @@ -47,7 +47,7 @@ def test_render_ingest_page_shows_pending_workflow(): "memory_prompt": "propose memories from raw/new-source.md", "steps": ["Read each raw file."], "batch": [{"raw": "raw/new-source.md", "suggested_source_page": "wiki/sources/new-source.md"}], - "post_checks": ["link validate"], + "post_checks": ["lnk validate"], }, } @@ -62,7 +62,7 @@ def test_render_ingest_page_shows_pending_workflow(): assert 'Validatewaitgraph current' in html assert "Copy this into your agent chat" in html assert 'data-copy-text="ingest raw/new-source.md into Link"' in html - assert 'data-copy-text="link validate"' in html + assert 'data-copy-text="lnk validate"' in html assert "Ingest path" in html assert "Ingest pending raw sources" in html assert "wiki/sources/new-source.md" in html @@ -127,7 +127,7 @@ def test_render_ingest_page_targets_next_step_commands(): "guidance": { "state": "empty", "summary": "Link is ready, but raw/ has no source files yet.", - "commands": ["link ingest-status /tmp/link"], + "commands": ["lnk ingest-status /tmp/link"], }, "safety": {"status": "clear", "summary": "No warnings.", "labels": []}, "pending_raw": [], @@ -137,8 +137,8 @@ def test_render_ingest_page_targets_next_step_commands(): html = render_ingest_page(payload, page_href=_page_href, layout=_layout) - assert 'data-copy-text="link ingest-status /tmp/link"' in html - assert "link validate /tmp/link" in html + assert 'data-copy-text="lnk ingest-status /tmp/link"' in html + assert "lnk validate /tmp/link" in html def test_render_ingest_page_blocks_secret_raw_without_proposal_link(): diff --git a/tests/test_web_memory_core.py b/tests/test_web_memory_core.py index fc2c2c5..2c97bf2 100644 --- a/tests/test_web_memory_core.py +++ b/tests/test_web_memory_core.py @@ -32,7 +32,7 @@ def test_memory_card_escapes_content_and_renders_actions(self): "actions": [{ "label": "Review", "kind": "review", - "command": "link review-memory local-memory", + "command": "lnk review-memory local-memory", "arguments": {"identifier": "local-memory"}, }], } @@ -44,7 +44,7 @@ def test_memory_card_escapes_content_and_renders_actions(self): self.assertIn('/graph?focus=local-memory&depth=2', html) self.assertIn("Use <local> memory.", html) self.assertIn('data-memory-action="review"', html) - self.assertIn('data-copy-text="link review-memory local-memory"', html) + self.assertIn('data-copy-text="lnk review-memory local-memory"', html) self.assertNotIn("", html) def test_memory_card_escapes_generated_trust_links(self): @@ -68,7 +68,7 @@ def test_memory_section_uses_action_hints_when_record_has_no_actions(self): action_hints=lambda _record: [{ "label": "Archive", "kind": "archive", - "command": "link archive-memory agent-memory", + "command": "lnk archive-memory agent-memory", "arguments": {"identifier": "agent-memory"}, }], href="/inbox", @@ -76,7 +76,7 @@ def test_memory_section_uses_action_hints_when_record_has_no_actions(self): self.assertIn('view all', html) self.assertIn('data-memory-action="archive"', html) - self.assertIn("link archive-memory agent-memory", html) + self.assertIn("lnk archive-memory agent-memory", html) def test_capture_card_escapes_warnings_and_commands(self): html = render_capture_card({ @@ -108,14 +108,14 @@ def test_next_actions_render_commands(self): html = render_memory_next_actions([{ "label": "Review", "detail": "Open inbox.", - "command": "link memory-inbox", + "command": "lnk memory-inbox", "href": "/inbox", }]) self.assertIn('Review', html) self.assertIn("Open inbox.", html) - self.assertIn("link memory-inbox", html) - self.assertIn('data-copy-text="link memory-inbox"', html) + self.assertIn("lnk memory-inbox", html) + self.assertIn('data-copy-text="lnk memory-inbox"', html) def test_memory_dashboard_next_actions_cover_empty_ready_and_review_states(self): empty_actions = memory_dashboard_next_actions( diff --git a/tests/test_web_memory_pages_core.py b/tests/test_web_memory_pages_core.py index ab64a44..b2fcd71 100644 --- a/tests/test_web_memory_pages_core.py +++ b/tests/test_web_memory_pages_core.py @@ -86,7 +86,7 @@ def test_render_memory_dashboard_page_shows_counts_next_actions_and_sections(): "archived_count": 0, "by_type": {"preference": 2}, "by_scope": {"project": 1}, - "next_actions": [{"label": "Review", "detail": "Confirm memory", "command": "link memory-inbox"}], + "next_actions": [{"label": "Review", "detail": "Confirm memory", "command": "lnk memory-inbox"}], "review": [], "captures": [], "recent_updates": [], @@ -102,7 +102,7 @@ def test_render_memory_dashboard_page_shows_counts_next_actions_and_sections(): assert 'data-copy-text="brief me from Link for project alpha"' in html assert 'data-copy-text="audit Link memory for project alpha"' in html assert "Types: preference: 2" in html - assert "link memory-inbox" in html + assert "lnk memory-inbox" in html assert "No memories need review." in html @@ -243,7 +243,7 @@ def test_render_inbox_page_lists_review_items_and_actions(): "tldr": "Needs review.", "issues": [{"severity": "warning", "code": "pending", "message": "Needs "}], "primary_action": {"label": "Review", "description": "Confirm it"}, - "actions": [{"label": "Mark reviewed", "command": "link review-memory memory-one"}], + "actions": [{"label": "Mark reviewed", "command": "lnk review-memory memory-one"}], } ], } @@ -255,7 +255,7 @@ def test_render_inbox_page_lists_review_items_and_actions(): assert "Memory <One>" in html assert "Needs <review>" in html assert "/explain-memory?memory=memory-one" in html - assert "link review-memory memory-one" in html + assert "lnk review-memory memory-one" in html def test_render_memory_explanation_page_shows_trust_context_actions_and_body(): @@ -271,7 +271,7 @@ def test_render_memory_explanation_page_shows_trust_context_actions_and_body(): "issue_count": 1, "issues": [{"severity": "warning", "code": "pending", "message": "Needs "}], "primary_action": {"label": "Review", "description": "Confirm it"}, - "actions": [{"label": "Forget", "command": "link forget-memory prefer-reviewable-memory"}], + "actions": [{"label": "Forget", "command": "lnk forget-memory prefer-reviewable-memory"}], }, "provenance": { "source": "", @@ -290,7 +290,7 @@ def test_render_memory_explanation_page_shows_trust_context_actions_and_body(): assert "needs_review" in html assert "Needs <review>" in html assert "Next: Review" in html - assert "link forget-memory prefer-reviewable-memory" in html + assert "lnk forget-memory prefer-reviewable-memory" in html assert "/graph?focus=prefer-reviewable-memory&depth=2" in html assert "Open local graph" in html assert "agent-memory" in html @@ -325,7 +325,7 @@ def test_render_memory_wins_page_shows_local_proof_signals(): ], "prompts": ["what does Link remember about me?"], "next_actions": [ - {"label": "Use memory", "reason": "Try the value loop.", "command": "link brief current-task ."} + {"label": "Use memory", "reason": "Try the value loop.", "command": "lnk brief current-task ."} ], } @@ -336,4 +336,4 @@ def test_render_memory_wins_page_shows_local_proof_signals(): assert "Reusable <context>" in html assert "Alpha <memory>" in html assert 'data-copy-text="what does Link remember about me?"' in html - assert "link brief current-task ." in html + assert "lnk brief current-task ." in html diff --git a/tests/test_web_prompts_core.py b/tests/test_web_prompts_core.py index 827437b..4e7827c 100644 --- a/tests/test_web_prompts_core.py +++ b/tests/test_web_prompts_core.py @@ -15,9 +15,9 @@ def _layout(title: str, body: str) -> str: def test_render_prompts_page_shows_project_and_commands(): payload = { "project": "client-launch", - "shortcut": "link next /tmp/link", + "shortcut": "lnk next /tmp/link", "prompts": [{"label": "Readiness", "prompt": "is Link ready?", "when": "Before work"}], - "commands": ["link health"], + "commands": ["lnk health"], } html = render_prompts_page(payload, layout=_layout) @@ -26,22 +26,22 @@ def test_render_prompts_page_shows_project_and_commands(): assert "Project examples are scoped to client-launch" in html assert "One Command" in html assert "Use this any time you forget what to ask next." in html - assert "link next /tmp/link" in html - assert 'data-copy-text="link next /tmp/link"' in html + assert "lnk next /tmp/link" in html + assert 'data-copy-text="lnk next /tmp/link"' in html assert "Ask Your Agent" in html assert "Local Checks" in html assert "is Link ready?" in html assert 'data-copy-text="is Link ready?"' in html assert "Before work" in html - assert "link health" in html - assert 'data-copy-text="link health"' in html + assert "lnk health" in html + assert 'data-copy-text="lnk health"' in html def test_render_prompts_page_escapes_payload_fields(): payload = { "project": "", "prompts": [{"label": "