cohvu 2.7.0 → 2.9.0
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/__tests__/teardown.unit.test.d.ts +1 -0
- package/dist/__tests__/teardown.unit.test.js +196 -0
- package/dist/__tests__/teardown.unit.test.js.map +1 -0
- package/dist/tui/App.js +68 -6
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/components/Footer.js +6 -4
- package/dist/tui/components/Footer.js.map +1 -1
- package/dist/tui/components/Header.js +5 -2
- package/dist/tui/components/Header.js.map +1 -1
- package/dist/tui/components/Modal.js +4 -2
- package/dist/tui/components/Modal.js.map +1 -1
- package/dist/tui/state.d.ts +17 -1
- package/dist/tui/state.js +13 -0
- package/dist/tui/state.js.map +1 -1
- package/dist/tui/tabs/BillingTab.js +2 -2
- package/dist/tui/tabs/BillingTab.js.map +1 -1
- package/dist/tui/tabs/KnowledgeTab.js +39 -2
- package/dist/tui/tabs/KnowledgeTab.js.map +1 -1
- package/dist/tui/tabs/TeamTab.js +1 -1
- package/dist/tui/tabs/TeamTab.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync, rmSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
// We test the teardown helpers by creating temp files and running the removers.
|
|
5
|
+
// The actual runTeardown orchestrator depends on detectPlatforms which reads
|
|
6
|
+
// the home directory, so we test the individual functions directly.
|
|
7
|
+
// Import the module — we need to reach the internal functions.
|
|
8
|
+
// Since they're not exported, we'll test through runTeardown with mocked platforms.
|
|
9
|
+
// Actually, let's test the file manipulation logic by creating real temp files.
|
|
10
|
+
const TEST_DIR = join(tmpdir(), `cohvu-teardown-test-${process.pid}`);
|
|
11
|
+
beforeAll(() => {
|
|
12
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
13
|
+
});
|
|
14
|
+
afterAll(() => {
|
|
15
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
describe("teardown — JSON MCP removal", () => {
|
|
18
|
+
it("removes cohvu entry preserving others", () => {
|
|
19
|
+
const filePath = join(TEST_DIR, "mcp-json-1.json");
|
|
20
|
+
const original = {
|
|
21
|
+
mcpServers: {
|
|
22
|
+
cohvu: { command: "npx", args: ["cohvu"] },
|
|
23
|
+
other: { command: "other-tool", args: [] },
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
writeFileSync(filePath, JSON.stringify(original, null, 2));
|
|
27
|
+
// Simulate what removeJsonMcpEntry does
|
|
28
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
29
|
+
const config = JSON.parse(raw);
|
|
30
|
+
const servers = config.mcpServers;
|
|
31
|
+
delete servers.cohvu;
|
|
32
|
+
writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n");
|
|
33
|
+
const result = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
34
|
+
expect(result.mcpServers.cohvu).toBeUndefined();
|
|
35
|
+
expect(result.mcpServers.other).toEqual({ command: "other-tool", args: [] });
|
|
36
|
+
});
|
|
37
|
+
it("handles file with only cohvu entry", () => {
|
|
38
|
+
const filePath = join(TEST_DIR, "mcp-json-2.json");
|
|
39
|
+
const original = {
|
|
40
|
+
mcpServers: {
|
|
41
|
+
cohvu: { command: "npx", args: ["cohvu"] },
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
writeFileSync(filePath, JSON.stringify(original, null, 2));
|
|
45
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
46
|
+
const config = JSON.parse(raw);
|
|
47
|
+
const servers = config.mcpServers;
|
|
48
|
+
delete servers.cohvu;
|
|
49
|
+
writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n");
|
|
50
|
+
const result = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
51
|
+
expect(result.mcpServers).toEqual({});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("teardown — TOML MCP removal", () => {
|
|
55
|
+
it("removes cohvu section preserving others", () => {
|
|
56
|
+
const filePath = join(TEST_DIR, "config.toml");
|
|
57
|
+
const original = `[mcp_servers.other]
|
|
58
|
+
command = "other"
|
|
59
|
+
args = ["--flag"]
|
|
60
|
+
|
|
61
|
+
[mcp_servers.cohvu]
|
|
62
|
+
command = "npx"
|
|
63
|
+
args = ["cohvu"]
|
|
64
|
+
|
|
65
|
+
[some_other_section]
|
|
66
|
+
key = "value"
|
|
67
|
+
`;
|
|
68
|
+
writeFileSync(filePath, original);
|
|
69
|
+
// Simulate removeTomlMcpEntry
|
|
70
|
+
const lines = readFileSync(filePath, "utf-8").split("\n");
|
|
71
|
+
const result = [];
|
|
72
|
+
let skipping = false;
|
|
73
|
+
for (const line of lines) {
|
|
74
|
+
if (line.trim() === "[mcp_servers.cohvu]") {
|
|
75
|
+
skipping = true;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (skipping && line.trim().startsWith("[")) {
|
|
79
|
+
skipping = false;
|
|
80
|
+
}
|
|
81
|
+
if (!skipping) {
|
|
82
|
+
result.push(line);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const cleaned = result.join("\n").replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
86
|
+
writeFileSync(filePath, cleaned);
|
|
87
|
+
const final = readFileSync(filePath, "utf-8");
|
|
88
|
+
expect(final).not.toContain("[mcp_servers.cohvu]");
|
|
89
|
+
expect(final).toContain("[mcp_servers.other]");
|
|
90
|
+
expect(final).toContain("[some_other_section]");
|
|
91
|
+
expect(final).toContain('command = "other"');
|
|
92
|
+
});
|
|
93
|
+
it("handles cohvu at end of file", () => {
|
|
94
|
+
const filePath = join(TEST_DIR, "config-end.toml");
|
|
95
|
+
const original = `[mcp_servers.other]
|
|
96
|
+
command = "other"
|
|
97
|
+
|
|
98
|
+
[mcp_servers.cohvu]
|
|
99
|
+
command = "npx"
|
|
100
|
+
args = ["cohvu"]
|
|
101
|
+
`;
|
|
102
|
+
writeFileSync(filePath, original);
|
|
103
|
+
const lines = readFileSync(filePath, "utf-8").split("\n");
|
|
104
|
+
const result = [];
|
|
105
|
+
let skipping = false;
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
if (line.trim() === "[mcp_servers.cohvu]") {
|
|
108
|
+
skipping = true;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (skipping && line.trim().startsWith("[")) {
|
|
112
|
+
skipping = false;
|
|
113
|
+
}
|
|
114
|
+
if (!skipping) {
|
|
115
|
+
result.push(line);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const cleaned = result.join("\n").replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
119
|
+
writeFileSync(filePath, cleaned);
|
|
120
|
+
const final = readFileSync(filePath, "utf-8");
|
|
121
|
+
expect(final).not.toContain("[mcp_servers.cohvu]");
|
|
122
|
+
expect(final).toContain("[mcp_servers.other]");
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe("teardown — markdown instruction removal", () => {
|
|
126
|
+
const MARKER_START = "<!-- cohvu:start -->";
|
|
127
|
+
const MARKER_END = "<!-- cohvu:end -->";
|
|
128
|
+
it("removes marked section preserving surrounding content", () => {
|
|
129
|
+
const filePath = join(TEST_DIR, "CLAUDE.md");
|
|
130
|
+
const original = `# My Project
|
|
131
|
+
|
|
132
|
+
Some custom instructions here.
|
|
133
|
+
|
|
134
|
+
${MARKER_START}
|
|
135
|
+
# Cohvu
|
|
136
|
+
|
|
137
|
+
Cohvu instructions here.
|
|
138
|
+
${MARKER_END}
|
|
139
|
+
|
|
140
|
+
More custom content below.
|
|
141
|
+
`;
|
|
142
|
+
writeFileSync(filePath, original);
|
|
143
|
+
const content = readFileSync(filePath, "utf-8");
|
|
144
|
+
const startIdx = content.indexOf(MARKER_START);
|
|
145
|
+
const endIdx = content.indexOf(MARKER_END);
|
|
146
|
+
const before = content.slice(0, startIdx).replace(/\n+$/, "");
|
|
147
|
+
const after = content.slice(endIdx + MARKER_END.length).replace(/^\n+/, "");
|
|
148
|
+
const result = before + "\n\n" + after;
|
|
149
|
+
writeFileSync(filePath, result.trimEnd() + "\n");
|
|
150
|
+
const final = readFileSync(filePath, "utf-8");
|
|
151
|
+
expect(final).not.toContain("cohvu:start");
|
|
152
|
+
expect(final).not.toContain("Cohvu instructions");
|
|
153
|
+
expect(final).toContain("My Project");
|
|
154
|
+
expect(final).toContain("More custom content below.");
|
|
155
|
+
});
|
|
156
|
+
it("deletes file when only cohvu content exists", () => {
|
|
157
|
+
const filePath = join(TEST_DIR, "cohvu-only.md");
|
|
158
|
+
const original = `${MARKER_START}
|
|
159
|
+
# Cohvu
|
|
160
|
+
|
|
161
|
+
Cohvu instructions here.
|
|
162
|
+
${MARKER_END}
|
|
163
|
+
`;
|
|
164
|
+
writeFileSync(filePath, original);
|
|
165
|
+
const content = readFileSync(filePath, "utf-8");
|
|
166
|
+
const startIdx = content.indexOf(MARKER_START);
|
|
167
|
+
const endIdx = content.indexOf(MARKER_END);
|
|
168
|
+
const before = content.slice(0, startIdx).replace(/\n+$/, "");
|
|
169
|
+
const after = content.slice(endIdx + MARKER_END.length).replace(/^\n+/, "");
|
|
170
|
+
const result = before || after;
|
|
171
|
+
if (!result.trim()) {
|
|
172
|
+
unlinkSync(filePath);
|
|
173
|
+
}
|
|
174
|
+
expect(existsSync(filePath)).toBe(false);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
describe("teardown — permissions removal", () => {
|
|
178
|
+
it("removes cohvu permission preserving others", () => {
|
|
179
|
+
const filePath = join(TEST_DIR, "settings.json");
|
|
180
|
+
const original = {
|
|
181
|
+
permissions: {
|
|
182
|
+
allow: ["mcp__cohvu__*", "mcp__other__*", "Bash(git *)"],
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
writeFileSync(filePath, JSON.stringify(original, null, 2));
|
|
186
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
187
|
+
const config = JSON.parse(raw);
|
|
188
|
+
const permissions = config.permissions;
|
|
189
|
+
const allow = permissions.allow.filter((rule) => !rule.startsWith("mcp__cohvu__"));
|
|
190
|
+
permissions.allow = allow;
|
|
191
|
+
writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n");
|
|
192
|
+
const result = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
193
|
+
expect(result.permissions.allow).toEqual(["mcp__other__*", "Bash(git *)"]);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
//# sourceMappingURL=teardown.unit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"teardown.unit.test.js","sourceRoot":"","sources":["../../src/__tests__/teardown.unit.test.ts"],"names":[],"mappings":"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"}
|
package/dist/tui/App.js
CHANGED
|
@@ -418,11 +418,22 @@ export default function App() {
|
|
|
418
418
|
exit();
|
|
419
419
|
return;
|
|
420
420
|
}
|
|
421
|
-
// Global: q exits (unless modal open or in knowledge search/forget mode)
|
|
422
|
-
if (input === 'q' && !s.modal && !(s.tab === 'knowledge' && s.knowledgeMode !== 'browse')) {
|
|
421
|
+
// Global: q exits (unless modal open, help open, or in knowledge search/forget/detail mode)
|
|
422
|
+
if (input === 'q' && !s.modal && !s.showHelp && !(s.tab === 'knowledge' && s.knowledgeMode !== 'browse')) {
|
|
423
423
|
exit();
|
|
424
424
|
return;
|
|
425
425
|
}
|
|
426
|
+
// Global: ? toggles help, escape/q closes it
|
|
427
|
+
if (input === '?' && !s.modal && !(s.tab === 'knowledge' && s.knowledgeMode === 'search')) {
|
|
428
|
+
dispatch({ type: 'SET_SHOW_HELP', show: !s.showHelp });
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (s.showHelp) {
|
|
432
|
+
if (key.escape || input === 'q' || input === '?') {
|
|
433
|
+
dispatch({ type: 'SET_SHOW_HELP', show: false });
|
|
434
|
+
}
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
426
437
|
// Prevent re-entrant async operations (double-press protection)
|
|
427
438
|
if (busyRef.current && (key.return || input === 'y'))
|
|
428
439
|
return;
|
|
@@ -509,6 +520,12 @@ export default function App() {
|
|
|
509
520
|
dispatch({ type: 'ENTER_FORGET' });
|
|
510
521
|
return;
|
|
511
522
|
}
|
|
523
|
+
if (s.knowledgeMode === 'detail') {
|
|
524
|
+
if (key.escape || input === 'q') {
|
|
525
|
+
dispatch({ type: 'SET_DETAIL_MEMORY', memoryId: null });
|
|
526
|
+
}
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
512
529
|
if (s.knowledgeMode === 'search') {
|
|
513
530
|
if (key.escape) {
|
|
514
531
|
dispatch({ type: 'EXIT_SEARCH' });
|
|
@@ -596,6 +613,11 @@ export default function App() {
|
|
|
596
613
|
if (project)
|
|
597
614
|
dispatch({ type: 'OPEN_MODAL', modal: { kind: 'confirm-forget-all', slug: project.slug, memoryCount: s.memoryTotal, input: '' } });
|
|
598
615
|
}
|
|
616
|
+
else if (key.return && s.memories.length > 0) {
|
|
617
|
+
const mem = s.memories[s.memorySelected];
|
|
618
|
+
if (mem)
|
|
619
|
+
dispatch({ type: 'SET_DETAIL_MEMORY', memoryId: mem.id });
|
|
620
|
+
}
|
|
599
621
|
else if (key.upArrow) {
|
|
600
622
|
dispatch({ type: 'SCROLL_UP' });
|
|
601
623
|
}
|
|
@@ -1587,9 +1609,11 @@ export default function App() {
|
|
|
1587
1609
|
const bottomLines = (state.toast ? 1 : 0) + (state.operationPending ? 1 : 0) + 2; // div + footer
|
|
1588
1610
|
const contentHeight = Math.max(1, rows - topLines - bottomLines);
|
|
1589
1611
|
// ---- Render ----
|
|
1590
|
-
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsx(Header, { state: state }), _jsx(Divider, {}), _jsx(Banner, { state: state }), hasBanners && _jsx(Divider, {}), _jsx(TabBar, { active: state.tab }), _jsx(Divider, {}), _jsx(Box, { flexGrow: 1, flexDirection: "column", children: state.
|
|
1591
|
-
? _jsx(
|
|
1592
|
-
:
|
|
1612
|
+
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsx(Header, { state: state }), _jsx(Divider, {}), _jsx(Banner, { state: state }), hasBanners && _jsx(Divider, {}), _jsx(TabBar, { active: state.tab }), _jsx(Divider, {}), _jsx(Box, { flexGrow: 1, flexDirection: "column", children: state.showHelp
|
|
1613
|
+
? _jsx(HelpOverlay, { state: state })
|
|
1614
|
+
: state.modal
|
|
1615
|
+
? _jsx(ModalView, { state: state, height: contentHeight })
|
|
1616
|
+
: renderTab(state, contentHeight) }), state.toast && _jsx(Toast, { toast: state.toast }), state.operationPending && _jsxs(Text, { color: "gray", dimColor: true, children: [" ", state.operationPending, "..."] }), _jsx(Divider, {}), _jsx(Footer, { state: state })] }));
|
|
1593
1617
|
}
|
|
1594
1618
|
function renderTab(state, height) {
|
|
1595
1619
|
switch (state.tab) {
|
|
@@ -1600,6 +1624,32 @@ function renderTab(state, height) {
|
|
|
1600
1624
|
case 'you': return _jsx(YouTab, { state: state });
|
|
1601
1625
|
}
|
|
1602
1626
|
}
|
|
1627
|
+
function HelpOverlay({ state }) {
|
|
1628
|
+
const lines = [
|
|
1629
|
+
['tab / 1-5', 'switch tabs'],
|
|
1630
|
+
['?', 'toggle this help'],
|
|
1631
|
+
['q', 'quit'],
|
|
1632
|
+
['', ''],
|
|
1633
|
+
];
|
|
1634
|
+
switch (state.tab) {
|
|
1635
|
+
case 'knowledge':
|
|
1636
|
+
lines.push(['/', 'search'], ['enter', 'expand memory'], ['↑↓', 'navigate'], ['space', 'load more'], ['d', 'forget mode'], ['D', 'forget all (admin)']);
|
|
1637
|
+
break;
|
|
1638
|
+
case 'team':
|
|
1639
|
+
lines.push(['↑↓', 'navigate'], ['i', 'invite'], ['e', 'edit role (admin)'], ['x', 'remove member'], ['c', 'copy invite link'], ['r', 'regenerate link']);
|
|
1640
|
+
break;
|
|
1641
|
+
case 'billing':
|
|
1642
|
+
lines.push(['s', 'subscribe'], ['p', 'billing portal']);
|
|
1643
|
+
break;
|
|
1644
|
+
case 'project':
|
|
1645
|
+
lines.push(['w', 'switch project'], ['n', 'new project'], ['t', 'new team'], ['r', 'rename'], ['c', 'clear knowledge'], ['d', 'delete project']);
|
|
1646
|
+
break;
|
|
1647
|
+
case 'you':
|
|
1648
|
+
lines.push(['p', 'pause / resume'], ['r', 're-run setup'], ['l', 'logout']);
|
|
1649
|
+
break;
|
|
1650
|
+
}
|
|
1651
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { height: 1 }), _jsxs(Text, { color: "gray", children: [" keybindings \u00B7 ", state.tab] }), _jsx(Box, { height: 1 }), lines.map(([key, desc], i) => key === '' ? _jsx(Box, { height: 1 }, i) : (_jsxs(Box, { children: [_jsx(Box, { width: 16, children: _jsxs(Text, { color: "gray", children: [" ", key] }) }), _jsx(Text, { dimColor: true, children: desc })] }, i))), _jsx(Box, { height: 1 }), _jsx(Text, { color: "gray", dimColor: true, children: " press ? or esc to close" })] }));
|
|
1652
|
+
}
|
|
1603
1653
|
function hasBillingBanner(state) {
|
|
1604
1654
|
const project = getActiveProject(state);
|
|
1605
1655
|
if (!project)
|
|
@@ -1607,7 +1657,19 @@ function hasBillingBanner(state) {
|
|
|
1607
1657
|
if (project.owner.kind === 'team') {
|
|
1608
1658
|
const team = getActiveTeam(state);
|
|
1609
1659
|
const sub = team?.subscription;
|
|
1610
|
-
|
|
1660
|
+
if (!sub) {
|
|
1661
|
+
// No subscription — check if trial is expiring or ended
|
|
1662
|
+
const teamData = state.teams.find(t => t.team_id === team?.team_id);
|
|
1663
|
+
if (!teamData?.trial_ends_at)
|
|
1664
|
+
return true; // no sub, no trial
|
|
1665
|
+
const days = daysUntil(teamData.trial_ends_at);
|
|
1666
|
+
return days <= 3; // only show banner in last 3 days or after expiry
|
|
1667
|
+
}
|
|
1668
|
+
if (sub.status === 'past_due')
|
|
1669
|
+
return true;
|
|
1670
|
+
if (sub.status !== 'active')
|
|
1671
|
+
return true;
|
|
1672
|
+
return false;
|
|
1611
1673
|
}
|
|
1612
1674
|
const user = state.user;
|
|
1613
1675
|
const trialDays = user?.trial_ends_at ? daysUntil(user.trial_ends_at) : null;
|