first-tree 0.0.4 → 0.0.6

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 (54) hide show
  1. package/README.md +36 -13
  2. package/dist/cli.js +5 -5
  3. package/dist/{help-Dtdj91HJ.js → help-DV9-AaFp.js} +1 -1
  4. package/dist/{init--VepFe6N.js → init-BgGH2_yC.js} +57 -11
  5. package/dist/onboarding-D7fGGOMN.js +10 -0
  6. package/dist/onboarding-lASHHmgO.js +2 -0
  7. package/dist/{repo-DY57bMqr.js → repo-Cc5U4DWT.js} +76 -5
  8. package/dist/source-integration-CuKjoheT.js +84 -0
  9. package/dist/{upgrade-Cgx_K2HM.js → upgrade-BvA9oKmi.js} +37 -9
  10. package/dist/{verify-mC9ZTd1f.js → verify-G8gNXzDX.js} +5 -1
  11. package/package.json +11 -9
  12. package/skills/first-tree/SKILL.md +30 -10
  13. package/skills/first-tree/agents/openai.yaml +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 +78 -6
  25. package/skills/first-tree/engine/repo.ts +74 -10
  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 +142 -4
  30. package/skills/first-tree/engine/runtime/installer.ts +18 -12
  31. package/skills/first-tree/engine/runtime/source-integration.ts +80 -0
  32. package/skills/first-tree/engine/upgrade.ts +103 -9
  33. package/skills/first-tree/engine/verify.ts +7 -0
  34. package/skills/first-tree/references/maintainer-architecture.md +4 -0
  35. package/skills/first-tree/references/maintainer-thin-cli.md +3 -0
  36. package/skills/first-tree/references/onboarding.md +56 -21
  37. package/skills/first-tree/references/principles.md +97 -57
  38. package/skills/first-tree/references/source-map.md +1 -0
  39. package/skills/first-tree/references/source-workspace-installation.md +52 -0
  40. package/skills/first-tree/references/upgrade-contract.md +67 -26
  41. package/skills/first-tree/scripts/check-skill-sync.sh +2 -0
  42. package/skills/first-tree/scripts/quick_validate.py +0 -0
  43. package/skills/first-tree/scripts/run-local-cli.sh +0 -0
  44. package/skills/first-tree/tests/asset-loader.test.ts +23 -1
  45. package/skills/first-tree/tests/helpers.ts +27 -3
  46. package/skills/first-tree/tests/init.test.ts +72 -3
  47. package/skills/first-tree/tests/repo.test.ts +46 -0
  48. package/skills/first-tree/tests/rules.test.ts +9 -7
  49. package/skills/first-tree/tests/skill-artifacts.test.ts +45 -0
  50. package/skills/first-tree/tests/upgrade.test.ts +58 -3
  51. package/skills/first-tree/tests/verify.test.ts +21 -3
  52. package/dist/installer-cH7N4RNj.js +0 -47
  53. package/dist/onboarding-C9cYSE6F.js +0 -2
  54. package/dist/onboarding-CPP8fF4D.js +0 -10
@@ -1,5 +1,5 @@
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";
1
+ import { C as LEGACY_SKILL_ROOT, D as SOURCE_INTEGRATION_MARKER, O as installedSkillRootsDisplay, T as SKILL_ROOT, b as LEGACY_REPO_SKILL_ROOT, c as CLAUDE_SKILL_ROOT, d as FRAMEWORK_TEMPLATES_DIR, f as FRAMEWORK_VERSION, g as LEGACY_AGENT_INSTRUCTIONS_FILE, i as AGENT_INSTRUCTIONS_TEMPLATE, m as INSTALLED_PROGRESS, n as Repo, p as FRAMEWORK_WORKFLOWS_DIR, r as AGENT_INSTRUCTIONS_FILE, s as CLAUDE_INSTRUCTIONS_FILE, v as LEGACY_FRAMEWORK_ROOT } from "./repo-Cc5U4DWT.js";
2
+ import { a as resolveCanonicalSkillRoot, i as resolveBundledPackageRoot, n as copyCanonicalSkill, t as upsertSourceIntegrationFiles } from "./source-integration-CuKjoheT.js";
3
3
  import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join, resolve } from "node:path";
5
5
  //#region skills/first-tree/engine/runtime/upgrader.ts
