first-tree 0.0.1 → 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-DIz6qmBX.js → verify-CxN6JiV9.js} +135 -8
  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,215 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { basename, join, relative } from "node:path";
3
+
4
+ const FRONTMATTER_RE = /^---\s*\n(.*?)\n---/s;
5
+ const VALID_TYPES = new Set(["human", "personal_assistant", "autonomous_agent"]);
6
+
7
+ function rel(path: string, root: string): string {
8
+ return relative(root, path);
9
+ }
10
+
11
+ function parseFrontmatter(path: string): string | null {
12
+ try {
13
+ const text = readFileSync(path, "utf-8");
14
+ const m = text.match(FRONTMATTER_RE);
15
+ return m ? m[1] : null;
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+
21
+ export function extractScalar(fm: string, key: string): string | null {
22
+ const re = new RegExp(`^${key}:\\s*"?([^"\\n]+?)"?\\s*$`, "m");
23
+ const m = fm.match(re);
24
+ return m ? m[1].trim() : null;
25
+ }
26
+
27
+ export function extractList(fm: string, key: string): string[] | null {
28
+ // Inline: key: [a, b]
29
+ const inlineRe = new RegExp(`^${key}:\\s*\\[([^\\]]*)\\]`, "m");
30
+ let m = fm.match(inlineRe);
31
+ if (m) {
32
+ const raw = m[1].trim();
33
+ if (!raw) return [];
34
+ return raw
35
+ .split(",")
36
+ .map((s) => s.trim().replace(/^['"]|['"]$/g, ""))
37
+ .filter(Boolean);
38
+ }
39
+
40
+ // Block: key:\n - a\n - b
41
+ const blockRe = new RegExp(`^${key}:\\s*\\n((?:\\s+-\\s+.+\\n?)+)`, "m");
42
+ m = fm.match(blockRe);
43
+ if (m) {
44
+ return m[1]
45
+ .trim()
46
+ .split("\n")
47
+ .filter((line) => line.trim())
48
+ .map((line) =>
49
+ line
50
+ .trim()
51
+ .replace(/^-\s*/, "")
52
+ .trim()
53
+ .replace(/^['"]|['"]$/g, ""),
54
+ );
55
+ }
56
+
57
+ return null;
58
+ }
59
+
60
+ export function validateMember(
61
+ nodePath: string,
62
+ treeRoot: string,
63
+ ): string[] {
64
+ const errors: string[] = [];
65
+ const loc = rel(nodePath, treeRoot);
66
+
67
+ const fm = parseFrontmatter(nodePath);
68
+ if (fm === null) return [`${loc}: no frontmatter found`];
69
+
70
+ // title
71
+ const title = extractScalar(fm, "title");
72
+ if (!title) errors.push(`${loc}: missing or empty 'title' field`);
73
+
74
+ // owners
75
+ const owners = extractList(fm, "owners");
76
+ if (owners === null) errors.push(`${loc}: missing 'owners' field`);
77
+
78
+ // type
79
+ const memberType = extractScalar(fm, "type");
80
+ if (!memberType) {
81
+ errors.push(`${loc}: missing 'type' field`);
82
+ } else if (!VALID_TYPES.has(memberType)) {
83
+ errors.push(
84
+ `${loc}: invalid type '${memberType}' — must be one of: ${[...VALID_TYPES].sort().join(", ")}`,
85
+ );
86
+ }
87
+
88
+ // role
89
+ const role = extractScalar(fm, "role");
90
+ if (!role) errors.push(`${loc}: missing or empty 'role' field`);
91
+
92
+ // domains
93
+ const domains = extractList(fm, "domains");
94
+ if (domains === null) {
95
+ errors.push(`${loc}: missing 'domains' field`);
96
+ } else if (domains.length === 0) {
97
+ errors.push(`${loc}: 'domains' must contain at least one entry`);
98
+ }
99
+
100
+ return errors;
101
+ }
102
+
103
+ /** Info collected per member for cross-validation. */
104
+ type MemberInfo = {
105
+ name: string;
106
+ relPath: string;
107
+ type: string | null;
108
+ delegateMention: string | null;
109
+ };
110
+
111
+ export function runValidateMembers(treeRoot: string): {
112
+ exitCode: number;
113
+ errors: string[];
114
+ } {
115
+ const membersDir = join(treeRoot, "members");
116
+ if (!existsSync(membersDir) || !statSync(membersDir).isDirectory()) {
117
+ console.log(`Members directory not found: ${membersDir}`);
118
+ return { exitCode: 1, errors: [] };
119
+ }
120
+
121
+ const allErrors: string[] = [];
122
+ let memberCount = 0;
123
+
124
+ // Collected for cross-validation (name uniqueness + delegate_mention)
125
+ const nameOccurrences = new Map<string, string[]>();
126
+ const members: MemberInfo[] = [];
127
+
128
+ function walk(dir: string): void {
129
+ for (const child of readdirSync(dir).sort()) {
130
+ const childPath = join(dir, child);
131
+
132
+ // Reject stray .md files
133
+ try {
134
+ const stat = statSync(childPath);
135
+ if (stat.isFile() && child.endsWith(".md") && child !== "NODE.md") {
136
+ allErrors.push(
137
+ `${rel(childPath, treeRoot)}: member must be a directory with NODE.md, not a standalone file — use ${rel(dir, treeRoot)}/${child.replace(/\.md$/, "")}/NODE.md instead`,
138
+ );
139
+ continue;
140
+ }
141
+ if (!stat.isDirectory()) continue;
142
+ } catch {
143
+ continue;
144
+ }
145
+
146
+ // Track directory name occurrences for uniqueness check
147
+ const relPath = relative(membersDir, childPath);
148
+ const occurrences = nameOccurrences.get(child) ?? [];
149
+ occurrences.push(relPath);
150
+ nameOccurrences.set(child, occurrences);
151
+
152
+ const nodePath = join(childPath, "NODE.md");
153
+ if (!existsSync(nodePath)) {
154
+ allErrors.push(`${rel(childPath, treeRoot)}/: directory exists but missing NODE.md`);
155
+ walk(childPath);
156
+ continue;
157
+ }
158
+
159
+ memberCount++;
160
+ allErrors.push(...validateMember(nodePath, treeRoot));
161
+
162
+ // Collect info for cross-validation
163
+ const fm = parseFrontmatter(nodePath);
164
+ if (fm) {
165
+ members.push({
166
+ name: child,
167
+ relPath,
168
+ type: extractScalar(fm, "type"),
169
+ delegateMention: extractScalar(fm, "delegate_mention"),
170
+ });
171
+ }
172
+
173
+ // Recurse into subdirectories
174
+ walk(childPath);
175
+ }
176
+ }
177
+
178
+ walk(membersDir);
179
+
180
+ // Cross-validation: directory name uniqueness
181
+ for (const [name, paths] of nameOccurrences) {
182
+ if (paths.length > 1) {
183
+ allErrors.push(
184
+ `Duplicate member directory name '${name}' found at: ${paths.map((p) => `members/${p}`).join(", ")} — directory names must be unique across all levels under members/`,
185
+ );
186
+ }
187
+ }
188
+
189
+ // Cross-validation: delegate_mention references
190
+ const memberByName = new Map(members.map((m) => [m.name, m]));
191
+ for (const member of members) {
192
+ if (!member.delegateMention) continue;
193
+ const target = memberByName.get(member.delegateMention);
194
+ if (!target) {
195
+ allErrors.push(
196
+ `members/${member.relPath}/NODE.md: delegate_mention '${member.delegateMention}' references non-existent member — the target must be a directory under members/`,
197
+ );
198
+ } else if (target.type !== "personal_assistant") {
199
+ allErrors.push(
200
+ `members/${member.relPath}/NODE.md: delegate_mention '${member.delegateMention}' must reference a member with type 'personal_assistant', but '${target.name}' has type '${target.type}'`,
201
+ );
202
+ }
203
+ }
204
+
205
+ if (allErrors.length > 0) {
206
+ console.log(`Found ${allErrors.length} member validation error(s):\n`);
207
+ for (const err of allErrors) {
208
+ console.log(` \u2717 ${err}`);
209
+ }
210
+ return { exitCode: 1, errors: allErrors };
211
+ }
212
+
213
+ console.log(`All ${memberCount} member(s) passed validation.`);
214
+ return { exitCode: 0, errors: allErrors };
215
+ }