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