@@ -37,13 +37,15 @@ function formatUpgradeTaskList(repo, localVersion, packagedVersion, layout) {
37
37
  const lines = [
38
38
  `# Context Tree Upgrade — v${localVersion} -> v${packagedVersion}\n`,
39
39
  "## Installed Skill",
40
- `- [ ] Review local customizations under \`${SKILL_ROOT}/\` and reapply them if needed`,
40
+ `- [ ] Review local customizations under ${installedSkillRootsDisplay()} and reapply them if needed`,
41
41
  `- [ ] Re-copy any workflow updates you want from \`${FRAMEWORK_WORKFLOWS_DIR}/\` into \`.github/workflows/\``,
42
- `- [ ] Re-check any local agent setup that references \`${SKILL_ROOT}/assets/framework/examples/\` or \`${SKILL_ROOT}/assets/framework/helpers/\``,
42
+ `- [ ] Re-check any local agent setup that references \`${CLAUDE_SKILL_ROOT}/assets/framework/examples/\` or \`${CLAUDE_SKILL_ROOT}/assets/framework/helpers/\``,
43
+ `- [ ] Re-check any repo scripts or workflow files that reference \`${SKILL_ROOT}/assets/framework/\``,
43
44
  ""
44
45
  ];
45
46
  const migrationTasks = [];
46
47
  if (layout === "legacy") migrationTasks.push("- [ ] Remove any stale `.context-tree/` references from repo-specific docs, scripts, or workflow files");
48
+ if (layout === "legacy-repo-skill") lines.push("## Migration", `- [ ] Remove any stale \`${LEGACY_REPO_SKILL_ROOT}/\` references from repo-specific docs, scripts, workflow files, or agent config`, "");
47
49
  if (layout === "legacy-skill") migrationTasks.push(`- [ ] Remove any stale \`${LEGACY_SKILL_ROOT}/\` references from repo-specific docs, scripts, workflow files, or agent config`);
48
50
  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
51
  else if (repo.hasLegacyAgentInstructionsFile()) migrationTasks.push(`- [ ] Rename \`${LEGACY_AGENT_INSTRUCTIONS_FILE}\` to \`${AGENT_INSTRUCTIONS_FILE}\` to use the canonical agent instructions filename`);
@@ -54,7 +56,8 @@ function formatUpgradeTaskList(repo, localVersion, packagedVersion, layout) {
54
56
  }
55
57
  function runUpgrade(repo, options) {
56
58
  const workingRepo = repo ?? new Repo();
57
- if (workingRepo.isLikelySourceRepo() && !workingRepo.looksLikeTreeRepo()) {
59
+ const workspaceOnlyIntegration = workingRepo.hasSourceWorkspaceIntegration() && !workingRepo.looksLikeTreeRepo();
60
+ if (workingRepo.isLikelySourceRepo() && !workingRepo.looksLikeTreeRepo() && !workspaceOnlyIntegration) {
58
61
  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
62
  return 1;
60
63
  }
@@ -87,7 +90,30 @@ function runUpgrade(repo, options) {
87
90
  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`.");
88
91
  return 1;
89
92
  }
90
- if (layout === "skill" && packagedVersion === localVersion) {
93
+ const missingInstalledRoots = workingRepo.missingInstalledSkillRoots();
94
+ const sourceRepoTreePathHint = `../${workingRepo.repoName()}-context`;
95
+ if (workspaceOnlyIntegration) {
96
+ if (layout === "skill" && missingInstalledRoots.length === 0 && packagedVersion === localVersion) {
97
+ const changedFiles = upsertSourceIntegrationFiles(workingRepo.root, `${workingRepo.repoName()}-context`).filter((update) => update.action !== "unchanged").map((update) => update.file);
98
+ if (changedFiles.length === 0) {
99
+ console.log(`Already up to date with the bundled skill (${FRAMEWORK_VERSION} = ${localVersion}).`);
100
+ console.log(`This repo only carries source/workspace integration. Upgrade the dedicated tree repo separately with \`context-tree upgrade --tree-path ${sourceRepoTreePathHint}\`.`);
101
+ return 0;
102
+ }
103
+ console.log(`Already up to date with the bundled skill (${FRAMEWORK_VERSION} = ${localVersion}).`);
104
+ console.log(`Updated the ${SOURCE_INTEGRATION_MARKER} marker lines in ${changedFiles.map((file) => `\`${file}\``).join(" and ")}.`);
105
+ console.log(`This repo only carries source/workspace integration. Upgrade the dedicated tree repo separately with \`context-tree upgrade --tree-path ${sourceRepoTreePathHint}\`.`);
106
+ return 0;
107
+ }
108
+ copyCanonicalSkill(sourceRoot, workingRepo.root);
109
+ const changedFiles = upsertSourceIntegrationFiles(workingRepo.root, `${workingRepo.repoName()}-context`).filter((update) => update.action !== "unchanged").map((update) => update.file);
110
+ console.log(`Refreshed ${installedSkillRootsDisplay()} in this source/workspace repo.`);
111
+ if (changedFiles.length > 0) console.log(`Updated the ${SOURCE_INTEGRATION_MARKER} marker lines in ${changedFiles.map((file) => `\`${file}\``).join(" and ")}.`);
112
+ else console.log(`The ${SOURCE_INTEGRATION_MARKER} marker lines in ${AGENT_INSTRUCTIONS_FILE} and ${CLAUDE_INSTRUCTIONS_FILE} were already current.`);
113
+ console.log(`This repo is not the Context Tree. Upgrade the dedicated tree repo separately with \`context-tree upgrade --tree-path ${sourceRepoTreePathHint}\`.`);
114
+ return 0;
115
+ }
116
+ if (layout === "skill" && missingInstalledRoots.length === 0 && packagedVersion === localVersion) {
91
117
  console.log(`Already up to date with the bundled skill (${FRAMEWORK_VERSION} = ${localVersion}).`);
92
118
  return 0;
93
119
  }
