@phren/cli 0.0.53 → 0.0.55

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
@@ -10,7 +10,7 @@
10
10
  </p>
11
11
 
12
12
  <p align="center">
13
- Every time you start a new session, your AI agent forgets everything it learned. Phren fixes that. Findings, decisions, and patterns persist as markdown in a git repo you control. No database, no hosted service, no vendor lock-in.
13
+ Persistent memory for AI agents. Findings, tasks, and patterns live in markdown files in a git repo you control. No database, no vendor lock-in. Works with Claude, Copilot, Cursor, and Codex.
14
14
  </p>
15
15
 
16
16
  ---
@@ -21,63 +21,108 @@ Every time you start a new session, your AI agent forgets everything it learned.
21
21
  npx @phren/cli init
22
22
  ```
23
23
 
24
- That single command creates `~/.phren`, wires up MCP, installs hooks, and gives your agents a memory they can actually keep. Re-running on a new machine with an existing remote picks up right where you left off.
24
+ One command. Sets up `~/.phren`, wires up MCP for your tools, installs hooks. Next time you open a project, context starts flowing automatically. On a new machine? Re-run init and you're back in sync.
25
25
 
26
- ## What phren tracks
26
+ ---
27
27
 
28
- - **Findings**: bugs hit, patterns discovered, decisions and their reasoning. Tagged by type (`[pattern]`, `[decision]`, `[pitfall]`, `[observation]`) with per-type decay rates
29
- - **Fragments**: named concepts (auth, build, React) that connect findings across projects. Search for a topic and phren pulls in everything linked to that fragment
30
- - **Tasks**: work items that persist across sessions with priority, pinning, and GitHub issue linking
31
- - **Sessions**: conversation boundaries with summaries and checkpoints, so the next session picks up where the last one left off
32
- - **Skills**: reusable slash commands you teach phren. Drop them in `~/.phren/global/skills/` and they work everywhere
28
+ ## What actually happens
33
29
 
34
- ## How it works
30
+ **When you open a prompt:**
31
+ - Hooks extract keywords from your question
32
+ - Phren searches findings across projects (FTS5 full-text with semantic fallback)
33
+ - Relevant snippets inject into your prompt before you hit send
34
+ - You ask; Claude already knows the gotchas
35
35
 
36
- - **Surfaces relevant context on every prompt** via hooks. Agents build on what they know instead of starting fresh
37
- - **Trust scores decay over time.** Old findings lose confidence. Decisions never decay. Observations expire in 14 days
38
- - **Syncs across machines** through git push/pull. No coordination service
39
- - **Works with Claude Code, Copilot, Cursor, and Codex.** One store, every agent
40
- - **Shell and web UI** for browsing, searching, and triaging (`phren` or `phren web-ui`)
36
+ **When you discover something:**
37
+ - `phren add-finding <project> "finding text"` captures it with optional tags (`[decision]`, `[pattern]`, `[pitfall]`, `[bug]`)
38
+ - Trust scores decay over time; decisions never do; observations expire in 14 days
39
+ - Findings link to fragments (named concepts like "auth" or "build") that connect knowledge across projects
41
40
 
42
- ## Quick start
41
+ **Sessions:**
42
+ - Mark boundaries with `session_start` / `session_end`
43
+ - Next session sees your prior summary, active tasks, recent findings, and where you left off
44
+ - Checkpoints track edited files and failing tests so you can resume exactly where you stopped
43
45
 
44
- ```bash
45
- npx @phren/cli init # set up phren (interactive walkthrough)
46
- ```
46
+ **Tasks:**
47
+ - Add with priority/section. Pin across sessions. Link to GitHub issues.
48
+ - Track completions and cross-project rollups.
47
49
 
48
- Init detects your tools, registers MCP servers, and installs lifecycle hooks. After it finishes, open a prompt in any tracked project. Phren is already injecting context.
50
+ ---
49
51
 
50
- To add a project later, run `phren add` from that directory. To browse what phren knows, run `phren` to open the interactive shell.
52
+ ## Key features
51
53
 
52
- ## Team stores
54
+ ### Fragment graph
55
+ Explore connections visually. Drag nodes to reorganize; graph auto-settles. Click a fragment to see every finding linked to it across all projects.
53
56
 
54
- Phren supports shared team knowledge repos alongside your personal store. A team store is a separate git repo that multiple people push to. Findings, tasks, and skills saved there are visible to everyone on the team.
57
+ ### Finding lifecycle
58
+ - **Supersede**: "Finding X is obsoleted by finding Y"
59
+ - **Retract**: "We were wrong about this; here's why"
60
+ - **Contradict**: "We have two findings that conflict; this is why"
55
61
 
56
- Create a team store:
62
+ Helps you reason about contradictions instead of hiding them.
57
63
 
58
- ```bash
59
- phren team init my-team --remote git@github.com:org/phren-team.git
60
- phren team add-project my-team my-project
61
- ```
64
+ ### Multi-agent support
65
+ Same store works with Claude Code, Copilot, Cursor, and Codex. Agents tag findings with their tool, so you see who discovered what.
66
+
67
+ ### Review queue
68
+ Mark findings as needing review (`[Review]` section). Phren surfaces review items on every session start. Approve, reject, or edit in place.
62
69
 
63
- Join an existing team store:
70
+ ### Governance & policies
71
+ Per-project retention policies. Confidence decay curves. Access control. Audit logs. Configure with `phren config` or the web UI.
64
72
 
73
+ ### Store subscriptions
74
+ Subscribe to specific projects in a team store — others stay hidden from search and context injection:
65
75
  ```bash
