get-tbd 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +38 -6
  2. package/dist/bin.mjs +3494 -949
  3. package/dist/bin.mjs.map +1 -1
  4. package/dist/cli.mjs +1827 -1312
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/{config-1ouUTKQr.mjs → config-B1w3pAkY.mjs} +142 -290
  7. package/dist/config-B1w3pAkY.mjs.map +1 -0
  8. package/dist/config-DXhifxOw.mjs +3 -0
  9. package/dist/doc-cache-CamtXfi4.mjs +1035 -0
  10. package/dist/doc-cache-CamtXfi4.mjs.map +1 -0
  11. package/dist/doc-cache-CiBwpDTf.mjs +3 -0
  12. package/dist/doc-fork-BMjqzfAs.mjs +3 -0
  13. package/dist/doc-fork-CoPi1G1N.mjs +304 -0
  14. package/dist/doc-fork-CoPi1G1N.mjs.map +1 -0
  15. package/dist/docref-C7g0sjvL.mjs +167 -0
  16. package/dist/docref-C7g0sjvL.mjs.map +1 -0
  17. package/dist/docs/README.md +38 -6
  18. package/dist/docs/SKILL.md +13 -4
  19. package/dist/docs/guidelines/backward-compatibility-rules.md +1 -0
  20. package/dist/docs/guidelines/bun-monorepo-patterns.md +1 -0
  21. package/dist/docs/guidelines/cli-agent-skill-patterns.md +124 -38
  22. package/dist/docs/guidelines/commit-conventions.md +1 -0
  23. package/dist/docs/guidelines/common-doc-guidelines.md +1 -0
  24. package/dist/docs/guidelines/convex-limits-best-practices.md +1 -0
  25. package/dist/docs/guidelines/convex-rules.md +1 -0
  26. package/dist/docs/guidelines/electron-app-development-patterns.md +1 -0
  27. package/dist/docs/guidelines/error-handling-rules.md +1 -0
  28. package/dist/docs/guidelines/general-coding-rules.md +1 -0
  29. package/dist/docs/guidelines/general-comment-rules.md +1 -0
  30. package/dist/docs/guidelines/general-eng-agent-principles.md +1 -0
  31. package/dist/docs/guidelines/general-tdd-guidelines.md +1 -0
  32. package/dist/docs/guidelines/general-testing-rules.md +1 -0
  33. package/dist/docs/guidelines/golden-testing-guidelines.md +1 -0
  34. package/dist/docs/guidelines/pnpm-monorepo-patterns.md +1 -0
  35. package/dist/docs/guidelines/python-cli-patterns.md +1 -0
  36. package/dist/docs/guidelines/python-modern-guidelines.md +1 -0
  37. package/dist/docs/guidelines/python-rules.md +1 -0
  38. package/dist/docs/guidelines/release-notes-guidelines.md +1 -0
  39. package/dist/docs/guidelines/supply-chain-hardening.md +3 -2
  40. package/dist/docs/guidelines/tbd-sync-troubleshooting.md +1 -0
  41. package/dist/docs/guidelines/typescript-cli-tool-rules.md +1 -0
  42. package/dist/docs/guidelines/typescript-code-coverage.md +1 -0
  43. package/dist/docs/guidelines/typescript-rules.md +1 -0
  44. package/dist/docs/guidelines/typescript-sorting-patterns.md +1 -0
  45. package/dist/docs/guidelines/typescript-yaml-handling-rules.md +1 -0
  46. package/dist/docs/references/docmap-format.md +64 -0
  47. package/dist/docs/references/docref-format.md +71 -0
  48. package/dist/docs/shortcuts/standard/checkout-third-party-repo.md +17 -4
  49. package/dist/docs/shortcuts/standard/new-shortcut.md +5 -5
  50. package/dist/docs/shortcuts/standard/plan-implementation-with-beads.md +1 -1
  51. package/dist/docs/shortcuts/standard/suggest-upstream-improvements.md +69 -0
  52. package/dist/docs/shortcuts/standard/sync-failure-recovery.md +1 -1
  53. package/dist/docs/shortcuts/standard/welcome-user.md +28 -0
  54. package/dist/docs/shortcuts/system/skill-baseline.md +13 -4
  55. package/dist/docs/tbd-design.md +141 -0
  56. package/dist/docs/tbd-docs.md +169 -5
  57. package/dist/docs/tbd-prime.md +0 -1
  58. package/dist/docs/templates/qa-playbook.md +3 -1
  59. package/dist/docs/templates/research-brief.md +2 -2
  60. package/dist/fork-manifest-ByU7U2do.mjs +253 -0
  61. package/dist/fork-manifest-ByU7U2do.mjs.map +1 -0
  62. package/dist/fork-manifest-C7lGRq-6.mjs +3 -0
  63. package/dist/{id-mapping-mtoSP9Qt.mjs → id-mapping-D0iitY-F.mjs} +1 -1
  64. package/dist/{id-mapping-687_UEsy.mjs → id-mapping-DAIeLKzm.mjs} +8 -200
  65. package/dist/id-mapping-DAIeLKzm.mjs.map +1 -0
  66. package/dist/index.d.mts +90 -13
  67. package/dist/index.mjs +3 -3
  68. package/dist/lockfile-BR0laSDy.mjs +198 -0
  69. package/dist/lockfile-BR0laSDy.mjs.map +1 -0
  70. package/dist/paths-C1DpnZJW.mjs +405 -0
  71. package/dist/paths-C1DpnZJW.mjs.map +1 -0
  72. package/dist/{schemas-f0EcuAVu.mjs → schemas-lCwRk30L.mjs} +19 -6
  73. package/dist/schemas-lCwRk30L.mjs.map +1 -0
  74. package/dist/{src-DTyyuaG_.mjs → src-CxKOynr1.mjs} +3 -3
  75. package/dist/src-CxKOynr1.mjs.map +1 -0
  76. package/dist/tbd +3494 -949
  77. package/dist/yaml-utils-BPy991by.mjs.map +1 -1
  78. package/package.json +1 -1
  79. package/dist/config-1ouUTKQr.mjs.map +0 -1
  80. package/dist/config-YRRW9l89.mjs +0 -3
  81. package/dist/id-mapping-687_UEsy.mjs.map +0 -1
  82. package/dist/schemas-f0EcuAVu.mjs.map +0 -1
  83. package/dist/src-DTyyuaG_.mjs.map +0 -1
