first-tree 0.0.5 → 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 (35) hide show
  1. package/README.md +21 -7
  2. package/dist/cli.js +5 -5
  3. package/dist/{help-5-WG9QFm.js → help-DV9-AaFp.js} +1 -1
  4. package/dist/{init-CAq0Uhq6.js → init-BgGH2_yC.js} +41 -7
  5. package/dist/onboarding-D7fGGOMN.js +10 -0
  6. package/dist/onboarding-lASHHmgO.js +2 -0
  7. package/dist/{repo-DkR12VUv.js → repo-Cc5U4DWT.js} +22 -2
  8. package/dist/{installer-UgNasLjl.js → source-integration-CuKjoheT.js} +36 -3
  9. package/dist/{upgrade-DYzuvv1k.js → upgrade-BvA9oKmi.js} +26 -3
  10. package/dist/{verify-C0IUSkMZ.js → verify-G8gNXzDX.js} +5 -1
  11. package/package.json +2 -2
  12. package/skills/first-tree/SKILL.md +21 -5
  13. package/skills/first-tree/agents/openai.yaml +1 -1
  14. package/skills/first-tree/assets/framework/VERSION +1 -1
  15. package/skills/first-tree/engine/init.ts +75 -4
  16. package/skills/first-tree/engine/repo.ts +38 -7
  17. package/skills/first-tree/engine/runtime/asset-loader.ts +6 -0
  18. package/skills/first-tree/engine/runtime/source-integration.ts +80 -0
  19. package/skills/first-tree/engine/upgrade.ts +68 -1
  20. package/skills/first-tree/engine/verify.ts +7 -0
  21. package/skills/first-tree/references/maintainer-architecture.md +4 -0
  22. package/skills/first-tree/references/maintainer-thin-cli.md +3 -0
  23. package/skills/first-tree/references/onboarding.md +28 -3
  24. package/skills/first-tree/references/principles.md +97 -57
  25. package/skills/first-tree/references/source-map.md +1 -0
  26. package/skills/first-tree/references/source-workspace-installation.md +52 -0
  27. package/skills/first-tree/references/upgrade-contract.md +19 -4
  28. package/skills/first-tree/scripts/check-skill-sync.sh +2 -0
  29. package/skills/first-tree/tests/init.test.ts +61 -0
  30. package/skills/first-tree/tests/repo.test.ts +20 -0
  31. package/skills/first-tree/tests/skill-artifacts.test.ts +39 -0
  32. package/skills/first-tree/tests/upgrade.test.ts +38 -2
  33. package/skills/first-tree/tests/verify.test.ts +18 -0
  34. package/dist/onboarding-3zYUeYQb.js +0 -2
  35. package/dist/onboarding-Dd63N-V1.js +0 -10
package/README.md CHANGED
@@ -36,7 +36,8 @@ Although the npm package is named `first-tree`, the installed CLI command is
36
36
  ## Quick Start
37
37
 
38
38
  Recommended workflow: start from your source or workspace repo and let
39
- `context-tree init` create a sibling dedicated tree repo.
39
+ `context-tree init` install local source/workspace integration and create a
40
+ sibling dedicated tree repo.
40
41
 
41
42
  ```bash
42
43
  cd my-app
@@ -53,15 +54,27 @@ context-tree init --here
53
54
  ```
54
55
 
55
56
  - `context-tree init` installs `.agents/skills/first-tree/` and
56
- `.claude/skills/first-tree/`, creates `NODE.md`, `AGENTS.md`,
57
- `members/NODE.md`, and writes a checklist to
57
+ `.claude/skills/first-tree/` in the current source/workspace repo, appends a
58
+ single `FIRST-TREE-SOURCE-INTEGRATION:` line to root `AGENTS.md` and
59
+ `CLAUDE.md`, then creates `NODE.md`, tree-scoped `AGENTS.md`,
60
+ `members/NODE.md`, and a checklist in the dedicated tree repo at
58
61
  `.agents/skills/first-tree/progress.md`.
62
+ - Never create `NODE.md`, `members/`, or tree-scoped `AGENTS.md` in the
63
+ source/workspace repo. Those files live only in the dedicated `*-context`
64
+ repo.
65
+ - After creating the dedicated tree repo, prefer pushing it to the same GitHub
66
+ organization as the source repo, add it back as a git submodule, and open a
67
+ PR to the source/workspace repo's default branch instead of merging
68
+ automatically.
59
69
  - `context-tree verify` checks both the progress checklist and deterministic
60
70
  tree validation. It is expected to fail until the required onboarding tasks
61
71
  are complete.
62
72
  - `context-tree upgrade` refreshes the installed skill from the currently
63
- running `first-tree` npm package. To force the newest published package for a
64
- one-off upgrade, run `npx first-tree@latest upgrade`.
73
+ running `first-tree` npm package. In a source/workspace repo it refreshes
74
+ only the local installed skill plus the
75
+ `FIRST-TREE-SOURCE-INTEGRATION:` line; use `--tree-path` to upgrade the
76
+ dedicated tree repo. To force the newest published package for a one-off
77
+ upgrade, run `npx first-tree@latest upgrade`.
65
78
 
66
79
  The package carries the bundled canonical skill, so `init` and `upgrade`
67
80
  install from the package payload instead of cloning this source repo at
@@ -71,9 +84,9 @@ runtime.
71
84
 
72
85
  | Command | What it does |
73
86
  | --- | --- |
