get-tbd 0.1.13 → 0.1.15
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 +47 -28
- package/dist/bin.mjs +410 -170
- package/dist/bin.mjs.map +1 -1
- package/dist/cli.mjs +202 -94
- package/dist/cli.mjs.map +1 -1
- package/dist/docs/README.md +47 -28
- package/dist/docs/SKILL.md +61 -18
- package/dist/docs/guidelines/bun-monorepo-patterns.md +2096 -0
- package/dist/docs/guidelines/cli-agent-skill-patterns.md +79 -5
- package/dist/docs/guidelines/error-handling-rules.md +66 -0
- package/dist/docs/guidelines/pnpm-monorepo-patterns.md +2868 -0
- package/dist/docs/guidelines/release-notes-guidelines.md +140 -0
- package/dist/docs/guidelines/{sync-troubleshooting.md → tbd-sync-troubleshooting.md} +1 -1
- package/dist/docs/guidelines/typescript-sorting-patterns.md +234 -0
- package/dist/docs/guidelines/typescript-yaml-handling-rules.md +195 -0
- package/dist/docs/install/claude-header.md +13 -6
- package/dist/docs/shortcuts/standard/agent-handoff.md +1 -0
- package/dist/docs/shortcuts/standard/checkout-third-party-repo.md +50 -0
- package/dist/docs/shortcuts/standard/{cleanup-all.md → code-cleanup-all.md} +3 -2
- package/dist/docs/shortcuts/standard/{cleanup-update-docstrings.md → code-cleanup-docstrings.md} +1 -0
- package/dist/docs/shortcuts/standard/{cleanup-remove-trivial-tests.md → code-cleanup-tests.md} +1 -0
- package/dist/docs/shortcuts/standard/{commit-code.md → code-review-and-commit.md} +1 -0
- package/dist/docs/shortcuts/standard/coding-spike.md +54 -0
- package/dist/docs/shortcuts/standard/create-or-update-pr-simple.md +1 -0
- package/dist/docs/shortcuts/standard/create-or-update-pr-with-validation-plan.md +1 -0
- package/dist/docs/shortcuts/standard/implement-beads.md +1 -0
- package/dist/docs/shortcuts/standard/merge-upstream.md +1 -0
- package/dist/docs/shortcuts/standard/new-architecture-doc.md +1 -0
- package/dist/docs/shortcuts/standard/new-guideline.md +8 -0
- package/dist/docs/shortcuts/standard/new-plan-spec.md +1 -0
- package/dist/docs/shortcuts/standard/new-research-brief.md +1 -0
- package/dist/docs/shortcuts/standard/new-shortcut.md +27 -1
- package/dist/docs/shortcuts/standard/new-validation-plan.md +1 -0
- package/dist/docs/shortcuts/standard/plan-implementation-with-beads.md +1 -0
- package/dist/docs/shortcuts/standard/precommit-process.md +1 -0
- package/dist/docs/shortcuts/standard/review-code-python.md +1 -0
- package/dist/docs/shortcuts/standard/review-code-typescript.md +1 -0
- package/dist/docs/shortcuts/standard/review-code.md +1 -0
- package/dist/docs/shortcuts/standard/review-github-pr.md +89 -17
- package/dist/docs/shortcuts/standard/revise-all-architecture-docs.md +1 -0
- package/dist/docs/shortcuts/standard/revise-architecture-doc.md +1 -0
- package/dist/docs/shortcuts/standard/setup-github-cli.md +1 -0
- package/dist/docs/shortcuts/standard/sync-failure-recovery.md +6 -53
- package/dist/docs/shortcuts/standard/update-specs-status.md +1 -0
- package/dist/docs/shortcuts/standard/welcome-user.md +2 -1
- package/dist/docs/shortcuts/system/skill-brief.md +1 -1
- package/dist/docs/shortcuts/system/skill.md +48 -12
- package/dist/docs/skill-brief.md +1 -1
- package/dist/docs/tbd-design.md +13 -1
- package/dist/index.d.mts +20 -6
- package/dist/index.mjs +2 -2
- package/dist/{src-BfhjLZXE.mjs → src-Ct16P2Ox.mjs} +154 -22
- package/dist/src-Ct16P2Ox.mjs.map +1 -0
- package/dist/tbd +410 -170
- package/package.json +1 -1
- package/dist/docs/guidelines/typescript-monorepo-patterns.md +0 -72
- package/dist/src-BfhjLZXE.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: TypeScript Monorepo Patterns
|
|
3
|
-
description: Modern patterns for TypeScript monorepo architecture
|
|
4
|
-
author: Joshua Levy (github.com/jlevy) with LLM assistance
|
|
5
|
-
---
|
|
6
|
-
# TypeScript Monorepo Patterns
|
|
7
|
-
|
|
8
|
-
## Recommended Stack
|
|
9
|
-
|
|
10
|
-
- **pnpm workspaces** for dependency management
|
|
11
|
-
- **tsdown** for building ESM/CJS dual outputs with TypeScript declarations
|
|
12
|
-
- **Changesets** for versioning and release automation
|
|
13
|
-
- **publint** for validating package publishability
|
|
14
|
-
- **lefthook** for fast local git hooks
|
|
15
|
-
|
|
16
|
-
## Key Principles
|
|
17
|
-
|
|
18
|
-
### Package Structure
|
|
19
|
-
|
|
20
|
-
- Use `packages/` directory from day one, even with a single package
|
|
21
|
-
- Structure for splitting: organize internal code (`core/`, `cli/`, `adapters/`)
|
|
22
|
-
- Scope package names: use `@org/package-name` format
|
|
23
|
-
|
|
24
|
-
### Build Configuration
|
|
25
|
-
|
|
26
|
-
- Use `moduleResolution: "Bundler"` with tsdown
|
|
27
|
-
- Configure dual format output (ESM `.js` and CJS `.cjs`)
|
|
28
|
-
- Generate separate declaration files for ESM (`.d.ts`) and CJS (`.d.cts`)
|
|
29
|
-
|
|
30
|
-
### Subpath Exports
|
|
31
|
-
|
|
32
|
-
Always define subpath exports in package.json:
|
|
33
|
-
|
|
34
|
-
```json
|
|
35
|
-
{
|
|
36
|
-
"exports": {
|
|
37
|
-
".": {
|
|
38
|
-
"import": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
|
|
39
|
-
"require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" }
|
|
40
|
-
},
|
|
41
|
-
"./cli": { ... }
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### Script Patterns
|
|
47
|
-
|
|
48
|
-
- `format` / `format:check`: Auto-format vs verify formatting
|
|
49
|
-
- `lint` / `lint:check`: Lint with fix vs verify only
|
|
50
|
-
- `build`: Run format then lint:check before building
|
|
51
|
-
|
|
52
|
-
### Pre-commit Hooks (lefthook)
|
|
53
|
-
|
|
54
|
-
Target 2-5 seconds total:
|
|
55
|
-
- Format staged files with Prettier (auto-stage fixed)
|
|
56
|
-
- Lint staged files with ESLint caching (auto-stage fixed)
|
|
57
|
-
- Type check with incremental mode
|
|
58
|
-
|
|
59
|
-
### CLI Development
|
|
60
|
-
|
|
61
|
-
Use the dual-script pattern:
|
|
62
|
-
- `cli-name`: Runs source via tsx (development)
|
|
63
|
-
- `cli-name:bin`: Runs built binary (verification)
|
|
64
|
-
|
|
65
|
-
## Best Practices
|
|
66
|
-
|
|
67
|
-
1. Types first in exports (always put `"types"` before `"default"`)
|
|
68
|
-
2. Optional peer deps for integrations (don’t force SDK dependencies)
|
|
69
|
-
3. Validate with publint before every release
|
|
70
|
-
4. Lock tooling versions in `packageManager` field
|
|
71
|
-
5. Keep root package.json private
|
|
72
|
-
6. Run CLI from source during development (no stale builds)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"src-BfhjLZXE.mjs","names":["parseYaml","stringifyYaml"],"sources":["../src/lib/schemas.ts","../src/utils/markdown-utils.ts","../src/file/parser.ts","../src/index.ts"],"sourcesContent":["/**\n * Zod schemas for tbd entities.\n *\n * These schemas are the normative specification for the file format.\n * See: tbd-design.md §2.6 Schemas\n */\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Common Types (§2.6.1)\n// =============================================================================\n\n/**\n * ISO8601 timestamp with Z suffix (UTC).\n */\nexport const Timestamp = z.string().datetime();\n\n/**\n * Issue ID: prefix + 26 lowercase alphanumeric characters (ULID format).\n * Format: is-{ulid} where ulid is 26 chars (a-z, 0-9).\n * Example: is-01hx5zzkbkactav9wevgemmvrz\n */\nexport const IssueId = z.string().regex(/^is-[0-9a-z]{26}$/);\n\n/**\n * Short ID: 1+ base36 characters used for external/display IDs.\n * Typically 4 chars for new IDs (e.g., a7k2, b3m9).\n * Imports may preserve longer numeric IDs (e.g., \"100\" from \"tbd-100\").\n */\nexport const ShortId = z.string().regex(/^[0-9a-z]+$/);\n\n/**\n * External Issue ID input: accepts {prefix}-{short} or just {short}.\n * Examples: bd-a7k2, a7k2, bd-100, 100\n */\nexport const ExternalIssueIdInput = z.string().regex(/^([a-z]+-)?[0-9a-z]+$/);\n\n/**\n * Edit counter - incremented on every local change.\n * NOTE: Version is NOT used for conflict detection (Git push rejection is used).\n * Content hash is used as tiebreaker during merge resolution.\n * Version is informational only - set to max(local, remote) + 1 after merges.\n */\nexport const Version = z.number().int().nonnegative();\n\n/**\n * Entity type discriminator.\n */\nexport const EntityType = z.literal('is');\n\n// =============================================================================\n// BaseEntity (§2.6.2)\n// =============================================================================\n\n/**\n * All entities share common fields.\n */\nexport const BaseEntity = z.object({\n type: EntityType,\n id: IssueId,\n version: Version,\n created_at: Timestamp,\n updated_at: Timestamp,\n\n // Extensibility namespace for third-party data\n extensions: z.record(z.string(), z.unknown()).optional(),\n});\n\n// =============================================================================\n// Issue Schema (§2.6.3)\n// =============================================================================\n\n/**\n * Issue status values matching Beads.\n */\nexport const IssueStatus = z.enum(['open', 'in_progress', 'blocked', 'deferred', 'closed']);\n\n/**\n * Issue kind/type values matching Beads.\n * Note: CLI uses --type flag, which maps to this `kind` field.\n */\nexport const IssueKind = z.enum(['bug', 'feature', 'task', 'epic', 'chore']);\n\n/**\n * Priority: 0 (highest/critical) to 4 (lowest).\n */\nexport const Priority = z.number().int().min(0).max(4);\n\n/**\n * Dependency types - only \"blocks\" supported initially.\n */\nexport const DependencyRelationType = z.enum(['blocks']);\n\n/**\n * A dependency relationship.\n */\nexport const Dependency = z.object({\n type: DependencyRelationType,\n target: IssueId,\n});\n\n/**\n * Full issue schema.\n *\n * Note: Fields use .nullable() in addition to .optional() because\n * YAML parses `field: null` as JavaScript null, not undefined.\n */\nexport const IssueSchema = BaseEntity.extend({\n type: z.literal('is'),\n\n title: z.string().min(1).max(500),\n description: z.string().max(50000).nullable().optional(),\n notes: z.string().max(50000).nullable().optional(),\n\n kind: IssueKind.default('task'),\n status: IssueStatus.default('open'),\n priority: Priority.default(2),\n\n assignee: z.string().nullable().optional(),\n labels: z.array(z.string()).default([]),\n dependencies: z.array(Dependency).default([]),\n\n // Hierarchical issues\n parent_id: IssueId.nullable().optional(),\n\n // Child ordering hints - soft ordering for children under this parent.\n // Array of internal IssueIds in preferred display order.\n // May contain stale IDs; display logic filters for actual children.\n child_order_hints: z.array(IssueId).nullable().optional(),\n\n // Beads compatibility\n due_date: Timestamp.nullable().optional(),\n deferred_until: Timestamp.nullable().optional(),\n\n created_by: z.string().nullable().optional(),\n closed_at: Timestamp.nullable().optional(),\n close_reason: z.string().nullable().optional(),\n\n // Spec linking - path to related spec/doc (relative to repo root)\n spec_path: z.string().nullable().optional(),\n});\n\n// =============================================================================\n// Config Schema (§2.6.4)\n// =============================================================================\n\n/**\n * Git branch name - restricted to safe characters.\n * Allows: alphanumeric, hyphens, underscores, forward slashes, and dots.\n * Prevents shell injection in git commands.\n */\nexport const GitBranchName = z\n .string()\n .min(1)\n .max(255)\n .regex(\n /^[a-zA-Z0-9._/-]+$/,\n 'Invalid branch name: only alphanumeric, dots, underscores, hyphens, and slashes allowed',\n );\n\n/**\n * Git remote name - restricted to safe characters.\n * Allows: alphanumeric, hyphens, underscores, and dots.\n * Prevents shell injection in git commands.\n */\nexport const GitRemoteName = z\n .string()\n .min(1)\n .max(255)\n .regex(\n /^[a-zA-Z0-9._-]+$/,\n 'Invalid remote name: only alphanumeric, dots, underscores, and hyphens allowed',\n );\n\n/**\n * Doc cache configuration - maps destination paths to source locations.\n *\n * Keys are destination paths relative to .tbd/docs/ (e.g., \"shortcuts/standard/commit-code.md\")\n * Values are source locations:\n * - internal: prefix for bundled docs (e.g., \"internal:shortcuts/standard/commit-code.md\")\n * - Full URL for external docs (e.g., \"https://raw.githubusercontent.com/org/repo/main/file.md\")\n *\n * Example:\n * ```yaml\n * doc_cache:\n * shortcuts/standard/commit-code.md: internal:shortcuts/standard/commit-code.md\n * shortcuts/custom/my-shortcut.md: https://raw.githubusercontent.com/org/repo/main/shortcuts/my-shortcut.md\n * ```\n */\nexport const DocCacheConfigSchema = z.record(z.string(), z.string());\n\n/**\n * Documentation cache configuration (consolidated structure).\n *\n * Combines file sync mappings and lookup paths into a single config block.\n * See: docs/project/specs/active/plan-2026-01-26-docs-cache-config-restructure.md\n */\nexport const DocsCacheSchema = z.object({\n /**\n * Files to sync: maps destination paths to source locations.\n * Keys are destination paths relative to .tbd/docs/\n * Values are source locations:\n * - internal: prefix for bundled docs (e.g., \"internal:shortcuts/standard/commit-code.md\")\n * - Full URL for external docs (e.g., \"https://raw.githubusercontent.com/org/repo/main/file.md\")\n */\n files: z.record(z.string(), z.string()).optional(),\n /**\n * Search paths for doc lookup (like shell $PATH).\n * Earlier paths take precedence when names conflict.\n */\n lookup_path: z\n .array(z.string())\n .default(['.tbd/docs/shortcuts/system', '.tbd/docs/shortcuts/standard']),\n});\n\n/**\n * Project configuration stored in .tbd/config.yml\n *\n * ⚠️ FORMAT VERSIONING: See tbd-format.ts for version history and migration rules.\n * The tbd_format field tracks breaking changes to this schema.\n *\n * ⚠️ FORWARD COMPATIBILITY POLICY:\n * This schema uses Zod's default strip() mode, which discards unknown fields.\n * To prevent data loss when users mix tbd versions:\n *\n * 1. **When changing config schema (adding, removing, or modifying fields), ALWAYS\n * bump the format version** (e.g., f03 → f04)\n * 2. Older tbd versions will error when they see an unknown format version\n * 3. The error message tells users to upgrade tbd\n *\n * This ensures older versions fail fast rather than silently corrupting config.\n * The format version check happens in config.ts via isCompatibleFormat().\n *\n * See tbd-format.ts for format version history and migration rules.\n */\nexport const ConfigSchema = z.object({\n /**\n * Format version for the .tbd/ directory structure.\n * See tbd-format.ts for version history and migration rules.\n * Only bumped for breaking changes that require migration.\n */\n tbd_format: z.string().default('f01'),\n\n tbd_version: z.string(),\n sync: z\n .object({\n branch: GitBranchName.default('tbd-sync'),\n remote: GitRemoteName.default('origin'),\n })\n .default({}),\n display: z.object({\n id_prefix: z.string().min(1).max(20), // Required: set during init --prefix or import\n }),\n settings: z\n .object({\n auto_sync: z.boolean().default(false),\n /**\n * How often to automatically sync documentation cache (in hours).\n * - Default: 24 (sync once per day when actively using tbd)\n * - Set to 0 to disable auto-sync\n * - Only triggers when accessing docs (shortcut, guidelines, template commands)\n */\n doc_auto_sync_hours: z.number().default(24),\n /**\n * Whether to install the ensure-gh-cli.sh hook script during setup.\n * When true (default), `tbd setup` installs a SessionStart hook that\n * ensures the GitHub CLI is available in agent sessions.\n * Set to false or use `tbd setup --no-gh-cli` to disable.\n */\n use_gh_cli: z.boolean().default(true),\n })\n .default({}),\n /**\n * Documentation cache configuration (consolidated).\n * Contains files to sync and lookup paths.\n * See DocsCacheSchema for structure details.\n */\n docs_cache: DocsCacheSchema.optional(),\n});\n\n// =============================================================================\n// Meta Schema (§2.6.5)\n// =============================================================================\n\n/**\n * Shared metadata stored in .tbd/data-sync/meta.yml\n */\nexport const MetaSchema = z.object({\n schema_version: z.number().int(),\n created_at: Timestamp,\n});\n\n// =============================================================================\n// Local State Schema (§2.6.6)\n// =============================================================================\n\n/**\n * Per-node state stored in .tbd/state.yml (gitignored).\n * Tracks local timing information that shouldn't be shared across nodes.\n */\nexport const LocalStateSchema = z.object({\n /** When this node last synced issues successfully */\n last_sync_at: Timestamp.optional(),\n /** When this node last synced the doc cache successfully */\n last_doc_sync_at: Timestamp.optional(),\n /** Whether the user has seen the welcome message */\n welcome_seen: z.boolean().optional(),\n});\n\n// =============================================================================\n// Attic Entry Schema (§2.6.7)\n// =============================================================================\n\n/**\n * Preserved conflict losers.\n */\nexport const AtticEntrySchema = z.object({\n entity_id: IssueId,\n timestamp: Timestamp,\n field: z.string(),\n lost_value: z.string(),\n winner_source: z.enum(['local', 'remote']),\n loser_source: z.enum(['local', 'remote']),\n context: z.object({\n local_version: Version,\n remote_version: Version,\n local_updated_at: Timestamp,\n remote_updated_at: Timestamp,\n }),\n});\n","/**\n * Markdown utilities for processing markdown content.\n *\n * Uses gray-matter for consistent frontmatter parsing across the codebase.\n */\n\nimport matter from 'gray-matter';\n\nexport interface ParsedMarkdown {\n /** Raw frontmatter string (without --- delimiters), or null if no frontmatter */\n frontmatter: string | null;\n /** Body content after frontmatter, with leading newlines trimmed */\n body: string;\n}\n\n/**\n * Normalize line endings to LF.\n */\nexport function normalizeLineEndings(content: string): string {\n return content.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n}\n\n/**\n * Parse markdown content into frontmatter and body.\n * Handles both LF and CRLF line endings.\n *\n * @returns Object with frontmatter (null if none) and body\n */\nexport function parseMarkdown(content: string): ParsedMarkdown {\n const normalized = normalizeLineEndings(content);\n\n if (!matter.test(normalized)) {\n return { frontmatter: null, body: content };\n }\n\n try {\n const parsed = matter(normalized);\n\n // Extract frontmatter from parsed.data by stringifying back to YAML\n // The matter property is unreliable, so we reconstruct from data\n const data = parsed.data;\n let frontmatter: string | null = null;\n\n if (data && Object.keys(data).length > 0) {\n // Reconstruct frontmatter as YAML lines\n const lines: string[] = [];\n for (const [key, value] of Object.entries(data)) {\n if (Array.isArray(value)) {\n lines.push(`${key}:`);\n for (const item of value) {\n lines.push(` - ${String(item)}`);\n }\n } else if (typeof value === 'object' && value !== null) {\n lines.push(`${key}:`);\n for (const [subKey, subValue] of Object.entries(value as Record<string, unknown>)) {\n lines.push(` ${subKey}: ${String(subValue)}`);\n }\n } else {\n lines.push(`${key}: ${String(value)}`);\n }\n }\n frontmatter = lines.join('\\n');\n } else {\n // Empty frontmatter (just --- followed by ---)\n frontmatter = '';\n }\n\n // Body with leading newlines trimmed\n const body = parsed.content.replace(/^\\n+/, '');\n\n return { frontmatter, body };\n } catch {\n // Invalid/unclosed frontmatter - treat as no frontmatter\n return { frontmatter: null, body: content };\n }\n}\n\n/**\n * Parse YAML frontmatter from markdown content.\n * Returns the frontmatter content (without delimiters) or null if no valid frontmatter.\n * Handles both LF and CRLF line endings.\n */\nexport function parseFrontmatter(content: string): string | null {\n return parseMarkdown(content).frontmatter;\n}\n\n/**\n * Strip YAML frontmatter from markdown content.\n * Returns the body content without frontmatter, with leading newlines trimmed.\n * Handles both LF and CRLF line endings.\n */\nexport function stripFrontmatter(content: string): string {\n return parseMarkdown(content).body;\n}\n\n/**\n * Insert content after YAML frontmatter.\n * If no frontmatter exists, prepends the content.\n * Content is inserted directly after ---. Include leading newlines in toInsert if needed.\n */\nexport function insertAfterFrontmatter(content: string, toInsert: string): string {\n const { frontmatter, body } = parseMarkdown(content);\n\n if (frontmatter === null) {\n return toInsert + content;\n }\n\n const frontmatterBlock = frontmatter ? `---\\n${frontmatter}\\n---` : '---\\n---';\n return `${frontmatterBlock}\\n${toInsert}\\n\\n${body}`;\n}\n","/**\n * YAML front matter parser and serializer for issue files.\n *\n * Issues are stored as Markdown files with YAML front matter:\n * ---\n * type: is\n * id: is-a1b2c3\n * ...\n * ---\n *\n * Description body here.\n *\n * ## Notes\n *\n * Working notes here.\n *\n * See: tbd-design.md §2.1 Markdown + YAML Front Matter Format\n */\n\nimport matter from 'gray-matter';\nimport { normalizeLineEndings } from '../utils/markdown-utils.js';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\n\nimport type { Issue } from '../lib/types.js';\nimport { IssueSchema } from '../lib/schemas.js';\n\n/**\n * gray-matter options using the 'yaml' package as engine.\n * This preserves date strings instead of converting them to Date objects.\n */\nexport const matterOptions = {\n engines: {\n yaml: {\n parse: (str: string): object => parseYaml(str) as object,\n stringify: (obj: object): string => stringifyYaml(obj),\n },\n },\n};\n\n/**\n * Parsed issue file content.\n */\nexport interface ParsedIssueFile {\n frontmatter: Record<string, unknown>;\n description: string;\n notes: string;\n}\n\n/**\n * Parse a Markdown file with YAML front matter.\n * Uses gray-matter for consistent frontmatter parsing.\n * Handles both LF and CRLF line endings.\n */\nexport function parseMarkdownWithFrontmatter(content: string): ParsedIssueFile {\n // Normalize CRLF to LF before parsing\n const normalizedContent = normalizeLineEndings(content);\n\n // Check for valid frontmatter\n if (!matter.test(normalizedContent)) {\n throw new Error('Invalid format: missing front matter opening delimiter');\n }\n\n const parsed = matter(normalizedContent, matterOptions);\n\n // gray-matter returns empty object if no closing delimiter found\n // but the raw matter string will be empty if parsing failed\n if (parsed.matter === '' && !normalizedContent.includes('---\\n---')) {\n // Check if there's actually a closing delimiter\n const lines = normalizedContent.split('\\n');\n let hasClosing = false;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i]?.trim() === '---') {\n hasClosing = true;\n break;\n }\n }\n if (!hasClosing) {\n throw new Error('Invalid format: missing front matter closing delimiter');\n }\n }\n\n const frontmatter = parsed.data as Record<string, unknown>;\n\n // Parse body - split into description and notes\n const body = parsed.content.trim();\n\n // Find notes section\n const notesMatch = /\\n## Notes\\n/i.exec(body);\n let description = body;\n let notes = '';\n\n if (notesMatch?.index !== undefined) {\n description = body.slice(0, notesMatch.index).trim();\n notes = body.slice(notesMatch.index + notesMatch[0].length).trim();\n }\n\n return { frontmatter, description, notes };\n}\n\n/**\n * Parse an issue from Markdown file content.\n */\nexport function parseIssue(content: string): Issue {\n const { frontmatter, description, notes } = parseMarkdownWithFrontmatter(content);\n\n // Merge body content into frontmatter\n const data = {\n ...frontmatter,\n description: description || undefined,\n notes: notes || undefined,\n };\n\n // Validate and parse with Zod\n return IssueSchema.parse(data);\n}\n\n/**\n * Serialize an issue to Markdown file content.\n * Uses canonical serialization for deterministic output.\n */\nexport function serializeIssue(issue: Issue): string {\n // Extract body fields\n const { description, notes, ...metadata } = issue;\n\n // Sort keys alphabetically for canonical output\n const sortedMetadata: Record<string, unknown> = {};\n for (const key of Object.keys(metadata).sort()) {\n sortedMetadata[key] = metadata[key as keyof typeof metadata];\n }\n\n // Serialize YAML with specific options for canonical output\n const yaml = stringifyYaml(sortedMetadata, {\n sortMapEntries: true,\n lineWidth: 0, // No wrapping\n nullStr: 'null',\n });\n\n // Build the file content\n // Note: No blank line between closing --- and body content\n const parts = ['---', yaml.trim(), '---'];\n\n if (description) {\n parts.push(description.trim());\n }\n\n if (notes) {\n parts.push('');\n parts.push('## Notes');\n parts.push('');\n parts.push(notes.trim());\n }\n\n // Single newline at end\n return parts.join('\\n') + '\\n';\n}\n","/**\n * tbd: Git-native issue tracking for AI agents and humans\n *\n * This is the library entry point. All exports here should be node-free\n * to support browser/edge runtime usage. CLI-specific code is in ./cli/.\n */\n\n// Version injected at build time\ndeclare const __TBD_VERSION__: string;\n\n/**\n * Package version, derived from git at build time.\n * Format: X.Y.Z for releases, X.Y.Z-dev.N.hash for dev builds.\n */\nexport const VERSION: string =\n typeof __TBD_VERSION__ !== 'undefined' ? __TBD_VERSION__ : 'development';\n\n// Re-export schemas for library consumers\nexport * from './lib/schemas.js';\nexport * from './lib/types.js';\n\n// Re-export core operations (these should be node-free)\nexport { parseIssue, serializeIssue } from './file/parser.js';\n"],"mappings":"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"}
|