mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 00:47:57 +00:00
chore: add docs:list helper
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
.bun-build
|
||||
*.bun-build
|
||||
bin/docs-list
|
||||
dist
|
||||
dist-ssr
|
||||
!packages/schema/dist
|
||||
|
||||
+4
-3
@@ -1,6 +1,8 @@
|
||||
---
|
||||
title: 'Docs'
|
||||
description: 'ClawdHub documentation index + reading order.'
|
||||
summary: 'Documentation index + reading order.'
|
||||
read_when:
|
||||
- New contributor onboarding
|
||||
- Looking for the right doc
|
||||
---
|
||||
|
||||
# Docs
|
||||
@@ -28,4 +30,3 @@ Feature/ops docs (already present):
|
||||
Docs tooling:
|
||||
|
||||
- `docs/mintlify.md`: publish these docs with Mintlify.
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
---
|
||||
title: 'Architecture'
|
||||
description: 'How the web app, Convex backend, and CLI interact.'
|
||||
summary: 'System overview: web app + Convex backend + CLI + shared schema.'
|
||||
read_when:
|
||||
- Orienting in codebase
|
||||
- Tracing a user flow across layers
|
||||
---
|
||||
|
||||
# Architecture
|
||||
@@ -58,4 +60,3 @@ description: 'How the web app, Convex backend, and CLI interact.'
|
||||
- Compute fingerprint; compare to registry state.
|
||||
- Optionally reports telemetry (see `docs/telemetry.md`).
|
||||
- Publishes new/changed skills (skips modified installed skills inside install root).
|
||||
|
||||
|
||||
+4
-3
@@ -1,6 +1,8 @@
|
||||
---
|
||||
title: 'Auth'
|
||||
description: 'GitHub OAuth (web) + API tokens (CLI).'
|
||||
summary: 'Auth overview: GitHub OAuth (web) + API tokens (CLI).'
|
||||
read_when:
|
||||
- Working on login/token flows
|
||||
- Debugging 401s
|
||||
---
|
||||
|
||||
# Auth
|
||||
@@ -50,4 +52,3 @@ Override:
|
||||
|
||||
- Tokens can be revoked in the web UI.
|
||||
- Revoked tokens return `401 Unauthorized` on CLI endpoints.
|
||||
|
||||
|
||||
+4
-3
@@ -1,6 +1,8 @@
|
||||
---
|
||||
title: 'CLI'
|
||||
description: 'Commands, flags, config files, and state on disk.'
|
||||
summary: 'CLI reference: commands, flags, config, lockfile, sync behavior.'
|
||||
read_when:
|
||||
- Working on CLI behavior
|
||||
- Debugging install/update/sync
|
||||
---
|
||||
|
||||
# CLI
|
||||
@@ -91,4 +93,3 @@ Telemetry:
|
||||
|
||||
- Sent during `sync` when logged in, unless `CLAWDHUB_DISABLE_TELEMETRY=1`.
|
||||
- Details: `docs/telemetry.md`.
|
||||
|
||||
|
||||
+4
-3
@@ -1,6 +1,8 @@
|
||||
---
|
||||
title: 'Deploy'
|
||||
description: 'Deploy web app + Convex backend, wire /api rewrites, set env.'
|
||||
summary: 'Deploy checklist: Convex backend + Vercel web app + /api rewrites.'
|
||||
read_when:
|
||||
- Shipping to production
|
||||
- Debugging /api routing
|
||||
---
|
||||
|
||||
# Deploy
|
||||
@@ -74,4 +76,3 @@ Then:
|
||||
clawdhub login --site https://<site>
|
||||
clawdhub whoami
|
||||
```
|
||||
|
||||
|
||||
+4
-3
@@ -1,6 +1,8 @@
|
||||
---
|
||||
title: 'HTTP API'
|
||||
description: 'Public endpoints + CLI endpoints (token auth).'
|
||||
summary: 'HTTP API reference (public + CLI endpoints + auth).'
|
||||
read_when:
|
||||
- Adding/changing endpoints
|
||||
- Debugging CLI ↔ registry requests
|
||||
---
|
||||
|
||||
# HTTP API
|
||||
@@ -119,4 +121,3 @@ Schema:
|
||||
```
|
||||
|
||||
If you self-host, serve this file (or set `CLAWDHUB_REGISTRY` explicitly).
|
||||
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
---
|
||||
summary: 'Copy/paste CLI smoke checklist for local verification.'
|
||||
read_when:
|
||||
- Pre-merge validation
|
||||
- Reproducing a reported CLI bug
|
||||
---
|
||||
|
||||
# Manual testing (CLI)
|
||||
|
||||
## Setup
|
||||
|
||||
+3
-3
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: 'Mintlify'
|
||||
description: 'Publish docs/ as a Mintlify docs site.'
|
||||
summary: 'Mintlify setup notes for publishing docs/.'
|
||||
read_when:
|
||||
- Setting up docs site
|
||||
---
|
||||
|
||||
# Mintlify
|
||||
@@ -40,4 +41,3 @@ Notes:
|
||||
- Keep “Quickstart” copy/paste friendly.
|
||||
- Provide CLI + HTTP API reference pages (done here).
|
||||
- Add a Troubleshooting page for common setup failures.
|
||||
|
||||
|
||||
+4
-3
@@ -1,6 +1,8 @@
|
||||
---
|
||||
title: 'Quickstart'
|
||||
description: 'Run ClawdHub locally and exercise the CLI + API.'
|
||||
summary: 'Local setup + CLI smoke: login, search, install, publish, sync.'
|
||||
read_when:
|
||||
- First run / local dev setup
|
||||
- Verifying end-to-end flows
|
||||
---
|
||||
|
||||
# Quickstart
|
||||
@@ -110,4 +112,3 @@ Dry run + non-interactive:
|
||||
```bash
|
||||
bun clawdhub sync --all --dry-run --no-input
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
---
|
||||
title: 'Skill Format'
|
||||
description: 'What the CLI uploads and what the registry stores.'
|
||||
summary: 'Skill folder format, required files, allowed file types, limits.'
|
||||
read_when:
|
||||
- Publishing skills
|
||||
- Debugging publish/sync failures
|
||||
---
|
||||
|
||||
# Skill format
|
||||
@@ -54,4 +56,3 @@ Limits (server-side):
|
||||
|
||||
- Each publish creates a new version (semver).
|
||||
- Tags are string pointers to a version; `latest` is commonly used.
|
||||
|
||||
|
||||
+7
-1
@@ -1,3 +1,10 @@
|
||||
---
|
||||
summary: 'Install telemetry collected via `clawdhub sync` + opt-out.'
|
||||
read_when:
|
||||
- Working on telemetry / privacy controls
|
||||
- Questions about what data is collected
|
||||
---
|
||||
|
||||
# Telemetry
|
||||
|
||||
ClawdHub uses **minimal telemetry** to compute **install counts** (what’s actually in use) and to power better sorting/filtering.
|
||||
@@ -82,4 +89,3 @@ export CLAWDHUB_DISABLE_TELEMETRY=1
|
||||
```
|
||||
|
||||
With this set, the CLI will not send telemetry during `clawdhub sync`.
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: 'Troubleshooting'
|
||||
description: 'Common setup and runtime issues (CLI + backend).'
|
||||
summary: 'Common setup/runtime issues (CLI + backend) and fixes.'
|
||||
read_when:
|
||||
- Something is broken and you need a fix-fast checklist
|
||||
---
|
||||
|
||||
# Troubleshooting
|
||||
@@ -46,4 +47,3 @@ clawdhub sync --root /path/to/skills
|
||||
|
||||
- Check `vercel.json` rewrite destination points at your Convex site URL.
|
||||
- Ensure `VITE_CONVEX_SITE_URL` and `CONVEX_SITE_URL` match your deployment.
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
---
|
||||
summary: 'Discord webhook events/payloads for skill publish + highlight.'
|
||||
read_when:
|
||||
- Working on webhooks/integrations
|
||||
---
|
||||
|
||||
# Webhooks (Discord)
|
||||
|
||||
ClawdHub can post Discord embeds when skills are published or highlighted.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"dev": "bun --bun vite dev --port 3000",
|
||||
"build": "bun --bun vite build",
|
||||
"preview": "bun --bun vite preview",
|
||||
"docs:list": "bun scripts/docs-list.ts",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:e2e": "vitest run -c vitest.e2e.config.ts",
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
||||
import { dirname, join, relative } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const DOCS_DIR = resolveDocsDir()
|
||||
|
||||
const EXCLUDED_DIRS = new Set(['archive', 'research'])
|
||||
|
||||
function resolveDocsDir() {
|
||||
const env = process.env.DOCS_DIR?.trim()
|
||||
if (env) return env
|
||||
|
||||
const fromCwd = join(process.cwd(), 'docs')
|
||||
if (existsSync(fromCwd)) return fromCwd
|
||||
|
||||
const docsListFile = fileURLToPath(import.meta.url)
|
||||
const docsListDir = dirname(docsListFile)
|
||||
return join(docsListDir, '..', 'docs')
|
||||
}
|
||||
|
||||
function compactStrings(values: unknown[]): string[] {
|
||||
const result: string[] = []
|
||||
for (const value of values) {
|
||||
if (value === null || value === undefined) continue
|
||||
const normalized = String(value).trim()
|
||||
if (normalized.length > 0) result.push(normalized)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function walkMarkdownFiles(dir: string, base: string = dir): string[] {
|
||||
const entries = readdirSync(dir, { withFileTypes: true })
|
||||
const files: string[] = []
|
||||
for (const entry of entries) {
|
||||
if (entry.name.startsWith('.')) continue
|
||||
const fullPath = join(dir, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
if (EXCLUDED_DIRS.has(entry.name)) continue
|
||||
files.push(...walkMarkdownFiles(fullPath, base))
|
||||
continue
|
||||
}
|
||||
if (entry.isFile() && entry.name.endsWith('.md')) {
|
||||
files.push(relative(base, fullPath))
|
||||
}
|
||||
}
|
||||
return files.sort((a, b) => a.localeCompare(b))
|
||||
}
|
||||
|
||||
function extractMetadata(fullPath: string): {
|
||||
summary: string | null
|
||||
readWhen: string[]
|
||||
error?: string
|
||||
} {
|
||||
const content = readFileSync(fullPath, 'utf8')
|
||||
|
||||
if (!content.startsWith('---')) {
|
||||
return { summary: null, readWhen: [], error: 'missing front matter' }
|
||||
}
|
||||
|
||||
const endIndex = content.indexOf('\n---', 3)
|
||||
if (endIndex === -1) {
|
||||
return { summary: null, readWhen: [], error: 'unterminated front matter' }
|
||||
}
|
||||
|
||||
const frontMatter = content.slice(3, endIndex).trim()
|
||||
const lines = frontMatter.split('\n')
|
||||
|
||||
let summaryLine: string | null = null
|
||||
const readWhen: string[] = []
|
||||
let collectingField: 'read_when' | null = null
|
||||
|
||||
for (const rawLine of lines) {
|
||||
const line = rawLine.trim()
|
||||
|
||||
if (line.startsWith('summary:')) {
|
||||
summaryLine = line
|
||||
collectingField = null
|
||||
continue
|
||||
}
|
||||
|
||||
if (line.startsWith('read_when:')) {
|
||||
collectingField = 'read_when'
|
||||
const inline = line.slice('read_when:'.length).trim()
|
||||
if (inline.startsWith('[') && inline.endsWith(']')) {
|
||||
try {
|
||||
const parsed = JSON.parse(inline.replace(/'/g, '"')) as unknown
|
||||
if (Array.isArray(parsed)) {
|
||||
readWhen.push(...compactStrings(parsed))
|
||||
}
|
||||
} catch {
|
||||
// ignore malformed inline arrays
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (collectingField === 'read_when') {
|
||||
if (line.startsWith('- ')) {
|
||||
const hint = line.slice(2).trim()
|
||||
if (hint) readWhen.push(hint)
|
||||
} else if (line === '') {
|
||||
// ignore
|
||||
} else {
|
||||
collectingField = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!summaryLine) {
|
||||
return { summary: null, readWhen, error: 'summary key missing' }
|
||||
}
|
||||
|
||||
const summaryValue = summaryLine.slice('summary:'.length).trim()
|
||||
const normalized = summaryValue
|
||||
.replace(/^['"]|['"]$/g, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim()
|
||||
|
||||
if (!normalized) {
|
||||
return { summary: null, readWhen, error: 'summary is empty' }
|
||||
}
|
||||
|
||||
return { summary: normalized, readWhen }
|
||||
}
|
||||
|
||||
console.log('Listing all markdown files in docs folder:')
|
||||
|
||||
const markdownFiles = walkMarkdownFiles(DOCS_DIR)
|
||||
|
||||
for (const relativePath of markdownFiles) {
|
||||
const fullPath = join(DOCS_DIR, relativePath)
|
||||
const { summary, readWhen, error } = extractMetadata(fullPath)
|
||||
if (summary) {
|
||||
console.log(`${relativePath} - ${summary}`)
|
||||
if (readWhen.length > 0) {
|
||||
console.log(` Read when: ${readWhen.join('; ')}`)
|
||||
}
|
||||
} else {
|
||||
const reason = error ? ` - [${error}]` : ''
|
||||
console.log(`${relativePath}${reason}`)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
'\nReminder: keep docs up to date as behavior changes. When your task matches any "Read when" hint above, read that doc before coding, and suggest new coverage when it is missing.',
|
||||
)
|
||||
Reference in New Issue
Block a user