shipfolio 1.0.10 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -6
- package/dist/cli.js +1212 -441
- package/dist/cli.js.map +1 -1
- package/dist/lib/orchestrator/detect.js +3 -1
- package/dist/lib/orchestrator/detect.js.map +1 -1
- package/dist/lib/orchestrator/prompt-builder.js +93 -174
- package/dist/lib/orchestrator/prompt-builder.js.map +1 -1
- package/dist/lib/scanner/git.js +1 -1
- package/dist/lib/scanner/git.js.map +1 -1
- package/dist/lib/scanner/index.js +42 -18
- package/dist/lib/scanner/index.js.map +1 -1
- package/dist/lib/spec/builder.js +100 -3
- package/dist/lib/spec/builder.js.map +1 -1
- package/dist/lib/spec/diff.js +179 -15
- package/dist/lib/spec/diff.js.map +1 -1
- package/package.json +6 -4
- package/prompts/fresh-build.md +15 -0
- package/prompts/update.md +5 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/scanner/git.ts","../../../src/utils/fs.ts","../../../src/scanner/detectors/node.ts","../../../src/scanner/detectors/python.ts","../../../src/scanner/detectors/rust.ts","../../../src/scanner/detectors/go.ts","../../../src/scanner/detectors/generic.ts","../../../src/scanner/extractors.ts","../../../src/utils/logger.ts","../../../src/scanner/index.ts"],"sourcesContent":["import simpleGit from \"simple-git\";\nimport { basename } from \"node:path\";\n\nexport interface GitMeta {\n isRepo: boolean;\n firstCommitDate: string | null;\n lastCommitDate: string | null;\n totalCommits: number;\n remoteUrl: string | null;\n lastCommitHash: string | null;\n}\n\nexport async function getGitMeta(projectPath: string): Promise<GitMeta> {\n const git = simpleGit(projectPath);\n const empty: GitMeta = {\n isRepo: false,\n firstCommitDate: null,\n lastCommitDate: null,\n totalCommits: 0,\n remoteUrl: null,\n lastCommitHash: null,\n };\n\n try {\n const isRepo = await git.checkIsRepo();\n if (!isRepo) return empty;\n } catch {\n return empty;\n }\n\n try {\n const log = await git.log({ maxCount: 1 });\n\n // Get first commit date\n let firstCommitDate: string | null = null;\n try {\n const firstResult = await git.raw([\"log\", \"--reverse\", \"--format=%aI\", \"--max-count=1\"]);\n firstCommitDate = firstResult.trim() || null;\n } catch {\n // ignore\n }\n\n let remoteUrl: string | null = null;\n try {\n const remotes = await git.getRemotes(true);\n const origin = remotes.find((r) => r.name === \"origin\");\n if (origin?.refs?.fetch) {\n remoteUrl = normalizeGitUrl(origin.refs.fetch);\n }\n } catch {\n // no remotes\n }\n\n // Get total commit count\n let totalCommits = 0;\n try {\n const result = await git.raw([\"rev-list\", \"--count\", \"HEAD\"]);\n totalCommits = parseInt(result.trim(), 10) || 0;\n } catch {\n totalCommits = 0;\n }\n\n return {\n isRepo: true,\n firstCommitDate,\n lastCommitDate: log.latest?.date || null,\n totalCommits,\n remoteUrl,\n lastCommitHash: log.latest?.hash || null,\n };\n } catch {\n return { ...empty, isRepo: true };\n }\n}\n\nfunction normalizeGitUrl(url: string): string {\n // Convert SSH to HTTPS\n if (url.startsWith(\"git@\")) {\n url = url.replace(\":\", \"/\").replace(\"git@\", \"https://\");\n }\n // Remove .git suffix\n if (url.endsWith(\".git\")) {\n url = url.slice(0, -4);\n }\n return url;\n}\n\nexport async function findGitRepos(\n rootPath: string,\n maxDepth: number = 3\n): Promise<string[]> {\n const { glob } = await import(\"glob\");\n const gitDirs = await glob(\"**/.git\", {\n cwd: rootPath,\n maxDepth: maxDepth + 1,\n dot: true,\n ignore: [\n \"**/node_modules/**/.git\",\n \"**/vendor/**/.git\",\n \"**/__pycache__/**/.git\",\n ],\n });\n\n return gitDirs\n .map((gitDir) => {\n const parts = gitDir.split(\"/\");\n parts.pop(); // remove .git\n return parts.length > 0\n ? `${rootPath}/${parts.join(\"/\")}`\n : rootPath;\n })\n .sort();\n}\n","import { readFile, writeFile, access, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport async function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readJson<T = unknown>(path: string): Promise<T> {\n const content = await readFile(path, \"utf-8\");\n return JSON.parse(content) as T;\n}\n\nexport async function writeJson(path: string, data: unknown): Promise<void> {\n await writeFile(path, JSON.stringify(data, null, 2), \"utf-8\");\n}\n\nexport async function readText(path: string): Promise<string> {\n return readFile(path, \"utf-8\");\n}\n\nexport async function writeText(path: string, content: string): Promise<void> {\n await writeFile(path, content, \"utf-8\");\n}\n\nexport async function ensureDir(path: string): Promise<void> {\n await mkdir(path, { recursive: true });\n}\n\nexport { join };\n","import { fileExists, readJson, join } from \"../../utils/fs.js\";\n\ninterface PackageJson {\n name?: string;\n description?: string;\n homepage?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nexport async function detectNode(projectPath: string) {\n const pkgPath = join(projectPath, \"package.json\");\n if (!(await fileExists(pkgPath))) return null;\n\n const pkg = await readJson<PackageJson>(pkgPath);\n const allDeps = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n };\n const depNames = Object.keys(allDeps);\n\n const techStack: string[] = [];\n\n // Frameworks\n if (depNames.includes(\"next\")) techStack.push(\"Next.js\");\n else if (depNames.includes(\"nuxt\")) techStack.push(\"Nuxt\");\n else if (depNames.includes(\"astro\")) techStack.push(\"Astro\");\n else if (depNames.includes(\"svelte\") || depNames.includes(\"@sveltejs/kit\"))\n techStack.push(\"Svelte\");\n\n // UI libraries\n if (depNames.includes(\"react\")) techStack.push(\"React\");\n if (depNames.includes(\"vue\")) techStack.push(\"Vue\");\n\n // Styling\n if (depNames.includes(\"tailwindcss\")) techStack.push(\"Tailwind CSS\");\n\n // Backend\n if (depNames.includes(\"express\")) techStack.push(\"Express\");\n if (depNames.includes(\"fastify\")) techStack.push(\"Fastify\");\n if (depNames.includes(\"hono\")) techStack.push(\"Hono\");\n\n // Database\n if (depNames.includes(\"prisma\") || depNames.includes(\"@prisma/client\"))\n techStack.push(\"Prisma\");\n if (depNames.includes(\"drizzle-orm\")) techStack.push(\"Drizzle\");\n if (depNames.includes(\"mongoose\")) techStack.push(\"MongoDB\");\n\n // AI/ML\n if (depNames.includes(\"openai\")) techStack.push(\"OpenAI\");\n if (depNames.includes(\"@anthropic-ai/sdk\")) techStack.push(\"Claude API\");\n if (depNames.includes(\"langchain\")) techStack.push(\"LangChain\");\n\n // Language\n if (depNames.includes(\"typescript\")) techStack.push(\"TypeScript\");\n else techStack.push(\"JavaScript\");\n\n return {\n name: pkg.name || null,\n description: pkg.description || null,\n homepage: pkg.homepage || null,\n techStack,\n };\n}\n","import { fileExists, readText, join } from \"../../utils/fs.js\";\n\nexport async function detectPython(projectPath: string) {\n const techStack: string[] = [\"Python\"];\n\n const reqPath = join(projectPath, \"requirements.txt\");\n const pyprojectPath = join(projectPath, \"pyproject.toml\");\n const setupPath = join(projectPath, \"setup.py\");\n\n let deps = \"\";\n\n if (await fileExists(reqPath)) {\n deps = await readText(reqPath);\n } else if (await fileExists(pyprojectPath)) {\n deps = await readText(pyprojectPath);\n } else if (await fileExists(setupPath)) {\n deps = await readText(setupPath);\n } else {\n return null;\n }\n\n const depsLower = deps.toLowerCase();\n\n if (depsLower.includes(\"django\")) techStack.push(\"Django\");\n if (depsLower.includes(\"flask\")) techStack.push(\"Flask\");\n if (depsLower.includes(\"fastapi\")) techStack.push(\"FastAPI\");\n if (depsLower.includes(\"pytorch\") || depsLower.includes(\"torch\"))\n techStack.push(\"PyTorch\");\n if (depsLower.includes(\"tensorflow\")) techStack.push(\"TensorFlow\");\n if (depsLower.includes(\"transformers\")) techStack.push(\"Transformers\");\n if (depsLower.includes(\"langchain\")) techStack.push(\"LangChain\");\n if (depsLower.includes(\"openai\")) techStack.push(\"OpenAI\");\n if (depsLower.includes(\"pandas\")) techStack.push(\"Pandas\");\n if (depsLower.includes(\"numpy\")) techStack.push(\"NumPy\");\n if (depsLower.includes(\"scikit\")) techStack.push(\"Scikit-learn\");\n\n return { techStack };\n}\n","import { fileExists, readText, join } from \"../../utils/fs.js\";\n\nexport async function detectRust(projectPath: string) {\n const cargoPath = join(projectPath, \"Cargo.toml\");\n if (!(await fileExists(cargoPath))) return null;\n\n const content = await readText(cargoPath);\n const techStack: string[] = [\"Rust\"];\n\n if (content.includes(\"actix\")) techStack.push(\"Actix\");\n if (content.includes(\"axum\")) techStack.push(\"Axum\");\n if (content.includes(\"tokio\")) techStack.push(\"Tokio\");\n if (content.includes(\"wasm\")) techStack.push(\"WebAssembly\");\n if (content.includes(\"tauri\")) techStack.push(\"Tauri\");\n if (content.includes(\"diesel\")) techStack.push(\"Diesel\");\n if (content.includes(\"sqlx\")) techStack.push(\"SQLx\");\n\n // Extract name from [package] section\n const nameMatch = content.match(/\\[package\\][\\s\\S]*?name\\s*=\\s*\"([^\"]+)\"/);\n const descMatch = content.match(\n /\\[package\\][\\s\\S]*?description\\s*=\\s*\"([^\"]+)\"/\n );\n\n return {\n name: nameMatch?.[1] || null,\n description: descMatch?.[1] || null,\n techStack,\n };\n}\n","import { fileExists, readText, join } from \"../../utils/fs.js\";\n\nexport async function detectGo(projectPath: string) {\n const goModPath = join(projectPath, \"go.mod\");\n if (!(await fileExists(goModPath))) return null;\n\n const content = await readText(goModPath);\n const techStack: string[] = [\"Go\"];\n\n if (content.includes(\"gin-gonic\")) techStack.push(\"Gin\");\n if (content.includes(\"echo\")) techStack.push(\"Echo\");\n if (content.includes(\"fiber\")) techStack.push(\"Fiber\");\n if (content.includes(\"grpc\")) techStack.push(\"gRPC\");\n if (content.includes(\"gorm\")) techStack.push(\"GORM\");\n if (content.includes(\"cobra\")) techStack.push(\"Cobra\");\n if (content.includes(\"ent\")) techStack.push(\"Ent\");\n\n return { techStack };\n}\n","import { glob } from \"glob\";\nimport { basename, extname } from \"node:path\";\n\nconst EXTENSION_MAP: Record<string, string> = {\n \".ts\": \"TypeScript\",\n \".tsx\": \"TypeScript\",\n \".js\": \"JavaScript\",\n \".jsx\": \"JavaScript\",\n \".py\": \"Python\",\n \".rs\": \"Rust\",\n \".go\": \"Go\",\n \".java\": \"Java\",\n \".kt\": \"Kotlin\",\n \".swift\": \"Swift\",\n \".rb\": \"Ruby\",\n \".php\": \"PHP\",\n \".cs\": \"C#\",\n \".cpp\": \"C++\",\n \".c\": \"C\",\n \".dart\": \"Dart\",\n \".lua\": \"Lua\",\n \".zig\": \"Zig\",\n \".sol\": \"Solidity\",\n \".ex\": \"Elixir\",\n \".exs\": \"Elixir\",\n};\n\nconst IGNORE_DIRS = [\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \"out\",\n \".next\",\n \"target\",\n \"vendor\",\n \"__pycache__\",\n \".venv\",\n \"venv\",\n];\n\nexport async function detectLanguages(\n projectPath: string\n): Promise<Record<string, number>> {\n const ignorePattern = IGNORE_DIRS.map((d) => `**/${d}/**`);\n const files = await glob(\"**/*.*\", {\n cwd: projectPath,\n ignore: ignorePattern,\n nodir: true,\n maxDepth: 5,\n });\n\n const counts: Record<string, number> = {};\n for (const file of files) {\n const ext = extname(file).toLowerCase();\n const lang = EXTENSION_MAP[ext];\n if (lang) {\n counts[lang] = (counts[lang] || 0) + 1;\n }\n }\n\n return counts;\n}\n\nexport function deriveNameFromPath(projectPath: string): string {\n return basename(projectPath);\n}\n","import { fileExists, readText, join } from \"../utils/fs.js\";\n\nexport async function extractReadme(\n projectPath: string\n): Promise<string | null> {\n const candidates = [\n \"README.md\",\n \"readme.md\",\n \"Readme.md\",\n \"README.txt\",\n \"README\",\n ];\n for (const name of candidates) {\n const p = join(projectPath, name);\n if (await fileExists(p)) {\n const content = await readText(p);\n // Limit to first 3000 chars to keep spec manageable\n return content.slice(0, 3000);\n }\n }\n return null;\n}\n\nexport function extractFirstParagraph(readme: string | null): string | null {\n if (!readme) return null;\n // Skip title lines (# heading)\n const lines = readme.split(\"\\n\");\n let collecting = false;\n const paragraphLines: string[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"#\")) {\n if (collecting && paragraphLines.length > 0) break;\n collecting = true;\n continue;\n }\n if (collecting && trimmed === \"\" && paragraphLines.length > 0) break;\n if (collecting && trimmed !== \"\" && !trimmed.startsWith(\"![\") && !trimmed.startsWith(\"[![\")) {\n paragraphLines.push(trimmed);\n }\n if (!collecting && trimmed !== \"\" && !trimmed.startsWith(\"[\") && !trimmed.startsWith(\"!\") && !trimmed.startsWith(\"[![\")) {\n paragraphLines.push(trimmed);\n collecting = true;\n }\n }\n\n return paragraphLines.length > 0 ? paragraphLines.join(\" \") : null;\n}\n\nexport function extractDemoUrl(readme: string | null): string | null {\n if (!readme) return null;\n // Look for common demo URL patterns\n const patterns = [\n /(?:demo|live|website|site|url|link)[\\s:]*\\[?[^\\]]*\\]?\\(?(https?:\\/\\/[^\\s)]+)/i,\n /\\[(?:demo|live|website|try it)\\]\\((https?:\\/\\/[^\\s)]+)\\)/i,\n /(https?:\\/\\/[^\\s)]+\\.(?:vercel|netlify|pages\\.dev|herokuapp|railway)\\.app[^\\s)]*)/i,\n ];\n for (const pattern of patterns) {\n const match = readme.match(pattern);\n if (match?.[1]) return match[1];\n }\n return null;\n}\n","import chalk from \"chalk\";\n\nexport const logger = {\n info(msg: string) {\n console.log(chalk.cyan(`-- ${msg}`));\n },\n success(msg: string) {\n console.log(chalk.green(`-- ${msg}`));\n },\n warn(msg: string) {\n console.log(chalk.yellow(`-- ${msg}`));\n },\n error(msg: string) {\n console.error(chalk.red(`-- ${msg}`));\n },\n plain(msg: string) {\n console.log(msg);\n },\n blank() {\n console.log();\n },\n header(msg: string) {\n console.log();\n console.log(chalk.bold(msg));\n console.log();\n },\n table(rows: string[][]) {\n if (rows.length === 0) return;\n const colWidths = rows[0].map((_, colIdx) =>\n Math.max(...rows.map((row) => (row[colIdx] || \"\").length))\n );\n for (const row of rows) {\n const line = row\n .map((cell, i) => (cell || \"\").padEnd(colWidths[i] + 2))\n .join(\"\");\n console.log(` ${line}`);\n }\n },\n};\n","import { findGitRepos, getGitMeta } from \"./git.js\";\nimport { detectNode } from \"./detectors/node.js\";\nimport { detectPython } from \"./detectors/python.js\";\nimport { detectRust } from \"./detectors/rust.js\";\nimport { detectGo } from \"./detectors/go.js\";\nimport { detectLanguages, deriveNameFromPath } from \"./detectors/generic.js\";\nimport {\n extractReadme,\n extractFirstParagraph,\n extractDemoUrl,\n} from \"./extractors.js\";\nimport type { ProjectMeta } from \"../spec/schema.js\";\nimport { logger } from \"../utils/logger.js\";\nimport ora from \"ora\";\n\nexport async function scanProjects(\n directories: string[]\n): Promise<ProjectMeta[]> {\n const allPaths: string[] = [];\n\n const spinner = ora(\"Scanning for projects...\").start();\n\n for (const dir of directories) {\n try {\n // Find git repos\n const repos = await findGitRepos(dir);\n allPaths.push(...repos);\n\n // Also find non-git project directories (with package.json, Cargo.toml, etc.)\n const nonGitDirs = await findNonGitProjects(dir, new Set(repos));\n allPaths.push(...nonGitDirs);\n } catch (err) {\n logger.warn(`Could not scan ${dir}: ${err}`);\n }\n }\n\n // Deduplicate\n const uniquePaths = [...new Set(allPaths)];\n spinner.text = `Found ${uniquePaths.length} projects. Extracting metadata...`;\n\n const projects: ProjectMeta[] = [];\n\n for (const projectPath of uniquePaths) {\n try {\n const project = await extractProjectMeta(projectPath);\n if (project) {\n projects.push(project);\n }\n } catch (err) {\n // Skip projects that fail extraction\n }\n }\n\n // Deduplicate IDs (same folder name in different parent dirs)\n const idCounts = new Map<string, number>();\n for (const project of projects) {\n const count = idCounts.get(project.id) || 0;\n if (count > 0) {\n project.id = `${project.id}-${count}`;\n }\n idCounts.set(project.id.replace(/-\\d+$/, \"\"), count + 1);\n }\n\n spinner.succeed(`Scanned ${projects.length} projects`);\n return projects;\n}\n\nasync function findNonGitProjects(\n rootPath: string,\n gitRepos: Set<string>\n): Promise<string[]> {\n const { glob } = await import(\"glob\");\n const indicators = [\n \"*/package.json\",\n \"*/Cargo.toml\",\n \"*/go.mod\",\n \"*/pyproject.toml\",\n \"*/requirements.txt\",\n \"*/setup.py\",\n ];\n const found = new Set<string>();\n for (const pattern of indicators) {\n const matches = await glob(pattern, {\n cwd: rootPath,\n ignore: [\"**/node_modules/**\", \"**/vendor/**\"],\n });\n for (const match of matches) {\n const dir = `${rootPath}/${match.split(\"/\")[0]}`;\n if (!gitRepos.has(dir) && !found.has(dir)) {\n found.add(dir);\n }\n }\n }\n return [...found].sort();\n}\n\nasync function extractProjectMeta(\n projectPath: string\n): Promise<ProjectMeta | null> {\n const gitMeta = await getGitMeta(projectPath);\n\n // Detect tech stack from various sources\n let techStack: string[] = [];\n let name: string | null = null;\n let description: string | null = null;\n let homepage: string | null = null;\n\n const nodeInfo = await detectNode(projectPath);\n if (nodeInfo) {\n techStack.push(...nodeInfo.techStack);\n name = nodeInfo.name || name;\n description = nodeInfo.description || description;\n homepage = nodeInfo.homepage || homepage;\n }\n\n const pythonInfo = await detectPython(projectPath);\n if (pythonInfo) {\n techStack.push(...pythonInfo.techStack);\n }\n\n const rustInfo = await detectRust(projectPath);\n if (rustInfo) {\n techStack.push(...rustInfo.techStack);\n name = rustInfo.name || name;\n description = rustInfo.description || description;\n }\n\n const goInfo = await detectGo(projectPath);\n if (goInfo) {\n techStack.push(...goInfo.techStack);\n }\n\n // Deduplicate tech stack\n techStack = [...new Set(techStack)];\n\n // Language breakdown\n const languages = await detectLanguages(projectPath);\n\n // If no tech stack detected, derive from languages\n if (techStack.length === 0) {\n const topLangs = Object.entries(languages)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 3)\n .map(([lang]) => lang);\n techStack = topLangs;\n }\n\n // README\n const readmeContent = await extractReadme(projectPath);\n if (!description) {\n description = extractFirstParagraph(readmeContent) || \"\";\n }\n\n // Demo URL\n const demoUrl = homepage || extractDemoUrl(readmeContent);\n\n // Name fallback\n if (!name) {\n name = deriveNameFromPath(projectPath);\n }\n\n // Generate stable ID from path (use folder name to keep it readable, but\n // append a hash suffix when the same folder name appears in multiple paths)\n const folderName = deriveNameFromPath(projectPath);\n const id = folderName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n return {\n id,\n name,\n localPath: projectPath,\n description: description || \"\",\n techStack,\n languages,\n firstCommitDate: gitMeta.firstCommitDate || \"\",\n lastCommitDate: gitMeta.lastCommitDate || \"\",\n totalCommits: gitMeta.totalCommits,\n remoteUrl: gitMeta.remoteUrl,\n demoUrl,\n readmeContent,\n lastScannedCommit: gitMeta.lastCommitHash || \"\",\n };\n}\n"],"mappings":"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","names":["glob","glob"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/scanner/git.ts","../../../src/utils/fs.ts","../../../src/scanner/detectors/node.ts","../../../src/scanner/detectors/python.ts","../../../src/scanner/detectors/rust.ts","../../../src/scanner/detectors/go.ts","../../../src/scanner/detectors/generic.ts","../../../src/scanner/index.ts","../../../src/scanner/extractors.ts","../../../src/utils/logger.ts"],"sourcesContent":["import simpleGit from \"simple-git\";\n\nexport interface GitMeta {\n isRepo: boolean;\n firstCommitDate: string | null;\n lastCommitDate: string | null;\n totalCommits: number;\n remoteUrl: string | null;\n lastCommitHash: string | null;\n}\n\nexport async function getGitMeta(projectPath: string): Promise<GitMeta> {\n const git = simpleGit(projectPath);\n const empty: GitMeta = {\n isRepo: false,\n firstCommitDate: null,\n lastCommitDate: null,\n totalCommits: 0,\n remoteUrl: null,\n lastCommitHash: null,\n };\n\n try {\n const isRepo = await git.checkIsRepo();\n if (!isRepo) return empty;\n } catch {\n return empty;\n }\n\n try {\n const log = await git.log({ maxCount: 1 });\n\n // Get first commit date\n let firstCommitDate: string | null = null;\n try {\n const firstResult = await git.raw([\"log\", \"--reverse\", \"--format=%aI\", \"--max-count=1\"]);\n firstCommitDate = firstResult.trim() || null;\n } catch {\n // ignore\n }\n\n let remoteUrl: string | null = null;\n try {\n const remotes = await git.getRemotes(true);\n const origin = remotes.find((r) => r.name === \"origin\");\n if (origin?.refs?.fetch) {\n remoteUrl = normalizeGitUrl(origin.refs.fetch);\n }\n } catch {\n // no remotes\n }\n\n // Get total commit count\n let totalCommits = 0;\n try {\n const result = await git.raw([\"rev-list\", \"--count\", \"HEAD\"]);\n totalCommits = parseInt(result.trim(), 10) || 0;\n } catch {\n totalCommits = 0;\n }\n\n return {\n isRepo: true,\n firstCommitDate,\n lastCommitDate: log.latest?.date || null,\n totalCommits,\n remoteUrl,\n lastCommitHash: log.latest?.hash || null,\n };\n } catch {\n return { ...empty, isRepo: true };\n }\n}\n\nfunction normalizeGitUrl(url: string): string {\n // Convert SSH to HTTPS\n if (url.startsWith(\"git@\")) {\n url = url.replace(\":\", \"/\").replace(\"git@\", \"https://\");\n }\n // Remove .git suffix\n if (url.endsWith(\".git\")) {\n url = url.slice(0, -4);\n }\n return url;\n}\n\nexport async function findGitRepos(\n rootPath: string,\n maxDepth: number = 6\n): Promise<string[]> {\n const { glob } = await import(\"glob\");\n const gitDirs = await glob(\"**/.git\", {\n cwd: rootPath,\n maxDepth: maxDepth + 1,\n dot: true,\n ignore: [\n \"**/node_modules/**/.git\",\n \"**/vendor/**/.git\",\n \"**/__pycache__/**/.git\",\n ],\n });\n\n return gitDirs\n .map((gitDir) => {\n const parts = gitDir.split(\"/\");\n parts.pop(); // remove .git\n return parts.length > 0\n ? `${rootPath}/${parts.join(\"/\")}`\n : rootPath;\n })\n .sort();\n}\n","import { readFile, writeFile, access, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nexport async function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readJson<T = unknown>(path: string): Promise<T> {\n const content = await readFile(path, \"utf-8\");\n return JSON.parse(content) as T;\n}\n\nexport async function writeJson(path: string, data: unknown): Promise<void> {\n await writeFile(path, JSON.stringify(data, null, 2), \"utf-8\");\n}\n\nexport async function readText(path: string): Promise<string> {\n return readFile(path, \"utf-8\");\n}\n\nexport async function writeText(path: string, content: string): Promise<void> {\n await writeFile(path, content, \"utf-8\");\n}\n\nexport async function ensureDir(path: string): Promise<void> {\n await mkdir(path, { recursive: true });\n}\n\nexport { join };\n","import { fileExists, readJson, join } from \"../../utils/fs.js\";\n\ninterface PackageJson {\n name?: string;\n description?: string;\n homepage?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\nexport async function detectNode(projectPath: string) {\n const pkgPath = join(projectPath, \"package.json\");\n if (!(await fileExists(pkgPath))) return null;\n\n const pkg = await readJson<PackageJson>(pkgPath);\n const allDeps = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n };\n const depNames = Object.keys(allDeps);\n\n const techStack: string[] = [];\n\n // Frameworks\n if (depNames.includes(\"next\")) techStack.push(\"Next.js\");\n else if (depNames.includes(\"nuxt\")) techStack.push(\"Nuxt\");\n else if (depNames.includes(\"astro\")) techStack.push(\"Astro\");\n else if (depNames.includes(\"svelte\") || depNames.includes(\"@sveltejs/kit\"))\n techStack.push(\"Svelte\");\n\n // UI libraries\n if (depNames.includes(\"react\")) techStack.push(\"React\");\n if (depNames.includes(\"vue\")) techStack.push(\"Vue\");\n\n // Styling\n if (depNames.includes(\"tailwindcss\")) techStack.push(\"Tailwind CSS\");\n\n // Backend\n if (depNames.includes(\"express\")) techStack.push(\"Express\");\n if (depNames.includes(\"fastify\")) techStack.push(\"Fastify\");\n if (depNames.includes(\"hono\")) techStack.push(\"Hono\");\n\n // Database\n if (depNames.includes(\"prisma\") || depNames.includes(\"@prisma/client\"))\n techStack.push(\"Prisma\");\n if (depNames.includes(\"drizzle-orm\")) techStack.push(\"Drizzle\");\n if (depNames.includes(\"mongoose\")) techStack.push(\"MongoDB\");\n\n // AI/ML\n if (depNames.includes(\"openai\")) techStack.push(\"OpenAI\");\n if (depNames.includes(\"@anthropic-ai/sdk\")) techStack.push(\"Claude API\");\n if (depNames.includes(\"langchain\")) techStack.push(\"LangChain\");\n\n // Language\n if (depNames.includes(\"typescript\")) techStack.push(\"TypeScript\");\n else techStack.push(\"JavaScript\");\n\n return {\n name: pkg.name || null,\n description: pkg.description || null,\n homepage: pkg.homepage || null,\n techStack,\n };\n}\n","import { fileExists, readText, join } from \"../../utils/fs.js\";\n\nexport async function detectPython(projectPath: string) {\n const techStack: string[] = [\"Python\"];\n\n const reqPath = join(projectPath, \"requirements.txt\");\n const pyprojectPath = join(projectPath, \"pyproject.toml\");\n const setupPath = join(projectPath, \"setup.py\");\n\n let deps = \"\";\n\n if (await fileExists(reqPath)) {\n deps = await readText(reqPath);\n } else if (await fileExists(pyprojectPath)) {\n deps = await readText(pyprojectPath);\n } else if (await fileExists(setupPath)) {\n deps = await readText(setupPath);\n } else {\n return null;\n }\n\n const depsLower = deps.toLowerCase();\n\n if (depsLower.includes(\"django\")) techStack.push(\"Django\");\n if (depsLower.includes(\"flask\")) techStack.push(\"Flask\");\n if (depsLower.includes(\"fastapi\")) techStack.push(\"FastAPI\");\n if (depsLower.includes(\"pytorch\") || depsLower.includes(\"torch\"))\n techStack.push(\"PyTorch\");\n if (depsLower.includes(\"tensorflow\")) techStack.push(\"TensorFlow\");\n if (depsLower.includes(\"transformers\")) techStack.push(\"Transformers\");\n if (depsLower.includes(\"langchain\")) techStack.push(\"LangChain\");\n if (depsLower.includes(\"openai\")) techStack.push(\"OpenAI\");\n if (depsLower.includes(\"pandas\")) techStack.push(\"Pandas\");\n if (depsLower.includes(\"numpy\")) techStack.push(\"NumPy\");\n if (depsLower.includes(\"scikit\")) techStack.push(\"Scikit-learn\");\n\n return { techStack };\n}\n","import { fileExists, readText, join } from \"../../utils/fs.js\";\n\nexport async function detectRust(projectPath: string) {\n const cargoPath = join(projectPath, \"Cargo.toml\");\n if (!(await fileExists(cargoPath))) return null;\n\n const content = await readText(cargoPath);\n const techStack: string[] = [\"Rust\"];\n\n if (content.includes(\"actix\")) techStack.push(\"Actix\");\n if (content.includes(\"axum\")) techStack.push(\"Axum\");\n if (content.includes(\"tokio\")) techStack.push(\"Tokio\");\n if (content.includes(\"wasm\")) techStack.push(\"WebAssembly\");\n if (content.includes(\"tauri\")) techStack.push(\"Tauri\");\n if (content.includes(\"diesel\")) techStack.push(\"Diesel\");\n if (content.includes(\"sqlx\")) techStack.push(\"SQLx\");\n\n // Extract name from [package] section\n const nameMatch = content.match(/\\[package\\][\\s\\S]*?name\\s*=\\s*\"([^\"]+)\"/);\n const descMatch = content.match(\n /\\[package\\][\\s\\S]*?description\\s*=\\s*\"([^\"]+)\"/\n );\n\n return {\n name: nameMatch?.[1] || null,\n description: descMatch?.[1] || null,\n techStack,\n };\n}\n","import { fileExists, readText, join } from \"../../utils/fs.js\";\n\nexport async function detectGo(projectPath: string) {\n const goModPath = join(projectPath, \"go.mod\");\n if (!(await fileExists(goModPath))) return null;\n\n const content = await readText(goModPath);\n const techStack: string[] = [\"Go\"];\n\n if (content.includes(\"gin-gonic\")) techStack.push(\"Gin\");\n if (content.includes(\"echo\")) techStack.push(\"Echo\");\n if (content.includes(\"fiber\")) techStack.push(\"Fiber\");\n if (content.includes(\"grpc\")) techStack.push(\"gRPC\");\n if (content.includes(\"gorm\")) techStack.push(\"GORM\");\n if (content.includes(\"cobra\")) techStack.push(\"Cobra\");\n if (content.includes(\"ent\")) techStack.push(\"Ent\");\n\n return { techStack };\n}\n","import { glob } from \"glob\";\nimport { basename, extname } from \"node:path\";\n\nconst EXTENSION_MAP: Record<string, string> = {\n \".ts\": \"TypeScript\",\n \".tsx\": \"TypeScript\",\n \".js\": \"JavaScript\",\n \".jsx\": \"JavaScript\",\n \".py\": \"Python\",\n \".rs\": \"Rust\",\n \".go\": \"Go\",\n \".java\": \"Java\",\n \".kt\": \"Kotlin\",\n \".swift\": \"Swift\",\n \".rb\": \"Ruby\",\n \".php\": \"PHP\",\n \".cs\": \"C#\",\n \".cpp\": \"C++\",\n \".c\": \"C\",\n \".dart\": \"Dart\",\n \".lua\": \"Lua\",\n \".zig\": \"Zig\",\n \".sol\": \"Solidity\",\n \".ex\": \"Elixir\",\n \".exs\": \"Elixir\",\n};\n\nconst IGNORE_DIRS = [\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \"out\",\n \".next\",\n \"target\",\n \"vendor\",\n \"__pycache__\",\n \".venv\",\n \"venv\",\n];\n\nexport async function detectLanguages(\n projectPath: string\n): Promise<Record<string, number>> {\n const ignorePattern = IGNORE_DIRS.map((d) => `**/${d}/**`);\n const files = await glob(\"**/*.*\", {\n cwd: projectPath,\n ignore: ignorePattern,\n nodir: true,\n maxDepth: 5,\n });\n\n const counts: Record<string, number> = {};\n for (const file of files) {\n const ext = extname(file).toLowerCase();\n const lang = EXTENSION_MAP[ext];\n if (lang) {\n counts[lang] = (counts[lang] || 0) + 1;\n }\n }\n\n return counts;\n}\n\nexport function deriveNameFromPath(projectPath: string): string {\n return basename(projectPath);\n}\n","import { findGitRepos, getGitMeta } from \"./git.js\";\nimport { detectNode } from \"./detectors/node.js\";\nimport { detectPython } from \"./detectors/python.js\";\nimport { detectRust } from \"./detectors/rust.js\";\nimport { detectGo } from \"./detectors/go.js\";\nimport { detectLanguages, deriveNameFromPath } from \"./detectors/generic.js\";\nimport { dirname, resolve as resolvePath } from \"node:path\";\nimport {\n extractReadme,\n extractFirstParagraph,\n extractDemoUrl,\n} from \"./extractors.js\";\nimport type { ProjectMeta } from \"../spec/schema.js\";\nimport { logger } from \"../utils/logger.js\";\nimport ora from \"ora\";\n\nconst DEFAULT_SCAN_DEPTH = 6;\nconst NON_GIT_PROJECT_INDICATORS = [\n \"package.json\",\n \"Cargo.toml\",\n \"go.mod\",\n \"pyproject.toml\",\n \"requirements.txt\",\n \"setup.py\",\n];\nconst SCAN_IGNORE_PATTERNS = [\n \"**/node_modules/**\",\n \"**/vendor/**\",\n \"**/.git/**\",\n \"**/.next/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/out/**\",\n \"**/coverage/**\",\n];\n\nexport async function scanProjects(\n directories: string[]\n): Promise<ProjectMeta[]> {\n const allPaths: string[] = [];\n\n const spinner = ora(\"Scanning for projects...\").start();\n\n for (const dir of directories) {\n try {\n // Find git repos\n const repos = await findGitRepos(dir, DEFAULT_SCAN_DEPTH);\n allPaths.push(...repos);\n\n // Also find non-git project directories (with package.json, Cargo.toml, etc.)\n const nonGitDirs = await findNonGitProjects(\n dir,\n repos,\n DEFAULT_SCAN_DEPTH\n );\n allPaths.push(...nonGitDirs);\n } catch (err) {\n logger.warn(`Could not scan ${dir}: ${err}`);\n }\n }\n\n // Deduplicate\n const uniquePaths = [...new Set(allPaths)];\n spinner.text = `Found ${uniquePaths.length} projects. Extracting metadata...`;\n\n const projects: ProjectMeta[] = [];\n\n for (const projectPath of uniquePaths) {\n try {\n const project = await extractProjectMeta(projectPath);\n if (project) {\n projects.push(project);\n }\n } catch (err) {\n // Skip projects that fail extraction\n }\n }\n\n // Deduplicate IDs (same folder name in different parent dirs)\n const idCounts = new Map<string, number>();\n for (const project of projects) {\n const count = idCounts.get(project.id) || 0;\n if (count > 0) {\n project.id = `${project.id}-${count}`;\n }\n idCounts.set(project.id.replace(/-\\d+$/, \"\"), count + 1);\n }\n\n spinner.succeed(`Scanned ${projects.length} projects`);\n return projects;\n}\n\nasync function findNonGitProjects(\n rootPath: string,\n gitRepos: string[],\n maxDepth: number\n): Promise<string[]> {\n const { glob } = await import(\"glob\");\n const found = new Set<string>();\n\n for (const indicator of NON_GIT_PROJECT_INDICATORS) {\n const matches = await glob(`**/${indicator}`, {\n cwd: rootPath,\n ignore: SCAN_IGNORE_PATTERNS,\n maxDepth,\n });\n\n for (const match of matches) {\n const candidateDir = dirname(resolvePath(rootPath, match));\n if (\n !isInsideTrackedRepo(candidateDir, gitRepos) &&\n !found.has(candidateDir)\n ) {\n found.add(candidateDir);\n }\n }\n }\n\n return [...found].sort();\n}\n\nfunction isInsideTrackedRepo(candidateDir: string, gitRepos: string[]): boolean {\n return gitRepos.some(\n (repoPath) =>\n candidateDir === repoPath || candidateDir.startsWith(`${repoPath}/`)\n );\n}\n\nasync function extractProjectMeta(\n projectPath: string\n): Promise<ProjectMeta | null> {\n const gitMeta = await getGitMeta(projectPath);\n\n // Detect tech stack from various sources\n let techStack: string[] = [];\n let name: string | null = null;\n let description: string | null = null;\n let homepage: string | null = null;\n\n const nodeInfo = await detectNode(projectPath);\n if (nodeInfo) {\n techStack.push(...nodeInfo.techStack);\n name = nodeInfo.name || name;\n description = nodeInfo.description || description;\n homepage = nodeInfo.homepage || homepage;\n }\n\n const pythonInfo = await detectPython(projectPath);\n if (pythonInfo) {\n techStack.push(...pythonInfo.techStack);\n }\n\n const rustInfo = await detectRust(projectPath);\n if (rustInfo) {\n techStack.push(...rustInfo.techStack);\n name = rustInfo.name || name;\n description = rustInfo.description || description;\n }\n\n const goInfo = await detectGo(projectPath);\n if (goInfo) {\n techStack.push(...goInfo.techStack);\n }\n\n // Deduplicate tech stack\n techStack = [...new Set(techStack)];\n\n // Language breakdown\n const languages = await detectLanguages(projectPath);\n\n // If no tech stack detected, derive from languages\n if (techStack.length === 0) {\n const topLangs = Object.entries(languages)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 3)\n .map(([lang]) => lang);\n techStack = topLangs;\n }\n\n // README\n const readmeContent = await extractReadme(projectPath);\n if (!description) {\n description = extractFirstParagraph(readmeContent) || \"\";\n }\n\n // Demo URL\n const demoUrl = homepage || extractDemoUrl(readmeContent);\n\n // Name fallback\n if (!name) {\n name = deriveNameFromPath(projectPath);\n }\n\n // Generate stable ID from path (use folder name to keep it readable, but\n // append a hash suffix when the same folder name appears in multiple paths)\n const folderName = deriveNameFromPath(projectPath);\n const id = folderName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n return {\n id,\n name,\n localPath: projectPath,\n description: description || \"\",\n techStack,\n languages,\n firstCommitDate: gitMeta.firstCommitDate || \"\",\n lastCommitDate: gitMeta.lastCommitDate || \"\",\n totalCommits: gitMeta.totalCommits,\n remoteUrl: gitMeta.remoteUrl,\n demoUrl,\n readmeContent,\n lastScannedCommit: gitMeta.lastCommitHash || \"\",\n };\n}\n","import { fileExists, readText, join } from \"../utils/fs.js\";\n\nexport async function extractReadme(\n projectPath: string\n): Promise<string | null> {\n const candidates = [\n \"README.md\",\n \"readme.md\",\n \"Readme.md\",\n \"README.txt\",\n \"README\",\n ];\n for (const name of candidates) {\n const p = join(projectPath, name);\n if (await fileExists(p)) {\n const content = await readText(p);\n // Limit to first 3000 chars to keep spec manageable\n return content.slice(0, 3000);\n }\n }\n return null;\n}\n\nexport function extractFirstParagraph(readme: string | null): string | null {\n if (!readme) return null;\n // Skip title lines (# heading)\n const lines = readme.split(\"\\n\");\n let collecting = false;\n const paragraphLines: string[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"#\")) {\n if (collecting && paragraphLines.length > 0) break;\n collecting = true;\n continue;\n }\n if (collecting && trimmed === \"\" && paragraphLines.length > 0) break;\n if (collecting && trimmed !== \"\" && !trimmed.startsWith(\"![\") && !trimmed.startsWith(\"[![\")) {\n paragraphLines.push(trimmed);\n }\n if (!collecting && trimmed !== \"\" && !trimmed.startsWith(\"[\") && !trimmed.startsWith(\"!\") && !trimmed.startsWith(\"[![\")) {\n paragraphLines.push(trimmed);\n collecting = true;\n }\n }\n\n return paragraphLines.length > 0 ? paragraphLines.join(\" \") : null;\n}\n\nexport function extractDemoUrl(readme: string | null): string | null {\n if (!readme) return null;\n // Look for common demo URL patterns\n const patterns = [\n /(?:demo|live|website|site|url|link)[\\s:]*\\[?[^\\]]*\\]?\\(?(https?:\\/\\/[^\\s)]+)/i,\n /\\[(?:demo|live|website|try it)\\]\\((https?:\\/\\/[^\\s)]+)\\)/i,\n /(https?:\\/\\/[^\\s)]+\\.(?:vercel|netlify|pages\\.dev|herokuapp|railway)\\.app[^\\s)]*)/i,\n ];\n for (const pattern of patterns) {\n const match = readme.match(pattern);\n if (match?.[1]) return match[1];\n }\n return null;\n}\n","import chalk from \"chalk\";\n\nexport const logger = {\n info(msg: string) {\n console.log(chalk.cyan(`-- ${msg}`));\n },\n success(msg: string) {\n console.log(chalk.green(`-- ${msg}`));\n },\n warn(msg: string) {\n console.log(chalk.yellow(`-- ${msg}`));\n },\n error(msg: string) {\n console.error(chalk.red(`-- ${msg}`));\n },\n plain(msg: string) {\n console.log(msg);\n },\n blank() {\n console.log();\n },\n header(msg: string) {\n console.log();\n console.log(chalk.bold(msg));\n console.log();\n },\n table(rows: string[][]) {\n if (rows.length === 0) return;\n const colWidths = rows[0].map((_, colIdx) =>\n Math.max(...rows.map((row) => (row[colIdx] || \"\").length))\n );\n for (const row of rows) {\n const line = row\n .map((cell, i) => (cell || \"\").padEnd(colWidths[i] + 2))\n .join(\"\");\n console.log(` ${line}`);\n }\n },\n};\n"],"mappings":"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","names":["glob","glob"]}
|
package/dist/lib/spec/builder.js
CHANGED
|
@@ -1,7 +1,104 @@
|
|
|
1
|
+
// src/spec/schema.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var sectionIdSchema = z.enum([
|
|
4
|
+
"hero",
|
|
5
|
+
"projects",
|
|
6
|
+
"skills",
|
|
7
|
+
"about",
|
|
8
|
+
"timeline",
|
|
9
|
+
"blog",
|
|
10
|
+
"metrics",
|
|
11
|
+
"contact"
|
|
12
|
+
]);
|
|
13
|
+
var engineTypeSchema = z.enum(["claude", "codex", "v0"]);
|
|
14
|
+
var deployPlatformSchema = z.enum(["cloudflare", "vercel", "local"]);
|
|
15
|
+
var styleConfigSchema = z.object({
|
|
16
|
+
theme: z.enum(["dark-minimal", "light-clean", "monochrome", "custom"]),
|
|
17
|
+
accentColor: z.string(),
|
|
18
|
+
font: z.string(),
|
|
19
|
+
animationLevel: z.enum(["none", "subtle", "moderate"])
|
|
20
|
+
});
|
|
21
|
+
var projectMetaSchema = z.object({
|
|
22
|
+
id: z.string(),
|
|
23
|
+
name: z.string(),
|
|
24
|
+
localPath: z.string(),
|
|
25
|
+
description: z.string(),
|
|
26
|
+
techStack: z.array(z.string()),
|
|
27
|
+
languages: z.record(z.number()),
|
|
28
|
+
firstCommitDate: z.string(),
|
|
29
|
+
lastCommitDate: z.string(),
|
|
30
|
+
totalCommits: z.number().int().nonnegative(),
|
|
31
|
+
remoteUrl: z.string().nullable(),
|
|
32
|
+
demoUrl: z.string().nullable(),
|
|
33
|
+
readmeContent: z.string().nullable(),
|
|
34
|
+
lastScannedCommit: z.string()
|
|
35
|
+
});
|
|
36
|
+
var projectCaseStudySchema = z.object({
|
|
37
|
+
featured: z.boolean(),
|
|
38
|
+
audience: z.string().nullable(),
|
|
39
|
+
problem: z.string().nullable(),
|
|
40
|
+
solution: z.string().nullable(),
|
|
41
|
+
impact: z.string().nullable(),
|
|
42
|
+
evidence: z.array(z.string()),
|
|
43
|
+
screenshots: z.array(z.string())
|
|
44
|
+
});
|
|
45
|
+
var ownerInfoSchema = z.object({
|
|
46
|
+
name: z.string(),
|
|
47
|
+
tagline: z.string(),
|
|
48
|
+
bio: z.union([z.literal("auto"), z.string()]),
|
|
49
|
+
photoUrl: z.string().nullable(),
|
|
50
|
+
social: z.object({
|
|
51
|
+
github: z.string().optional(),
|
|
52
|
+
twitter: z.string().optional(),
|
|
53
|
+
linkedin: z.string().optional(),
|
|
54
|
+
blog: z.string().optional(),
|
|
55
|
+
email: z.string().optional()
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
var projectEntrySchema = projectMetaSchema.extend({
|
|
59
|
+
included: z.boolean(),
|
|
60
|
+
overrideDescription: z.string().nullable(),
|
|
61
|
+
showSourceLink: z.boolean(),
|
|
62
|
+
role: z.enum(["solo", "lead", "contributor"]),
|
|
63
|
+
trackedProjectPaths: z.array(z.string()),
|
|
64
|
+
metrics: z.object({
|
|
65
|
+
users: z.string().optional(),
|
|
66
|
+
revenue: z.string().optional(),
|
|
67
|
+
downloads: z.string().optional(),
|
|
68
|
+
stars: z.number().int().optional(),
|
|
69
|
+
custom: z.record(z.string()).optional()
|
|
70
|
+
}),
|
|
71
|
+
caseStudy: projectCaseStudySchema,
|
|
72
|
+
children: z.array(projectMetaSchema).optional()
|
|
73
|
+
});
|
|
74
|
+
var deployConfigSchema = z.object({
|
|
75
|
+
platform: deployPlatformSchema,
|
|
76
|
+
projectName: z.string(),
|
|
77
|
+
customDomain: z.string().optional(),
|
|
78
|
+
url: z.string().optional()
|
|
79
|
+
});
|
|
80
|
+
var shipfolioSpecSchema = z.object({
|
|
81
|
+
version: z.string(),
|
|
82
|
+
generatedAt: z.string(),
|
|
83
|
+
engine: engineTypeSchema,
|
|
84
|
+
framework: z.literal("next"),
|
|
85
|
+
style: styleConfigSchema,
|
|
86
|
+
owner: ownerInfoSchema,
|
|
87
|
+
projects: z.array(projectEntrySchema),
|
|
88
|
+
sections: z.array(sectionIdSchema),
|
|
89
|
+
deploy: deployConfigSchema
|
|
90
|
+
});
|
|
91
|
+
var shipfolioConfigSchema = shipfolioSpecSchema.extend({
|
|
92
|
+
sitePath: z.string()
|
|
93
|
+
});
|
|
94
|
+
function parseShipfolioSpec(input) {
|
|
95
|
+
return shipfolioSpecSchema.parse(input);
|
|
96
|
+
}
|
|
97
|
+
|
|
1
98
|
// src/spec/builder.ts
|
|
2
99
|
function buildSpec(interview) {
|
|
3
|
-
return {
|
|
4
|
-
version: "1.
|
|
100
|
+
return parseShipfolioSpec({
|
|
101
|
+
version: "1.1.0",
|
|
5
102
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6
103
|
engine: interview.engine,
|
|
7
104
|
framework: "next",
|
|
@@ -14,7 +111,7 @@ function buildSpec(interview) {
|
|
|
14
111
|
projectName: interview.deploy.projectName,
|
|
15
112
|
customDomain: interview.deploy.customDomain
|
|
16
113
|
}
|
|
17
|
-
};
|
|
114
|
+
});
|
|
18
115
|
}
|
|
19
116
|
export {
|
|
20
117
|
buildSpec
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/spec/builder.ts"],"sourcesContent":["import type { ShipfolioSpec } from \"./schema.js\";\nimport type { InterviewResult } from \"../interviewer/index.js\";\n\nexport function buildSpec(interview: InterviewResult): ShipfolioSpec {\n return {\n version: \"1.
|
|
1
|
+
{"version":3,"sources":["../../../src/spec/schema.ts","../../../src/spec/builder.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const sectionIdSchema = z.enum([\n \"hero\",\n \"projects\",\n \"skills\",\n \"about\",\n \"timeline\",\n \"blog\",\n \"metrics\",\n \"contact\",\n]);\nexport type SectionId = z.infer<typeof sectionIdSchema>;\n\nexport const engineTypeSchema = z.enum([\"claude\", \"codex\", \"v0\"]);\nexport type EngineType = z.infer<typeof engineTypeSchema>;\n\nexport const deployPlatformSchema = z.enum([\"cloudflare\", \"vercel\", \"local\"]);\nexport type DeployPlatform = z.infer<typeof deployPlatformSchema>;\n\nexport const styleConfigSchema = z.object({\n theme: z.enum([\"dark-minimal\", \"light-clean\", \"monochrome\", \"custom\"]),\n accentColor: z.string(),\n font: z.string(),\n animationLevel: z.enum([\"none\", \"subtle\", \"moderate\"]),\n});\nexport type StyleConfig = z.infer<typeof styleConfigSchema>;\n\nexport const projectMetaSchema = z.object({\n id: z.string(),\n name: z.string(),\n localPath: z.string(),\n description: z.string(),\n techStack: z.array(z.string()),\n languages: z.record(z.number()),\n firstCommitDate: z.string(),\n lastCommitDate: z.string(),\n totalCommits: z.number().int().nonnegative(),\n remoteUrl: z.string().nullable(),\n demoUrl: z.string().nullable(),\n readmeContent: z.string().nullable(),\n lastScannedCommit: z.string(),\n});\nexport type ProjectMeta = z.infer<typeof projectMetaSchema>;\n\nexport const projectCaseStudySchema = z.object({\n featured: z.boolean(),\n audience: z.string().nullable(),\n problem: z.string().nullable(),\n solution: z.string().nullable(),\n impact: z.string().nullable(),\n evidence: z.array(z.string()),\n screenshots: z.array(z.string()),\n});\nexport type ProjectCaseStudy = z.infer<typeof projectCaseStudySchema>;\n\nexport const ownerInfoSchema = z.object({\n name: z.string(),\n tagline: z.string(),\n bio: z.union([z.literal(\"auto\"), z.string()]),\n photoUrl: z.string().nullable(),\n social: z.object({\n github: z.string().optional(),\n twitter: z.string().optional(),\n linkedin: z.string().optional(),\n blog: z.string().optional(),\n email: z.string().optional(),\n }),\n});\nexport type OwnerInfo = z.infer<typeof ownerInfoSchema>;\n\nexport const projectEntrySchema: z.ZodType<{\n id: string;\n name: string;\n localPath: string;\n description: string;\n techStack: string[];\n languages: Record<string, number>;\n firstCommitDate: string;\n lastCommitDate: string;\n totalCommits: number;\n remoteUrl: string | null;\n demoUrl: string | null;\n readmeContent: string | null;\n lastScannedCommit: string;\n included: boolean;\n overrideDescription: string | null;\n showSourceLink: boolean;\n role: \"solo\" | \"lead\" | \"contributor\";\n trackedProjectPaths: string[];\n metrics: {\n users?: string | undefined;\n revenue?: string | undefined;\n downloads?: string | undefined;\n stars?: number | undefined;\n custom?: Record<string, string> | undefined;\n };\n caseStudy: ProjectCaseStudy;\n children?: ProjectMeta[] | undefined;\n}> = projectMetaSchema.extend({\n included: z.boolean(),\n overrideDescription: z.string().nullable(),\n showSourceLink: z.boolean(),\n role: z.enum([\"solo\", \"lead\", \"contributor\"]),\n trackedProjectPaths: z.array(z.string()),\n metrics: z.object({\n users: z.string().optional(),\n revenue: z.string().optional(),\n downloads: z.string().optional(),\n stars: z.number().int().optional(),\n custom: z.record(z.string()).optional(),\n }),\n caseStudy: projectCaseStudySchema,\n children: z.array(projectMetaSchema).optional(),\n});\nexport type ProjectEntry = z.infer<typeof projectEntrySchema>;\n\nexport const deployConfigSchema = z.object({\n platform: deployPlatformSchema,\n projectName: z.string(),\n customDomain: z.string().optional(),\n url: z.string().optional(),\n});\nexport type DeployConfig = z.infer<typeof deployConfigSchema>;\n\nexport const shipfolioSpecSchema = z.object({\n version: z.string(),\n generatedAt: z.string(),\n engine: engineTypeSchema,\n framework: z.literal(\"next\"),\n style: styleConfigSchema,\n owner: ownerInfoSchema,\n projects: z.array(projectEntrySchema),\n sections: z.array(sectionIdSchema),\n deploy: deployConfigSchema,\n});\nexport type ShipfolioSpec = z.infer<typeof shipfolioSpecSchema>;\n\nexport const shipfolioConfigSchema = shipfolioSpecSchema.extend({\n sitePath: z.string(),\n});\nexport type ShipfolioConfig = z.infer<typeof shipfolioConfigSchema>;\n\nexport function parseShipfolioSpec(input: unknown): ShipfolioSpec {\n return shipfolioSpecSchema.parse(input);\n}\n\nexport function parseShipfolioConfig(input: unknown): ShipfolioConfig {\n return shipfolioConfigSchema.parse(input);\n}\n\nexport interface ProjectUpdate {\n project: ProjectEntry;\n newCommits: number;\n readmeChanged: boolean;\n depsChanged: boolean;\n changedPaths: string[];\n removedPaths: string[];\n}\n\nexport interface SiteDiff {\n newProjects: ProjectMeta[];\n updatedProjects: ProjectUpdate[];\n removedProjects: ProjectEntry[];\n unchangedProjects: ProjectEntry[];\n}\n","import { parseShipfolioSpec, type ShipfolioSpec } from \"./schema.js\";\nimport type { InterviewResult } from \"../interviewer/index.js\";\n\nexport function buildSpec(interview: InterviewResult): ShipfolioSpec {\n return parseShipfolioSpec({\n version: \"1.1.0\",\n generatedAt: new Date().toISOString(),\n engine: interview.engine,\n framework: \"next\",\n style: interview.style,\n owner: interview.owner,\n projects: interview.projects,\n sections: interview.sections,\n deploy: {\n platform: interview.deploy.platform,\n projectName: interview.deploy.projectName,\n customDomain: interview.deploy.customDomain,\n },\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/lib/spec/diff.js
CHANGED
|
@@ -1,32 +1,196 @@
|
|
|
1
|
+
// src/spec/project-utils.ts
|
|
2
|
+
function slugify(value) {
|
|
3
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
4
|
+
}
|
|
5
|
+
function uniqueStrings(values) {
|
|
6
|
+
return [...new Set(values.filter(Boolean))];
|
|
7
|
+
}
|
|
8
|
+
function defaultCaseStudy(featured = false) {
|
|
9
|
+
return {
|
|
10
|
+
featured,
|
|
11
|
+
audience: null,
|
|
12
|
+
problem: null,
|
|
13
|
+
solution: null,
|
|
14
|
+
impact: null,
|
|
15
|
+
evidence: [],
|
|
16
|
+
screenshots: []
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function buildTrackingSignature(projects) {
|
|
20
|
+
return projects.map((project) => `${project.localPath}:${project.lastScannedCommit || "none"}`).sort().join("|");
|
|
21
|
+
}
|
|
22
|
+
function buildMergedProjectMeta(projects, name) {
|
|
23
|
+
const techStack = uniqueStrings(projects.flatMap((project) => project.techStack));
|
|
24
|
+
const languages = {};
|
|
25
|
+
for (const project of projects) {
|
|
26
|
+
for (const [lang, count] of Object.entries(project.languages)) {
|
|
27
|
+
languages[lang] = (languages[lang] || 0) + count;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const firstDates = projects.map((project) => project.firstCommitDate).filter(Boolean).sort();
|
|
31
|
+
const lastDates = projects.map((project) => project.lastCommitDate).filter(Boolean).sort();
|
|
32
|
+
const totalCommits = projects.reduce(
|
|
33
|
+
(sum, project) => sum + project.totalCommits,
|
|
34
|
+
0
|
|
35
|
+
);
|
|
36
|
+
const remoteUrl = projects.find((project) => project.remoteUrl)?.remoteUrl || null;
|
|
37
|
+
const demoUrl = projects.find((project) => project.demoUrl)?.demoUrl || null;
|
|
38
|
+
const readmeParts = projects.filter((project) => project.readmeContent).map((project) => `--- ${project.name} ---
|
|
39
|
+
${project.readmeContent}`);
|
|
40
|
+
const readmeContent = readmeParts.length > 0 ? readmeParts.join("\n\n") : null;
|
|
41
|
+
const descriptions = projects.map((project) => project.description).filter(Boolean);
|
|
42
|
+
return {
|
|
43
|
+
id: slugify(name),
|
|
44
|
+
name,
|
|
45
|
+
localPath: projects[0]?.localPath || "",
|
|
46
|
+
description: descriptions.join(" | "),
|
|
47
|
+
techStack,
|
|
48
|
+
languages,
|
|
49
|
+
firstCommitDate: firstDates[0] || "",
|
|
50
|
+
lastCommitDate: lastDates[lastDates.length - 1] || "",
|
|
51
|
+
totalCommits,
|
|
52
|
+
remoteUrl,
|
|
53
|
+
demoUrl,
|
|
54
|
+
readmeContent,
|
|
55
|
+
lastScannedCommit: buildTrackingSignature(projects)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function getTrackedProjectPaths(project) {
|
|
59
|
+
if (project.trackedProjectPaths && project.trackedProjectPaths.length > 0) {
|
|
60
|
+
return uniqueStrings(project.trackedProjectPaths);
|
|
61
|
+
}
|
|
62
|
+
if (project.children && project.children.length > 0) {
|
|
63
|
+
return uniqueStrings(project.children.map((child) => child.localPath));
|
|
64
|
+
}
|
|
65
|
+
return project.localPath ? [project.localPath] : [];
|
|
66
|
+
}
|
|
67
|
+
function createProjectEntry(meta, overrides = {}) {
|
|
68
|
+
const baseMetrics = overrides.metrics || {};
|
|
69
|
+
const baseCaseStudy = overrides.caseStudy ? {
|
|
70
|
+
...defaultCaseStudy(overrides.caseStudy.featured),
|
|
71
|
+
...overrides.caseStudy,
|
|
72
|
+
evidence: overrides.caseStudy.evidence || [],
|
|
73
|
+
screenshots: overrides.caseStudy.screenshots || []
|
|
74
|
+
} : defaultCaseStudy(false);
|
|
75
|
+
return {
|
|
76
|
+
...meta,
|
|
77
|
+
included: overrides.included ?? true,
|
|
78
|
+
overrideDescription: overrides.overrideDescription ?? null,
|
|
79
|
+
showSourceLink: overrides.showSourceLink ?? !!meta.remoteUrl,
|
|
80
|
+
role: overrides.role ?? "solo",
|
|
81
|
+
trackedProjectPaths: overrides.trackedProjectPaths && overrides.trackedProjectPaths.length > 0 ? uniqueStrings(overrides.trackedProjectPaths) : [meta.localPath],
|
|
82
|
+
metrics: {
|
|
83
|
+
...baseMetrics,
|
|
84
|
+
custom: baseMetrics.custom || void 0
|
|
85
|
+
},
|
|
86
|
+
caseStudy: baseCaseStudy,
|
|
87
|
+
children: overrides.children
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
1
91
|
// src/spec/diff.ts
|
|
2
92
|
function computeDiff(oldConfig, newScan) {
|
|
3
|
-
const oldProjectMap = new Map(
|
|
4
|
-
oldConfig.projects.map((p) => [p.localPath, p])
|
|
5
|
-
);
|
|
6
93
|
const newProjectMap = new Map(newScan.map((p) => [p.localPath, p]));
|
|
94
|
+
const trackedOldPaths = /* @__PURE__ */ new Set();
|
|
7
95
|
const newProjects = [];
|
|
8
96
|
const updatedProjects = [];
|
|
9
97
|
const removedProjects = [];
|
|
10
98
|
const unchangedProjects = [];
|
|
11
|
-
for (const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
99
|
+
for (const oldProject of oldConfig.projects) {
|
|
100
|
+
const trackedPaths = getTrackedProjectPaths(oldProject);
|
|
101
|
+
for (const trackedPath of trackedPaths) {
|
|
102
|
+
trackedOldPaths.add(trackedPath);
|
|
103
|
+
}
|
|
104
|
+
const currentMatches = trackedPaths.map((path) => newProjectMap.get(path)).filter((project) => Boolean(project));
|
|
105
|
+
const removedPaths = trackedPaths.filter((path) => !newProjectMap.has(path));
|
|
106
|
+
if (currentMatches.length === 0) {
|
|
107
|
+
removedProjects.push(oldProject);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (trackedPaths.length > 1) {
|
|
111
|
+
const mergedMeta = buildMergedProjectMeta(currentMatches, oldProject.name);
|
|
112
|
+
const oldChildrenByPath = new Map(
|
|
113
|
+
(oldProject.children || []).map((child) => [child.localPath, child])
|
|
114
|
+
);
|
|
115
|
+
const oldTrackingSignature = oldProject.children && oldProject.children.length > 0 ? buildTrackingSignature(oldProject.children) : oldProject.lastScannedCommit;
|
|
116
|
+
let readmeChanged = removedPaths.length > 0;
|
|
117
|
+
let depsChanged = removedPaths.length > 0;
|
|
118
|
+
let newCommits = 0;
|
|
119
|
+
for (const currentProject2 of currentMatches) {
|
|
120
|
+
const oldChild = oldChildrenByPath.get(currentProject2.localPath);
|
|
121
|
+
if (!oldChild) {
|
|
122
|
+
readmeChanged = true;
|
|
123
|
+
depsChanged = true;
|
|
124
|
+
newCommits += currentProject2.totalCommits;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (currentProject2.readmeContent !== oldChild.readmeContent) {
|
|
128
|
+
readmeChanged = true;
|
|
129
|
+
}
|
|
130
|
+
if (JSON.stringify(currentProject2.techStack) !== JSON.stringify(oldChild.techStack)) {
|
|
131
|
+
depsChanged = true;
|
|
132
|
+
}
|
|
133
|
+
newCommits += Math.max(
|
|
134
|
+
currentProject2.totalCommits - oldChild.totalCommits,
|
|
135
|
+
0
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
const changedPaths = currentMatches.filter((currentProject2) => {
|
|
139
|
+
const oldChild = oldChildrenByPath.get(currentProject2.localPath);
|
|
140
|
+
if (!oldChild) return true;
|
|
141
|
+
return currentProject2.lastScannedCommit !== oldChild.lastScannedCommit;
|
|
142
|
+
}).map((project) => project.localPath);
|
|
143
|
+
const hasUpdate = removedPaths.length > 0 || trackedPaths.length !== currentMatches.length || mergedMeta.lastScannedCommit !== oldTrackingSignature || readmeChanged || depsChanged;
|
|
144
|
+
if (hasUpdate) {
|
|
145
|
+
updatedProjects.push({
|
|
146
|
+
project: createProjectEntry(mergedMeta, {
|
|
147
|
+
included: oldProject.included,
|
|
148
|
+
overrideDescription: oldProject.overrideDescription,
|
|
149
|
+
showSourceLink: oldProject.showSourceLink,
|
|
150
|
+
role: oldProject.role,
|
|
151
|
+
metrics: oldProject.metrics,
|
|
152
|
+
caseStudy: oldProject.caseStudy,
|
|
153
|
+
trackedProjectPaths: currentMatches.map((project) => project.localPath),
|
|
154
|
+
children: currentMatches
|
|
155
|
+
}),
|
|
156
|
+
newCommits,
|
|
157
|
+
readmeChanged,
|
|
158
|
+
depsChanged,
|
|
159
|
+
changedPaths,
|
|
160
|
+
removedPaths
|
|
161
|
+
});
|
|
162
|
+
} else {
|
|
163
|
+
unchangedProjects.push(oldProject);
|
|
164
|
+
}
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const currentProject = currentMatches[0];
|
|
168
|
+
if (currentProject.lastScannedCommit !== oldProject.lastScannedCommit) {
|
|
169
|
+
const newCommits = currentProject.totalCommits - oldProject.totalCommits;
|
|
17
170
|
updatedProjects.push({
|
|
18
|
-
project:
|
|
171
|
+
project: createProjectEntry(currentProject, {
|
|
172
|
+
included: oldProject.included,
|
|
173
|
+
overrideDescription: oldProject.overrideDescription,
|
|
174
|
+
showSourceLink: oldProject.showSourceLink,
|
|
175
|
+
role: oldProject.role,
|
|
176
|
+
metrics: oldProject.metrics,
|
|
177
|
+
caseStudy: oldProject.caseStudy,
|
|
178
|
+
trackedProjectPaths: trackedPaths,
|
|
179
|
+
children: oldProject.children
|
|
180
|
+
}),
|
|
19
181
|
newCommits: Math.max(newCommits, 0),
|
|
20
|
-
readmeChanged:
|
|
21
|
-
depsChanged: JSON.stringify(
|
|
182
|
+
readmeChanged: currentProject.readmeContent !== oldProject.readmeContent,
|
|
183
|
+
depsChanged: JSON.stringify(currentProject.techStack) !== JSON.stringify(oldProject.techStack),
|
|
184
|
+
changedPaths: [currentProject.localPath],
|
|
185
|
+
removedPaths
|
|
22
186
|
});
|
|
23
187
|
} else {
|
|
24
188
|
unchangedProjects.push(oldProject);
|
|
25
189
|
}
|
|
26
190
|
}
|
|
27
|
-
for (const [path,
|
|
28
|
-
if (!
|
|
29
|
-
|
|
191
|
+
for (const [path, meta] of newProjectMap) {
|
|
192
|
+
if (!trackedOldPaths.has(path)) {
|
|
193
|
+
newProjects.push(meta);
|
|
30
194
|
}
|
|
31
195
|
}
|
|
32
196
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/spec/diff.ts"],"sourcesContent":["import type {\n ProjectMeta,\n ProjectEntry,\n ShipfolioConfig,\n SiteDiff,\n ProjectUpdate,\n} from \"./schema.js\";\n\nexport function computeDiff(\n oldConfig: ShipfolioConfig,\n newScan: ProjectMeta[]\n): SiteDiff {\n const oldProjectMap = new Map(\n oldConfig.projects.map((p) => [p.localPath, p])\n );\n const newProjectMap = new Map(newScan.map((p) => [p.localPath, p]));\n\n const newProjects: ProjectMeta[] = [];\n const updatedProjects: ProjectUpdate[] = [];\n const removedProjects: ProjectEntry[] = [];\n const unchangedProjects: ProjectEntry[] = [];\n\n // Check new and updated\n for (const [path, meta] of newProjectMap) {\n const oldProject = oldProjectMap.get(path);\n if (!oldProject) {\n newProjects.push(meta);\n } else if (meta.lastScannedCommit !== oldProject.lastScannedCommit) {\n const newCommits = meta.totalCommits - oldProject.totalCommits;\n updatedProjects.push({\n project: meta,\n newCommits: Math.max(newCommits, 0),\n readmeChanged: meta.readmeContent !== oldProject.readmeContent,\n depsChanged:\n JSON.stringify(meta.techStack) !==\n JSON.stringify(oldProject.techStack),\n });\n } else {\n unchangedProjects.push(oldProject);\n }\n }\n\n // Check removed\n for (const [path, project] of oldProjectMap) {\n if (!newProjectMap.has(path)) {\n removedProjects.push(project);\n }\n }\n\n return {\n newProjects,\n updatedProjects,\n removedProjects,\n unchangedProjects,\n };\n}\n"],"mappings":";AAQO,SAAS,YACd,WACA,SACU;AACV,QAAM,gBAAgB,IAAI;AAAA,IACxB,UAAU,SAAS,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;AAAA,EAChD;AACA,QAAM,gBAAgB,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;AAElE,QAAM,cAA6B,CAAC;AACpC,QAAM,kBAAmC,CAAC;AAC1C,QAAM,kBAAkC,CAAC;AACzC,QAAM,oBAAoC,CAAC;AAG3C,aAAW,CAAC,MAAM,IAAI,KAAK,eAAe;AACxC,UAAM,aAAa,cAAc,IAAI,IAAI;AACzC,QAAI,CAAC,YAAY;AACf,kBAAY,KAAK,IAAI;AAAA,IACvB,WAAW,KAAK,sBAAsB,WAAW,mBAAmB;AAClE,YAAM,aAAa,KAAK,eAAe,WAAW;AAClD,sBAAgB,KAAK;AAAA,QACnB,SAAS;AAAA,QACT,YAAY,KAAK,IAAI,YAAY,CAAC;AAAA,QAClC,eAAe,KAAK,kBAAkB,WAAW;AAAA,QACjD,aACE,KAAK,UAAU,KAAK,SAAS,MAC7B,KAAK,UAAU,WAAW,SAAS;AAAA,MACvC,CAAC;AAAA,IACH,OAAO;AACL,wBAAkB,KAAK,UAAU;AAAA,IACnC;AAAA,EACF;AAGA,aAAW,CAAC,MAAM,OAAO,KAAK,eAAe;AAC3C,QAAI,CAAC,cAAc,IAAI,IAAI,GAAG;AAC5B,sBAAgB,KAAK,OAAO;AAAA,IAC9B;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/spec/project-utils.ts","../../../src/spec/diff.ts"],"sourcesContent":["import type {\n ProjectCaseStudy,\n ProjectEntry,\n ProjectMeta,\n} from \"./schema.js\";\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction uniqueStrings(values: string[]): string[] {\n return [...new Set(values.filter(Boolean))];\n}\n\nexport function defaultCaseStudy(featured = false): ProjectCaseStudy {\n return {\n featured,\n audience: null,\n problem: null,\n solution: null,\n impact: null,\n evidence: [],\n screenshots: [],\n };\n}\n\nexport function buildTrackingSignature(projects: ProjectMeta[]): string {\n return projects\n .map((project) => `${project.localPath}:${project.lastScannedCommit || \"none\"}`)\n .sort()\n .join(\"|\");\n}\n\nexport function buildMergedProjectMeta(\n projects: ProjectMeta[],\n name: string\n): ProjectMeta {\n const techStack = uniqueStrings(projects.flatMap((project) => project.techStack));\n\n const languages: Record<string, number> = {};\n for (const project of projects) {\n for (const [lang, count] of Object.entries(project.languages)) {\n languages[lang] = (languages[lang] || 0) + count;\n }\n }\n\n const firstDates = projects\n .map((project) => project.firstCommitDate)\n .filter(Boolean)\n .sort();\n const lastDates = projects\n .map((project) => project.lastCommitDate)\n .filter(Boolean)\n .sort();\n\n const totalCommits = projects.reduce(\n (sum, project) => sum + project.totalCommits,\n 0\n );\n\n const remoteUrl =\n projects.find((project) => project.remoteUrl)?.remoteUrl || null;\n const demoUrl =\n projects.find((project) => project.demoUrl)?.demoUrl || null;\n\n const readmeParts = projects\n .filter((project) => project.readmeContent)\n .map((project) => `--- ${project.name} ---\\n${project.readmeContent}`);\n const readmeContent = readmeParts.length > 0 ? readmeParts.join(\"\\n\\n\") : null;\n\n const descriptions = projects\n .map((project) => project.description)\n .filter(Boolean);\n\n return {\n id: slugify(name),\n name,\n localPath: projects[0]?.localPath || \"\",\n description: descriptions.join(\" | \"),\n techStack,\n languages,\n firstCommitDate: firstDates[0] || \"\",\n lastCommitDate: lastDates[lastDates.length - 1] || \"\",\n totalCommits,\n remoteUrl,\n demoUrl,\n readmeContent,\n lastScannedCommit: buildTrackingSignature(projects),\n };\n}\n\nexport function getTrackedProjectPaths(\n project: Pick<ProjectMeta, \"localPath\"> & {\n trackedProjectPaths?: string[];\n children?: ProjectMeta[];\n }\n): string[] {\n if (project.trackedProjectPaths && project.trackedProjectPaths.length > 0) {\n return uniqueStrings(project.trackedProjectPaths);\n }\n if (project.children && project.children.length > 0) {\n return uniqueStrings(project.children.map((child) => child.localPath));\n }\n return project.localPath ? [project.localPath] : [];\n}\n\nexport function createProjectEntry(\n meta: ProjectMeta,\n overrides: Partial<ProjectEntry> = {}\n): ProjectEntry {\n const baseMetrics = overrides.metrics || {};\n const baseCaseStudy = overrides.caseStudy\n ? {\n ...defaultCaseStudy(overrides.caseStudy.featured),\n ...overrides.caseStudy,\n evidence: overrides.caseStudy.evidence || [],\n screenshots: overrides.caseStudy.screenshots || [],\n }\n : defaultCaseStudy(false);\n\n return {\n ...meta,\n included: overrides.included ?? true,\n overrideDescription: overrides.overrideDescription ?? null,\n showSourceLink: overrides.showSourceLink ?? !!meta.remoteUrl,\n role: overrides.role ?? \"solo\",\n trackedProjectPaths:\n overrides.trackedProjectPaths && overrides.trackedProjectPaths.length > 0\n ? uniqueStrings(overrides.trackedProjectPaths)\n : [meta.localPath],\n metrics: {\n ...baseMetrics,\n custom: baseMetrics.custom || undefined,\n },\n caseStudy: baseCaseStudy,\n children: overrides.children,\n };\n}\n","import type {\n ProjectMeta,\n ProjectEntry,\n ShipfolioConfig,\n SiteDiff,\n ProjectUpdate,\n} from \"./schema.js\";\nimport {\n buildMergedProjectMeta,\n buildTrackingSignature,\n createProjectEntry,\n getTrackedProjectPaths,\n} from \"./project-utils.js\";\n\nexport function computeDiff(\n oldConfig: ShipfolioConfig,\n newScan: ProjectMeta[]\n): SiteDiff {\n const newProjectMap = new Map(newScan.map((p) => [p.localPath, p]));\n const trackedOldPaths = new Set<string>();\n\n const newProjects: ProjectMeta[] = [];\n const updatedProjects: ProjectUpdate[] = [];\n const removedProjects: ProjectEntry[] = [];\n const unchangedProjects: ProjectEntry[] = [];\n\n for (const oldProject of oldConfig.projects) {\n const trackedPaths = getTrackedProjectPaths(oldProject);\n for (const trackedPath of trackedPaths) {\n trackedOldPaths.add(trackedPath);\n }\n\n const currentMatches = trackedPaths\n .map((path) => newProjectMap.get(path))\n .filter((project): project is ProjectMeta => Boolean(project));\n const removedPaths = trackedPaths.filter((path) => !newProjectMap.has(path));\n\n if (currentMatches.length === 0) {\n removedProjects.push(oldProject);\n continue;\n }\n\n if (trackedPaths.length > 1) {\n const mergedMeta = buildMergedProjectMeta(currentMatches, oldProject.name);\n const oldChildrenByPath = new Map(\n (oldProject.children || []).map((child) => [child.localPath, child])\n );\n const oldTrackingSignature =\n oldProject.children && oldProject.children.length > 0\n ? buildTrackingSignature(oldProject.children)\n : oldProject.lastScannedCommit;\n\n let readmeChanged = removedPaths.length > 0;\n let depsChanged = removedPaths.length > 0;\n let newCommits = 0;\n\n for (const currentProject of currentMatches) {\n const oldChild = oldChildrenByPath.get(currentProject.localPath);\n if (!oldChild) {\n readmeChanged = true;\n depsChanged = true;\n newCommits += currentProject.totalCommits;\n continue;\n }\n\n if (currentProject.readmeContent !== oldChild.readmeContent) {\n readmeChanged = true;\n }\n if (\n JSON.stringify(currentProject.techStack) !==\n JSON.stringify(oldChild.techStack)\n ) {\n depsChanged = true;\n }\n newCommits += Math.max(\n currentProject.totalCommits - oldChild.totalCommits,\n 0\n );\n }\n\n const changedPaths = currentMatches\n .filter((currentProject) => {\n const oldChild = oldChildrenByPath.get(currentProject.localPath);\n if (!oldChild) return true;\n return currentProject.lastScannedCommit !== oldChild.lastScannedCommit;\n })\n .map((project) => project.localPath);\n\n const hasUpdate =\n removedPaths.length > 0 ||\n trackedPaths.length !== currentMatches.length ||\n mergedMeta.lastScannedCommit !== oldTrackingSignature ||\n readmeChanged ||\n depsChanged;\n\n if (hasUpdate) {\n updatedProjects.push({\n project: createProjectEntry(mergedMeta, {\n included: oldProject.included,\n overrideDescription: oldProject.overrideDescription,\n showSourceLink: oldProject.showSourceLink,\n role: oldProject.role,\n metrics: oldProject.metrics,\n caseStudy: oldProject.caseStudy,\n trackedProjectPaths: currentMatches.map((project) => project.localPath),\n children: currentMatches,\n }),\n newCommits,\n readmeChanged,\n depsChanged,\n changedPaths,\n removedPaths,\n });\n } else {\n unchangedProjects.push(oldProject);\n }\n\n continue;\n }\n\n const currentProject = currentMatches[0];\n if (currentProject.lastScannedCommit !== oldProject.lastScannedCommit) {\n const newCommits = currentProject.totalCommits - oldProject.totalCommits;\n updatedProjects.push({\n project: createProjectEntry(currentProject, {\n included: oldProject.included,\n overrideDescription: oldProject.overrideDescription,\n showSourceLink: oldProject.showSourceLink,\n role: oldProject.role,\n metrics: oldProject.metrics,\n caseStudy: oldProject.caseStudy,\n trackedProjectPaths: trackedPaths,\n children: oldProject.children,\n }),\n newCommits: Math.max(newCommits, 0),\n readmeChanged: currentProject.readmeContent !== oldProject.readmeContent,\n depsChanged:\n JSON.stringify(currentProject.techStack) !==\n JSON.stringify(oldProject.techStack),\n changedPaths: [currentProject.localPath],\n removedPaths,\n });\n } else {\n unchangedProjects.push(oldProject);\n }\n }\n\n for (const [path, meta] of newProjectMap) {\n if (!trackedOldPaths.has(path)) {\n newProjects.push(meta);\n }\n }\n\n return {\n newProjects,\n updatedProjects,\n removedProjects,\n unchangedProjects,\n };\n}\n"],"mappings":"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","names":["currentProject"]}
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shipfolio",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Generate and deploy your personal portfolio site from local projects using AI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"shipfolio": "
|
|
7
|
+
"shipfolio": "bin/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsup",
|
|
11
11
|
"dev": "tsup --watch",
|
|
12
|
-
"
|
|
12
|
+
"typecheck": "tsc --noEmit",
|
|
13
|
+
"prepublishOnly": "npm run typecheck && npm run build"
|
|
13
14
|
},
|
|
14
15
|
"files": [
|
|
15
16
|
"bin",
|
|
@@ -35,7 +36,8 @@
|
|
|
35
36
|
"ora": "^8.2.0",
|
|
36
37
|
"playwright": "^1.50.0",
|
|
37
38
|
"serve-handler": "^6.1.6",
|
|
38
|
-
"simple-git": "^3.27.0"
|
|
39
|
+
"simple-git": "^3.27.0",
|
|
40
|
+
"zod": "^3.25.76"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@types/node": "^22.12.0",
|
package/prompts/fresh-build.md
CHANGED
|
@@ -46,8 +46,20 @@ For each project in the spec:
|
|
|
46
46
|
- Write a 2-3 sentence narrative description based on the README content and tech stack
|
|
47
47
|
- Focus on what it does and why it matters
|
|
48
48
|
- If user provided an override description, use that instead
|
|
49
|
+
- If caseStudy fields are present, treat them as the highest-priority source of truth
|
|
49
50
|
- Maintain consistent voice across all descriptions
|
|
50
51
|
|
|
52
|
+
For featured projects:
|
|
53
|
+
- Present them more prominently than non-featured projects
|
|
54
|
+
- Use the problem, solution, impact, and evidence fields to shape richer case-study content
|
|
55
|
+
- Prefer concrete proof points over generic claims
|
|
56
|
+
|
|
57
|
+
General content rules:
|
|
58
|
+
- Do not fabricate blog posts, metrics, user numbers, screenshots, or timeline events that are not present in the spec
|
|
59
|
+
- If a section lacks real data, keep it concise and derive it only from the provided projects and owner info
|
|
60
|
+
- Use screenshot URLs only when they are provided in the spec
|
|
61
|
+
- Make the strongest 1-2 featured projects carry the page narrative; keep the rest compact
|
|
62
|
+
|
|
51
63
|
For the bio (if set to "auto"):
|
|
52
64
|
- Generate a professional, authentic bio based on the project portfolio
|
|
53
65
|
- Emphasize shipping velocity and breadth
|
|
@@ -88,6 +100,8 @@ src/components/project-grid.tsx
|
|
|
88
100
|
src/components/skills.tsx
|
|
89
101
|
src/components/about.tsx
|
|
90
102
|
src/components/timeline.tsx
|
|
103
|
+
src/components/blog.tsx
|
|
104
|
+
src/components/metrics.tsx
|
|
91
105
|
src/components/contact.tsx
|
|
92
106
|
src/components/navigation.tsx
|
|
93
107
|
src/components/footer.tsx
|
|
@@ -106,3 +120,4 @@ shipfolio.config.json
|
|
|
106
120
|
- `npm install && npm run build` must succeed without errors
|
|
107
121
|
- Do not use next/image (incompatible with static export) -- use standard <img> tags
|
|
108
122
|
- Do not use features that require a server (API routes, middleware, ISR)
|
|
123
|
+
- Do not set `typescript.ignoreBuildErrors` or bypass build failures
|
package/prompts/update.md
CHANGED
|
@@ -30,8 +30,10 @@ architecture, and any custom modifications the user has made.
|
|
|
30
30
|
4. For new projects: follow the exact same card format and component pattern
|
|
31
31
|
as existing project cards
|
|
32
32
|
5. Preserve all custom CSS, custom components, and manual edits
|
|
33
|
-
6. Update
|
|
34
|
-
|
|
33
|
+
6. Update the existing content source of truth used by the site. If the site already
|
|
34
|
+
has structured data files, modify those; otherwise make the smallest viable edits
|
|
35
|
+
to the current components
|
|
36
|
+
7. Update owner/profile data wherever the current site stores it
|
|
35
37
|
8. Update shipfolio.config.json with new timestamps and project list
|
|
36
38
|
9. If a new section type is needed, create it following the existing
|
|
37
39
|
component patterns and design tokens in the codebase
|
|
@@ -44,3 +46,4 @@ architecture, and any custom modifications the user has made.
|
|
|
44
46
|
- Do not break the build -- `npm run build` must succeed
|
|
45
47
|
- Do not add new dependencies unless absolutely necessary
|
|
46
48
|
- Keep the existing @media print styles working
|
|
49
|
+
- Do not set `typescript.ignoreBuildErrors` or bypass build failures
|