@@ -97,9 +123,11 @@ function runUpgrade(repo, options) {
97
123
  recursive: true,
98
124
  force: true
99
125
  });
100
- console.log("Migrated legacy .context-tree/ layout to skills/first-tree/.");
101
- } else if (layout === "legacy-skill") console.log("Migrated skills/first-tree-cli-framework/ to skills/first-tree/.");
102
- else console.log("Refreshed skills/first-tree/ from the bundled first-tree package.");
126
+ console.log(`Migrated legacy .context-tree/ layout to ${installedSkillRootsDisplay()}.`);
127
+ } else if (layout === "legacy-repo-skill") console.log(`Migrated legacy ${LEGACY_REPO_SKILL_ROOT}/ layout to ${installedSkillRootsDisplay()}.`);
128
+ else if (layout === "legacy-skill") console.log(`Migrated ${LEGACY_SKILL_ROOT}/ to ${installedSkillRootsDisplay()}.`);
129
+ else if (missingInstalledRoots.length > 0) console.log(`Repaired missing installed skill roots (${missingInstalledRoots.map((root) => `${root}/`).join(", ")}) and refreshed ${installedSkillRootsDisplay()} from the bundled first-tree package.`);
130
+ else console.log(`Refreshed ${installedSkillRootsDisplay()} from the bundled first-tree package.`);
103
131
  const output = formatUpgradeTaskList(workingRepo, localVersion, packagedVersion, layout);
104
132
  console.log(`\n${output}`);
105
133
  writeProgress(workingRepo, output);
@@ -1,4 +1,4 @@
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";
1
+ import { C as LEGACY_SKILL_ROOT, T as SKILL_ROOT, g as LEGACY_AGENT_INSTRUCTIONS_FILE, n as Repo, r as AGENT_INSTRUCTIONS_FILE } from "./repo-Cc5U4DWT.js";
2
2
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
3
  import { join, relative, resolve } from "node:path";
4
4
  //#region skills/first-tree/engine/validators/members.ts
@@ -515,6 +515,10 @@ function defaultNodeValidator(root) {
515
515
  function runVerify(repo, nodeValidator) {
516
516
  const r = repo ?? new Repo();
517
517
  const validate = nodeValidator ?? defaultNodeValidator;
518
+ if (r.hasSourceWorkspaceIntegration() && !r.looksLikeTreeRepo()) {
519
+ console.error(`Error: this repo only has the first-tree source/workspace integration installed. Verify the dedicated tree repo instead, for example \`context-tree verify --tree-path ../${r.repoName()}-context\`.`);
520
+ return 1;
521
+ }
518
522
  if (r.isLikelySourceRepo() && !r.looksLikeTreeRepo()) {
519
523
  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
524
  return 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "first-tree",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
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": {
@@ -30,6 +30,15 @@
30
30
  "dist",
31
31
  "skills/first-tree"
32
32
  ],
33
+ "scripts": {
34
+ "build": "tsdown",
35
+ "prepack": "pnpm build",
36
+ "test": "vitest run",
37
+ "eval": "vitest run --config vitest.eval.config.ts",
38
+ "typecheck": "tsc --noEmit",
39
+ "validate:skill": "python3 ./skills/first-tree/scripts/quick_validate.py ./skills/first-tree && bash ./skills/first-tree/scripts/check-skill-sync.sh"
40
+ },
41
+ "packageManager": "pnpm@10.25.0",
33
42
  "license": "Apache-2.0",
34
43
  "devDependencies": {
35
44
  "@types/node": "^25.5.0",
@@ -37,12 +46,5 @@
37
46
  "typescript": "^5.8.0",
38
47
  "vitest": "^3.2.0",
39
48
  "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"
47
49
  }
48
- }
50
+ }
@@ -6,8 +6,9 @@ description: Maintain the canonical `first-tree` skill and the thin `context-tre
6
6
  # First Tree
7
7
 
8
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.
9
+ `context-tree` CLI or the installed `.agents/skills/first-tree/` and
10
+ `.claude/skills/first-tree/` payloads that `context-tree init` ships to user
11
+ repos.
11
12
 
12
13
  ## Source Of Truth
13
14
 
@@ -17,9 +18,9 @@ Use this skill when the task depends on the exact behavior of the
17
18
  repos.
18
19
  - `engine/` holds the canonical framework and CLI behavior.
19
20
  - `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.
