first-tree 0.0.4 → 0.0.5

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 (46) hide show
  1. package/README.md +17 -8
  2. package/dist/cli.js +4 -4
  3. package/dist/{help-Dtdj91HJ.js → help-5-WG9QFm.js} +1 -1
  4. package/dist/{init--VepFe6N.js → init-CAq0Uhq6.js} +19 -7
  5. package/dist/{installer-cH7N4RNj.js → installer-UgNasLjl.js} +19 -15
  6. package/dist/onboarding-3zYUeYQb.js +2 -0
  7. package/dist/onboarding-Dd63N-V1.js +10 -0
  8. package/dist/{repo-DY57bMqr.js → repo-DkR12VUv.js} +55 -4
  9. package/dist/{upgrade-Cgx_K2HM.js → upgrade-DYzuvv1k.js} +13 -8
  10. package/dist/{verify-mC9ZTd1f.js → verify-C0IUSkMZ.js} +1 -1
  11. package/package.json +12 -10
  12. package/skills/first-tree/SKILL.md +10 -6
  13. package/skills/first-tree/assets/framework/VERSION +1 -1
  14. package/skills/first-tree/assets/framework/examples/claude-code/README.md +2 -2
  15. package/skills/first-tree/assets/framework/examples/claude-code/settings.json +1 -1
  16. package/skills/first-tree/assets/framework/helpers/generate-codeowners.ts +1 -1
  17. package/skills/first-tree/assets/framework/helpers/inject-tree-context.sh +0 -0
  18. package/skills/first-tree/assets/framework/helpers/run-review.ts +1 -1
  19. package/skills/first-tree/assets/framework/templates/agents.md.template +2 -2
  20. package/skills/first-tree/assets/framework/templates/members-domain.md.template +1 -1
  21. package/skills/first-tree/assets/framework/templates/root-node.md.template +3 -3
  22. package/skills/first-tree/assets/framework/workflows/codeowners.yml +1 -1
  23. package/skills/first-tree/assets/framework/workflows/pr-review.yml +1 -1
  24. package/skills/first-tree/engine/init.ts +3 -2
  25. package/skills/first-tree/engine/repo.ts +36 -3
  26. package/skills/first-tree/engine/rules/agent-integration.ts +3 -1
  27. package/skills/first-tree/engine/rules/framework.ts +2 -2
  28. package/skills/first-tree/engine/runtime/adapters.ts +6 -2
  29. package/skills/first-tree/engine/runtime/asset-loader.ts +136 -4
  30. package/skills/first-tree/engine/runtime/installer.ts +18 -12
  31. package/skills/first-tree/engine/upgrade.ts +35 -8
  32. package/skills/first-tree/references/onboarding.md +28 -18
  33. package/skills/first-tree/references/upgrade-contract.md +49 -23
  34. package/skills/first-tree/scripts/check-skill-sync.sh +0 -0
  35. package/skills/first-tree/scripts/quick_validate.py +0 -0
  36. package/skills/first-tree/scripts/run-local-cli.sh +0 -0
  37. package/skills/first-tree/tests/asset-loader.test.ts +23 -1
  38. package/skills/first-tree/tests/helpers.ts +27 -3
  39. package/skills/first-tree/tests/init.test.ts +11 -3
  40. package/skills/first-tree/tests/repo.test.ts +26 -0
  41. package/skills/first-tree/tests/rules.test.ts +9 -7
  42. package/skills/first-tree/tests/skill-artifacts.test.ts +6 -0
  43. package/skills/first-tree/tests/upgrade.test.ts +20 -1
  44. package/skills/first-tree/tests/verify.test.ts +3 -3
  45. package/dist/onboarding-C9cYSE6F.js +0 -2
  46. package/dist/onboarding-CPP8fF4D.js +0 -10
@@ -45,7 +45,7 @@ jobs:
45
45
  gh pr view "$PR_NUMBER" --json headRefOid -q .headRefOid > /tmp/pr-head-sha.txt
46
46
 
47
47
  - name: Run Claude review
48
- run: npx tsx skills/first-tree/assets/framework/helpers/run-review.ts
48
+ run: npx tsx .agents/skills/first-tree/assets/framework/helpers/run-review.ts
49
49
 
50
50
  - name: Parse and post review
51
51
  run: |
@@ -23,6 +23,7 @@ import {
23
23
  FRAMEWORK_VERSION,
24
24
  INSTALLED_PROGRESS,
25
25
  LEGACY_AGENT_INSTRUCTIONS_FILE,
26
+ installedSkillRootsDisplay,
26
27
  } from "#skill/engine/runtime/asset-loader.js";
27
28
 