74
- | `context-tree init` | Create or refresh a dedicated context tree repo; use `--here` to initialize the current repo in place |
87
+ | `context-tree init` | Install source/workspace integration locally and create or refresh a dedicated context tree repo; use `--here` to initialize the current repo in place |
75
88
  | `context-tree verify` | Run verification checks against the current tree |
76
- | `context-tree upgrade` | Refresh the installed skill from the current `first-tree` npm package and write follow-up tasks |
89
+ | `context-tree upgrade` | Refresh the installed skill from the current `first-tree` npm package; in a source/workspace repo it updates only local integration, while tree repos also get follow-up tasks |
77
90
  | `context-tree help onboarding` | Print the onboarding guide |
78
91
  | `context-tree --help` | Show the available commands |
79
92
  | `context-tree --version` | Print the installed CLI version |
@@ -134,6 +147,7 @@ live in `skills/first-tree/`.
134
147
 
135
148
  - User-facing overview: `skills/first-tree/references/about.md`
136
149
  - User onboarding: `skills/first-tree/references/onboarding.md`
150
+ - Source/workspace install contract: `skills/first-tree/references/source-workspace-installation.md`
137
151
  - Maintainer entrypoint: `skills/first-tree/references/source-map.md`
138
152
 
139
153
  If you are maintaining this repo, start with the source map instead of relying
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ const USAGE = `usage: context-tree <command>
7
7
  New to context-tree? Run \`context-tree help onboarding\` first.
8
8
 
9
9
  Commands:
10
- init Create or refresh a dedicated context tree repo
10
+ init Install source/workspace integration and create or refresh a dedicated context tree repo
11
11
  verify Run verification checks against a tree repo
12
12
  upgrade Refresh the installed skill in a tree repo
13
13
  help Show help for a topic (e.g. \`help onboarding\`)
@@ -44,18 +44,18 @@ async function runCli(args, output = console.log) {
44
44
  const command = args[0];
45
45
  switch (command) {
46
46
  case "init": {
47
- const { runInit } = await import("./init-CAq0Uhq6.js");
47
+ const { runInit } = await import("./init-BgGH2_yC.js");
48
48
  return runInit(args.slice(1));
49
49
  }
50
50
  case "verify": {
51
- const { runVerify } = await import("./verify-C0IUSkMZ.js");
51
+ const { runVerify } = await import("./verify-G8gNXzDX.js");
52
52
  return runVerify(args.slice(1));
53
53
  }
54
54
  case "upgrade": {
55
- const { runUpgrade } = await import("./upgrade-DYzuvv1k.js");
55
+ const { runUpgrade } = await import("./upgrade-BvA9oKmi.js");
56
56
  return runUpgrade(args.slice(1));
57
57
  }
58
- case "help": return (await import("./help-5-WG9QFm.js")).runHelp(args.slice(1), write);
58
+ case "help": return (await import("./help-DV9-AaFp.js")).runHelp(args.slice(1), write);
59
59
  default:
60
60
  write(`Unknown command: ${command}`);
61
61
  write(USAGE);
@@ -12,7 +12,7 @@ async function runHelp(args, output = console.log) {
12
12
  }
13
13
  switch (topic) {
14
14
  case "onboarding": {
15
- const { runOnboarding } = await import("./onboarding-3zYUeYQb.js");
15
+ const { runOnboarding } = await import("./onboarding-lASHHmgO.js");
16
16
  return runOnboarding(output);
17
17
  }
18
18
  default:
@@ -1,6 +1,6 @@
1
- import { T as installedSkillRootsDisplay, b as LEGACY_SKILL_EXAMPLES_DIR, c as FRAMEWORK_ASSET_ROOT, d as FRAMEWORK_VERSION, f as FRAMEWORK_WORKFLOWS_DIR, g as LEGACY_EXAMPLES_DIR, h as LEGACY_AGENT_INSTRUCTIONS_FILE, i as AGENT_INSTRUCTIONS_TEMPLATE, l as FRAMEWORK_EXAMPLES_DIR, n as Repo, o as CLAUDE_FRAMEWORK_EXAMPLES_DIR, p as INSTALLED_PROGRESS, r as AGENT_INSTRUCTIONS_FILE, t as FRAMEWORK_END_MARKER, u as FRAMEWORK_TEMPLATES_DIR, v as LEGACY_REPO_SKILL_EXAMPLES_DIR } from "./repo-DkR12VUv.js";
2
- import { n as onboarding_default } from "./onboarding-Dd63N-V1.js";
3
- import { n as renderTemplateFile, r as resolveBundledPackageRoot, t as copyCanonicalSkill } from "./installer-UgNasLjl.js";
1
+ import { D as SOURCE_INTEGRATION_MARKER, O as installedSkillRootsDisplay, _ as LEGACY_EXAMPLES_DIR, d as FRAMEWORK_TEMPLATES_DIR, f as FRAMEWORK_VERSION, g as LEGACY_AGENT_INSTRUCTIONS_FILE, i as AGENT_INSTRUCTIONS_TEMPLATE, l as FRAMEWORK_ASSET_ROOT, m as INSTALLED_PROGRESS, n as Repo, o as CLAUDE_FRAMEWORK_EXAMPLES_DIR, p as FRAMEWORK_WORKFLOWS_DIR, r as AGENT_INSTRUCTIONS_FILE, s as CLAUDE_INSTRUCTIONS_FILE, t as FRAMEWORK_END_MARKER, u as FRAMEWORK_EXAMPLES_DIR, x as LEGACY_SKILL_EXAMPLES_DIR, y as LEGACY_REPO_SKILL_EXAMPLES_DIR } from "./repo-Cc5U4DWT.js";
2
+ import { n as onboarding_default } from "./onboarding-D7fGGOMN.js";
3
+ import { i as resolveBundledPackageRoot, n as copyCanonicalSkill, r as renderTemplateFile, t as upsertSourceIntegrationFiles } from "./source-integration-CuKjoheT.js";
4
4
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
5
5
  import { execFileSync } from "node:child_process";
6
6
  import { dirname, join, relative, resolve } from "node:path";
@@ -205,8 +205,10 @@ function evaluateAll(repo) {
205
205
  const INTERACTIVE_TOOL = "AskUserQuestion";
206
206
  const INIT_USAGE = `usage: context-tree init [--here] [--tree-name NAME] [--tree-path PATH]