@@ -0,0 +1,167 @@
1
+ //#region src/docref/docref.ts
2
+ /** Error thrown when a string is not a valid docref. */
3
+ var DocRefError = class extends Error {
4
+ constructor(input, detail) {
5
+ super(`Invalid docref ${JSON.stringify(input)}: ${detail}`);
6
+ this.input = input;
7
+ this.name = "DocRefError";
8
+ }
9
+ };
10
+ const GIT_SCHEMES = ["github", "gitlab"];
11
+ /**
12
+ * True for strings that address a local filesystem path: anchored relative
13
+ * (`./`, `../`), absolute (`/`), or a Windows drive-letter path (`C:/`, `C:\`).
14
+ */
15
+ function looksLocal(input) {
16
+ return input.startsWith("./") || input.startsWith("../") || input.startsWith("/") || /^[A-Za-z]:[\\/]/.test(input);
17
+ }
18
+ /**
19
+ * Parse a `host:owner/repo[@ref]//path[#fragment]` body (everything after the scheme).
20
+ */
21
+ function parseGitBody(host, body, input) {
22
+ const sep = body.indexOf("//");
23
+ if (sep === -1) throw new DocRefError(input, `git docref must contain "//" separating repo from path`);
24
+ const repoPart = body.slice(0, sep);
25
+ const pathPart = body.slice(sep + 2);
26
+ const hashIndex = pathPart.indexOf("#");
27
+ const path = hashIndex === -1 ? pathPart : pathPart.slice(0, hashIndex);
28
+ const fragment = hashIndex === -1 ? void 0 : pathPart.slice(hashIndex + 1) || void 0;
29
+ if (!path) throw new DocRefError(input, "git docref has an empty path");
30
+ const atIndex = repoPart.indexOf("@");
31
+ const ownerRepo = atIndex === -1 ? repoPart : repoPart.slice(0, atIndex);
32
+ const ref = atIndex === -1 ? void 0 : repoPart.slice(atIndex + 1) || void 0;
33
+ const slash = ownerRepo.indexOf("/");
34
+ if (slash === -1) throw new DocRefError(input, "git docref must be \"owner/repo\"");
35
+ const owner = ownerRepo.slice(0, slash);
36
+ const repo = ownerRepo.slice(slash + 1);
37
+ if (!owner || !repo) throw new DocRefError(input, "git docref must be \"owner/repo\"");
38
+ return {
39
+ kind: "git",
40
+ host,
41
+ owner,
42
+ repo,
43
+ path,
44
+ ...ref !== void 0 ? { ref } : {},
45
+ ...fragment !== void 0 ? { fragment } : {}
46
+ };
47
+ }
48
+ /**
49
+ * If `url` points at a known git host's file view, return the equivalent git
50
+ * docref; otherwise return null (caller keeps it as a plain URL).
51
+ * URL fragments are preserved; normalization must never silently drop data.
52
+ */
53
+ function gitRefFromUrl(url) {
54
+ let parsed;
55
+ try {
56
+ parsed = new URL(url);
57
+ } catch {
58
+ return null;
59
+ }
60
+ const segments = parsed.pathname.split("/").filter(Boolean);
61
+ const fragment = parsed.hash ? parsed.hash.slice(1) || void 0 : void 0;
62
+ const frag = fragment !== void 0 ? { fragment } : {};
63
+ if (parsed.hostname === "github.com" && segments[2] === "blob" && segments.length >= 5) {
64
+ const [owner, repo, , ref, ...rest] = segments;
65
+ return {
66
+ kind: "git",
67
+ host: "github",
68
+ owner,
69
+ repo,
70
+ ref,
71
+ path: rest.join("/"),
72
+ ...frag
73
+ };
74
+ }
75
+ if (parsed.hostname === "raw.githubusercontent.com" && segments.length >= 4) {
76
+ const [owner, repo, ref, ...rest] = segments;
77
+ return {
78
+ kind: "git",
79
+ host: "github",
80
+ owner,
81
+ repo,
82
+ ref,
83
+ path: rest.join("/"),
84
+ ...frag
85
+ };
86
+ }
87
+ if (parsed.hostname === "gitlab.com" && segments[2] === "-" && segments[3] === "blob") {
88
+ const [owner, repo, , , ref, ...rest] = segments;
89
+ if (owner && repo && ref && rest.length > 0) return {
90
+ kind: "git",
91
+ host: "gitlab",
92
+ owner,
93
+ repo,
94
+ ref,
95
+ path: rest.join("/"),
96
+ ...frag
97
+ };
98
+ }
99
+ return null;
100
+ }
101
+ /**
102
+ * Parse a docref string into a structured {@link DocRef}.
103
+ * Throws {@link DocRefError} if the string is not a valid docref.
104
+ */
105
+ function parseDocRef(input) {
106
+ const raw = input.trim();
107
+ if (!raw) throw new DocRefError(input, "empty");
108
+ if (raw.startsWith("internal:")) {
109
+ const path = raw.slice(9);
110
+ if (!path) throw new DocRefError(input, "internal docref has an empty path");
111
+ return {
112
+ kind: "internal",
113
+ path
114
+ };
115
+ }
116
+ for (const host of GIT_SCHEMES) {
117
+ const prefix = `${host}:`;
118
+ if (raw.startsWith(prefix)) return parseGitBody(host, raw.slice(prefix.length), input);
119
+ }
120
+ if (raw.startsWith("http://") || raw.startsWith("https://")) return gitRefFromUrl(raw) ?? {
121
+ kind: "url",
122
+ url: raw
123
+ };
124
+ if (looksLocal(raw)) return {
125
+ kind: "local",
126
+ path: raw
127
+ };
128
+ if (raw.startsWith("~")) throw new DocRefError(input, "home-relative (~) paths are not supported; use an absolute or ./-relative path");
129
+ if (/^[a-z][a-z0-9+.-]*:/i.test(raw)) throw new DocRefError(input, "unknown scheme");
130
+ throw new DocRefError(input, "local paths must start with \"./\", \"../\", or \"/\" (bare relative paths are not valid docrefs)");
131
+ }
132
+ /** Parse a docref, returning null instead of throwing on invalid input. */
133
+ function tryParseDocRef(input) {
134
+ try {
135
+ return parseDocRef(input);
136
+ } catch {
137
+ return null;
138
+ }
139
+ }
140
+ /** Serialize a {@link DocRef} back to its canonical string form. */
141
+ function formatDocRef(ref) {
142
+ switch (ref.kind) {
143
+ case "internal": return `internal:${ref.path}`;
144
+ case "local": return ref.path;
145
+ case "url": return ref.url;
146
+ case "git": {
147
+ const refPart = ref.ref ? `@${ref.ref}` : "";
148
+ const fragPart = ref.fragment ? `#${ref.fragment}` : "";
149
+ return `${ref.host}:${ref.owner}/${ref.repo}${refPart}//${ref.path}${fragPart}`;
150
+ }
151
+ }
152
+ }
153
+ /**
154
+ * Normalize a docref string to its canonical form (parse + re-format).
155
+ * Rationalizes git web URLs into the `github:`/`gitlab:` scheme.
156
+ */
157
+ function normalizeDocRef(input) {
158
+ return formatDocRef(parseDocRef(input));
159
+ }
160
+ /** True if `input` parses as a valid docref. */
161
+ function isDocRef(input) {
162
+ return tryParseDocRef(input) !== null;
163
+ }
164
+
165
+ //#endregion
166
+ export { tryParseDocRef as a, parseDocRef as i, isDocRef as n, normalizeDocRef as r, DocRefError as t };
167
+ //# sourceMappingURL=docref-C7g0sjvL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docref-C7g0sjvL.mjs","names":[],"sources":["../src/docref/docref.ts"],"sourcesContent":["/**\n * docref: a single-string, URI-like address for any document.\n *\n * This module is intentionally standalone and dependency-free (no tbd-internal\n * imports) so it can move to its own package later. It is the one address syntax\n * used everywhere a doc's source or location is named: config source strings, the\n * fork manifest's `source` field, `tbd docs add` arguments, and `local_dirs` entries.\n *\n * Supported forms (docref v0.1):\n * internal:guidelines/python-rules.md bundled doc shipped inside the consuming\n * tool (app-relative, not tbd-specific)\n * ./docs/general/ ../shared/ /abs/f.md local paths; must be anchored with\n * \"./\", \"../\", \"/\", or a Windows drive\n * letter (C:/ or C:\\)\n * https://example.com/style.md plain URL\n * github:owner/repo@ref//path/to/file.md git-hosted (also gitlab:)\n * github:owner/repo@ref//file.md#section optional fragment, preserved\n *\n * The grammar is deliberately strict: bare relative strings (\"guidelines/x.md\") and\n * home-relative paths (\"~/x.md\") are NOT valid docrefs. Consumers that want lenient\n * input may coerce at their own boundary (e.g. prepend \"./\") before parsing; a\n * strict grammar plus lenient consumers composes; the reverse cannot be tightened.\n *\n * Additional protocols (for example a host-bearing git scheme for forges beyond\n * GitHub/GitLab) may be added in future versions.\n *\n * Web URLs that point at a known git host are normalized to the `github:`/`gitlab:`\n * form so there is one canonical address for a given file:\n * https://github.com/o/r/blob/main/f.md -> github:o/r@main//f.md\n * https://raw.githubusercontent.com/o/r/main/f.md -> github:o/r@main//f.md\n */\n\n/** Scheme of a git-hosted docref. */\nexport type GitHost = 'github' | 'gitlab';\n\n/** A parsed document reference. */\nexport type DocRef =\n | { readonly kind: 'internal'; readonly path: string }\n | { readonly kind: 'local'; readonly path: string }\n | { readonly kind: 'url'; readonly url: string }\n | {\n readonly kind: 'git';\n readonly host: GitHost;\n readonly owner: string;\n readonly repo: string;\n readonly ref?: string;\n readonly path: string;\n /** Optional in-document anchor (e.g. a heading slug), preserved verbatim. */\n readonly fragment?: string;\n };\n\n/** Error thrown when a string is not a valid docref. */\nexport class DocRefError extends Error {\n constructor(\n public readonly input: string,\n detail: string,\n ) {\n super(`Invalid docref ${JSON.stringify(input)}: ${detail}`);\n this.name = 'DocRefError';\n }\n}\n\nconst GIT_SCHEMES: readonly GitHost[] = ['github', 'gitlab'];\n\n/**\n * True for strings that address a local filesystem path: anchored relative\n * (`./`, `../`), absolute (`/`), or a Windows drive-letter path (`C:/`, `C:\\`).\n */\nfunction looksLocal(input: string): boolean {\n return (\n input.startsWith('./') ||\n input.startsWith('../') ||\n input.startsWith('/') ||\n /^[A-Za-z]:[\\\\/]/.test(input)\n );\n}\n\n/** Strip a single leading `./` for tidy comparison; other forms are left as-is. */\nfunction tidyLocal(path: string): string {\n return path.startsWith('./') ? path.slice(2) : path;\n}\n\n/**\n * Parse a `host:owner/repo[@ref]//path[#fragment]` body (everything after the scheme).\n */\nfunction parseGitBody(host: GitHost, body: string, input: string): DocRef {\n const sep = body.indexOf('//');\n if (sep === -1) {\n throw new DocRefError(input, `git docref must contain \"//\" separating repo from path`);\n }\n const repoPart = body.slice(0, sep);\n const pathPart = body.slice(sep + 2);\n\n const hashIndex = pathPart.indexOf('#');\n const path = hashIndex === -1 ? pathPart : pathPart.slice(0, hashIndex);\n const fragment = hashIndex === -1 ? undefined : pathPart.slice(hashIndex + 1) || undefined;\n if (!path) {\n throw new DocRefError(input, 'git docref has an empty path');\n }\n\n const atIndex = repoPart.indexOf('@');\n const ownerRepo = atIndex === -1 ? repoPart : repoPart.slice(0, atIndex);\n const ref = atIndex === -1 ? undefined : repoPart.slice(atIndex + 1) || undefined;\n\n const slash = ownerRepo.indexOf('/');\n if (slash === -1) {\n throw new DocRefError(input, 'git docref must be \"owner/repo\"');\n }\n const owner = ownerRepo.slice(0, slash);\n const repo = ownerRepo.slice(slash + 1);\n if (!owner || !repo) {\n throw new DocRefError(input, 'git docref must be \"owner/repo\"');\n }\n\n return {\n kind: 'git',\n host,\n owner,\n repo,\n path,\n ...(ref !== undefined ? { ref } : {}),\n ...(fragment !== undefined ? { fragment } : {}),\n };\n}\n\n/**\n * If `url` points at a known git host's file view, return the equivalent git\n * docref; otherwise return null (caller keeps it as a plain URL).\n * URL fragments are preserved; normalization must never silently drop data.\n */\nfunction gitRefFromUrl(url: string): DocRef | null {\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n const segments = parsed.pathname.split('/').filter(Boolean);\n const fragment = parsed.hash ? parsed.hash.slice(1) || undefined : undefined;\n const frag = fragment !== undefined ? { fragment } : {};\n\n // https://github.com/{owner}/{repo}/blob/{ref}/{path...}\n if (parsed.hostname === 'github.com' && segments[2] === 'blob' && segments.length >= 5) {\n const [owner, repo, , ref, ...rest] = segments;\n return {\n kind: 'git',\n host: 'github',\n owner: owner!,\n repo: repo!,\n ref,\n path: rest.join('/'),\n ...frag,\n };\n }\n // https://raw.githubusercontent.com/{owner}/{repo}/{ref}/{path...}\n if (parsed.hostname === 'raw.githubusercontent.com' && segments.length >= 4) {\n const [owner, repo, ref, ...rest] = segments;\n return {\n kind: 'git',\n host: 'github',\n owner: owner!,\n repo: repo!,\n ref,\n path: rest.join('/'),\n ...frag,\n };\n }\n // https://gitlab.com/{owner}/{repo}/-/blob/{ref}/{path...}\n if (parsed.hostname === 'gitlab.com' && segments[2] === '-' && segments[3] === 'blob') {\n const [owner, repo, , , ref, ...rest] = segments;\n if (owner && repo && ref && rest.length > 0) {\n return { kind: 'git', host: 'gitlab', owner, repo, ref, path: rest.join('/'), ...frag };\n }\n }\n return null;\n}\n\n/**\n * Parse a docref string into a structured {@link DocRef}.\n * Throws {@link DocRefError} if the string is not a valid docref.\n */\nexport function parseDocRef(input: string): DocRef {\n const raw = input.trim();\n if (!raw) {\n throw new DocRefError(input, 'empty');\n }\n\n // Internal bundled docs.\n if (raw.startsWith('internal:')) {\n const path = raw.slice('internal:'.length);\n if (!path) {\n throw new DocRefError(input, 'internal docref has an empty path');\n }\n return { kind: 'internal', path };\n }\n\n // Git-hosted schemes.\n for (const host of GIT_SCHEMES) {\n const prefix = `${host}:`;\n if (raw.startsWith(prefix)) {\n return parseGitBody(host, raw.slice(prefix.length), input);\n }\n }\n\n // Web URLs: normalize known git hosts, otherwise keep as a plain URL.\n if (raw.startsWith('http://') || raw.startsWith('https://')) {\n return gitRefFromUrl(raw) ?? { kind: 'url', url: raw };\n }\n\n // Local filesystem paths (anchored; includes Windows drive letters).\n if (looksLocal(raw)) {\n return { kind: 'local', path: raw };\n }\n\n if (raw.startsWith('~')) {\n throw new DocRefError(\n input,\n 'home-relative (~) paths are not supported; use an absolute or ./-relative path',\n );\n }\n if (/^[a-z][a-z0-9+.-]*:/i.test(raw)) {\n throw new DocRefError(input, 'unknown scheme');\n }\n throw new DocRefError(\n input,\n 'local paths must start with \"./\", \"../\", or \"/\" (bare relative paths are not valid docrefs)',\n );\n}\n\n/** Parse a docref, returning null instead of throwing on invalid input. */\nexport function tryParseDocRef(input: string): DocRef | null {\n try {\n return parseDocRef(input);\n } catch {\n return null;\n }\n}\n\n/** Serialize a {@link DocRef} back to its canonical string form. */\nexport function formatDocRef(ref: DocRef): string {\n switch (ref.kind) {\n case 'internal':\n return `internal:${ref.path}`;\n case 'local':\n return ref.path;\n case 'url':\n return ref.url;\n case 'git': {\n const refPart = ref.ref ? `@${ref.ref}` : '';\n const fragPart = ref.fragment ? `#${ref.fragment}` : '';\n return `${ref.host}:${ref.owner}/${ref.repo}${refPart}//${ref.path}${fragPart}`;\n }\n }\n}\n\n/**\n * Normalize a docref string to its canonical form (parse + re-format).\n * Rationalizes git web URLs into the `github:`/`gitlab:` scheme.\n */\nexport function normalizeDocRef(input: string): string {\n return formatDocRef(parseDocRef(input));\n}\n\n/** True if `input` parses as a valid docref. */\nexport function isDocRef(input: string): boolean {\n return tryParseDocRef(input) !== null;\n}\n\n/**\n * Whether two docrefs address the same document, ignoring a leading `./` on\n * local paths. Comparison is purely syntactic; no case normalization of hosts\n * or owners. Useful for de-duping config entries.\n */\nexport function docRefsEqual(a: DocRef, b: DocRef): boolean {\n if (a.kind !== b.kind) return false;\n if (a.kind === 'local' && b.kind === 'local') {\n return tidyLocal(a.path) === tidyLocal(b.path);\n }\n return formatDocRef(a) === formatDocRef(b);\n}\n"],"mappings":"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"}
@@ -37,7 +37,6 @@ will work in Cursor, Codex, or any agent environment that can use the `tbd` CLI.
37
37
  ## Quick Start
