@phren/cli 0.0.56 → 0.0.57

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.
@@ -0,0 +1,101 @@
1
+ import { listProjectCards } from "../data/access.js";
2
+ import { renderShell } from "./view.js";
3
+ import { getListItems } from "./input.js";
4
+ // ── View cycling order (same as the shell tab bar) ──────────────────────────
5
+ const VIEW_ORDER = [
6
+ "Projects", "Tasks", "Findings", "Review Queue", "Skills", "Hooks",
7
+ ];
8
+ // ── Render ──────────────────────────────────────────────────────────────────
9
+ /** Render a full shell frame for the given state. Read-only, no mutations. */
10
+ export async function renderMenuFrame(phrenPath, profile, state) {
11
+ const shellState = {
12
+ version: 3,
13
+ view: state.view,
14
+ project: state.project,
15
+ filter: state.filter,
16
+ };
17
+ const ctx = {
18
+ phrenPath,
19
+ profile,
20
+ state: shellState,
21
+ currentCursor: () => state.cursor,
22
+ currentScroll: () => state.scroll,
23
+ setScroll: () => { },
24
+ };
25
+ const stubDoctor = async () => ({
26
+ ok: true,
27
+ checks: [],
28
+ });
29
+ const output = await renderShell(ctx, "navigate", // always navigate mode (no input mode in agent menu)
30
+ "", // no inputCtx
31
+ "", // no inputBuf
32
+ false, // no help
33
+ "Tab: Chat ←→: Views ↑↓: Navigate Enter: Select /: Filter Esc: Back", stubDoctor, null, // subsectionsCache
34
+ () => { }, // setHealthLineCount
35
+ () => { });
36
+ const listCount = getListItems(phrenPath, profile, shellState, 0).length;
37
+ return { output, listCount };
38
+ }
39
+ // ── Navigation (pure function) ──────────────────────────────────────────────
40
+ /**
41
+ * Apply a key to the menu state. Returns a new state, or null to signal
42
+ * "exit menu mode" (Tab or Escape at top level).
43
+ */
44
+ export function handleMenuKey(state, keyName, listCount, phrenPath, profile) {
45
+ switch (keyName) {
46
+ // Exit menu
47
+ case "tab":
48
+ case "q":
49
+ return null;
50
+ // View cycling
51
+ case "left": {
52
+ const idx = VIEW_ORDER.indexOf(state.view);
53
+ const next = idx <= 0 ? VIEW_ORDER[VIEW_ORDER.length - 1] : VIEW_ORDER[idx - 1];
54
+ return { ...state, view: next, cursor: 0, scroll: 0, filter: undefined };
55
+ }
56
+ case "right": {
57
+ const idx = VIEW_ORDER.indexOf(state.view);
58
+ const next = idx >= VIEW_ORDER.length - 1 ? VIEW_ORDER[0] : VIEW_ORDER[idx + 1];
59
+ return { ...state, view: next, cursor: 0, scroll: 0, filter: undefined };
60
+ }
61
+ // Cursor movement
62
+ case "up":
63
+ return { ...state, cursor: Math.max(0, state.cursor - 1) };
64
+ case "down":
65
+ return { ...state, cursor: Math.min(Math.max(0, listCount - 1), state.cursor + 1) };
66
+ // Enter: drill into project (Projects view only)
67
+ case "return": {
68
+ if (state.view === "Projects" && phrenPath && profile) {
69
+ const cards = listProjectCards(phrenPath, profile);
70
+ const filtered = state.filter
71
+ ? cards.filter((c) => `${c.name} ${c.summary} ${c.docs.join(" ")}`.toLowerCase().includes(state.filter.toLowerCase()))
72
+ : cards;
73
+ const selected = filtered[state.cursor];
74
+ if (selected) {
75
+ return { ...state, view: "Tasks", project: selected.name, cursor: 0, scroll: 0 };
76
+ }
77
+ }
78
+ // Escape from sub-view back to Projects
79
+ if (state.view !== "Projects" && state.project) {
80
+ return state; // no-op for non-projects views on enter
81
+ }
82
+ return state;
83
+ }
84
+ // Escape: clear filter, or go back to projects, or exit
85
+ case "escape": {
86
+ if (state.filter)
87
+ return { ...state, filter: undefined, cursor: 0, scroll: 0 };
88
+ if (state.view !== "Projects" && state.project) {
89
+ return { ...state, view: "Projects", project: undefined, cursor: 0, scroll: 0 };
90
+ }
91
+ return null; // exit menu
92
+ }
93
+ // Health shortcut
94
+ case "h":
95
+ if (state.view === "Health")
96
+ return { ...state, view: "Projects", cursor: 0, scroll: 0 };
97
+ return { ...state, view: "Health", cursor: 0, scroll: 0 };
98
+ default:
99
+ return state;
100
+ }
101
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phren/cli",
3
- "version": "0.0.56",
3
+ "version": "0.0.57",
4
4
  "description": "Knowledge layer for AI agents — CLI, MCP server, and data layer",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,7 +15,8 @@
15
15
  "./data/access": "./dist/data/access.js",
16
16
  "./data/tasks": "./dist/data/tasks.js",
17
17
  "./core/finding": "./dist/core/finding.js",
18
- "./session/utils": "./dist/session/utils.js"
18
+ "./session/utils": "./dist/session/utils.js",
19
+ "./shell/render-api": "./dist/shell/render-api.js"
19
20
  },
20
21
  "files": [
21
22
  "dist",