first-tree 0.0.2 → 0.0.4

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 (80) hide show
  1. package/README.md +116 -40
  2. package/dist/cli.js +46 -17
  3. package/dist/help-Dtdj91HJ.js +25 -0
  4. package/dist/init--VepFe6N.js +403 -0
  5. package/dist/installer-cH7N4RNj.js +47 -0
  6. package/dist/onboarding-C9cYSE6F.js +2 -0
  7. package/dist/onboarding-CPP8fF4D.js +10 -0
  8. package/dist/repo-DY57bMqr.js +318 -0
  9. package/dist/upgrade-Cgx_K2HM.js +135 -0
  10. package/dist/{verify-CSRIkuoM.js → verify-mC9ZTd1f.js} +118 -29
  11. package/package.json +33 -10
  12. package/skills/first-tree/SKILL.md +113 -0
  13. package/skills/first-tree/agents/openai.yaml +4 -0
  14. package/skills/first-tree/assets/framework/VERSION +1 -0
  15. package/skills/first-tree/assets/framework/examples/claude-code/README.md +14 -0
  16. package/skills/first-tree/assets/framework/examples/claude-code/settings.json +14 -0
  17. package/skills/first-tree/assets/framework/helpers/generate-codeowners.ts +224 -0
  18. package/skills/first-tree/assets/framework/helpers/inject-tree-context.sh +15 -0
  19. package/skills/first-tree/assets/framework/helpers/run-review.ts +193 -0
  20. package/skills/first-tree/assets/framework/manifest.json +11 -0
  21. package/skills/first-tree/assets/framework/prompts/pr-review.md +38 -0
  22. package/skills/first-tree/assets/framework/templates/agents.md.template +49 -0
  23. package/skills/first-tree/assets/framework/templates/member-node.md.template +18 -0
  24. package/skills/first-tree/assets/framework/templates/members-domain.md.template +45 -0
  25. package/skills/first-tree/assets/framework/templates/root-node.md.template +41 -0
  26. package/skills/first-tree/assets/framework/workflows/codeowners.yml +31 -0
  27. package/skills/first-tree/assets/framework/workflows/pr-review.yml +146 -0
  28. package/skills/first-tree/assets/framework/workflows/validate.yml +19 -0
  29. package/skills/first-tree/engine/commands/help.ts +32 -0
  30. package/skills/first-tree/engine/commands/init.ts +1 -0
  31. package/skills/first-tree/engine/commands/upgrade.ts +1 -0
  32. package/skills/first-tree/engine/commands/verify.ts +1 -0
  33. package/skills/first-tree/engine/init.ts +414 -0
  34. package/skills/first-tree/engine/onboarding.ts +10 -0
  35. package/skills/first-tree/engine/repo.ts +360 -0
  36. package/skills/first-tree/engine/rules/agent-instructions.ts +59 -0
  37. package/skills/first-tree/engine/rules/agent-integration.ts +19 -0
  38. package/skills/first-tree/engine/rules/ci-validation.ts +72 -0
  39. package/skills/first-tree/engine/rules/framework.ts +13 -0
  40. package/skills/first-tree/engine/rules/index.ts +41 -0
  41. package/skills/first-tree/engine/rules/members.ts +21 -0
  42. package/skills/first-tree/engine/rules/populate-tree.ts +36 -0
  43. package/skills/first-tree/engine/rules/root-node.ts +41 -0
  44. package/skills/first-tree/engine/runtime/adapters.ts +22 -0
  45. package/skills/first-tree/engine/runtime/asset-loader.ts +141 -0
  46. package/skills/first-tree/engine/runtime/installer.ts +82 -0
  47. package/skills/first-tree/engine/runtime/upgrader.ts +23 -0
  48. package/skills/first-tree/engine/upgrade.ts +233 -0
  49. package/skills/first-tree/engine/validators/members.ts +215 -0
  50. package/skills/first-tree/engine/validators/nodes.ts +559 -0
  51. package/skills/first-tree/engine/verify.ts +155 -0
  52. package/skills/first-tree/references/about.md +36 -0
  53. package/skills/first-tree/references/maintainer-architecture.md +59 -0
  54. package/skills/first-tree/references/maintainer-build-and-distribution.md +59 -0
  55. package/skills/first-tree/references/maintainer-testing.md +58 -0
  56. package/skills/first-tree/references/maintainer-thin-cli.md +38 -0
  57. package/skills/first-tree/references/onboarding.md +185 -0
  58. package/skills/first-tree/references/ownership-and-naming.md +94 -0
  59. package/skills/first-tree/references/principles.md +113 -0
  60. package/skills/first-tree/references/source-map.md +94 -0
  61. package/skills/first-tree/references/upgrade-contract.md +94 -0
  62. package/skills/first-tree/scripts/check-skill-sync.sh +133 -0
  63. package/skills/first-tree/scripts/quick_validate.py +95 -0
  64. package/skills/first-tree/scripts/run-local-cli.sh +35 -0
  65. package/skills/first-tree/tests/asset-loader.test.ts +75 -0
  66. package/skills/first-tree/tests/generate-codeowners.test.ts +94 -0
  67. package/skills/first-tree/tests/helpers.ts +169 -0
  68. package/skills/first-tree/tests/init.test.ts +250 -0
  69. package/skills/first-tree/tests/repo.test.ts +440 -0
  70. package/skills/first-tree/tests/rules.test.ts +413 -0
  71. package/skills/first-tree/tests/run-review.test.ts +155 -0
  72. package/skills/first-tree/tests/skill-artifacts.test.ts +311 -0
  73. package/skills/first-tree/tests/thin-cli.test.ts +104 -0
  74. package/skills/first-tree/tests/upgrade.test.ts +103 -0
  75. package/skills/first-tree/tests/validate-members.test.ts +224 -0
  76. package/skills/first-tree/tests/validate-nodes.test.ts +198 -0
  77. package/skills/first-tree/tests/verify.test.ts +241 -0
  78. package/dist/init-CE_944sb.js +0 -283
  79. package/dist/repo-BByc3VvM.js +0 -111
  80. package/dist/upgrade-Chr7z0CY.js +0 -82
