---
name: gmd:vault-clip
description: Clip a web URL into the Obsidian vault as a curator-compatible inbox proposal (article / paper / repo / docs / discussion templates; AI-extracted tldr / tags / key_claims / entities; dedup via vault url: grep)
argument-hint: "<url> [--force] [--no-ai]"
allowed-tools:
  - Read
  - Write
  - Bash
  - WebFetch
---

<objective>
Direct user invocation surface for the vault-clip skill (Phase 31 / WEBCLIP-06). Implements the full WEBCLIP pipeline:

1. Component-gate check (`isEnabled('obsidian')`).
2. Vault-root resolution.
3. WebFetch the URL with single-retry-on-timeout.
4. validateFetchResult (extracts metadata + computes quality).
5. AI extraction (default-on; same-turn JSON authoring per the locked prompt in SKILL.md). `--no-ai` skips this step.
6. routeUrl → template selection + paywall flag.
7. Dedup grep via findExistingClip; `--force` overrides.
8. writeClip → atomic write under `<vaultRoot>/Knowledge/_inbox/<slug>.md`.

Researcher auto-invocation is **deferred to v1.5+ (WEBCLIP-future-01)** per critic-protocol §4 Approach A. v1.6-A and v1.7-A authors orchestrate explicit `/gmd:vault-clip <url>` calls in PLAN.md task lists.
</objective>

<execution_context>
@${CLAUDE_PLUGIN_ROOT}/skills/vault-clip/SKILL.md
</execution_context>

<process>
Follow the `vault-clip` skill end-to-end:

1. **Parse args.** Required positional: `<url>`. Optional flags: `--force` (overwrite on dedup hit), `--no-ai` (skip AI extraction; write with `ai_extract: 'skipped'`). If `<url>` is missing, exit with usage hint.

2. **Component gate.** Run `node -e "console.log(require('./bin/lib/config.js').isEnabled('obsidian'))"`. If output is `false`, print `vault disabled — enable components.obsidian.enabled in ~/.claude/get-magic-done/config.json` and exit 0 (graceful — not a failure; mirrors existing component-gate convention).

3. **Vault-root resolution.** Run `node -e "console.log(require('./bin/lib/vault-project.js').resolveVaultRoot(process.cwd()).path)"`. Capture as `$VAULT_ROOT`. On failure (typed error), surface the error and exit.

4. **WebFetch.** Use the WebFetch tool to retrieve the URL. On HTTP error, capture status + reason. On timeout (>30s), retry ONCE; persistent timeout → fetch_failed envelope.

5. **validateFetchResult.** Pipe the fetched markdown into `node -e "..."` calling `require('./skills/vault-clip/lib/clip.js').validateFetchResult(...)`. Surface fetch_failed envelope and exit if not ok.

6. **AI extraction (skip if --no-ai).** Apply the LOCKED prompt template from `skills/vault-clip/SKILL.md ## AI Extraction Prompt` section. Author strict JSON inline. Pipe through `validateExtractionJson`. On malformed → ONE retry. On persistent failure → set `ai_extract: 'failed'` flag, continue with no extracted fields. With `--no-ai` flag → set `ai_extract: 'skipped'` and continue with no extracted fields.

7. **routeUrl.** `node -e "console.log(JSON.stringify(require('./skills/vault-clip/lib/url-route.js').routeUrl('<url>')))"` → captures template + paywall.

8. **writeClip.** Final invocation. Pass all assembled args (vaultRoot, title, url, source, captured_at, status:ok|ai_extract_failed, template, quality, tldr, tags, key_claims, entities, clipped_by:'vault-clip', clipper_caller:'human-cli', paywall, ai_extract, source_body, force). Surface output envelope to the user.

9. **Print final envelope.** Format:

   ```
   status: {status}
   path:   {vault-relative-path or null}
   dedup:  {dedup_match path or none}
   timings: fetch={fetch_ms}ms extract={extract_ms}ms write={write_ms}ms total={total_ms}ms
   ```

   For the human invoker, prefer the path under `<vaultRoot>/Knowledge/_inbox/...` (full absolute path is more useful in a slash-command session than the vault-relative form).

The skill's lib (skills/vault-clip/lib/clip.js) handles the load-bearing primitives (validate / write / dedup / component-gate). This slash command is the orchestration glue.
</process>