207
207
 
208
- By default, running \`context-tree init\` inside a source or workspace repo creates
209
- a sibling dedicated tree repo named \`<repo>-context\`.
208
+ By default, running \`context-tree init\` inside a source or workspace repo installs
209
+ the first-tree skill in the current repo, updates \`AGENTS.md\` and \`CLAUDE.md\`
210
+ with a \`${SOURCE_INTEGRATION_MARKER}\` line, and creates a sibling dedicated tree
211
+ repo named \`<repo>-context\`.
210
212
 
211
213
  Options:
212
214
  --here Initialize the current repo in place
@@ -246,6 +248,14 @@ function formatTaskList(groups, context) {
246
248
  if (context?.dedicatedTreeRepo) {
247
249
  lines.push("This repository is the dedicated Context Tree. Keep decisions, rationale, cross-domain relationships, and ownership here; keep execution detail in your source repositories.", "");
248
250
  if (context.sourceRepoPath) lines.push(`**Bootstrap source repo:** \`${context.sourceRepoPath}\``, "");
251
+ if (context.sourceRepoName) {
252
+ lines.push(`**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.`, "");
253
+ lines.push("## Source Workspace Workflow");
254
+ lines.push(`- [ ] Publish this dedicated tree repo to the same GitHub organization as \`${context.sourceRepoName}\``);
255
+ lines.push(`- [ ] Add this tree repo back to \`${context.sourceRepoName}\` as a git submodule after the remote exists`);
256
+ lines.push(`- [ ] 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`);
257
+ lines.push("");
258
+ }
249
259
  lines.push("When you publish this tree repo, keep it in the same GitHub organization as the source repo unless you have a reason not to.", "");
250
260
  }
251
261
  lines.push(`**Agent instructions:** Before starting work, analyze the full task list below and identify all information you need from the user. Ask the user for their code repositories or project directories so you can analyze the source yourself — derive project descriptions, domains, and members from the code instead of asking the user to describe them. Collect everything upfront using the **${INTERACTIVE_TOOL}** tool with structured options — present selectable choices (with label and description) so the user can pick instead of typing free-form answers. You may batch up to 4 questions per ${INTERACTIVE_TOOL} call.\n`);