21
+ - In maintainer docs, use `context-tree` for the CLI, `skills/first-tree/` for
22
+ the bundled source path, and `.agents/skills/first-tree/` /
23
+ `.claude/skills/first-tree/` for installed user-repo paths.
23
24
 
24
25
  ## When To Read What
25
26
 
@@ -27,6 +28,7 @@ Use this skill when the task depends on the exact behavior of the
27
28
  2. Read the user-facing reference that matches the task:
28
29
  - `references/onboarding.md`
29
30
  - `references/about.md`
31
+ - `references/source-workspace-installation.md`
30
32
  - `references/principles.md`
31
33
  - `references/ownership-and-naming.md`
32
34
  - `references/upgrade-contract.md`
@@ -57,15 +59,29 @@ Use this skill when the task depends on the exact behavior of the
57
59
 
58
60
  ### Working In A User Tree Repo
59
61
 
62
+ - When a user asks to install first-tree for an existing source/workspace repo,
63
+ the current repo keeps only the installed skill plus a
64
+ `FIRST-TREE-SOURCE-INTEGRATION:` line in `AGENTS.md` and `CLAUDE.md`. Do not
65
+ create `NODE.md`, `members/`, or tree-scoped `AGENTS.md` there.
60
66
  - `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.
67
+ repo when invoked from a source/workspace repo. It installs the bundled skill
68
+ into the source/workspace repo and scaffolds tree files only in the
69
+ dedicated tree repo. Use `--here` to initialize the current repo in place
70
+ when you are already inside the tree repo.
63
71
  - `context-tree init` installs this skill into the target tree repo and
72
+ scaffolds `.agents/skills/first-tree/`, `.claude/skills/first-tree/`,
64
73
  `NODE.md`, `AGENTS.md`, and `members/NODE.md`.
74
+ - The default source/workspace workflow is: create or reuse `<repo>-context`,
75
+ prefer pushing it in the same GitHub organization as the source repo, add it
76
+ back to the source/workspace repo as a git submodule, and open a PR to the
77
+ source/workspace repo's default branch instead of merging automatically.
65
78
  - `context-tree upgrade` refreshes the installed skill from the copy bundled
66
- with the currently running `first-tree` package. To pick up a newer
67
- framework, run a newer package version first. It also migrates older repos
68
- that still use `skills/first-tree-cli-framework/`.
79
+ with the currently running `first-tree` package. In a source/workspace repo
80
+ it refreshes only the local skill plus the
81
+ `FIRST-TREE-SOURCE-INTEGRATION:` line; upgrade the dedicated tree repo
82
+ separately with `--tree-path`. To pick up a newer framework, run a newer
83
+ package version first. It also migrates older repos that still use
84
+ `skills/first-tree/` or `skills/first-tree-cli-framework/`.
69
85
  - The user's tree content lives outside the skill; the skill only carries the
70
86
  reusable framework payload plus maintenance guidance.
71
87
  - The tree still stores decisions, constraints, and ownership; execution detail
@@ -79,6 +95,8 @@ Use this skill when the task depends on the exact behavior of the
79
95
  - Keep decision knowledge in the tree and execution detail in source systems.
80
96
  - Keep the skill as the only canonical knowledge source. The root CLI/package
81
97
  shell must not become a second source of framework semantics.
98
+ - Keep the CLI name written as `context-tree` in maintainer and user-facing
99
+ docs so it is not confused with the `first-tree` package that ships it.
82
100
  - Keep normal `init` / `upgrade` flows self-contained. They must work from the
83
101
  skill bundled in the current package without cloning the source repo or
84
102
  relying on network access.
@@ -104,6 +122,8 @@ Use this skill when the task depends on the exact behavior of the
104
122
  - `engine/`: canonical framework and CLI behavior
105
123
  - `tests/`: canonical unit and structure validation
106
124
  - `references/source-map.md`: canonical reading index
125
+ - `references/source-workspace-installation.md`: source/workspace install
126
+ contract
107
127
  - `references/maintainer-architecture.md`: source-repo architecture and
108
128
  invariants
109
129
  - `references/maintainer-thin-cli.md`: root shell contract
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "First Tree"
3
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."
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. When a source/workspace repo installs first-tree, keep that repo limited to local skill integration plus the FIRST-TREE-SOURCE-INTEGRATION marker lines, and keep all NODE.md/tree content only in a dedicated sibling *-context repo."
@@ -6,9 +6,9 @@ Copy `settings.json` to your tree repo's `.claude/` directory:
6
6
 
7
7
  ```bash
