get-tbd 0.1.18 → 0.1.20

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 CHANGED
@@ -13576,6 +13576,12 @@ const YAML_STRINGIFY_OPTIONS_COMPACT = {
13576
13576
  * ~50 lines is slightly more than one terminal screen.
13577
13577
  */
13578
13578
  const PAGINATION_LINE_THRESHOLD = 50;
13579
+ /**
13580
+ * Maximum lines to display when showing parent bead context in `tbd show`.
13581
+ * The parent's full serialized output is truncated to this many lines,
13582
+ * with an ellipsis and omitted-line count appended if exceeded.
13583
+ */
13584
+ const PARENT_CONTEXT_MAX_LINES = 50;
13579
13585
 
13580
13586
  //#endregion
13581
13587
  //#region src/utils/yaml-utils.ts
@@ -13878,7 +13884,7 @@ function serializeIssue(issue) {
13878
13884
  * Package version, derived from git at build time.
13879
13885
  * Format: X.Y.Z for releases, X.Y.Z-dev.N.hash for dev builds.
13880
13886
  */
13881
- const VERSION$1 = "0.1.18";
13887
+ const VERSION$1 = "0.1.20";
13882
13888
 
13883
13889
  //#endregion
13884
13890
  //#region src/cli/lib/version.ts
@@ -97286,17 +97292,15 @@ async function writeFileAsync(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
97286
97292
  *
97287
97293
  * On main/dev branches:
97288
97294
  * .tbd/
97289
- * config.yml - Project configuration (tracked)
97290
- * state.yml - Local state (gitignored)
97291
- * .gitignore - Ignores docs/, data-sync-worktree/, data-sync/
97292
- * docs/ - Installed documentation (gitignored, regenerated on setup)
97293
- * data-sync-worktree/ - Hidden worktree checkout of tbd-sync branch
97294
- * .tbd/
97295
- * data-sync/
97296
- * issues/
97297
- * mappings/
97298
- * attic/
97299
- * meta.yml
97295
+ * Committed to the repo:
97296
+ * config.yml - Project configuration
97297
+ * .gitignore - Controls what's gitignored below
97298
+ * workspaces/ - Persistent state (outbox, named workspaces)
97299
+ * Gitignored (local only):
97300
+ * state.yml - Local state
97301
+ * docs/ - Installed documentation (regenerated on setup)
97302
+ * data-sync-worktree/ - Hidden worktree checkout of tbd-sync branch
97303
+ * .tbd/data-sync/ - issues/, mappings/, attic/, meta.yml
97300
97304
  *
97301
97305
  * On tbd-sync branch:
97302
97306
  * .tbd/
@@ -97444,14 +97448,14 @@ let _resolvedAllowFallback = null;
97444
97448
  * 1. Worktree path if worktree exists: .tbd/data-sync-worktree/.tbd/data-sync/
97445
97449
  * 2. Direct path as fallback (only if allowFallback: true)
97446
97450
  *
97447
- * @param baseDir - The base directory of the repository (default: process.cwd())
97451
+ * @param baseDir - The tbd root directory (from requireInit or findTbdRoot)
97448
97452
  * @param options - Options for path resolution
97449
97453
  * @returns Resolved data sync directory path
97450
97454
  * @throws WorktreeMissingError if worktree missing and allowFallback is false
97451
97455
  *
97452
97456
  * See: plan-2026-01-28-sync-worktree-recovery-and-hardening.md
97453
97457
  */
97454
- async function resolveDataSyncDir(baseDir = process.cwd(), options) {
97458
+ async function resolveDataSyncDir(baseDir, options) {
97455
97459
  const allowFallback = options?.allowFallback ?? true;
97456
97460
  if (_resolvedDataSyncDir && _resolvedBaseDir === baseDir && _resolvedAllowFallback === allowFallback) return _resolvedDataSyncDir;
97457
97461
  const worktreePath = join(baseDir, DATA_SYNC_DIR_VIA_WORKTREE);
@@ -97474,7 +97478,7 @@ async function resolveDataSyncDir(baseDir = process.cwd(), options) {
97474
97478
  /**
97475
97479
  * Resolve attic directory path.
97476
97480
  */
97477
- async function resolveAtticDir(baseDir = process.cwd(), options) {
97481
+ async function resolveAtticDir(baseDir, options) {
97478
97482
  return join(await resolveDataSyncDir(baseDir, options), "attic");
97479
97483
  }
97480
97484
  /**
@@ -97807,13 +97811,17 @@ async function writeConfig(baseDir, config) {
97807
97811
  await writeFile(configPath, content);
97808
97812
  }
97809
97813
  /**
97810
- * Check if tbd is initialized in the given directory (immediate check only).
97811
- * Returns true if .tbd/ directory exists directly in baseDir.
97814
+ * Check if tbd is properly initialized in the given directory.
97815
+ * Returns true only if .tbd/config.yml exists (not just a .tbd/ directory).
97816
+ *
97817
+ * This prevents spurious .tbd/ directories (e.g., containing only state.yml
97818
+ * created by a bug) from being mistaken for tbd roots. A valid tbd root
97819
+ * always has config.yml created during `tbd init`.
97812
97820
  */
97813
97821
  async function hasTbdDir(dir) {
97814
- const tbdDir = join(dir, ".tbd");
97822
+ const configPath = join(dir, CONFIG_FILE);
97815
97823
  try {
97816
- await access(tbdDir);
97824
+ await access(configPath);
97817
97825
  return true;
97818
97826
  } catch {
97819
97827
  return false;
@@ -97858,11 +97866,20 @@ async function readLocalState(baseDir) {
97858
97866
  }
97859
97867
  /**
97860
97868
  * Write local state to .tbd/state.yml
97869
+ *
97870
+ * Uses `atomically` for safe writes (atomic rename, auto parent-dir creation).
97871
+ * However, we intentionally guard against .tbd/ not existing: `atomically`
97872
+ * would auto-create it, which is wrong if baseDir is a subdirectory rather
97873
+ * than the true tbd root. Only `tbd init` (via initConfig) should create .tbd/.
97861
97874
  */
97862
97875
  async function writeLocalState(baseDir, state) {
97863
- const statePath = join(baseDir, STATE_FILE);
97864
- await mkdir(join(baseDir, ".tbd"), { recursive: true });
97865
- await writeFile(statePath, stringifyYaml(state, { lineWidth: 0 }));
97876
+ const tbdDir = join(baseDir, ".tbd");
97877
+ try {
97878
+ await access(tbdDir);
97879
+ } catch {
97880
+ throw new Error(`Cannot write state: .tbd/ directory does not exist at ${baseDir}. Run 'tbd init' first or ensure the correct tbd root is being used.`);
97881
+ }
97882
+ await writeFile(join(baseDir, STATE_FILE), stringifyYaml(state, { lineWidth: 0 }));
97866
97883
  }
97867
97884
  /**
97868
97885
  * Update specific fields in local state (merge with existing).
@@ -99137,7 +99154,10 @@ Example:
99137
99154
  "",
99138
99155
  "# Temporary files",
99139
99156
  "*.tmp",
99140
- "*.temp"
99157
+ "*.temp",
99158
+ "",
99159
+ "# workspaces/ stores state (including outbox) committed to the working branch",
99160
+ "!workspaces/"
99141
99161
  ]);
99142
99162
  this.output.debug(`Created ${TBD_DIR}/.gitignore`);
99143
99163
  await mkdir(join(cwd, TBD_SHORTCUTS_SYSTEM), { recursive: true });
@@ -100789,6 +100809,45 @@ const listCommand = new Command("list").description("List issues").option("--sta
100789
100809
  *
100790
100810
  * See: tbd-design.md §4.4 Show
100791
100811
  */
100812
+ /**
100813
+ * Render a serialized issue with colorized output, optionally truncated.
100814
+ *
100815
+ * @param issue - The issue to render
100816
+ * @param colors - Color functions
100817
+ * @param maxLines - If set, truncate output to this many lines with an omission notice
100818
+ * @returns Array of formatted lines
100819
+ */
100820
+ function renderIssueLines(issue, colors) {
100821
+ const serialized = serializeIssue(issue);
100822
+ const output = [];
100823
+ for (const line of serialized.split("\n")) if (line === "---") output.push(colors.dim(line));
100824
+ else if (line.startsWith("id:")) output.push(`${colors.dim("id:")} ${colors.id(line.slice(4))}`);
100825
+ else if (line.startsWith("status:")) {
100826
+ const status = line.slice(8).trim();
100827
+ const statusColor = getStatusColor(status, colors);
100828
+ output.push(`${colors.dim("status:")} ${statusColor(status)}`);
100829
+ } else if (line.startsWith("priority:")) {
100830
+ const priority = parseInt(line.slice(10).trim(), 10);
100831
+ const priorityColor = getPriorityColor(priority, colors);
100832
+ output.push(`${colors.dim("priority:")} ${priorityColor(formatPriority(priority))}`);
100833
+ } else if (line.startsWith("title:")) output.push(`${colors.dim("title:")} ${colors.bold(line.slice(7))}`);
100834
+ else if (line.startsWith("spec_path:")) output.push(`${colors.dim("spec_path:")} ${colors.id(line.slice(11))}`);
100835
+ else if (line.startsWith("## Notes")) output.push(colors.bold(line));
100836
+ else if (line.startsWith(" - ")) output.push(` - ${colors.label(line.slice(4))}`);
100837
+ else output.push(line);
100838
+ return output;
100839
+ }
100840
+ /**
100841
+ * Print lines with optional max-lines truncation.
100842
+ * If maxLines is set and the output exceeds it, truncates and appends an omission notice.
100843
+ */
100844
+ function printWithTruncation(lines, colors, maxLines) {
100845
+ if (maxLines && lines.length > maxLines) {
100846
+ const omitted = lines.length - maxLines;
100847
+ for (const line of lines.slice(0, maxLines)) console.log(line);
100848
+ console.log(colors.dim(`… [${omitted} line${omitted === 1 ? "" : "s"} omitted]`));
100849
+ } else for (const line of lines) console.log(line);
100850
+ }
100792
100851
  var ShowHandler = class extends BaseCommand {
100793
100852
  async run(id, command, options) {
100794
100853
  const ctx = await loadFullContext(command);
@@ -100799,29 +100858,28 @@ var ShowHandler = class extends BaseCommand {
100799
100858
  } catch {
100800
100859
  throw new NotFoundError("Issue", id);
100801
100860
  }
100861
+ let parentIssue;
100862
+ if (issue.parent_id && options.parent !== false) try {
100863
+ parentIssue = await readIssue(ctx.dataSyncDir, issue.parent_id);
100864
+ } catch {}
100865
+ const maxLines = options.maxLines ? parseInt(options.maxLines, 10) : void 0;
100802
100866
  const displayId = ctx.displayId(issue.id);
100803
100867
  const displayIssue = {
100804
100868
  ...issue,
100805
- displayId
100869
+ displayId,
100870
+ ...parentIssue ? { parent: {
100871
+ ...parentIssue,
100872
+ displayId: ctx.displayId(parentIssue.id)
100873
+ } } : {}
100806
100874
  };
100807
100875
  this.output.data(displayIssue, () => {
100808
100876
  const colors = this.output.getColors();
100809
- const lines = serializeIssue(issue).split("\n");
100810
- for (const line of lines) if (line === "---") console.log(colors.dim(line));
100811
- else if (line.startsWith("id:")) console.log(`${colors.dim("id:")} ${colors.id(line.slice(4))}`);
100812
- else if (line.startsWith("status:")) {
100813
- const status = line.slice(8).trim();
100814
- const statusColor = getStatusColor(status, colors);
100815
- console.log(`${colors.dim("status:")} ${statusColor(status)}`);
100816
- } else if (line.startsWith("priority:")) {
100817
- const priority = parseInt(line.slice(10).trim(), 10);
100818
- const priorityColor = getPriorityColor(priority, colors);
100819
- console.log(`${colors.dim("priority:")} ${priorityColor(formatPriority(priority))}`);
100820
- } else if (line.startsWith("title:")) console.log(`${colors.dim("title:")} ${colors.bold(line.slice(7))}`);
100821
- else if (line.startsWith("spec_path:")) console.log(`${colors.dim("spec_path:")} ${colors.id(line.slice(11))}`);
100822
- else if (line.startsWith("## Notes")) console.log(colors.bold(line));
100823
- else if (line.startsWith(" - ")) console.log(` - ${colors.label(line.slice(4))}`);
100824
- else console.log(line);
100877
+ printWithTruncation(renderIssueLines(issue, colors), colors, maxLines);
100878
+ if (parentIssue) {
100879
+ console.log("");
100880
+ console.log(colors.dim("The parent of this bead is:"));
100881
+ printWithTruncation(renderIssueLines(parentIssue, colors), colors, PARENT_CONTEXT_MAX_LINES);
100882
+ }
100825
100883
  if (options.showOrder) {
100826
100884
  console.log("");
100827
100885
  console.log(colors.dim("child_order_hints:"));
@@ -100834,7 +100892,7 @@ var ShowHandler = class extends BaseCommand {
100834
100892
  });
100835
100893
  }
100836
100894
  };
100837
- const showCommand = new Command("show").description("Show issue details").argument("<id>", "Issue ID").option("--show-order", "Display children ordering hints").action(async (id, options, command) => {
100895
+ const showCommand = new Command("show").description("Show issue details").argument("<id>", "Issue ID").option("--show-order", "Display children ordering hints").option("--no-parent", "Suppress automatic parent context display").option("--max-lines <n>", "Truncate output to N lines").action(async (id, options, command) => {
100838
100896
  await new ShowHandler(command).run(id, command, options);
100839
100897
  });
100840
100898
 
@@ -103165,6 +103223,8 @@ var SyncHandler = class extends BaseCommand {
103165
103223
  console.log(" 1. Commit: git add .tbd/workspaces && git commit -m 'tbd: save outbox'");
103166
103224
  console.log(" 2. Push your working branch: git push");
103167
103225
  console.log(" 3. Run 'tbd sync' when push access is available");
103226
+ console.log("");
103227
+ console.log(" WARNING: Do NOT add .tbd/workspaces/ to .gitignore -- that would cause data loss.");
103168
103228
  } else {
103169
103229
  const totalInOutbox = existingOutboxCount + result.saved;
103170
103230
  if (existingOutboxCount > 0) {
@@ -103180,6 +103240,8 @@ var SyncHandler = class extends BaseCommand {
103180
103240
  console.log(" 2. Push your working branch: git push");
103181
103241
  console.log(" 3. Run 'tbd sync' when push access is available");
103182
103242
  console.log(" (outbox will be imported automatically on successful sync)");
103243
+ console.log("");
103244
+ console.log(" WARNING: Do NOT add .tbd/workspaces/ to .gitignore -- that would cause data loss.");
103183
103245
  }
103184
103246
  } catch (saveError) {
103185
103247
  const saveErrorMsg = saveError instanceof Error ? saveError.message : String(saveError);
@@ -103295,44 +103357,17 @@ function formatTimestampAgo(timestamp) {
103295
103357
  * See: tbd-design.md §4.8 Search Commands
103296
103358
  */
103297
103359
  const STALE_THRESHOLD_MS = 300 * 1e3;
103298
- /**
103299
- * Read local state file.
103300
- */
103301
- async function readState() {
103302
- try {
103303
- return (0, import_dist$2.parse)(await readFile(STATE_FILE, "utf-8"));
103304
- } catch {
103305
- return {};
103306
- }
103307
- }
103308
- /**
103309
- * Update local state file.
103310
- */
103311
- async function updateState(updates) {
103312
- await writeFile(STATE_FILE, stringifyYaml({
103313
- ...await readState(),
103314
- ...updates
103315
- }));
103316
- }
103317
- /**
103318
- * Check if worktree is stale and needs refresh.
103319
- */
103320
- async function isWorktreeStale() {
103321
- const state = await readState();
103322
- if (!state.last_sync_at) return true;
103323
- const lastSync = new Date(state.last_sync_at).getTime();
103324
- return Date.now() - lastSync > STALE_THRESHOLD_MS;
103325
- }
103326
103360
  var SearchHandler = class extends BaseCommand {
103327
103361
  async run(query, options) {
103328
103362
  const tbdRoot = await requireInit();
103329
103363
  if (!options.noRefresh) {
103330
- const state = await readState();
103331
- if (await isWorktreeStale()) {
103364
+ const state = await readLocalState(tbdRoot);
103365
+ const lastSync = state.last_sync_at ? new Date(state.last_sync_at).getTime() : 0;
103366
+ if (Date.now() - lastSync > STALE_THRESHOLD_MS) {
103332
103367
  const lastSyncAgo = formatTimestampAgo(state.last_sync_at);
103333
103368
  const staleInfo = lastSyncAgo ? ` (last synced ${lastSyncAgo})` : "";
103334
103369
  this.output.info(`Refreshing worktree${staleInfo}...`);
103335
- await updateState({ last_sync_at: now() });
103370
+ await updateLocalState(tbdRoot, { last_sync_at: now() });
103336
103371
  }
103337
103372
  }
103338
103373
  let issues;
@@ -103961,10 +103996,10 @@ const PRIORITY_LABELS = [
103961
103996
  ];
103962
103997
  var StatsHandler = class extends BaseCommand {
103963
103998
  async run() {
103964
- await requireInit();
103999
+ const tbdRoot = await requireInit();
103965
104000
  let issues;
103966
104001
  try {
103967
- issues = await listIssues(await resolveDataSyncDir());
104002
+ issues = await listIssues(await resolveDataSyncDir(tbdRoot));
103968
104003
  } catch {
103969
104004
  throw new NotInitializedError("No issue store found. Run `tbd init` first.");
103970
104005
  }
@@ -104909,10 +104944,10 @@ const doctorCommand = new Command("doctor").description("Diagnose and repair rep
104909
104944
  */
104910
104945
  var ConfigShowHandler = class extends BaseCommand {
104911
104946
  async run() {
104912
- await requireInit();
104947
+ const tbdRoot = await requireInit();
104913
104948
  let config;
104914
104949
  try {
104915
- config = await readConfig(".");
104950
+ config = await readConfig(tbdRoot);
104916
104951
  } catch {
104917
104952
  throw new NotInitializedError("No configuration found. Run `tbd init` first.");
104918
104953
  }
@@ -104931,10 +104966,10 @@ var ConfigShowHandler = class extends BaseCommand {
104931
104966
  };
104932
104967
  var ConfigSetHandler = class extends BaseCommand {
104933
104968
  async run(key, value) {
104934
- await requireInit();
104969
+ const tbdRoot = await requireInit();
104935
104970
  let config;
104936
104971
  try {
104937
- config = await readConfig(".");
104972
+ config = await readConfig(tbdRoot);
104938
104973
  } catch {
104939
104974
  throw new NotInitializedError("No configuration found. Run `tbd init` first.");
104940
104975
  }
@@ -104950,7 +104985,7 @@ var ConfigSetHandler = class extends BaseCommand {
104950
104985
  throw new ValidationError(`Invalid key: ${key}`);
104951
104986
  }
104952
104987
  await this.execute(async () => {
104953
- await writeConfig(".", config);
104988
+ await writeConfig(tbdRoot, config);
104954
104989
  }, "Failed to write config");
104955
104990
  this.output.success(`Set ${key} = ${value}`);
104956
104991
  }
@@ -104975,10 +105010,10 @@ var ConfigSetHandler = class extends BaseCommand {
104975
105010
  };
104976
105011
  var ConfigGetHandler = class extends BaseCommand {
104977
105012
  async run(key) {
104978
- await requireInit();
105013
+ const tbdRoot = await requireInit();
104979
105014
  let config;
104980
105015
  try {
104981
- config = await readConfig(".");
105016
+ config = await readConfig(tbdRoot);
104982
105017
  } catch {
104983
105018
  throw new NotInitializedError("No configuration found. Run `tbd init` first.");
104984
105019
  }
@@ -105030,8 +105065,8 @@ function parseAtticFilename(filename) {
105030
105065
  /**
105031
105066
  * List all attic entries.
105032
105067
  */
105033
- async function listAtticEntries(filterById) {
105034
- const atticPath = await resolveAtticDir();
105068
+ async function listAtticEntries(tbdRoot, filterById) {
105069
+ const atticPath = await resolveAtticDir(tbdRoot);
105035
105070
  let files;
105036
105071
  try {
105037
105072
  files = await readdir(atticPath);
@@ -105057,7 +105092,7 @@ async function listAtticEntries(filterById) {
105057
105092
  var AtticListHandler = class extends BaseCommand {
105058
105093
  async run(id) {
105059
105094
  const tbdRoot = await requireInit();
105060
- const entries = await listAtticEntries(id ? normalizeIssueId(id) : void 0);
105095
+ const entries = await listAtticEntries(tbdRoot, id ? normalizeIssueId(id) : void 0);
105061
105096
  const mapping = await loadIdMapping(await resolveDataSyncDir(tbdRoot));
105062
105097
  const prefix = (await readConfig(tbdRoot)).display.id_prefix;
105063
105098
  const showDebug = this.ctx.debug;
@@ -105084,7 +105119,7 @@ var AtticListHandler = class extends BaseCommand {
105084
105119
  var AtticShowHandler = class extends BaseCommand {
105085
105120
  async run(id, timestamp) {
105086
105121
  const tbdRoot = await requireInit();
105087
- const entry = (await listAtticEntries(normalizeIssueId(id))).find((e) => e.timestamp === timestamp || e.timestamp.replace(/:/g, "-") === timestamp);
105122
+ const entry = (await listAtticEntries(tbdRoot, normalizeIssueId(id))).find((e) => e.timestamp === timestamp || e.timestamp.replace(/:/g, "-") === timestamp);
105088
105123
  if (!entry) throw new NotFoundError("Attic entry", `${id} at ${timestamp}`);
105089
105124
  const mapping = await loadIdMapping(await resolveDataSyncDir(tbdRoot));
105090
105125
  const prefix = (await readConfig(tbdRoot)).display.id_prefix;
@@ -105114,7 +105149,7 @@ var AtticRestoreHandler = class extends BaseCommand {
105114
105149
  async run(id, timestamp) {
105115
105150
  const tbdRoot = await requireInit();
105116
105151
  const normalizedId = normalizeIssueId(id);
105117
- const entry = (await listAtticEntries(normalizedId)).find((e) => e.timestamp === timestamp || e.timestamp.replace(/:/g, "-") === timestamp);
105152
+ const entry = (await listAtticEntries(tbdRoot, normalizedId)).find((e) => e.timestamp === timestamp || e.timestamp.replace(/:/g, "-") === timestamp);
105118
105153
  if (!entry) throw new NotFoundError("Attic entry", `${id} at ${timestamp}`);
105119
105154
  if (this.checkDryRun("Would restore from attic", {
105120
105155
  id: normalizedId,
@@ -105991,27 +106026,26 @@ const readmeCommand = new Command("readme").description("Display the README (sam
105991
106026
  var UninstallHandler = class extends BaseCommand {
105992
106027
  async run(options) {
105993
106028
  const colors = this.output.getColors();
105994
- try {
105995
- await access(".tbd");
105996
- } catch {
105997
- throw new NotInitializedError("No .tbd directory found. Nothing to uninstall.");
105998
- }
106029
+ const tbdRoot = await findTbdRoot(process.cwd());
106030
+ if (!tbdRoot) throw new NotInitializedError("No .tbd directory found. Nothing to uninstall.");
105999
106031
  let config;
106000
106032
  try {
106001
- config = await readConfig(".");
106033
+ config = await readConfig(tbdRoot);
106002
106034
  } catch {
106003
106035
  config = null;
106004
106036
  }
106005
106037
  const syncBranch = config?.sync.branch ?? SYNC_BRANCH;
106006
106038
  const remote = config?.sync.remote ?? "origin";
106007
- const worktreePath = join(".tbd", "data-sync-worktree");
106039
+ const tbdDir = join(tbdRoot, ".tbd");
106040
+ const worktreePath = join(tbdDir, "data-sync-worktree");
106041
+ const displayPath = (p) => relative(process.cwd(), p) || p;
106008
106042
  const items = [];
106009
106043
  let worktreeExists = false;
106010
106044
  try {
106011
106045
  await access(worktreePath);
106012
106046
  worktreeExists = true;
106013
106047
  const worktreeStats = await this.getDirectoryStats(worktreePath);
106014
- items.push(` - Worktree: ${worktreePath} (${worktreeStats.files} files)`);
106048
+ items.push(` - Worktree: ${displayPath(worktreePath)} (${worktreeStats.files} files)`);
106015
106049
  } catch {}
106016
106050
  let localBranchExists = false;
106017
106051
  try {
@@ -106039,8 +106073,8 @@ var UninstallHandler = class extends BaseCommand {
106039
106073
  remoteBranchExists = true;
106040
106074
  items.push(` - Remote branch: ${remote}/${syncBranch}`);
106041
106075
  } catch {}
106042
- const tbdStats = await this.getDirectoryStats(".tbd");
106043
- items.push(` - Directory: .tbd/ (${tbdStats.files} files)`);
106076
+ const tbdStats = await this.getDirectoryStats(tbdDir);
106077
+ items.push(` - Directory: ${displayPath(tbdDir)}/ (${tbdStats.files} files)`);
106044
106078
  console.log(colors.bold("The following will be removed:"));
106045
106079
  console.log("");
106046
106080
  for (const item of items) console.log(colors.warn(item));
@@ -106113,7 +106147,7 @@ var UninstallHandler = class extends BaseCommand {
106113
106147
  });
106114
106148
  } catch {}
106115
106149
  try {
106116
- await rm(".tbd", {
106150
+ await rm(tbdDir, {
106117
106151
  recursive: true,
106118
106152
  force: true
106119
106153
  });
@@ -107381,7 +107415,7 @@ function inferGuidelineCategory(name) {
107381
107415
  if (name.startsWith("typescript-")) return "typescript";
107382
107416
  if (name.startsWith("python-")) return "python";
107383
107417
  if (name.includes("tdd") || name.includes("testing") || name.includes("golden")) return "testing";
107384
- if (name.startsWith("general-") || name.includes("rules") || name.includes("patterns") || name.startsWith("backward-") || name.startsWith("convex-") || name.startsWith("release-")) return "general";
107418
+ if (name.startsWith("general-") || name.includes("rules") || name.includes("patterns") || name.startsWith("backward-") || name.startsWith("convex-") || name.startsWith("release-") || name.startsWith("writing-")) return "general";
107385
107419
  }
107386
107420
  var GuidelinesHandler = class extends DocCommandHandler {
107387
107421
  constructor(command) {
@@ -108285,7 +108319,10 @@ var SetupDefaultHandler = class extends BaseCommand {
108285
108319
  "",
108286
108320
  "# Temporary files",
108287
108321
  "*.tmp",
108288
- "*.temp"
108322
+ "*.temp",
108323
+ "",
108324
+ "# workspaces/ stores state (including outbox) committed to the working branch",
108325
+ "!workspaces/"
108289
108326
  ]);
108290
108327
  if (tbdGitignoreResult.created) console.log(` ${colors.success("✓")} Created .tbd/.gitignore`);
108291
108328
  else if (tbdGitignoreResult.added.length > 0) console.log(` ${colors.success("✓")} Updated .tbd/.gitignore with new patterns`);
@@ -108412,7 +108449,10 @@ Example:
108412
108449
  "",
108413
108450
  "# Temporary files",
108414
108451
  "*.tmp",
108415
- "*.temp"
108452
+ "*.temp",
108453
+ "",
108454
+ "# workspaces/ stores state (including outbox) committed to the working branch",
108455
+ "!workspaces/"
108416
108456
  ]);
108417
108457
  if (tbdGitignoreResult.created) console.log(` ${colors.success("✓")} Created .tbd/.gitignore`);
108418
108458
  else if (tbdGitignoreResult.added.length > 0) console.log(` ${colors.success("✓")} Updated .tbd/.gitignore`);