@@ -283,20 +293,44 @@ function runInit(repo, options) {
283
293
  const r = initTarget.repo;
284
294
  const taskListContext = initTarget.dedicatedTreeRepo ? {
285
295
  dedicatedTreeRepo: true,
296
+ sourceRepoName: sourceRepo.repoName(),
286
297
  sourceRepoPath: relativePathFrom(r.root, sourceRepo.root)
287
298
  } : void 0;
299
+ let sourceRoot = null;
300
+ const resolveSourceRoot = () => {
301
+ if (sourceRoot !== null) return sourceRoot;
302
+ sourceRoot = options?.sourceRoot ?? resolveBundledPackageRoot();
303
+ return sourceRoot;
304
+ };
288
305
  if (initTarget.dedicatedTreeRepo) {
289
306
  console.log("Recommended workflow: keep the Context Tree in a dedicated repo separate from your source/workspace repo.");
290
307
  console.log(` Source repo: ${sourceRepo.root}`);
291
308
  console.log(` Tree repo: ${r.root}`);
292
309
  if (initTarget.createdGitRepo) console.log(" Initialized a new git repo for the tree.");
310
+ console.log(` The source/workspace repo should keep only the installed skill and the ${SOURCE_INTEGRATION_MARKER} lines in ${AGENT_INSTRUCTIONS_FILE} and ${CLAUDE_INSTRUCTIONS_FILE}.`);
311
+ console.log(` Never add NODE.md, members/, or tree-scoped ${AGENT_INSTRUCTIONS_FILE} to the source/workspace repo.`);
312
+ console.log();
313
+ }
314
+ if (initTarget.dedicatedTreeRepo) try {
315
+ const resolvedSourceRoot = resolveSourceRoot();
316
+ if (!sourceRepo.hasCurrentInstalledSkill()) {
317
+ console.log("Installing the first-tree skill into the source/workspace repo...");
318
+ installSkill(resolvedSourceRoot, sourceRepo.root);
319
+ }
320
+ const changedFiles = upsertSourceIntegrationFiles(sourceRepo.root, r.repoName()).filter((update) => update.action !== "unchanged").map((update) => update.file);
321
+ if (changedFiles.length > 0) console.log(` Updated source/workspace instructions in ${changedFiles.map((file) => `\`${file}\``).join(" and ")}`);
322
+ else console.log(` Source/workspace instructions already contain ${SOURCE_INTEGRATION_MARKER}`);
293
323
  console.log();
324
+ } catch (err) {
325
+ const message = err instanceof Error ? err.message : "unknown error";
326
+ console.error(`Error: ${message}`);
327
+ return 1;
294
328
  }
295
329
  if (!r.hasCurrentInstalledSkill()) try {
296
- const sourceRoot = options?.sourceRoot ?? resolveBundledPackageRoot();
330
+ const resolvedSourceRoot = resolveSourceRoot();
297
331
  console.log("Installing the framework skill bundled with this first-tree package...");
298
332
  console.log("Installing skill and scaffolding...");
299
- installSkill(sourceRoot, r.root);
333
+ installSkill(resolvedSourceRoot, r.root);
300
334
  renderTemplates(r.root);
301
335
  console.log();
302
336
  } catch (err) {
@@ -0,0 +1,10 @@
1
+ //#region skills/first-tree/references/onboarding.md
2
+ var onboarding_default = "# Context Tree Onboarding\n\nYou are setting up a **Context Tree** — the living source of truth for an organization. This document tells you what it is and how to bootstrap one.\n\n---\n\n## What Is a Context Tree\n\nA Context Tree is a Git repository where every directory is a **domain** and every file is a **node**. Each node captures decisions, designs, and cross-domain relationships — the knowledge that would otherwise scatter across PRs, documents, and people's heads.\n\nKey properties:\n\n- **Nodes are markdown files.** Each directory has a `NODE.md` that describes the domain. Leaf `.md` files capture specific decisions or designs.\n- **Every node has an owner.** Declared in YAML frontmatter. Owners approve changes to their nodes.\n- **Organized by concern, not by repo or team.** An agent working on \"add SSO\" finds all auth context in one place — not split across 4 repos.\n- **The tree is never a snapshot — it's the current state.** When decisions change, the tree updates. Stale nodes are bugs.\n\n### Frontmatter Format\n\nEvery node has frontmatter:\n\n```yaml\n---\ntitle: \"Auth Architecture\"\nowners: [alice, bob]\nsoft_links: [/infrastructure/deployments]\n---\n```\n\n- `owners` — who can approve changes. `owners: []` inherits from parent. `owners: [*]` means anyone.\n- `soft_links` — cross-references to related nodes in other domains.\n\n### What Belongs in the Tree\n\nInformation an agent needs to **decide** on an approach — not to execute it.\n\n**Yes:** \"Auth spans 4 repos: backend issues JWTs, frontend uses Better Auth, extension uses OAuth popup, desktop uses localhost callback.\"\n\n**No:** The function signature of `auth_service.verify()` — that's in the code.\n\n---\n\n## Four Principles\n\n1. **Source of truth for decisions, not execution.** The tree captures the *what* and *why*. Execution details stay in source systems.\n2. **Agents are first-class participants.** The tree is designed for agents to navigate and update.\n3. **Transparency by default.** Reading is open to all. Writing requires owner approval.\n4. **Git-native.** Nodes are files, domains are directories. History, ownership, and review follow Git conventions.\n\n---\n\n## How to Set Up a Context Tree\n\n### Prerequisites\n\n- A source/workspace Git repository, or an already-created dedicated tree repo\n- Node.js 18+\n- The npm package is `first-tree`, the installed CLI command is\n `context-tree`.\n- `context-tree init` installs the framework skill into\n `.agents/skills/first-tree/` and `.claude/skills/first-tree/`.\n- Use `npx first-tree init` for one-off runs, or `npm install -g first-tree`\n to add the `context-tree` command to your PATH\n\n### Step 1: Initialize\n\nRecommended workflow: run `context-tree init` from your source or workspace repo.\nThe CLI will install the bundled skill in the current repo, update root\n`AGENTS.md` and `CLAUDE.md` with a `FIRST-TREE-SOURCE-INTEGRATION:` line, and\ncreate a sibling dedicated tree repo named `<repo>-context` by default. Tree\nfiles are scaffolded only in the dedicated tree repo.\n\n```bash\ncd my-org\ncontext-tree init\ncd ../my-org-context\n```\n\nIf you already created a dedicated tree repo manually, initialize it in place:\n\n```bash\nmkdir my-org-context && cd my-org-context\ngit init\ncontext-tree init --here\n```\n\nEither way, the framework installs into `.agents/skills/first-tree/` and\n`.claude/skills/first-tree/`, renders scaffolding (`NODE.md`, `AGENTS.md`,\n`members/NODE.md`), and generates a task list in\n`.agents/skills/first-tree/progress.md`.\n\nPublishing tip: keep the tree repo in the same GitHub organization as the\nsource repo unless you have a reason not to.\n\nHard boundary: do **not** create `NODE.md`, `members/`, or tree-scoped\n`AGENTS.md` in the source/workspace repo. Those files belong only in the\ndedicated `*-context` repo.\n\nDefault agent workflow after initialization:\n\n1. Create and push the dedicated `*-context` repo in the same GitHub\n organization as the source repo.\n2. Add the dedicated tree repo back to the source/workspace repo as a `git submodule`.\n3. Open a PR against the source/workspace repo's default branch for the local\n skill integration plus the new submodule pointer. Do not merge it\n automatically.\n\n### Step 2: Work Through the Task List\n\nRead `.agents/skills/first-tree/progress.md`. It contains a checklist tailored\nto the current state of the repo. Complete each task:\n\n- Fill in `NODE.md` with your organization name, owners, and domains\n- Add project-specific instructions to `AGENTS.md` below the framework markers\n- Create member nodes under `members/`\n- Optionally configure agent integration (for Claude Code, the installed hook\n assets live under `.claude/skills/first-tree/`)\n- Copy validation workflows from\n `.agents/skills/first-tree/assets/framework/workflows/` to\n `.github/workflows/`\n\nAs you complete each task, check it off in\n`.agents/skills/first-tree/progress.md` by changing `- [ ]` to `- [x]`.\n\n### Step 3: Verify\n\n```bash\ncontext-tree verify\n```\n\nOr, from your source/workspace repo:\n\n```bash\ncontext-tree verify --tree-path ../my-org-context\n```\n\nThis fails if any items in `.agents/skills/first-tree/progress.md` remain\nunchecked, and runs deterministic checks (valid frontmatter, node structure,\nmember nodes exist).\n\nDo not run `context-tree verify` in the source/workspace repo itself. That repo\nonly carries the installed skill plus the\n`FIRST-TREE-SOURCE-INTEGRATION:` line.\n\n### Step 4: Design Your Domains\n\nCreate top-level directories for your organization's primary concerns. Each needs a `NODE.md`:\n\n```\nmy-org-tree/\n NODE.md # root — lists all domains\n engineering/\n NODE.md # decisions about architecture, infra, tooling\n product/\n NODE.md # strategy, roadmap, user research\n marketing/\n NODE.md # positioning, campaigns\n members/\n NODE.md # team members and agents\n alice/\n NODE.md # individual member node\n```\n\n### Step 5: Populate from Existing Work\n\nFor each domain, extract knowledge from existing repos, docs, and systems:\n\n- Decisions and their rationale\n- Cross-domain relationships and dependencies\n- Constraints that aren't obvious from the code\n\nThe tree doesn't duplicate source code — it captures what connects things and why they were built that way.\n\n---\n\n## CLI Reference\n\n| Command | Description |\n|---------|-------------|\n| `context-tree init` | Install local source/workspace integration and create or refresh a dedicated tree repo. By default, running in a source/workspace repo creates a sibling `<repo>-context`; use `--here` to initialize the current repo in place. |\n| `context-tree verify` | Check the installed progress file for unchecked items + run deterministic validation. Use `--tree-path` when invoking from another working directory. |\n| `context-tree upgrade` | Refresh the installed framework skill from the currently running `first-tree` npm package and generate follow-up tasks. Use `--tree-path` when invoking from another working directory. |\n| `context-tree help onboarding` | Print this onboarding guide. |\n\n---\n\n## Upgrading the Framework\n\nWhen the framework updates:\n\n```bash\ncontext-tree upgrade\n```\n\n`context-tree upgrade` refreshes `.agents/skills/first-tree/` and\n`.claude/skills/first-tree/` from the skill bundled with the currently running\n`first-tree` npm package, preserves your tree content, and generates follow-up\ntasks in `.agents/skills/first-tree/progress.md`.\n\nIf you run `context-tree upgrade` in the source/workspace repo, it refreshes\nonly the local installed skill plus the `FIRST-TREE-SOURCE-INTEGRATION:` lines.\nRun `context-tree upgrade --tree-path ../my-org-context` to upgrade the\ndedicated tree repo itself.\n\nIf your repo still uses the older `skills/first-tree/`,\n`skills/first-tree-cli-framework/`, or `.context-tree/` layouts,\n`context-tree upgrade` will migrate it to the current installed layout first.\n\nTo pick up a newer framework release, first run a newer package version, for\nexample `npx first-tree@latest upgrade`, or update your global `first-tree`\ninstall before running `context-tree upgrade`.\n\n---\n\n## Further Reading\n\n- `.agents/skills/first-tree/references/principles.md` — Core principles with detailed examples\n- `.agents/skills/first-tree/references/source-workspace-installation.md` — Source/workspace install contract\n- `.agents/skills/first-tree/references/ownership-and-naming.md` — How nodes are named and owned\n- `AGENTS.md` in your tree — The before/during/after workflow for every task\n";
3
+ //#endregion
4
+ //#region skills/first-tree/engine/onboarding.ts
5
+ function runOnboarding(output = console.log) {
6
+ output(onboarding_default);
7
+ return 0;
8
+ }
9
+ //#endregion
10
+ export { onboarding_default as n, runOnboarding as t };
@@ -0,0 +1,2 @@
1
+ import { t as runOnboarding } from "./onboarding-D7fGGOMN.js";
2
+ export { runOnboarding };
@@ -20,6 +20,9 @@ const INSTALLED_PROGRESS = join(SKILL_ROOT, "progress.md");
20
20
  const AGENT_INSTRUCTIONS_FILE = "AGENTS.md";
21
21
  const LEGACY_AGENT_INSTRUCTIONS_FILE = "AGENT.md";
22
22
  const AGENT_INSTRUCTIONS_TEMPLATE = "agents.md.template";
23
+ const CLAUDE_INSTRUCTIONS_FILE = "CLAUDE.md";
24
+ const SOURCE_INTEGRATION_MARKER = "FIRST-TREE-SOURCE-INTEGRATION:";
25
+ const SOURCE_INTEGRATION_FILES = [AGENT_INSTRUCTIONS_FILE, CLAUDE_INSTRUCTIONS_FILE];
23
26
  join(CLAUDE_SKILL_ROOT, "agents");
24
27
  join(CLAUDE_SKILL_ROOT, "references");
25
28
  const CLAUDE_FRAMEWORK_ASSET_ROOT = join(CLAUDE_SKILL_ROOT, "assets", "framework");
@@ -112,7 +115,9 @@ const FRONTMATTER_RE = /^---\s*\n(.*?)\n---/s;
112
115
  const OWNERS_RE = /^owners:\s*\[([^\]]*)\]/m;