8
8
  mkdir -p .claude
9
- cp skills/first-tree/assets/framework/examples/claude-code/settings.json .claude/settings.json
9
+ cp .claude/skills/first-tree/assets/framework/examples/claude-code/settings.json .claude/settings.json
10
10
  ```
11
11
 
12
12
  ## What It Does
13
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.
14
+ The `SessionStart` hook runs `./.claude/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.
@@ -5,7 +5,7 @@
5
5
  "hooks": [
6
6
  {
7
7
  "type": "command",
8
- "command": "./skills/first-tree/assets/framework/helpers/inject-tree-context.sh"
8
+ "command": "./.claude/skills/first-tree/assets/framework/helpers/inject-tree-context.sh"
9
9
  }
10
10
  ]
11
11
  }
@@ -205,7 +205,7 @@ export function generate(
205
205
  return 0;
206
206
  }
207
207
  console.log(
208
- "CODEOWNERS is out-of-date. Run: npx tsx skills/first-tree/assets/framework/helpers/generate-codeowners.ts",
208
+ "CODEOWNERS is out-of-date. Run: npx tsx .agents/skills/first-tree/assets/framework/helpers/generate-codeowners.ts",
209
209
  );
210
210
  return 1;
211
211
  }
@@ -39,7 +39,7 @@ function buildPrompt(diffPath: string): string {
39
39
  ["Root NODE.md", "NODE.md"],
40
40
  [
41
41
  "Review Instructions",
42
- "skills/first-tree/assets/framework/prompts/pr-review.md",
42
+ ".agents/skills/first-tree/assets/framework/prompts/pr-review.md",
43
43
  ],
44
44
  ];
