first-tree 0.0.2 → 0.0.3

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 +73 -39
  2. package/dist/cli.js +27 -13
  3. package/dist/help-xEI-s9iN.js +25 -0
  4. package/dist/init-DtOjj0wc.js +253 -0
  5. package/dist/installer-rcZpGLnM.js +47 -0
  6. package/dist/onboarding-6Fr5Gkrk.js +2 -0
  7. package/dist/onboarding-B9zPGvvG.js +10 -0
  8. package/dist/repo-BTJG8BU1.js +187 -0
  9. package/dist/upgrade-COGgI7Rj.js +96 -0
  10. package/dist/{verify-CSRIkuoM.js → verify-CxN6JiV9.js} +53 -24
  11. package/package.json +33 -10
  12. package/skills/first-tree/SKILL.md +109 -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 +179 -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/agent.md.template +48 -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 +38 -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 +145 -0
  34. package/skills/first-tree/engine/onboarding.ts +10 -0
  35. package/skills/first-tree/engine/repo.ts +184 -0
  36. package/skills/first-tree/engine/rules/agent-instructions.ts +37 -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 +134 -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 +176 -0
  49. package/skills/first-tree/engine/validators/members.ts +215 -0
  50. package/skills/first-tree/engine/validators/nodes.ts +514 -0
  51. package/skills/first-tree/engine/verify.ts +97 -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 +56 -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 +162 -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 +85 -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 +149 -0
  68. package/skills/first-tree/tests/init.test.ts +153 -0
  69. package/skills/first-tree/tests/repo.test.ts +362 -0
  70. package/skills/first-tree/tests/rules.test.ts +394 -0
  71. package/skills/first-tree/tests/run-review.test.ts +155 -0
  72. package/skills/first-tree/tests/skill-artifacts.test.ts +307 -0
  73. package/skills/first-tree/tests/thin-cli.test.ts +59 -0
  74. package/skills/first-tree/tests/upgrade.test.ts +89 -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 +142 -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,187 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ const SKILL_ROOT = join("skills", "first-tree");