38
38
 
39
39
  > [!TIP]
40
- >
41
40
  > If running on your own machine, install the `tbd` CLI yourself:
42
41
  >
43
42
  > **`npm install -g get-tbd@latest`**
@@ -50,6 +49,11 @@ will work in Cursor, Codex, or any agent environment that can use the `tbd` CLI.
50
49
  >
51
50
  > ***“install tbd (npm install -g get-tbd@latest) and run tbd prime for instructions to
52
51
  > set up this project”***
52
+ >
53
+ > If tbd is already set up in the repo and you want the latest version, tell the agent:
54
+ >
55
+ > ***“upgrade tbd (npm install -g get-tbd@latest), run tbd setup --auto, and commit the
56
+ > changes”***
53
57
 
54
58
  That’s it.
55
59
  Running `tbd prime` gives agents full workflow context on how to use `tbd` and
@@ -122,7 +126,6 @@ agents handling different aspects that I manage) is slower, because it forces yo
122
126
  design, but it gives higher quality results.
123
127
 
124
128
  > [!NOTE]
125
- >
126
129
  > We use *Beads* (capitalized) to refer to Steve Yegge’s original
127
130
  > [`bd` tool](https://github.com/steveyegge/beads).
128
131
  > Lowercase “beads” refers generically to the issues stored in `tbd` or `bd`.
@@ -160,8 +163,8 @@ You just talk naturally.
160
163
 
161
164
  > [!NOTE]
162
165
  > For full technical details, see the [reference docs](packages/tbd/docs/tbd-docs.md)
163
- > (run `tbd docs`) or the full [design doc](packages/tbd/docs/tbd-design.md)
164
- > (`tbd design`).
166
+ > (run `tbd docs show tbd-docs`) or the full
167
+ > [design doc](packages/tbd/docs/tbd-design.md) (`tbd design`).
165
168
 
166
169
  - **Git-native:** Beads live in your repo, synced to a separate, dedicated `tbd-sync`
167
170
  branch. Your code history stays clean—no bead churn polluting your logs.
@@ -225,7 +228,6 @@ practices. These aren’t generic tips; they’re mostly my own detailed and som
225
228
  opinionated rules with concrete examples, built from months of heavy agentic coding.
226
229
 
227
230
  > [!TIP]
228
- >
229
231
  > An example: I *strongly* believe there are much better ways to do testing
230
232
  > proliferating hundreds of unit and integration tests.
231
233
  > So (with help from some Opus 4.5 and GPT-5 Pro) I wrote a multi-page brief about
@@ -292,6 +294,25 @@ tbd setup --from-beads
292
294
  > **Tip:** Run `tbd setup --auto` anytime to refresh skill files, hooks, and configs
293
295
  > with the latest shortcuts, guidelines, and templates.
294
296
 
297
+ ### Upgrading
298
+
299
+ Upgrading an existing installation is the same two commands, run by you or your agent:
300
+
301
+ ```bash
302
+ npm install -g get-tbd@latest # Upgrade the CLI
303
+ tbd setup --auto # Refresh skills/hooks and apply any format migration
304
+ ```
305
+
306
+ If the new version bumps the repository format (`tbd_format` in `.tbd/config.yml`),
307
+ setup migrates it automatically and prints a notice; **commit the resulting diff** to
308
+ publish the upgrade to your team.
309
+ Teammates still on an older tbd then see “This repository requires a newer version of
310
+ tbd” until they run the same two commands.
311
+ Issue data is never touched by an upgrade, and the migration is revertible: see
312
+ “Aborting a Format Upgrade” under Troubleshooting in the CLI manual (`tbd docs manual`).
313
+ If you have forked docs in `docs/tbd/`, `tbd sync` prints a notice when their upstream
314
+ versions moved; run `tbd docs update` to merge the changes in.
315
+
295
316
  ### Team Setup
296
317
 
297
318
  `tbd` is designed for teams where one person sets up the project and others join later.
@@ -394,11 +415,21 @@ tbd template --list # List all templates
394
415
  tbd template plan-spec # Get a plan spec template
395
416
 
396
417
  # Add your own from any URL
418
+ # (per-kind aliases for `tbd docs add <docref>`)
397
419
  tbd guidelines --add=<url> --name=<name>
398
420
  tbd shortcut --add=<url> --name=<name>
399
421
  tbd template --add=<url> --name=<name>
400
422
  ```
401
423
 
424
+ **Forkable: see them in your repo.** By default these docs are served from a hidden,
425
+ gitignored cache. Fork any of them into `docs/tbd/` and they become visible on GitHub,
426
+ reviewable in PRs, and editable in place—tbd serves your copy instead, and
427
+ `tbd docs update` merges upstream improvements into it after an upgrade:
428
+
429
+ ```bash
430
+ tbd docs fork --all # Or fork by name: tbd docs fork <name> [<name>...]
431
+ ```
432
+
402
433
  **Available shortcuts:**
403
434
 
404
435
  | Category | Shortcut | Purpose |
@@ -480,7 +511,8 @@ Every command supports these flags for automation:
480
511
  ```bash
481
512
  tbd # Full orientation and workflow guidance
482
513
  tbd readme # This file
483
- tbd docs # Full CLI reference
514
+ tbd docs # Managed-docs overview (cached, forked, and local docs)
515
+ tbd docs show tbd-docs # Full CLI reference (the manual; alias: tbd docs manual)
484
516
  ```
485
517
 
486
518
  Or read online:
@@ -31,12 +31,15 @@ allowed-tools: Bash(tbd:*), Read, Write
31
31
  ## Installation
32
32
 
33
33
  ```bash
34
- npm install -g get-tbd@latest
34
+ npm install -g get-tbd@latest # Install or upgrade the CLI (same command for both)
35
35
  tbd setup --auto --prefix=<name> # Fresh project (--prefix is REQUIRED: 2-8 alphabetic chars recommended. ALWAYS ASK THE USER FOR THE PREFIX; do not guess it)
36
- tbd setup --auto # Existing tbd project (prefix already set)
36
+ tbd setup --auto # Existing tbd project — also the upgrade step (applies any format migration; commit the diff it reports)
37
37
  tbd setup --from-beads # Migration from .beads/ if `bd` has been used
38
38
  ```
39
39
 
40
+ If tbd refuses with “This repository requires a newer version of tbd”, run the two
41
+ install/upgrade commands above.
42
+
40
43
  ## Routine Commands
41
44
 
42
45
  ```bash
@@ -48,7 +51,7 @@ tbd setup --auto # Run any time to refresh setup
48
51
  tbd prime # Restore full context on tbd after compaction
49
52
  ```
50
53
 
51
- ## CRITICAL: You Operate tbd The User Doesn’t
54
+ ## CRITICAL: You Operate tbd, the User Doesn’t
52
55
 
53
56
  **You are the tbd operator:** Users talk naturally; you translate their requests to tbd
54
57
  actions. DO NOT tell users to run tbd commands.
@@ -96,6 +99,10 @@ or want help → run `tbd shortcut welcome-user`
96
99
  | **Documentation** | |
97
100
  | “Research this topic” | `tbd shortcut new-research-brief` |
98
101
  | “Document architecture” | `tbd shortcut new-architecture-doc` |
102
+ | “What guidelines/docs are there?” | `tbd docs list` |
103
+ | “Make the guidelines visible / customize doc X” | `tbd docs fork --category=general --category=<lang>` (recommended: general + the repo’s languages), or `tbd docs fork <name>` / `--all`; then edit in `docs/tbd/` |
104
+ | “Update the guidelines to the latest” | `tbd docs update`; on conflicts ask the user, then `--merge` or `--keep-ours` |
105
+ | “I deleted a forked doc file” | `tbd docs status` shows it `missing`; restore with `tbd docs fork <name> --force` or finalize with `tbd docs unfork <name>` |
99
106
  | **Cleanup & Maintenance** | |
100
107
  | “Clean up this code” / “Remove dead code” | `tbd shortcut code-cleanup-all` |
101
108
  | “Fix repository problems” | `tbd doctor --fix` |
@@ -110,7 +117,7 @@ these apply to all code regardless of language.
110
117
  Then load the group for the language or framework in use (TypeScript, Python, Convex,
111
118
  etc.). Run `tbd guidelines --list` to see all available guidelines.
112
119
 
113
- **Note:** Never gitignore `.tbd/workspaces/` the outbox must be committed to your
120
+ **Note:** Never gitignore `.tbd/workspaces/`; the outbox must be committed to your
114
121
  working branch. See `tbd guidelines tbd-sync-troubleshooting` for details.
115
122
 
116
123
  ## CRITICAL: Session Closing Protocol
@@ -185,6 +192,8 @@ working branch. See `tbd guidelines tbd-sync-troubleshooting` for details.
185
192
  | `tbd guidelines <name>` | Load coding guidelines |
186
193
  | `tbd guidelines --list` | List guidelines |
187
194
  | `tbd template <name>` | Output a template |
195
+ | `tbd docs` / `tbd docs list` | Managed-docs overview / cross-kind list with state markers |
196
+ | `tbd docs fork/unfork/update <name>` | Fork docs into `docs/tbd/`, return to upstream, pull upstream updates |
188
197
 
189
198
  ## Quick Reference
190
199
 
@@ -2,6 +2,7 @@
2
2
  title: Backward Compatibility Rules
3
3
  description: Guidelines for maintaining backward compatibility across code, APIs, file formats, and database schemas
4
4
  author: Joshua Levy (github.com/jlevy) with LLM assistance
5
+ category: general
5
6
  ---
6
7
  ## Backward Compatibility Guidelines
7
8
 
@@ -2,6 +2,7 @@
2
2
  title: Bun Monorepo Patterns
3
3
  description: Modern patterns for Bun-based TypeScript monorepo architecture
4
4
  author: Joshua Levy (github.com/jlevy) with LLM assistance
5
+ category: typescript
5
6
  ---
6
7
  # Bun Monorepo Patterns
7
8