first-tree 0.0.3 → 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 (43) hide show
  1. package/README.md +69 -27
  2. package/dist/cli.js +28 -13
  3. package/dist/{help-xEI-s9iN.js → help-Dtdj91HJ.js} +1 -1
  4. package/dist/{init-DtOjj0wc.js → init--VepFe6N.js} +171 -21
  5. package/dist/{installer-rcZpGLnM.js → installer-cH7N4RNj.js} +2 -2
  6. package/dist/onboarding-C9cYSE6F.js +2 -0
  7. package/dist/onboarding-CPP8fF4D.js +10 -0
  8. package/dist/{repo-BTJG8BU1.js → repo-DY57bMqr.js} +143 -12
  9. package/dist/{upgrade-COGgI7Rj.js → upgrade-Cgx_K2HM.js} +46 -7
  10. package/dist/{verify-CxN6JiV9.js → verify-mC9ZTd1f.js} +66 -6
  11. package/package.json +1 -1
  12. package/skills/first-tree/SKILL.md +8 -4
  13. package/skills/first-tree/assets/framework/VERSION +1 -1
  14. package/skills/first-tree/assets/framework/helpers/run-review.ts +16 -2
  15. package/skills/first-tree/assets/framework/templates/{agent.md.template → agents.md.template} +1 -0
  16. package/skills/first-tree/assets/framework/templates/root-node.md.template +6 -3
  17. package/skills/first-tree/engine/commands/init.ts +1 -1
  18. package/skills/first-tree/engine/commands/upgrade.ts +1 -1
  19. package/skills/first-tree/engine/commands/verify.ts +1 -1
  20. package/skills/first-tree/engine/init.ts +285 -16
  21. package/skills/first-tree/engine/repo.ts +185 -9
  22. package/skills/first-tree/engine/rules/agent-instructions.ts +29 -7
  23. package/skills/first-tree/engine/runtime/asset-loader.ts +7 -0
  24. package/skills/first-tree/engine/upgrade.ts +66 -9
  25. package/skills/first-tree/engine/validators/nodes.ts +48 -3
  26. package/skills/first-tree/engine/verify.ts +61 -3
  27. package/skills/first-tree/references/maintainer-architecture.md +1 -1
  28. package/skills/first-tree/references/maintainer-build-and-distribution.md +3 -0
  29. package/skills/first-tree/references/maintainer-thin-cli.md +1 -1
  30. package/skills/first-tree/references/onboarding.md +32 -9
  31. package/skills/first-tree/references/source-map.md +3 -3
  32. package/skills/first-tree/references/upgrade-contract.md +14 -5
  33. package/skills/first-tree/scripts/check-skill-sync.sh +1 -1
  34. package/skills/first-tree/tests/helpers.ts +24 -4
  35. package/skills/first-tree/tests/init.test.ts +103 -6
  36. package/skills/first-tree/tests/repo.test.ts +87 -9
  37. package/skills/first-tree/tests/rules.test.ts +26 -7
  38. package/skills/first-tree/tests/skill-artifacts.test.ts +4 -0
  39. package/skills/first-tree/tests/thin-cli.test.ts +52 -7
  40. package/skills/first-tree/tests/upgrade.test.ts +19 -5
  41. package/skills/first-tree/tests/verify.test.ts +106 -7
  42. package/dist/onboarding-6Fr5Gkrk.js +0 -2
  43. package/dist/onboarding-B9zPGvvG.js +0 -10
@@ -1,6 +1,8 @@
1
1
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
- import { join, resolve } from "node:path";
3
- const SKILL_ROOT = join("skills", "first-tree");
2
+ import { basename, dirname, join, resolve } from "node:path";
3
+ //#region skills/first-tree/engine/runtime/asset-loader.ts
4
+ const SKILL_NAME = "first-tree";
5
+ const SKILL_ROOT = join("skills", SKILL_NAME);
4
6
  join(SKILL_ROOT, "agents");
5
7
  join(SKILL_ROOT, "references");
6
8
  const FRAMEWORK_ASSET_ROOT = join(SKILL_ROOT, "assets", "framework");
@@ -12,7 +14,11 @@ join(FRAMEWORK_ASSET_ROOT, "prompts");
12
14
  const FRAMEWORK_EXAMPLES_DIR = join(FRAMEWORK_ASSET_ROOT, "examples");
13
15
  join(FRAMEWORK_ASSET_ROOT, "helpers");
14
16
  const INSTALLED_PROGRESS = join(SKILL_ROOT, "progress.md");
