@jefuriiij/synthra 0.1.4 → 0.1.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/dist/cli/index.js CHANGED
@@ -18,7 +18,7 @@ var init_package = __esm({
18
18
  "package.json"() {
19
19
  package_default = {
20
20
  name: "@jefuriiij/synthra",
21
- version: "0.1.4",
21
+ version: "0.1.6",
22
22
  publishConfig: {
23
23
  access: "public"
24
24
  },
@@ -864,7 +864,7 @@ var public_default = `<!doctype html>
864
864
 
865
865
  <!-- ============ Footer ============ -->
866
866
  <footer class="foot">
867
- <div>Synth<em>ra</em> \xB7 v0.1.4</div>
867
+ <div>Synth<em>ra</em> \xB7 v0.1.6</div>
868
868
  <div>Cost figures approximate \xB7 @jefuriiij</div>
869
869
  </footer>
870
870
 
@@ -3031,7 +3031,7 @@ import { mkdir as mkdir5, readFile as readFile10, stat as stat2, writeFile as wr
3031
3031
 
3032
3032
  // src/hooks/claude-md.ts
3033
3033
  import { readFile as readFile9, writeFile as writeFile4 } from "fs/promises";
3034
- var POLICY_VERSION = 1;
3034
+ var POLICY_VERSION = 2;
3035
3035
  var POLICY_BEGIN = `<!-- synthra-policy v${POLICY_VERSION} BEGIN -->`;
3036
3036
  var POLICY_END = `<!-- synthra-policy v${POLICY_VERSION} END -->`;
3037
3037
  var ANY_BLOCK_RE = /<!--\s*synthra-policy\s+v\d+\s+BEGIN\s*-->[\s\S]*?<!--\s*synthra-policy\s+v\d+\s+END\s*-->\s*/g;
@@ -3040,33 +3040,84 @@ function policyBlock() {
3040
3040
  POLICY_BEGIN,
3041
3041
  "## Synthra context policy",
3042
3042
  "",
3043
- "Synthra has pre-loaded a structured context pack into this session and",
3044
- "exposes the project's code graph through three MCP tools. **Prefer these",
3045
- "tools over Grep / Glob** \u2014 they are faster, cheaper, and already filtered",
3043
+ "Synthra has pre-loaded structured context into this session and exposes",
3044
+ "the project's code graph through MCP tools. **Prefer these tools over",
3045
+ "Grep / Glob / Read** \u2014 they are faster, cheaper, and already filtered",
3046
3046
  "to relevant files.",
3047
3047
  "",
3048
+ "> **Tool namespace.** Synthra's MCP tools are exposed as",
3049
+ "> `mcp__synthra__graph_continue`, `mcp__synthra__graph_read`, and",
3050
+ "> `mcp__synthra__graph_register_edit`. Below they are referred to by",
3051
+ "> their short names (`graph_continue` etc.) for readability \u2014 use the",
3052
+ "> full namespaced form when actually invoking them.",
3053
+ "",
3048
3054
  "### Tools",
3049
3055
  "",
3050
- "- **`graph_continue(query)`** \u2014 your default first move when you need",
3051
- " project context. Returns signatures + top function bodies + linked test",
3052
- ' files, with a `confidence` label. If `confidence === "high"`, **stop**:',
3053
- " do not call Grep/Glob for the same query.",
3054
- '- **`graph_read(target)`** \u2014 fetch source for a specific `"file/path.ts"`',
3055
- ' or `"file/path.ts::SymbolName"`. Use this once you know what you want.',
3056
+ "- **`graph_continue(query)`** \u2014 returns a `Confidence` label, the list",
3057
+ " of relevant `Files`, and signatures + top function bodies for those",
3058
+ " files. Your default first move when you need project context.",
3059
+ "- **`graph_read(target)`** \u2014 fetch source. Prefer the",
3060
+ ' `"file/path.ts::SymbolName"` form over a bare file path \u2014 reading one',
3061
+ " symbol is ~50 tokens, reading a whole file is thousands.",
3056
3062
  "- **`graph_register_edit(files)`** \u2014 after you edit files, call this so",
3057
- " Synthra ranks them higher and avoids surfacing stale snapshots.",
3063
+ " subsequent turns weight your changes and avoid stale snapshots.",
3064
+ "",
3065
+ "### When to call `graph_continue` \u2014 and when to skip",
3066
+ "",
3067
+ "**Call `graph_continue` only when you do NOT already know the relevant",
3068
+ "files.**",
3069
+ "",
3070
+ "Call it when:",
3071
+ "- This is the first message of a new task or conversation",
3072
+ "- The task shifts to a different area of the codebase",
3073
+ "- You need files you haven't seen yet in this session",
3074
+ "",
3075
+ "**Skip `graph_continue` when:**",
3076
+ "- You already identified the relevant files earlier in this conversation",
3077
+ "- You are doing follow-up work on files already read (verify, refactor,",
3078
+ " test, docs, cleanup, commit)",
3079
+ "- The task is pure text (commit message, explanation, summary)",
3080
+ "",
3081
+ 'If skipping, go directly to `graph_read("file.ts::symbol")` on what',
3082
+ "you already know.",
3083
+ "",
3084
+ "### Confidence caps",
3085
+ "",
3086
+ "When `graph_continue` returns:",
3087
+ "",
3088
+ "- **`Confidence: high`** \u2192 Stop. Do NOT Grep, Glob, or further explore",
3089
+ " for this query. The graph already has it.",
3090
+ "- **`Confidence: medium`** \u2192 Read the listed `Files` directly via",
3091
+ ' `graph_read("file::symbol")` *before* trying Grep. The graph has',
3092
+ " narrowed the search space \u2014 use it, don't bypass it.",
3093
+ "- **`Confidence: low`** \u2192 You may use Grep / Glob, but the PreToolUse",
3094
+ " hook may still block redundant calls.",
3095
+ "",
3096
+ "### Reading code",
3097
+ "",
3098
+ "- **Always use `file::symbol` notation** with `graph_read`. Whole-file",
3099
+ " reads should be rare \u2014 only when you genuinely need the full file.",
3100
+ "- If `graph_continue`'s `Files` list contains a `::` entry, pass it",
3101
+ " verbatim to `graph_read`.",
3058
3102
  "",
3059
- "### Rules",
3103
+ "### Don'ts",
3060
3104
  "",
3061
- "1. Call `graph_continue` **before** Grep / Glob for any question about",
3062
- " project code. Grep / Glob calls for the same query may be blocked at",
3063
- " the hook layer when the graph already has a confident answer.",
3064
- '2. When `graph_continue` returns `confidence: "high"`, treat the pack as',
3065
- " authoritative \u2014 don't second-guess it with a Grep.",
3066
- "3. Use `graph_read` instead of `Read` when you only need a specific symbol",
3067
- " from a file (you get less noise + line numbers).",
3068
- "4. After editing files, call `graph_register_edit(files)` so subsequent",
3069
- " turns weight your changes correctly.",
3105
+ "- Don't Grep / Glob before calling `graph_continue` when required \u2014 the",
3106
+ " PreToolUse hook may block it.",
3107
+ "- Don't call `graph_continue` more than once per turn.",
3108
+ "- Don't read whole files when a symbol-level read would suffice.",
3109
+ "",
3110
+ "### Session-end resume note",
3111
+ "",
3112
+ `When the user signals they're done (e.g. "bye", "wrap up", "done"),`,
3113
+ "proactively update `.synthra/CONTEXT.md` with:",
3114
+ "",
3115
+ "- **Current Task**: one sentence on what was being worked on",
3116
+ "- **Key Decisions**: bullet list, max 3 items",
3117
+ "- **Next Steps**: bullet list, max 3 items",
3118
+ "",
3119
+ "Keep `CONTEXT.md` under 20 lines total. Don't summarise the conversation",
3120
+ "\u2014 write only what's needed to resume next session.",
3070
3121
  "",
3071
3122
  "_This block is managed by Synthra. Edits inside the BEGIN/END markers",
3072
3123
  "are overwritten on every `syn .` run._",
@@ -3097,8 +3148,16 @@ async function patchClaudeMd(path) {
3097
3148
  }
3098
3149
 
3099
3150
  // src/cli/bootstrap.ts
3100
- var GITIGNORE_MARKER = "# added by synthra";
3101
- var GITIGNORE_ENTRY = ".synthra-graph/";
3151
+ var GITIGNORE_ENTRIES = [
3152
+ {
3153
+ comment: "added by synthra (heavy generated state \u2014 gitignored by design)",
3154
+ entry: ".synthra-graph/"
3155
+ },
3156
+ {
3157
+ comment: "added by synthra \u2014 MCP registration. Remove this line if you want to share the synthra MCP entry with teammates via committed .mcp.json",
3158
+ entry: ".mcp.json"
3159
+ }
3160
+ ];
3102
3161
  async function exists(path) {
3103
3162
  try {
3104
3163
  await stat2(path);
@@ -3118,11 +3177,12 @@ async function patchGitignore(path) {
3118
3177
  existing = await readFile10(path, "utf8");
3119
3178
  } catch {
3120
3179
  }
3121
- const lines = existing.split(/\r?\n/);
3122
- if (lines.some((l) => l.trim() === GITIGNORE_ENTRY)) return false;
3123
- const appendix = (existing.length === 0 || existing.endsWith("\n") ? "" : "\n") + (existing.length ? "\n" : "") + `${GITIGNORE_MARKER}
3124
- ${GITIGNORE_ENTRY}
3125
- `;
3180
+ const trimmed = new Set(existing.split(/\r?\n/).map((l) => l.trim()));
3181
+ const missing = GITIGNORE_ENTRIES.filter((e) => !trimmed.has(e.entry));
3182
+ if (missing.length === 0) return false;
3183
+ const block = missing.map((m) => `# ${m.comment}
3184
+ ${m.entry}`).join("\n") + "\n";
3185
+ const appendix = (existing.length === 0 || existing.endsWith("\n") ? "" : "\n") + (existing.length ? "\n" : "") + block;
3126
3186
  await writeFile5(path, existing + appendix, "utf8");
3127
3187
  return true;
3128
3188
  }
@@ -4744,10 +4804,10 @@ function runClaude(bin, args, cwd, stdio = "pipe") {
4744
4804
  }
4745
4805
  async function registerMcp(bin, mcpPort, cwd) {
4746
4806
  const url = `http://127.0.0.1:${mcpPort}/mcp`;
4747
- await runClaude(bin, ["mcp", "remove", MCP_NAME, "--scope", "local"], cwd).catch(() => void 0);
4807
+ await runClaude(bin, ["mcp", "remove", MCP_NAME, "--scope", "project"], cwd).catch(() => void 0);
4748
4808
  const reg = await runClaude(
4749
4809
  bin,
4750
- ["mcp", "add", MCP_NAME, "--transport", "http", "--scope", "local", url],
4810
+ ["mcp", "add", MCP_NAME, "--transport", "http", "--scope", "project", url],
4751
4811
  cwd
4752
4812
  );
4753
4813
  if (reg.code !== 0) {
@@ -4759,7 +4819,7 @@ async function registerMcp(bin, mcpPort, cwd) {
4759
4819
  return true;
4760
4820
  }
4761
4821
  async function unregisterMcp(bin, cwd) {
4762
- const r = await runClaude(bin, ["mcp", "remove", MCP_NAME, "--scope", "local"], cwd);
4822
+ const r = await runClaude(bin, ["mcp", "remove", MCP_NAME, "--scope", "project"], cwd);
4763
4823
  if (r.code === 0) log.debug("unregistered MCP server");
4764
4824
  }
4765
4825
  async function spawnClaude(bin, opts) {