45
45
  for (const [heading, path] of files) {
@@ -13,7 +13,7 @@ You are working in a **Context Tree** — the living source of truth for decisio
13
13
 
14
14
  4. **Git-native tree structure.** Each node is a file; each domain is a directory. Soft links allow cross-references without the complexity of a full graph. History, ownership, and review follow Git conventions.
15
15
 
16
- See `skills/first-tree/references/principles.md` for detailed explanations and examples.
16
+ See `.agents/skills/first-tree/references/principles.md` for detailed explanations and examples.
17
17
 
18
18
  ## Before Every Task
19
19
 
@@ -41,7 +41,7 @@ Ask yourself: **Does the tree need updating?**
41
41
 
42
42
  ## Reference
43
43
 
44
- For ownership rules, tree structure, and key files, see [NODE.md](NODE.md) and `skills/first-tree/references/ownership-and-naming.md`.
44
+ For ownership rules, tree structure, and key files, see [NODE.md](NODE.md) and `.agents/skills/first-tree/references/ownership-and-naming.md`.
45
45
  <!-- END CONTEXT-TREE FRAMEWORK -->
46
46
 
47
47
  # Project-Specific Instructions
@@ -34,7 +34,7 @@ domains:
34
34
  ---
35
35
  ```
36
36
 
37
- See `skills/first-tree/assets/framework/templates/member-node.md.template` for a full scaffold.
37
+ See `.agents/skills/first-tree/assets/framework/templates/member-node.md.template` for a full scaffold.
38
38
 
39
39
  ---
40
40
 
@@ -34,8 +34,8 @@ tree describes.
34
34
 
35
35
  See [AGENTS.md](AGENTS.md) for agent instructions — the before/during/after workflow, ownership model, and tree maintenance.
36
36
 
37
- See [about.md](skills/first-tree/references/about.md) for background — the problem, the idea, and who it's for.
37
+ See [about.md](.agents/skills/first-tree/references/about.md) for background — the problem, the idea, and who it's for.
38
38
 
39
39
  See the installed framework documentation:
40
- - [principles.md](skills/first-tree/references/principles.md) — core principles with examples
41
- - [ownership-and-naming.md](skills/first-tree/references/ownership-and-naming.md) — node naming and ownership model
40
+ - [principles.md](.agents/skills/first-tree/references/principles.md) — core principles with examples
41
+ - [ownership-and-naming.md](.agents/skills/first-tree/references/ownership-and-naming.md) — node naming and ownership model
@@ -19,7 +19,7 @@ jobs:
19
19
  with:
20
20
  node-version: "22"
21
21
 
22
- - run: npx tsx skills/first-tree/assets/framework/helpers/generate-codeowners.ts
22
+ - run: npx tsx .agents/skills/first-tree/assets/framework/helpers/generate-codeowners.ts
23
23
 
24
24
  - name: Commit if changed
25
25
  run: |
@@ -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: |
@@ -19,11 +19,15 @@ import {
19
19
  import {
20
20
  AGENT_INSTRUCTIONS_FILE,
21
21
  AGENT_INSTRUCTIONS_TEMPLATE,
22
+ CLAUDE_INSTRUCTIONS_FILE,
22
23
  FRAMEWORK_ASSET_ROOT,
23
24
  FRAMEWORK_VERSION,
24
25
  INSTALLED_PROGRESS,
25
26
  LEGACY_AGENT_INSTRUCTIONS_FILE,
27
+ installedSkillRootsDisplay,
28
+ SOURCE_INTEGRATION_MARKER,
26
29
  } from "#skill/engine/runtime/asset-loader.js";
30
+ import { upsertSourceIntegrationFiles } from "#skill/engine/runtime/source-integration.js";
27
31
 
28
32
  /**
29
33
  * The interactive prompt tool the agent should use to present choices.
@@ -33,8 +37,10 @@ import {
33
37
  export const INTERACTIVE_TOOL = "AskUserQuestion";
34
38
  export const INIT_USAGE = `usage: context-tree init [--here] [--tree-name NAME] [--tree-path PATH]
35
39
 
36
- By default, running \`context-tree init\` inside a source or workspace repo creates
37
- a sibling dedicated tree repo named \`<repo>-context\`.
40
+ By default, running \`context-tree init\` inside a source or workspace repo installs
41
+ the first-tree skill in the current repo, updates \`AGENTS.md\` and \`CLAUDE.md\`
42
+ with a \`${SOURCE_INTEGRATION_MARKER}\` line, and creates a sibling dedicated tree
43
+ repo named \`<repo>-context\`.
38
44
 
39
45
  Options:
40
46
  --here Initialize the current repo in place
@@ -61,13 +67,14 @@ const TEMPLATE_MAP: TemplateTarget[] = [
61
67
 
62
68
  interface TaskListContext {
63
69
  sourceRepoPath?: string;
70
+ sourceRepoName?: string;
64
71
  dedicatedTreeRepo?: boolean;
65
72
  }
66
73
 
67
74
  function installSkill(source: string, target: string): void {
68
75
  copyCanonicalSkill(source, target);
69
76
  console.log(
70
- " Installed skills/first-tree/ from the bundled first-tree package",
77
+ ` Installed ${installedSkillRootsDisplay()} from the bundled first-tree package`,
71
78
  );
72
79
  }
73
80
 
@@ -105,6 +112,23 @@ export function formatTaskList(
105
112
  if (context.sourceRepoPath) {
106
113
  lines.push(`**Bootstrap source repo:** \`${context.sourceRepoPath}\``, "");
107
114
  }