66
- phren team join git@github.com:org/phren-team.git
76
+ phren store subscribe team-store arc intranet
77
+ phren store unsubscribe team-store legacy-projects
67
78
  ```
68
79
 
69
- Each team store syncs independently. Run `phren team list` to see all registered stores.
80
+ ### Progressive disclosure
81
+ Enable `PHREN_FEATURE_PROGRESSIVE_DISCLOSURE=1` to get compact memory indices instead of full snippets. Call `get_memory_detail(id)` to expand only what you need.
82
+
83
+ ### Semantic dedup & conflict detection
84
+ Optional: enable LLM-based duplicate detection and contradiction flagging on `add_finding`. Prevents near-duplicate entries and catches "always use X" vs "never use X" contradictions.
70
85
 
71
- ### Filtering Team Store Projects
86
+ ### Skills & hooks
87
+ Drop custom slash commands into `~/.phren/global/skills/`. Hooks run on user prompt, tool use, and session events — wire phren into your own workflows.
88
+
89
+ ---
72
90
 
73
- Subscribe to only the projects you care about:
91
+ ## CLI quick reference
74
92
 
75
93
  ```bash
76
- phren store subscribe qualus-shared arc intranet ogrid
77
- phren store unsubscribe qualus-shared dendron powergrid-api
94
+ phren Interactive shell (explore/search)
95
+ phren search <query> Full-text search with FTS5
96
+ phren add-finding <project> "insight" Capture a finding
97
+ phren task add <project> "item" Add a task
98
+ phren session_start <project> Start a session
99
+ phren store list List personal + team stores
100
+ phren team init <name> --remote <url> Create a team store
101
+ phren team join <url> Join a team store
102
+ phren web-ui [--port 3499] Launch the web UI
103
+ phren doctor Health check & auto-fix
78
104
  ```
79
105
 
80
- Unsubscribed projects still exist in the store but won't appear in search, UI, or context injection.
106
+ See full CLI docs at [alaarab.github.io/phren](https://alaarab.github.io/phren/).
107
+
108
+ ---
109
+
110
+ ## Team stores
111
+
112
+ Shared knowledge repos for teams. One person creates with `phren team init`, others join with `phren team join`. Findings, tasks, and skills sync across team members.
113
+
114
+ Each team store can be configured with per-project subscriptions so people only see what they care about.
115
+
116
+ ---
117
+
118
+ ## Platforms
119
+
120
+ - **Claude Code** (VS Code, Web, Desktop) — MCP hooks + CLI
121
+ - **Copilot** (VS Code, GitHub.com) — MCP hooks
122
+ - **Cursor** (IDE) — MCP hooks + built-in skill system
123
+ - **Codex** (Claude Agent SDK) — MCP tools + hooks
124
+
125
+ All use the same phren store. No vendor lock-in.
81
126
 
82
127
  ---
83
128
 
@@ -7,10 +7,10 @@ import { handleExtractMemories } from "./extract.js";
7
7
  import { handleGovernMemories, handlePruneMemories, handleConsolidateMemories, handleMaintain, handleBackgroundMaintenance, } from "./govern.js";
8
8
  import { handleConfig, handleIndexPolicy, handleRetentionPolicy, handleWorkflowPolicy, } from "./config.js";
9
9
  import { parseSearchArgs } from "./search.js";
10
- import { handleDetectSkills, handleFindingNamespace, handleHooksNamespace, handleProjectsNamespace, handleSkillsNamespace, handleSkillList, handlePromoteNamespace, handleStoreNamespace, handleTaskNamespace, } from "./namespaces.js";
10
+ import { handleDetectSkills, handleFindingNamespace, handleHooksNamespace, handleProfileNamespace, handleProjectsNamespace, handleSkillsNamespace, handleSkillList, handlePromoteNamespace, handleReviewNamespace, handleStoreNamespace, handleTaskNamespace, } from "./namespaces.js";
11
11
  import { handleTeamNamespace } from "./team.js";
12
12
  import { handleTaskView, handleSessionsView, handleQuickstart, handleDebugInjection, handleInspectIndex, } from "./ops.js";
13
- import { handleAddFinding, handleDoctor, handleFragmentSearch, handleMemoryUi, handleTruths, handlePinCanonical, handleQualityFeedback, handleRelatedDocs, handleReview, handleConsolidationStatus, handleSessionContext, handleSearch, handleShell, handleStatus, handleUpdate, } from "./actions.js";
13
+ import { handleAddFinding, handleDoctor, handleFragmentSearch, handleMemoryUi, handleTruths, handlePinCanonical, handleQualityFeedback, handleRelatedDocs, handleConsolidationStatus, handleSessionContext, handleSearch, handleShell, handleStatus, handleUpdate, } from "./actions.js";
14
14
  import { handleGraphNamespace } from "./graph.js";
15
15
  import { resolveRuntimeProfile } from "../runtime-profile.js";
16
16
  // ── CLI router ───────────────────────────────────────────────────────────────
@@ -103,7 +103,7 @@ export async function runCliCommand(command, args) {
103
103
  case "graph":
104
104
  return handleGraphNamespace(args);
105
105
  case "review":
106
- return handleReview(args);
106
+ return handleReviewNamespace(args);
107
107
  case "consolidation-status":
108
108
  return handleConsolidationStatus(args);
109
109
  case "session-context":
@@ -112,6 +112,8 @@ export async function runCliCommand(command, args) {
112
112
  return handleTruths(args[0]);
113
113
  case "store":
114
114
  return handleStoreNamespace(args);
115
+ case "profile":
116
+ return handleProfileNamespace(args);
115
117
  case "team":
116
118
  return handleTeamNamespace(args);
117
119
  case "promote":
@@ -1771,6 +1771,82 @@ export async function handleStoreNamespace(args) {
1771
1771
  printStoreUsage();
1772
1772
  process.exit(1);
1773
1773
  }
1774
+ // ── Profile namespace ────────────────────────────────────────────────────────
1775
+ function printProfileUsage() {
1776
+ console.log("Usage:");
1777
+ console.log(" phren profile list List all available profiles");
1778
+ console.log(" phren profile switch <name> Switch to an active profile");
1779
+ }
1780
+ export function handleProfileNamespace(args) {
1781
+ const subcommand = args[0];
1782
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
1783
+ printProfileUsage();
1784
+ return;
1785
+ }
1786
+ const phrenPath = getPhrenPath();
1787
+ if (subcommand === "list") {
1788
+ const { listProfiles } = require("../profile-store.js");
1789
+ const result = listProfiles(phrenPath);
1790
+ if (!result.ok) {
1791
+ console.error(`Failed to list profiles: ${result.error}`);
1792
+ process.exit(1);
1793
+ }
1794
+ const profiles = result.data || [];
1795
+ if (profiles.length === 0) {
1796
+ console.log("No profiles available.");
1797
+ return;
1798
+ }
1799
+ const { listMachines } = require("../profile-store.js");
1800
+ const machinesResult = listMachines(phrenPath);
1801
+ const machines = machinesResult.ok ? machinesResult.data : {};
1802
+ const { getMachineName } = require("../machine-identity.js");
1803
+ const currentMachine = getMachineName();
1804
+ const activeProfile = machines[currentMachine];
1805
+ console.log(`${profiles.length} profile(s):\n`);
1806
+ for (const profile of profiles) {
1807
+ const isCurrent = profile.name === activeProfile ? " (current)" : "";
1808
+ const projectCount = profile.projects?.length ?? 0;
1809
+ console.log(` ${profile.name}${isCurrent}`);
1810
+ console.log(` projects: ${projectCount}`);
1811
+ console.log();
1812
+ }
1813
+ return;
1814
+ }
1815
+ if (subcommand === "switch") {
1816
+ const profileName = args[1];
1817
+ if (!profileName) {
1818
+ console.error("Usage: phren profile switch <name>");
1819
+ process.exit(1);
1820
+ }
1821
+ const { setMachineProfile, getDefaultMachineAlias, listProfiles } = require("../profile-store.js");
1822
+ // Validate that profile exists
1823
+ const listResult = listProfiles(phrenPath);
1824
+ if (!listResult.ok) {
1825
+ console.error(`Failed to list profiles: ${listResult.error}`);
1826
+ process.exit(1);
1827
+ }
1828
+ const profiles = listResult.data || [];
1829
+ if (!profiles.some((p) => p.name === profileName)) {
1830
+ console.error(`Profile not found: "${profileName}"`);
1831
+ console.log("Available profiles:");
1832
+ for (const p of profiles) {
1833
+ console.log(` - ${p.name}`);
1834
+ }
1835
+ process.exit(1);
1836
+ }
1837
+ const machineAlias = getDefaultMachineAlias();
1838
+ const result = setMachineProfile(phrenPath, machineAlias, profileName);
1839
+ if (!result.ok) {
1840
+ console.error(`Failed to switch profile: ${result.error}`);
1841
+ process.exit(1);
1842
+ }
1843
+ console.log(`Switched to profile: ${profileName} (machine: ${machineAlias})`);
1844
+ return;
1845
+ }
1846
+ console.error(`Unknown profile subcommand: ${subcommand}`);
1847
+ printProfileUsage();
1848
+ process.exit(1);
1849
+ }
1774
1850
  // ── Promote namespace ────────────────────────────────────────────────────────
1775
1851
  export async function handlePromoteNamespace(args) {
1776
1852
  if (!args[0] || args[0] === "--help" || args[0] === "-h") {
@@ -1836,6 +1912,50 @@ export async function handlePromoteNamespace(args) {
1836
1912
  console.log(`Promoted to ${toStore}/${project}:`);
1837
1913
  console.log(` "${match.text.slice(0, 120)}${match.text.length > 120 ? "..." : ""}"`);
1838
1914
  }
1915
+ // ── Review namespace ────────────────────────────────────────────────────────
1916
+ export async function handleReviewNamespace(args) {
1917
+ const subcommand = args[0];
1918
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
1919
+ console.log("Usage:");
1920
+ console.log(" phren review [project] Show review queue items");
1921
+ console.log(" phren review approve <project> <text> Approve and remove item");
1922
+ console.log(" phren review reject <project> <text> Reject and remove item");
1923
+ console.log("");
1924
+ console.log("Examples:");
1925
+ console.log(' phren review myproject');
1926
+ console.log(' phren review approve myproject "Always validate input"');
1927
+ console.log(' phren review reject myproject "Avoid async in loops"');
1928
+ return;
1929
+ }
1930
+ // Handle "approve" and "reject" subcommands
1931
+ if (subcommand === "approve" || subcommand === "reject") {
1932
+ const action = subcommand;
1933
+ const project = args[1];
1934
+ const lineText = args.slice(2).join(" ");
1935
+ if (!project || !lineText) {
1936
+ console.error(`Usage: phren review ${action} <project> <text>`);
1937
+ process.exit(1);
1938
+ }
1939
+ if (!isValidProjectName(project)) {
1940
+ console.error(`Invalid project name: "${project}".`);
1941
+ process.exit(1);
1942
+ }
1943
+ const phrenPath = getPhrenPath();
1944
+ const { approveQueueItem, rejectQueueItem } = await import("../data/access.js");
1945
+ const result = action === "approve"
1946
+ ? approveQueueItem(phrenPath, project, lineText)
1947
+ : rejectQueueItem(phrenPath, project, lineText);
1948
+ if (!result.ok) {
1949
+ console.error(`Failed to ${action} item: ${result.error ?? "Unknown error"}`);
1950
+ process.exit(1);
1951
+ }
1952
+ console.log(`${action === "approve" ? "✓ Approved" : "✗ Rejected"}: ${lineText.slice(0, 100)}${lineText.length > 100 ? "..." : ""}`);
1953
+ return;
1954
+ }
1955
+ // Default: show review queue (first arg is project name if not a subcommand)
1956
+ const { handleReview } = await import("./actions.js");
1957
+ return handleReview(args);
1958
+ }
1839
1959
  function countStoreProjects(store) {
1840
1960
  if (!fs.existsSync(store.path))
1841
1961
  return 0;