15
- const LEGACY_SKILL_ROOT = join("skills", "first-tree-cli-framework");
17
+ const AGENT_INSTRUCTIONS_FILE = "AGENTS.md";
18
+ const LEGACY_AGENT_INSTRUCTIONS_FILE = "AGENT.md";
19
+ const AGENT_INSTRUCTIONS_TEMPLATE = "agents.md.template";
20
+ const LEGACY_SKILL_NAME = "first-tree-cli-framework";
21
+ const LEGACY_SKILL_ROOT = join("skills", LEGACY_SKILL_NAME);
16
22
  const LEGACY_SKILL_ASSET_ROOT = join(LEGACY_SKILL_ROOT, "assets", "framework");
17
23
  const LEGACY_SKILL_VERSION = join(LEGACY_SKILL_ASSET_ROOT, "VERSION");
18
24
  join(LEGACY_SKILL_ASSET_ROOT, "templates");
@@ -49,6 +55,9 @@ function progressFileCandidates() {
49
55
  LEGACY_PROGRESS
50
56
  ];
51
57
  }
58
+ function agentInstructionsFileCandidates() {
59
+ return [AGENT_INSTRUCTIONS_FILE, LEGACY_AGENT_INSTRUCTIONS_FILE];
60
+ }
52
61
  function resolveFirstExistingPath(root, candidates) {
53
62
  for (const candidate of candidates) if (pathExists(root, candidate)) return candidate;
54
63
  return null;
@@ -64,11 +73,82 @@ function detectFrameworkLayout(root) {
64
73
  const FRONTMATTER_RE = /^---\s*\n(.*?)\n---/s;
65
74
  const OWNERS_RE = /^owners:\s*\[([^\]]*)\]/m;
66
75
  const TITLE_RE = /^title:\s*['"]?(.+?)['"]?\s*$/m;
76
+ const EMPTY_REPO_ENTRY_ALLOWLIST = new Set([
77
+ ".DS_Store",
78
+ ".editorconfig",
79
+ ".gitattributes",
80
+ ".github",
81
+ ".gitignore",
82
+ "AGENT.md",
83
+ "AGENTS.md",
84
+ "CLAUDE.md",
85
+ "LICENSE",
86
+ "LICENSE.md",
87
+ "LICENSE.txt",
88
+ "README",
89
+ "README.md",
90
+ "README.txt"
91
+ ]);
92
+ const SOURCE_FILE_HINTS = new Set([
93
+ ".gitmodules",
94
+ "Cargo.toml",
95
+ "Dockerfile",
96
+ "Gemfile",
97
+ "Makefile",
98
+ "bun.lock",
99
+ "bun.lockb",
100
+ "docker-compose.yml",
101
+ "go.mod",
102
+ "package-lock.json",
103
+ "package.json",
104
+ "pnpm-lock.yaml",
105
+ "pyproject.toml",
106
+ "requirements.txt",
107
+ "tsconfig.json",
108
+ "uv.lock",
109
+ "vite.config.ts",
110
+ "vite.config.js"
111
+ ]);
112
+ const SOURCE_DIR_HINTS = new Set([
113
+ "app",
114
+ "apps",
115
+ "backend",
116
+ "cli",
117
+ "client",
118
+ "docs",
119
+ "e2e",
120
+ "frontend",
121
+ "lib",
122
+ "packages",
123
+ "scripts",
124
+ "server",
125
+ "src",
126
+ "test",
127
+ "tests"
128
+ ]);
67
129
  const FRAMEWORK_END_MARKER = "<!-- END CONTEXT-TREE FRAMEWORK -->";
130
+ function hasGitMetadata(root) {
131
+ try {
132
+ const stat = statSync(join(root, ".git"));
133
+ return stat.isDirectory() || stat.isFile();
134
+ } catch {
135
+ return false;
136
+ }
137
+ }
138
+ function discoverGitRoot(start) {
139
+ let dir = start;
140
+ while (true) {
141
+ if (hasGitMetadata(dir)) return dir;
142
+ const parent = dirname(dir);
143
+ if (parent === dir) return null;
144
+ dir = parent;
145
+ }
146
+ }
68
147
  var Repo = class {
69
148
  root;
70
149
  constructor(root) {
71
- this.root = resolve(root ?? process.cwd());
150
+ const start = resolve(root ?? process.cwd());
151
+ this.root = root === void 0 ? discoverGitRoot(start) ?? start : start;
72
152
  }
73
153
  pathExists(relPath) {
74
154
  return existsSync(join(this.root, relPath));
@@ -109,11 +189,7 @@ var Repo = class {
109
189
  return [".claude/settings.json", ".codex/config.json"].some((c) => this.pathExists(c));
110
190
  }
111
191
  isGitRepo() {
112
- try {
113
- return statSync(join(this.root, ".git")).isDirectory();
114
- } catch {
115
- return false;
116
- }
192
+ return hasGitMetadata(this.root);
117
193
  }
118
194
  hasFramework() {
119
195
  return this.frameworkLayout() !== null;
@@ -142,8 +218,25 @@ var Repo = class {
142
218
  if (layout === "legacy-skill") return LEGACY_SKILL_VERSION;
143
219
  return FRAMEWORK_VERSION;
144
220
  }
145
- hasAgentMdMarkers() {
146
- const text = this.readFile("AGENT.md");
221
+ agentInstructionsPath() {
222
+ return resolveFirstExistingPath(this.root, agentInstructionsFileCandidates());
223
+ }
224
+ hasCanonicalAgentInstructionsFile() {
225
+ return this.pathExists(AGENT_INSTRUCTIONS_FILE);
226
+ }
227
+ hasLegacyAgentInstructionsFile() {
228
+ return this.pathExists(LEGACY_AGENT_INSTRUCTIONS_FILE);
229
+ }
230
+ hasDuplicateAgentInstructionsFiles() {
231
+ return this.hasCanonicalAgentInstructionsFile() && this.hasLegacyAgentInstructionsFile();
232
+ }
233
+ readAgentInstructions() {
234
+ const relPath = this.agentInstructionsPath();
235
+ if (relPath === null) return null;
236
+ return this.readFile(relPath);
237
+ }
238
+ hasAgentInstructionsMarkers() {
239
+ const text = this.readAgentInstructions();
147
240
  if (text === null) return false;
148
241
  return text.includes("<!-- BEGIN CONTEXT-TREE FRAMEWORK") && text.includes("<!-- END CONTEXT-TREE FRAMEWORK -->");
149
242
  }
@@ -182,6 +275,44 @@ var Repo = class {
182
275
  hasPlaceholderNode() {
183
276
  return this.fileContains("NODE.md", "<!-- PLACEHOLDER");
184
277
  }
278
+ repoName() {
279
+ return basename(this.root);
280
+ }
281
+ topLevelEntries() {
282
+ try {
283
+ return readdirSync(this.root).filter((entry) => entry !== ".git");
284
+ } catch {
285
+ return [];
286
+ }
287
+ }
288
+ looksLikeTreeRepo() {
289
+ if (this.pathExists("package.json") && this.pathExists("src/cli.ts") && this.pathExists("skills/first-tree/SKILL.md") && this.progressPath() === null && this.frontmatter("NODE.md") === null && !this.hasAgentInstructionsMarkers() && !this.pathExists("members/NODE.md")) return false;
290
+ return this.progressPath() !== null || this.hasFramework() || this.hasAgentInstructionsMarkers() || this.pathExists("members/NODE.md") || this.frontmatter("NODE.md") !== null;
291
+ }
292
+ isLikelyEmptyRepo() {
293
+ return this.topLevelEntries().filter((entry) => !EMPTY_REPO_ENTRY_ALLOWLIST.has(entry)).length === 0;
294
+ }
295
+ isLikelySourceRepo() {
296
+ if (this.looksLikeTreeRepo()) return false;
297
+ const entries = this.topLevelEntries().filter((entry) => !EMPTY_REPO_ENTRY_ALLOWLIST.has(entry));
298
+ if (entries.length === 0) return false;
299
+ let directoryCount = 0;
300
+ for (const entry of entries) {
301
+ if (SOURCE_FILE_HINTS.has(entry)) return true;
302
+ if (isDirectory(this.root, entry)) {
303
+ directoryCount += 1;
304
+ if (SOURCE_DIR_HINTS.has(entry)) return true;
305
+ }
306
+ }
307
+ return directoryCount >= 2 || entries.length >= 4;
308
+ }
185
309
  };
310
+ function isDirectory(root, relPath) {
311
+ try {
312
+ return statSync(join(root, relPath)).isDirectory();
313
+ } catch {
314
+ return false;
315
+ }
316
+ }
186
317
  //#endregion
187
- export { FRAMEWORK_TEMPLATES_DIR as a, INSTALLED_PROGRESS as c, SKILL_ROOT as d, FRAMEWORK_EXAMPLES_DIR as i, LEGACY_FRAMEWORK_ROOT as l, Repo as n, FRAMEWORK_VERSION as o, FRAMEWORK_ASSET_ROOT as r, FRAMEWORK_WORKFLOWS_DIR as s, FRAMEWORK_END_MARKER as t, LEGACY_SKILL_ROOT as u };
318
+ export { FRAMEWORK_ASSET_ROOT as a, FRAMEWORK_VERSION as c, LEGACY_AGENT_INSTRUCTIONS_FILE as d, LEGACY_FRAMEWORK_ROOT as f, SKILL_ROOT as g, SKILL_NAME as h, AGENT_INSTRUCTIONS_TEMPLATE as i, FRAMEWORK_WORKFLOWS_DIR as l, LEGACY_SKILL_ROOT as m, Repo as n, FRAMEWORK_EXAMPLES_DIR as o, LEGACY_SKILL_NAME as p, AGENT_INSTRUCTIONS_FILE as r, FRAMEWORK_TEMPLATES_DIR as s, FRAMEWORK_END_MARKER as t, INSTALLED_PROGRESS as u };
@@ -1,7 +1,7 @@
1
- import { a as FRAMEWORK_TEMPLATES_DIR, c as INSTALLED_PROGRESS, d as SKILL_ROOT, l as LEGACY_FRAMEWORK_ROOT, n as Repo, o as FRAMEWORK_VERSION, s as FRAMEWORK_WORKFLOWS_DIR, u as LEGACY_SKILL_ROOT } from "./repo-BTJG8BU1.js";
2
- import { i as resolveCanonicalSkillRoot, r as resolveBundledPackageRoot, t as copyCanonicalSkill } from "./installer-rcZpGLnM.js";
1
+ import { c as FRAMEWORK_VERSION, d as LEGACY_AGENT_INSTRUCTIONS_FILE, f as LEGACY_FRAMEWORK_ROOT, g as SKILL_ROOT, i as AGENT_INSTRUCTIONS_TEMPLATE, l as FRAMEWORK_WORKFLOWS_DIR, m as LEGACY_SKILL_ROOT, n as Repo, r as AGENT_INSTRUCTIONS_FILE, s as FRAMEWORK_TEMPLATES_DIR, u as INSTALLED_PROGRESS } from "./repo-DY57bMqr.js";
2
+ import { i as resolveCanonicalSkillRoot, r as resolveBundledPackageRoot, t as copyCanonicalSkill } from "./installer-cH7N4RNj.js";
3
3
  import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
- import { dirname, join } from "node:path";
4
+ import { dirname, join, resolve } from "node:path";
5
5
  //#region skills/first-tree/engine/runtime/upgrader.ts
6
6
  function compareFrameworkVersions(left, right) {
7
7
  const result = left.localeCompare(right, void 0, {
@@ -22,6 +22,12 @@ function readSourceVersion(sourceRoot) {
22
22
  }
23
23
  //#endregion
24
24
  //#region skills/first-tree/engine/upgrade.ts
25
+ const UPGRADE_USAGE = `usage: context-tree upgrade [--tree-path PATH]
26
+
27
+ Options:
28
+ --tree-path PATH Upgrade a tree repo from another working directory
29
+ --help Show this help message
30
+ `;
25
31
  function writeProgress(repo, content) {
26
32
  const progressPath = join(repo.root, repo.preferredProgressPath());
27
33
  mkdirSync(dirname(progressPath), { recursive: true });
@@ -36,14 +42,22 @@ function formatUpgradeTaskList(repo, localVersion, packagedVersion, layout) {
36
42
  `- [ ] Re-check any local agent setup that references \`${SKILL_ROOT}/assets/framework/examples/\` or \`${SKILL_ROOT}/assets/framework/helpers/\``,
37
43
  ""
38
44
  ];
39
- if (layout === "legacy") lines.push("## Migration", "- [ ] Remove any stale `.context-tree/` references from repo-specific docs, scripts, or workflow files", "");
40
- if (layout === "legacy-skill") lines.push("## Migration", `- [ ] Remove any stale \`${LEGACY_SKILL_ROOT}/\` references from repo-specific docs, scripts, workflow files, or agent config`, "");
41
- if (repo.hasAgentMdMarkers()) lines.push("## Agent Instructions", `- [ ] Compare the framework section in \`AGENT.md\` with \`${FRAMEWORK_TEMPLATES_DIR}/agent.md.template\` and update the content between the markers if needed`, "");
45
+ const migrationTasks = [];
46
+ if (layout === "legacy") migrationTasks.push("- [ ] Remove any stale `.context-tree/` references from repo-specific docs, scripts, or workflow files");
47
+ if (layout === "legacy-skill") migrationTasks.push(`- [ ] Remove any stale \`${LEGACY_SKILL_ROOT}/\` references from repo-specific docs, scripts, workflow files, or agent config`);
48
+ if (repo.hasCanonicalAgentInstructionsFile() && repo.hasLegacyAgentInstructionsFile()) migrationTasks.push(`- [ ] Merge any remaining user-authored content from \`${LEGACY_AGENT_INSTRUCTIONS_FILE}\` into \`${AGENT_INSTRUCTIONS_FILE}\`, then delete the legacy file`);
49
+ else if (repo.hasLegacyAgentInstructionsFile()) migrationTasks.push(`- [ ] Rename \`${LEGACY_AGENT_INSTRUCTIONS_FILE}\` to \`${AGENT_INSTRUCTIONS_FILE}\` to use the canonical agent instructions filename`);
50
+ if (migrationTasks.length > 0) lines.push("## Migration", ...migrationTasks, "");
51
+ if (repo.hasAgentInstructionsMarkers()) lines.push("## Agent Instructions", `- [ ] Compare the framework section in \`${AGENT_INSTRUCTIONS_FILE}\` with \`${FRAMEWORK_TEMPLATES_DIR}/${AGENT_INSTRUCTIONS_TEMPLATE}\` and update the content between the markers if needed`, "");
42
52
  lines.push("## Verification", `- [ ] \`${FRAMEWORK_VERSION}\` reads \`${packagedVersion}\``, "- [ ] `context-tree verify` passes", "", "---", "", `**Important:** As you complete each task, check it off in \`${INSTALLED_PROGRESS}\` by changing \`- [ ]\` to \`- [x]\`. Run \`context-tree verify\` when done — it will fail if any items remain unchecked.`, "");
43
53
  return lines.join("\n");
44
54
  }
45
55
  function runUpgrade(repo, options) {
46
56
  const workingRepo = repo ?? new Repo();
57
+ if (workingRepo.isLikelySourceRepo() && !workingRepo.looksLikeTreeRepo()) {
58
+ console.error("Error: no installed framework skill found here. This looks like a source/workspace repo. Run `context-tree init` to create a dedicated tree repo, or pass `--tree-path` to upgrade an existing tree repo.");
59
+ return 1;
60
+ }
47
61
  if (!workingRepo.hasFramework()) {
48
62
  console.error("Error: no installed framework skill found. Run `context-tree init` first.");
49
63
  return 1;
@@ -92,5 +106,30 @@ function runUpgrade(repo, options) {
92
106
  console.log(`Progress file written to ${workingRepo.preferredProgressPath()}`);
93
107
  return 0;
94
108
  }
109
+ function runUpgradeCli(args = []) {
110
+ if (args.includes("--help") || args.includes("-h")) {
111
+ console.log(UPGRADE_USAGE);
112
+ return 0;
113
+ }
114
+ let treePath;
115
+ for (let index = 0; index < args.length; index += 1) {
116
+ const arg = args[index];
117
+ if (arg === "--tree-path") {
118
+ const value = args[index + 1];
119
+ if (!value) {
120
+ console.error("Missing value for --tree-path");
121
+ console.log(UPGRADE_USAGE);
122
+ return 1;
123
+ }
124
+ treePath = value;
125
+ index += 1;
126
+ continue;
127
+ }
128
+ console.error(`Unknown upgrade option: ${arg}`);
129
+ console.log(UPGRADE_USAGE);
130
+ return 1;
131
+ }
132
+ return runUpgrade(treePath ? new Repo(resolve(process.cwd(), treePath)) : void 0);
133
+ }
95
134
  //#endregion
96
- export { runUpgrade };
135
+ export { runUpgradeCli as runUpgrade };
@@ -1,6 +1,6 @@
1
- import { n as Repo } from "./repo-BTJG8BU1.js";
1
+ import { d as LEGACY_AGENT_INSTRUCTIONS_FILE, g as SKILL_ROOT, m as LEGACY_SKILL_ROOT, n as Repo, r as AGENT_INSTRUCTIONS_FILE } from "./repo-DY57bMqr.js";
2
2
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
- import { join, relative } from "node:path";
3
+ import { join, relative, resolve } from "node:path";
4
4
  //#region skills/first-tree/engine/validators/members.ts
5
5
  const FRONTMATTER_RE$1 = /^---\s*\n(.*?)\n---/s;
6
6
  const VALID_TYPES = new Set([
@@ -133,7 +133,11 @@ const SOFT_LINKS_BLOCK_RE = /^soft_links:\s*\n((?:\s+-\s+.+\n?)+)/m;
133
133
  const TITLE_RE = /^title:\s*['"]?(.+?)['"]?\s*$/m;
134
134
  const GITHUB_USER_RE = /^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,37}[a-zA-Z0-9])?$/;
135
135
  const SKIP = new Set(["node_modules", "__pycache__"]);
136
- const SKIP_FILES = new Set(["AGENT.md", "CLAUDE.md"]);
136
+ const SKIP_FILES = new Set([
137
+ AGENT_INSTRUCTIONS_FILE,
138
+ LEGACY_AGENT_INSTRUCTIONS_FILE,
139
+ "CLAUDE.md"
140
+ ]);
137
141
  const MIN_BODY_LENGTH = 20;
138
142
  var Findings = class {
139
143
  errors = [];
@@ -181,8 +185,23 @@ function setTreeRoot(root) {
181
185
  function rel(path) {
182
186
  return relative(treeRoot, path);
183
187
  }
188
+ function isInstalledSkillPath(relPath) {
189
+ return relPath === SKILL_ROOT || relPath.startsWith(`${SKILL_ROOT}/`) || relPath === LEGACY_SKILL_ROOT || relPath.startsWith(`${LEGACY_SKILL_ROOT}/`);
190
+ }
191
+ function isFrameworkContainerDir(relPath, fullPath) {
192
+ if (relPath !== "skills") return false;
193
+ try {
194
+ const entries = readdirSync(fullPath).filter((entry) => !entry.startsWith("."));
195
+ if (entries.length === 0) return false;
196
+ return entries.every((entry) => entry === "first-tree" || entry === "first-tree-cli-framework");
197
+ } catch {
198
+ return false;
199
+ }
200
+ }
184
201
  function shouldSkip(path) {
185
- return relative(treeRoot, path).split("/").some((part) => SKIP.has(part) || part.startsWith("."));
202
+ const relPath = relative(treeRoot, path);
203
+ if (relPath.split("/").some((part) => SKIP.has(part) || part.startsWith("."))) return true;
204
+ return isInstalledSkillPath(relPath) || isFrameworkContainerDir(relPath, path);
186
205
  }
187
206
  function readText(path) {
188
207
  if (!textCache.has(path)) try {
@@ -469,6 +488,12 @@ function runValidateNodes(root) {
469
488
  //#endregion
470
489
  //#region skills/first-tree/engine/verify.ts
471
490
  const UNCHECKED_RE = /^- \[ \] (.+)$/gm;
491
+ const VERIFY_USAGE = `usage: context-tree verify [--tree-path PATH]
492
+
493
+ Options:
494
+ --tree-path PATH Verify a tree repo from another working directory
495
+ --help Show this help message
496
+ `;
472
497
  function check(label, passed) {
473
498
  console.log(` ${passed ? "✓" : "✗"} [${passed ? "PASS" : "FAIL"}] ${label}`);
474
499
  return passed;
@@ -490,6 +515,10 @@ function defaultNodeValidator(root) {
490
515
  function runVerify(repo, nodeValidator) {
491
516
  const r = repo ?? new Repo();
492
517
  const validate = nodeValidator ?? defaultNodeValidator;
518
+ if (r.isLikelySourceRepo() && !r.looksLikeTreeRepo()) {
519
+ console.error("Error: no installed framework skill found here. This looks like a source/workspace repo. Run `context-tree init` to create a dedicated tree repo, or pass `--tree-path` to verify an existing tree repo.");
520
+ return 1;
521
+ }
493
522
  let allPassed = true;
494
523
  const progressPath = r.progressPath() ?? r.preferredProgressPath();
495
524
  const frameworkVersionPath = r.frameworkVersionPath();
@@ -506,7 +535,13 @@ function runVerify(repo, nodeValidator) {
506
535
  allPassed = check(`${frameworkVersionPath} exists`, r.hasFramework()) && allPassed;
507
536
  const fm = r.frontmatter("NODE.md");
508
537
  allPassed = check("Root NODE.md has valid frontmatter (title, owners)", fm !== null && fm.title !== void 0 && fm.owners !== void 0) && allPassed;
509
- allPassed = check("AGENT.md exists with framework markers", r.hasAgentMdMarkers()) && allPassed;
538
+ const hasCanonicalAgentInstructions = r.hasCanonicalAgentInstructionsFile();
539
+ const hasLegacyAgentInstructions = r.hasLegacyAgentInstructionsFile();
540
+ if (hasLegacyAgentInstructions) {
541
+ const followUp = hasCanonicalAgentInstructions ? `Remove legacy \`${LEGACY_AGENT_INSTRUCTIONS_FILE}\` after confirming its contents are in \`${AGENT_INSTRUCTIONS_FILE}\`.` : `Rename \`${LEGACY_AGENT_INSTRUCTIONS_FILE}\` to \`${AGENT_INSTRUCTIONS_FILE}\`.`;
542
+ console.log(` Legacy agent instructions detected. ${followUp}\n`);
543
+ }
544
+ allPassed = check(`AGENTS.md is the only agent instructions file and has framework markers`, hasCanonicalAgentInstructions && !hasLegacyAgentInstructions && r.hasAgentInstructionsMarkers()) && allPassed;
510
545
  const { exitCode } = validate(r.root);
511
546
  allPassed = check("Node validation passes", exitCode === 0) && allPassed;
512
547
  allPassed = check("Member validation passes", runValidateMembers(r.root).exitCode === 0) && allPassed;
@@ -515,5 +550,30 @@ function runVerify(repo, nodeValidator) {
515
550
  else console.log("Some checks failed. See above for details.");
516
551
  return allPassed ? 0 : 1;
517
552
  }
553
+ function runVerifyCli(args = []) {
554
+ if (args.includes("--help") || args.includes("-h")) {
555
+ console.log(VERIFY_USAGE);
556
+ return 0;
557
+ }
558
+ let treePath;
559
+ for (let index = 0; index < args.length; index += 1) {
560
+ const arg = args[index];
561
+ if (arg === "--tree-path") {
562
+ const value = args[index + 1];
563
+ if (!value) {
564
+ console.error("Missing value for --tree-path");
565
+ console.log(VERIFY_USAGE);
566
+ return 1;
567
+ }
568
+ treePath = value;
569
+ index += 1;
570
+ continue;
571
+ }
572
+ console.error(`Unknown verify option: ${arg}`);
573
+ console.log(VERIFY_USAGE);
574
+ return 1;
575
+ }
576
+ return runVerify(treePath ? new Repo(resolve(process.cwd(), treePath)) : void 0);
577
+ }
518
578
  //#endregion
519
- export { runVerify };
579
+ export { runVerifyCli as runVerify };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "first-tree",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "CLI tools for Context Tree — the living source of truth for your organization.",
5
5
  "homepage": "https://github.com/agent-team-foundation/first-tree#readme",
6
6
  "bugs": {
@@ -51,14 +51,17 @@ Use this skill when the task depends on the exact behavior of the
51
51
  thin CLI shell, not as a tree repo.
52
52
  - Keep command behavior, validator behavior, shipped assets, maintainer
53
53
  references, and package shell aligned.
54
- - If root README/AGENT/CI text explains something non-obvious, migrate that
54
+ - If root README/AGENTS/CI text explains something non-obvious, migrate that
55
55
  information into `references/` and trim the root file back down.
56
56
  - If you change runtime assets or skill references, run `pnpm validate:skill`.
57
57
 
58
58
  ### Working In A User Tree Repo
59
59
 
60
- - `context-tree init` installs this skill into the user's repo and scaffolds
61
- `NODE.md`, `AGENT.md`, and `members/NODE.md`.
60
+ - `context-tree init` defaults to creating or reusing a sibling dedicated tree
61
+ repo when invoked from a source/workspace repo. Use `--here` to initialize
62
+ the current repo in place when you are already inside the tree repo.
63
+ - `context-tree init` installs this skill into the target tree repo and
64
+ `NODE.md`, `AGENTS.md`, and `members/NODE.md`.
62
65
  - `context-tree upgrade` refreshes the installed skill from the copy bundled
63
66
  with the currently running `first-tree` package. To pick up a newer
64
67
  framework, run a newer package version first. It also migrates older repos
@@ -77,7 +80,8 @@ Use this skill when the task depends on the exact behavior of the
77
80
  - Keep the skill as the only canonical knowledge source. The root CLI/package
78
81
  shell must not become a second source of framework semantics.
79
82
  - Keep normal `init` / `upgrade` flows self-contained. They must work from the
80
- skill bundled in the current package without cloning the source repo.
83
+ skill bundled in the current package without cloning the source repo or
84
+ relying on network access.
81
85
  - Make upgrade behavior explicit. If you change installed paths, update
82
86
  `references/upgrade-contract.md`, task text, and tests together.
83
87
 
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { execFileSync } from "node:child_process";
11
- import { readFileSync, writeFileSync } from "node:fs";
11
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
12
12
  import { homedir } from "node:os";
13
13
  import { join } from "node:path";
14
14
 
@@ -17,11 +17,25 @@ const MAX_ATTEMPTS = 3;
17
17
  // Per-invocation budget cap. Worst case is $1.50 total (3 × $0.50),
18
18
  // though retries are cheap in practice due to cached context via --continue.
19
19
  const MAX_BUDGET_USD = 0.5;
20
+ const AGENT_INSTRUCTIONS_PATHS = ["AGENTS.md", "AGENT.md"] as const;
21
+
22
+ function resolveAgentInstructionsPath(): string {
23
+ for (const candidate of AGENT_INSTRUCTIONS_PATHS) {
24
+ if (existsSync(candidate)) {
25
+ return candidate;
26
+ }
27
+ }
28
+
29
+ throw new Error(
30
+ "Missing AGENTS.md in repo root (legacy AGENT.md is also accepted during migration).",
31
+ );
32
+ }
20
33
 
21
34
  function buildPrompt(diffPath: string): string {
22
35
  const parts: string[] = [];
36
+ const agentInstructionsPath = resolveAgentInstructionsPath();
23
37
  const files: [string, string][] = [
24
- ["AGENT.md", "AGENT.md"],
38
+ [agentInstructionsPath, agentInstructionsPath],
25
39
  ["Root NODE.md", "NODE.md"],
26
40
  [
27
41
  "Review Instructions",
@@ -28,6 +28,7 @@ Do not skip this. The tree is already a compression of expensive knowledge — c
28
28
 
29
29
  - **Decide in the tree, execute in source systems.** If the task involves a decision (not just a bug fix), draft or update the relevant tree node before executing.
30
30
  - **The tree is not for execution details.** Function signatures, DB schemas, API endpoints, ad copy — those live in source systems. The tree captures the *why* and *how things connect*.
31
+ - **Bring source repos in as additional working directories.** When you need to inspect or edit code, open the relevant source repositories alongside this tree instead of copying execution detail into the tree.
31
32
  - **Respect ownership.** Each node declares owners in its frontmatter. If your changes touch a domain you don't own, flag it — the owner needs to review.
32
33
 
33
34
  ## After Every Task
@@ -5,9 +5,9 @@ owners: [<your-github-username>]
5
5
 
6
6
  # <Your Organization>
7
7
 
8
- <!-- PLACEHOLDER: Replace this with a description of your organization and what this tree captures. -->
8
+ <!-- PLACEHOLDER: Replace this with a description of your organization and what this tree captures across your source repos and systems. -->
9
9
 
10
- The living source of truth for your organization. A structured knowledge base that agents and humans build and maintain together.
10
+ The living source of truth for your organization. A structured knowledge base that agents and humans build and maintain together across one or more source repositories and systems.
11
11
 
12
12
  ---
13
13
 
@@ -29,7 +29,10 @@ The living source of truth for your organization. A structured knowledge base th
29
29
 
30
30
  ## Working with the Tree
31
31
 
32
- See [AGENT.md](AGENT.md) for agent instructions the before/during/after workflow, ownership model, and tree maintenance.
32
+ Keep decision context here. Keep implementation detail in the source repos this
33
+ tree describes.
34
+
35
+ See [AGENTS.md](AGENTS.md) for agent instructions — the before/during/after workflow, ownership model, and tree maintenance.
33
36
 
34
37
  See [about.md](skills/first-tree/references/about.md) for background — the problem, the idea, and who it's for.
35
38
 
@@ -1 +1 @@
1
- export * from "#skill/engine/init.js";
1
+ export { INIT_USAGE, parseInitArgs, runInitCli as runInit } from "#skill/engine/init.js";
@@ -1 +1 @@
1
- export * from "#skill/engine/upgrade.js";
1
+ export { UPGRADE_USAGE, runUpgradeCli as runUpgrade } from "#skill/engine/upgrade.js";
@@ -1 +1 @@
1
- export * from "#skill/engine/verify.js";
1
+ export { VERIFY_USAGE, runVerifyCli as runVerify } from "#skill/engine/verify.js";