28
29
  /**
@@ -67,7 +68,7 @@ interface TaskListContext {
67
68
  function installSkill(source: string, target: string): void {
68
69
  copyCanonicalSkill(source, target);
69
70
  console.log(
70
- " Installed skills/first-tree/ from the bundled first-tree package",
71
+ ` Installed ${installedSkillRootsDisplay()} from the bundled first-tree package`,
71
72
  );
72
73
  }
73
74
 
@@ -198,7 +199,7 @@ export function runInit(repo?: Repo, options?: InitOptions): number {
198
199
  console.log();
199
200
  }
200
201
 
201
- if (!r.hasFramework()) {
202
+ if (!r.hasCurrentInstalledSkill()) {
202
203
  try {
203
204
  const sourceRoot = options?.sourceRoot ?? resolveBundledPackageRoot();
204
205
  console.log(
@@ -2,14 +2,19 @@ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
2
  import { basename, dirname, join, resolve } from "node:path";
3
3
  import {
4
4
  AGENT_INSTRUCTIONS_FILE,
5
+ CLAUDE_FRAMEWORK_VERSION,
6
+ CLAUDE_INSTALLED_PROGRESS,
5
7
  FRAMEWORK_VERSION,
6
- LEGACY_SKILL_PROGRESS,
7
- LEGACY_SKILL_VERSION,
8
+ INSTALLED_PROGRESS,
8
9
  LEGACY_AGENT_INSTRUCTIONS_FILE,
9
10
  LEGACY_PROGRESS,
11
+ LEGACY_REPO_SKILL_PROGRESS,
12
+ LEGACY_REPO_SKILL_VERSION,
13
+ LEGACY_SKILL_PROGRESS,
14
+ LEGACY_SKILL_VERSION,
10
15
  LEGACY_VERSION,
11
- INSTALLED_PROGRESS,
12
16
  agentInstructionsFileCandidates,
17
+ installedSkillRoots,
13
18
  type FrameworkLayout,
14
19
  detectFrameworkLayout,
15
20
  frameworkVersionCandidates,
@@ -164,6 +169,22 @@ export class Repo {
164
169
  return knownConfigs.some((c) => this.pathExists(c));
165
170
  }
166
171
 
172
+ installedSkillRoots(): string[] {
173
+ return installedSkillRoots();
174
+ }
175
+
176
+ missingInstalledSkillRoots(): string[] {
177
+ return this.installedSkillRoots().filter(
178
+ (root) =>
179
+ !this.pathExists(join(root, "SKILL.md")) ||
180
+ !this.pathExists(join(root, "assets", "framework", "VERSION")),
181
+ );
182
+ }
183
+
184
+ hasCurrentInstalledSkill(): boolean {
185
+ return this.missingInstalledSkillRoots().length === 0;
186
+ }
187
+
167
188
  isGitRepo(): boolean {
168
189
  return hasGitMetadata(this.root);
169
190
  }
@@ -198,6 +219,12 @@ export class Repo {
198
219
  if (layout === "legacy-skill") {
199
220
  return LEGACY_SKILL_PROGRESS;
200
221
  }
222
+ if (layout === "legacy-repo-skill") {
223
+ return LEGACY_REPO_SKILL_PROGRESS;
224
+ }
225
+ if (layout === "claude-skill") {
226
+ return CLAUDE_INSTALLED_PROGRESS;
227
+ }
201
228
  return INSTALLED_PROGRESS;
202
229
  }
203
230
 
@@ -209,6 +236,12 @@ export class Repo {
209
236
  if (layout === "legacy-skill") {
210
237
  return LEGACY_SKILL_VERSION;
211
238
  }
239
+ if (layout === "legacy-repo-skill") {
240
+ return LEGACY_REPO_SKILL_VERSION;
241
+ }
242
+ if (layout === "claude-skill") {
243
+ return CLAUDE_FRAMEWORK_VERSION;
244
+ }
212
245
  return FRAMEWORK_VERSION;
213
246
  }
214
247
 
@@ -1,13 +1,15 @@
1
1
  import type { Repo } from "#skill/engine/repo.js";
2
2
  import type { RuleResult } from "#skill/engine/rules/index.js";
3
+ import { claudeCodeExampleCandidates } from "#skill/engine/runtime/adapters.js";
3
4
  import { FRAMEWORK_EXAMPLES_DIR } from "#skill/engine/runtime/asset-loader.js";
4
5
 
5
6
  export function evaluate(repo: Repo): RuleResult {
6
7
  const tasks: string[] = [];
8
+ const [claudeExamplePath] = claudeCodeExampleCandidates();
7
9
  if (repo.pathExists(".claude/settings.json")) {
8
10
  if (!repo.fileContains(".claude/settings.json", "inject-tree-context")) {
9
11
  tasks.push(
10
- `Add SessionStart hook to \`.claude/settings.json\` (see \`${FRAMEWORK_EXAMPLES_DIR}/claude-code/\`)`,
12
+ `Add SessionStart hook to \`.claude/settings.json\` (see \`${claudeExamplePath}/\`)`,
11
13
  );
12
14
  }
13
15
  } else if (!repo.anyAgentConfig()) {
@@ -1,12 +1,12 @@
1
1
  import type { Repo } from "#skill/engine/repo.js";
2
2
  import type { RuleResult } from "#skill/engine/rules/index.js";
3
- import { SKILL_ROOT } from "#skill/engine/runtime/asset-loader.js";
3
+ import { installedSkillRootsDisplay } from "#skill/engine/runtime/asset-loader.js";
4
4
 
5
5
  export function evaluate(repo: Repo): RuleResult {
6
6
  const tasks: string[] = [];
7
7
  if (!repo.hasFramework()) {
8
8
  tasks.push(
9
- `\`${SKILL_ROOT}/\` not found — run \`context-tree init\` to install the framework skill bundled with the current \`first-tree\` package`,
9
+ `${installedSkillRootsDisplay()} not found — run \`context-tree init\` to install the framework skill bundled with the current \`first-tree\` package`,
10
10
  );
11
11
  }
12
12
  return { group: "Framework", order: 1, tasks };
@@ -1,7 +1,9 @@
1
1
  import { join } from "node:path";
2
2
  import {
3
+ CLAUDE_FRAMEWORK_EXAMPLES_DIR,
4
+ CLAUDE_FRAMEWORK_HELPERS_DIR,
3
5
  FRAMEWORK_EXAMPLES_DIR,
4
- FRAMEWORK_HELPERS_DIR,
6
+ LEGACY_REPO_SKILL_EXAMPLES_DIR,
5
7
  LEGACY_SKILL_EXAMPLES_DIR,
6
8
  LEGACY_EXAMPLES_DIR,
7
9
  } from "#skill/engine/runtime/asset-loader.js";
@@ -11,12 +13,14 @@ export const CODEX_CONFIG_PATH = ".codex/config.json";
11
13
 
12
14
  export function claudeCodeExampleCandidates(): string[] {
13
15
  return [
16
+ join(CLAUDE_FRAMEWORK_EXAMPLES_DIR, "claude-code"),
14
17
  join(FRAMEWORK_EXAMPLES_DIR, "claude-code"),
18
+ join(LEGACY_REPO_SKILL_EXAMPLES_DIR, "claude-code"),
15
19
  join(LEGACY_SKILL_EXAMPLES_DIR, "claude-code"),
16
20
  join(LEGACY_EXAMPLES_DIR, "claude-code"),
17
21
  ];
18
22
  }
19
23
 
20
24
  export function injectTreeContextHint(): string {
21
- return join(FRAMEWORK_HELPERS_DIR, "inject-tree-context.sh");
25
+ return join(CLAUDE_FRAMEWORK_HELPERS_DIR, "inject-tree-context.sh");
22
26
  }
@@ -2,7 +2,11 @@ import { existsSync, statSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
4
  export const SKILL_NAME = "first-tree";
5
- export const SKILL_ROOT = join("skills", SKILL_NAME);
5
+ export const BUNDLED_SKILL_ROOT = join("skills", SKILL_NAME);
6
+ export const SKILL_ROOT = join(".agents", "skills", SKILL_NAME);
7
+ export const CLAUDE_SKILL_ROOT = join(".claude", "skills", SKILL_NAME);
8
+ export const INSTALLED_SKILL_ROOTS = [SKILL_ROOT, CLAUDE_SKILL_ROOT] as const;
9
+
6
10
  export const SKILL_AGENTS_DIR = join(SKILL_ROOT, "agents");
7
11
  export const SKILL_REFERENCES_DIR = join(SKILL_ROOT, "references");
8
12
  export const FRAMEWORK_ASSET_ROOT = join(SKILL_ROOT, "assets", "framework");
@@ -18,6 +22,93 @@ export const AGENT_INSTRUCTIONS_FILE = "AGENTS.md";
18
22
  export const LEGACY_AGENT_INSTRUCTIONS_FILE = "AGENT.md";
19
23
  export const AGENT_INSTRUCTIONS_TEMPLATE = "agents.md.template";
20
24
 
25
+ export const CLAUDE_SKILL_AGENTS_DIR = join(CLAUDE_SKILL_ROOT, "agents");
26
+ export const CLAUDE_SKILL_REFERENCES_DIR = join(CLAUDE_SKILL_ROOT, "references");
27
+ export const CLAUDE_FRAMEWORK_ASSET_ROOT = join(
28
+ CLAUDE_SKILL_ROOT,
29
+ "assets",
30
+ "framework",
31
+ );
32
+ export const CLAUDE_FRAMEWORK_MANIFEST = join(
33
+ CLAUDE_FRAMEWORK_ASSET_ROOT,
34
+ "manifest.json",
35
+ );
36
+ export const CLAUDE_FRAMEWORK_VERSION = join(
37
+ CLAUDE_FRAMEWORK_ASSET_ROOT,
38
+ "VERSION",
39
+ );
40
+ export const CLAUDE_FRAMEWORK_TEMPLATES_DIR = join(
41
+ CLAUDE_FRAMEWORK_ASSET_ROOT,
42
+ "templates",
43
+ );
44
+ export const CLAUDE_FRAMEWORK_WORKFLOWS_DIR = join(
45
+ CLAUDE_FRAMEWORK_ASSET_ROOT,
46
+ "workflows",
47
+ );
48
+ export const CLAUDE_FRAMEWORK_PROMPTS_DIR = join(
49
+ CLAUDE_FRAMEWORK_ASSET_ROOT,
50
+ "prompts",
51
+ );
52
+ export const CLAUDE_FRAMEWORK_EXAMPLES_DIR = join(
53
+ CLAUDE_FRAMEWORK_ASSET_ROOT,
54
+ "examples",
55
+ );
56
+ export const CLAUDE_FRAMEWORK_HELPERS_DIR = join(
57
+ CLAUDE_FRAMEWORK_ASSET_ROOT,
58
+ "helpers",
59
+ );
60
+ export const CLAUDE_INSTALLED_PROGRESS = join(
61
+ CLAUDE_SKILL_ROOT,
62
+ "progress.md",
63
+ );
64
+
65
+ export const LEGACY_REPO_SKILL_ROOT = join("skills", SKILL_NAME);
66
+ export const LEGACY_REPO_SKILL_AGENTS_DIR = join(
67
+ LEGACY_REPO_SKILL_ROOT,
68
+ "agents",
69
+ );
70
+ export const LEGACY_REPO_SKILL_REFERENCES_DIR = join(
71
+ LEGACY_REPO_SKILL_ROOT,
72
+ "references",
73
+ );
74
+ export const LEGACY_REPO_SKILL_ASSET_ROOT = join(
75
+ LEGACY_REPO_SKILL_ROOT,
76
+ "assets",
77
+ "framework",
78
+ );
79
+ export const LEGACY_REPO_SKILL_MANIFEST = join(
80
+ LEGACY_REPO_SKILL_ASSET_ROOT,
81
+ "manifest.json",
82
+ );
83
+ export const LEGACY_REPO_SKILL_VERSION = join(
84
+ LEGACY_REPO_SKILL_ASSET_ROOT,
85
+ "VERSION",
86
+ );
87
+ export const LEGACY_REPO_SKILL_TEMPLATES_DIR = join(
88
+ LEGACY_REPO_SKILL_ASSET_ROOT,
89
+ "templates",
90
+ );
91
+ export const LEGACY_REPO_SKILL_WORKFLOWS_DIR = join(
92
+ LEGACY_REPO_SKILL_ASSET_ROOT,
93
+ "workflows",
94
+ );
95
+ export const LEGACY_REPO_SKILL_PROMPTS_DIR = join(
96
+ LEGACY_REPO_SKILL_ASSET_ROOT,
97
+ "prompts",
98
+ );
99
+ export const LEGACY_REPO_SKILL_EXAMPLES_DIR = join(
100
+ LEGACY_REPO_SKILL_ASSET_ROOT,
101
+ "examples",
102
+ );
103
+ export const LEGACY_REPO_SKILL_HELPERS_DIR = join(
104
+ LEGACY_REPO_SKILL_ASSET_ROOT,
105
+ "helpers",
106
+ );
107
+ export const LEGACY_REPO_SKILL_PROGRESS = join(
108
+ LEGACY_REPO_SKILL_ROOT,
109
+ "progress.md",
110
+ );
111
+
21
112
  export const LEGACY_SKILL_NAME = "first-tree-cli-framework";
22
113
  export const LEGACY_SKILL_ROOT = join("skills", LEGACY_SKILL_NAME);
23
114
  export const LEGACY_SKILL_ASSET_ROOT = join(
@@ -52,7 +143,12 @@ export const LEGACY_WORKFLOWS_DIR = join(LEGACY_FRAMEWORK_ROOT, "workflows");
52
143
  export const LEGACY_PROMPTS_DIR = join(LEGACY_FRAMEWORK_ROOT, "prompts");
53
144
  export const LEGACY_EXAMPLES_DIR = join(LEGACY_FRAMEWORK_ROOT, "examples");
54
145
 
55
- export type FrameworkLayout = "skill" | "legacy-skill" | "legacy";
146
+ export type FrameworkLayout =
147
+ | "skill"
148
+ | "claude-skill"
149
+ | "legacy-repo-skill"
150
+ | "legacy-skill"
151
+ | "legacy";
56
152
 
57
153
  function pathExists(root: string, relPath: string): boolean {
58
154
  const fullPath = join(root, relPath);
@@ -63,12 +159,34 @@ function pathExists(root: string, relPath: string): boolean {
63
159
  }
64
160
  }
65
161
 
162
+ export function installedSkillRoots(): string[] {
163
+ return [...INSTALLED_SKILL_ROOTS];
164
+ }
165
+
166
+ export function installedSkillRootsDisplay(): string {
167
+ return installedSkillRoots()
168
+ .map((root) => `\`${root}/\``)
169
+ .join(" and ");
170
+ }
171
+
66
172
  export function frameworkVersionCandidates(): string[] {
67
- return [FRAMEWORK_VERSION, LEGACY_SKILL_VERSION, LEGACY_VERSION];
173
+ return [
174
+ FRAMEWORK_VERSION,
175
+ CLAUDE_FRAMEWORK_VERSION,
176
+ LEGACY_REPO_SKILL_VERSION,
177
+ LEGACY_SKILL_VERSION,
178
+ LEGACY_VERSION,
179
+ ];
68
180
  }
69
181
 
70
182
  export function progressFileCandidates(): string[] {
71
- return [INSTALLED_PROGRESS, LEGACY_SKILL_PROGRESS, LEGACY_PROGRESS];
183
+ return [
184
+ INSTALLED_PROGRESS,
185
+ CLAUDE_INSTALLED_PROGRESS,
186
+ LEGACY_REPO_SKILL_PROGRESS,
187
+ LEGACY_SKILL_PROGRESS,
188
+ LEGACY_PROGRESS,
189
+ ];
72
190
  }
73
191
 
74
192
  export function agentInstructionsFileCandidates(): string[] {
@@ -78,6 +196,8 @@ export function agentInstructionsFileCandidates(): string[] {
78
196
  export function frameworkTemplateDirCandidates(): string[] {
79
197
  return [
80
198
  FRAMEWORK_TEMPLATES_DIR,
199
+ CLAUDE_FRAMEWORK_TEMPLATES_DIR,
200
+ LEGACY_REPO_SKILL_TEMPLATES_DIR,
81
201
  LEGACY_SKILL_TEMPLATES_DIR,
82
202
  LEGACY_TEMPLATES_DIR,
83
203
  ];
@@ -86,6 +206,8 @@ export function frameworkTemplateDirCandidates(): string[] {
86
206
  export function frameworkWorkflowDirCandidates(): string[] {
87
207
  return [
88
208
  FRAMEWORK_WORKFLOWS_DIR,
209
+ CLAUDE_FRAMEWORK_WORKFLOWS_DIR,
210
+ LEGACY_REPO_SKILL_WORKFLOWS_DIR,
89
211
  LEGACY_SKILL_WORKFLOWS_DIR,
90
212
  LEGACY_WORKFLOWS_DIR,
91
213
  ];
@@ -94,6 +216,8 @@ export function frameworkWorkflowDirCandidates(): string[] {
94
216
  export function frameworkPromptDirCandidates(): string[] {
95
217
  return [
96
218
  FRAMEWORK_PROMPTS_DIR,
219
+ CLAUDE_FRAMEWORK_PROMPTS_DIR,
220
+ LEGACY_REPO_SKILL_PROMPTS_DIR,
97
221
  LEGACY_SKILL_PROMPTS_DIR,
98
222
  LEGACY_PROMPTS_DIR,
99
223
  ];
@@ -102,6 +226,8 @@ export function frameworkPromptDirCandidates(): string[] {
102
226
  export function frameworkExampleDirCandidates(): string[] {
103
227
  return [
104
228
  FRAMEWORK_EXAMPLES_DIR,
229
+ CLAUDE_FRAMEWORK_EXAMPLES_DIR,
230
+ LEGACY_REPO_SKILL_EXAMPLES_DIR,
105
231
  LEGACY_SKILL_EXAMPLES_DIR,
106
232
  LEGACY_EXAMPLES_DIR,
107
233
  ];
@@ -123,6 +249,12 @@ export function detectFrameworkLayout(root: string): FrameworkLayout | null {
123
249
  if (pathExists(root, FRAMEWORK_VERSION)) {
124
250
  return "skill";
125
251
  }
252
+ if (pathExists(root, CLAUDE_FRAMEWORK_VERSION)) {
253
+ return "claude-skill";
254
+ }
255
+ if (pathExists(root, LEGACY_REPO_SKILL_VERSION)) {
256
+ return "legacy-repo-skill";
257
+ }
126
258
  if (pathExists(root, LEGACY_SKILL_VERSION)) {
127
259
  return "legacy-skill";
128
260
  }
@@ -2,9 +2,10 @@ import { copyFileSync, cpSync, existsSync, mkdirSync, rmSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import {
5
- FRAMEWORK_ASSET_ROOT,
5
+ BUNDLED_SKILL_ROOT,
6
+ INSTALLED_SKILL_ROOTS,
7
+ LEGACY_REPO_SKILL_ROOT,
6
8
  LEGACY_SKILL_ROOT,
7
- SKILL_ROOT,
8
9
  } from "#skill/engine/runtime/asset-loader.js";
9
10
 
10
11
  export function resolveBundledPackageRoot(startUrl = import.meta.url): string {
@@ -12,7 +13,7 @@ export function resolveBundledPackageRoot(startUrl = import.meta.url): string {
12
13
  while (true) {
13
14
  if (
14
15
  existsSync(join(dir, "package.json")) &&
15
- existsSync(join(dir, SKILL_ROOT, "SKILL.md"))
16
+ existsSync(join(dir, BUNDLED_SKILL_ROOT, "SKILL.md"))
16
17
  ) {
17
18
  return dir;
18
19
  }
@@ -38,7 +39,7 @@ export function resolveCanonicalSkillRoot(sourceRoot: string): string {
38
39
  return directSkillRoot;
39
40
  }
40
41
 
41
- const nestedSkillRoot = join(sourceRoot, SKILL_ROOT);
42
+ const nestedSkillRoot = join(sourceRoot, BUNDLED_SKILL_ROOT);
42
43
  if (
43
44
  existsSync(join(nestedSkillRoot, "SKILL.md")) &&
44
45
  existsSync(join(nestedSkillRoot, "assets", "framework", "VERSION"))
@@ -53,16 +54,21 @@ export function resolveCanonicalSkillRoot(sourceRoot: string): string {
53
54
 
54
55
  export function copyCanonicalSkill(sourceRoot: string, targetRoot: string): void {
55
56
  const src = resolveCanonicalSkillRoot(sourceRoot);
56
- const dst = join(targetRoot, SKILL_ROOT);
57
- const legacyDst = join(targetRoot, LEGACY_SKILL_ROOT);
58
- if (existsSync(dst)) {
59
- rmSync(dst, { recursive: true, force: true });
57
+ for (const relPath of [
58
+ ...INSTALLED_SKILL_ROOTS,
59
+ LEGACY_REPO_SKILL_ROOT,
60
+ LEGACY_SKILL_ROOT,
61
+ ]) {
62
+ const fullPath = join(targetRoot, relPath);
63
+ if (existsSync(fullPath)) {
64
+ rmSync(fullPath, { recursive: true, force: true });
65
+ }
60
66
  }
61
- if (legacyDst !== dst && existsSync(legacyDst)) {
62
- rmSync(legacyDst, { recursive: true, force: true });
67
+ for (const relPath of INSTALLED_SKILL_ROOTS) {
68
+ const dst = join(targetRoot, relPath);
69
+ mkdirSync(dirname(dst), { recursive: true });
70
+ cpSync(src, dst, { recursive: true });
63
71
  }
64
- mkdirSync(dirname(dst), { recursive: true });
65
- cpSync(src, dst, { recursive: true });
66
72
  }
67
73
 
68
74
  export function renderTemplateFile(
@@ -4,14 +4,17 @@ import { Repo } from "#skill/engine/repo.js";
4
4
  import {
5
5
  AGENT_INSTRUCTIONS_FILE,
6
6
  AGENT_INSTRUCTIONS_TEMPLATE,
7
+ CLAUDE_SKILL_ROOT,
7
8
  FRAMEWORK_WORKFLOWS_DIR,
8
9
  FRAMEWORK_TEMPLATES_DIR,
9
10
  FRAMEWORK_VERSION,
10
11
  INSTALLED_PROGRESS,
11
12
  LEGACY_AGENT_INSTRUCTIONS_FILE,
12
13
  LEGACY_FRAMEWORK_ROOT,
14
+ LEGACY_REPO_SKILL_ROOT,
13
15
  LEGACY_SKILL_ROOT,
14
16
  SKILL_ROOT,
17
+ installedSkillRootsDisplay,
15
18
  type FrameworkLayout,
16
19
  } from "#skill/engine/runtime/asset-loader.js";
17
20
  import {
@@ -45,9 +48,10 @@ function formatUpgradeTaskList(
45
48
  const lines: string[] = [
46
49
  `# Context Tree Upgrade — v${localVersion} -> v${packagedVersion}\n`,
47
50
  "## Installed Skill",
48
- `- [ ] Review local customizations under \`${SKILL_ROOT}/\` and reapply them if needed`,
51
+ `- [ ] Review local customizations under ${installedSkillRootsDisplay()} and reapply them if needed`,
49
52
  `- [ ] Re-copy any workflow updates you want from \`${FRAMEWORK_WORKFLOWS_DIR}/\` into \`.github/workflows/\``,
50
- `- [ ] Re-check any local agent setup that references \`${SKILL_ROOT}/assets/framework/examples/\` or \`${SKILL_ROOT}/assets/framework/helpers/\``,
53
+ `- [ ] Re-check any local agent setup that references \`${CLAUDE_SKILL_ROOT}/assets/framework/examples/\` or \`${CLAUDE_SKILL_ROOT}/assets/framework/helpers/\``,
54
+ `- [ ] Re-check any repo scripts or workflow files that reference \`${SKILL_ROOT}/assets/framework/\``,
51
55
  "",
52
56
  ];
53
57
 
@@ -58,6 +62,14 @@ function formatUpgradeTaskList(
58
62
  );
59
63
  }
60
64
 
65
+ if (layout === "legacy-repo-skill") {
66
+ lines.push(
67
+ "## Migration",
68
+ `- [ ] Remove any stale \`${LEGACY_REPO_SKILL_ROOT}/\` references from repo-specific docs, scripts, workflow files, or agent config`,
69
+ "",
70
+ );
71
+ }
72
+
61
73
  if (layout === "legacy-skill") {
62
74
  migrationTasks.push(
63
75
  `- [ ] Remove any stale \`${LEGACY_SKILL_ROOT}/\` references from repo-specific docs, scripts, workflow files, or agent config`,
@@ -165,7 +177,12 @@ export function runUpgrade(repo?: Repo, options?: UpgradeOptions): number {
165
177
  return 1;
166
178
  }
167
179
 
168
- if (layout === "skill" && packagedVersion === localVersion) {
180
+ const missingInstalledRoots = workingRepo.missingInstalledSkillRoots();
181
+ if (
182
+ layout === "skill" &&
183
+ missingInstalledRoots.length === 0 &&
184
+ packagedVersion === localVersion
185
+ ) {
169
186
  console.log(
170
187
  `Already up to date with the bundled skill (${FRAMEWORK_VERSION} = ${localVersion}).`,
171
188
  );
@@ -179,16 +196,26 @@ export function runUpgrade(repo?: Repo, options?: UpgradeOptions): number {
179
196
  force: true,
180
197
  });
181
198
  console.log(
182
- "Migrated legacy .context-tree/ layout to skills/first-tree/.",
199
+ `Migrated legacy .context-tree/ layout to ${installedSkillRootsDisplay()}.`,
183
200
  );
184
- } else if (layout === "legacy-skill") {
201
+ } else if (layout === "legacy-repo-skill") {
185
202
  console.log(
186
- "Migrated skills/first-tree-cli-framework/ to skills/first-tree/.",
203
+ `Migrated legacy ${LEGACY_REPO_SKILL_ROOT}/ layout to ${installedSkillRootsDisplay()}.`,
187
204
  );
188
- } else {
205
+ } else if (layout === "legacy-skill") {
189
206
  console.log(
190
- "Refreshed skills/first-tree/ from the bundled first-tree package.",
207
+ `Migrated ${LEGACY_SKILL_ROOT}/ to ${installedSkillRootsDisplay()}.`,
191
208
  );
209
+ } else {
210
+ if (missingInstalledRoots.length > 0) {
211
+ console.log(
212
+ `Repaired missing installed skill roots (${missingInstalledRoots.map((root) => `${root}/`).join(", ")}) and refreshed ${installedSkillRootsDisplay()} from the bundled first-tree package.`,
213
+ );
214
+ } else {
215
+ console.log(
216
+ `Refreshed ${installedSkillRootsDisplay()} from the bundled first-tree package.`,
217
+ );
218
+ }
192
219
  }
193
220
 
194
221
  const output = formatUpgradeTaskList(
@@ -56,8 +56,9 @@ Information an agent needs to **decide** on an approach — not to execute it.
56
56
  - A source/workspace Git repository, or an already-created dedicated tree repo
57
57
  - Node.js 18+
58
58
  - The npm package is `first-tree`, the installed CLI command is
59
- `context-tree`, and the installed skill directory in the tree is
60
- `skills/first-tree/`
59
+ `context-tree`.
60
+ - `context-tree init` installs the framework skill into
61
+ `.agents/skills/first-tree/` and `.claude/skills/first-tree/`.
61
62
  - Use `npx first-tree init` for one-off runs, or `npm install -g first-tree`
62
63
  to add the `context-tree` command to your PATH
63
64
 
@@ -81,24 +82,30 @@ git init
81
82
  context-tree init --here
82
83
  ```
83
84
 
84
- Either way, the framework installs into `skills/first-tree/`, renders
85
- scaffolding (`NODE.md`, `AGENTS.md`, `members/NODE.md`), and generates a task
86
- list in `skills/first-tree/progress.md`.
85
+ Either way, the framework installs into `.agents/skills/first-tree/` and
86
+ `.claude/skills/first-tree/`, renders scaffolding (`NODE.md`, `AGENTS.md`,
87
+ `members/NODE.md`), and generates a task list in
88
+ `.agents/skills/first-tree/progress.md`.
87
89
 
88
90
  Publishing tip: keep the tree repo in the same GitHub organization as the
89
91
  source repo unless you have a reason not to.
90
92
 
91
93
  ### Step 2: Work Through the Task List
92
94
 
93
- Read `skills/first-tree/progress.md`. It contains a checklist tailored to the current state of the repo. Complete each task:
95
+ Read `.agents/skills/first-tree/progress.md`. It contains a checklist tailored
96
+ to the current state of the repo. Complete each task:
94
97
 
95
98
  - Fill in `NODE.md` with your organization name, owners, and domains
96
99
  - Add project-specific instructions to `AGENTS.md` below the framework markers
97
100
  - Create member nodes under `members/`
98
- - Optionally configure agent integration (e.g., Claude Code session hooks)
99
- - Copy validation workflows from `skills/first-tree/assets/framework/workflows/` to `.github/workflows/`
101
+ - Optionally configure agent integration (for Claude Code, the installed hook
102
+ assets live under `.claude/skills/first-tree/`)
103
+ - Copy validation workflows from
104
+ `.agents/skills/first-tree/assets/framework/workflows/` to
105
+ `.github/workflows/`
100
106
 
101
- As you complete each task, check it off in `skills/first-tree/progress.md` by changing `- [ ]` to `- [x]`.
107
+ As you complete each task, check it off in
108
+ `.agents/skills/first-tree/progress.md` by changing `- [ ]` to `- [x]`.
102
109
 
103
110
  ### Step 3: Verify
104
111
 
@@ -112,7 +119,9 @@ Or, from your source/workspace repo:
112
119
  context-tree verify --tree-path ../my-org-context
113
120
  ```
114
121
 
115
- This fails if any items in `skills/first-tree/progress.md` remain unchecked, and runs deterministic checks (valid frontmatter, node structure, member nodes exist).
122
+ This fails if any items in `.agents/skills/first-tree/progress.md` remain
123
+ unchecked, and runs deterministic checks (valid frontmatter, node structure,
124
+ member nodes exist).
116
125
 
117
126
  ### Step 4: Design Your Domains
118
127
 
@@ -164,13 +173,14 @@ When the framework updates:
164
173
  context-tree upgrade
165
174
  ```
166
175
 
167
- `context-tree upgrade` refreshes `skills/first-tree/` from the
168
- skill bundled with the currently running `first-tree` npm package, preserves your
169
- tree content, and generates follow-up tasks in
170
- `skills/first-tree/progress.md`.
176
+ `context-tree upgrade` refreshes `.agents/skills/first-tree/` and
177
+ `.claude/skills/first-tree/` from the skill bundled with the currently running
178
+ `first-tree` npm package, preserves your tree content, and generates follow-up
179
+ tasks in `.agents/skills/first-tree/progress.md`.
171
180
 
172
- If your repo still uses the older `skills/first-tree-cli-framework/` path,
173
- `context-tree upgrade` will migrate it to `skills/first-tree/` first.
181
+ If your repo still uses the older `skills/first-tree/`,
182
+ `skills/first-tree-cli-framework/`, or `.context-tree/` layouts,
183
+ `context-tree upgrade` will migrate it to the current installed layout first.
174
184
 
175
185
  To pick up a newer framework release, first run a newer package version, for
176
186
  example `npx first-tree@latest upgrade`, or update your global `first-tree`
@@ -180,6 +190,6 @@ install before running `context-tree upgrade`.
180
190
 
181
191
  ## Further Reading
182
192
 
183
- - `skills/first-tree/references/principles.md` — Core principles with detailed examples
184
- - `skills/first-tree/references/ownership-and-naming.md` — How nodes are named and owned
193
+ - `.agents/skills/first-tree/references/principles.md` — Core principles with detailed examples
194
+ - `.agents/skills/first-tree/references/ownership-and-naming.md` — How nodes are named and owned
185
195
  - `AGENTS.md` in your tree — The before/during/after workflow for every task