Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 18 additions & 72 deletions .claude/skills/magic-links/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,33 @@
# Magic Links System

This project uses a build-time magic link system (adapted from surfdeeper) for semantic document linking.
Wikipedia-style links using `@sailkit/atlas`.

## Syntax

### Standard Magic Links
```markdown
[Display Text](:document-id)
[Display Text](:document-id|:fallback-id)
```
[[context-collapse]] → [context-collapse](/concepts/context-collapse/)
[[context-collapse|Learn about context]] → [Learn about context](/concepts/context-collapse/)

Example:
```markdown
[Read about context collapse](:context-collapse)
```

### Learning Links (optional variant)
```markdown
[[document-id]]
[[document-id|Custom Display Text]]
[:context-collapse] → same, alternate syntax
[:context-collapse|Learn about context] → same with display text
```

These render as auto-numbered references.
## Configuration

## How It Works

1. Author writes `[text](:id)` in any markdown file
2. At build time, the Remark plugin scans all content collections
3. Builds a map of `id` → `slug` from frontmatter
4. Rewrites `:id` references to actual routes like `/concept/{slug}`

## Document Frontmatter
```javascript
// astro.config.mjs
import { remarkMagicLinks } from '@sailkit/atlas';

Each content file needs an `id` in frontmatter:

```yaml
---
title: Context Collapse
id: context-collapse # Used for magic link resolution
aliases: [context-loss] # Optional alternative IDs
---
remarkPlugins: [[remarkMagicLinks, {
urlBuilder: (id) => `/concepts/${id}/`,
syntax: 'both',
}]]
```

## Collections

Magic links resolve across these collections:
- `concepts` → `/concept/{slug}`
- `failure-modes` → `/failure-mode/{slug}`
- `patterns` → `/pattern/{slug}`

## Implementation Files

If implementing this system, you need:

1. **Remark plugin** (`scripts/remark-magic-links.mjs`):
- `buildIdMap()` - scans content directories, extracts id/slug/aliases
- Plugin function - transforms `:id` links to actual URLs

2. **Astro config** (`astro.config.mjs`):
```javascript
import remarkMagicLinks from "./scripts/remark-magic-links.mjs";

export default defineConfig({
markdown: {
remarkPlugins: [remarkMagicLinks],
},
});
```

3. **Validation script** (`scripts/validate-concept-ids.js`):
- Checks for duplicate IDs
- Validates all magic link references resolve
- Run with `npm run lint:links`

## When Adding New Content

1. Always include a unique `id` in frontmatter
2. Use kebab-case for IDs: `context-collapse`, not `contextCollapse`
3. Add `aliases` array if the concept has common alternative names
4. Run link validation before committing
## How It Works

## Error Handling
1. Write `[[some-page]]` in markdown
2. Remark plugin finds it in text nodes (not code blocks)
3. Transforms to `[some-page](/concepts/some-page/)` using `urlBuilder`

- Unresolved magic links are converted to plain text (no broken links)
- Build-time validation catches broken references
- Duplicate IDs cause validation failure
No frontmatter required. No validation. The ID is the slug.
12 changes: 12 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import { remarkMagicLinks } from 'sailkit/packages/atlas';
import { createSlugResolver } from './src/utils/slug-resolver.ts';

const urlBuilder = createSlugResolver('./src/content');

export default defineConfig({
integrations: [mdx()],
markdown: {
remarkPlugins: [
[remarkMagicLinks, {
urlBuilder,
syntax: 'both',
}],
],
},
});
Loading