113
116
  const TITLE_RE = /^title:\s*['"]?(.+?)['"]?\s*$/m;
114
117
  const EMPTY_REPO_ENTRY_ALLOWLIST = new Set([
118
+ ".agents",
115
119
  ".DS_Store",
120
+ ".claude",
116
121
  ".editorconfig",
117
122
  ".gitattributes",
118
123
  ".github",
@@ -291,6 +296,15 @@ var Repo = class {
291
296
  if (text === null) return false;
292
297
  return text.includes("<!-- BEGIN CONTEXT-TREE FRAMEWORK") && text.includes("<!-- END CONTEXT-TREE FRAMEWORK -->");
293
298
  }
299
+ hasSourceIntegrationFile(relPath) {
300
+ return this.fileContains(relPath, SOURCE_INTEGRATION_MARKER);
301
+ }
302
+ hasSourceWorkspaceIntegration() {
303
+ return SOURCE_INTEGRATION_FILES.some((file) => this.hasSourceIntegrationFile(file));
304
+ }
305
+ hasTreeContent() {
306
+ return this.progressPath() !== null || this.hasAgentInstructionsMarkers() || this.pathExists("members/NODE.md") || this.frontmatter("NODE.md") !== null;
307
+ }
294
308
  hasMembers() {
295
309
  const membersDir = join(this.root, "members");
296
310
  try {
@@ -338,13 +352,19 @@ var Repo = class {
338
352
  }
339
353
  looksLikeTreeRepo() {
340
354
  if (this.pathExists("package.json") && this.pathExists("src/cli.ts") && this.pathExists("skills/first-tree/SKILL.md") && this.progressPath() === null && this.frontmatter("NODE.md") === null && !this.hasAgentInstructionsMarkers() && !this.pathExists("members/NODE.md")) return false;
341
- return this.progressPath() !== null || this.hasFramework() || this.hasAgentInstructionsMarkers() || this.pathExists("members/NODE.md") || this.frontmatter("NODE.md") !== null;
355
+ if (this.hasTreeContent()) return true;
356
+ if (this.hasFramework() && this.hasSourceWorkspaceIntegration()) return false;
357
+ if (this.hasFramework()) return !this.hasLikelySourceRepoSignals();
358
+ return false;
342
359
  }
343
360
  isLikelyEmptyRepo() {
344
361
  return this.topLevelEntries().filter((entry) => !EMPTY_REPO_ENTRY_ALLOWLIST.has(entry)).length === 0;
345
362
  }
346
363
  isLikelySourceRepo() {
347
364
  if (this.looksLikeTreeRepo()) return false;
365
+ return this.hasLikelySourceRepoSignals();
366
+ }
367
+ hasLikelySourceRepoSignals() {
348
368
  const entries = this.topLevelEntries().filter((entry) => !EMPTY_REPO_ENTRY_ALLOWLIST.has(entry));
349
369
  if (entries.length === 0) return false;
350
370
  let directoryCount = 0;
@@ -366,4 +386,4 @@ function isDirectory(root, relPath) {
366
386
  }
367
387
  }
368
388
  //#endregion
369
- export { SKILL_NAME as C, LEGACY_SKILL_ROOT as S, installedSkillRootsDisplay as T, LEGACY_FRAMEWORK_ROOT as _, BUNDLED_SKILL_ROOT as a, LEGACY_SKILL_EXAMPLES_DIR as b, FRAMEWORK_ASSET_ROOT as c, FRAMEWORK_VERSION as d, FRAMEWORK_WORKFLOWS_DIR as f, LEGACY_EXAMPLES_DIR as g, LEGACY_AGENT_INSTRUCTIONS_FILE as h, AGENT_INSTRUCTIONS_TEMPLATE as i, FRAMEWORK_EXAMPLES_DIR as l, INSTALLED_SKILL_ROOTS as m, Repo as n, CLAUDE_FRAMEWORK_EXAMPLES_DIR as o, INSTALLED_PROGRESS as p, AGENT_INSTRUCTIONS_FILE as r, CLAUDE_SKILL_ROOT as s, FRAMEWORK_END_MARKER as t, FRAMEWORK_TEMPLATES_DIR as u, LEGACY_REPO_SKILL_EXAMPLES_DIR as v, SKILL_ROOT as w, LEGACY_SKILL_NAME as x, LEGACY_REPO_SKILL_ROOT as y };
389
+ export { LEGACY_SKILL_ROOT as C, SOURCE_INTEGRATION_MARKER as D, SOURCE_INTEGRATION_FILES as E, installedSkillRootsDisplay as O, LEGACY_SKILL_NAME as S, SKILL_ROOT as T, LEGACY_EXAMPLES_DIR as _, BUNDLED_SKILL_ROOT as a, LEGACY_REPO_SKILL_ROOT as b, CLAUDE_SKILL_ROOT as c, FRAMEWORK_TEMPLATES_DIR as d, FRAMEWORK_VERSION as f, LEGACY_AGENT_INSTRUCTIONS_FILE as g, INSTALLED_SKILL_ROOTS as h, AGENT_INSTRUCTIONS_TEMPLATE as i, FRAMEWORK_ASSET_ROOT as l, INSTALLED_PROGRESS as m, Repo as n, CLAUDE_FRAMEWORK_EXAMPLES_DIR as o, FRAMEWORK_WORKFLOWS_DIR as p, AGENT_INSTRUCTIONS_FILE as r, CLAUDE_INSTRUCTIONS_FILE as s, FRAMEWORK_END_MARKER as t, FRAMEWORK_EXAMPLES_DIR as u, LEGACY_FRAMEWORK_ROOT as v, SKILL_NAME as w, LEGACY_SKILL_EXAMPLES_DIR as x, LEGACY_REPO_SKILL_EXAMPLES_DIR as y };
@@ -1,5 +1,5 @@
1
- import { S as LEGACY_SKILL_ROOT, a as BUNDLED_SKILL_ROOT, m as INSTALLED_SKILL_ROOTS, y as LEGACY_REPO_SKILL_ROOT } from "./repo-DkR12VUv.js";
2
- import { copyFileSync, cpSync, existsSync, mkdirSync, rmSync } from "node:fs";
1
+ import { C as LEGACY_SKILL_ROOT, D as SOURCE_INTEGRATION_MARKER, E as SOURCE_INTEGRATION_FILES, a as BUNDLED_SKILL_ROOT, b as LEGACY_REPO_SKILL_ROOT, h as INSTALLED_SKILL_ROOTS } from "./repo-Cc5U4DWT.js";
2
+ import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { dirname, join } from "node:path";
5
5
  //#region skills/first-tree/engine/runtime/installer.ts
@@ -48,4 +48,37 @@ function renderTemplateFile(frameworkRoot, templateName, targetRoot, targetPath)
48
48
  return true;
49
49
  }
50
50
  //#endregion
51
- export { resolveCanonicalSkillRoot as i, renderTemplateFile as n, resolveBundledPackageRoot as r, copyCanonicalSkill as t };
51
+ //#region skills/first-tree/engine/runtime/source-integration.ts
52
+ function buildSourceIntegrationLine(treeRepoName) {
53
+ return `${SOURCE_INTEGRATION_MARKER} Use the installed \`first-tree\` skill here; keep all Context Tree files only in the sibling dedicated \`${treeRepoName}\` repo/submodule and update that tree when decisions or constraints change.`;
54
+ }
55
+ function upsertSourceIntegrationFiles(root, treeRepoName) {
56
+ return SOURCE_INTEGRATION_FILES.map((file) => upsertSourceIntegrationFile(root, file, treeRepoName));
57
+ }
58
+ function upsertSourceIntegrationFile(root, file, treeRepoName) {
59
+ const fullPath = join(root, file);
60
+ const exists = existsSync(fullPath);
61
+ const nextLine = buildSourceIntegrationLine(treeRepoName);
62
+ const normalized = (exists ? readFileSync(fullPath, "utf-8") : null)?.replaceAll("\r\n", "\n") ?? "";
63
+ const lines = normalized === "" ? [] : normalized.split("\n");
64
+ const markerIndex = lines.findIndex((line) => line.startsWith(SOURCE_INTEGRATION_MARKER));
65
+ if (markerIndex >= 0) {
66
+ if (lines[markerIndex] === nextLine) return {
67
+ action: "unchanged",
68
+ file
69
+ };
70
+ lines[markerIndex] = nextLine;
71
+ } else {
72
+ if (lines.length > 0 && lines.at(-1) !== "") lines.push("");
73
+ lines.push(nextLine);
74
+ }
75
+ let nextText = lines.join("\n");
76
+ if (nextText !== "" && !nextText.endsWith("\n")) nextText += "\n";
77
+ writeFileSync(fullPath, nextText);
78
+ return {
79
+ action: exists ? "updated" : "created",
80
+ file
81
+ };
82
+ }
83
+ //#endregion
84
+ export { resolveCanonicalSkillRoot as a, resolveBundledPackageRoot as i, copyCanonicalSkill as n, renderTemplateFile as r, upsertSourceIntegrationFiles as t };
@@ -1,5 +1,5 @@
1
- import { S as LEGACY_SKILL_ROOT, T as installedSkillRootsDisplay, _ as LEGACY_FRAMEWORK_ROOT, d as FRAMEWORK_VERSION, f as FRAMEWORK_WORKFLOWS_DIR, h as LEGACY_AGENT_INSTRUCTIONS_FILE, i as AGENT_INSTRUCTIONS_TEMPLATE, n as Repo, p as INSTALLED_PROGRESS, r as AGENT_INSTRUCTIONS_FILE, s as CLAUDE_SKILL_ROOT, u as FRAMEWORK_TEMPLATES_DIR, w as SKILL_ROOT, y as LEGACY_REPO_SKILL_ROOT } from "./repo-DkR12VUv.js";
2
- import { i as resolveCanonicalSkillRoot, r as resolveBundledPackageRoot, t as copyCanonicalSkill } from "./installer-UgNasLjl.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
@@ -56,7 +56,8 @@ function formatUpgradeTaskList(repo, localVersion, packagedVersion, layout) {
56
56
  }
57
57
  function runUpgrade(repo, options) {
58
58
  const workingRepo = repo ?? new Repo();
59
- if (workingRepo.isLikelySourceRepo() && !workingRepo.looksLikeTreeRepo()) {
59
+ const workspaceOnlyIntegration = workingRepo.hasSourceWorkspaceIntegration() && !workingRepo.looksLikeTreeRepo();
60
+ if (workingRepo.isLikelySourceRepo() && !workingRepo.looksLikeTreeRepo() && !workspaceOnlyIntegration) {
60
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.");
61
62
  return 1;
62
63
  }
@@ -90,6 +91,28 @@ function runUpgrade(repo, options) {
90
91
  return 1;
91
92
  }
92
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
+ }
93
116
  if (layout === "skill" && missingInstalledRoots.length === 0 && packagedVersion === localVersion) {
94
117
  console.log(`Already up to date with the bundled skill (${FRAMEWORK_VERSION} = ${localVersion}).`);
95
118
  return 0;
@@ -1,4 +1,4 @@
1
- import { S as LEGACY_SKILL_ROOT, h as LEGACY_AGENT_INSTRUCTIONS_FILE, n as Repo, r as AGENT_INSTRUCTIONS_FILE, w as SKILL_ROOT } from "./repo-DkR12VUv.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.5",
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": {
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "type": "module",
21
21
  "bin": {
22
- "context-tree": "dist/cli.js"
22
+ "context-tree": "./dist/cli.js"
23
23
  },
24
24
  "imports": {
25
25
  "#skill/*": "./skills/first-tree/*",
@@ -28,6 +28,7 @@ repos.
28
28
  2. Read the user-facing reference that matches the task:
29
29
  - `references/onboarding.md`
30
30
  - `references/about.md`
31
+ - `references/source-workspace-installation.md`
31
32
  - `references/principles.md`
32
33
  - `references/ownership-and-naming.md`
33
34
  - `references/upgrade-contract.md`
@@ -58,16 +59,29 @@ repos.
58
59
 
59
60
  ### Working In A User Tree Repo
60
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.
61
66
  - `context-tree init` defaults to creating or reusing a sibling dedicated tree
62
- repo when invoked from a source/workspace repo. Use `--here` to initialize
63
- 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.
64
71
  - `context-tree init` installs this skill into the target tree repo and
65
72
  scaffolds `.agents/skills/first-tree/`, `.claude/skills/first-tree/`,
66
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.
67
78
  - `context-tree upgrade` refreshes the installed skill from the copy bundled
68
- with the currently running `first-tree` package. To pick up a newer
69
- framework, run a newer package version first. It also migrates older repos
70
- that still use `skills/first-tree/` or `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/`.
71
85
  - The user's tree content lives outside the skill; the skill only carries the
72
86
  reusable framework payload plus maintenance guidance.
73
87
  - The tree still stores decisions, constraints, and ownership; execution detail
@@ -108,6 +122,8 @@ repos.
108
122
  - `engine/`: canonical framework and CLI behavior
109
123
  - `tests/`: canonical unit and structure validation
110
124
  - `references/source-map.md`: canonical reading index
125
+ - `references/source-workspace-installation.md`: source/workspace install
126
+ contract
111
127
  - `references/maintainer-architecture.md`: source-repo architecture and
112
128
  invariants
113
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."
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.2.0
@@ -19,12 +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,
26
27
  installedSkillRootsDisplay,
28
+ SOURCE_INTEGRATION_MARKER,
27
29
  } from "#skill/engine/runtime/asset-loader.js";
30
+ import { upsertSourceIntegrationFiles } from "#skill/engine/runtime/source-integration.js";
28
31
 
29
32
  /**
30
33
  * The interactive prompt tool the agent should use to present choices.
@@ -34,8 +37,10 @@ import {
34
37
  export const INTERACTIVE_TOOL = "AskUserQuestion";
35
38
  export const INIT_USAGE = `usage: context-tree init [--here] [--tree-name NAME] [--tree-path PATH]
36
39
 
37
- By default, running \`context-tree init\` inside a source or workspace repo creates
38
- 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\`.
39
44
 
40
45
  Options:
41
46
  --here Initialize the current repo in place
@@ -62,6 +67,7 @@ const TEMPLATE_MAP: TemplateTarget[] = [
62
67
 
63
68
  interface TaskListContext {
64
69
  sourceRepoPath?: string;
70
+ sourceRepoName?: string;
65
71
  dedicatedTreeRepo?: boolean;
66
72
  }
67
73
 
@@ -106,6 +112,23 @@ export function formatTaskList(
106
112
  if (context.sourceRepoPath) {
107
113
  lines.push(`**Bootstrap source repo:** \`${context.sourceRepoPath}\``, "");
108
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
+ }
109
132
  lines.push(
110
133
  "When you publish this tree repo, keep it in the same GitHub organization" +
111
134
  " as the source repo unless you have a reason not to.",
@@ -182,9 +205,19 @@ export function runInit(repo?: Repo, options?: InitOptions): number {
182
205
  const taskListContext = initTarget.dedicatedTreeRepo
183
206
  ? {
184
207
  dedicatedTreeRepo: true,
208
+ sourceRepoName: sourceRepo.repoName(),
185
209
  sourceRepoPath: relativePathFrom(r.root, sourceRepo.root),
186
210
  }
187
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
+ };
188
221
 
189
222
  if (initTarget.dedicatedTreeRepo) {
190
223
  console.log(
@@ -196,17 +229,55 @@ export function runInit(repo?: Repo, options?: InitOptions): number {
196
229
  if (initTarget.createdGitRepo) {
197
230
  console.log(" Initialized a new git repo for the tree.");
198
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
+ );
199
239
  console.log();
200
240
  }
201
241
 
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
+
202
273
  if (!r.hasCurrentInstalledSkill()) {
203
274
  try {
204
- const sourceRoot = options?.sourceRoot ?? resolveBundledPackageRoot();
275
+ const resolvedSourceRoot = resolveSourceRoot();
205
276
  console.log(
206
277
  "Installing the framework skill bundled with this first-tree package...",
207
278
  );
208
279
  console.log("Installing skill and scaffolding...");
209
- installSkill(sourceRoot, r.root);
280
+ installSkill(resolvedSourceRoot, r.root);
210
281
  renderTemplates(r.root);
211
282
  console.log();
212
283
  } catch (err) {