@phren/cli 0.0.42 → 0.0.43

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.
@@ -291,7 +291,7 @@ export async function handleUpdate(args) {
291
291
  process.exitCode = 1;
292
292
  }
293
293
  else {
294
- console.log("Run 'npx phren init' to refresh hooks and config.");
294
+ console.log("Run 'phren init' to refresh hooks and config.");
295
295
  }
296
296
  }
297
297
  export async function handleReview(args) {
@@ -86,13 +86,13 @@ export function getUntrackedProjectNotice(phrenPath, cwd) {
86
86
  return [
87
87
  "<phren-notice>",
88
88
  "This project directory is not tracked by phren yet.",
89
- "Run `npx phren add` to track it now.",
90
- `Suggested command: \`npx phren add \"${projectDir}\"\``,
89
+ "Run `phren add` to track it now.",
90
+ `Suggested command: \`phren add \"${projectDir}\"\``,
91
91
  "Ask the user whether they want to add it to phren now.",
92
- "If they say no, tell them they can always run `npx phren add` later.",
92
+ "If they say no, tell them they can always run `phren add` later.",
93
93
  "If they say yes, also ask whether phren should manage repo instruction files or leave their existing repo-owned CLAUDE/AGENTS files alone.",
94
- `Then use the \`add_project\` MCP tool with path="${projectDir}" and ownership="phren-managed"|"detached"|"repo-managed", or run \`npx phren add\` from that directory.`,
95
- "After onboarding, run `npx phren doctor` if hooks or MCP tools are not responding.",
94
+ `Then use the \`add_project\` MCP tool with path="${projectDir}" and ownership="phren-managed"|"detached"|"repo-managed", or run \`phren add\` from that directory.`,
95
+ "After onboarding, run `phren doctor` if hooks or MCP tools are not responding.",
96
96
  "<phren-notice>",
97
97
  "",
98
98
  ].join("\n");
@@ -127,8 +127,8 @@ export function getSessionStartOnboardingNotice(phrenPath, cwd, activeProject) {
127
127
  return [
128
128
  "<phren-notice>",
129
129
  "Phren onboarding: no tracked projects are active for this workspace yet.",
130
- "Start in a project repo and run `npx phren add` so SessionStart can inject project context.",
131
- "Run `npx phren doctor` to verify hooks and MCP wiring after setup.",
130
+ "Start in a project repo and run `phren add` so SessionStart can inject project context.",
131
+ "Run `phren doctor` to verify hooks and MCP wiring after setup.",
132
132
  "<phren-notice>",
133
133
  "",
134
134
  ].join("\n");
@@ -141,7 +141,7 @@ export function getSessionStartOnboardingNotice(phrenPath, cwd, activeProject) {
141
141
  "<phren-notice>",
142
142
  `Phren onboarding: project "${activeProject}" is tracked but memory is still empty.`,
143
143
  "Capture one finding with `add_finding` and one task with `add_task` to seed future SessionStart context.",
144
- "Run `npx phren doctor` if setup seems incomplete.",
144
+ "Run `phren doctor` if setup seems incomplete.",
145
145
  "<phren-notice>",
146
146
  "",
147
147
  ].join("\n");
@@ -594,7 +594,7 @@ export async function handleProjectsNamespace(args, profile) {
594
594
  }
595
595
  if (subcommand === "add") {
596
596
  console.error("`phren projects add` has been removed from the supported workflow.");
597
- console.error("Use `cd ~/your-project && npx phren add` so enrollment stays path-based.");
597
+ console.error("Use `cd ~/your-project && phren add` so enrollment stays path-based.");
598
598
  process.exit(1);
599
599
  }
600
600
  if (subcommand === "remove") {
@@ -864,7 +864,7 @@ function handleProjectsList(profile) {
864
864
  .filter((name) => name !== "global")
865
865
  .sort();
866
866
  if (!projects.length) {
867
- console.log("No projects found. Run: cd ~/your-project && npx phren add");
867
+ console.log("No projects found. Run: cd ~/your-project && phren add");
868
868
  return;
869
869
  }
870
870
  console.log(`\nProjects in ${phrenPath}:\n`);
@@ -888,7 +888,7 @@ function handleProjectsList(profile) {
888
888
  console.log(` ${name}${tagStr}`);
889
889
  }
890
890
  console.log(`\n${projects.length} project(s) total.`);
891
- console.log("Add another project: cd ~/your-project && npx phren add");
891
+ console.log("Add another project: cd ~/your-project && phren add");
892
892
  }
893
893
  async function handleProjectsRemove(name, profile) {
894
894
  if (!isValidProjectName(name)) {
@@ -1763,7 +1763,6 @@ export async function handlePromoteNamespace(args) {
1763
1763
  // Write to target store
1764
1764
  const targetProjectDir = path.join(targetStore.path, project);
1765
1765
  fs.mkdirSync(targetProjectDir, { recursive: true });
1766
- const targetFindingsPath = path.join(targetProjectDir, "FINDINGS.md");
1767
1766
  const { addFindingToFile } = await import("../shared/content.js");
1768
1767
  const result = addFindingToFile(targetStore.path, project, match.text);
1769
1768
  if (!result.ok) {
@@ -60,13 +60,13 @@ export function getUntrackedProjectNotice(phrenPath, cwd) {
60
60
  return [
61
61
  "<phren-notice>",
62
62
  "This project directory is not tracked by phren yet.",
63
- "Run `npx phren add` to track it now.",
64
- `Suggested command: \`npx phren add "${projectDir}"\``,
63
+ "Run `phren add` to track it now.",
64
+ `Suggested command: \`phren add "${projectDir}"\``,
65
65
  "Ask the user whether they want to add it to phren now.",
66
- "If they say no, tell them they can always run `npx phren add` later.",
66
+ "If they say no, tell them they can always run `phren add` later.",
67
67
  "If they say yes, also ask whether phren should manage repo instruction files or leave their existing repo-owned CLAUDE/AGENTS files alone.",
68
- `Then use the \`add_project\` MCP tool with path="${projectDir}" and ownership="phren-managed"|"detached"|"repo-managed", or run \`npx phren add\` from that directory.`,
69
- "After onboarding, run `npx phren doctor` if hooks or MCP tools are not responding.",
68
+ `Then use the \`add_project\` MCP tool with path="${projectDir}" and ownership="phren-managed"|"detached"|"repo-managed", or run \`phren add\` from that directory.`,
69
+ "After onboarding, run `phren doctor` if hooks or MCP tools are not responding.",
70
70
  "<phren-notice>",
71
71
  "",
72
72
  ].join("\n");
@@ -83,8 +83,8 @@ export function getSessionStartOnboardingNotice(phrenPath, cwd, activeProject) {
83
83
  return [
84
84
  "<phren-notice>",
85
85
  "Phren onboarding: no tracked projects are active for this workspace yet.",
86
- "Start in a project repo and run `npx phren add` so SessionStart can inject project context.",
87
- "Run `npx phren doctor` to verify hooks and MCP wiring after setup.",
86
+ "Start in a project repo and run `phren add` so SessionStart can inject project context.",
87
+ "Run `phren doctor` to verify hooks and MCP wiring after setup.",
88
88
  "<phren-notice>",
89
89
  "",
90
90
  ].join("\n");
@@ -97,7 +97,7 @@ export function getSessionStartOnboardingNotice(phrenPath, cwd, activeProject) {
97
97
  "<phren-notice>",
98
98
  `Phren onboarding: project "${activeProject}" is tracked but memory is still empty.`,
99
99
  "Capture one finding with `add_finding` and one task with `add_task` to seed future SessionStart context.",
100
- "Run `npx phren doctor` if setup seems incomplete.",
100
+ "Run `phren doctor` if setup seems incomplete.",
101
101
  "<phren-notice>",
102
102
  "",
103
103
  ].join("\n");
@@ -338,12 +338,15 @@ export function isDuplicateFinding(existingContent, newLearning, threshold = 0.6
338
338
  return true;
339
339
  }
340
340
  // Second pass: Jaccard similarity (strip metadata before comparing)
341
+ // Threshold lowered from 0.55 to 0.40 to catch agent paraphrases —
342
+ // swarm agents often report the same insight with different wording,
343
+ // and 0.55 let too many through.
341
344
  const newTokens = jaccardTokenize(stripMetadata(newLearning));
342
345
  const existingTokens = jaccardTokenize(stripMetadata(bullet));
343
346
  if (newTokens.size < 3 || existingTokens.size < 3)
344
347
  continue; // too few tokens for reliable Jaccard
345
348
  const jaccard = jaccardSimilarity(newTokens, existingTokens);
346
- if (jaccard > 0.55) {
349
+ if (jaccard > 0.40) {
347
350
  debugLog(`duplicate-detection: Jaccard ${Math.round(jaccard * 100)}% with existing: "${bullet.slice(0, 80)}"`);
348
351
  return true;
349
352
  }
@@ -489,7 +492,7 @@ export async function checkSemanticDedup(phrenPath, project, newLearning, signal
489
492
  if (tokA.size < 3 || tokB.size < 3)
490
493
  continue;
491
494
  const jaccard = jaccardSimilarity(tokA, tokB);
492
- if (jaccard >= 0.55)
495
+ if (jaccard >= 0.40)
493
496
  continue; // already caught by sync isDuplicateFinding
494
497
  if (jaccard >= 0.3) {
495
498
  const isDup = await semanticDedup(a, b, phrenPath, signal);
@@ -265,7 +265,7 @@ export async function runTopLevelCommand(argv) {
265
265
  const phrenPath = defaultPhrenPath();
266
266
  const profile = (process.env.PHREN_PROFILE) || undefined;
267
267
  if (!fs.existsSync(phrenPath) || !fs.existsSync(path.join(phrenPath, ".config"))) {
268
- console.log("phren is not set up yet. Run: npx phren init");
268
+ console.log("phren is not set up yet. Run: phren init");
269
269
  return finish(1);
270
270
  }
271
271
  const ownership = ownershipArg
@@ -381,7 +381,7 @@ export async function runTopLevelCommand(argv) {
381
381
  const note = getVerifyOutcomeNote(phrenPath, result.checks);
382
382
  if (note)
383
383
  console.log(`\nNote: ${note}`);
384
- console.log(`\nRun \`npx phren init\` to fix setup issues.`);
384
+ console.log(`\nRun \`phren init\` to fix setup issues.`);
385
385
  }
386
386
  return finish(result.ok ? 0 : 1);
387
387
  }
@@ -408,7 +408,7 @@ export async function runTopLevelCommand(argv) {
408
408
  }
409
409
  }
410
410
  if (argvCommand === "link") {
411
- console.error("`phren link` has been removed. Use `npx phren init` instead.");
411
+ console.error("`phren link` has been removed. Use `phren init` instead.");
412
412
  return finish(1);
413
413
  }
414
414
  if (argvCommand === "--health") {
@@ -167,6 +167,7 @@ export function resolveFindingSessionId(phrenPath, project, explicitSessionId) {
167
167
  .sort((a, b) => sessionSortValue(b) - sessionSortValue(a));
168
168
  if (matchingProject.length > 0)
169
169
  return matchingProject[0].sessionId;
170
- const sorted = active.sort((a, b) => sessionSortValue(b) - sessionSortValue(a));
171
- return sorted[0]?.sessionId;
170
+ // Do not attribute findings to an unrelated active session from another project.
171
+ // Missing session provenance is safer than cross-project contamination.
172
+ return undefined;
172
173
  }
@@ -207,7 +207,7 @@ export function configureClaude(phrenPath, opts = {}) {
207
207
  eventHooks.push({ matcher: "", hooks: [hookBody] });
208
208
  }
209
209
  };
210
- const toolHookEnabled = hooksEnabled && isFeatureEnabled("PHREN_FEATURE_TOOL_HOOK", false);
210
+ const toolHookEnabled = hooksEnabled && isFeatureEnabled("PHREN_FEATURE_TOOL_HOOK", true);
211
211
  if (hooksEnabled) {
212
212
  upsertPhrenHook("UserPromptSubmit", {
213
213
  type: "command",
@@ -89,7 +89,7 @@ export function configureHooksIfEnabled(phrenPath, hooksEnabled, verb) {
89
89
  }
90
90
  }
91
91
  else {
92
- log(` Hooks are disabled by preference (run: npx phren hooks-mode on)`);
92
+ log(` Hooks are disabled by preference (run: phren hooks-mode on)`);
93
93
  }
94
94
  // Install phren CLI wrapper at ~/.local/bin/phren so the bare command works
95
95
  const wrapperInstalled = installPhrenCliWrapper(phrenPath);
@@ -14,7 +14,7 @@ export async function runHooksMode(modeArg) {
14
14
  if (!normalizedArg || normalizedArg === "status") {
15
15
  const current = getHooksEnabledPreference(phrenPath);
16
16
  log(`Hooks mode: ${current ? "on (active)" : "off (disabled)"}`);
17
- log(`Change mode: npx phren hooks-mode on|off`);
17
+ log(`Change mode: phren hooks-mode on|off`);
18
18
  return;
19
19
  }
20
20
  const mode = parseMcpMode(normalizedArg);
@@ -15,8 +15,8 @@ export async function runMcpMode(modeArg) {
15
15
  const hooks = getHooksEnabledPreference(phrenPath);
16
16
  log(`MCP mode: ${current ? "on (recommended)" : "off (hooks-only fallback)"}`);
17
17
  log(`Hooks mode: ${hooks ? "on (active)" : "off (disabled)"}`);
18
- log(`Change mode: npx phren mcp-mode on|off`);
19
- log(`Hooks toggle: npx phren hooks-mode on|off`);
18
+ log(`Change mode: phren mcp-mode on|off`);
19
+ log(`Hooks toggle: phren hooks-mode on|off`);
20
20
  return;
21
21
  }
22
22
  const mode = parseMcpMode(normalizedArg);
@@ -280,7 +280,7 @@ export async function runWalkthrough(phrenPath, options) {
280
280
  log(" phren-managed: Phren may mirror CLAUDE.md / AGENTS.md into the repo");
281
281
  log(" detached: Phren keeps its own docs but does not write into the repo");
282
282
  log(" repo-managed: keep the repo's existing CLAUDE/AGENTS files as canonical");
283
- log(" Change later: npx phren config project-ownership <mode>");
283
+ log(" Change later: phren config project-ownership <mode>");
284
284
  const projectOwnershipDefault = await prompts.select("Default project ownership", [
285
285
  { value: "detached", name: "detached (default)" },
286
286
  { value: "phren-managed", name: "phren-managed" },
@@ -291,7 +291,7 @@ export async function runWalkthrough(phrenPath, options) {
291
291
  log("directly: search memory, manage tasks, save findings, etc.");
292
292
  log(" Recommended for: Claude Code, Cursor, Copilot CLI, Codex");
293
293
  log(" Alternative: hooks-only mode (read-only context injection, any agent)");
294
- log(" Change later: npx phren mcp-mode on|off");
294
+ log(" Change later: phren mcp-mode on|off");
295
295
  const mcp = (await prompts.confirm("Enable MCP?", true)) ? "on" : "off";
296
296
  printSection("Hooks");
297
297
  log("Hooks run shell commands at session start, prompt submit, and session end.");
@@ -299,7 +299,7 @@ export async function runWalkthrough(phrenPath, options) {
299
299
  log(" - UserPromptSubmit: searches phren and injects relevant context");
300
300
  log(" - Stop: commits and pushes any new findings after each response");
301
301
  log(" What they touch: ~/.claude/settings.json (hooks section only)");
302
- log(" Change later: npx phren hooks-mode on|off");
302
+ log(" Change later: phren hooks-mode on|off");
303
303
  const hooks = (await prompts.confirm("Enable hooks?", true)) ? "on" : "off";
304
304
  printSection("Semantic Search (Optional)");
305
305
  log("Phren can use a local embedding model for semantic (fuzzy) search via Ollama.");
@@ -347,7 +347,7 @@ export async function runWalkthrough(phrenPath, options) {
347
347
  let findingsProactivity = "high";
348
348
  if (autoCaptureEnabled) {
349
349
  log(" Findings capture level controls how eager phren is to save lessons automatically.");
350
- log(" Change later: npx phren config proactivity.findings <high|medium|low>");
350
+ log(" Change later: phren config proactivity.findings <high|medium|low>");
351
351
  findingsProactivity = await prompts.select("Findings capture level", [
352
352
  { value: "high", name: "high (recommended)" },
353
353
  { value: "medium", name: "medium" },
@@ -363,7 +363,7 @@ export async function runWalkthrough(phrenPath, options) {
363
363
  log(" suggest: proposes tasks but waits for approval before writing");
364
364
  log(" manual: tasks are fully manual — you add them yourself");
365
365
  log(" off: never touch tasks automatically");
366
- log(" Change later: npx phren config workflow set --taskMode=<mode>");
366
+ log(" Change later: phren config workflow set --taskMode=<mode>");
367
367
  const taskMode = await prompts.select("Task mode", [
368
368
  { value: "auto", name: "auto (recommended)" },
369
369
  { value: "suggest", name: "suggest" },
@@ -376,7 +376,7 @@ export async function runWalkthrough(phrenPath, options) {
376
376
  log(" high (recommended): captures tasks as they come up naturally");
377
377
  log(" medium: only when you explicitly mention a task");
378
378
  log(" low: minimal auto-capture");
379
- log(" Change later: npx phren config proactivity.tasks <high|medium|low>");
379
+ log(" Change later: phren config proactivity.tasks <high|medium|low>");
380
380
  taskProactivity = await prompts.select("Task proactivity", [
381
381
  { value: "high", name: "high (recommended)" },
382
382
  { value: "medium", name: "medium" },
@@ -387,7 +387,7 @@ export async function runWalkthrough(phrenPath, options) {
387
387
  log("Choose how strict review gates should be for risky or low-confidence writes.");
388
388
  log(" lowConfidenceThreshold: confidence cutoff used to mark writes as risky");
389
389
  log(" riskySections: sections always treated as risky");
390
- log(" Change later: npx phren config workflow set --lowConfidenceThreshold=0.7 --riskySections=Stale,Conflicts");
390
+ log(" Change later: phren config workflow set --lowConfidenceThreshold=0.7 --riskySections=Stale,Conflicts");
391
391
  const thresholdAnswer = await prompts.input("Low-confidence threshold [0.0-1.0]", "0.7");
392
392
  const lowConfidenceThreshold = parseLowConfidenceThreshold(thresholdAnswer, 0.7);
393
393
  const riskySectionsAnswer = await prompts.input("Risky sections [Review,Stale,Conflicts]", "Stale,Conflicts");
@@ -437,7 +437,7 @@ export async function runWalkthrough(phrenPath, options) {
437
437
  log(" conservative — decisions and pitfalls only");
438
438
  log(" balanced — non-obvious patterns, decisions, pitfalls, bugs (recommended)");
439
439
  log(" aggressive — everything worth remembering, err on the side of capturing");
440
- log(" Change later: npx phren config finding-sensitivity <level>");
440
+ log(" Change later: phren config finding-sensitivity <level>");
441
441
  const findingSensitivity = await prompts.select("Finding sensitivity", [
442
442
  { value: "balanced", name: "balanced (recommended)" },
443
443
  { value: "conservative", name: "conservative" },
@@ -466,7 +466,7 @@ export async function runWalkthrough(phrenPath, options) {
466
466
  bootstrapCurrentProject = await prompts.confirm("Add this project to phren now?", true);
467
467
  if (!bootstrapCurrentProject) {
468
468
  bootstrapCurrentProject = false;
469
- log(style.warning(` Skipped. Later: cd ${detectedProject} && npx phren add`));
469
+ log(style.warning(` Skipped. Later: cd ${detectedProject} && phren add`));
470
470
  }
471
471
  else {
472
472
  bootstrapOwnership = await prompts.select("Ownership for detected project", [
@@ -260,7 +260,7 @@ export async function runInit(opts = {}) {
260
260
  shouldBootstrapCurrentProject = await prompts.confirm("Add this project to phren now?", true);
261
261
  if (!shouldBootstrapCurrentProject) {
262
262
  shouldBootstrapCurrentProject = false;
263
- log(style.warning(` Skipped. Later: cd ${pendingBootstrap.path} && npx phren add`));
263
+ log(style.warning(` Skipped. Later: cd ${pendingBootstrap.path} && phren add`));
264
264
  }
265
265
  else {
266
266
  bootstrapOwnership = await prompts.select("Ownership for detected project", [
@@ -352,7 +352,7 @@ export async function runInit(opts = {}) {
352
352
  const previousVersion = prefs.installedVersion;
353
353
  if (isVersionNewer(VERSION, previousVersion)) {
354
354
  log(`\n Starter template update available: v${previousVersion} -> v${VERSION}`);
355
- log(` Run \`npx phren init --apply-starter-update\` to refresh global/CLAUDE.md and global skills.`);
355
+ log(` Run \`phren init --apply-starter-update\` to refresh global/CLAUDE.md and global skills.`);
356
356
  }
357
357
  if (opts.applyStarterUpdate) {
358
358
  const updated = applyStarterTemplateUpdates(phrenPath);
@@ -395,8 +395,8 @@ export async function runInit(opts = {}) {
395
395
  log(`\n\x1b[95m◆\x1b[0m phren updated successfully`);
396
396
  log(`\nNext steps:`);
397
397
  log(` 1. Start a new Claude session in your project directory — phren injects context automatically`);
398
- log(` 2. Run \`npx phren doctor\` to verify everything is wired correctly`);
399
- log(` 3. Change defaults anytime: \`npx phren config project-ownership\`, \`npx phren config workflow\`, \`npx phren config proactivity.findings\`, \`npx phren config proactivity.tasks\``);
398
+ log(` 2. Run \`phren doctor\` to verify everything is wired correctly`);
399
+ log(` 3. Change defaults anytime: \`phren config project-ownership\`, \`phren config workflow\`, \`phren config proactivity.findings\`, \`phren config proactivity.tasks\``);
400
400
  log(` 4. After your first week, run phren-discover to surface gaps in your project knowledge`);
401
401
  log(` 5. After working across projects, run phren-consolidate to find cross-project patterns`);
402
402
  log(``);
@@ -581,8 +581,8 @@ export async function runInit(opts = {}) {
581
581
  log(`\nNext steps:`);
582
582
  let step = 1;
583
583
  log(` ${step++}. Start a new Claude session in your project directory — phren injects context automatically`);
584
- log(` ${step++}. Run \`npx phren doctor\` to verify everything is wired correctly`);
585
- log(` ${step++}. Change defaults anytime: \`npx phren config project-ownership\`, \`npx phren config workflow\`, \`npx phren config proactivity.findings\`, \`npx phren config proactivity.tasks\``);
584
+ log(` ${step++}. Run \`phren doctor\` to verify everything is wired correctly`);
585
+ log(` ${step++}. Change defaults anytime: \`phren config project-ownership\`, \`phren config workflow\`, \`phren config proactivity.findings\`, \`phren config proactivity.tasks\``);
586
586
  const gh = opts._walkthroughGithub;
587
587
  if (gh) {
588
588
  const remote = gh.username
@@ -607,9 +607,9 @@ export async function runInit(opts = {}) {
607
607
  log(` git remote add origin git@github.com:YOUR_USERNAME/my-phren.git`);
608
608
  log(` git push -u origin main`);
609
609
  }
610
- log(` ${step++}. Add more projects: cd ~/your-project && npx phren add`);
610
+ log(` ${step++}. Add more projects: cd ~/your-project && phren add`);
611
611
  if (!mcpEnabled) {
612
- log(` ${step++}. Turn MCP on: npx phren mcp-mode on`);
612
+ log(` ${step++}. Turn MCP on: phren mcp-mode on`);
613
613
  }
614
614
  log(` ${step++}. After your first week, run phren-discover to surface gaps in your project knowledge`);
615
615
  log(` ${step++}. After working across projects, run phren-consolidate to find cross-project patterns`);
@@ -1089,9 +1089,15 @@ export function updateMachinesYaml(phrenPath, machine, profile) {
1089
1089
  */
1090
1090
  export function detectProjectDir(dir, phrenPath) {
1091
1091
  const home = os.homedir();
1092
+ const tmpRoot = path.resolve(os.tmpdir());
1092
1093
  const resolvedPhrenPath = path.resolve(phrenPath);
1093
1094
  let current = path.resolve(dir);
1094
1095
  while (true) {
1096
+ // Never treat the shared OS temp root itself as a project. Tools may drop
1097
+ // global instruction files there, which would otherwise hijack detection
1098
+ // for arbitrary temp subdirectories.
1099
+ if (current === tmpRoot)
1100
+ return null;
1095
1101
  if (current === home || current === resolvedPhrenPath)
1096
1102
  return null;
1097
1103
  if (current.startsWith(resolvedPhrenPath + path.sep))
@@ -197,8 +197,8 @@ export async function runFreshInstall(phrenPath, opts, params) {
197
197
  log(`\nNext steps:`);
198
198
  let step = 1;
199
199
  log(` ${step++}. Start a new Claude session in your project directory — phren injects context automatically`);
200
- log(` ${step++}. Run \`npx phren doctor\` to verify everything is wired correctly`);
201
- log(` ${step++}. Change defaults anytime: \`npx phren config project-ownership\`, \`npx phren config workflow\`, \`npx phren config proactivity.findings\`, \`npx phren config proactivity.tasks\``);
200
+ log(` ${step++}. Run \`phren doctor\` to verify everything is wired correctly`);
201
+ log(` ${step++}. Change defaults anytime: \`phren config project-ownership\`, \`phren config workflow\`, \`phren config proactivity.findings\`, \`phren config proactivity.tasks\``);
202
202
  const gh = opts._walkthroughGithub;
203
203
  if (gh) {
204
204
  const remote = gh.username
@@ -223,9 +223,9 @@ export async function runFreshInstall(phrenPath, opts, params) {
223
223
  log(` git remote add origin git@github.com:YOUR_USERNAME/my-phren.git`);
224
224
  log(` git push -u origin main`);
225
225
  }
226
- log(` ${step++}. Add more projects: cd ~/your-project && npx phren add`);
226
+ log(` ${step++}. Add more projects: cd ~/your-project && phren add`);
227
227
  if (!mcpEnabled) {
228
- log(` ${step++}. Turn MCP on: npx phren mcp-mode on`);
228
+ log(` ${step++}. Turn MCP on: phren mcp-mode on`);
229
229
  }
230
230
  log(` ${step++}. After your first week, run phren-discover to surface gaps in your project knowledge`);
231
231
  log(` ${step++}. After working across projects, run phren-consolidate to find cross-project patterns`);
@@ -21,6 +21,6 @@ export function configureHooksIfEnabled(phrenPath, hooksEnabled, verb) {
21
21
  }
22
22
  }
23
23
  else {
24
- log(` Hooks are disabled by preference (run: npx phren hooks-mode on)`);
24
+ log(` Hooks are disabled by preference (run: phren hooks-mode on)`);
25
25
  }
26
26
  }
@@ -24,8 +24,8 @@ export async function runMcpMode(modeArg) {
24
24
  const hooks = getHooksEnabledPreference(phrenPath);
25
25
  log(`MCP mode: ${current ? "on (recommended)" : "off (hooks-only fallback)"}`);
26
26
  log(`Hooks mode: ${hooks ? "on (active)" : "off (disabled)"}`);
27
- log(`Change mode: npx phren mcp-mode on|off`);
28
- log(`Hooks toggle: npx phren hooks-mode on|off`);
27
+ log(`Change mode: phren mcp-mode on|off`);
28
+ log(`Hooks toggle: phren hooks-mode on|off`);
29
29
  return;
30
30
  }
31
31
  const mode = parseMcpMode(normalizedArg);
@@ -93,7 +93,7 @@ export async function runHooksMode(modeArg) {
93
93
  if (!normalizedArg || normalizedArg === "status") {
94
94
  const current = getHooksEnabledPreference(phrenPath);
95
95
  log(`Hooks mode: ${current ? "on (active)" : "off (disabled)"}`);
96
- log(`Change mode: npx phren hooks-mode on|off`);
96
+ log(`Change mode: phren hooks-mode on|off`);
97
97
  return;
98
98
  }
99
99
  const mode = parseMcpMode(normalizedArg);
@@ -53,7 +53,7 @@ export async function runExistingInstallUpdate(phrenPath, opts, params) {
53
53
  const previousVersion = prefs.installedVersion;
54
54
  if (isVersionNewer(VERSION, previousVersion)) {
55
55
  log(`\n Starter template update available: v${previousVersion} -> v${VERSION}`);
56
- log(` Run \`npx phren init --apply-starter-update\` to refresh global/CLAUDE.md and global skills.`);
56
+ log(` Run \`phren init --apply-starter-update\` to refresh global/CLAUDE.md and global skills.`);
57
57
  }
58
58
  if (opts.applyStarterUpdate) {
59
59
  const updated = applyStarterTemplateUpdates(phrenPath);
@@ -88,8 +88,8 @@ export async function runExistingInstallUpdate(phrenPath, opts, params) {
88
88
  log(`\n\x1b[95m◆\x1b[0m phren updated successfully`);
89
89
  log(`\nNext steps:`);
90
90
  log(` 1. Start a new Claude session in your project directory — phren injects context automatically`);
91
- log(` 2. Run \`npx phren doctor\` to verify everything is wired correctly`);
92
- log(` 3. Change defaults anytime: \`npx phren config project-ownership\`, \`npx phren config workflow\`, \`npx phren config proactivity.findings\`, \`npx phren config proactivity.tasks\``);
91
+ log(` 2. Run \`phren doctor\` to verify everything is wired correctly`);
92
+ log(` 3. Change defaults anytime: \`phren config project-ownership\`, \`phren config workflow\`, \`phren config proactivity.findings\`, \`phren config proactivity.tasks\``);
93
93
  log(` 4. After your first week, run phren-discover to surface gaps in your project knowledge`);
94
94
  log(` 5. After working across projects, run phren-consolidate to find cross-project patterns`);
95
95
  log(``);
@@ -269,7 +269,7 @@ export async function runWalkthrough(phrenPath) {
269
269
  log(" phren-managed: Phren may mirror CLAUDE.md / AGENTS.md into the repo");
270
270
  log(" detached: Phren keeps its own docs but does not write into the repo");
271
271
  log(" repo-managed: keep the repo's existing CLAUDE/AGENTS files as canonical");
272
- log(" Change later: npx phren config project-ownership <mode>");
272
+ log(" Change later: phren config project-ownership <mode>");
273
273
  const projectOwnershipDefault = await prompts.select("Default project ownership", [
274
274
  { value: "detached", name: "detached (default)" },
275
275
  { value: "phren-managed", name: "phren-managed" },
@@ -280,7 +280,7 @@ export async function runWalkthrough(phrenPath) {
280
280
  log("directly: search memory, manage tasks, save findings, etc.");
281
281
  log(" Recommended for: Claude Code, Cursor, Copilot CLI, Codex");
282
282
  log(" Alternative: hooks-only mode (read-only context injection, any agent)");
283
- log(" Change later: npx phren mcp-mode on|off");
283
+ log(" Change later: phren mcp-mode on|off");
284
284
  const mcp = (await prompts.confirm("Enable MCP?", true)) ? "on" : "off";
285
285
  printSection("Hooks");
286
286
  log("Hooks run shell commands at session start, prompt submit, and session end.");
@@ -288,7 +288,7 @@ export async function runWalkthrough(phrenPath) {
288
288
  log(" - UserPromptSubmit: searches phren and injects relevant context");
289
289
  log(" - Stop: commits and pushes any new findings after each response");
290
290
  log(" What they touch: ~/.claude/settings.json (hooks section only)");
291
- log(" Change later: npx phren hooks-mode on|off");
291
+ log(" Change later: phren hooks-mode on|off");
292
292
  const hooks = (await prompts.confirm("Enable hooks?", true)) ? "on" : "off";
293
293
  printSection("Semantic Search (Optional)");
294
294
  log("Phren can use a local embedding model for semantic (fuzzy) search via Ollama.");
@@ -336,7 +336,7 @@ export async function runWalkthrough(phrenPath) {
336
336
  let findingsProactivity = "high";
337
337
  if (autoCaptureEnabled) {
338
338
  log(" Findings capture level controls how eager phren is to save lessons automatically.");
339
- log(" Change later: npx phren config proactivity.findings <high|medium|low>");
339
+ log(" Change later: phren config proactivity.findings <high|medium|low>");
340
340
  findingsProactivity = await prompts.select("Findings capture level", [
341
341
  { value: "high", name: "high (recommended)" },
342
342
  { value: "medium", name: "medium" },
@@ -352,7 +352,7 @@ export async function runWalkthrough(phrenPath) {
352
352
  log(" suggest: proposes tasks but waits for approval before writing");
353
353
  log(" manual: tasks are fully manual — you add them yourself");
354
354
  log(" off: never touch tasks automatically");
355
- log(" Change later: npx phren config workflow set --taskMode=<mode>");
355
+ log(" Change later: phren config workflow set --taskMode=<mode>");
356
356
  const taskMode = await prompts.select("Task mode", [
357
357
  { value: "auto", name: "auto (recommended)" },
358
358
  { value: "suggest", name: "suggest" },
@@ -365,7 +365,7 @@ export async function runWalkthrough(phrenPath) {
365
365
  log(" high (recommended): captures tasks as they come up naturally");
366
366
  log(" medium: only when you explicitly mention a task");
367
367
  log(" low: minimal auto-capture");
368
- log(" Change later: npx phren config proactivity.tasks <high|medium|low>");
368
+ log(" Change later: phren config proactivity.tasks <high|medium|low>");
369
369
  taskProactivity = await prompts.select("Task proactivity", [
370
370
  { value: "high", name: "high (recommended)" },
371
371
  { value: "medium", name: "medium" },
@@ -376,7 +376,7 @@ export async function runWalkthrough(phrenPath) {
376
376
  log("Choose how strict review gates should be for risky or low-confidence writes.");
377
377
  log(" lowConfidenceThreshold: confidence cutoff used to mark writes as risky");
378
378
  log(" riskySections: sections always treated as risky");
379
- log(" Change later: npx phren config workflow set --lowConfidenceThreshold=0.7 --riskySections=Stale,Conflicts");
379
+ log(" Change later: phren config workflow set --lowConfidenceThreshold=0.7 --riskySections=Stale,Conflicts");
380
380
  const thresholdAnswer = await prompts.input("Low-confidence threshold [0.0-1.0]", "0.7");
381
381
  const lowConfidenceThreshold = parseLowConfidenceThreshold(thresholdAnswer, 0.7);
382
382
  const riskySectionsAnswer = await prompts.input("Risky sections [Review,Stale,Conflicts]", "Stale,Conflicts");
@@ -426,7 +426,7 @@ export async function runWalkthrough(phrenPath) {
426
426
  log(" conservative — decisions and pitfalls only");
427
427
  log(" balanced — non-obvious patterns, decisions, pitfalls, bugs (recommended)");
428
428
  log(" aggressive — everything worth remembering, err on the side of capturing");
429
- log(" Change later: npx phren config finding-sensitivity <level>");
429
+ log(" Change later: phren config finding-sensitivity <level>");
430
430
  const findingSensitivity = await prompts.select("Finding sensitivity", [
431
431
  { value: "balanced", name: "balanced (recommended)" },
432
432
  { value: "conservative", name: "conservative" },
@@ -455,7 +455,7 @@ export async function runWalkthrough(phrenPath) {
455
455
  bootstrapCurrentProject = await prompts.confirm("Add this project to phren now?", true);
456
456
  if (!bootstrapCurrentProject) {
457
457
  bootstrapCurrentProject = false;
458
- log(style.warning(` Skipped. Later: cd ${detectedProject} && npx phren add`));
458
+ log(style.warning(` Skipped. Later: cd ${detectedProject} && phren add`));
459
459
  }
460
460
  else {
461
461
  bootstrapOwnership = await prompts.select("Ownership for detected project", [
@@ -76,7 +76,7 @@ function maybeOfferStarterTemplateUpdate(phrenPath) {
76
76
  const prefs = JSON.parse(fs.readFileSync(prefsPath, "utf8"));
77
77
  if (isVersionNewer(current, prefs.installedVersion)) {
78
78
  log(` Starter template update available: v${prefs.installedVersion} -> v${current}`);
79
- log(` Run \`npx phren init --apply-starter-update\` to refresh global/CLAUDE.md and global skills.`);
79
+ log(` Run \`phren init --apply-starter-update\` to refresh global/CLAUDE.md and global skills.`);
80
80
  }
81
81
  }
82
82
  catch (err) {
@@ -209,7 +209,7 @@ export function findPhrenPathWithArg(arg) {
209
209
  const existing = findPhrenPath();
210
210
  if (existing)
211
211
  return existing;
212
- throw new Error(`${PhrenError.NOT_FOUND}: phren root not found. Run 'npx phren init'.`);
212
+ throw new Error(`${PhrenError.NOT_FOUND}: phren root not found. Run 'phren init'.`);
213
213
  }
214
214
  export function isProjectLocalMode(phrenPath) {
215
215
  try {
@@ -513,7 +513,7 @@ export function getPhrenPath() {
513
513
  if (!lazyPhrenPath) {
514
514
  const existing = findPhrenPath();
515
515
  if (!existing)
516
- throw new Error(`${PhrenError.NOT_FOUND}: phren root not found. Run 'npx phren init'.`);
516
+ throw new Error(`${PhrenError.NOT_FOUND}: phren root not found. Run 'phren init'.`);
517
517
  lazyPhrenPath = existing;
518
518
  }
519
519
  return lazyPhrenPath;
@@ -45,7 +45,7 @@ export function resolveActiveProfile(phrenPath, requestedProfile) {
45
45
  export function listMachines(phrenPath) {
46
46
  const machinesPath = path.join(phrenPath, "machines.yaml");
47
47
  if (!fs.existsSync(machinesPath))
48
- return phrenErr(`machines.yaml not found. Run 'npx phren init' to set up your phren.`, PhrenError.FILE_NOT_FOUND);
48
+ return phrenErr(`machines.yaml not found. Run 'phren init' to set up your phren.`, PhrenError.FILE_NOT_FOUND);
49
49
  try {
50
50
  const raw = fs.readFileSync(machinesPath, "utf8");
51
51
  const parsed = yaml.load(raw, { schema: yaml.CORE_SCHEMA });
@@ -205,7 +205,7 @@ export function getActiveProfileDefaults(phrenPath, profile) {
205
205
  export function listProfiles(phrenPath) {
206
206
  const profilesDir = path.join(phrenPath, "profiles");
207
207
  if (!fs.existsSync(profilesDir))
208
- return phrenErr(`No profiles/ directory found. Run 'npx phren init' to set up your phren.`, PhrenError.FILE_NOT_FOUND);
208
+ return phrenErr(`No profiles/ directory found. Run 'phren init' to set up your phren.`, PhrenError.FILE_NOT_FOUND);
209
209
  const files = fs.readdirSync(profilesDir).filter((file) => file.endsWith(".yaml")).sort();
210
210
  const profiles = [];
211
211
  for (const file of files) {
@@ -53,7 +53,7 @@ function hasCommandHook(value) {
53
53
  export async function runStatus() {
54
54
  const phrenPath = findPhrenPath();
55
55
  if (!phrenPath) {
56
- console.log(`${RED}phren not found${RESET}. Run ${CYAN}npx phren init${RESET} to set up.`);
56
+ console.log(`${RED}phren not found${RESET}. Run ${CYAN}npx @phren/cli init${RESET} to set up.`);
57
57
  process.exit(1);
58
58
  }
59
59
  const cwd = process.cwd();
@@ -17,8 +17,8 @@ import { getActiveTaskForSession } from "../task/lifecycle.js";
17
17
  import { FINDING_PROVENANCE_SOURCES } from "../content/citation.js";
18
18
  import { isInactiveFindingLine, supersedeFinding, retractFinding as retractFindingLifecycle, resolveFindingContradiction, } from "../finding/lifecycle.js";
19
19
  import { permissionDeniedError } from "../governance/rbac.js";
20
- const JACCARD_MAYBE_LOW = 0.30;
21
- const JACCARD_MAYBE_HIGH = 0.55; // above this isDuplicateFinding already catches it
20
+ const JACCARD_MAYBE_LOW = 0.25;
21
+ const JACCARD_MAYBE_HIGH = 0.40; // above this isDuplicateFinding already catches it
22
22
  function findJaccardCandidates(phrenPath, project, finding) {
23
23
  try {
24
24
  const findingsPath = path.join(phrenPath, project, "FINDINGS.md");
@@ -18,6 +18,7 @@ import { listTaskCheckpoints, writeTaskCheckpoint } from "../session/checkpoints
18
18
  import { markImpactEntriesCompletedForSession } from "../finding/impact.js";
19
19
  import { atomicWriteJson, debugError, scanSessionFiles } from "../session/utils.js";
20
20
  import { getRuntimeHealth } from "../governance/policy.js";
21
+ import { getProjectSourcePath, readProjectConfig } from "../project-config.js";
21
22
  const STALE_SESSION_MS = 24 * 60 * 60 * 1000; // 24 hours
22
23
  function collectGitStatusSnapshot(cwd) {
23
24
  try {
@@ -221,9 +222,11 @@ function cleanupStaleSessions(phrenPath) {
221
222
  // (no endedAt) should never be removed regardless of age.
222
223
  if (state && !state.endedAt)
223
224
  continue;
224
- // prefer startedAt from the JSON content over mtime (reliable on noatime mounts)
225
- const ageMs = state?.startedAt
226
- ? Date.now() - new Date(state.startedAt).getTime()
225
+ // For ended sessions, age out by end time rather than start time so
226
+ // long-running sessions do not disappear immediately after they finish.
227
+ const expirationAnchor = state?.endedAt || state?.startedAt;
228
+ const ageMs = expirationAnchor
229
+ ? Date.now() - new Date(expirationAnchor).getTime()
227
230
  : Date.now() - fs.statSync(fullPath).mtimeMs;
228
231
  if (ageMs > STALE_SESSION_MS) {
229
232
  fs.unlinkSync(fullPath);
@@ -607,7 +610,10 @@ export function register(server, ctx) {
607
610
  })();
608
611
  if (activeTask) {
609
612
  const taskId = activeTask.stableId || activeTask.id;
610
- const { gitStatus, editedFiles } = collectGitStatusSnapshot(process.cwd());
613
+ const projectConfig = readProjectConfig(phrenPath, endedState.project);
614
+ const snapshotRoot = getProjectSourcePath(phrenPath, endedState.project, projectConfig) ||
615
+ path.join(phrenPath, endedState.project);
616
+ const { gitStatus, editedFiles } = collectGitStatusSnapshot(snapshotRoot);
611
617
  const resumptionHint = extractResumptionHint(effectiveSummary, activeTask.line, activeTask.context || "No prior attempt captured");
612
618
  writeTaskCheckpoint(phrenPath, {
613
619
  project: endedState.project,
@@ -5,7 +5,7 @@ import * as path from "path";
5
5
  import { isValidProjectName } from "../utils.js";
6
6
  import { addTask as addTaskStore, addTasks as addTasksBatch, taskMarkdown, completeTask as completeTaskStore, completeTasks as completeTasksBatch, removeTask as removeTaskStore, removeTasks as removeTasksBatch, linkTaskIssue, pinTask, workNextTask, tidyDoneTasks, readTasks, readTasksAcrossProjects, resolveTaskItem, TASKS_FILENAME, updateTask as updateTaskStore, promoteTask, } from "../data/access.js";
7
7
  import { applyGravity } from "../data/tasks.js";
8
- import { parseGithubIssueUrl, } from "../task/github.js";
8
+ import { buildTaskIssueBody, createGithubIssueForTask, parseGithubIssueUrl, resolveProjectGithubRepo, } from "../task/github.js";
9
9
  import { clearTaskCheckpoint } from "../session/checkpoints.js";
10
10
  import { incrementSessionTasksCompleted } from "./session.js";
11
11
  import { normalizeMemoryScope } from "../shared.js";
@@ -366,6 +366,7 @@ export function register(server, ctx) {
366
366
  github_issue: z.union([z.number().int().positive(), z.string()]).optional().describe("GitHub issue number (for example 14 or '#14')."),
367
367
  github_url: z.string().optional().describe("GitHub issue URL to associate with the task item."),
368
368
  unlink_github: z.boolean().optional().describe("If true, remove any linked GitHub issue metadata from the item."),
369
+ create_issue: z.boolean().optional().describe("If true, create a GitHub issue for this task and link it."),
369
370
  pin: z.boolean().optional().describe("If true, pin the task so it floats to the top of its section."),
370
371
  promote: z.boolean().optional().describe("If true, clear the speculative flag on this task (confirm the user wants it)."),
371
372
  move_to_active: z.boolean().optional().describe("Used with promote: also move the task to the Active section."),
@@ -382,6 +383,25 @@ export function register(server, ctx) {
382
383
  if (!updates.work_next && !item) {
383
384
  return mcpResponse({ ok: false, error: "item is required unless updates.work_next is true." });
384
385
  }
386
+ if (updates.create_issue) {
387
+ const extraUpdates = [
388
+ updates.text,
389
+ updates.priority,
390
+ updates.context,
391
+ updates.section,
392
+ updates.github_issue,
393
+ updates.github_url,
394
+ updates.unlink_github,
395
+ updates.pin,
396
+ updates.promote,
397
+ updates.move_to_active,
398
+ updates.work_next,
399
+ updates.replace_context,
400
+ ].some((value) => value !== undefined);
401
+ if (extraUpdates) {
402
+ return mcpResponse({ ok: false, error: "create_issue must be used by itself." });
403
+ }
404
+ }
385
405
  // Cross-validate github_issue and github_url
386
406
  if (updates.github_url) {
387
407
  const parsed = parseGithubIssueUrl(updates.github_url);
@@ -423,6 +443,45 @@ export function register(server, ctx) {
423
443
  data: { project, item: result.data },
424
444
  });
425
445
  }
446
+ if (updates.create_issue) {
447
+ const resolved = resolveTaskItem(phrenPath, project, item);
448
+ if (!resolved.ok)
449
+ return mcpResponse({ ok: false, error: resolved.error });
450
+ const repo = resolveProjectGithubRepo(phrenPath, project);
451
+ if (!repo) {
452
+ return mcpResponse({
453
+ ok: false,
454
+ error: "Could not infer a GitHub repo. Add a GitHub URL to CLAUDE.md or summary.md, or link an existing issue instead.",
455
+ });
456
+ }
457
+ const created = createGithubIssueForTask({
458
+ repo,
459
+ title: resolved.data.line.replace(/\s*\[(high|medium|low)\]\s*$/i, "").trim(),
460
+ body: buildTaskIssueBody(project, resolved.data),
461
+ });
462
+ if (!created.ok)
463
+ return mcpResponse({ ok: false, error: created.error, errorCode: created.code });
464
+ const linked = linkTaskIssue(phrenPath, project, item, {
465
+ github_issue: created.data.issueNumber,
466
+ github_url: created.data.url,
467
+ });
468
+ if (!linked.ok)
469
+ return mcpResponse({ ok: false, error: linked.error, errorCode: linked.code });
470
+ refreshTaskIndex(updateFileInIndex, phrenPath, project);
471
+ return mcpResponse({
472
+ ok: true,
473
+ message: `Created GitHub issue ${created.data.issueNumber ? `#${created.data.issueNumber}` : created.data.url} for ${project} task.`,
474
+ data: {
475
+ project,
476
+ item,
477
+ issue_number: created.data.issueNumber ?? null,
478
+ issue_url: created.data.url,
479
+ githubIssue: linked.data.githubIssue ?? null,
480
+ githubUrl: linked.data.githubUrl || null,
481
+ stableId: linked.data.stableId || null,
482
+ },
483
+ });
484
+ }
426
485
  // Handle github issue linking via update_task when github_issue or github_url is set (and no other field updates)
427
486
  if ((updates.github_issue !== undefined || updates.github_url || updates.unlink_github) && !updates.text && !updates.priority && !updates.context && !updates.section) {
428
487
  if (updates.unlink_github && (updates.github_issue !== undefined || updates.github_url)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phren/cli",
3
- "version": "0.0.42",
3
+ "version": "0.0.43",
4
4
  "description": "Knowledge layer for AI agents. Phren learns and recalls.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -102,7 +102,7 @@ If this fails (not a git repo, no remote), tell the user. Don't silently skip.
102
102
  Run init against the pulled phren repo so hooks, MCP registration, and machine/profile wiring are refreshed:
103
103
 
104
104
  ```bash
105
- PHREN_PATH="$PHREN_DIR" npx phren init -y
105
+ PHREN_PATH="$PHREN_DIR" phren init -y
106
106
  ```
107
107
 
108
108
  If the user is in an untracked repo afterward, tell them to open a session there and let the agent ask, or run `phren add` from that directory.
package/starter/README.md CHANGED
@@ -32,7 +32,7 @@ New to phren? Here's what each file does and when it matters.
32
32
 
33
33
  **FINDINGS.md** fills itself. As Claude discovers insights, patterns, and decisions during your sessions, it tells phren and entries land here grouped by date. Old entries fade from retrieval over time. Wrong entries can be removed with `remove_finding()`.
34
34
 
35
- **tasks.md** is your task board file. It keeps Active (working now), Queue (up next), and Done (finished) in one place so the work history stays with the project. You can also manage it from `npx phren shell`.
35
+ **tasks.md** is your task board file. It keeps Active (working now), Queue (up next), and Done (finished) in one place so the work history stays with the project. You can also manage it from `phren shell`.
36
36
 
37
37
  **global/CLAUDE.md** applies everywhere. Your style preferences, tool choices, things Claude should always know regardless of which project you're in.
38
38
 
@@ -40,14 +40,14 @@ New to phren? Here's what each file does and when it matters.
40
40
 
41
41
  ## Getting started
42
42
 
43
- If you got here via `npx phren init`, you're already set up. Restart Claude Code and you're good.
43
+ If you got here via `npx @phren/cli init`, you're already set up. Restart Claude Code and you're good.
44
44
 
45
45
  If you cloned manually:
46
46
 
47
- 1. Add the MCP server: `claude mcp add phren -- npx phren ~/.phren`
47
+ 1. Add the MCP server: `claude mcp add phren -- npx @phren/cli ~/.phren`
48
48
  2. Install skills: `/plugin marketplace add alaarab/phren` then `/plugin install phren@phren`
49
49
  3. Restart Claude Code
50
- 4. Add a project: run `/phren-init my-project` or scaffold one with a template such as `npx phren init --template python-project`
50
+ 4. Add a project: run `/phren-init my-project` or scaffold one with a template such as `phren init --template python-project`
51
51
  5. Push to a private GitHub repo to sync across machines
52
52
 
53
53
  ## Day-to-day workflow
@@ -56,7 +56,7 @@ If you cloned manually:
56
56
  2. **Work normally**: Claude reads your project docs and builds on what phren remembers
57
57
  3. **Fragments accumulate**: tell phren what you learned, or he picks up insights automatically
58
58
  4. **Session ends**: phren commits and pushes what he collected
59
- 5. **Review occasionally**: run `npx phren shell` to triage what phren queued, manage tasks, and check health
59
+ 5. **Review occasionally**: run `phren shell` to triage what phren queued, manage tasks, and check health
60
60
 
61
61
  ## Syncing across machines
62
62
 
@@ -71,4 +71,4 @@ Each profile in `profiles/` lists which projects that machine should see. After
71
71
 
72
72
  ## Troubleshooting
73
73
 
74
- Run `npx phren doctor --fix` to check and repair your setup.
74
+ Run `phren doctor --fix` to check and repair your setup.
@@ -5,4 +5,4 @@
5
5
  # work-laptop: work
6
6
  # home-desktop: personal
7
7
  #
8
- # After adding a line, run: npx phren init
8
+ # After adding a line, run: phren init
@@ -22,4 +22,4 @@
22
22
 
23
23
  ## Done
24
24
 
25
- - [x] Run `npx phren init` to set up phren
25
+ - [x] Run `phren init` to set up phren
@@ -1,6 +1,6 @@
1
1
  # starter/templates/
2
2
 
3
- Project templates bundled in the npm package, used by `npx phren init --template <name>`.
3
+ Project templates bundled in the npm package, used by `phren init --template <name>`.
4
4
 
5
5
  Each subdirectory (frontend, library, monorepo, python-project) contains pre-filled project files with sensible defaults for that project type. When a user runs init with `--template`, these files are copied into their `~/.phren/<project>/` directory. For other project types, adaptive init infers topics and structure from repo content.
6
6