first-tree 0.0.6 → 0.0.8
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 +16 -5
- package/dist/bootstrap-YRjfHJp7.js +28 -0
- package/dist/cli.js +14 -5
- package/dist/{help-DV9-AaFp.js → help-CDfaFrzl.js} +1 -1
- package/dist/{init-BgGH2_yC.js → init-DjSVkUeR.js} +19 -8
- package/dist/onboarding-BiHx2jy5.js +10 -0
- package/dist/onboarding-Ce033qaW.js +2 -0
- package/dist/publish-D0crNDjz.js +504 -0
- package/dist/{repo-Cc5U4DWT.js → repo-BeVpMoHi.js} +2 -15
- package/dist/{source-integration-CuKjoheT.js → source-integration-DMxnl8Dw.js} +2 -6
- package/dist/{upgrade-BvA9oKmi.js → upgrade-B_NTlNrx.js} +2 -4
- package/dist/{verify-G8gNXzDX.js → verify-Chhm1vOF.js} +3 -3
- package/package.json +1 -1
- package/skills/first-tree/SKILL.md +25 -6
- package/skills/first-tree/agents/openai.yaml +1 -1
- package/skills/first-tree/assets/framework/VERSION +1 -1
- package/skills/first-tree/engine/commands/publish.ts +5 -0
- package/skills/first-tree/engine/init.ts +24 -6
- package/skills/first-tree/engine/publish.ts +807 -0
- package/skills/first-tree/engine/repo.ts +0 -8
- package/skills/first-tree/engine/runtime/adapters.ts +0 -2
- package/skills/first-tree/engine/runtime/asset-loader.ts +1 -36
- package/skills/first-tree/engine/runtime/bootstrap.ts +40 -0
- package/skills/first-tree/engine/runtime/installer.ts +0 -2
- package/skills/first-tree/engine/upgrade.ts +0 -11
- package/skills/first-tree/engine/validators/nodes.ts +2 -11
- package/skills/first-tree/references/maintainer-build-and-distribution.md +3 -0
- package/skills/first-tree/references/maintainer-thin-cli.md +1 -1
- package/skills/first-tree/references/onboarding.md +18 -12
- package/skills/first-tree/references/source-map.md +3 -1
- package/skills/first-tree/references/source-workspace-installation.md +25 -13
- package/skills/first-tree/references/upgrade-contract.md +15 -8
- package/skills/first-tree/scripts/check-skill-sync.sh +0 -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 +25 -0
- package/skills/first-tree/tests/publish.test.ts +248 -0
- package/skills/first-tree/tests/repo.test.ts +0 -25
- package/skills/first-tree/tests/skill-artifacts.test.ts +16 -1
- package/skills/first-tree/tests/thin-cli.test.ts +6 -0
- package/skills/first-tree/tests/upgrade.test.ts +0 -21
- package/dist/onboarding-D7fGGOMN.js +0 -10
- package/dist/onboarding-lASHHmgO.js +0 -2
|
@@ -10,8 +10,6 @@ import {
|
|
|
10
10
|
LEGACY_PROGRESS,
|
|
11
11
|
LEGACY_REPO_SKILL_PROGRESS,
|
|
12
12
|
LEGACY_REPO_SKILL_VERSION,
|
|
13
|
-
LEGACY_SKILL_PROGRESS,
|
|
14
|
-
LEGACY_SKILL_VERSION,
|
|
15
13
|
LEGACY_VERSION,
|
|
16
14
|
agentInstructionsFileCandidates,
|
|
17
15
|
installedSkillRoots,
|
|
@@ -220,9 +218,6 @@ export class Repo {
|
|
|
220
218
|
if (layout === "legacy") {
|
|
221
219
|
return LEGACY_PROGRESS;
|
|
222
220
|
}
|
|
223
|
-
if (layout === "legacy-skill") {
|
|
224
|
-
return LEGACY_SKILL_PROGRESS;
|
|
225
|
-
}
|
|
226
221
|
if (layout === "legacy-repo-skill") {
|
|
227
222
|
return LEGACY_REPO_SKILL_PROGRESS;
|
|
228
223
|
}
|
|
@@ -237,9 +232,6 @@ export class Repo {
|
|
|
237
232
|
if (layout === "legacy") {
|
|
238
233
|
return LEGACY_VERSION;
|
|
239
234
|
}
|
|
240
|
-
if (layout === "legacy-skill") {
|
|
241
|
-
return LEGACY_SKILL_VERSION;
|
|
242
|
-
}
|
|
243
235
|
if (layout === "legacy-repo-skill") {
|
|
244
236
|
return LEGACY_REPO_SKILL_VERSION;
|
|
245
237
|
}
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
CLAUDE_FRAMEWORK_HELPERS_DIR,
|
|
5
5
|
FRAMEWORK_EXAMPLES_DIR,
|
|
6
6
|
LEGACY_REPO_SKILL_EXAMPLES_DIR,
|
|
7
|
-
LEGACY_SKILL_EXAMPLES_DIR,
|
|
8
7
|
LEGACY_EXAMPLES_DIR,
|
|
9
8
|
} from "#skill/engine/runtime/asset-loader.js";
|
|
10
9
|
|
|
@@ -16,7 +15,6 @@ export function claudeCodeExampleCandidates(): string[] {
|
|
|
16
15
|
join(CLAUDE_FRAMEWORK_EXAMPLES_DIR, "claude-code"),
|
|
17
16
|
join(FRAMEWORK_EXAMPLES_DIR, "claude-code"),
|
|
18
17
|
join(LEGACY_REPO_SKILL_EXAMPLES_DIR, "claude-code"),
|
|
19
|
-
join(LEGACY_SKILL_EXAMPLES_DIR, "claude-code"),
|
|
20
18
|
join(LEGACY_EXAMPLES_DIR, "claude-code"),
|
|
21
19
|
];
|
|
22
20
|
}
|
|
@@ -18,6 +18,7 @@ export const FRAMEWORK_PROMPTS_DIR = join(FRAMEWORK_ASSET_ROOT, "prompts");
|
|
|
18
18
|
export const FRAMEWORK_EXAMPLES_DIR = join(FRAMEWORK_ASSET_ROOT, "examples");
|
|
19
19
|
export const FRAMEWORK_HELPERS_DIR = join(FRAMEWORK_ASSET_ROOT, "helpers");
|
|
20
20
|
export const INSTALLED_PROGRESS = join(SKILL_ROOT, "progress.md");
|
|
21
|
+
export const BOOTSTRAP_STATE = join(SKILL_ROOT, "bootstrap.json");
|
|
21
22
|
export const AGENT_INSTRUCTIONS_FILE = "AGENTS.md";
|
|
22
23
|
export const LEGACY_AGENT_INSTRUCTIONS_FILE = "AGENT.md";
|
|
23
24
|
export const AGENT_INSTRUCTIONS_TEMPLATE = "agents.md.template";
|
|
@@ -115,32 +116,6 @@ export const LEGACY_REPO_SKILL_PROGRESS = join(
|
|
|
115
116
|
"progress.md",
|
|
116
117
|
);
|
|
117
118
|
|
|
118
|
-
export const LEGACY_SKILL_NAME = "first-tree-cli-framework";
|
|
119
|
-
export const LEGACY_SKILL_ROOT = join("skills", LEGACY_SKILL_NAME);
|
|
120
|
-
export const LEGACY_SKILL_ASSET_ROOT = join(
|
|
121
|
-
LEGACY_SKILL_ROOT,
|
|
122
|
-
"assets",
|
|
123
|
-
"framework",
|
|
124
|
-
);
|
|
125
|
-
export const LEGACY_SKILL_VERSION = join(LEGACY_SKILL_ASSET_ROOT, "VERSION");
|
|
126
|
-
export const LEGACY_SKILL_TEMPLATES_DIR = join(
|
|
127
|
-
LEGACY_SKILL_ASSET_ROOT,
|
|
128
|
-
"templates",
|
|
129
|
-
);
|
|
130
|
-
export const LEGACY_SKILL_WORKFLOWS_DIR = join(
|
|
131
|
-
LEGACY_SKILL_ASSET_ROOT,
|
|
132
|
-
"workflows",
|
|
133
|
-
);
|
|
134
|
-
export const LEGACY_SKILL_PROMPTS_DIR = join(
|
|
135
|
-
LEGACY_SKILL_ASSET_ROOT,
|
|
136
|
-
"prompts",
|
|
137
|
-
);
|
|
138
|
-
export const LEGACY_SKILL_EXAMPLES_DIR = join(
|
|
139
|
-
LEGACY_SKILL_ASSET_ROOT,
|
|
140
|
-
"examples",
|
|
141
|
-
);
|
|
142
|
-
export const LEGACY_SKILL_PROGRESS = join(LEGACY_SKILL_ROOT, "progress.md");
|
|
143
|
-
|
|
144
119
|
export const LEGACY_FRAMEWORK_ROOT = ".context-tree";
|
|
145
120
|
export const LEGACY_VERSION = join(LEGACY_FRAMEWORK_ROOT, "VERSION");
|
|
146
121
|
export const LEGACY_PROGRESS = join(LEGACY_FRAMEWORK_ROOT, "progress.md");
|
|
@@ -153,7 +128,6 @@ export type FrameworkLayout =
|
|
|
153
128
|
| "skill"
|
|
154
129
|
| "claude-skill"
|
|
155
130
|
| "legacy-repo-skill"
|
|
156
|
-
| "legacy-skill"
|
|
157
131
|
| "legacy";
|
|
158
132
|
|
|
159
133
|
function pathExists(root: string, relPath: string): boolean {
|
|
@@ -180,7 +154,6 @@ export function frameworkVersionCandidates(): string[] {
|
|
|
180
154
|
FRAMEWORK_VERSION,
|
|
181
155
|
CLAUDE_FRAMEWORK_VERSION,
|
|
182
156
|
LEGACY_REPO_SKILL_VERSION,
|
|
183
|
-
LEGACY_SKILL_VERSION,
|
|
184
157
|
LEGACY_VERSION,
|
|
185
158
|
];
|
|
186
159
|
}
|
|
@@ -190,7 +163,6 @@ export function progressFileCandidates(): string[] {
|
|
|
190
163
|
INSTALLED_PROGRESS,
|
|
191
164
|
CLAUDE_INSTALLED_PROGRESS,
|
|
192
165
|
LEGACY_REPO_SKILL_PROGRESS,
|
|
193
|
-
LEGACY_SKILL_PROGRESS,
|
|
194
166
|
LEGACY_PROGRESS,
|
|
195
167
|
];
|
|
196
168
|
}
|
|
@@ -204,7 +176,6 @@ export function frameworkTemplateDirCandidates(): string[] {
|
|
|
204
176
|
FRAMEWORK_TEMPLATES_DIR,
|
|
205
177
|
CLAUDE_FRAMEWORK_TEMPLATES_DIR,
|
|
206
178
|
LEGACY_REPO_SKILL_TEMPLATES_DIR,
|
|
207
|
-
LEGACY_SKILL_TEMPLATES_DIR,
|
|
208
179
|
LEGACY_TEMPLATES_DIR,
|
|
209
180
|
];
|
|
210
181
|
}
|
|
@@ -214,7 +185,6 @@ export function frameworkWorkflowDirCandidates(): string[] {
|
|
|
214
185
|
FRAMEWORK_WORKFLOWS_DIR,
|
|
215
186
|
CLAUDE_FRAMEWORK_WORKFLOWS_DIR,
|
|
216
187
|
LEGACY_REPO_SKILL_WORKFLOWS_DIR,
|
|
217
|
-
LEGACY_SKILL_WORKFLOWS_DIR,
|
|
218
188
|
LEGACY_WORKFLOWS_DIR,
|
|
219
189
|
];
|
|
220
190
|
}
|
|
@@ -224,7 +194,6 @@ export function frameworkPromptDirCandidates(): string[] {
|
|
|
224
194
|
FRAMEWORK_PROMPTS_DIR,
|
|
225
195
|
CLAUDE_FRAMEWORK_PROMPTS_DIR,
|
|
226
196
|
LEGACY_REPO_SKILL_PROMPTS_DIR,
|
|
227
|
-
LEGACY_SKILL_PROMPTS_DIR,
|
|
228
197
|
LEGACY_PROMPTS_DIR,
|
|
229
198
|
];
|
|
230
199
|
}
|
|
@@ -234,7 +203,6 @@ export function frameworkExampleDirCandidates(): string[] {
|
|
|
234
203
|
FRAMEWORK_EXAMPLES_DIR,
|
|
235
204
|
CLAUDE_FRAMEWORK_EXAMPLES_DIR,
|
|
236
205
|
LEGACY_REPO_SKILL_EXAMPLES_DIR,
|
|
237
|
-
LEGACY_SKILL_EXAMPLES_DIR,
|
|
238
206
|
LEGACY_EXAMPLES_DIR,
|
|
239
207
|
];
|
|
240
208
|
}
|
|
@@ -261,9 +229,6 @@ export function detectFrameworkLayout(root: string): FrameworkLayout | null {
|
|
|
261
229
|
if (pathExists(root, LEGACY_REPO_SKILL_VERSION)) {
|
|
262
230
|
return "legacy-repo-skill";
|
|
263
231
|
}
|
|
264
|
-
if (pathExists(root, LEGACY_SKILL_VERSION)) {
|
|
265
|
-
return "legacy-skill";
|
|
266
|
-
}
|
|
267
232
|
if (pathExists(root, LEGACY_VERSION)) {
|
|
268
233
|
return "legacy";
|
|
269
234
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { BOOTSTRAP_STATE } from "#skill/engine/runtime/asset-loader.js";
|
|
4
|
+
|
|
5
|
+
export interface BootstrapState {
|
|
6
|
+
sourceRepoName: string;
|
|
7
|
+
sourceRepoPath: string;
|
|
8
|
+
treeRepoName: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function bootstrapStatePath(root: string): string {
|
|
12
|
+
return join(root, BOOTSTRAP_STATE);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function readBootstrapState(root: string): BootstrapState | null {
|
|
16
|
+
const path = bootstrapStatePath(root);
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8")) as Partial<BootstrapState>;
|
|
19
|
+
if (
|
|
20
|
+
typeof parsed.sourceRepoName !== "string"
|
|
21
|
+
|| typeof parsed.sourceRepoPath !== "string"
|
|
22
|
+
|| typeof parsed.treeRepoName !== "string"
|
|
23
|
+
) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
sourceRepoName: parsed.sourceRepoName,
|
|
28
|
+
sourceRepoPath: parsed.sourceRepoPath,
|
|
29
|
+
treeRepoName: parsed.treeRepoName,
|
|
30
|
+
};
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function writeBootstrapState(root: string, state: BootstrapState): void {
|
|
37
|
+
const path = bootstrapStatePath(root);
|
|
38
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
39
|
+
writeFileSync(path, `${JSON.stringify(state, null, 2)}\n`);
|
|
40
|
+
}
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
BUNDLED_SKILL_ROOT,
|
|
6
6
|
INSTALLED_SKILL_ROOTS,
|
|
7
7
|
LEGACY_REPO_SKILL_ROOT,
|
|
8
|
-
LEGACY_SKILL_ROOT,
|
|
9
8
|
} from "#skill/engine/runtime/asset-loader.js";
|
|
10
9
|
|
|
11
10
|
export function resolveBundledPackageRoot(startUrl = import.meta.url): string {
|
|
@@ -57,7 +56,6 @@ export function copyCanonicalSkill(sourceRoot: string, targetRoot: string): void
|
|
|
57
56
|
for (const relPath of [
|
|
58
57
|
...INSTALLED_SKILL_ROOTS,
|
|
59
58
|
LEGACY_REPO_SKILL_ROOT,
|
|
60
|
-
LEGACY_SKILL_ROOT,
|
|
61
59
|
]) {
|
|
62
60
|
const fullPath = join(targetRoot, relPath);
|
|
63
61
|
if (existsSync(fullPath)) {
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
LEGACY_AGENT_INSTRUCTIONS_FILE,
|
|
14
14
|
LEGACY_FRAMEWORK_ROOT,
|
|
15
15
|
LEGACY_REPO_SKILL_ROOT,
|
|
16
|
-
LEGACY_SKILL_ROOT,
|
|
17
16
|
SKILL_ROOT,
|
|
18
17
|
SOURCE_INTEGRATION_MARKER,
|
|
19
18
|
installedSkillRootsDisplay,
|
|
@@ -73,12 +72,6 @@ function formatUpgradeTaskList(
|
|
|
73
72
|
);
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
if (layout === "legacy-skill") {
|
|
77
|
-
migrationTasks.push(
|
|
78
|
-
`- [ ] Remove any stale \`${LEGACY_SKILL_ROOT}/\` references from repo-specific docs, scripts, workflow files, or agent config`,
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
75
|
if (repo.hasCanonicalAgentInstructionsFile() && repo.hasLegacyAgentInstructionsFile()) {
|
|
83
76
|
migrationTasks.push(
|
|
84
77
|
`- [ ] Merge any remaining user-authored content from \`${LEGACY_AGENT_INSTRUCTIONS_FILE}\` into \`${AGENT_INSTRUCTIONS_FILE}\`, then delete the legacy file`,
|
|
@@ -269,10 +262,6 @@ export function runUpgrade(repo?: Repo, options?: UpgradeOptions): number {
|
|
|
269
262
|
console.log(
|
|
270
263
|
`Migrated legacy ${LEGACY_REPO_SKILL_ROOT}/ layout to ${installedSkillRootsDisplay()}.`,
|
|
271
264
|
);
|
|
272
|
-
} else if (layout === "legacy-skill") {
|
|
273
|
-
console.log(
|
|
274
|
-
`Migrated ${LEGACY_SKILL_ROOT}/ to ${installedSkillRootsDisplay()}.`,
|
|
275
|
-
);
|
|
276
265
|
} else {
|
|
277
266
|
if (missingInstalledRoots.length > 0) {
|
|
278
267
|
console.log(
|
|
@@ -3,8 +3,6 @@ import { join, relative, posix } from "node:path";
|
|
|
3
3
|
import {
|
|
4
4
|
AGENT_INSTRUCTIONS_FILE,
|
|
5
5
|
LEGACY_AGENT_INSTRUCTIONS_FILE,
|
|
6
|
-
LEGACY_SKILL_NAME,
|
|
7
|
-
LEGACY_SKILL_ROOT,
|
|
8
6
|
SKILL_NAME,
|
|
9
7
|
SKILL_ROOT,
|
|
10
8
|
} from "#skill/engine/runtime/asset-loader.js";
|
|
@@ -88,12 +86,7 @@ function rel(path: string): string {
|
|
|
88
86
|
}
|
|
89
87
|
|
|
90
88
|
function isInstalledSkillPath(relPath: string): boolean {
|
|
91
|
-
return (
|
|
92
|
-
relPath === SKILL_ROOT ||
|
|
93
|
-
relPath.startsWith(`${SKILL_ROOT}/`) ||
|
|
94
|
-
relPath === LEGACY_SKILL_ROOT ||
|
|
95
|
-
relPath.startsWith(`${LEGACY_SKILL_ROOT}/`)
|
|
96
|
-
);
|
|
89
|
+
return relPath === SKILL_ROOT || relPath.startsWith(`${SKILL_ROOT}/`);
|
|
97
90
|
}
|
|
98
91
|
|
|
99
92
|
function isFrameworkContainerDir(relPath: string, fullPath: string): boolean {
|
|
@@ -106,9 +99,7 @@ function isFrameworkContainerDir(relPath: string, fullPath: string): boolean {
|
|
|
106
99
|
if (entries.length === 0) {
|
|
107
100
|
return false;
|
|
108
101
|
}
|
|
109
|
-
return entries.every(
|
|
110
|
-
(entry) => entry === SKILL_NAME || entry === LEGACY_SKILL_NAME,
|
|
111
|
-
);
|
|
102
|
+
return entries.every((entry) => entry === SKILL_NAME);
|
|
112
103
|
} catch {
|
|
113
104
|
return false;
|
|
114
105
|
}
|
|
@@ -53,6 +53,9 @@ another skill reference, not only in the files themselves.
|
|
|
53
53
|
- Default dedicated-tree-repo creation must stay local-only. It may create a
|
|
54
54
|
sibling git repo on disk, but it must not require remote repo creation or
|
|
55
55
|
source-repo cloning.
|
|
56
|
+
- `context-tree publish` is the explicit networked second-stage command for
|
|
57
|
+
GitHub repo creation, submodule add-back, and optional source-repo PR
|
|
58
|
+
opening. Keep that remote behavior there instead of expanding default `init`.
|
|
56
59
|
- If you change anything that gets copied into user repos, bump
|
|
57
60
|
`assets/framework/VERSION` and keep the upgrade task text in sync.
|
|
58
61
|
- If packaging changes alter what gets installed into user repos, update
|
|
@@ -33,7 +33,7 @@ These root files are shell code, not canonical knowledge stores:
|
|
|
33
33
|
- Keep root prose short. It should point to the skill, not duplicate the skill.
|
|
34
34
|
- Keep command semantics, install layout rules, and maintainer guidance in the
|
|
35
35
|
skill references.
|
|
36
|
-
- If init/upgrade semantics change for source/workspace repos versus dedicated
|
|
36
|
+
- If init/publish/upgrade semantics change for source/workspace repos versus dedicated
|
|
37
37
|
tree repos, update `references/source-workspace-installation.md` and
|
|
38
38
|
`references/upgrade-contract.md` instead of expanding root shell prose.
|
|
39
39
|
- If the shell gains behavior that is not obviously mechanical, move that
|
|
@@ -55,6 +55,8 @@ Information an agent needs to **decide** on an approach — not to execute it.
|
|
|
55
55
|
|
|
56
56
|
- A source/workspace Git repository, or an already-created dedicated tree repo
|
|
57
57
|
- Node.js 18+
|
|
58
|
+
- GitHub CLI (`gh`) if you want `context-tree publish` to create the remote
|
|
59
|
+
`*-context` repo and open the source-repo PR for you
|
|
58
60
|
- The npm package is `first-tree`, the installed CLI command is
|
|
59
61
|
`context-tree`.
|
|
60
62
|
- `context-tree init` installs the framework skill into
|
|
@@ -74,6 +76,7 @@ files are scaffolded only in the dedicated tree repo.
|
|
|
74
76
|
cd my-org
|
|
75
77
|
context-tree init
|
|
76
78
|
cd ../my-org-context
|
|
79
|
+
context-tree publish --open-pr
|
|
77
80
|
```
|
|
78
81
|
|
|
79
82
|
If you already created a dedicated tree repo manually, initialize it in place:
|
|
@@ -84,26 +87,29 @@ git init
|
|
|
84
87
|
context-tree init --here
|
|
85
88
|
```
|
|
86
89
|
|
|
90
|
+
Only use `--here` after you have already switched into the dedicated tree repo.
|
|
91
|
+
Do not use it inside the source/workspace repo unless you intentionally want
|
|
92
|
+
that repo itself to become the Context Tree.
|
|
93
|
+
|
|
87
94
|
Either way, the framework installs into `.agents/skills/first-tree/` and
|
|
88
95
|
`.claude/skills/first-tree/`, renders scaffolding (`NODE.md`, `AGENTS.md`,
|
|
89
96
|
`members/NODE.md`), and generates a task list in
|
|
90
97
|
`.agents/skills/first-tree/progress.md`.
|
|
91
98
|
|
|
92
|
-
Publishing tip: keep the tree repo in the same GitHub organization as the
|
|
93
|
-
source repo unless you have a reason not to.
|
|
94
|
-
|
|
95
99
|
Hard boundary: do **not** create `NODE.md`, `members/`, or tree-scoped
|
|
96
100
|
`AGENTS.md` in the source/workspace repo. Those files belong only in the
|
|
97
101
|
dedicated `*-context` repo.
|
|
98
102
|
|
|
99
103
|
Default agent workflow after initialization:
|
|
100
104
|
|
|
101
|
-
1.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
1. Draft the initial tree version in the dedicated `*-context` repo.
|
|
106
|
+
2. Run `context-tree publish --open-pr` from that dedicated tree repo. It will
|
|
107
|
+
create or reuse the GitHub `*-context` repo in the same owner/org as the
|
|
108
|
+
source repo, add it back to the source/workspace repo as a `git submodule`,
|
|
109
|
+
and open the source-repo PR.
|
|
110
|
+
3. After publish succeeds, treat the source repo's submodule checkout as the
|
|
111
|
+
canonical local working copy for the tree. The temporary sibling bootstrap
|
|
112
|
+
checkout can be deleted when you no longer need it.
|
|
107
113
|
|
|
108
114
|
### Step 2: Work Through the Task List
|
|
109
115
|
|
|
@@ -177,7 +183,8 @@ The tree doesn't duplicate source code — it captures what connects things and
|
|
|
177
183
|
|
|
178
184
|
| Command | Description |
|
|
179
185
|
|---------|-------------|
|
|
180
|
-
| `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`
|
|
186
|
+
| `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. |
|
|
187
|
+
| `context-tree publish` | Publish a dedicated tree repo to GitHub, add it back to the source/workspace repo as a submodule, and optionally open the source-repo PR. |
|
|
181
188
|
| `context-tree verify` | Check the installed progress file for unchecked items + run deterministic validation. Use `--tree-path` when invoking from another working directory. |
|
|
182
189
|
| `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. |
|
|
183
190
|
| `context-tree help onboarding` | Print this onboarding guide. |
|
|
@@ -202,8 +209,7 @@ only the local installed skill plus the `FIRST-TREE-SOURCE-INTEGRATION:` lines.
|
|
|
202
209
|
Run `context-tree upgrade --tree-path ../my-org-context` to upgrade the
|
|
203
210
|
dedicated tree repo itself.
|
|
204
211
|
|
|
205
|
-
If your repo still uses the older `skills/first-tree
|
|
206
|
-
`skills/first-tree-cli-framework/`, or `.context-tree/` layouts,
|
|
212
|
+
If your repo still uses the older `skills/first-tree/` or `.context-tree/` layouts,
|
|
207
213
|
`context-tree upgrade` will migrate it to the current installed layout first.
|
|
208
214
|
|
|
209
215
|
To pick up a newer framework release, first run a newer package version, for
|
|
@@ -42,12 +42,13 @@ These skill-owned files implement the framework behavior.
|
|
|
42
42
|
| Path | Purpose |
|
|
43
43
|
| --- | --- |
|
|
44
44
|
| `engine/commands/` | Stable command entrypoints that the thin CLI imports |
|
|
45
|
-
| `engine/init.ts` / `engine/verify.ts` / `engine/upgrade.ts` | Command implementations for install, verify, and upgrade |
|
|
45
|
+
| `engine/init.ts` / `engine/publish.ts` / `engine/verify.ts` / `engine/upgrade.ts` | Command implementations for install, publish, verify, and upgrade |
|
|
46
46
|
| `engine/onboarding.ts` | Canonical onboarding text loader |
|
|
47
47
|
| `engine/repo.ts` | Repo inspection, source-vs-tree heuristics, and worktree-aware git-root helpers |
|
|
48
48
|
| `engine/rules/` | Situation-aware task generation after `init` |
|
|
49
49
|
| `engine/validators/` | Deterministic tree and member validation |
|
|
50
50
|
| `engine/runtime/asset-loader.ts` | Path constants plus legacy-layout detection |
|
|
51
|
+
| `engine/runtime/bootstrap.ts` | Dedicated-tree bootstrap metadata for the publish workflow |
|
|
51
52
|
| `engine/runtime/installer.ts` | Bundled-package discovery, skill copy, and template-render helpers |
|
|
52
53
|
| `engine/runtime/upgrader.ts` | Packaged-skill version comparison helpers |
|
|
53
54
|
| `engine/runtime/adapters.ts` | Agent-integration path helpers |
|
|
@@ -74,6 +75,7 @@ not become the only place important maintainer knowledge lives.
|
|
|
74
75
|
| Path | Coverage |
|
|
75
76
|
| --- | --- |
|
|
76
77
|
| `tests/init.test.ts` | Init scaffolding behavior |
|
|
78
|
+
| `tests/publish.test.ts` | Publish workflow orchestration |
|
|
77
79
|
| `tests/verify.test.ts` | Verification and progress gating |
|
|
78
80
|
| `tests/rules.test.ts` | Task generation text |
|
|
79
81
|
| `tests/asset-loader.test.ts` | Layout detection and path precedence |
|
|
@@ -21,25 +21,37 @@ existing source or workspace repository.
|
|
|
21
21
|
the dedicated tree repo rather than copying that material into the source
|
|
22
22
|
repo.
|
|
23
23
|
|
|
24
|
+
## Agent Decision Rule
|
|
25
|
+
|
|
26
|
+
- Treat "install and use first-tree" in a source/workspace repo as a two-repo
|
|
27
|
+
workflow: local integration in the current repo plus tree bootstrap in a
|
|
28
|
+
sibling `*-context` repo.
|
|
29
|
+
- Do not run `context-tree init --here` in the source/workspace repo unless the
|
|
30
|
+
user explicitly says that repo itself should become the Context Tree.
|
|
31
|
+
- If you cannot create the sibling repo locally, cannot push it to GitHub, or
|
|
32
|
+
cannot add the submodule yet, pause and report the blocker. Do not fall back
|
|
33
|
+
to creating `NODE.md`, `members/`, or tree-scoped `AGENTS.md` in the
|
|
34
|
+
source/workspace repo.
|
|
35
|
+
|
|
24
36
|
## Default Agent Workflow
|
|
25
37
|
|
|
26
38
|
When an agent is asked to install first-tree for a source/workspace repo, the
|
|
27
39
|
default workflow is:
|
|
28
40
|
|
|
29
|
-
1.
|
|
30
|
-
2.
|
|
31
|
-
|
|
32
|
-
3. Create a sibling dedicated tree repo named `<repo>-context`.
|
|
33
|
-
4. Prefer creating and pushing that repo in the same GitHub organization as
|
|
34
|
-
the source repo, matching the source repo's default visibility unless the
|
|
35
|
-
user asks for something else.
|
|
36
|
-
5. Add the dedicated tree repo back to the source/workspace repo as a `git submodule`.
|
|
37
|
-
6. Run `context-tree init --here` inside the dedicated tree repo.
|
|
38
|
-
7. Draft the first tree version from the real codebase, docs, and ownership
|
|
41
|
+
1. Run `context-tree init` from the current source/workspace repo.
|
|
42
|
+
2. Switch into the sibling dedicated tree repo named `<repo>-context`.
|
|
43
|
+
3. Draft the first tree version from the real codebase, docs, and ownership
|
|
39
44
|
signals.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
4. Run `context-tree publish --open-pr` from the dedicated tree repo. It will:
|
|
46
|
+
create or reuse the GitHub `*-context` repo in the same owner/org as the
|
|
47
|
+
source repo, push the tree, add it back to the source/workspace repo as a
|
|
48
|
+
`git submodule`, and open the source-repo PR.
|
|
49
|
+
5. After publish succeeds, treat the source repo's submodule checkout as the
|
|
50
|
+
canonical local working copy for the tree. The temporary sibling bootstrap
|
|
51
|
+
checkout can be deleted when you no longer need it.
|
|
52
|
+
|
|
53
|
+
If the dedicated tree repo was initialized manually with `context-tree init --here`
|
|
54
|
+
and publish cannot infer the source repo, pass `--source-repo PATH`.
|
|
43
55
|
|
|
44
56
|
## Verification And Upgrade
|
|
45
57
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Upgrade Contract
|
|
2
2
|
|
|
3
3
|
This file describes the current installed-layout contract and the compatibility
|
|
4
|
-
rules we keep for legacy `skills/first-tree
|
|
5
|
-
`skills/first-tree-cli-framework/`, and `.context-tree/` repos.
|
|
4
|
+
rules we keep for legacy `skills/first-tree/` and `.context-tree/` repos.
|
|
6
5
|
|
|
7
6
|
## Canonical Source
|
|
8
7
|
|
|
@@ -60,6 +59,9 @@ For a dedicated tree repo, the tree content still lives outside the skill:
|
|
|
60
59
|
- `NODE.md`
|
|
61
60
|
- `AGENTS.md`
|
|
62
61
|
- `members/`
|
|
62
|
+
- `.agents/skills/first-tree/bootstrap.json` when `context-tree init` was run
|
|
63
|
+
from a separate source/workspace repo and the publish workflow needs to
|
|
64
|
+
remember that source repo path
|
|
63
65
|
|
|
64
66
|
The repo-owned `.agents/skills/first-tree/` path is the primary installed root
|
|
65
67
|
for progress state, workflow references, and helper scripts. The matching
|
|
@@ -84,6 +86,14 @@ skill discovery and hooks.
|
|
|
84
86
|
- validates root/frontmatter/agent markers
|
|
85
87
|
- runs node and member validators
|
|
86
88
|
- must reject source/workspace repos that carry only local integration
|
|
89
|
+
- `context-tree publish`
|
|
90
|
+
- is the explicit second-stage command for publishing a dedicated tree repo
|
|
91
|
+
to GitHub after local bootstrap
|
|
92
|
+
- reads dedicated-tree bootstrap metadata from
|
|
93
|
+
`.agents/skills/first-tree/bootstrap.json` when available
|
|
94
|
+
- may create or reuse the GitHub `*-context` repo, push tree commits, add it
|
|
95
|
+
back to the source/workspace repo as a git submodule, and optionally open
|
|
96
|
+
the source-repo PR
|
|
87
97
|
- `context-tree upgrade`
|
|
88
98
|
- compares the installed skill payload version to the skill bundled with the
|
|
89
99
|
currently running `first-tree` package
|
|
@@ -92,9 +102,6 @@ skill discovery and hooks.
|
|
|
92
102
|
skill plus the `FIRST-TREE-SOURCE-INTEGRATION:` lines
|
|
93
103
|
- migrates repos that still use the previous `skills/first-tree/` path onto
|
|
94
104
|
`.agents/skills/first-tree/` and `.claude/skills/first-tree/`
|
|
95
|
-
- migrates repos that still use the previous
|
|
96
|
-
`skills/first-tree-cli-framework/` path onto `.agents/skills/first-tree/`
|
|
97
|
-
and `.claude/skills/first-tree/`
|
|
98
105
|
- migrates legacy `.context-tree/` repos onto the installed skill layout
|
|
99
106
|
- preserves user-authored sections such as the editable part of `AGENTS.md`
|
|
100
107
|
|
|
@@ -111,9 +118,9 @@ skill discovery and hooks.
|
|
|
111
118
|
- Normal `context-tree init` and `context-tree upgrade` flows do not clone the
|
|
112
119
|
source repo or require network access.
|
|
113
120
|
- `context-tree verify` may still read a legacy
|
|
114
|
-
`.claude/skills/first-tree/...`, `skills/first-tree/...`,
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
`.claude/skills/first-tree/...`, `skills/first-tree/...`, or
|
|
122
|
+
`.context-tree/...` layout in an existing user repo so the repo can be
|
|
123
|
+
repaired or upgraded in place.
|
|
117
124
|
- `context-tree upgrade` must migrate either legacy layout onto
|
|
118
125
|
`.agents/skills/first-tree/` and `.claude/skills/first-tree/`, and remove
|
|
119
126
|
old skill directories afterward.
|
|
@@ -7,8 +7,6 @@ import {
|
|
|
7
7
|
INSTALLED_PROGRESS,
|
|
8
8
|
LEGACY_REPO_SKILL_PROGRESS,
|
|
9
9
|
LEGACY_REPO_SKILL_VERSION,
|
|
10
|
-
LEGACY_SKILL_PROGRESS,
|
|
11
|
-
LEGACY_SKILL_VERSION,
|
|
12
10
|
LEGACY_PROGRESS,
|
|
13
11
|
LEGACY_VERSION,
|
|
14
12
|
detectFrameworkLayout,
|
|
@@ -57,37 +55,15 @@ describe("asset-loader", () => {
|
|
|
57
55
|
).toBe(LEGACY_REPO_SKILL_VERSION);
|
|
58
56
|
});
|
|
59
57
|
|
|
60
|
-
it("detects the previous installed skill name before the .context-tree layout", () => {
|
|
61
|
-
const tmp = useTmpDir();
|
|
62
|
-
mkdirSync(
|
|
63
|
-
join(tmp.path, "skills", "first-tree-cli-framework", "assets", "framework"),
|
|
64
|
-
{
|
|
65
|
-
recursive: true,
|
|
66
|
-
},
|
|
67
|
-
);
|
|
68
|
-
mkdirSync(join(tmp.path, ".context-tree"), { recursive: true });
|
|
69
|
-
writeFileSync(join(tmp.path, LEGACY_SKILL_VERSION), "0.2.0\n");
|
|
70
|
-
writeFileSync(join(tmp.path, LEGACY_VERSION), "0.1.0\n");
|
|
71
|
-
|
|
72
|
-
expect(detectFrameworkLayout(tmp.path)).toBe("legacy-skill");
|
|
73
|
-
expect(
|
|
74
|
-
resolveFirstExistingPath(tmp.path, frameworkVersionCandidates()),
|
|
75
|
-
).toBe(LEGACY_SKILL_VERSION);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
58
|
it("prefers the installed progress file candidate", () => {
|
|
79
59
|
const tmp = useTmpDir();
|
|
80
60
|
mkdirSync(join(tmp.path, ".agents", "skills", "first-tree"), { recursive: true });
|
|
81
61
|
mkdirSync(join(tmp.path, ".claude", "skills", "first-tree"), { recursive: true });
|
|
82
62
|
mkdirSync(join(tmp.path, "skills", "first-tree"), { recursive: true });
|
|
83
|
-
mkdirSync(join(tmp.path, "skills", "first-tree-cli-framework"), {
|
|
84
|
-
recursive: true,
|
|
85
|
-
});
|
|
86
63
|
mkdirSync(join(tmp.path, ".context-tree"), { recursive: true });
|
|
87
64
|
writeFileSync(join(tmp.path, INSTALLED_PROGRESS), "new");
|
|
88
65
|
writeFileSync(join(tmp.path, CLAUDE_INSTALLED_PROGRESS), "claude");
|
|
89
66
|
writeFileSync(join(tmp.path, LEGACY_REPO_SKILL_PROGRESS), "old-repo-skill");
|
|
90
|
-
writeFileSync(join(tmp.path, LEGACY_SKILL_PROGRESS), "old-skill");
|
|
91
67
|
writeFileSync(join(tmp.path, LEGACY_PROGRESS), "old");
|
|
92
68
|
|
|
93
69
|
expect(resolveFirstExistingPath(tmp.path, progressFileCandidates())).toBe(
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
FRAMEWORK_VERSION,
|
|
10
10
|
LEGACY_AGENT_INSTRUCTIONS_FILE,
|
|
11
11
|
LEGACY_REPO_SKILL_VERSION,
|
|
12
|
-
LEGACY_SKILL_VERSION,
|
|
13
12
|
LEGACY_VERSION,
|
|
14
13
|
SKILL_ROOT,
|
|
15
14
|
} from "#skill/engine/runtime/asset-loader.js";
|
|
@@ -74,19 +73,6 @@ export function makeLegacyRepoFramework(root: string, version = "0.1.0"): void {
|
|
|
74
73
|
writeFileSync(join(root, LEGACY_REPO_SKILL_VERSION), `${version}\n`);
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
export function makeLegacyNamedFramework(
|
|
78
|
-
root: string,
|
|
79
|
-
version = "0.1.0",
|
|
80
|
-
): void {
|
|
81
|
-
mkdirSync(
|
|
82
|
-
join(root, "skills", "first-tree-cli-framework", "assets", "framework"),
|
|
83
|
-
{
|
|
84
|
-
recursive: true,
|
|
85
|
-
},
|
|
86
|
-
);
|
|
87
|
-
writeFileSync(join(root, LEGACY_SKILL_VERSION), `${version}\n`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
76
|
export function makeSourceSkill(root: string, version = "0.2.0"): void {
|
|
91
77
|
const skillRoot = join(root, "skills", "first-tree");
|
|
92
78
|
mkdirSync(join(skillRoot, "agents"), { recursive: true });
|
|
@@ -3,6 +3,7 @@ import { basename, dirname, join } from "node:path";
|
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
import {
|
|
5
5
|
formatTaskList,
|
|
6
|
+
INIT_USAGE,
|
|
6
7
|
parseInitArgs,
|
|
7
8
|
writeProgress,
|
|
8
9
|
runInit,
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
import { Repo } from "#skill/engine/repo.js";
|
|
11
12
|
import {
|
|
12
13
|
AGENT_INSTRUCTIONS_FILE,
|
|
14
|
+
BOOTSTRAP_STATE,
|
|
13
15
|
CLAUDE_INSTRUCTIONS_FILE,
|
|
14
16
|
FRAMEWORK_VERSION,
|
|
15
17
|
INSTALLED_PROGRESS,
|
|
@@ -79,6 +81,17 @@ describe("formatTaskList", () => {
|
|
|
79
81
|
expect(output).toContain("# Context Tree Init");
|
|
80
82
|
expect(output).toContain("## Verification");
|
|
81
83
|
});
|
|
84
|
+
|
|
85
|
+
it("documents the publish workflow for dedicated tree repos", () => {
|
|
86
|
+
const output = formatTaskList([], {
|
|
87
|
+
dedicatedTreeRepo: true,
|
|
88
|
+
sourceRepoName: "ADHD",
|
|
89
|
+
sourceRepoPath: "../ADHD",
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(output).toContain("context-tree publish --open-pr");
|
|
93
|
+
expect(output).toContain("canonical local working copy");
|
|
94
|
+
});
|
|
82
95
|
});
|
|
83
96
|
|
|
84
97
|
// --- writeProgress ---
|
|
@@ -233,6 +246,13 @@ describe("runInit", () => {
|
|
|
233
246
|
expect(existsSync(join(treeRepo, AGENT_INSTRUCTIONS_FILE))).toBe(true);
|
|
234
247
|
expect(existsSync(join(treeRepo, "members", "NODE.md"))).toBe(true);
|
|
235
248
|
expect(existsSync(join(treeRepo, INSTALLED_PROGRESS))).toBe(true);
|
|
249
|
+
expect(
|
|
250
|
+
JSON.parse(readFileSync(join(treeRepo, BOOTSTRAP_STATE), "utf-8")),
|
|
251
|
+
).toEqual({
|
|
252
|
+
sourceRepoName: basename(sourceRepoDir.path),
|
|
253
|
+
sourceRepoPath: `../${basename(sourceRepoDir.path)}`,
|
|
254
|
+
treeRepoName: basename(treeRepo),
|
|
255
|
+
});
|
|
236
256
|
expect(existsSync(join(sourceRepoDir.path, "NODE.md"))).toBe(false);
|
|
237
257
|
expect(existsSync(join(sourceRepoDir.path, "members", "NODE.md"))).toBe(false);
|
|
238
258
|
expect(existsSync(join(sourceRepoDir.path, INSTALLED_PROGRESS))).toBe(false);
|
|
@@ -302,6 +322,11 @@ describe("runInit", () => {
|
|
|
302
322
|
});
|
|
303
323
|
|
|
304
324
|
describe("parseInitArgs", () => {
|
|
325
|
+
it("documents that --here is only for dedicated tree repos", () => {
|
|
326
|
+
expect(INIT_USAGE).toContain("Do not use `--here` inside a source/workspace repo");
|
|
327
|
+
expect(INIT_USAGE).toContain("already in the dedicated tree repo");
|
|
328
|
+
});
|
|
329
|
+
|
|
305
330
|
it("parses dedicated repo options", () => {
|
|
306
331
|
expect(parseInitArgs(["--tree-name", "acme-context"])).toEqual({
|
|
307
332
|
treeName: "acme-context",
|