get-tbd 0.1.8

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.
Files changed (60) hide show
  1. package/README.md +508 -0
  2. package/dist/bin-bootstrap.cjs +25 -0
  3. package/dist/bin-bootstrap.cjs.map +1 -0
  4. package/dist/bin.d.mts +2 -0
  5. package/dist/bin.mjs +106320 -0
  6. package/dist/bin.mjs.map +1 -0
  7. package/dist/cli.d.mts +13 -0
  8. package/dist/cli.mjs +9711 -0
  9. package/dist/cli.mjs.map +1 -0
  10. package/dist/docs/README.md +508 -0
  11. package/dist/docs/SKILL.md +222 -0
  12. package/dist/docs/guidelines/backward-compatibility-rules.md +78 -0
  13. package/dist/docs/guidelines/commit-conventions.md +78 -0
  14. package/dist/docs/guidelines/convex-limits-best-practices.md +170 -0
  15. package/dist/docs/guidelines/convex-rules.md +942 -0
  16. package/dist/docs/guidelines/general-coding-rules.md +36 -0
  17. package/dist/docs/guidelines/general-comment-rules.md +45 -0
  18. package/dist/docs/guidelines/general-eng-assistant-rules.md +54 -0
  19. package/dist/docs/guidelines/general-style-rules.md +37 -0
  20. package/dist/docs/guidelines/general-tdd-guidelines.md +52 -0
  21. package/dist/docs/guidelines/general-testing-rules.md +26 -0
  22. package/dist/docs/guidelines/golden-testing-guidelines.md +72 -0
  23. package/dist/docs/guidelines/python-cli-patterns.md +84 -0
  24. package/dist/docs/guidelines/python-rules.md +60 -0
  25. package/dist/docs/guidelines/typescript-cli-tool-rules.md +346 -0
  26. package/dist/docs/guidelines/typescript-code-coverage.md +171 -0
  27. package/dist/docs/guidelines/typescript-monorepo-patterns.md +71 -0
  28. package/dist/docs/guidelines/typescript-rules.md +55 -0
  29. package/dist/docs/install/claude-header.md +12 -0
  30. package/dist/docs/install/ensure-gh-cli.sh +88 -0
  31. package/dist/docs/shortcuts/standard/commit-code.md +23 -0
  32. package/dist/docs/shortcuts/standard/create-or-update-pr-simple.md +29 -0
  33. package/dist/docs/shortcuts/standard/create-or-update-pr-with-validation-plan.md +48 -0
  34. package/dist/docs/shortcuts/standard/implement-beads.md +31 -0
  35. package/dist/docs/shortcuts/standard/new-architecture-doc.md +31 -0
  36. package/dist/docs/shortcuts/standard/new-implementation-beads-from-spec.md +28 -0
  37. package/dist/docs/shortcuts/standard/new-plan-spec.md +49 -0
  38. package/dist/docs/shortcuts/standard/new-research-brief.md +30 -0
  39. package/dist/docs/shortcuts/standard/new-validation-plan.md +51 -0
  40. package/dist/docs/shortcuts/standard/precommit-process.md +88 -0
  41. package/dist/docs/shortcuts/standard/review-code-python.md +47 -0
  42. package/dist/docs/shortcuts/standard/review-code-typescript.md +46 -0
  43. package/dist/docs/shortcuts/standard/welcome-user.md +65 -0
  44. package/dist/docs/shortcuts/system/shortcut-explanation.md +61 -0
  45. package/dist/docs/shortcuts/system/skill-brief.md +40 -0
  46. package/dist/docs/shortcuts/system/skill.md +210 -0
  47. package/dist/docs/skill-brief.md +40 -0
  48. package/dist/docs/tbd-closing.md +31 -0
  49. package/dist/docs/tbd-design.md +5308 -0
  50. package/dist/docs/tbd-docs.md +1113 -0
  51. package/dist/docs/tbd-prime.md +119 -0
  52. package/dist/docs/templates/architecture-doc.md +117 -0
  53. package/dist/docs/templates/plan-spec.md +69 -0
  54. package/dist/docs/templates/research-brief.md +71 -0
  55. package/dist/index.d.mts +567 -0
  56. package/dist/index.mjs +3 -0
  57. package/dist/src-CWD4YCBk.mjs +319 -0
  58. package/dist/src-CWD4YCBk.mjs.map +1 -0
  59. package/dist/tbd +106320 -0
  60. package/package.json +92 -0