4
+ join(SKILL_ROOT, "agents");
5
+ join(SKILL_ROOT, "references");
6
+ const FRAMEWORK_ASSET_ROOT = join(SKILL_ROOT, "assets", "framework");
7
+ join(FRAMEWORK_ASSET_ROOT, "manifest.json");
8
+ const FRAMEWORK_VERSION = join(FRAMEWORK_ASSET_ROOT, "VERSION");
9
+ const FRAMEWORK_TEMPLATES_DIR = join(FRAMEWORK_ASSET_ROOT, "templates");
10
+ const FRAMEWORK_WORKFLOWS_DIR = join(FRAMEWORK_ASSET_ROOT, "workflows");
11
+ join(FRAMEWORK_ASSET_ROOT, "prompts");
12
+ const FRAMEWORK_EXAMPLES_DIR = join(FRAMEWORK_ASSET_ROOT, "examples");
13
+ join(FRAMEWORK_ASSET_ROOT, "helpers");
14
+ const INSTALLED_PROGRESS = join(SKILL_ROOT, "progress.md");
15
+ const LEGACY_SKILL_ROOT = join("skills", "first-tree-cli-framework");
16
+ const LEGACY_SKILL_ASSET_ROOT = join(LEGACY_SKILL_ROOT, "assets", "framework");
17
+ const LEGACY_SKILL_VERSION = join(LEGACY_SKILL_ASSET_ROOT, "VERSION");
18
+ join(LEGACY_SKILL_ASSET_ROOT, "templates");
19
+ join(LEGACY_SKILL_ASSET_ROOT, "workflows");
20
+ join(LEGACY_SKILL_ASSET_ROOT, "prompts");
21
+ join(LEGACY_SKILL_ASSET_ROOT, "examples");
22
+ const LEGACY_SKILL_PROGRESS = join(LEGACY_SKILL_ROOT, "progress.md");
23
+ const LEGACY_FRAMEWORK_ROOT = ".context-tree";
24
+ const LEGACY_VERSION = join(LEGACY_FRAMEWORK_ROOT, "VERSION");
25
+ const LEGACY_PROGRESS = join(LEGACY_FRAMEWORK_ROOT, "progress.md");
26
+ join(LEGACY_FRAMEWORK_ROOT, "templates");
27
+ join(LEGACY_FRAMEWORK_ROOT, "workflows");
28
+ join(LEGACY_FRAMEWORK_ROOT, "prompts");
29
+ join(LEGACY_FRAMEWORK_ROOT, "examples");
30
+ function pathExists(root, relPath) {
31
+ const fullPath = join(root, relPath);
32
+ try {
33
+ return existsSync(fullPath);
34
+ } catch {
35
+ return false;
36
+ }
37
+ }
38
+ function frameworkVersionCandidates() {
39
+ return [
40
+ FRAMEWORK_VERSION,
41
+ LEGACY_SKILL_VERSION,
42
+ LEGACY_VERSION
43
+ ];
44
+ }
45
+ function progressFileCandidates() {
46
+ return [
47
+ INSTALLED_PROGRESS,
48
+ LEGACY_SKILL_PROGRESS,
49
+ LEGACY_PROGRESS
50
+ ];
51
+ }
52
+ function resolveFirstExistingPath(root, candidates) {
53
+ for (const candidate of candidates) if (pathExists(root, candidate)) return candidate;
54
+ return null;
55
+ }
56
+ function detectFrameworkLayout(root) {
57
+ if (pathExists(root, FRAMEWORK_VERSION)) return "skill";
58
+ if (pathExists(root, LEGACY_SKILL_VERSION)) return "legacy-skill";
59
+ if (pathExists(root, LEGACY_VERSION)) return "legacy";
60
+ return null;
61
+ }
62
+ //#endregion
63
+ //#region skills/first-tree/engine/repo.ts
64
+ const FRONTMATTER_RE = /^---\s*\n(.*?)\n---/s;
65
+ const OWNERS_RE = /^owners:\s*\[([^\]]*)\]/m;
66
+ const TITLE_RE = /^title:\s*['"]?(.+?)['"]?\s*$/m;
67
+ const FRAMEWORK_END_MARKER = "<!-- END CONTEXT-TREE FRAMEWORK -->";
68
+ var Repo = class {
69
+ root;
70
+ constructor(root) {
71
+ this.root = resolve(root ?? process.cwd());
72
+ }
73
+ pathExists(relPath) {
74
+ return existsSync(join(this.root, relPath));
75
+ }
76
+ fileContains(relPath, text) {
77
+ const fullPath = join(this.root, relPath);
78
+ try {
79
+ if (!statSync(fullPath).isFile()) return false;
80
+ return readFileSync(fullPath, "utf-8").includes(text);
81
+ } catch {
82
+ return false;
83
+ }
84
+ }
85
+ readFile(relPath) {
86
+ try {
87
+ return readFileSync(join(this.root, relPath), "utf-8");
88
+ } catch {
89
+ return null;
90
+ }
91
+ }
92
+ frontmatter(relPath) {
93
+ const text = this.readFile(relPath);
94
+ if (text === null) return null;
95
+ const m = text.match(FRONTMATTER_RE);
96
+ if (!m) return null;
97
+ const fm = m[1];
98
+ const result = {};
99
+ const titleM = fm.match(TITLE_RE);
100
+ if (titleM) result.title = titleM[1].trim();
101
+ const ownersM = fm.match(OWNERS_RE);
102
+ if (ownersM) {
103
+ const raw = ownersM[1].trim();
104
+ result.owners = raw ? raw.split(",").map((o) => o.trim()).filter(Boolean) : [];
105
+ }
106
+ return result.title !== void 0 || result.owners !== void 0 ? result : null;
107
+ }
108
+ anyAgentConfig() {
109
+ return [".claude/settings.json", ".codex/config.json"].some((c) => this.pathExists(c));
110
+ }
111
+ isGitRepo() {
112
+ try {
113
+ return statSync(join(this.root, ".git")).isDirectory();
114
+ } catch {
115
+ return false;
116
+ }
117
+ }
118
+ hasFramework() {
119
+ return this.frameworkLayout() !== null;
120
+ }
121
+ frameworkLayout() {
122
+ return detectFrameworkLayout(this.root);
123
+ }
124
+ readVersion() {
125
+ const versionPath = resolveFirstExistingPath(this.root, frameworkVersionCandidates());
126
+ if (versionPath === null) return null;
127
+ const text = this.readFile(versionPath);
128
+ return text ? text.trim() : null;
129
+ }
130
+ progressPath() {
131
+ return resolveFirstExistingPath(this.root, progressFileCandidates());
132
+ }
133
+ preferredProgressPath() {
134
+ const layout = this.frameworkLayout();
135
+ if (layout === "legacy") return LEGACY_PROGRESS;
136
+ if (layout === "legacy-skill") return LEGACY_SKILL_PROGRESS;
137
+ return INSTALLED_PROGRESS;
138
+ }
139
+ frameworkVersionPath() {
140
+ const layout = this.frameworkLayout();
141
+ if (layout === "legacy") return LEGACY_VERSION;
142
+ if (layout === "legacy-skill") return LEGACY_SKILL_VERSION;
143
+ return FRAMEWORK_VERSION;
144
+ }
145
+ hasAgentMdMarkers() {
146
+ const text = this.readFile("AGENT.md");
147
+ if (text === null) return false;
148
+ return text.includes("<!-- BEGIN CONTEXT-TREE FRAMEWORK") && text.includes("<!-- END CONTEXT-TREE FRAMEWORK -->");
149
+ }
150
+ hasMembers() {
151
+ const membersDir = join(this.root, "members");
152
+ try {
153
+ if (!statSync(membersDir).isDirectory()) return false;
154
+ } catch {
155
+ return false;
156
+ }
157
+ return existsSync(join(membersDir, "NODE.md"));
158
+ }
159
+ memberCount() {
160
+ const membersDir = join(this.root, "members");
161
+ try {
162
+ if (!statSync(membersDir).isDirectory()) return 0;
163
+ } catch {
164
+ return 0;
165
+ }
166
+ let count = 0;
167
+ const walk = (dir) => {
168
+ for (const entry of readdirSync(dir)) {
169
+ const childPath = join(dir, entry);
170
+ try {
171
+ if (!statSync(childPath).isDirectory()) continue;
172
+ } catch {
173
+ continue;
174
+ }
175
+ if (existsSync(join(childPath, "NODE.md"))) count++;
176
+ walk(childPath);
177
+ }
178
+ };
179
+ walk(membersDir);
180
+ return count;
181
+ }
182
+ hasPlaceholderNode() {
183
+ return this.fileContains("NODE.md", "<!-- PLACEHOLDER");
184
+ }
185
+ };
186
+ //#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 };
@@ -0,0 +1,96 @@
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";
3
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+ //#region skills/first-tree/engine/runtime/upgrader.ts
6
+ function compareFrameworkVersions(left, right) {
7
+ const result = left.localeCompare(right, void 0, {
8
+ numeric: true,
9
+ sensitivity: "base"
10
+ });
11
+ if (result < 0) return -1;
12
+ if (result > 0) return 1;
13
+ return 0;
14
+ }
15
+ function readSourceVersion(sourceRoot) {
16
+ const versionPath = join(resolveCanonicalSkillRoot(sourceRoot), "assets", "framework", "VERSION");
17
+ try {
18
+ return readFileSync(versionPath, "utf-8").trim();
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+ //#endregion
24
+ //#region skills/first-tree/engine/upgrade.ts
25
+ function writeProgress(repo, content) {
26
+ const progressPath = join(repo.root, repo.preferredProgressPath());
27
+ mkdirSync(dirname(progressPath), { recursive: true });
28
+ writeFileSync(progressPath, content);
29
+ }
30
+ function formatUpgradeTaskList(repo, localVersion, packagedVersion, layout) {
31
+ const lines = [
32
+ `# Context Tree Upgrade — v${localVersion} -> v${packagedVersion}\n`,
33
+ "## Installed Skill",
34
+ `- [ ] Review local customizations under \`${SKILL_ROOT}/\` and reapply them if needed`,
35
+ `- [ ] Re-copy any workflow updates you want from \`${FRAMEWORK_WORKFLOWS_DIR}/\` into \`.github/workflows/\``,
36
+ `- [ ] Re-check any local agent setup that references \`${SKILL_ROOT}/assets/framework/examples/\` or \`${SKILL_ROOT}/assets/framework/helpers/\``,
37
+ ""
38
+ ];
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`, "");
42
+ 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
+ return lines.join("\n");
44
+ }
45
+ function runUpgrade(repo, options) {
46
+ const workingRepo = repo ?? new Repo();
47
+ if (!workingRepo.hasFramework()) {
48
+ console.error("Error: no installed framework skill found. Run `context-tree init` first.");
49
+ return 1;
50
+ }
51
+ const layout = workingRepo.frameworkLayout();
52
+ if (layout === null) {
53
+ console.error("Error: no installed framework skill found. Run `context-tree init` first.");
54
+ return 1;
55
+ }
56
+ const localVersion = workingRepo.readVersion() ?? "unknown";
57
+ console.log(`Local framework version: ${localVersion}\n`);
58
+ console.log("Checking the framework skill bundled with this first-tree package...");
59
+ let sourceRoot;
60
+ try {
61
+ sourceRoot = options?.sourceRoot ?? resolveBundledPackageRoot();
62
+ } catch (err) {
63
+ const message = err instanceof Error ? err.message : "unknown error";
64
+ console.error(`Error: ${message}`);
65
+ return 1;
66
+ }
67
+ const packagedVersion = readSourceVersion(sourceRoot);
68
+ if (packagedVersion === null) {
69
+ console.log("Could not read the bundled framework version. Reinstall or update `first-tree` and try again.");
70
+ return 1;
71
+ }
72
+ if (localVersion !== "unknown" && compareFrameworkVersions(localVersion, packagedVersion) > 0) {
73
+ console.log("The installed framework is newer than the skill bundled with this `first-tree` package. Install a newer package version before running `context-tree upgrade`.");
74
+ return 1;
75
+ }
76
+ if (layout === "skill" && packagedVersion === localVersion) {
77
+ console.log(`Already up to date with the bundled skill (${FRAMEWORK_VERSION} = ${localVersion}).`);
78
+ return 0;
79
+ }
80
+ copyCanonicalSkill(sourceRoot, workingRepo.root);
81
+ if (layout === "legacy") {
82
+ rmSync(join(workingRepo.root, LEGACY_FRAMEWORK_ROOT), {
83
+ recursive: true,
84
+ force: true
85
+ });
86
+ console.log("Migrated legacy .context-tree/ layout to skills/first-tree/.");
87
+ } else if (layout === "legacy-skill") console.log("Migrated skills/first-tree-cli-framework/ to skills/first-tree/.");
88
+ else console.log("Refreshed skills/first-tree/ from the bundled first-tree package.");
89
+ const output = formatUpgradeTaskList(workingRepo, localVersion, packagedVersion, layout);
90
+ console.log(`\n${output}`);
91
+ writeProgress(workingRepo, output);
92
+ console.log(`Progress file written to ${workingRepo.preferredProgressPath()}`);
93
+ return 0;
94
+ }
95
+ //#endregion
96
+ export { runUpgrade };
@@ -1,7 +1,7 @@
1
- import { n as Repo } from "./repo-BByc3VvM.js";
1
+ import { n as Repo } from "./repo-BTJG8BU1.js";
2
2
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
3
  import { join, relative } from "node:path";
4
- //#region src/validators/members.ts
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([
7
7
  "human",
@@ -64,25 +64,51 @@ function runValidateMembers(treeRoot) {
64
64
  }
65
65
  const allErrors = [];
66
66
  let memberCount = 0;
67
- for (const child of readdirSync(membersDir).sort()) {
68
- const childPath = join(membersDir, child);
69
- try {
70
- const stat = statSync(childPath);
71
- if (stat.isFile() && child.endsWith(".md") && child !== "NODE.md") {
72
- allErrors.push(`${rel$1(childPath, treeRoot)}: member must be a directory with NODE.md, not a standalone file — use members/${child.replace(/\.md$/, "")}/NODE.md instead`);
67
+ const nameOccurrences = /* @__PURE__ */ new Map();
68
+ const members = [];
69
+ function walk(dir) {
70
+ for (const child of readdirSync(dir).sort()) {
71
+ const childPath = join(dir, child);
72
+ try {
73
+ const stat = statSync(childPath);
74
+ if (stat.isFile() && child.endsWith(".md") && child !== "NODE.md") {
75
+ allErrors.push(`${rel$1(childPath, treeRoot)}: member must be a directory with NODE.md, not a standalone file — use ${rel$1(dir, treeRoot)}/${child.replace(/\.md$/, "")}/NODE.md instead`);
76
+ continue;
77
+ }
78
+ if (!stat.isDirectory()) continue;
79
+ } catch {
73
80
  continue;
74
81
  }
75
- if (!stat.isDirectory()) continue;
76
- } catch {
77
- continue;
78
- }
79
- const nodePath = join(childPath, "NODE.md");
80
- if (!existsSync(nodePath)) {
81
- allErrors.push(`${rel$1(childPath, treeRoot)}/: directory exists but missing NODE.md`);
82
- continue;
82
+ const relPath = relative(membersDir, childPath);
83
+ const occurrences = nameOccurrences.get(child) ?? [];
84
+ occurrences.push(relPath);
85
+ nameOccurrences.set(child, occurrences);
86
+ const nodePath = join(childPath, "NODE.md");
87
+ if (!existsSync(nodePath)) {
88
+ allErrors.push(`${rel$1(childPath, treeRoot)}/: directory exists but missing NODE.md`);
89
+ walk(childPath);
90
+ continue;
91
+ }
92
+ memberCount++;
93
+ allErrors.push(...validateMember(nodePath, treeRoot));
94
+ const fm = parseFrontmatter$1(nodePath);
95
+ if (fm) members.push({
96
+ name: child,
97
+ relPath,
98
+ type: extractScalar(fm, "type"),
99
+ delegateMention: extractScalar(fm, "delegate_mention")
100
+ });
101
+ walk(childPath);
83
102
  }
84
- memberCount++;
85
- allErrors.push(...validateMember(nodePath, treeRoot));
103
+ }
104
+ walk(membersDir);
105
+ for (const [name, paths] of nameOccurrences) if (paths.length > 1) allErrors.push(`Duplicate member directory name '${name}' found at: ${paths.map((p) => `members/${p}`).join(", ")} — directory names must be unique across all levels under members/`);
106
+ const memberByName = new Map(members.map((m) => [m.name, m]));
107
+ for (const member of members) {
108
+ if (!member.delegateMention) continue;
109
+ const target = memberByName.get(member.delegateMention);
110
+ if (!target) allErrors.push(`members/${member.relPath}/NODE.md: delegate_mention '${member.delegateMention}' references non-existent member — the target must be a directory under members/`);
111
+ else if (target.type !== "personal_assistant") allErrors.push(`members/${member.relPath}/NODE.md: delegate_mention '${member.delegateMention}' must reference a member with type 'personal_assistant', but '${target.name}' has type '${target.type}'`);
86
112
  }
87
113
  if (allErrors.length > 0) {
88
114
  console.log(`Found ${allErrors.length} member validation error(s):\n`);
@@ -99,7 +125,7 @@ function runValidateMembers(treeRoot) {
99
125
  };
100
126
  }
101
127
  //#endregion
102
- //#region src/validators/nodes.ts
128
+ //#region skills/first-tree/engine/validators/nodes.ts
103
129
  const FRONTMATTER_RE = /^---\s*\n(.*?)\n---/s;
104
130
  const OWNERS_RE = /^owners:\s*\[([^\]]*)\]/m;
105
131
  const SOFT_LINKS_INLINE_RE = /^soft_links:\s*\[([^\]]*)\]/m;
@@ -441,14 +467,15 @@ function runValidateNodes(root) {
441
467
  };
442
468
  }
443
469
  //#endregion
444
- //#region src/verify.ts
470
+ //#region skills/first-tree/engine/verify.ts
445
471
  const UNCHECKED_RE = /^- \[ \] (.+)$/gm;
446
472
  function check(label, passed) {
447
473
  console.log(` ${passed ? "✓" : "✗"} [${passed ? "PASS" : "FAIL"}] ${label}`);
448
474
  return passed;
449
475
  }
450
476
  function checkProgress(repo) {
451
- const text = repo.readFile(".context-tree/progress.md");
477
+ const progressPath = repo.progressPath();
478
+ const text = progressPath === null ? null : repo.readFile(progressPath);
452
479
  if (text === null) return [];
453
480
  const matches = [];
454
481
  let m;
@@ -464,17 +491,19 @@ function runVerify(repo, nodeValidator) {
464
491
  const r = repo ?? new Repo();
465
492
  const validate = nodeValidator ?? defaultNodeValidator;
466
493
  let allPassed = true;
494
+ const progressPath = r.progressPath() ?? r.preferredProgressPath();
495
+ const frameworkVersionPath = r.frameworkVersionPath();
467
496
  console.log("Context Tree Verification\n");
468
497
  const unchecked = checkProgress(r);
469
498
  if (unchecked.length > 0) {
470
- console.log(" Unchecked items in .context-tree/progress.md:\n");
499
+ console.log(` Unchecked items in ${progressPath}:\n`);
471
500
  for (const item of unchecked) console.log(` - [ ] ${item}`);
472
501
  console.log();
473
- console.log(" Verify each step above and check it off in progress.md before running verify again.\n");
502
+ console.log(` Verify each step above and check it off in ${progressPath} before running verify again.\n`);
474
503
  allPassed = false;
475
504
  }
476
505
  console.log(" Checks:\n");
477
- allPassed = check(".context-tree/VERSION exists", r.hasFramework()) && allPassed;
506
+ allPassed = check(`${frameworkVersionPath} exists`, r.hasFramework()) && allPassed;
478
507
  const fm = r.frontmatter("NODE.md");
479
508
  allPassed = check("Root NODE.md has valid frontmatter (title, owners)", fm !== null && fm.title !== void 0 && fm.owners !== void 0) && allPassed;
480
509
  allPassed = check("AGENT.md exists with framework markers", r.hasAgentMdMarkers()) && allPassed;
package/package.json CHANGED
@@ -1,25 +1,48 @@
1
1
  {
2
2
  "name": "first-tree",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "CLI tools for Context Tree — the living source of truth for your organization.",
5
+ "homepage": "https://github.com/agent-team-foundation/first-tree#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/agent-team-foundation/first-tree/issues"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/agent-team-foundation/first-tree.git"
12
+ },
13
+ "keywords": [
14
+ "context-tree",
15
+ "cli",
16
+ "agents",
17
+ "knowledge-base",
18
+ "markdown"
19
+ ],
5
20
  "type": "module",
6
21
  "bin": {
7
22
  "context-tree": "./dist/cli.js"
8
23
  },
24
+ "imports": {
25
+ "#skill/*": "./skills/first-tree/*",
26
+ "#evals/*": "./evals/*",
27
+ "#src/*": "./src/*"
28
+ },
9
29
  "files": [
10
- "dist"
30
+ "dist",
31
+ "skills/first-tree"
11
32
  ],
12
- "scripts": {
13
- "build": "tsdown src/cli.ts --format esm --out-dir dist",
14
- "prepack": "pnpm build",
15
- "test": "vitest run",
16
- "typecheck": "tsc --noEmit"
17
- },
18
33
  "license": "Apache-2.0",
19
34
  "devDependencies": {
20
35
  "@types/node": "^25.5.0",
21
36
  "tsdown": "^0.12.0",
22
37
  "typescript": "^5.8.0",
23
- "vitest": "^3.2.0"
38
+ "vitest": "^3.2.0",
39
+ "yaml": "^2.8.3"
40
+ },
41
+ "scripts": {
42
+ "build": "tsdown",
43
+ "test": "vitest run",
44
+ "eval": "vitest run --config vitest.eval.config.ts",
45
+ "typecheck": "tsc --noEmit",
46
+ "validate:skill": "python3 ./skills/first-tree/scripts/quick_validate.py ./skills/first-tree && bash ./skills/first-tree/scripts/check-skill-sync.sh"
24
47
  }
25
- }
48
+ }
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: first-tree
3
+ description: Maintain the canonical `first-tree` skill and the thin `context-tree` CLI distributed by the `first-tree` npm package. Use when modifying `context-tree` commands (`init`, `verify`, `upgrade`, `help onboarding`), the installed skill payload under `assets/framework/`, maintainer references, or the build, packaging, test, and CI wiring that supports the framework.
4
+ ---
5
+
6
+ # First Tree
7
+
8
+ Use this skill when the task depends on the exact behavior of the
9
+ `context-tree` CLI or the installed `skills/first-tree/` payload that
10
+ `context-tree init` ships to user repos.
11
+
12
+ ## Source Of Truth
13
+
14
+ - `skills/first-tree/` is the only canonical copy.
15
+ - `references/` holds the explanatory docs the skill should load on demand.
16
+ - `assets/framework/` holds the runtime payload that gets installed into user
17
+ repos.
18
+ - `engine/` holds the canonical framework and CLI behavior.
19
+ - `scripts/` holds maintenance helpers for validating and running the skill.
20
+ - In maintainer docs, use `context-tree` for the CLI and `skills/first-tree/`
21
+ for the installed skill path so it is not confused with the `first-tree`
22
+ npm package.
23
+
24
+ ## When To Read What
25
+
26
+ 1. Start with `references/source-map.md` to locate the right files.
27
+ 2. Read the user-facing reference that matches the task:
28
+ - `references/onboarding.md`
29
+ - `references/about.md`
30
+ - `references/principles.md`
31
+ - `references/ownership-and-naming.md`
32
+ - `references/upgrade-contract.md`
33
+ 3. Read the maintainer reference that matches the shell or validation surface:
34
+ - `references/maintainer-architecture.md`
35
+ - `references/maintainer-thin-cli.md`
36
+ - `references/maintainer-build-and-distribution.md`
37
+ - `references/maintainer-testing.md`
38
+ 4. Open `engine/` when changing `init`, `verify`, `upgrade`, command routing,
39
+ repo inspection, rules, runtime helpers, or validators.
40
+ 5. Open `assets/framework/` only when the task changes shipped templates,
41
+ workflows, prompts, examples, or helper scripts.
42
+ 6. Open `tests/` when changing validation coverage or maintainer workflows.
43
+ 7. Use `./scripts/run-local-cli.sh <command>` when you need to exercise the
44
+ live CLI from this repo.
45
+
46
+ ## Working Modes
47
+
48
+ ### Maintaining `first-tree`
49
+
50
+ - Treat this repo as the distribution source for one canonical skill plus a
51
+ thin CLI shell, not as a tree repo.
52
+ - Keep command behavior, validator behavior, shipped assets, maintainer
53
+ references, and package shell aligned.
54
+ - If root README/AGENT/CI text explains something non-obvious, migrate that
55
+ information into `references/` and trim the root file back down.
56
+ - If you change runtime assets or skill references, run `pnpm validate:skill`.
57
+
58
+ ### Working In A User Tree Repo
59
+
60
+ - `context-tree init` installs this skill into the user's repo and scaffolds
61
+ `NODE.md`, `AGENT.md`, and `members/NODE.md`.
62
+ - `context-tree upgrade` refreshes the installed skill from the copy bundled
63
+ with the currently running `first-tree` package. To pick up a newer
64
+ framework, run a newer package version first. It also migrates older repos
65
+ that still use `skills/first-tree-cli-framework/`.
66
+ - The user's tree content lives outside the skill; the skill only carries the
67
+ reusable framework payload plus maintenance guidance.
68
+ - The tree still stores decisions, constraints, and ownership; execution detail
69
+ stays in source systems.
70
+
71
+ ## Non-Negotiables
72
+
73
+ - Preserve the CLI contract that it scaffolds, prints task lists, and validates
74
+ state; it does not fully automate tree maintenance.
75
+ - Keep shipped assets generic. They must not contain org-specific content.
76
+ - Keep decision knowledge in the tree and execution detail in source systems.
77
+ - Keep the skill as the only canonical knowledge source. The root CLI/package
78
+ shell must not become a second source of framework semantics.
79
+ - Keep normal `init` / `upgrade` flows self-contained. They must work from the
80
+ skill bundled in the current package without cloning the source repo.
81
+ - Make upgrade behavior explicit. If you change installed paths, update
82
+ `references/upgrade-contract.md`, task text, and tests together.
83
+
84
+ ## Validation
85
+
86
+ - Repo checks: `pnpm typecheck`, `pnpm test`, `pnpm build`
87
+ - Packaging check: `pnpm pack` when changing package contents or install/upgrade
88
+ behavior
89
+ - Skill checks:
90
+ - `pnpm validate:skill`
91
+ - `python3 ./skills/first-tree/scripts/quick_validate.py ./skills/first-tree`
92
+ - `bash ./skills/first-tree/scripts/check-skill-sync.sh`
93
+
94
+ ## Key Files
95
+
96
+ - `assets/framework/manifest.json`: runtime asset contract
97
+ - `assets/framework/templates/`: generated scaffolds
98
+ - `assets/framework/workflows/`: CI templates
99
+ - `assets/framework/helpers/`: shipped helper scripts and review tooling
100
+ - `engine/`: canonical framework and CLI behavior
101
+ - `tests/`: canonical unit and structure validation
102
+ - `references/source-map.md`: canonical reading index
103
+ - `references/maintainer-architecture.md`: source-repo architecture and
104
+ invariants
105
+ - `references/maintainer-thin-cli.md`: root shell contract
106
+ - `references/maintainer-build-and-distribution.md`: packaging and release
107
+ guidance
108
+ - `references/maintainer-testing.md`: validation workflow
109
+ - `references/upgrade-contract.md`: installed layout and upgrade semantics
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "First Tree"
3
+ short_description: "Maintain the First Tree skill and thin Context Tree CLI"
4
+ default_prompt: "Use $first-tree to maintain the canonical first-tree skill, its thin context-tree CLI, or its build, packaging, test, eval, and CI wiring."
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,14 @@
1
+ # Claude Code Integration
2
+
3
+ ## Setup
4
+
5
+ Copy `settings.json` to your tree repo's `.claude/` directory:
6
+
7
+ ```bash
8
+ mkdir -p .claude
9
+ cp skills/first-tree/assets/framework/examples/claude-code/settings.json .claude/settings.json
10
+ ```
11
+
12
+ ## What It Does
13
+
14
+ The `SessionStart` hook runs `./skills/first-tree/assets/framework/helpers/inject-tree-context.sh` when a Claude Code session begins. This injects the root `NODE.md` content as additional context, giving the agent an overview of the tree structure before any task.
@@ -0,0 +1,14 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "./skills/first-tree/assets/framework/helpers/inject-tree-context.sh"
9
+ }
10
+ ]
11
+ }
12
+ ]
13
+ }
14
+ }