115
+ if (context.sourceRepoName) {
116
+ lines.push(
117
+ `**Source/workspace contract:** Keep \`${context.sourceRepoName}\` limited to the installed skill plus the \`${SOURCE_INTEGRATION_MARKER}\` lines in \`${AGENT_INSTRUCTIONS_FILE}\` and \`${CLAUDE_INSTRUCTIONS_FILE}\`. Never add \`NODE.md\`, \`members/\`, or tree-scoped \`${AGENT_INSTRUCTIONS_FILE}\` there.`,
118
+ "",
119
+ );
120
+ lines.push("## Source Workspace Workflow");
121
+ lines.push(
122
+ `- [ ] Publish this dedicated tree repo to the same GitHub organization as \`${context.sourceRepoName}\``,
123
+ );
124
+ lines.push(
125
+ `- [ ] Add this tree repo back to \`${context.sourceRepoName}\` as a git submodule after the remote exists`,
126
+ );
127
+ lines.push(
128
+ `- [ ] Open a PR against the source/workspace repo's default branch with only the installed skill, the \`${SOURCE_INTEGRATION_MARKER}\` marker lines in \`${AGENT_INSTRUCTIONS_FILE}\` / \`${CLAUDE_INSTRUCTIONS_FILE}\`, and the new submodule pointer`,
129
+ );
130
+ lines.push("");
131
+ }
108
132
  lines.push(
109
133
  "When you publish this tree repo, keep it in the same GitHub organization" +
110
134
  " as the source repo unless you have a reason not to.",
@@ -181,9 +205,19 @@ export function runInit(repo?: Repo, options?: InitOptions): number {
181
205
  const taskListContext = initTarget.dedicatedTreeRepo
182
206
  ? {
183
207
  dedicatedTreeRepo: true,
208
+ sourceRepoName: sourceRepo.repoName(),
184
209
  sourceRepoPath: relativePathFrom(r.root, sourceRepo.root),
185
210
  }
186
211
  : undefined;
212
+ let sourceRoot: string | null = null;
213
+
214
+ const resolveSourceRoot = (): string => {
215
+ if (sourceRoot !== null) {
216
+ return sourceRoot;
217
+ }
218
+ sourceRoot = options?.sourceRoot ?? resolveBundledPackageRoot();
219
+ return sourceRoot;
220
+ };
187
221
 
188
222
  if (initTarget.dedicatedTreeRepo) {
189
223
  console.log(
@@ -195,17 +229,55 @@ export function runInit(repo?: Repo, options?: InitOptions): number {
195
229
  if (initTarget.createdGitRepo) {
196
230
  console.log(" Initialized a new git repo for the tree.");
197
231
  }
232
+ console.log(
233
+ " The source/workspace repo should keep only the installed skill and the" +
234
+ ` ${SOURCE_INTEGRATION_MARKER} lines in ${AGENT_INSTRUCTIONS_FILE} and ${CLAUDE_INSTRUCTIONS_FILE}.`,
235
+ );
236
+ console.log(
237
+ ` Never add NODE.md, members/, or tree-scoped ${AGENT_INSTRUCTIONS_FILE} to the source/workspace repo.`,
238
+ );
198
239
  console.log();
199
240
  }
200
241
 
201
- if (!r.hasFramework()) {
242
+ if (initTarget.dedicatedTreeRepo) {
243
+ try {
244
+ const resolvedSourceRoot = resolveSourceRoot();
245
+ const hadSourceSkill = sourceRepo.hasCurrentInstalledSkill();
246
+ if (!hadSourceSkill) {
247
+ console.log(
248
+ "Installing the first-tree skill into the source/workspace repo...",
249
+ );
250
+ installSkill(resolvedSourceRoot, sourceRepo.root);
251
+ }
252
+ const updates = upsertSourceIntegrationFiles(sourceRepo.root, r.repoName());
253
+ const changedFiles = updates
254
+ .filter((update) => update.action !== "unchanged")
255
+ .map((update) => update.file);
256
+ if (changedFiles.length > 0) {
257
+ console.log(
258
+ ` Updated source/workspace instructions in ${changedFiles.map((file) => `\`${file}\``).join(" and ")}`,
259
+ );
260
+ } else {
261
+ console.log(
262
+ ` Source/workspace instructions already contain ${SOURCE_INTEGRATION_MARKER}`,
263
+ );
264
+ }
265
+ console.log();
266
+ } catch (err) {
267
+ const message = err instanceof Error ? err.message : "unknown error";
268
+ console.error(`Error: ${message}`);
269
+ return 1;
270
+ }
271
+ }
272
+
273
+ if (!r.hasCurrentInstalledSkill()) {
202
274
  try {
203
- const sourceRoot = options?.sourceRoot ?? resolveBundledPackageRoot();
275
+ const resolvedSourceRoot = resolveSourceRoot();
204
276
  console.log(
205
277
  "Installing the framework skill bundled with this first-tree package...",
206
278
  );
207
279
  console.log("Installing skill and scaffolding...");
208
- installSkill(sourceRoot, r.root);
280
+ installSkill(resolvedSourceRoot, r.root);
209
281
  renderTemplates(r.root);
210
282
  console.log();
211
283
  } catch (err) {
@@ -2,26 +2,35 @@ 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,
16
21
  progressFileCandidates,
17
22
  resolveFirstExistingPath,
23
+ SOURCE_INTEGRATION_FILES,
24
+ SOURCE_INTEGRATION_MARKER,
18
25
  } from "#skill/engine/runtime/asset-loader.js";
19
26
 
20
27
  const FRONTMATTER_RE = /^---\s*\n(.*?)\n---/s;
21
28
  const OWNERS_RE = /^owners:\s*\[([^\]]*)\]/m;
22
29
  const TITLE_RE = /^title:\s*['"]?(.+?)['"]?\s*$/m;
23
30
  const EMPTY_REPO_ENTRY_ALLOWLIST = new Set([
31
+ ".agents",
24
32
  ".DS_Store",
33
+ ".claude",
25
34
  ".editorconfig",
26
35
  ".gitattributes",
27
36
  ".github",
@@ -164,6 +173,22 @@ export class Repo {
164
173
  return knownConfigs.some((c) => this.pathExists(c));
165
174
  }
166
175
 
176
+ installedSkillRoots(): string[] {
177
+ return installedSkillRoots();
178
+ }
179
+
180
+ missingInstalledSkillRoots(): string[] {
181
+ return this.installedSkillRoots().filter(
182
+ (root) =>
183
+ !this.pathExists(join(root, "SKILL.md")) ||
184
+ !this.pathExists(join(root, "assets", "framework", "VERSION")),
185
+ );
186
+ }
187
+
188
+ hasCurrentInstalledSkill(): boolean {
189
+ return this.missingInstalledSkillRoots().length === 0;
190
+ }
191
+
167
192
  isGitRepo(): boolean {
168
193
  return hasGitMetadata(this.root);
169
194
  }
@@ -198,6 +223,12 @@ export class Repo {
198
223
  if (layout === "legacy-skill") {
199
224
  return LEGACY_SKILL_PROGRESS;
200
225
  }
226
+ if (layout === "legacy-repo-skill") {
227
+ return LEGACY_REPO_SKILL_PROGRESS;
228
+ }
229
+ if (layout === "claude-skill") {
230
+ return CLAUDE_INSTALLED_PROGRESS;
231
+ }
201
232
  return INSTALLED_PROGRESS;
202
233
  }
203
234
 
@@ -209,6 +240,12 @@ export class Repo {
209
240
  if (layout === "legacy-skill") {
210
241
  return LEGACY_SKILL_VERSION;
211
242
  }
243
+ if (layout === "legacy-repo-skill") {
244
+ return LEGACY_REPO_SKILL_VERSION;
245
+ }
246
+ if (layout === "claude-skill") {
247
+ return CLAUDE_FRAMEWORK_VERSION;
248
+ }
212
249
  return FRAMEWORK_VERSION;
213
250
  }
214
251
 
@@ -240,6 +277,23 @@ export class Repo {
240
277
  return text.includes(FRAMEWORK_BEGIN_MARKER) && text.includes(FRAMEWORK_END_MARKER);
241
278
  }
242
279
 
280
+ hasSourceIntegrationFile(relPath: string): boolean {
281
+ return this.fileContains(relPath, SOURCE_INTEGRATION_MARKER);
282
+ }
283
+
284
+ hasSourceWorkspaceIntegration(): boolean {
285
+ return SOURCE_INTEGRATION_FILES.some((file) => this.hasSourceIntegrationFile(file));
286
+ }
287
+
288
+ hasTreeContent(): boolean {
289
+ return (
290
+ this.progressPath() !== null
291
+ || this.hasAgentInstructionsMarkers()
292
+ || this.pathExists("members/NODE.md")
293
+ || this.frontmatter("NODE.md") !== null
294
+ );
295
+ }
296
+
243
297
  hasMembers(): boolean {
244
298
  const membersDir = join(this.root, "members");
245
299
  try {
@@ -305,13 +359,19 @@ export class Repo {
305
359
  return false;
306
360
  }
307
361
 
308
- return (
309
- this.progressPath() !== null
310
- || this.hasFramework()
311
- || this.hasAgentInstructionsMarkers()
312
- || this.pathExists("members/NODE.md")
313
- || this.frontmatter("NODE.md") !== null
314
- );
362
+ if (this.hasTreeContent()) {
363
+ return true;
364
+ }
365
+
366
+ if (this.hasFramework() && this.hasSourceWorkspaceIntegration()) {
367
+ return false;
368
+ }
369
+
370
+ if (this.hasFramework()) {
371
+ return !this.hasLikelySourceRepoSignals();
372
+ }
373
+
374
+ return false;
315
375
  }
316
376
 
317
377
  isLikelyEmptyRepo(): boolean {
@@ -326,6 +386,10 @@ export class Repo {
326
386
  return false;
327
387
  }
328
388
 
389
+ return this.hasLikelySourceRepoSignals();
390
+ }
391
+
392
+ private hasLikelySourceRepoSignals(): boolean {
329
393
  const entries = this.topLevelEntries().filter(
330
394
  (entry) => !EMPTY_REPO_ENTRY_ALLOWLIST.has(entry),
331
395
  );