@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
- Once a project is connected, `create` and `connect` record the tree in the
57
- project's own `AGENTS.md`, so any agent that reads instruction files knows the
58
- tree exists without host-specific configuration.
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
 
@@ -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-pointer.ts
7629
- const BEGIN = "<!-- context-tree:begin -->";
7630
- const END = "<!-- context-tree:end -->";
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, then record it in the project so any agent can find it. */
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: git(destination, [
25871
- "symbolic-ref",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@first-tree-ai/context-tree",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Durable, structured project context for coding agents: a CLI plus framework-neutral skills.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -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"