get-tbd 0.2.2 → 0.2.3
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/dist/bin.mjs +640 -223
- package/dist/bin.mjs.map +1 -1
- package/dist/cli.mjs +631 -226
- package/dist/cli.mjs.map +1 -1
- package/dist/{config-BJz1m9eN.mjs → config-1ouUTKQr.mjs} +15 -4
- package/dist/config-1ouUTKQr.mjs.map +1 -0
- package/dist/{config-DlCUMyCG.mjs → config-YRRW9l89.mjs} +1 -1
- package/dist/docs/SKILL.md +8 -1
- package/dist/docs/guidelines/cli-agent-skill-patterns.md +105 -15
- package/dist/docs/guidelines/error-handling-rules.md +3 -0
- package/dist/docs/guidelines/general-coding-rules.md +2 -1
- package/dist/docs/guidelines/general-comment-rules.md +2 -1
- package/dist/docs/guidelines/general-eng-agent-principles.md +126 -0
- package/dist/docs/guidelines/general-tdd-guidelines.md +6 -0
- package/dist/docs/guidelines/general-testing-rules.md +4 -0
- package/dist/docs/guidelines/python-cli-patterns.md +4 -0
- package/dist/docs/guidelines/python-modern-guidelines.md +3 -0
- package/dist/docs/guidelines/python-rules.md +6 -0
- package/dist/docs/guidelines/tbd-sync-troubleshooting.md +24 -2
- package/dist/docs/guidelines/typescript-cli-tool-rules.md +3 -3
- package/dist/docs/guidelines/typescript-code-coverage.md +6 -4
- package/dist/docs/guidelines/typescript-rules.md +7 -9
- package/dist/docs/guidelines/typescript-sorting-patterns.md +1 -1
- package/dist/docs/guidelines/typescript-yaml-handling-rules.md +5 -5
- package/dist/docs/shortcuts/standard/new-shortcut.md +14 -0
- package/dist/docs/shortcuts/standard/setup-github-cli.md +4 -1
- package/dist/docs/shortcuts/system/shortcut-explanation.md +16 -1
- package/dist/docs/shortcuts/system/skill-baseline.md +8 -1
- package/dist/docs/tbd-design.md +3 -3
- package/dist/index.mjs +1 -1
- package/dist/{src-BpvcrLnq.mjs → src-DTyyuaG_.mjs} +2 -2
- package/dist/{src-BpvcrLnq.mjs.map → src-DTyyuaG_.mjs.map} +1 -1
- package/dist/tbd +640 -223
- package/package.json +1 -1
- package/dist/config-BJz1m9eN.mjs.map +0 -1
- package/dist/docs/guidelines/general-eng-assistant-rules.md +0 -59
|
@@ -5,6 +5,9 @@ author: Joshua Levy (github.com/jlevy) with LLM assistance
|
|
|
5
5
|
---
|
|
6
6
|
# Python Modern Guidelines
|
|
7
7
|
|
|
8
|
+
**Related**: `python-rules` (general Python coding rules), `python-cli-patterns` (for
|
|
9
|
+
CLI tools).
|
|
10
|
+
|
|
8
11
|
These are rules for a modern Python project using uv.
|
|
9
12
|
|
|
10
13
|
## Support Only Modern Python Versions
|
|
@@ -5,6 +5,9 @@ author: Joshua Levy (github.com/jlevy) with LLM assistance
|
|
|
5
5
|
---
|
|
6
6
|
# Python Rules
|
|
7
7
|
|
|
8
|
+
**Related**: `python-modern-guidelines` (uv and modern Python practices),
|
|
9
|
+
`python-cli-patterns` (for CLI tools).
|
|
10
|
+
|
|
8
11
|
These are general rules that *must* be followed on this project for Python code.
|
|
9
12
|
|
|
10
13
|
## Project Setup and Developer Workflows
|
|
@@ -63,6 +66,9 @@ These are general rules that *must* be followed on this project for Python code.
|
|
|
63
66
|
variable aliases or comments about backward compatibility) UNLESS the user has
|
|
64
67
|
confirmed that it is necessary.
|
|
65
68
|
|
|
69
|
+
- For the full policy on what compatibility to preserve, see
|
|
70
|
+
`backward-compatibility-rules`.
|
|
71
|
+
|
|
66
72
|
## Coding Conventions and Imports
|
|
67
73
|
|
|
68
74
|
- Always use full, absolute imports for paths.
|
|
@@ -31,6 +31,24 @@ tbd sync
|
|
|
31
31
|
4. Import happens automatically when you later run `tbd sync` in an environment that can
|
|
32
32
|
push
|
|
33
33
|
|
|
34
|
+
### Push Fails Because Histories Are Unrelated
|
|
35
|
+
|
|
36
|
+
**Symptoms:**
|
|
37
|
+
- `tbd sync` aborts with “`origin/tbd-sync` has an unrelated history (no common
|
|
38
|
+
ancestor)”
|
|
39
|
+
- `tbd doctor` reports the remote sync branch histories are unrelated
|
|
40
|
+
- Push cannot fast-forward and a merge refuses
|
|
41
|
+
|
|
42
|
+
**Causes:**
|
|
43
|
+
- The local `tbd-sync` branch and `origin/tbd-sync` were created independently—for
|
|
44
|
+
example, two clones each initialized their own sync branch, or the remote branch was
|
|
45
|
+
replaced—so the two have no common ancestor
|
|
46
|
+
|
|
47
|
+
**Solutions:**
|
|
48
|
+
1. Run `tbd doctor --fix` to reconcile the unrelated histories.
|
|
49
|
+
This is non-destructive: a backup branch is created first.
|
|
50
|
+
2. Run `tbd sync` again to confirm the push succeeds.
|
|
51
|
+
|
|
34
52
|
### “Already in sync” but data not on remote
|
|
35
53
|
|
|
36
54
|
**Symptoms:**
|
|
@@ -60,7 +78,8 @@ auto-saving, since the issue is likely temporary.
|
|
|
60
78
|
1. Check network connectivity
|
|
61
79
|
2. Verify remote URL: `git remote -v`
|
|
62
80
|
3. Retry: `tbd sync`
|
|
63
|
-
4. If persistent,
|
|
81
|
+
4. If persistent, save for later: `tbd save --outbox` (auto-imports on the next
|
|
82
|
+
successful sync)
|
|
64
83
|
|
|
65
84
|
### Bulk Trivial Changes in Outbox (Version/Timestamp Only)
|
|
66
85
|
|
|
@@ -193,7 +212,8 @@ The `.tbd/.gitignore` file contains a `!workspaces/` negation pattern to prevent
|
|
|
193
212
|
- Commands fail with worktree errors
|
|
194
213
|
|
|
195
214
|
**Solutions:**
|
|
196
|
-
1. Run `tbd doctor --fix` to auto-repair
|
|
215
|
+
1. Run `tbd doctor --fix` to auto-repair, or `tbd sync --fix` to repair the worktree as
|
|
216
|
+
part of a sync
|
|
197
217
|
2. If that fails, manually repair:
|
|
198
218
|
```bash
|
|
199
219
|
rm -rf "$(git rev-parse --path-format=absolute --git-common-dir)/tbd/data-sync-worktree"
|
|
@@ -258,6 +278,8 @@ tbd sync
|
|
|
258
278
|
**CLI Options:**
|
|
259
279
|
- `--no-auto-save`: Skip automatic save to outbox on failure
|
|
260
280
|
- `--no-outbox`: Skip automatic import from outbox on success
|
|
281
|
+
- `--fix`: Repair an unhealthy worktree before syncing
|
|
282
|
+
- `--status`: Show sync status without syncing
|
|
261
283
|
|
|
262
284
|
See `tbd shortcut sync-failure-recovery` for the full workflow.
|
|
263
285
|
|
|
@@ -13,9 +13,9 @@ Commander 14 moves to security-only maintenance until May 2027.
|
|
|
13
13
|
|
|
14
14
|
**Related**:
|
|
15
15
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
|
|
16
|
+
- `typescript-rules`
|
|
17
|
+
- `error-handling-rules`—failure paths and exit codes are part of every CLI command.
|
|
18
|
+
- `supply-chain-hardening`—follow the 14-day package-age rule for every CLI dependency.
|
|
19
19
|
Bundlers and CLI dependencies that execute at install time (`postinstall` scripts) are
|
|
20
20
|
a primary attack surface.
|
|
21
21
|
|
|
@@ -12,10 +12,12 @@ not adopt yet.
|
|
|
12
12
|
|
|
13
13
|
**Related**:
|
|
14
14
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
- `typescript-rules`
|
|
16
|
+
- `general-testing-rules` and `general-tdd-guidelines`—what to test and the red-green
|
|
17
|
+
workflow that coverage measures.
|
|
18
|
+
- `pnpm-monorepo-patterns`—the Testing section covers the monorepo test setup.
|
|
19
|
+
- `supply-chain-hardening`—follow the 14-day package-age rule when installing or
|
|
20
|
+
upgrading `vitest` and `@vitest/coverage-v8`.
|
|
19
21
|
|
|
20
22
|
## Coverage Metrics
|
|
21
23
|
|
|
@@ -15,15 +15,13 @@ yet production-ready**—do not adopt for shipped builds.
|
|
|
15
15
|
|
|
16
16
|
**Related**:
|
|
17
17
|
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
14-day package-age rule applies to every TypeScript dependency (`zod`, `commander`,
|
|
26
|
-
`vitest`, `eslint`, type packages, etc.).
|
|
18
|
+
- `typescript-cli-tool-rules`
|
|
19
|
+
- `typescript-sorting-patterns`
|
|
20
|
+
- `typescript-yaml-handling-rules`
|
|
21
|
+
- `typescript-code-coverage`
|
|
22
|
+
- `pnpm-monorepo-patterns` and `bun-monorepo-patterns`
|
|
23
|
+
- `supply-chain-hardening`—the 14-day package-age rule applies to every TypeScript
|
|
24
|
+
dependency (`zod`, `commander`, `vitest`, `eslint`, type packages, etc.).
|
|
27
25
|
|
|
28
26
|
## Coding Style
|
|
29
27
|
|
|
@@ -5,7 +5,7 @@ author: Joshua Levy (github.com/jlevy) with LLM assistance
|
|
|
5
5
|
---
|
|
6
6
|
# TypeScript Sorting Patterns
|
|
7
7
|
|
|
8
|
-
**Related**: `
|
|
8
|
+
**Related**: `typescript-rules`, `general-testing-rules`
|
|
9
9
|
|
|
10
10
|
## 1. Always Make Sorting Deterministic
|
|
11
11
|
|
|
@@ -14,9 +14,9 @@ Zod 4.x is the recommended validation companion.
|
|
|
14
14
|
|
|
15
15
|
**Related**:
|
|
16
16
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
17
|
+
- `typescript-rules`
|
|
18
|
+
- `supply-chain-hardening`—follow the 14-day package-age rule for `yaml`, `zod`, and
|
|
19
|
+
`gray-matter`.
|
|
20
20
|
|
|
21
21
|
These guidelines ensure consistent, safe, and readable YAML handling across TypeScript
|
|
22
22
|
codebases. YAML is deceptively tricky—inconsistent quoting, serialization differences,
|
|
@@ -203,8 +203,8 @@ const output = `---\n${stringifyYaml(data)}---\n\n${content}`;
|
|
|
203
203
|
|
|
204
204
|
## Related Guidelines
|
|
205
205
|
|
|
206
|
-
- For general TypeScript rules, see `
|
|
207
|
-
- For error handling patterns, see `
|
|
206
|
+
- For general TypeScript rules, see `typescript-rules`
|
|
207
|
+
- For error handling patterns, see `error-handling-rules`
|
|
208
208
|
|
|
209
209
|
<!-- This document follows common-doc-guidelines.md.
|
|
210
210
|
See github.com/jlevy/practical-prose and review guidelines before editing.
|
|
@@ -53,6 +53,20 @@ Examples: `code-review-and-commit`, `new-plan-spec`, `review-code-typescript`
|
|
|
53
53
|
- **Reference, don’t duplicate**: Point to `tbd shortcut/guidelines/template` commands
|
|
54
54
|
- **Trust the agent**: They’ll adapt to context
|
|
55
55
|
|
|
56
|
+
## Referencing Other Shortcuts
|
|
57
|
+
|
|
58
|
+
Refer to other shortcuts, guidelines, and templates by **name**, using the command that
|
|
59
|
+
surfaces them — never by file path:
|
|
60
|
+
|
|
61
|
+
- Another shortcut: `tbd shortcut <name>` (e.g. `tbd shortcut precommit-process`)
|
|
62
|
+
- A guideline: `tbd guidelines <name>` (e.g. `tbd guidelines typescript-rules`)
|
|
63
|
+
- A template: `tbd template <name>` (e.g. `tbd template plan-spec`)
|
|
64
|
+
|
|
65
|
+
The name stays valid wherever the file ends up — read directly, served via
|
|
66
|
+
`tbd shortcut`, or embedded in another tool.
|
|
67
|
+
File paths and relative markdown links break when shortcuts are flattened into the
|
|
68
|
+
`.tbd` cache or relocated, so don’t use them to point at other shortcuts.
|
|
69
|
+
|
|
56
70
|
## Testing
|
|
57
71
|
|
|
58
72
|
```bash
|
|
@@ -64,7 +64,10 @@ If this fails for any reason, follow the steps below.
|
|
|
64
64
|
|
|
65
65
|
Set `GH_TOKEN` environment variable with a GitHub personal access token **before**
|
|
66
66
|
starting the session.
|
|
67
|
-
|
|
67
|
+
Create a [Personal Access Token](https://github.com/settings/tokens?type=beta)
|
|
68
|
+
(fine-grained recommended) with **Contents** and **Pull requests** read/write
|
|
69
|
+
permissions, then export it (e.g. add `export GH_TOKEN=...` to your shell profile or set
|
|
70
|
+
it in your agent environment).
|
|
68
71
|
|
|
69
72
|
## Quick Reference
|
|
70
73
|
|
|
@@ -24,6 +24,20 @@ These may include:
|
|
|
24
24
|
- File operations and git workflows
|
|
25
25
|
- Prompts for gathering information from the user
|
|
26
26
|
|
|
27
|
+
## Referencing Other Shortcuts, Guidelines, and Templates
|
|
28
|
+
|
|
29
|
+
Shortcuts refer to each other by **name**, using the command that surfaces them:
|
|
30
|
+
|
|
31
|
+
- Another shortcut: `tbd shortcut <name>` (e.g. `tbd shortcut precommit-process`)
|
|
32
|
+
- A guideline: `tbd guidelines <name>` (e.g. `tbd guidelines typescript-rules`)
|
|
33
|
+
- A template: `tbd template <name>` (e.g. `tbd template plan-spec`)
|
|
34
|
+
|
|
35
|
+
Always reference by name, never by file path.
|
|
36
|
+
The name is stable wherever the shortcut lives, so the reference stays valid whether the
|
|
37
|
+
file is read directly, served via `tbd shortcut`, or embedded in another tool.
|
|
38
|
+
File paths (and relative markdown links) break when shortcuts are flattened into the
|
|
39
|
+
`.tbd` cache or relocated, so avoid them inside shortcut bodies.
|
|
40
|
+
|
|
27
41
|
## Example Workflow
|
|
28
42
|
|
|
29
43
|
User: “I want to create a new research brief”
|
|
@@ -37,7 +51,8 @@ Agent:
|
|
|
37
51
|
|
|
38
52
|
Shortcuts are loaded from directories in the doc path (searched in order):
|
|
39
53
|
|
|
40
|
-
- `.tbd/docs/shortcuts/system/` - Core system docs (skill
|
|
54
|
+
- `.tbd/docs/shortcuts/system/` - Core system docs (skill-baseline,
|
|
55
|
+
shortcut-explanation, etc.)
|
|
41
56
|
- `.tbd/docs/shortcuts/standard/` - Standard workflow shortcuts
|
|
42
57
|
|
|
43
58
|
Directories earlier in the doc path take precedence.
|
|
@@ -9,7 +9,7 @@ description: Full tbd workflow guide for agents
|
|
|
9
9
|
Drop-in replacement for `bd`.
|
|
10
10
|
2. **Spec-Driven Workflows**: Plan features → break into beads → implement
|
|
11
11
|
systematically.
|
|
12
|
-
3. **Knowledge Injection**:
|
|
12
|
+
3. **Knowledge Injection**: 25+ engineering guidelines (TypeScript, Python, TDD,
|
|
13
13
|
testing, Convex, monorepos) available on demand.
|
|
14
14
|
4. **Shortcuts**: Reusable instruction templates for common workflows (code review,
|
|
15
15
|
commits, PRs, cleanup, handoffs).
|
|
@@ -71,6 +71,7 @@ or want help → run `tbd shortcut welcome-user`
|
|
|
71
71
|
| “Create a PR” / “File a PR” | `tbd shortcut create-or-update-pr-simple` |
|
|
72
72
|
| “Merge main into my branch” | `tbd shortcut merge-upstream` |
|
|
73
73
|
| **Guidelines & Knowledge** | |
|
|
74
|
+
| *(any engineering work)* | Load the **General engineering** group first (see below) |
|
|
74
75
|
| “Use TypeScript best practices” | `tbd guidelines typescript-rules` |
|
|
75
76
|
| “Use Python best practices” | `tbd guidelines python-rules` |
|
|
76
77
|
| “Build a TypeScript CLI” | `tbd guidelines typescript-cli-tool-rules` |
|
|
@@ -89,6 +90,12 @@ or want help → run `tbd shortcut welcome-user`
|
|
|
89
90
|
| “Check out this library’s source” | `tbd shortcut checkout-third-party-repo` |
|
|
90
91
|
| *(your choice whenever appropriate)* | `tbd list`, `tbd dep add`, `tbd close`, `tbd sync`, etc. |
|
|
91
92
|
|
|
93
|
+
**Loading guidelines for engineering work:** Before writing or reviewing code, load the
|
|
94
|
+
**General engineering** group—the `general-*` rules plus `error-handling-rules`—since
|
|
95
|
+
these apply to all code regardless of language.
|
|
96
|
+
Then load the group for the language or framework in use (TypeScript, Python, Convex,
|
|
97
|
+
etc.). Run `tbd guidelines --list` to see all available guidelines.
|
|
98
|
+
|
|
92
99
|
**Note:** Never gitignore `.tbd/workspaces/` — the outbox must be committed to your
|
|
93
100
|
working branch. See `tbd guidelines tbd-sync-troubleshooting` for details.
|
|
94
101
|
|
package/dist/docs/tbd-design.md
CHANGED
|
@@ -18,8 +18,7 @@ agents.
|
|
|
18
18
|
- [1. Introduction](#1-introduction)
|
|
19
19
|
- [1.1 What is tbd?](#11-what-is-tbd)
|
|
20
20
|
- [1.2 When to Use tbd vs Beads](#12-when-to-use-tbd-vs-beads)
|
|
21
|
-
- [1.3 Why Replace Beads?
|
|
22
|
-
(Architecture Comparison)](#13-why-replace-beads-architecture-comparison)
|
|
21
|
+
- [1.3 Why Replace Beads? (Architecture Comparison)](#13-why-replace-beads-architecture-comparison)
|
|
23
22
|
- [1.4 Design Goals](#14-design-goals)
|
|
24
23
|
- [1.5 Design Principles](#15-design-principles)
|
|
25
24
|
- [1.6 Non-Goals](#16-non-goals)
|
|
@@ -659,6 +658,7 @@ serialization:
|
|
|
659
658
|
> directory.
|
|
660
659
|
|
|
661
660
|
> **Why canonical format?** Deterministic serialization ensures:
|
|
661
|
+
>
|
|
662
662
|
> 1. Git diffs show only actual content changes (no spurious whitespace/ordering noise)
|
|
663
663
|
> 2. Testing is reliable (same input produces same output)
|
|
664
664
|
> 3. Future caching/deduplication can use content hashes if needed
|
|
@@ -2290,7 +2290,7 @@ const issueMergeRules: MergeRules<Issue> = {
|
|
|
2290
2290
|
created_by: { strategy: 'preserve_oldest' },
|
|
2291
2291
|
closed_at: { strategy: 'lww' }, // See status/closed_at rules below
|
|
2292
2292
|
close_reason: { strategy: 'lww' },
|
|
2293
|
-
child_order_hints: { strategy: '
|
|
2293
|
+
child_order_hints: { strategy: 'union' }, // Append-only set of child IDs; union (dedupe)
|
|
2294
2294
|
};
|
|
2295
2295
|
```
|
|
2296
2296
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { A as Priority, C as IssueSchema, D as LocalStateSchema, E as LOCAL_STATE_FIELD_ORDER, F as Version, M as SyncStorage, N as Timestamp, O as META_FIELD_ORDER, P as Ulid, S as IssueKind, T as IssueTitle, _ as ISSUE_BODY_MAX_LENGTH, a as CONFIG_FIELD_ORDER, b as IdMappingYamlSchema, c as DATA_SYNC_SCHEMA_VERSION, d as DocCacheConfigSchema, f as DocsCacheSchema, g as GitRemoteName, h as GitBranchName, i as COMMON_DIR_LAYOUT_FIELD_ORDER, j as ShortId, k as MetaSchema, l as Dependency, m as ExternalIssueIdInput, n as AtticEntrySchema, o as CommonDirLayoutSchema, p as EntityType, r as BaseEntity, s as ConfigSchema, t as ATTIC_ENTRY_FIELD_ORDER, u as DependencyRelationType, v as ISSUE_FIELD_ORDER, w as IssueStatus, x as IssueId, y as ISSUE_TITLE_MAX_LENGTH } from "./schemas-f0EcuAVu.mjs";
|
|
2
|
-
import { c as noopLogger, i as serializeIssue, n as parseIssue, t as VERSION } from "./src-
|
|
2
|
+
import { c as noopLogger, i as serializeIssue, n as parseIssue, t as VERSION } from "./src-DTyyuaG_.mjs";
|
|
3
3
|
|
|
4
4
|
export { ATTIC_ENTRY_FIELD_ORDER, AtticEntrySchema, BaseEntity, COMMON_DIR_LAYOUT_FIELD_ORDER, CONFIG_FIELD_ORDER, CommonDirLayoutSchema, ConfigSchema, DATA_SYNC_SCHEMA_VERSION, Dependency, DependencyRelationType, DocCacheConfigSchema, DocsCacheSchema, EntityType, ExternalIssueIdInput, GitBranchName, GitRemoteName, ISSUE_BODY_MAX_LENGTH, ISSUE_FIELD_ORDER, ISSUE_TITLE_MAX_LENGTH, IdMappingYamlSchema, IssueId, IssueKind, IssueSchema, IssueStatus, IssueTitle, LOCAL_STATE_FIELD_ORDER, LocalStateSchema, META_FIELD_ORDER, MetaSchema, Priority, ShortId, SyncStorage, Timestamp, Ulid, VERSION, Version, noopLogger, parseIssue, serializeIssue };
|
|
@@ -183,8 +183,8 @@ function serializeIssue(issue) {
|
|
|
183
183
|
* Package version, derived from git at build time.
|
|
184
184
|
* Format: X.Y.Z for releases, X.Y.Z-dev.N.hash for dev builds.
|
|
185
185
|
*/
|
|
186
|
-
const VERSION = "0.2.
|
|
186
|
+
const VERSION = "0.2.3";
|
|
187
187
|
|
|
188
188
|
//#endregion
|
|
189
189
|
export { insertAfterFrontmatter as a, noopLogger as c, serializeIssue as i, parseIssue as n, parseMarkdown as o, parseMarkdownWithFrontmatter as r, stripFrontmatter as s, VERSION as t };
|
|
190
|
-
//# sourceMappingURL=src-
|
|
190
|
+
//# sourceMappingURL=src-DTyyuaG_.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"src-BpvcrLnq.mjs","names":["parseYaml"],"sources":["../src/lib/types.ts","../src/utils/markdown-utils.ts","../src/file/parser.ts","../src/index.ts"],"sourcesContent":["/**\n * TypeScript types derived from Zod schemas.\n *\n * These types are the canonical TypeScript interface for tbd entities.\n */\n\nimport type { z } from 'zod';\n\nimport type {\n IssueSchema,\n IssueStatus,\n IssueKind,\n Priority,\n Dependency,\n ConfigSchema,\n CommonDirLayoutSchema,\n MetaSchema,\n LocalStateSchema,\n AtticEntrySchema,\n} from './schemas.js';\n\n// =============================================================================\n// Entity Types\n// =============================================================================\n\n/**\n * A tbd issue entity.\n */\nexport type Issue = z.infer<typeof IssueSchema>;\n\n/**\n * Issue status enum values.\n */\nexport type IssueStatusType = z.infer<typeof IssueStatus>;\n\n/**\n * Issue kind enum values.\n */\nexport type IssueKindType = z.infer<typeof IssueKind>;\n\n/**\n * Priority level (0-4).\n */\nexport type PriorityType = z.infer<typeof Priority>;\n\n/**\n * A dependency relationship.\n */\nexport type DependencyType = z.infer<typeof Dependency>;\n\n// =============================================================================\n// Configuration Types\n// =============================================================================\n\n/**\n * Project configuration.\n */\nexport type Config = z.infer<typeof ConfigSchema>;\n\n/**\n * Git common-dir local layout metadata.\n */\nexport type CommonDirLayout = z.infer<typeof CommonDirLayoutSchema>;\n\n/**\n * Shared metadata.\n */\nexport type Meta = z.infer<typeof MetaSchema>;\n\n/**\n * Per-node local state.\n */\nexport type LocalState = z.infer<typeof LocalStateSchema>;\n\n/**\n * Attic entry for conflict losers.\n */\nexport type AtticEntry = z.infer<typeof AtticEntrySchema>;\n\n// =============================================================================\n// Input Types for Commands\n// =============================================================================\n\n/**\n * Options for creating an issue.\n */\nexport interface CreateIssueOptions {\n title: string;\n description?: string;\n kind?: IssueKindType;\n priority?: PriorityType;\n assignee?: string;\n labels?: string[];\n parent_id?: string;\n due_date?: string;\n deferred_until?: string;\n}\n\n/**\n * Options for updating an issue.\n */\nexport interface UpdateIssueOptions {\n title?: string;\n description?: string;\n notes?: string;\n kind?: IssueKindType;\n status?: IssueStatusType;\n priority?: PriorityType;\n assignee?: string | null;\n addLabels?: string[];\n removeLabels?: string[];\n parent_id?: string | null;\n due_date?: string | null;\n deferred_until?: string | null;\n}\n\n/**\n * Options for listing issues.\n */\nexport interface ListIssuesOptions {\n status?: IssueStatusType | IssueStatusType[];\n kind?: IssueKindType | IssueKindType[];\n priority?: PriorityType;\n assignee?: string;\n labels?: string[];\n parent?: string;\n all?: boolean;\n sort?: 'priority' | 'created' | 'updated';\n limit?: number;\n}\n\n/**\n * Options for searching issues.\n */\nexport interface SearchIssuesOptions {\n query: string;\n status?: IssueStatusType | IssueStatusType[];\n limit?: number;\n}\n\n// =============================================================================\n// CLI Utility Types\n// =============================================================================\n\n/**\n * A documentation section with title and slug.\n * Used by docs and design commands.\n */\nexport interface DocSection {\n title: string;\n slug: string;\n}\n\n/**\n * Logger interface for long-running operations in non-CLI layers.\n *\n * Allows core logic (file/, lib/) to report progress without depending on\n * the CLI output layer. CLI commands create an OperationLogger via\n * `OutputManager.logger(spinner)` and pass it to core functions.\n *\n * All methods are required. Use `noopLogger` when no logging is needed.\n */\nexport interface OperationLogger {\n /** Key milestones — drives the spinner in CLI context */\n progress: (message: string) => void;\n /** Operational detail (shown with --verbose or --debug) */\n info: (message: string) => void;\n /** Non-fatal warnings */\n warn: (message: string) => void;\n /** Internal state for troubleshooting (shown with --debug only) */\n debug: (message: string) => void;\n}\n\n/**\n * No-op logger for when no logging is needed.\n * Analogous to noopSpinner in the CLI layer.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nconst noop = () => {};\nexport const noopLogger: OperationLogger = {\n progress: noop,\n info: noop,\n warn: noop,\n debug: noop,\n};\n","/**\n * Markdown utilities for processing markdown content.\n *\n * Uses gray-matter for parsing and centralized yaml-utils for stringify to ensure\n * proper handling of special YAML characters (colons, quotes, etc.).\n */\n\nimport matter from 'gray-matter';\n\nimport { stringifyYamlCompact } from './yaml-utils.js';\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 // Use centralized yaml-utils for proper handling of special characters\n // (colons, quotes, multiline strings, etc.)\n frontmatter = stringifyYamlCompact(data).trimEnd();\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 { parse as parseYaml } from 'yaml';\n\nimport { normalizeLineEndings } from '../utils/markdown-utils.js';\nimport { sortKeys, stringifyYaml } from '../utils/yaml-utils.js';\nimport type { Issue } from '../lib/types.js';\nimport { IssueSchema, ISSUE_FIELD_ORDER } 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 using canonical field order (not alphabetical)\n const sortedMetadata = sortKeys(metadata, ISSUE_FIELD_ORDER);\n\n // Serialize YAML with compact output for frontmatter.\n // sortMapEntries: false preserves our manual ordering.\n const yaml = stringifyYaml(sortedMetadata, {\n lineWidth: 0,\n nullStr: 'null',\n sortMapEntries: false,\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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"}
|
|
1
|
+
{"version":3,"file":"src-DTyyuaG_.mjs","names":["parseYaml"],"sources":["../src/lib/types.ts","../src/utils/markdown-utils.ts","../src/file/parser.ts","../src/index.ts"],"sourcesContent":["/**\n * TypeScript types derived from Zod schemas.\n *\n * These types are the canonical TypeScript interface for tbd entities.\n */\n\nimport type { z } from 'zod';\n\nimport type {\n IssueSchema,\n IssueStatus,\n IssueKind,\n Priority,\n Dependency,\n ConfigSchema,\n CommonDirLayoutSchema,\n MetaSchema,\n LocalStateSchema,\n AtticEntrySchema,\n} from './schemas.js';\n\n// =============================================================================\n// Entity Types\n// =============================================================================\n\n/**\n * A tbd issue entity.\n */\nexport type Issue = z.infer<typeof IssueSchema>;\n\n/**\n * Issue status enum values.\n */\nexport type IssueStatusType = z.infer<typeof IssueStatus>;\n\n/**\n * Issue kind enum values.\n */\nexport type IssueKindType = z.infer<typeof IssueKind>;\n\n/**\n * Priority level (0-4).\n */\nexport type PriorityType = z.infer<typeof Priority>;\n\n/**\n * A dependency relationship.\n */\nexport type DependencyType = z.infer<typeof Dependency>;\n\n// =============================================================================\n// Configuration Types\n// =============================================================================\n\n/**\n * Project configuration.\n */\nexport type Config = z.infer<typeof ConfigSchema>;\n\n/**\n * Git common-dir local layout metadata.\n */\nexport type CommonDirLayout = z.infer<typeof CommonDirLayoutSchema>;\n\n/**\n * Shared metadata.\n */\nexport type Meta = z.infer<typeof MetaSchema>;\n\n/**\n * Per-node local state.\n */\nexport type LocalState = z.infer<typeof LocalStateSchema>;\n\n/**\n * Attic entry for conflict losers.\n */\nexport type AtticEntry = z.infer<typeof AtticEntrySchema>;\n\n// =============================================================================\n// Input Types for Commands\n// =============================================================================\n\n/**\n * Options for creating an issue.\n */\nexport interface CreateIssueOptions {\n title: string;\n description?: string;\n kind?: IssueKindType;\n priority?: PriorityType;\n assignee?: string;\n labels?: string[];\n parent_id?: string;\n due_date?: string;\n deferred_until?: string;\n}\n\n/**\n * Options for updating an issue.\n */\nexport interface UpdateIssueOptions {\n title?: string;\n description?: string;\n notes?: string;\n kind?: IssueKindType;\n status?: IssueStatusType;\n priority?: PriorityType;\n assignee?: string | null;\n addLabels?: string[];\n removeLabels?: string[];\n parent_id?: string | null;\n due_date?: string | null;\n deferred_until?: string | null;\n}\n\n/**\n * Options for listing issues.\n */\nexport interface ListIssuesOptions {\n status?: IssueStatusType | IssueStatusType[];\n kind?: IssueKindType | IssueKindType[];\n priority?: PriorityType;\n assignee?: string;\n labels?: string[];\n parent?: string;\n all?: boolean;\n sort?: 'priority' | 'created' | 'updated';\n limit?: number;\n}\n\n/**\n * Options for searching issues.\n */\nexport interface SearchIssuesOptions {\n query: string;\n status?: IssueStatusType | IssueStatusType[];\n limit?: number;\n}\n\n// =============================================================================\n// CLI Utility Types\n// =============================================================================\n\n/**\n * A documentation section with title and slug.\n * Used by docs and design commands.\n */\nexport interface DocSection {\n title: string;\n slug: string;\n}\n\n/**\n * Logger interface for long-running operations in non-CLI layers.\n *\n * Allows core logic (file/, lib/) to report progress without depending on\n * the CLI output layer. CLI commands create an OperationLogger via\n * `OutputManager.logger(spinner)` and pass it to core functions.\n *\n * All methods are required. Use `noopLogger` when no logging is needed.\n */\nexport interface OperationLogger {\n /** Key milestones — drives the spinner in CLI context */\n progress: (message: string) => void;\n /** Operational detail (shown with --verbose or --debug) */\n info: (message: string) => void;\n /** Non-fatal warnings */\n warn: (message: string) => void;\n /** Internal state for troubleshooting (shown with --debug only) */\n debug: (message: string) => void;\n}\n\n/**\n * No-op logger for when no logging is needed.\n * Analogous to noopSpinner in the CLI layer.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nconst noop = () => {};\nexport const noopLogger: OperationLogger = {\n progress: noop,\n info: noop,\n warn: noop,\n debug: noop,\n};\n","/**\n * Markdown utilities for processing markdown content.\n *\n * Uses gray-matter for parsing and centralized yaml-utils for stringify to ensure\n * proper handling of special YAML characters (colons, quotes, etc.).\n */\n\nimport matter from 'gray-matter';\n\nimport { stringifyYamlCompact } from './yaml-utils.js';\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 // Use centralized yaml-utils for proper handling of special characters\n // (colons, quotes, multiline strings, etc.)\n frontmatter = stringifyYamlCompact(data).trimEnd();\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 { parse as parseYaml } from 'yaml';\n\nimport { normalizeLineEndings } from '../utils/markdown-utils.js';\nimport { sortKeys, stringifyYaml } from '../utils/yaml-utils.js';\nimport type { Issue } from '../lib/types.js';\nimport { IssueSchema, ISSUE_FIELD_ORDER } 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 using canonical field order (not alphabetical)\n const sortedMetadata = sortKeys(metadata, ISSUE_FIELD_ORDER);\n\n // Serialize YAML with compact output for frontmatter.\n // sortMapEntries: false preserves our manual ordering.\n const yaml = stringifyYaml(sortedMetadata, {\n lineWidth: 0,\n nullStr: 'null',\n sortMapEntries: false,\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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"}
|