first-tree 0.0.5 → 0.0.7
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.
- package/README.md +25 -7
- package/dist/cli.js +9 -6
- package/dist/{help-5-WG9QFm.js → help-BRO4mTG6.js} +1 -1
- package/dist/{init-CAq0Uhq6.js → init-BSs0ILp_.js} +49 -9
- package/dist/onboarding-BS8btkG4.js +2 -0
- package/dist/onboarding-D3hnxIie.js +10 -0
- package/dist/{repo-DkR12VUv.js → repo-0z7N9r17.js} +22 -16
- package/dist/source-integration-C2iiN4k_.js +80 -0
- package/dist/{upgrade-DYzuvv1k.js → upgrade-DvBdbph3.js} +26 -5
- package/dist/{verify-C0IUSkMZ.js → verify-DRt5mCqO.js} +7 -3
- package/package.json +2 -2
- package/skills/first-tree/SKILL.md +31 -5
- package/skills/first-tree/agents/openai.yaml +1 -1
- package/skills/first-tree/engine/init.ts +88 -5
- package/skills/first-tree/engine/repo.ts +38 -15
- package/skills/first-tree/engine/runtime/adapters.ts +0 -2
- package/skills/first-tree/engine/runtime/asset-loader.ts +6 -36
- package/skills/first-tree/engine/runtime/installer.ts +0 -2
- package/skills/first-tree/engine/runtime/source-integration.ts +80 -0
- package/skills/first-tree/engine/upgrade.ts +68 -12
- package/skills/first-tree/engine/validators/nodes.ts +2 -11
- package/skills/first-tree/engine/verify.ts +7 -0
- package/skills/first-tree/references/maintainer-architecture.md +4 -0
- package/skills/first-tree/references/maintainer-thin-cli.md +3 -0
- package/skills/first-tree/references/onboarding.md +33 -5
- package/skills/first-tree/references/principles.md +97 -57
- package/skills/first-tree/references/source-map.md +1 -0
- package/skills/first-tree/references/source-workspace-installation.md +64 -0
- package/skills/first-tree/references/upgrade-contract.md +23 -12
- package/skills/first-tree/scripts/check-skill-sync.sh +2 -1
- package/skills/first-tree/tests/asset-loader.test.ts +0 -24
- package/skills/first-tree/tests/helpers.ts +0 -14
- package/skills/first-tree/tests/init.test.ts +67 -0
- package/skills/first-tree/tests/repo.test.ts +20 -25
- package/skills/first-tree/tests/skill-artifacts.test.ts +43 -1
- package/skills/first-tree/tests/thin-cli.test.ts +5 -0
- package/skills/first-tree/tests/upgrade.test.ts +38 -23
- package/skills/first-tree/tests/verify.test.ts +18 -0
- package/dist/installer-UgNasLjl.js +0 -51
- package/dist/onboarding-3zYUeYQb.js +0 -2
- 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`
|
|
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
|
|
@@ -52,16 +53,32 @@ git init
|
|
|
52
53
|
context-tree init --here
|
|
53
54
|
```
|
|
54
55
|
|
|
56
|
+
Only use `--here` after you have already switched into the dedicated tree repo.
|
|
57
|
+
Do not use it inside the source/workspace repo unless you intentionally want
|
|
58
|
+
that repo itself to become the Context Tree.
|
|
59
|
+
|
|
55
60
|
- `context-tree init` installs `.agents/skills/first-tree/` and
|
|
56
|
-
`.claude/skills/first-tree
|
|
57
|
-
`
|
|
61
|
+
`.claude/skills/first-tree/` in the current source/workspace repo, appends a
|
|
62
|
+
single `FIRST-TREE-SOURCE-INTEGRATION:` line to root `AGENTS.md` and
|
|
63
|
+
`CLAUDE.md`, then creates `NODE.md`, tree-scoped `AGENTS.md`,
|
|
64
|
+
`members/NODE.md`, and a checklist in the dedicated tree repo at
|
|
58
65
|
`.agents/skills/first-tree/progress.md`.
|
|
66
|
+
- Never create `NODE.md`, `members/`, or tree-scoped `AGENTS.md` in the
|
|
67
|
+
source/workspace repo. Those files live only in the dedicated `*-context`
|
|
68
|
+
repo.
|
|
69
|
+
- After creating the dedicated tree repo, prefer pushing it to the same GitHub
|
|
70
|
+
organization as the source repo, add it back as a git submodule, and open a
|
|
71
|
+
PR to the source/workspace repo's default branch instead of merging
|
|
72
|
+
automatically.
|
|
59
73
|
- `context-tree verify` checks both the progress checklist and deterministic
|
|
60
74
|
tree validation. It is expected to fail until the required onboarding tasks
|
|
61
75
|
are complete.
|
|
62
76
|
- `context-tree upgrade` refreshes the installed skill from the currently
|
|
63
|
-
running `first-tree` npm package.
|
|
64
|
-
|
|
77
|
+
running `first-tree` npm package. In a source/workspace repo it refreshes
|
|
78
|
+
only the local installed skill plus the
|
|
79
|
+
`FIRST-TREE-SOURCE-INTEGRATION:` line; use `--tree-path` to upgrade the
|
|
80
|
+
dedicated tree repo. To force the newest published package for a one-off
|
|
81
|
+
upgrade, run `npx first-tree@latest upgrade`.
|
|
65
82
|
|
|
66
83
|
The package carries the bundled canonical skill, so `init` and `upgrade`
|
|
67
84
|
install from the package payload instead of cloning this source repo at
|
|
@@ -71,9 +88,9 @@ runtime.
|
|
|
71
88
|
|
|
72
89
|
| Command | What it does |
|
|
73
90
|
| --- | --- |
|
|
74
|
-
| `context-tree init` |
|
|
91
|
+
| `context-tree init` | Install source/workspace integration locally and create or refresh a dedicated context tree repo; use `--here` only when you are already inside the dedicated tree repo |
|
|
75
92
|
| `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
|
|
93
|
+
| `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
94
|
| `context-tree help onboarding` | Print the onboarding guide |
|
|
78
95
|
| `context-tree --help` | Show the available commands |
|
|
79
96
|
| `context-tree --version` | Print the installed CLI version |
|
|
@@ -134,6 +151,7 @@ live in `skills/first-tree/`.
|
|
|
134
151
|
|
|
135
152
|
- User-facing overview: `skills/first-tree/references/about.md`
|
|
136
153
|
- User onboarding: `skills/first-tree/references/onboarding.md`
|
|
154
|
+
- Source/workspace install contract: `skills/first-tree/references/source-workspace-installation.md`
|
|
137
155
|
- Maintainer entrypoint: `skills/first-tree/references/source-map.md`
|
|
138
156
|
|
|
139
157
|
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
|
|
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\`)
|
|
@@ -18,9 +18,12 @@ Options:
|
|
|
18
18
|
|
|
19
19
|
Common examples:
|
|
20
20
|
context-tree init
|
|
21
|
-
context-tree init --here
|
|
21
|
+
mkdir my-org-context && cd my-org-context && git init && context-tree init --here
|
|
22
22
|
context-tree verify --tree-path ../my-org-context
|
|
23
23
|
context-tree upgrade --tree-path ../my-org-context
|
|
24
|
+
|
|
25
|
+
Note:
|
|
26
|
+
\`--here\` is for when the current repo is already the dedicated tree repo.
|
|
24
27
|
`;
|
|
25
28
|
function isDirectExecution(argv1, metaUrl = import.meta.url) {
|
|
26
29
|
if (argv1 === void 0) return false;
|
|
@@ -44,18 +47,18 @@ async function runCli(args, output = console.log) {
|
|
|
44
47
|
const command = args[0];
|
|
45
48
|
switch (command) {
|
|
46
49
|
case "init": {
|
|
47
|
-
const { runInit } = await import("./init-
|
|
50
|
+
const { runInit } = await import("./init-BSs0ILp_.js");
|
|
48
51
|
return runInit(args.slice(1));
|
|
49
52
|
}
|
|
50
53
|
case "verify": {
|
|
51
|
-
const { runVerify } = await import("./verify-
|
|
54
|
+
const { runVerify } = await import("./verify-DRt5mCqO.js");
|
|
52
55
|
return runVerify(args.slice(1));
|
|
53
56
|
}
|
|
54
57
|
case "upgrade": {
|
|
55
|
-
const { runUpgrade } = await import("./upgrade-
|
|
58
|
+
const { runUpgrade } = await import("./upgrade-DvBdbph3.js");
|
|
56
59
|
return runUpgrade(args.slice(1));
|
|
57
60
|
}
|
|
58
|
-
case "help": return (await import("./help-
|
|
61
|
+
case "help": return (await import("./help-BRO4mTG6.js")).runHelp(args.slice(1), write);
|
|
59
62
|
default:
|
|
60
63
|
write(`Unknown command: ${command}`);
|
|
61
64
|
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-
|
|
15
|
+
const { runOnboarding } = await import("./onboarding-BS8btkG4.js");
|
|
16
16
|
return runOnboarding(output);
|
|
17
17
|
}
|
|
18
18
|
default:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { T as installedSkillRootsDisplay,
|
|
2
|
-
import { n as onboarding_default } from "./onboarding-
|
|
3
|
-
import { n as
|
|
1
|
+
import { T 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, w as SOURCE_INTEGRATION_MARKER, y as LEGACY_REPO_SKILL_EXAMPLES_DIR } from "./repo-0z7N9r17.js";
|
|
2
|
+
import { n as onboarding_default } from "./onboarding-D3hnxIie.js";
|
|
3
|
+
import { i as resolveBundledPackageRoot, n as copyCanonicalSkill, r as renderTemplateFile, t as upsertSourceIntegrationFiles } from "./source-integration-C2iiN4k_.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";
|
|
@@ -53,7 +53,6 @@ function claudeCodeExampleCandidates() {
|
|
|
53
53
|
join(CLAUDE_FRAMEWORK_EXAMPLES_DIR, "claude-code"),
|
|
54
54
|
join(FRAMEWORK_EXAMPLES_DIR, "claude-code"),
|
|
55
55
|
join(LEGACY_REPO_SKILL_EXAMPLES_DIR, "claude-code"),
|
|
56
|
-
join(LEGACY_SKILL_EXAMPLES_DIR, "claude-code"),
|
|
57
56
|
join(LEGACY_EXAMPLES_DIR, "claude-code")
|
|
58
57
|
];
|
|
59
58
|
}
|
|
@@ -205,11 +204,16 @@ function evaluateAll(repo) {
|
|
|
205
204
|
const INTERACTIVE_TOOL = "AskUserQuestion";
|
|
206
205
|
const INIT_USAGE = `usage: context-tree init [--here] [--tree-name NAME] [--tree-path PATH]
|
|
207
206
|
|
|
208
|
-
By default, running \`context-tree init\` inside a source or workspace repo
|
|
209
|
-
|
|
207
|
+
By default, running \`context-tree init\` inside a source or workspace repo installs
|
|
208
|
+
the first-tree skill in the current repo, updates \`AGENTS.md\` and \`CLAUDE.md\`
|
|
209
|
+
with a \`${SOURCE_INTEGRATION_MARKER}\` line, and creates a sibling dedicated tree
|
|
210
|
+
repo named \`<repo>-context\`.
|
|
211
|
+
|
|
212
|
+
Do not use \`--here\` inside a source/workspace repo unless you explicitly want
|
|
213
|
+
that repo itself to become the Context Tree.
|
|
210
214
|
|
|
211
215
|
Options:
|
|
212
|
-
--here Initialize the current repo in place
|
|
216
|
+
--here Initialize the current repo in place after you are already in the dedicated tree repo
|
|
213
217
|
--tree-name NAME Name the dedicated sibling tree repo to create
|
|
214
218
|
--tree-path PATH Use an explicit tree repo path
|
|
215
219
|
--help Show this help message
|
|
@@ -246,6 +250,14 @@ function formatTaskList(groups, context) {
|
|
|
246
250
|
if (context?.dedicatedTreeRepo) {
|
|
247
251
|
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
252
|
if (context.sourceRepoPath) lines.push(`**Bootstrap source repo:** \`${context.sourceRepoPath}\``, "");
|
|
253
|
+
if (context.sourceRepoName) {
|
|
254
|
+
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.`, "");
|
|
255
|
+
lines.push("## Source Workspace Workflow");
|
|
256
|
+
lines.push(`- [ ] Publish this dedicated tree repo to the same GitHub organization as \`${context.sourceRepoName}\``);
|
|
257
|
+
lines.push(`- [ ] Add this tree repo back to \`${context.sourceRepoName}\` as a git submodule after the remote exists`);
|
|
258
|
+
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`);
|
|
259
|
+
lines.push("");
|
|
260
|
+
}
|
|
249
261
|
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
262
|
}
|
|
251
263
|
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`);
|
|
@@ -281,22 +293,50 @@ function runInit(repo, options) {
|
|
|
281
293
|
return 1;
|
|
282
294
|
}
|
|
283
295
|
const r = initTarget.repo;
|
|
296
|
+
if (options?.here && sourceRepo.isLikelySourceRepo() && !sourceRepo.looksLikeTreeRepo()) {
|
|
297
|
+
console.log(`Warning: \`context-tree init --here\` is initializing this source/workspace repo in place. This will create \`NODE.md\`, \`members/\`, and tree-scoped ${AGENT_INSTRUCTIONS_FILE} here. Use plain \`context-tree init\` to create a sibling dedicated tree repo instead.`);
|
|
298
|
+
console.log();
|
|
299
|
+
}
|
|
284
300
|
const taskListContext = initTarget.dedicatedTreeRepo ? {
|
|
285
301
|
dedicatedTreeRepo: true,
|
|
302
|
+
sourceRepoName: sourceRepo.repoName(),
|
|
286
303
|
sourceRepoPath: relativePathFrom(r.root, sourceRepo.root)
|
|
287
304
|
} : void 0;
|
|
305
|
+
let sourceRoot = null;
|
|
306
|
+
const resolveSourceRoot = () => {
|
|
307
|
+
if (sourceRoot !== null) return sourceRoot;
|
|
308
|
+
sourceRoot = options?.sourceRoot ?? resolveBundledPackageRoot();
|
|
309
|
+
return sourceRoot;
|
|
310
|
+
};
|
|
288
311
|
if (initTarget.dedicatedTreeRepo) {
|
|
289
312
|
console.log("Recommended workflow: keep the Context Tree in a dedicated repo separate from your source/workspace repo.");
|
|
290
313
|
console.log(` Source repo: ${sourceRepo.root}`);
|
|
291
314
|
console.log(` Tree repo: ${r.root}`);
|
|
292
315
|
if (initTarget.createdGitRepo) console.log(" Initialized a new git repo for the tree.");
|
|
316
|
+
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}.`);
|
|
317
|
+
console.log(` Never add NODE.md, members/, or tree-scoped ${AGENT_INSTRUCTIONS_FILE} to the source/workspace repo.`);
|
|
318
|
+
console.log();
|
|
319
|
+
}
|
|
320
|
+
if (initTarget.dedicatedTreeRepo) try {
|
|
321
|
+
const resolvedSourceRoot = resolveSourceRoot();
|
|
322
|
+
if (!sourceRepo.hasCurrentInstalledSkill()) {
|
|
323
|
+
console.log("Installing the first-tree skill into the source/workspace repo...");
|
|
324
|
+
installSkill(resolvedSourceRoot, sourceRepo.root);
|
|
325
|
+
}
|
|
326
|
+
const changedFiles = upsertSourceIntegrationFiles(sourceRepo.root, r.repoName()).filter((update) => update.action !== "unchanged").map((update) => update.file);
|
|
327
|
+
if (changedFiles.length > 0) console.log(` Updated source/workspace instructions in ${changedFiles.map((file) => `\`${file}\``).join(" and ")}`);
|
|
328
|
+
else console.log(` Source/workspace instructions already contain ${SOURCE_INTEGRATION_MARKER}`);
|
|
293
329
|
console.log();
|
|
330
|
+
} catch (err) {
|
|
331
|
+
const message = err instanceof Error ? err.message : "unknown error";
|
|
332
|
+
console.error(`Error: ${message}`);
|
|
333
|
+
return 1;
|
|
294
334
|
}
|
|
295
335
|
if (!r.hasCurrentInstalledSkill()) try {
|
|
296
|
-
const
|
|
336
|
+
const resolvedSourceRoot = resolveSourceRoot();
|
|
297
337
|
console.log("Installing the framework skill bundled with this first-tree package...");
|
|
298
338
|
console.log("Installing skill and scaffolding...");
|
|
299
|
-
installSkill(
|
|
339
|
+
installSkill(resolvedSourceRoot, r.root);
|
|
300
340
|
renderTemplates(r.root);
|
|
301
341
|
console.log();
|
|
302
342
|
} 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\nOnly use `--here` after you have already switched into the dedicated tree repo.\nDo not use it inside the source/workspace repo unless you intentionally want\nthat repo itself to become the Context Tree.\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` only when you are already inside the dedicated tree repo. |\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/` 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 };
|
|
@@ -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");
|
|
@@ -43,15 +46,6 @@ join(LEGACY_REPO_SKILL_ASSET_ROOT, "prompts");
|
|
|
43
46
|
const LEGACY_REPO_SKILL_EXAMPLES_DIR = join(LEGACY_REPO_SKILL_ASSET_ROOT, "examples");
|
|
44
47
|
join(LEGACY_REPO_SKILL_ASSET_ROOT, "helpers");
|
|
45
48
|
const LEGACY_REPO_SKILL_PROGRESS = join(LEGACY_REPO_SKILL_ROOT, "progress.md");
|
|
46
|
-
const LEGACY_SKILL_NAME = "first-tree-cli-framework";
|
|
47
|
-
const LEGACY_SKILL_ROOT = join("skills", LEGACY_SKILL_NAME);
|
|
48
|
-
const LEGACY_SKILL_ASSET_ROOT = join(LEGACY_SKILL_ROOT, "assets", "framework");
|
|
49
|
-
const LEGACY_SKILL_VERSION = join(LEGACY_SKILL_ASSET_ROOT, "VERSION");
|
|
50
|
-
join(LEGACY_SKILL_ASSET_ROOT, "templates");
|
|
51
|
-
join(LEGACY_SKILL_ASSET_ROOT, "workflows");
|
|
52
|
-
join(LEGACY_SKILL_ASSET_ROOT, "prompts");
|
|
53
|
-
const LEGACY_SKILL_EXAMPLES_DIR = join(LEGACY_SKILL_ASSET_ROOT, "examples");
|
|
54
|
-
const LEGACY_SKILL_PROGRESS = join(LEGACY_SKILL_ROOT, "progress.md");
|
|
55
49
|
const LEGACY_FRAMEWORK_ROOT = ".context-tree";
|
|
56
50
|
const LEGACY_VERSION = join(LEGACY_FRAMEWORK_ROOT, "VERSION");
|
|
57
51
|
const LEGACY_PROGRESS = join(LEGACY_FRAMEWORK_ROOT, "progress.md");
|
|
@@ -78,7 +72,6 @@ function frameworkVersionCandidates() {
|
|
|
78
72
|
FRAMEWORK_VERSION,
|
|
79
73
|
CLAUDE_FRAMEWORK_VERSION,
|
|
80
74
|
LEGACY_REPO_SKILL_VERSION,
|
|
81
|
-
LEGACY_SKILL_VERSION,
|
|
82
75
|
LEGACY_VERSION
|
|
83
76
|
];
|
|
84
77
|
}
|
|
@@ -87,7 +80,6 @@ function progressFileCandidates() {
|
|
|
87
80
|
INSTALLED_PROGRESS,
|
|
88
81
|
CLAUDE_INSTALLED_PROGRESS,
|
|
89
82
|
LEGACY_REPO_SKILL_PROGRESS,
|
|
90
|
-
LEGACY_SKILL_PROGRESS,
|
|
91
83
|
LEGACY_PROGRESS
|
|
92
84
|
];
|
|
93
85
|
}
|
|
@@ -102,7 +94,6 @@ function detectFrameworkLayout(root) {
|
|
|
102
94
|
if (pathExists(root, FRAMEWORK_VERSION)) return "skill";
|
|
103
95
|
if (pathExists(root, CLAUDE_FRAMEWORK_VERSION)) return "claude-skill";
|
|
104
96
|
if (pathExists(root, LEGACY_REPO_SKILL_VERSION)) return "legacy-repo-skill";
|
|
105
|
-
if (pathExists(root, LEGACY_SKILL_VERSION)) return "legacy-skill";
|
|
106
97
|
if (pathExists(root, LEGACY_VERSION)) return "legacy";
|
|
107
98
|
return null;
|
|
108
99
|
}
|
|
@@ -112,7 +103,9 @@ const FRONTMATTER_RE = /^---\s*\n(.*?)\n---/s;
|
|
|
112
103
|
const OWNERS_RE = /^owners:\s*\[([^\]]*)\]/m;
|
|
113
104
|
const TITLE_RE = /^title:\s*['"]?(.+?)['"]?\s*$/m;
|
|
114
105
|
const EMPTY_REPO_ENTRY_ALLOWLIST = new Set([
|
|
106
|
+
".agents",
|
|
115
107
|
".DS_Store",
|
|
108
|
+
".claude",
|
|
116
109
|
".editorconfig",
|
|
117
110
|
".gitattributes",
|
|
118
111
|
".github",
|
|
@@ -256,7 +249,6 @@ var Repo = class {
|
|
|
256
249
|
preferredProgressPath() {
|
|
257
250
|
const layout = this.frameworkLayout();
|
|
258
251
|
if (layout === "legacy") return LEGACY_PROGRESS;
|
|
259
|
-
if (layout === "legacy-skill") return LEGACY_SKILL_PROGRESS;
|
|
260
252
|
if (layout === "legacy-repo-skill") return LEGACY_REPO_SKILL_PROGRESS;
|
|
261
253
|
if (layout === "claude-skill") return CLAUDE_INSTALLED_PROGRESS;
|
|
262
254
|
return INSTALLED_PROGRESS;
|
|
@@ -264,7 +256,6 @@ var Repo = class {
|
|
|
264
256
|
frameworkVersionPath() {
|
|
265
257
|
const layout = this.frameworkLayout();
|
|
266
258
|
if (layout === "legacy") return LEGACY_VERSION;
|
|
267
|
-
if (layout === "legacy-skill") return LEGACY_SKILL_VERSION;
|
|
268
259
|
if (layout === "legacy-repo-skill") return LEGACY_REPO_SKILL_VERSION;
|
|
269
260
|
if (layout === "claude-skill") return CLAUDE_FRAMEWORK_VERSION;
|
|
270
261
|
return FRAMEWORK_VERSION;
|
|
@@ -291,6 +282,15 @@ var Repo = class {
|
|
|
291
282
|
if (text === null) return false;
|
|
292
283
|
return text.includes("<!-- BEGIN CONTEXT-TREE FRAMEWORK") && text.includes("<!-- END CONTEXT-TREE FRAMEWORK -->");
|
|
293
284
|
}
|
|
285
|
+
hasSourceIntegrationFile(relPath) {
|
|
286
|
+
return this.fileContains(relPath, SOURCE_INTEGRATION_MARKER);
|
|
287
|
+
}
|
|
288
|
+
hasSourceWorkspaceIntegration() {
|
|
289
|
+
return SOURCE_INTEGRATION_FILES.some((file) => this.hasSourceIntegrationFile(file));
|
|
290
|
+
}
|
|
291
|
+
hasTreeContent() {
|
|
292
|
+
return this.progressPath() !== null || this.hasAgentInstructionsMarkers() || this.pathExists("members/NODE.md") || this.frontmatter("NODE.md") !== null;
|
|
293
|
+
}
|
|
294
294
|
hasMembers() {
|
|
295
295
|
const membersDir = join(this.root, "members");
|
|
296
296
|
try {
|
|
@@ -338,13 +338,19 @@ var Repo = class {
|
|
|
338
338
|
}
|
|
339
339
|
looksLikeTreeRepo() {
|
|
340
340
|
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
|
-
|
|
341
|
+
if (this.hasTreeContent()) return true;
|
|
342
|
+
if (this.hasFramework() && this.hasSourceWorkspaceIntegration()) return false;
|
|
343
|
+
if (this.hasFramework()) return !this.hasLikelySourceRepoSignals();
|
|
344
|
+
return false;
|
|
342
345
|
}
|
|
343
346
|
isLikelyEmptyRepo() {
|
|
344
347
|
return this.topLevelEntries().filter((entry) => !EMPTY_REPO_ENTRY_ALLOWLIST.has(entry)).length === 0;
|
|
345
348
|
}
|
|
346
349
|
isLikelySourceRepo() {
|
|
347
350
|
if (this.looksLikeTreeRepo()) return false;
|
|
351
|
+
return this.hasLikelySourceRepoSignals();
|
|
352
|
+
}
|
|
353
|
+
hasLikelySourceRepoSignals() {
|
|
348
354
|
const entries = this.topLevelEntries().filter((entry) => !EMPTY_REPO_ENTRY_ALLOWLIST.has(entry));
|
|
349
355
|
if (entries.length === 0) return false;
|
|
350
356
|
let directoryCount = 0;
|
|
@@ -366,4 +372,4 @@ function isDirectory(root, relPath) {
|
|
|
366
372
|
}
|
|
367
373
|
}
|
|
368
374
|
//#endregion
|
|
369
|
-
export {
|
|
375
|
+
export { SOURCE_INTEGRATION_FILES as C, SKILL_ROOT as S, installedSkillRootsDisplay 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, SOURCE_INTEGRATION_MARKER as w, SKILL_NAME as x, LEGACY_REPO_SKILL_EXAMPLES_DIR as y };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { C as SOURCE_INTEGRATION_FILES, a as BUNDLED_SKILL_ROOT, b as LEGACY_REPO_SKILL_ROOT, h as INSTALLED_SKILL_ROOTS, w as SOURCE_INTEGRATION_MARKER } from "./repo-0z7N9r17.js";
|
|
2
|
+
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
//#region skills/first-tree/engine/runtime/installer.ts
|
|
6
|
+
function resolveBundledPackageRoot(startUrl = import.meta.url) {
|
|
7
|
+
let dir = dirname(fileURLToPath(startUrl));
|
|
8
|
+
while (true) {
|
|
9
|
+
if (existsSync(join(dir, "package.json")) && existsSync(join(dir, BUNDLED_SKILL_ROOT, "SKILL.md"))) return dir;
|
|
10
|
+
const parent = dirname(dir);
|
|
11
|
+
if (parent === dir) break;
|
|
12
|
+
dir = parent;
|
|
13
|
+
}
|
|
14
|
+
throw new Error("Could not locate the bundled `first-tree` package root. Reinstall the package and try again.");
|
|
15
|
+
}
|
|
16
|
+
function resolveCanonicalSkillRoot(sourceRoot) {
|
|
17
|
+
const directSkillRoot = sourceRoot;
|
|
18
|
+
if (existsSync(join(directSkillRoot, "SKILL.md")) && existsSync(join(directSkillRoot, "assets", "framework", "VERSION"))) return directSkillRoot;
|
|
19
|
+
const nestedSkillRoot = join(sourceRoot, BUNDLED_SKILL_ROOT);
|
|
20
|
+
if (existsSync(join(nestedSkillRoot, "SKILL.md")) && existsSync(join(nestedSkillRoot, "assets", "framework", "VERSION"))) return nestedSkillRoot;
|
|
21
|
+
throw new Error(`Canonical skill not found under ${sourceRoot}. Reinstall the \`first-tree\` package and try again.`);
|
|
22
|
+
}
|
|
23
|
+
function copyCanonicalSkill(sourceRoot, targetRoot) {
|
|
24
|
+
const src = resolveCanonicalSkillRoot(sourceRoot);
|
|
25
|
+
for (const relPath of [...INSTALLED_SKILL_ROOTS, LEGACY_REPO_SKILL_ROOT]) {
|
|
26
|
+
const fullPath = join(targetRoot, relPath);
|
|
27
|
+
if (existsSync(fullPath)) rmSync(fullPath, {
|
|
28
|
+
recursive: true,
|
|
29
|
+
force: true
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
for (const relPath of INSTALLED_SKILL_ROOTS) {
|
|
33
|
+
const dst = join(targetRoot, relPath);
|
|
34
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
35
|
+
cpSync(src, dst, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function renderTemplateFile(frameworkRoot, templateName, targetRoot, targetPath) {
|
|
39
|
+
const src = join(frameworkRoot, "templates", templateName);
|
|
40
|
+
const dst = join(targetRoot, targetPath);
|
|
41
|
+
if (existsSync(dst) || !existsSync(src)) return false;
|
|
42
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
43
|
+
copyFileSync(src, dst);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region skills/first-tree/engine/runtime/source-integration.ts
|
|
48
|
+
function buildSourceIntegrationLine(treeRepoName) {
|
|
49
|
+
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.`;
|
|
50
|
+
}
|
|
51
|
+
function upsertSourceIntegrationFiles(root, treeRepoName) {
|
|
52
|
+
return SOURCE_INTEGRATION_FILES.map((file) => upsertSourceIntegrationFile(root, file, treeRepoName));
|
|
53
|
+
}
|
|
54
|
+
function upsertSourceIntegrationFile(root, file, treeRepoName) {
|
|
55
|
+
const fullPath = join(root, file);
|
|
56
|
+
const exists = existsSync(fullPath);
|
|
57
|
+
const nextLine = buildSourceIntegrationLine(treeRepoName);
|
|
58
|
+
const normalized = (exists ? readFileSync(fullPath, "utf-8") : null)?.replaceAll("\r\n", "\n") ?? "";
|
|
59
|
+
const lines = normalized === "" ? [] : normalized.split("\n");
|
|
60
|
+
const markerIndex = lines.findIndex((line) => line.startsWith(SOURCE_INTEGRATION_MARKER));
|
|
61
|
+
if (markerIndex >= 0) {
|
|
62
|
+
if (lines[markerIndex] === nextLine) return {
|
|
63
|
+
action: "unchanged",
|
|
64
|
+
file
|
|
65
|
+
};
|
|
66
|
+
lines[markerIndex] = nextLine;
|
|
67
|
+
} else {
|
|
68
|
+
if (lines.length > 0 && lines.at(-1) !== "") lines.push("");
|
|
69
|
+
lines.push(nextLine);
|
|
70
|
+
}
|
|
71
|
+
let nextText = lines.join("\n");
|
|
72
|
+
if (nextText !== "" && !nextText.endsWith("\n")) nextText += "\n";
|
|
73
|
+
writeFileSync(fullPath, nextText);
|
|
74
|
+
return {
|
|
75
|
+
action: exists ? "updated" : "created",
|
|
76
|
+
file
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
export { resolveCanonicalSkillRoot as a, resolveBundledPackageRoot as i, copyCanonicalSkill as n, renderTemplateFile as r, upsertSourceIntegrationFiles as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as
|
|
2
|
-
import {
|
|
1
|
+
import { S as SKILL_ROOT, T as installedSkillRootsDisplay, 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, w as SOURCE_INTEGRATION_MARKER } from "./repo-0z7N9r17.js";
|
|
2
|
+
import { a as resolveCanonicalSkillRoot, i as resolveBundledPackageRoot, n as copyCanonicalSkill, t as upsertSourceIntegrationFiles } from "./source-integration-C2iiN4k_.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
|
|
@@ -46,7 +46,6 @@ function formatUpgradeTaskList(repo, localVersion, packagedVersion, layout) {
|
|
|
46
46
|
const migrationTasks = [];
|
|
47
47
|
if (layout === "legacy") migrationTasks.push("- [ ] Remove any stale `.context-tree/` references from repo-specific docs, scripts, or workflow files");
|
|
48
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`, "");
|
|
49
|
-
if (layout === "legacy-skill") migrationTasks.push(`- [ ] Remove any stale \`${LEGACY_SKILL_ROOT}/\` references from repo-specific docs, scripts, workflow files, or agent config`);
|
|
50
49
|
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`);
|
|
51
50
|
else if (repo.hasLegacyAgentInstructionsFile()) migrationTasks.push(`- [ ] Rename \`${LEGACY_AGENT_INSTRUCTIONS_FILE}\` to \`${AGENT_INSTRUCTIONS_FILE}\` to use the canonical agent instructions filename`);
|
|
52
51
|
if (migrationTasks.length > 0) lines.push("## Migration", ...migrationTasks, "");
|
|
@@ -56,7 +55,8 @@ function formatUpgradeTaskList(repo, localVersion, packagedVersion, layout) {
|
|
|
56
55
|
}
|
|
57
56
|
function runUpgrade(repo, options) {
|
|
58
57
|
const workingRepo = repo ?? new Repo();
|
|
59
|
-
|
|
58
|
+
const workspaceOnlyIntegration = workingRepo.hasSourceWorkspaceIntegration() && !workingRepo.looksLikeTreeRepo();
|
|
59
|
+
if (workingRepo.isLikelySourceRepo() && !workingRepo.looksLikeTreeRepo() && !workspaceOnlyIntegration) {
|
|
60
60
|
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
61
|
return 1;
|
|
62
62
|
}
|
|
@@ -90,6 +90,28 @@ function runUpgrade(repo, options) {
|
|
|
90
90
|
return 1;
|
|
91
91
|
}
|
|
92
92
|
const missingInstalledRoots = workingRepo.missingInstalledSkillRoots();
|
|
93
|
+
const sourceRepoTreePathHint = `../${workingRepo.repoName()}-context`;
|
|
94
|
+
if (workspaceOnlyIntegration) {
|
|
95
|
+
if (layout === "skill" && missingInstalledRoots.length === 0 && packagedVersion === localVersion) {
|
|
96
|
+
const changedFiles = upsertSourceIntegrationFiles(workingRepo.root, `${workingRepo.repoName()}-context`).filter((update) => update.action !== "unchanged").map((update) => update.file);
|
|
97
|
+
if (changedFiles.length === 0) {
|
|
98
|
+
console.log(`Already up to date with the bundled skill (${FRAMEWORK_VERSION} = ${localVersion}).`);
|
|
99
|
+
console.log(`This repo only carries source/workspace integration. Upgrade the dedicated tree repo separately with \`context-tree upgrade --tree-path ${sourceRepoTreePathHint}\`.`);
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
console.log(`Already up to date with the bundled skill (${FRAMEWORK_VERSION} = ${localVersion}).`);
|
|
103
|
+
console.log(`Updated the ${SOURCE_INTEGRATION_MARKER} marker lines in ${changedFiles.map((file) => `\`${file}\``).join(" and ")}.`);
|
|
104
|
+
console.log(`This repo only carries source/workspace integration. Upgrade the dedicated tree repo separately with \`context-tree upgrade --tree-path ${sourceRepoTreePathHint}\`.`);
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
copyCanonicalSkill(sourceRoot, workingRepo.root);
|
|
108
|
+
const changedFiles = upsertSourceIntegrationFiles(workingRepo.root, `${workingRepo.repoName()}-context`).filter((update) => update.action !== "unchanged").map((update) => update.file);
|
|
109
|
+
console.log(`Refreshed ${installedSkillRootsDisplay()} in this source/workspace repo.`);
|
|
110
|
+
if (changedFiles.length > 0) console.log(`Updated the ${SOURCE_INTEGRATION_MARKER} marker lines in ${changedFiles.map((file) => `\`${file}\``).join(" and ")}.`);
|
|
111
|
+
else console.log(`The ${SOURCE_INTEGRATION_MARKER} marker lines in ${AGENT_INSTRUCTIONS_FILE} and ${CLAUDE_INSTRUCTIONS_FILE} were already current.`);
|
|
112
|
+
console.log(`This repo is not the Context Tree. Upgrade the dedicated tree repo separately with \`context-tree upgrade --tree-path ${sourceRepoTreePathHint}\`.`);
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
93
115
|
if (layout === "skill" && missingInstalledRoots.length === 0 && packagedVersion === localVersion) {
|
|
94
116
|
console.log(`Already up to date with the bundled skill (${FRAMEWORK_VERSION} = ${localVersion}).`);
|
|
95
117
|
return 0;
|
|
@@ -102,7 +124,6 @@ function runUpgrade(repo, options) {
|
|
|
102
124
|
});
|
|
103
125
|
console.log(`Migrated legacy .context-tree/ layout to ${installedSkillRootsDisplay()}.`);
|
|
104
126
|
} else if (layout === "legacy-repo-skill") console.log(`Migrated legacy ${LEGACY_REPO_SKILL_ROOT}/ layout to ${installedSkillRootsDisplay()}.`);
|
|
105
|
-
else if (layout === "legacy-skill") console.log(`Migrated ${LEGACY_SKILL_ROOT}/ to ${installedSkillRootsDisplay()}.`);
|
|
106
127
|
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.`);
|
|
107
128
|
else console.log(`Refreshed ${installedSkillRootsDisplay()} from the bundled first-tree package.`);
|
|
108
129
|
const output = formatUpgradeTaskList(workingRepo, localVersion, packagedVersion, layout);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as
|
|
1
|
+
import { S as SKILL_ROOT, g as LEGACY_AGENT_INSTRUCTIONS_FILE, n as Repo, r as AGENT_INSTRUCTIONS_FILE, x as SKILL_NAME } from "./repo-0z7N9r17.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
|
|
@@ -186,14 +186,14 @@ function rel(path) {
|
|
|
186
186
|
return relative(treeRoot, path);
|
|
187
187
|
}
|
|
188
188
|
function isInstalledSkillPath(relPath) {
|
|
189
|
-
return relPath === SKILL_ROOT || relPath.startsWith(`${SKILL_ROOT}/`)
|
|
189
|
+
return relPath === SKILL_ROOT || relPath.startsWith(`${SKILL_ROOT}/`);
|
|
190
190
|
}
|
|
191
191
|
function isFrameworkContainerDir(relPath, fullPath) {
|
|
192
192
|
if (relPath !== "skills") return false;
|
|
193
193
|
try {
|
|
194
194
|
const entries = readdirSync(fullPath).filter((entry) => !entry.startsWith("."));
|
|
195
195
|
if (entries.length === 0) return false;
|
|
196
|
-
return entries.every((entry) => entry ===
|
|
196
|
+
return entries.every((entry) => entry === SKILL_NAME);
|
|
197
197
|
} catch {
|
|
198
198
|
return false;
|
|
199
199
|
}
|
|
@@ -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.
|
|
3
|
+
"version": "0.0.7",
|
|
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/*",
|