@@ -0,0 +1,440 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { describe, expect, it } from "vitest";
4
+ import { Repo } from "#skill/engine/repo.js";
5
+ import {
6
+ AGENT_INSTRUCTIONS_FILE,
7
+ FRAMEWORK_VERSION,
8
+ INSTALLED_PROGRESS,
9
+ LEGACY_AGENT_INSTRUCTIONS_FILE,
10
+ LEGACY_SKILL_PROGRESS,
11
+ LEGACY_SKILL_VERSION,
12
+ LEGACY_PROGRESS,
13
+ LEGACY_VERSION,
14
+ } from "#skill/engine/runtime/asset-loader.js";
15
+ import {
16
+ useTmpDir,
17
+ makeFramework,
18
+ makeGitRepo,
19
+ makeLegacyFramework,
20
+ makeLegacyNamedFramework,
21
+ makeSourceRepo,
22
+ makeSourceSkill,
23
+ } from "./helpers.js";
24
+
25
+ // --- pathExists ---
26
+
27
+ describe("pathExists", () => {
28
+ it("returns true for existing file", () => {
29
+ const tmp = useTmpDir();
30
+ writeFileSync(join(tmp.path, "file.txt"), "hello");
31
+ const repo = new Repo(tmp.path);
32
+ expect(repo.pathExists("file.txt")).toBe(true);
33
+ });
34
+
35
+ it("returns false for missing file", () => {
36
+ const tmp = useTmpDir();
37
+ const repo = new Repo(tmp.path);
38
+ expect(repo.pathExists("no-such-file.txt")).toBe(false);
39
+ });
40
+ });
41
+
42
+ // --- fileContains ---
43
+
44
+ describe("fileContains", () => {
45
+ it("returns true when text is present", () => {
46
+ const tmp = useTmpDir();
47
+ writeFileSync(join(tmp.path, "f.md"), "hello world");
48
+ const repo = new Repo(tmp.path);
49
+ expect(repo.fileContains("f.md", "hello")).toBe(true);
50
+ });
51
+
52
+ it("returns false when text is missing", () => {
53
+ const tmp = useTmpDir();
54
+ writeFileSync(join(tmp.path, "f.md"), "hello world");
55
+ const repo = new Repo(tmp.path);
56
+ expect(repo.fileContains("f.md", "goodbye")).toBe(false);
57
+ });
58
+
59
+ it("returns false when file is missing", () => {
60
+ const tmp = useTmpDir();
61
+ const repo = new Repo(tmp.path);
62
+ expect(repo.fileContains("missing.md", "anything")).toBe(false);
63
+ });
64
+ });
65
+
66
+ // --- frontmatter ---
67
+
68
+ describe("frontmatter", () => {
69
+ it("parses valid title and owners", () => {
70
+ const tmp = useTmpDir();
71
+ writeFileSync(
72
+ join(tmp.path, "NODE.md"),
73
+ "---\ntitle: My Tree\nowners: [alice, bob]\n---\n# Content\n",
74
+ );
75
+ const repo = new Repo(tmp.path);
76
+ const fm = repo.frontmatter("NODE.md");
77
+ expect(fm).not.toBeNull();
78
+ expect(fm!.title).toBe("My Tree");
79
+ expect(fm!.owners).toEqual(["alice", "bob"]);
80
+ });
81
+
82
+ it("returns null for missing frontmatter", () => {
83
+ const tmp = useTmpDir();
84
+ writeFileSync(
85
+ join(tmp.path, "NODE.md"),
86
+ "# Just a heading\nNo frontmatter here.\n",
87
+ );
88
+ const repo = new Repo(tmp.path);
89
+ expect(repo.frontmatter("NODE.md")).toBeNull();
90
+ });
91
+
92
+ it("handles partial — title only", () => {
93
+ const tmp = useTmpDir();
94
+ writeFileSync(join(tmp.path, "NODE.md"), "---\ntitle: Partial\n---\n");
95
+ const repo = new Repo(tmp.path);
96
+ const fm = repo.frontmatter("NODE.md");
97
+ expect(fm).not.toBeNull();
98
+ expect(fm!.title).toBe("Partial");
99
+ expect(fm!.owners).toBeUndefined();
100
+ });
101
+
102
+ it("handles partial — owners only", () => {
103
+ const tmp = useTmpDir();
104
+ writeFileSync(join(tmp.path, "NODE.md"), "---\nowners: [alice]\n---\n");
105
+ const repo = new Repo(tmp.path);
106
+ const fm = repo.frontmatter("NODE.md");
107
+ expect(fm).not.toBeNull();
108
+ expect(fm!.owners).toEqual(["alice"]);
109
+ expect(fm!.title).toBeUndefined();
110
+ });
111
+
112
+ it("returns null for missing file", () => {
113
+ const tmp = useTmpDir();
114
+ const repo = new Repo(tmp.path);
115
+ expect(repo.frontmatter("NODE.md")).toBeNull();
116
+ });
117
+ });
118
+
119
+ // --- anyAgentConfig ---
120
+
121
+ describe("anyAgentConfig", () => {
122
+ it("returns true with claude settings", () => {
123
+ const tmp = useTmpDir();
124
+ mkdirSync(join(tmp.path, ".claude"));
125
+ writeFileSync(join(tmp.path, ".claude", "settings.json"), "{}");
126
+ const repo = new Repo(tmp.path);
127
+ expect(repo.anyAgentConfig()).toBe(true);
128
+ });
129
+
130
+ it("returns false without any config", () => {
131
+ const tmp = useTmpDir();
132
+ const repo = new Repo(tmp.path);
133
+ expect(repo.anyAgentConfig()).toBe(false);
134
+ });
135
+ });
136
+
137
+ // --- isGitRepo ---
138
+
139
+ describe("isGitRepo", () => {
140
+ it("returns true with .git dir", () => {
141
+ const tmp = useTmpDir();
142
+ makeGitRepo(tmp.path);
143
+ const repo = new Repo(tmp.path);
144
+ expect(repo.isGitRepo()).toBe(true);
145
+ });
146
+
147
+ it("returns true with .git file", () => {
148
+ const tmp = useTmpDir();
149
+ writeFileSync(join(tmp.path, ".git"), "gitdir: /tmp/example\n");
150
+ const repo = new Repo(tmp.path);
151
+ expect(repo.isGitRepo()).toBe(true);
152
+ });
153
+
154
+ it("returns false without .git dir", () => {
155
+ const tmp = useTmpDir();
156
+ const repo = new Repo(tmp.path);
157
+ expect(repo.isGitRepo()).toBe(false);
158
+ });
159
+ });
160
+
161
+ // --- hasFramework ---
162
+
163
+ describe("hasFramework", () => {
164
+ it("returns true with installed skill version file", () => {
165
+ const tmp = useTmpDir();
166
+ makeFramework(tmp.path);
167
+ const repo = new Repo(tmp.path);
168
+ expect(repo.hasFramework()).toBe(true);
169
+ });
170
+
171
+ it("returns true with legacy version file", () => {
172
+ const tmp = useTmpDir();
173
+ makeLegacyFramework(tmp.path);
174
+ const repo = new Repo(tmp.path);
175
+ expect(repo.hasFramework()).toBe(true);
176
+ });
177
+
178
+ it("returns true with the previous installed skill name", () => {
179
+ const tmp = useTmpDir();
180
+ makeLegacyNamedFramework(tmp.path);
181
+ const repo = new Repo(tmp.path);
182
+ expect(repo.hasFramework()).toBe(true);
183
+ });
184
+
185
+ it("returns false without VERSION file", () => {
186
+ const tmp = useTmpDir();
187
+ const repo = new Repo(tmp.path);
188
+ expect(repo.hasFramework()).toBe(false);
189
+ });
190
+ });
191
+
192
+ // --- readVersion ---
193
+
194
+ describe("readVersion", () => {
195
+ it("reads the installed skill version", () => {
196
+ const tmp = useTmpDir();
197
+ makeFramework(tmp.path, "0.2.0");
198
+ const repo = new Repo(tmp.path);
199
+ expect(repo.readVersion()).toBe("0.2.0");
200
+ });
201
+
202
+ it("falls back to the legacy version", () => {
203
+ const tmp = useTmpDir();
204
+ makeLegacyFramework(tmp.path, "0.3.0");
205
+ const repo = new Repo(tmp.path);
206
+ expect(repo.readVersion()).toBe("0.3.0");
207
+ });
208
+
209
+ it("reads the previous installed skill version", () => {
210
+ const tmp = useTmpDir();
211
+ makeLegacyNamedFramework(tmp.path, "0.2.5");
212
+ const repo = new Repo(tmp.path);
213
+ expect(repo.readVersion()).toBe("0.2.5");
214
+ });
215
+
216
+ it("returns null when missing", () => {
217
+ const tmp = useTmpDir();
218
+ const repo = new Repo(tmp.path);
219
+ expect(repo.readVersion()).toBeNull();
220
+ });
221
+ });
222
+
223
+ // --- preferredProgressPath / frameworkVersionPath ---
224
+
225
+ describe("path preferences", () => {
226
+ it("prefers the installed-skill paths by default", () => {
227
+ const tmp = useTmpDir();
228
+ const repo = new Repo(tmp.path);
229
+ expect(repo.preferredProgressPath()).toBe(INSTALLED_PROGRESS);
230
+ expect(repo.frameworkVersionPath()).toBe(FRAMEWORK_VERSION);
231
+ });
232
+
233
+ it("switches path preferences for legacy repos", () => {
234
+ const tmp = useTmpDir();
235
+ makeLegacyFramework(tmp.path);
236
+ const repo = new Repo(tmp.path);
237
+ expect(repo.preferredProgressPath()).toBe(LEGACY_PROGRESS);
238
+ expect(repo.frameworkVersionPath()).toBe(LEGACY_VERSION);
239
+ });
240
+
241
+ it("switches path preferences for repos using the previous skill name", () => {
242
+ const tmp = useTmpDir();
243
+ makeLegacyNamedFramework(tmp.path);
244
+ const repo = new Repo(tmp.path);
245
+ expect(repo.preferredProgressPath()).toBe(LEGACY_SKILL_PROGRESS);
246
+ expect(repo.frameworkVersionPath()).toBe(LEGACY_SKILL_VERSION);
247
+ });
248
+ });
249
+
250
+ // --- agent instructions helpers ---
251
+
252
+ describe("agent instructions helpers", () => {
253
+ it("prefers AGENTS.md when both filenames exist", () => {
254
+ const tmp = useTmpDir();
255
+ writeFileSync(
256
+ join(tmp.path, AGENT_INSTRUCTIONS_FILE),
257
+ "<!-- BEGIN CONTEXT-TREE FRAMEWORK -->\nstuff\n<!-- END CONTEXT-TREE FRAMEWORK -->\n",
258
+ );
259
+ writeFileSync(
260
+ join(tmp.path, LEGACY_AGENT_INSTRUCTIONS_FILE),
261
+ "# Legacy instructions\n",
262
+ );
263
+ const repo = new Repo(tmp.path);
264
+ expect(repo.agentInstructionsPath()).toBe(AGENT_INSTRUCTIONS_FILE);
265
+ expect(repo.hasCanonicalAgentInstructionsFile()).toBe(true);
266
+ expect(repo.hasLegacyAgentInstructionsFile()).toBe(true);
267
+ expect(repo.hasDuplicateAgentInstructionsFiles()).toBe(true);
268
+ expect(repo.hasAgentInstructionsMarkers()).toBe(true);
269
+ });
270
+
271
+ it("falls back to legacy AGENT.md while migrating", () => {
272
+ const tmp = useTmpDir();
273
+ writeFileSync(
274
+ join(tmp.path, LEGACY_AGENT_INSTRUCTIONS_FILE),
275
+ "<!-- BEGIN CONTEXT-TREE FRAMEWORK -->\nstuff\n<!-- END CONTEXT-TREE FRAMEWORK -->\n",
276
+ );
277
+ const repo = new Repo(tmp.path);
278
+ expect(repo.agentInstructionsPath()).toBe(LEGACY_AGENT_INSTRUCTIONS_FILE);
279
+ expect(repo.hasCanonicalAgentInstructionsFile()).toBe(false);
280
+ expect(repo.hasLegacyAgentInstructionsFile()).toBe(true);
281
+ expect(repo.hasDuplicateAgentInstructionsFiles()).toBe(false);
282
+ expect(repo.hasAgentInstructionsMarkers()).toBe(true);
283
+ });
284
+
285
+ it("returns false without markers", () => {
286
+ const tmp = useTmpDir();
287
+ writeFileSync(
288
+ join(tmp.path, AGENT_INSTRUCTIONS_FILE),
289
+ "# Agent instructions\nNo markers here.\n",
290
+ );
291
+ const repo = new Repo(tmp.path);
292
+ expect(repo.hasAgentInstructionsMarkers()).toBe(false);
293
+ });
294
+
295
+ it("returns false when file is missing", () => {
296
+ const tmp = useTmpDir();
297
+ const repo = new Repo(tmp.path);
298
+ expect(repo.agentInstructionsPath()).toBeNull();
299
+ expect(repo.hasAgentInstructionsMarkers()).toBe(false);
300
+ });
301
+ });
302
+
303
+ // --- hasMembers ---
304
+
305
+ describe("hasMembers", () => {
306
+ it("returns true with members/NODE.md", () => {
307
+ const tmp = useTmpDir();
308
+ const members = join(tmp.path, "members");
309
+ mkdirSync(members);
310
+ writeFileSync(join(members, "NODE.md"), "---\ntitle: Members\n---\n");
311
+ const repo = new Repo(tmp.path);
312
+ expect(repo.hasMembers()).toBe(true);
313
+ });
314
+
315
+ it("returns false without members dir", () => {
316
+ const tmp = useTmpDir();
317
+ const repo = new Repo(tmp.path);
318
+ expect(repo.hasMembers()).toBe(false);
319
+ });
320
+
321
+ it("returns false with dir but no NODE.md", () => {
322
+ const tmp = useTmpDir();
323
+ mkdirSync(join(tmp.path, "members"));
324
+ const repo = new Repo(tmp.path);
325
+ expect(repo.hasMembers()).toBe(false);
326
+ });
327
+ });
328
+
329
+ // --- memberCount ---
330
+
331
+ describe("memberCount", () => {
332
+ it("returns 0 with no members dir", () => {
333
+ const tmp = useTmpDir();
334
+ const repo = new Repo(tmp.path);
335
+ expect(repo.memberCount()).toBe(0);
336
+ });
337
+
338
+ it("counts one member", () => {
339
+ const tmp = useTmpDir();
340
+ const members = join(tmp.path, "members");
341
+ mkdirSync(members);
342
+ const alice = join(members, "alice");
343
+ mkdirSync(alice);
344
+ writeFileSync(join(alice, "NODE.md"), "---\ntitle: Alice\n---\n");
345
+ const repo = new Repo(tmp.path);
346
+ expect(repo.memberCount()).toBe(1);
347
+ });
348
+
349
+ it("counts two members", () => {
350
+ const tmp = useTmpDir();
351
+ const members = join(tmp.path, "members");
352
+ mkdirSync(members);
353
+ for (const name of ["alice", "bob"]) {
354
+ const d = join(members, name);
355
+ mkdirSync(d);
356
+ writeFileSync(join(d, "NODE.md"), `---\ntitle: ${name}\n---\n`);
357
+ }
358
+ const repo = new Repo(tmp.path);
359
+ expect(repo.memberCount()).toBe(2);
360
+ });
361
+
362
+ it("ignores dirs without NODE.md", () => {
363
+ const tmp = useTmpDir();
364
+ const members = join(tmp.path, "members");
365
+ mkdirSync(members);
366
+ mkdirSync(join(members, "alice")); // no NODE.md
367
+ const bob = join(members, "bob");
368
+ mkdirSync(bob);
369
+ writeFileSync(join(bob, "NODE.md"), "---\ntitle: Bob\n---\n");
370
+ const repo = new Repo(tmp.path);
371
+ expect(repo.memberCount()).toBe(1);
372
+ });
373
+ });
374
+
375
+ // --- hasPlaceholderNode ---
376
+
377
+ describe("hasPlaceholderNode", () => {
378
+ it("returns true with placeholder", () => {
379
+ const tmp = useTmpDir();
380
+ writeFileSync(
381
+ join(tmp.path, "NODE.md"),
382
+ "---\ntitle: My Tree\n---\n<!-- PLACEHOLDER: fill in -->\n",
383
+ );
384
+ const repo = new Repo(tmp.path);
385
+ expect(repo.hasPlaceholderNode()).toBe(true);
386
+ });
387
+
388
+ it("returns false without placeholder", () => {
389
+ const tmp = useTmpDir();
390
+ writeFileSync(
391
+ join(tmp.path, "NODE.md"),
392
+ "---\ntitle: My Tree\n---\n# Real content\n",
393
+ );
394
+ const repo = new Repo(tmp.path);
395
+ expect(repo.hasPlaceholderNode()).toBe(false);
396
+ });
397
+ });
398
+
399
+ // --- init heuristics ---
400
+
401
+ describe("init heuristics", () => {
402
+ it("treats a code repo as a likely source repo", () => {
403
+ const tmp = useTmpDir();
404
+ makeSourceRepo(tmp.path);
405
+ const repo = new Repo(tmp.path);
406
+ expect(repo.isLikelySourceRepo()).toBe(true);
407
+ expect(repo.isLikelyEmptyRepo()).toBe(false);
408
+ });
409
+
410
+ it("treats a fresh tree repo as empty enough for in-place init", () => {
411
+ const tmp = useTmpDir();
412
+ makeGitRepo(tmp.path);
413
+ writeFileSync(join(tmp.path, "README.md"), "# My Org Context\n");
414
+ const repo = new Repo(tmp.path);
415
+ expect(repo.isLikelyEmptyRepo()).toBe(true);
416
+ expect(repo.isLikelySourceRepo()).toBe(false);
417
+ });
418
+
419
+ it("recognizes a populated tree repo", () => {
420
+ const tmp = useTmpDir();
421
+ makeFramework(tmp.path);
422
+ writeFileSync(
423
+ join(tmp.path, "NODE.md"),
424
+ "---\ntitle: My Tree\nowners: [alice]\n---\n# Tree\n",
425
+ );
426
+ const repo = new Repo(tmp.path);
427
+ expect(repo.looksLikeTreeRepo()).toBe(true);
428
+ expect(repo.isLikelySourceRepo()).toBe(false);
429
+ });
430
+
431
+ it("does not mistake the framework source repo for a user tree repo", () => {
432
+ const tmp = useTmpDir();
433
+ makeSourceRepo(tmp.path);
434
+ makeSourceSkill(tmp.path, "0.2.0");
435
+ writeFileSync(join(tmp.path, "src", "cli.ts"), "export {};\n");
436
+ const repo = new Repo(tmp.path);
437
+ expect(repo.looksLikeTreeRepo()).toBe(false);
438
+ expect(repo.isLikelySourceRepo()).toBe(true);
439
+ });
440
+ });