@first-tree-ai/context-tree 0.1.9 → 0.1.10
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
CHANGED
|
@@ -53,9 +53,9 @@ for an agent that is not present; uninstall removes every owned-prefix directory
|
|
|
53
53
|
and nothing else. Adding support for another agent is one entry in the host table
|
|
54
54
|
in `src/core/install.ts`.
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
`create` and `connect` leave project instructions unchanged. When a project has
|
|
57
|
+
a regular `AGENTS.md` and no `CLAUDE.md` entry, they best-effort create a
|
|
58
|
+
`CLAUDE.md` symlink to `AGENTS.md`. Connections are stored separately.
|
|
59
59
|
|
|
60
60
|
## Six skills
|
|
61
61
|
|
package/dist/cli/index.mjs
CHANGED
|
@@ -7418,23 +7418,15 @@ object({
|
|
|
7418
7418
|
schemaVersion: literal(1),
|
|
7419
7419
|
tree: contextTreeStateSchema
|
|
7420
7420
|
}).strict();
|
|
7421
|
-
/** Whether the project's AGENTS.md pointer was created, rewritten, or left alone. */
|
|
7422
|
-
const projectPointerOutcomeSchema = _enum([
|
|
7423
|
-
"written",
|
|
7424
|
-
"updated",
|
|
7425
|
-
"skipped"
|
|
7426
|
-
]);
|
|
7427
7421
|
object({
|
|
7428
7422
|
branch: string$2().trim().min(1),
|
|
7429
7423
|
commitSha: string$2(),
|
|
7430
7424
|
created: boolean(),
|
|
7431
|
-
pointer: projectPointerOutcomeSchema,
|
|
7432
7425
|
schemaVersion: literal(1),
|
|
7433
7426
|
title: string$2().trim().min(1),
|
|
7434
7427
|
treePath: absoluteSingleLinePathSchema
|
|
7435
7428
|
}).strict();
|
|
7436
7429
|
object({
|
|
7437
|
-
pointer: projectPointerOutcomeSchema,
|
|
7438
7430
|
schemaVersion: literal(1),
|
|
7439
7431
|
tree: contextTreeStateSchema
|
|
7440
7432
|
}).strict();
|
|
@@ -7625,66 +7617,13 @@ function canonicalProjectRoot(path, runner) {
|
|
|
7625
7617
|
return realpathSync(toplevel);
|
|
7626
7618
|
}
|
|
7627
7619
|
//#endregion
|
|
7628
|
-
//#region src/core/internal/project-
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
function pointerBlock(treePath) {
|
|
7632
|
-
return [
|
|
7633
|
-
BEGIN,
|
|
7634
|
-
"## Context Tree",
|
|
7635
|
-
"",
|
|
7636
|
-
`This project is connected to a Context Tree at \`${treePath}\`.`,
|
|
7637
|
-
"",
|
|
7638
|
-
"Read the decisions and constraints that bear on a task before planning or",
|
|
7639
|
-
"changing code, and record durable decisions there. Use the `context-tree-read`",
|
|
7640
|
-
"and `context-tree-write` skills rather than editing the tree by hand.",
|
|
7641
|
-
END
|
|
7642
|
-
].join("\n");
|
|
7643
|
-
}
|
|
7644
|
-
/** A real regular file, a real absent path, or a refusal. */
|
|
7645
|
-
function regularFileContent(path) {
|
|
7646
|
-
const entry = lstatSync(path, { throwIfNoEntry: false });
|
|
7647
|
-
if (entry === void 0) return void 0;
|
|
7648
|
-
if (entry.isSymbolicLink() || !entry.isFile()) throw new Error(`Refusing to write the Context Tree pointer through a symlink or non-file: ${path}`);
|
|
7649
|
-
return readFileSync(path, "utf8");
|
|
7650
|
-
}
|
|
7651
|
-
/**
|
|
7652
|
-
* Record the connected tree in the project's own `AGENTS.md`, so any agent that reads
|
|
7653
|
-
* instruction files knows the tree exists without a host-specific session hook.
|
|
7654
|
-
*
|
|
7655
|
-
* Idempotent: a delimited block is rewritten in place, never appended twice, so
|
|
7656
|
-
* switching the connected tree updates the existing pointer. All other content is
|
|
7657
|
-
* preserved, and an existing regular `CLAUDE.md` is left alone.
|
|
7658
|
-
*/
|
|
7659
|
-
function writeProjectPointer(projectPath, treePath) {
|
|
7660
|
-
const agentsPath = join(projectPath, "AGENTS.md");
|
|
7661
|
-
const block = pointerBlock(treePath);
|
|
7662
|
-
const existing = regularFileContent(agentsPath);
|
|
7663
|
-
if (existing === void 0) {
|
|
7664
|
-
writeFileSync(agentsPath, `# AGENTS.md\n\n${block}\n`, {
|
|
7665
|
-
encoding: "utf8",
|
|
7666
|
-
mode: 420
|
|
7667
|
-
});
|
|
7668
|
-
linkClaudeMarkdown(projectPath);
|
|
7669
|
-
return "written";
|
|
7670
|
-
}
|
|
7671
|
-
const start = existing.indexOf(BEGIN);
|
|
7672
|
-
const end = existing.indexOf(END);
|
|
7673
|
-
if (start !== -1 && end > start) {
|
|
7674
|
-
const replaced = `${existing.slice(0, start)}${block}${existing.slice(end + 25)}`;
|
|
7675
|
-
if (replaced === existing) return "skipped";
|
|
7676
|
-
writeFileSync(agentsPath, replaced, { encoding: "utf8" });
|
|
7677
|
-
return "updated";
|
|
7678
|
-
}
|
|
7679
|
-
writeFileSync(agentsPath, `${existing}${existing.endsWith("\n\n") ? "" : existing.endsWith("\n") ? "\n" : "\n\n"}${block}\n`, { encoding: "utf8" });
|
|
7680
|
-
linkClaudeMarkdown(projectPath);
|
|
7681
|
-
return "written";
|
|
7682
|
-
}
|
|
7683
|
-
/** Point CLAUDE.md at AGENTS.md only when the project has no CLAUDE.md of its own. */
|
|
7684
|
-
function linkClaudeMarkdown(projectPath) {
|
|
7685
|
-
const claudePath = join(projectPath, "CLAUDE.md");
|
|
7686
|
-
if (lstatSync(claudePath, { throwIfNoEntry: false }) !== void 0) return;
|
|
7620
|
+
//#region src/core/internal/project-instructions.ts
|
|
7621
|
+
/** Best-effort convenience link; existing project instruction entries are never changed. */
|
|
7622
|
+
function linkProjectInstructions(projectPath) {
|
|
7687
7623
|
try {
|
|
7624
|
+
if (!lstatSync(join(projectPath, "AGENTS.md"), { throwIfNoEntry: false })?.isFile()) return;
|
|
7625
|
+
const claudePath = join(projectPath, "CLAUDE.md");
|
|
7626
|
+
if (lstatSync(claudePath, { throwIfNoEntry: false }) !== void 0) return;
|
|
7688
7627
|
symlinkSync("AGENTS.md", claudePath, "file");
|
|
7689
7628
|
} catch {}
|
|
7690
7629
|
}
|
|
@@ -25666,14 +25605,14 @@ function realManagedDirectory(name, destination) {
|
|
|
25666
25605
|
* clean, fully valid Git checkout at an explicit disk path in place.
|
|
25667
25606
|
*/
|
|
25668
25607
|
function connectProject(options, runner) {
|
|
25669
|
-
/** Store the connection
|
|
25608
|
+
/** Store the connection and offer instruction discovery for existing project instructions. */
|
|
25670
25609
|
const connect = (tree) => {
|
|
25671
25610
|
const result = upsertConnection({
|
|
25672
25611
|
projectPath: options.projectPath,
|
|
25673
25612
|
tree
|
|
25674
25613
|
}, runner);
|
|
25614
|
+
linkProjectInstructions(canonicalProjectRoot(options.projectPath, runner));
|
|
25675
25615
|
return {
|
|
25676
|
-
pointer: writeProjectPointer(canonicalProjectRoot(options.projectPath, runner), result.tree.path),
|
|
25677
25616
|
schemaVersion: 1,
|
|
25678
25617
|
tree: result.tree
|
|
25679
25618
|
};
|
|
@@ -25866,21 +25805,23 @@ function projectName(canonicalRoot) {
|
|
|
25866
25805
|
return /^[a-z\d]/u.test(normalized) ? normalized : "project";
|
|
25867
25806
|
}
|
|
25868
25807
|
function existingCreateResult(canonical, destination, runner) {
|
|
25808
|
+
const branch = git(destination, [
|
|
25809
|
+
"symbolic-ref",
|
|
25810
|
+
"--short",
|
|
25811
|
+
"HEAD"
|
|
25812
|
+
], {
|
|
25813
|
+
message: "Failed to resolve the managed tree branch.",
|
|
25814
|
+
runner
|
|
25815
|
+
});
|
|
25816
|
+
const commitSha = git(destination, ["rev-parse", "HEAD"], {
|
|
25817
|
+
message: "Failed to resolve the managed tree commit.",
|
|
25818
|
+
runner
|
|
25819
|
+
});
|
|
25820
|
+
linkProjectInstructions(canonical);
|
|
25869
25821
|
return {
|
|
25870
|
-
branch
|
|
25871
|
-
|
|
25872
|
-
"--short",
|
|
25873
|
-
"HEAD"
|
|
25874
|
-
], {
|
|
25875
|
-
message: "Failed to resolve the managed tree branch.",
|
|
25876
|
-
runner
|
|
25877
|
-
}),
|
|
25878
|
-
commitSha: git(destination, ["rev-parse", "HEAD"], {
|
|
25879
|
-
message: "Failed to resolve the managed tree commit.",
|
|
25880
|
-
runner
|
|
25881
|
-
}),
|
|
25822
|
+
branch,
|
|
25823
|
+
commitSha,
|
|
25882
25824
|
created: false,
|
|
25883
|
-
pointer: writeProjectPointer(canonical, destination),
|
|
25884
25825
|
schemaVersion: 1,
|
|
25885
25826
|
title: readRootNode(destination).frontmatter.title,
|
|
25886
25827
|
treePath: destination
|
|
@@ -25912,11 +25853,11 @@ function createProject(projectPath, runner) {
|
|
|
25912
25853
|
path: scaffold.root
|
|
25913
25854
|
}
|
|
25914
25855
|
}, runner);
|
|
25856
|
+
linkProjectInstructions(canonical);
|
|
25915
25857
|
return {
|
|
25916
25858
|
branch: scaffold.branch,
|
|
25917
25859
|
commitSha: scaffold.commit,
|
|
25918
25860
|
created: true,
|
|
25919
|
-
pointer: writeProjectPointer(canonical, scaffold.root),
|
|
25920
25861
|
schemaVersion: 1,
|
|
25921
25862
|
title: name,
|
|
25922
25863
|
treePath: scaffold.root
|
|
@@ -26526,11 +26467,6 @@ function reclaimAbandonedWrites(root, checkoutBranch, runner) {
|
|
|
26526
26467
|
}
|
|
26527
26468
|
//#endregion
|
|
26528
26469
|
//#region src/cli/format.ts
|
|
26529
|
-
const POINTER_NOTE = {
|
|
26530
|
-
skipped: "left unchanged",
|
|
26531
|
-
updated: "updated",
|
|
26532
|
-
written: "written"
|
|
26533
|
-
};
|
|
26534
26470
|
function treeLines(tree, indent = " ") {
|
|
26535
26471
|
const lines = [`${indent}Path: ${tree.path}`];
|
|
26536
26472
|
if (tree.kind === "github") lines.push(`${indent}Repository: ${tree.repository}`);
|
|
@@ -26541,16 +26477,11 @@ function formatCreate(result) {
|
|
|
26541
26477
|
`${result.created ? "Created" : "Reused"} managed Context Tree "${result.title}".`,
|
|
26542
26478
|
` Path: ${result.treePath}`,
|
|
26543
26479
|
` Branch: ${result.branch}`,
|
|
26544
|
-
` Commit: ${result.commitSha}
|
|
26545
|
-
` AGENTS.md: ${POINTER_NOTE[result.pointer]}`
|
|
26480
|
+
` Commit: ${result.commitSha}`
|
|
26546
26481
|
].join("\n");
|
|
26547
26482
|
}
|
|
26548
26483
|
function formatConnect(result) {
|
|
26549
|
-
return [
|
|
26550
|
-
`Connected ${result.tree.kind} Context Tree.`,
|
|
26551
|
-
...treeLines(result.tree),
|
|
26552
|
-
` AGENTS.md: ${POINTER_NOTE[result.pointer]}`
|
|
26553
|
-
].join("\n");
|
|
26484
|
+
return [`Connected ${result.tree.kind} Context Tree.`, ...treeLines(result.tree)].join("\n");
|
|
26554
26485
|
}
|
|
26555
26486
|
function formatResolve(result) {
|
|
26556
26487
|
return [`Connected ${result.tree.kind} Context Tree.`, ...treeLines(result.tree)].join("\n");
|
package/package.json
CHANGED
|
@@ -26,11 +26,6 @@ for a target yourself; pass through only what the user typed or confirmed. An
|
|
|
26
26
|
explicit connect switches the project's connection. Report whether the
|
|
27
27
|
connected tree is local or GitHub-backed, with its canonical path.
|
|
28
28
|
|
|
29
|
-
`connect` also records the tree in the project's own `AGENTS.md`, replacing any
|
|
30
|
-
previous Context Tree pointer rather than adding a second one. The result's
|
|
31
|
-
`pointer` field reports `written`, `updated`, or `skipped`; when it is not
|
|
32
|
-
`skipped`, tell the user that `AGENTS.md` in their project changed.
|
|
33
|
-
|
|
34
29
|
If connection reports `INVALID_TREE` or `DIRTY_TREE`, report the failure and
|
|
35
30
|
stop. The tree must be repaired or committed at its own location before it can
|
|
36
31
|
be connected.
|
|
@@ -20,11 +20,6 @@ already taken, or the project is already connected to a different tree, report
|
|
|
20
20
|
the `connect` command the error supplies and stop. Do not replace or remove the
|
|
21
21
|
existing managed tree or connection.
|
|
22
22
|
|
|
23
|
-
`create` also records the tree in the project's own `AGENTS.md`, so later
|
|
24
|
-
sessions and other agents find it without any host-specific setup. The result's
|
|
25
|
-
`pointer` field reports `written`, `updated`, or `skipped`; when it is not
|
|
26
|
-
`skipped`, tell the user that `AGENTS.md` in their project changed.
|
|
27
|
-
|
|
28
23
|
After the tree is created or reused, run `context-tree resolve --json`. When the tree is
|
|
29
24
|
local, ask the user whether to publish it as a private GitHub repository. An
|
|
30
25
|
explicit prior request to publish counts as confirmation; otherwise a "no"
|