Transform Markdown files into interactive project navigation hubs with clickable file paths, automatic file creation, and intelligent navigation for code assistant workflows.
Transform Markdown documentation into an interactive project navigation system. This skill helps you understand, modify, and extend the Project.md VSCode extension — designed specifically to enhance workflows for code assistants like Claude Code.
Project.md makes file references in Markdown documents actionable:
```
project-md/
├── src/
│ ├── extension.ts # Main extension logic
│ └── test/
│ └── extension.test.ts # Test suite
├── dist/ # Compiled output (esbuild)
├── package.json # Extension manifest
└── tsconfig.json # TypeScript configuration
```
1. **Document Link Provider**: Converts file paths to clickable links
2. **Definition Provider**: Enables F12 "Go to Definition" on paths
3. **Command Handler**: Handles clicks with auto-creation logic
The extension uses three regex patterns to detect different reference styles:
**Pattern 1 - Markdown Links**: `[text](./path/file.md)`
**Pattern 2 - Bare References**: `./src/index.ts` or `Check ./config/app.ts`
**Pattern 3 - Inline Code**: `` `./src/utils.ts` ``
All patterns support `@` prefix (e.g., `@./config/settings.ts`) for future aliasing features.
When a user clicks a path:
1. Extension gets document's directory
2. Removes `@` prefix if present
3. Resolves relative path to absolute: `path.resolve(baseDir, rel)`
4. Checks if file/directory exists
5. Creates file if missing, or reveals directory, or opens existing file
Located in `openPathLike()` function in `src/extension.ts`:
```typescript
async function openPathLike(targetFsPath: string) {
const stat = await fs.promises.stat(targetFsPath).catch(() => undefined);
if (!stat) {
// Create parent directories + empty file
await fs.promises.mkdir(path.dirname(targetFsPath), { recursive: true });
await fs.promises.writeFile(targetFsPath, "", "utf8");
}
// Open in editor
const td = await vscode.workspace.openTextDocument(vscode.Uri.file(targetFsPath));
await vscode.window.showTextDocument(td);
}
```
**Why auto-create files?**
Optimizes workflow when documenting — especially useful when LLMs reference files that don't exist yet.
**Why separate Definition Provider?**
F12 only navigates to *existing* files (user expectation: "go to code"), while clicking creates new files (explicit intent).
**Why three regex patterns?**
Clarity and maintainability. Each pattern has single responsibility. Easy to debug and extend.
```bash
npm install
npm run watch # Start watch mode
```
Press F5 in VSCode to launch Extension Development Host.
1. **Add regex pattern** in `src/extension.ts`:
```typescript
const NEW_PATTERN_RE = /your-regex-here/g;
```
2. **Update `getRefRanges()`** to include new pattern:
```typescript
for (const m of doc.getText().matchAll(NEW_PATTERN_RE)) {
const rel = m[1]; // captured path
const startIdx = m.index! + (m[0].indexOf(rel));
// ... add to hits array with range intersection check
}
```
3. **Test thoroughly** with sample Markdown documents
Currently `@` is stripped without special resolution. To add workspace-root aliasing:
1. **Get workspace root**:
```typescript
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
```
2. **Update path resolution**:
```typescript
const abs = rel.startsWith('@')
? path.resolve(workspaceRoot, rel.slice(2))
: path.resolve(baseDir, rel);
```
3. **Add configuration setting** in `package.json`:
```json
"contributes": {
"configuration": {
"properties": {
"project-md.atPrefixAlias": {
"type": "string",
"default": "${workspaceFolder}",
"description": "Directory that @ prefix resolves to"
}
}
}
}
```
1. **Create diagnostic provider**:
```typescript
const diagnosticCollection = vscode.languages.createDiagnosticCollection('project-md');
function updateDiagnostics(doc: vscode.TextDocument) {
const diagnostics: vscode.Diagnostic[] = [];
for (const hit of getRefRanges(doc)) {
if (!fs.existsSync(hit.targetFsPath)) {
const diag = new vscode.Diagnostic(
hit.range,
`File not found: ${hit.targetFsPath}`,
vscode.DiagnosticSeverity.Warning
);
diagnostics.push(diag);
}
}
diagnosticCollection.set(doc.uri, diagnostics);
}
```
2. **Register on document change**:
```typescript
context.subscriptions.push(
vscode.workspace.onDidChangeTextDocument(e => updateDiagnostics(e.document))
);
```
1. **Define TextMate grammar** in `package.json`:
```json
"contributes": {
"grammars": [{
"scopeName": "markdown.projectmd.injection",
"path": "./syntaxes/projectmd.json",
"injectTo": ["text.html.markdown"]
}]
}
```
2. **Create `syntaxes/projectmd.json`**:
```json
{
"patterns": [{
"match": "(\\./|\\.\\./)([^\\s`\\)\\]]+)",
"name": "meta.path.projectmd"
}]
}
```
Located in `package.json`:
**Path traversal protection**: `path.resolve()` normalizes paths (prevents `../../../etc/passwd`)
**File creation safety**:
**Remaining risk**: User could click malicious path in untrusted Markdown (same risk as clicking any link)
**Document scanning**: O(n) where n = document length, runs synchronously
**Memory per document**: ~1-5KB for typical files (10-50 detected paths)
**Extension footprint**: <500KB bundled
**Optimization opportunities**:
1. **Path validation with diagnostics** (warnings for broken references)
2. **Quick fix code actions** ("Create file" action on validation warnings)
3. **Workspace-relative path support** (`${workspaceFolder}/...`)
4. **Configuration options** (toggle auto-create, customize @ alias)
5. **Claude Code integration** (auto-link claude.md references, validate @-references)
6. **Syntax highlighting** (visual differentiation for paths)
7. **Multi-root workspace support** (monorepo handling)
**Enable logging**:
```typescript
const outputChannel = vscode.window.createOutputChannel("Project.md");
outputChannel.appendLine(`Detected ${hits.length} paths`);
```
**Inspect tokens**: Run "Developer: Inspect Editor Tokens and Scopes" from Command Palette, hover over path
**Test regex patterns**: Use regex101.com with ECMAScript/JavaScript flavor
When modifying this extension:
1. **Test all three path patterns** with unit tests
2. **Verify error handling** on all async operations
3. **Avoid `any` types** without justification
4. **No blocking operations** in providers (keep UI responsive)
5. **Clear error messages** with context (include file paths)
6. **Run `npm run lint`** before committing
7. **Update CHANGELOG.md** for user-facing changes
Key APIs used:
Full VSCode Extension API: https://code.visualstudio.com/api/references/vscode-api
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/vscode-projectmd-enhancement/raw