get-tbd 0.2.1 → 0.2.2
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 +14 -7
- package/dist/bin.mjs.map +1 -1
- package/dist/cli.mjs +14 -7
- package/dist/cli.mjs.map +1 -1
- package/dist/docs/guidelines/bun-monorepo-patterns.md +65 -66
- package/dist/docs/guidelines/cli-agent-skill-patterns.md +314 -158
- package/dist/docs/guidelines/common-doc-guidelines.md +2 -2
- package/dist/docs/guidelines/convex-limits-best-practices.md +39 -39
- package/dist/docs/guidelines/convex-rules.md +13 -13
- package/dist/docs/guidelines/electron-app-development-patterns.md +18 -18
- package/dist/docs/guidelines/general-comment-rules.md +1 -1
- package/dist/docs/guidelines/general-tdd-guidelines.md +4 -4
- package/dist/docs/guidelines/golden-testing-guidelines.md +9 -9
- package/dist/docs/guidelines/pnpm-monorepo-patterns.md +49 -49
- package/dist/docs/guidelines/python-cli-patterns.md +1 -1
- package/dist/docs/guidelines/python-modern-guidelines.md +4 -4
- package/dist/docs/guidelines/release-notes-guidelines.md +18 -2
- package/dist/docs/guidelines/supply-chain-hardening.md +84 -29
- package/dist/docs/guidelines/tbd-sync-troubleshooting.md +3 -3
- package/dist/docs/guidelines/typescript-cli-tool-rules.md +17 -17
- package/dist/docs/guidelines/typescript-code-coverage.md +5 -5
- package/dist/docs/guidelines/typescript-rules.md +3 -3
- package/dist/docs/guidelines/typescript-yaml-handling-rules.md +3 -3
- package/dist/docs/tbd-design.md +40 -40
- package/dist/docs/tbd-docs.md +1 -1
- package/dist/docs/tbd-prime.md +3 -3
- package/dist/index.mjs +1 -1
- package/dist/{src-CtZIHxYM.mjs → src-BpvcrLnq.mjs} +2 -2
- package/dist/{src-CtZIHxYM.mjs.map → src-BpvcrLnq.mjs.map} +1 -1
- package/dist/tbd +14 -7
- package/package.json +1 -1
|
@@ -14,8 +14,8 @@ Commander 14 moves to security-only maintenance until May 2027.
|
|
|
14
14
|
**Related**:
|
|
15
15
|
|
|
16
16
|
- [TypeScript Rules](./typescript-rules.md)
|
|
17
|
-
- [Supply-Chain Mitigation](./pnpm-monorepo-patterns.md#supply-chain-mitigation)
|
|
18
|
-
|
|
17
|
+
- [Supply-Chain Mitigation](./pnpm-monorepo-patterns.md#supply-chain-mitigation)—follow
|
|
18
|
+
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
|
|
|
@@ -368,10 +368,10 @@ context, output, and error handling.
|
|
|
368
368
|
Strict separation of data and diagnostics enables pipeline composability.
|
|
369
369
|
|
|
370
370
|
- **Data to stdout:** `data()`, `success()`, `notice()`, `dryRun()`, `table()`,
|
|
371
|
-
`list()`, `count()
|
|
371
|
+
`list()`, `count()`—all go to `console.log` (stdout).
|
|
372
372
|
|
|
373
373
|
- **Diagnostics to stderr:** `info()`, `warn()`, `error()`, `command()`, `debug()`,
|
|
374
|
-
`spinner
|
|
374
|
+
`spinner`—all go to `console.error` (stderr) or `process.stderr.write`.
|
|
375
375
|
|
|
376
376
|
- **JSON mode wraps diagnostics:** `warn()` outputs `{"warning": "..."}` to stderr.
|
|
377
377
|
`error()` outputs `{"error": "..."}` to stderr.
|
|
@@ -579,7 +579,7 @@ seamlessly in local dev and in remote environments.
|
|
|
579
579
|
precedence logic, or runtime conditional loading from inside the CLI).
|
|
580
580
|
|
|
581
581
|
- **Use `dotenv` only when needed:** Add `dotenv` only if your CLI must load env files
|
|
582
|
-
programmatically
|
|
582
|
+
programmatically—e.g., it reads them after parsing command-line flags, performs
|
|
583
583
|
variable expansion, or supports custom file paths the user cannot pre-bake into the
|
|
584
584
|
`node` invocation.
|
|
585
585
|
|
|
@@ -615,11 +615,11 @@ seamlessly in local dev and in remote environments.
|
|
|
615
615
|
|
|
616
616
|
- **Standard environment variables to respect:**
|
|
617
617
|
|
|
618
|
-
- `NO_COLOR
|
|
619
|
-
- `FORCE_COLOR
|
|
620
|
-
- `CI
|
|
621
|
-
- `DEBUG
|
|
622
|
-
- `PAGER
|
|
618
|
+
- `NO_COLOR`—disable colors (standard)
|
|
619
|
+
- `FORCE_COLOR`—force colors
|
|
620
|
+
- `CI`—detect CI environment, force non-interactive
|
|
621
|
+
- `DEBUG`—enable debug logging (or a namespaced equivalent like `<TOOL>_DEBUG`)
|
|
622
|
+
- `PAGER`—custom pager command for long output
|
|
623
623
|
|
|
624
624
|
## Sub-Command Logging for Testability
|
|
625
625
|
|
|
@@ -705,27 +705,27 @@ runtimes, Cloudflare Workers, etc.).
|
|
|
705
705
|
|
|
706
706
|
**Key patterns:**
|
|
707
707
|
|
|
708
|
-
- **Base Command Pattern
|
|
708
|
+
- **Base Command Pattern**—All handlers extend `BaseCommand`, which provides
|
|
709
709
|
`CommandContext`, `OutputManager`, `execute()` error wrapping, and `checkDryRun()`
|
|
710
710
|
|
|
711
|
-
- **Dual Output Mode
|
|
711
|
+
- **Dual Output Mode**—`OutputManager.data(data, textFormatter)` switches between JSON
|
|
712
712
|
and text formatting based on `--json` flag
|
|
713
713
|
|
|
714
|
-
- **Handler + Command Structure
|
|
714
|
+
- **Handler + Command Structure**—Command definition (`.option()`, `.action()`) is
|
|
715
715
|
separate from handler class implementation.
|
|
716
716
|
Action handlers do `new XxxHandler(command)` then `handler.run(options)`
|
|
717
717
|
|
|
718
|
-
- **Version Handling
|
|
718
|
+
- **Version Handling**—Prefer deterministic runtime version resolution: build-time
|
|
719
719
|
injection first, then environment override for dev/test, then `package.json` fallback
|
|
720
720
|
|
|
721
|
-
- **Global Options
|
|
721
|
+
- **Global Options**—Define `--dry-run`, `--verbose`, `--quiet`, `--json`, `--color`,
|
|
722
722
|
and `--debug` at program level, plus tool-specific options as needed.
|
|
723
723
|
Only add `--non-interactive` and `--yes` if the CLI has interactive prompts
|
|
724
724
|
|
|
725
|
-
- **Stdout/Stderr Separation
|
|
725
|
+
- **Stdout/Stderr Separation**—Data to stdout, diagnostics to stderr for pipeline
|
|
726
726
|
compatibility. See the Stdout/Stderr Separation section above for details
|
|
727
727
|
|
|
728
|
-
- **Terminal Width Management
|
|
728
|
+
- **Terminal Width Management**—Cap help text and formatted output at a maximum width
|
|
729
729
|
(e.g., 88 chars) for readability, using narrower if the terminal is smaller
|
|
730
730
|
|
|
731
731
|
## Best Practices
|
|
@@ -7,14 +7,14 @@ author: Joshua Levy (github.com/jlevy) with LLM assistance
|
|
|
7
7
|
|
|
8
8
|
**Last Updated**: 2026-05-21
|
|
9
9
|
|
|
10
|
-
**Tracks**: Vitest `^4.1.7`, `@vitest/coverage-v8` `^4.1.7`. Vitest 5.0 is in beta
|
|
10
|
+
**Tracks**: Vitest `^4.1.7`, `@vitest/coverage-v8` `^4.1.7`. Vitest 5.0 is in beta—do
|
|
11
11
|
not adopt yet.
|
|
12
12
|
|
|
13
13
|
**Related**:
|
|
14
14
|
|
|
15
|
-
- [Companion: pnpm Monorepo Patterns
|
|
16
|
-
- [Supply-Chain Mitigation](./pnpm-monorepo-patterns.md#supply-chain-mitigation)
|
|
17
|
-
|
|
15
|
+
- [Companion: pnpm Monorepo Patterns—Testing](./pnpm-monorepo-patterns.md#8-testing)
|
|
16
|
+
- [Supply-Chain Mitigation](./pnpm-monorepo-patterns.md#supply-chain-mitigation)—follow
|
|
17
|
+
the 14-day package-age rule when installing or upgrading `vitest` and
|
|
18
18
|
`@vitest/coverage-v8`.
|
|
19
19
|
|
|
20
20
|
## Coverage Metrics
|
|
@@ -95,7 +95,7 @@ upgrade: use `ncu --cooldown 14` or `pnpm install --frozen-lockfile`.
|
|
|
95
95
|
- **`coverage.all` was removed** in Vitest 4. Use `coverage.include` and
|
|
96
96
|
`coverage.exclude` to define exactly which files are reported.
|
|
97
97
|
- Coverage reporters and v8 provider now ship as part of `@vitest/coverage-v8` aligned
|
|
98
|
-
with the Vitest major version
|
|
98
|
+
with the Vitest major version—pin them together.
|
|
99
99
|
|
|
100
100
|
### Example Configuration
|
|
101
101
|
|
|
@@ -11,7 +11,7 @@ alwaysApply: true
|
|
|
11
11
|
|
|
12
12
|
**Tracks**: TypeScript `^6.0.3` (stable).
|
|
13
13
|
TypeScript 7.0 Beta (`@typescript/native-preview`, binary `tsgo`) is available but **not
|
|
14
|
-
yet production-ready
|
|
14
|
+
yet production-ready**—do not adopt for shipped builds.
|
|
15
15
|
|
|
16
16
|
**Related**:
|
|
17
17
|
|
|
@@ -21,7 +21,7 @@ yet production-ready** — do not adopt for shipped builds.
|
|
|
21
21
|
- [TypeScript Code Coverage](./typescript-code-coverage.md)
|
|
22
22
|
- [pnpm Monorepo Patterns](./pnpm-monorepo-patterns.md) and
|
|
23
23
|
[Bun Monorepo Patterns](./bun-monorepo-patterns.md)
|
|
24
|
-
- [Supply-Chain Mitigation](./pnpm-monorepo-patterns.md#supply-chain-mitigation)
|
|
24
|
+
- [Supply-Chain Mitigation](./pnpm-monorepo-patterns.md#supply-chain-mitigation)—the
|
|
25
25
|
14-day package-age rule applies to every TypeScript dependency (`zod`, `commander`,
|
|
26
26
|
`vitest`, `eslint`, type packages, etc.).
|
|
27
27
|
|
|
@@ -352,7 +352,7 @@ yet production-ready** — do not adopt for shipped builds.
|
|
|
352
352
|
|
|
353
353
|
- **Barrel files:** The rules differ for libraries vs applications.
|
|
354
354
|
|
|
355
|
-
**For libraries:** Use exactly ONE barrel file
|
|
355
|
+
**For libraries:** Use exactly ONE barrel file—the root `index.ts` that defines the
|
|
356
356
|
public API. This is essential for consumers who `import { X } from 'your-library'`. Do
|
|
357
357
|
NOT create module-level barrels (like `utils/index.ts` or `harness/index.ts`).
|
|
358
358
|
Internal code should import directly from source files.
|
|
@@ -9,14 +9,14 @@ globs: "*.ts"
|
|
|
9
9
|
**Last Updated**: 2026-05-21
|
|
10
10
|
|
|
11
11
|
**Tracks**: `yaml@^2.8.4` (latest stable; 2026-05-02). The `yaml@3.0.0-1` release is
|
|
12
|
-
tagged `next` (pre-release)
|
|
12
|
+
tagged `next` (pre-release)—do not adopt yet.
|
|
13
13
|
Zod 4.x is the recommended validation companion.
|
|
14
14
|
|
|
15
15
|
**Related**:
|
|
16
16
|
|
|
17
17
|
- [TypeScript Rules](./typescript-rules.md)
|
|
18
|
-
- [Supply-Chain Mitigation](./pnpm-monorepo-patterns.md#supply-chain-mitigation)
|
|
19
|
-
|
|
18
|
+
- [Supply-Chain Mitigation](./pnpm-monorepo-patterns.md#supply-chain-mitigation)—follow
|
|
19
|
+
the 14-day package-age rule for `yaml`, `zod`, and `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,
|
package/dist/docs/tbd-design.md
CHANGED
|
@@ -235,23 +235,23 @@ on demand.
|
|
|
235
235
|
|
|
236
236
|
tbd provides **three integrated capabilities**:
|
|
237
237
|
|
|
238
|
-
1. **Task tracking (beads)
|
|
238
|
+
1. **Task tracking (beads)**—Git-native issues, bugs, epics, and dependencies that
|
|
239
239
|
persist across sessions.
|
|
240
240
|
This alone is a step change in what agents can do.
|
|
241
|
-
2. **Spec-driven planning
|
|
242
|
-
|
|
243
|
-
3. **Instant knowledge injection
|
|
244
|
-
Python, Convex, monorepo architecture, TDD, and more
|
|
241
|
+
2. **Spec-driven planning**—Workflows for writing specs, breaking them into issues, and
|
|
242
|
+
implementing systematically.
|
|
243
|
+
3. **Instant knowledge injection**—17+ detailed guideline docs covering TypeScript,
|
|
244
|
+
Python, Convex, monorepo architecture, TDD, and more—injected into the agent’s
|
|
245
245
|
context on demand via shortcuts, guidelines, and templates.
|
|
246
246
|
|
|
247
247
|
The **issue tracking layer** has four core principles:
|
|
248
248
|
|
|
249
|
-
- **Durable storage in git
|
|
250
|
-
|
|
251
|
-
- **Works in almost any environment
|
|
249
|
+
- **Durable storage in git**—Issues are version-controlled and distributed via standard
|
|
250
|
+
git
|
|
251
|
+
- **Works in almost any environment**—No daemon, no SQLite, no file locking issues on
|
|
252
252
|
network drives
|
|
253
|
-
- **Simple, self-documenting CLI
|
|
254
|
-
- **Transparent internal format
|
|
253
|
+
- **Simple, self-documenting CLI**—Designed for both AI agents and humans
|
|
254
|
+
- **Transparent internal format**—Markdown/YAML files that are debuggable and friendly
|
|
255
255
|
to other tooling
|
|
256
256
|
|
|
257
257
|
It does *not* aim to be a full solution for real-time agent coordination.
|
|
@@ -291,17 +291,17 @@ layered on top of tbd or handled by other tools.
|
|
|
291
291
|
|
|
292
292
|
**Related Projects:**
|
|
293
293
|
|
|
294
|
-
- [Beads](https://github.com/steveyegge/beads)
|
|
295
|
-
|
|
296
|
-
- [Agent Mail](https://github.com/Dicklesworthstone/mcp_agent_mail)
|
|
294
|
+
- [Beads](https://github.com/steveyegge/beads)—The original git-backed issue tracker tbd
|
|
295
|
+
is designed to replace
|
|
296
|
+
- [Agent Mail](https://github.com/Dicklesworthstone/mcp_agent_mail)—Real-time agent
|
|
297
297
|
messaging via MCP (complementary to tbd for coordination)
|
|
298
|
-
- [Gas Town](https://github.com/steveyegge/gastown)
|
|
298
|
+
- [Gas Town](https://github.com/steveyegge/gastown)—Multi-agent orchestration platform
|
|
299
299
|
(complementary to tbd for real-time coordination)
|
|
300
|
-
- [ticket](https://github.com/wedow/ticket)
|
|
300
|
+
- [ticket](https://github.com/wedow/ticket)—Bash-based Markdown+YAML tracker (~1900
|
|
301
301
|
tickets in production)
|
|
302
|
-
- [git-bug](https://github.com/git-bug/git-bug)
|
|
303
|
-
- [git-issue](https://github.com/dspinellis/git-issue)
|
|
304
|
-
|
|
302
|
+
- [git-bug](https://github.com/git-bug/git-bug)—Issues stored as git objects
|
|
303
|
+
- [git-issue](https://github.com/dspinellis/git-issue)—Shell-based with optional GitHub
|
|
304
|
+
sync
|
|
305
305
|
|
|
306
306
|
### 1.2 When to Use tbd vs Beads
|
|
307
307
|
|
|
@@ -790,10 +790,10 @@ $GIT_COMMON_DIR/tbd/
|
|
|
790
790
|
└── meta.yml # Metadata (schema version)
|
|
791
791
|
```
|
|
792
792
|
|
|
793
|
-
> **Future: Simple Mode
|
|
794
|
-
>
|
|
795
|
-
>
|
|
796
|
-
>
|
|
793
|
+
> **Future: Simple Mode**—For users who don’t need multi-machine sync, tbd could support
|
|
794
|
+
> a “simple mode” where `data-sync/` is committed directly to main instead of using a
|
|
795
|
+
> worktree. This would be enabled by removing `data-sync` from `.tbd/.gitignore`. Not
|
|
796
|
+
> implemented in V1, but the naming structure supports this future option.
|
|
797
797
|
|
|
798
798
|
**Why this structure?**
|
|
799
799
|
|
|
@@ -887,7 +887,7 @@ backups/
|
|
|
887
887
|
> Both differ from `.tbd/data-sync/attic/` on the sync branch which stores merge
|
|
888
888
|
> conflict losers.
|
|
889
889
|
>
|
|
890
|
-
> **Note:** `workspaces/` must not be gitignored
|
|
890
|
+
> **Note:** `workspaces/` must not be gitignored—it stores outbox data that must be
|
|
891
891
|
> committed to the working branch.
|
|
892
892
|
|
|
893
893
|
#### .tbd/.gitattributes Contents
|
|
@@ -904,7 +904,7 @@ that directory.
|
|
|
904
904
|
```
|
|
905
905
|
|
|
906
906
|
> **Why this is needed:** When a feature branch with outbox changes is merged back to
|
|
907
|
-
> main (which has no outbox), git’s 3-way merge can delete `ids.yml` entirely
|
|
907
|
+
> main (which has no outbox), git’s 3-way merge can delete `ids.yml` entirely—treating
|
|
908
908
|
> “no file” on main as the correct state.
|
|
909
909
|
> This causes all tbd commands to crash with “No short ID mapping found”.
|
|
910
910
|
> The `merge=union` built-in merge driver keeps all lines from both sides, preventing
|
|
@@ -1037,8 +1037,8 @@ async function checkWorktreeHealth(baseDir: string): Promise<{
|
|
|
1037
1037
|
|
|
1038
1038
|
| Term | Path | Purpose |
|
|
1039
1039
|
| --- | --- | --- |
|
|
1040
|
-
| **Worktree path** | `$GIT_COMMON_DIR/tbd/data-sync-worktree/.tbd/data-sync/` | **Production path
|
|
1041
|
-
| **Direct path** | `.tbd/data-sync/` | **Legacy fallback path
|
|
1040
|
+
| **Worktree path** | `$GIT_COMMON_DIR/tbd/data-sync-worktree/.tbd/data-sync/` | **Production path**—inside hidden worktree checkout |
|
|
1041
|
+
| **Direct path** | `.tbd/data-sync/` | **Legacy fallback path**—gitignored on main, should NEVER contain data in production |
|
|
1042
1042
|
|
|
1043
1043
|
**Invariant:** In production, the worktree path is the ONLY correct path for issue data.
|
|
1044
1044
|
The direct path exists ONLY for test fixtures that don’t use git.
|
|
@@ -1082,7 +1082,7 @@ async function resolveDataSyncDir(
|
|
|
1082
1082
|
|
|
1083
1083
|
1. Production code MUST call `resolveDataSyncDir()` without `allowFallback`
|
|
1084
1084
|
2. Only test code may use `allowFallback: true`
|
|
1085
|
-
3. If `.tbd/data-sync/issues/` contains data on main branch, this indicates a bug
|
|
1085
|
+
3. If `.tbd/data-sync/issues/` contains data on main branch, this indicates a bug—data
|
|
1086
1086
|
was written to wrong location due to missing worktree
|
|
1087
1087
|
|
|
1088
1088
|
#### Worktree Error Classes
|
|
@@ -1114,7 +1114,7 @@ export class SyncBranchError extends TbdError {
|
|
|
1114
1114
|
Workspaces are directories under `.tbd/workspaces/` that store issue data for sync
|
|
1115
1115
|
failure recovery, backups, and bulk editing workflows.
|
|
1116
1116
|
|
|
1117
|
-
> **Note:** `.tbd/workspaces/` must not be gitignored
|
|
1117
|
+
> **Note:** `.tbd/workspaces/` must not be gitignored—outbox data must be committed to
|
|
1118
1118
|
> the working branch.
|
|
1119
1119
|
|
|
1120
1120
|
#### Workspace Structure
|
|
@@ -2147,7 +2147,7 @@ SYNC(options):
|
|
|
2147
2147
|
```
|
|
2148
2148
|
|
|
2149
2149
|
**Critical Invariant:** All operations in steps 1-6 MUST use the resolved `dataSyncDir`
|
|
2150
|
-
path consistently. Never read from or write to `.tbd/data-sync/` directly
|
|
2150
|
+
path consistently. Never read from or write to `.tbd/data-sync/` directly—always go
|
|
2151
2151
|
through the shared worktree at `$GIT_COMMON_DIR/tbd/data-sync-worktree/.tbd/data-sync/`.
|
|
2152
2152
|
|
|
2153
2153
|
**Why most syncs are trivial (no merge needed):**
|
|
@@ -2374,8 +2374,8 @@ The CLI Layer provides a Beads-compatible command interface.
|
|
|
2374
2374
|
|
|
2375
2375
|
All tbd commands require the repository to be initialized, except:
|
|
2376
2376
|
|
|
2377
|
-
- `tbd init
|
|
2378
|
-
- `tbd import --from-beads
|
|
2377
|
+
- `tbd init`—Creates a new tbd repository
|
|
2378
|
+
- `tbd import --from-beads`—Can initialize and import in one step (auto-runs init if
|
|
2379
2379
|
needed)
|
|
2380
2380
|
|
|
2381
2381
|
**Behavior when not initialized:**
|
|
@@ -2572,8 +2572,8 @@ Options:
|
|
|
2572
2572
|
created/updated: newest first).
|
|
2573
2573
|
The tiebreaker for issues with equal primary values is the internal ULID, which sorts
|
|
2574
2574
|
lexicographically in chronological creation order.
|
|
2575
|
-
This ensures deterministic, stable ordering
|
|
2576
|
-
|
|
2575
|
+
This ensures deterministic, stable ordering—issues created earlier always appear before
|
|
2576
|
+
issues created later within the same priority level.
|
|
2577
2577
|
|
|
2578
2578
|
**Examples:**
|
|
2579
2579
|
|
|
@@ -3498,9 +3498,9 @@ This follows the same convention as `git`, `ls`, `grep`, and other Unix tools.
|
|
|
3498
3498
|
The actor name (used for `created_by` and recorded in sync commits) is resolved in this
|
|
3499
3499
|
order:
|
|
3500
3500
|
|
|
3501
|
-
1. `--actor <name>` CLI flag (highest priority)
|
|
3501
|
+
1. `--actor <name>` CLI flag (highest priority)—*not yet implemented*
|
|
3502
3502
|
|
|
3503
|
-
2. `TBD_ACTOR` environment variable
|
|
3503
|
+
2. `TBD_ACTOR` environment variable—*not yet implemented*
|
|
3504
3504
|
|
|
3505
3505
|
3. Git user.email from git config
|
|
3506
3506
|
|
|
@@ -4571,8 +4571,8 @@ all environments including cloud sandboxes.
|
|
|
4571
4571
|
|
|
4572
4572
|
Claude Code hooks are always installed to the **project-local** `.claude/` directory,
|
|
4573
4573
|
adjacent to `.git/` and `.tbd/` at the git repository root.
|
|
4574
|
-
There is no global/user-level installation
|
|
4575
|
-
|
|
4574
|
+
There is no global/user-level installation—this avoids confusion and ensures hooks work
|
|
4575
|
+
in any environment (local dev, Claude Code Cloud, etc.).
|
|
4576
4576
|
|
|
4577
4577
|
**A. JSON Settings Hooks** (installed to `.claude/settings.json` at project root)
|
|
4578
4578
|
|
|
@@ -4674,7 +4674,7 @@ Options:
|
|
|
4674
4674
|
- `tbd list --status=in_progress` - Your active work
|
|
4675
4675
|
- `tbd show <id>` - Detailed issue view with dependencies
|
|
4676
4676
|
|
|
4677
|
-
### Creating
|
|
4677
|
+
### Creating and Updating
|
|
4678
4678
|
- `tbd create "title" --type=task|bug|feature --priority=P2` - New issue
|
|
4679
4679
|
- Priority: P0-P4 (P0=critical, P2=medium, P4=backlog)
|
|
4680
4680
|
- `tbd update <id> --status=in_progress` - Claim work
|
|
@@ -4682,12 +4682,12 @@ Options:
|
|
|
4682
4682
|
- `tbd close <id>` - Mark complete
|
|
4683
4683
|
- `tbd close <id> --reason "explanation"` - Close with reason
|
|
4684
4684
|
|
|
4685
|
-
### Dependencies
|
|
4685
|
+
### Dependencies and Blocking
|
|
4686
4686
|
- `tbd dep add <issue> <depends-on>` - Add dependency
|
|
4687
4687
|
- `tbd blocked` - Show all blocked issues
|
|
4688
4688
|
- `tbd show <id>` - See what's blocking/blocked by this issue
|
|
4689
4689
|
|
|
4690
|
-
### Sync
|
|
4690
|
+
### Sync and Collaboration
|
|
4691
4691
|
- `tbd sync` - Sync with git remote (run at session end)
|
|
4692
4692
|
- `tbd sync --status` - Check sync status without syncing
|
|
4693
4693
|
|
package/dist/docs/tbd-docs.md
CHANGED
|
@@ -30,7 +30,7 @@ Why a separate branch?
|
|
|
30
30
|
- No conflicts across main or feature branches
|
|
31
31
|
- Issues shared across all branches
|
|
32
32
|
|
|
33
|
-
## File
|
|
33
|
+
## File Format
|
|
34
34
|
|
|
35
35
|
You usually don’t need to worry about where issues are stored, but it may be comforting
|
|
36
36
|
to know that internally it’s very simple and transparent.
|
package/dist/docs/tbd-prime.md
CHANGED
|
@@ -52,7 +52,7 @@ Every session must end with tbd in a clean state:
|
|
|
52
52
|
- `tbd show <id>` - Detailed issue view with dependencies
|
|
53
53
|
- Auto-displays parent context for child issues (use `--no-parent` to suppress)
|
|
54
54
|
|
|
55
|
-
### Creating
|
|
55
|
+
### Creating and Updating
|
|
56
56
|
|
|
57
57
|
- `tbd create "title" --type task|bug|feature --priority 2` - New issue
|
|
58
58
|
- Priority: 0-4 (0=critical, 2=medium, 4=backlog).
|
|
@@ -63,13 +63,13 @@ Every session must end with tbd in a clean state:
|
|
|
63
63
|
- `tbd close <id> --reason "explanation"` - Close with reason
|
|
64
64
|
- **Tip**: When creating multiple issues, use parallel subagents for efficiency
|
|
65
65
|
|
|
66
|
-
### Dependencies
|
|
66
|
+
### Dependencies and Blocking
|
|
67
67
|
|
|
68
68
|
- `tbd dep add <issue> <depends-on>` - Add dependency (issue depends on depends-on)
|
|
69
69
|
- `tbd blocked` - Show all blocked issues
|
|
70
70
|
- `tbd show <id>` - See what’s blocking/blocked by this issue
|
|
71
71
|
|
|
72
|
-
### Sync
|
|
72
|
+
### Sync and Collaboration
|
|
73
73
|
|
|
74
74
|
- `tbd sync` - Sync with git remote (run at session end)
|
|
75
75
|
- `tbd sync --status` - Check sync status without syncing
|
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-BpvcrLnq.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.2";
|
|
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-BpvcrLnq.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"src-CtZIHxYM.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-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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"}
|
package/dist/tbd
CHANGED
|
@@ -14104,7 +14104,7 @@ function serializeIssue(issue) {
|
|
|
14104
14104
|
* Package version, derived from git at build time.
|
|
14105
14105
|
* Format: X.Y.Z for releases, X.Y.Z-dev.N.hash for dev builds.
|
|
14106
14106
|
*/
|
|
14107
|
-
const VERSION$1 = "0.2.
|
|
14107
|
+
const VERSION$1 = "0.2.2";
|
|
14108
14108
|
|
|
14109
14109
|
//#endregion
|
|
14110
14110
|
//#region src/cli/lib/version.ts
|
|
@@ -110500,10 +110500,16 @@ var SetupAutoHandler = class extends BaseCommand {
|
|
|
110500
110500
|
const entries = await readdir(scriptsDir, { withFileTypes: true });
|
|
110501
110501
|
for (const entry of entries) if (entry.isFile()) {
|
|
110502
110502
|
const filename = entry.name;
|
|
110503
|
-
if (LEGACY_TBD_SCRIPTS.includes(filename))
|
|
110504
|
-
|
|
110505
|
-
|
|
110506
|
-
|
|
110503
|
+
if (LEGACY_TBD_SCRIPTS.includes(filename)) {
|
|
110504
|
+
if (this.ctx.dryRun) {
|
|
110505
|
+
scriptsRemoved.push(filename);
|
|
110506
|
+
continue;
|
|
110507
|
+
}
|
|
110508
|
+
try {
|
|
110509
|
+
await rm(join(scriptsDir, filename));
|
|
110510
|
+
scriptsRemoved.push(filename);
|
|
110511
|
+
} catch {}
|
|
110512
|
+
}
|
|
110507
110513
|
}
|
|
110508
110514
|
} catch {}
|
|
110509
110515
|
return scriptsRemoved;
|
|
@@ -110547,7 +110553,7 @@ var SetupAutoHandler = class extends BaseCommand {
|
|
|
110547
110553
|
modified = true;
|
|
110548
110554
|
}
|
|
110549
110555
|
}
|
|
110550
|
-
if (modified) {
|
|
110556
|
+
if (modified && !this.ctx.dryRun) {
|
|
110551
110557
|
if (Object.keys(hooks).length === 0) delete settings.hooks;
|
|
110552
110558
|
await writeFile(projectSettingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
110553
110559
|
}
|
|
@@ -110565,7 +110571,8 @@ var SetupAutoHandler = class extends BaseCommand {
|
|
|
110565
110571
|
const parts = [];
|
|
110566
110572
|
if (scriptsRemoved.length > 0) parts.push(`${scriptsRemoved.length} script(s)`);
|
|
110567
110573
|
if (hooksRemoved > 0) parts.push(`${hooksRemoved} hook(s)`);
|
|
110568
|
-
|
|
110574
|
+
if (this.ctx.dryRun) this.output.dryRun(`Would clean up legacy ${parts.join(" and ")}`);
|
|
110575
|
+
else console.log(colors.dim(`Cleaned up legacy ${parts.join(" and ")}`));
|
|
110569
110576
|
}
|
|
110570
110577
|
await this.syncDocs(cwd);
|
|
110571
110578
|
const targeting = this.resolveTargeting();
|