@@ -0,0 +1,319 @@
1
+ import { z } from "zod";
2
+ import matter from "gray-matter";
3
+ import { parse, stringify } from "yaml";
4
+
5
+ //#region src/lib/schemas.ts
6
+ /**
7
+ * Zod schemas for tbd entities.
8
+ *
9
+ * These schemas are the normative specification for the file format.
10
+ * See: tbd-design.md §2.6 Schemas
11
+ */
12
+ /**
13
+ * ISO8601 timestamp with Z suffix (UTC).
14
+ */
15
+ const Timestamp = z.string().datetime();
16
+ /**
17
+ * Issue ID: prefix + 26 lowercase alphanumeric characters (ULID format).
18
+ * Format: is-{ulid} where ulid is 26 chars (a-z, 0-9).
19
+ * Example: is-01hx5zzkbkactav9wevgemmvrz
20
+ */
21
+ const IssueId = z.string().regex(/^is-[0-9a-z]{26}$/);
22
+ /**
23
+ * Short ID: 1+ base36 characters used for external/display IDs.
24
+ * Typically 4 chars for new IDs (e.g., a7k2, b3m9).
25
+ * Imports may preserve longer numeric IDs (e.g., "100" from "tbd-100").
26
+ */
27
+ const ShortId = z.string().regex(/^[0-9a-z]+$/);
28
+ /**
29
+ * External Issue ID input: accepts {prefix}-{short} or just {short}.
30
+ * Examples: bd-a7k2, a7k2, bd-100, 100
31
+ */
32
+ const ExternalIssueIdInput = z.string().regex(/^([a-z]+-)?[0-9a-z]+$/);
33
+ /**
34
+ * Edit counter - incremented on every local change.
35
+ * NOTE: Version is NOT used for conflict detection (Git push rejection is used).
36
+ * Content hash is used as tiebreaker during merge resolution.
37
+ * Version is informational only - set to max(local, remote) + 1 after merges.
38
+ */
39
+ const Version = z.number().int().nonnegative();
40
+ /**
41
+ * Entity type discriminator.
42
+ */
43
+ const EntityType = z.literal("is");
44
+ /**
45
+ * All entities share common fields.
46
+ */
47
+ const BaseEntity = z.object({
48
+ type: EntityType,
49
+ id: IssueId,
50
+ version: Version,
51
+ created_at: Timestamp,
52
+ updated_at: Timestamp,
53
+ extensions: z.record(z.string(), z.unknown()).optional()
54
+ });
55
+ /**
56
+ * Issue status values matching Beads.
57
+ */
58
+ const IssueStatus = z.enum([
59
+ "open",
60
+ "in_progress",
61
+ "blocked",
62
+ "deferred",
63
+ "closed"
64
+ ]);
65
+ /**
66
+ * Issue kind/type values matching Beads.
67
+ * Note: CLI uses --type flag, which maps to this `kind` field.
68
+ */
69
+ const IssueKind = z.enum([
70
+ "bug",
71
+ "feature",
72
+ "task",
73
+ "epic",
74
+ "chore"
75
+ ]);
76
+ /**
77
+ * Priority: 0 (highest/critical) to 4 (lowest).
78
+ */
79
+ const Priority = z.number().int().min(0).max(4);
80
+ /**
81
+ * Dependency types - only "blocks" supported initially.
82
+ */
83
+ const DependencyRelationType = z.enum(["blocks"]);
84
+ /**
85
+ * A dependency relationship.
86
+ */
87
+ const Dependency = z.object({
88
+ type: DependencyRelationType,
89
+ target: IssueId
90
+ });
91
+ /**
92
+ * Full issue schema.
93
+ *
94
+ * Note: Fields use .nullable() in addition to .optional() because
95
+ * YAML parses `field: null` as JavaScript null, not undefined.
96
+ */
97
+ const IssueSchema = BaseEntity.extend({
98
+ type: z.literal("is"),
99
+ title: z.string().min(1).max(500),
100
+ description: z.string().max(5e4).nullable().optional(),
101
+ notes: z.string().max(5e4).nullable().optional(),
102
+ kind: IssueKind.default("task"),
103
+ status: IssueStatus.default("open"),
104
+ priority: Priority.default(2),
105
+ assignee: z.string().nullable().optional(),
106
+ labels: z.array(z.string()).default([]),
107
+ dependencies: z.array(Dependency).default([]),
108
+ parent_id: IssueId.nullable().optional(),
109
+ due_date: Timestamp.nullable().optional(),
110
+ deferred_until: Timestamp.nullable().optional(),
111
+ created_by: z.string().nullable().optional(),
112
+ closed_at: Timestamp.nullable().optional(),
113
+ close_reason: z.string().nullable().optional(),
114
+ spec_path: z.string().nullable().optional()
115
+ });
116
+ /**
117
+ * Git branch name - restricted to safe characters.
118
+ * Allows: alphanumeric, hyphens, underscores, forward slashes, and dots.
119
+ * Prevents shell injection in git commands.
120
+ */
121
+ const GitBranchName = z.string().min(1).max(255).regex(/^[a-zA-Z0-9._/-]+$/, "Invalid branch name: only alphanumeric, dots, underscores, hyphens, and slashes allowed");
122
+ /**
123
+ * Git remote name - restricted to safe characters.
124
+ * Allows: alphanumeric, hyphens, underscores, and dots.
125
+ * Prevents shell injection in git commands.
126
+ */
127
+ const GitRemoteName = z.string().min(1).max(255).regex(/^[a-zA-Z0-9._-]+$/, "Invalid remote name: only alphanumeric, dots, underscores, and hyphens allowed");
128
+ /**
129
+ * Doc cache configuration - maps destination paths to source locations.
130
+ *
131
+ * Keys are destination paths relative to .tbd/docs/ (e.g., "shortcuts/standard/commit-code.md")
132
+ * Values are source locations:
133
+ * - internal: prefix for bundled docs (e.g., "internal:shortcuts/standard/commit-code.md")
134
+ * - Full URL for external docs (e.g., "https://raw.githubusercontent.com/org/repo/main/file.md")
135
+ *
136
+ * Example:
137
+ * ```yaml
138
+ * doc_cache:
139
+ * shortcuts/standard/commit-code.md: internal:shortcuts/standard/commit-code.md
140
+ * shortcuts/custom/my-shortcut.md: https://raw.githubusercontent.com/org/repo/main/shortcuts/my-shortcut.md
141
+ * ```
142
+ */
143
+ const DocCacheConfigSchema = z.record(z.string(), z.string());
144
+ /**
145
+ * Documentation cache configuration (consolidated structure).
146
+ *
147
+ * Combines file sync mappings and lookup paths into a single config block.
148
+ * See: docs/project/specs/active/plan-2026-01-26-docs-cache-config-restructure.md
149
+ */
150
+ const DocsCacheSchema = z.object({
151
+ files: z.record(z.string(), z.string()).optional(),
152
+ lookup_path: z.array(z.string()).default([".tbd/docs/shortcuts/system", ".tbd/docs/shortcuts/standard"])
153
+ });
154
+ /**
155
+ * Project configuration stored in .tbd/config.yml
156
+ *
157
+ * ⚠️ FORMAT VERSIONING: See tbd-format.ts for version history and migration rules.
158
+ * The tbd_format field tracks breaking changes to this schema.
159
+ */
160
+ const ConfigSchema = z.object({
161
+ tbd_format: z.string().default("f01"),
162
+ tbd_version: z.string(),
163
+ sync: z.object({
164
+ branch: GitBranchName.default("tbd-sync"),
165
+ remote: GitRemoteName.default("origin")
166
+ }).default({}),
167
+ display: z.object({ id_prefix: z.string().min(1).max(20) }),
168
+ settings: z.object({
169
+ auto_sync: z.boolean().default(false),
170
+ doc_auto_sync_hours: z.number().default(24),
171
+ use_gh_cli: z.boolean().default(true)
172
+ }).default({}),
173
+ docs_cache: DocsCacheSchema.optional()
174
+ });
175
+ /**
176
+ * Shared metadata stored in .tbd/data-sync/meta.yml
177
+ */
178
+ const MetaSchema = z.object({
179
+ schema_version: z.number().int(),
180
+ created_at: Timestamp
181
+ });
182
+ /**
183
+ * Per-node state stored in .tbd/state.yml (gitignored).
184
+ * Tracks local timing information that shouldn't be shared across nodes.
185
+ */
186
+ const LocalStateSchema = z.object({
187
+ last_sync_at: Timestamp.optional(),
188
+ last_doc_sync_at: Timestamp.optional(),
189
+ welcome_seen: z.boolean().optional()
190
+ });
191
+ /**
192
+ * Preserved conflict losers.
193
+ */
194
+ const AtticEntrySchema = z.object({
195
+ entity_id: IssueId,
196
+ timestamp: Timestamp,
197
+ field: z.string(),
198
+ lost_value: z.string(),
199
+ winner_source: z.enum(["local", "remote"]),
200
+ loser_source: z.enum(["local", "remote"]),
201
+ context: z.object({
202
+ local_version: Version,
203
+ remote_version: Version,
204
+ local_updated_at: Timestamp,
205
+ remote_updated_at: Timestamp
206
+ })
207
+ });
208
+
209
+ //#endregion
210
+ //#region src/file/parser.ts
211
+ /**
212
+ * YAML front matter parser and serializer for issue files.
213
+ *
214
+ * Issues are stored as Markdown files with YAML front matter:
215
+ * ---
216
+ * type: is
217
+ * id: is-a1b2c3
218
+ * ...
219
+ * ---
220
+ *
221
+ * Description body here.
222
+ *
223
+ * ## Notes
224
+ *
225
+ * Working notes here.
226
+ *
227
+ * See: tbd-design.md §2.1 Markdown + YAML Front Matter Format
228
+ */
229
+ /**
230
+ * gray-matter options using the 'yaml' package as engine.
231
+ * This preserves date strings instead of converting them to Date objects.
232
+ */
233
+ const matterOptions = { engines: { yaml: {
234
+ parse: (str) => parse(str),
235
+ stringify: (obj) => stringify(obj)
236
+ } } };
237
+ /**
238
+ * Parse a Markdown file with YAML front matter.
239
+ * Uses gray-matter for consistent frontmatter parsing.
240
+ * Handles both LF and CRLF line endings.
241
+ */
242
+ function parseMarkdownWithFrontmatter(content) {
243
+ const normalizedContent = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
244
+ if (!matter.test(normalizedContent)) throw new Error("Invalid format: missing front matter opening delimiter");
245
+ const parsed = matter(normalizedContent, matterOptions);
246
+ if (parsed.matter === "" && !normalizedContent.includes("---\n---")) {
247
+ const lines = normalizedContent.split("\n");
248
+ let hasClosing = false;
249
+ for (let i = 1; i < lines.length; i++) if (lines[i]?.trim() === "---") {
250
+ hasClosing = true;
251
+ break;
252
+ }
253
+ if (!hasClosing) throw new Error("Invalid format: missing front matter closing delimiter");
254
+ }
255
+ const frontmatter = parsed.data;
256
+ const body = parsed.content.trim();
257
+ const notesMatch = /\n## Notes\n/i.exec(body);
258
+ let description = body;
259
+ let notes = "";
260
+ if (notesMatch?.index !== void 0) {
261
+ description = body.slice(0, notesMatch.index).trim();
262
+ notes = body.slice(notesMatch.index + notesMatch[0].length).trim();
263
+ }
264
+ return {
265
+ frontmatter,
266
+ description,
267
+ notes
268
+ };
269
+ }
270
+ /**
271
+ * Parse an issue from Markdown file content.
272
+ */
273
+ function parseIssue(content) {
274
+ const { frontmatter, description, notes } = parseMarkdownWithFrontmatter(content);
275
+ const data = {
276
+ ...frontmatter,
277
+ description: description || void 0,
278
+ notes: notes || void 0
279
+ };
280
+ return IssueSchema.parse(data);
281
+ }
282
+ /**
283
+ * Serialize an issue to Markdown file content.
284
+ * Uses canonical serialization for deterministic output.
285
+ */
286
+ function serializeIssue(issue) {
287
+ const { description, notes, ...metadata } = issue;
288
+ const sortedMetadata = {};
289
+ for (const key of Object.keys(metadata).sort()) sortedMetadata[key] = metadata[key];
290
+ const parts = [
291
+ "---",
292
+ stringify(sortedMetadata, {
293
+ sortMapEntries: true,
294
+ lineWidth: 0,
295
+ nullStr: "null"
296
+ }).trim(),
297
+ "---"
298
+ ];
299
+ if (description) parts.push(description.trim());
300
+ if (notes) {
301
+ parts.push("");
302
+ parts.push("## Notes");
303
+ parts.push("");
304
+ parts.push(notes.trim());
305
+ }
306
+ return parts.join("\n") + "\n";
307
+ }
308
+
309
+ //#endregion
310
+ //#region src/index.ts
311
+ /**
312
+ * Package version, derived from git at build time.
313
+ * Format: X.Y.Z for releases, X.Y.Z-dev.N.hash for dev builds.
314
+ */
315
+ const VERSION = "0.1.8";
316
+
317
+ //#endregion
318
+ export { ShortId as C, Priority as S, Version as T, IssueKind as _, AtticEntrySchema as a, LocalStateSchema as b, Dependency as c, DocsCacheSchema as d, EntityType as f, IssueId as g, GitRemoteName as h, serializeIssue as i, DependencyRelationType as l, GitBranchName as m, parseIssue as n, BaseEntity as o, ExternalIssueIdInput as p, parseMarkdownWithFrontmatter as r, ConfigSchema as s, VERSION as t, DocCacheConfigSchema as u, IssueSchema as v, Timestamp as w, MetaSchema as x, IssueStatus as y };
319
+ //# sourceMappingURL=src-CWD4YCBk.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src-CWD4YCBk.mjs","names":["parseYaml","stringifyYaml"],"sources":["../src/lib/schemas.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 // 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 */\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 * 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 { 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 = content.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\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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"}