fapony 0.6.2 → 0.7.1
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 +144 -156
- package/package.json +5 -6
- package/skill/define-convention/SKILL.md +3 -3
- package/skill/lookup-before-edit/SKILL.md +2 -1
- package/skill/move-to-done/SKILL.md +18 -22
- package/skill/plan-with-pony/SKILL.md +4 -4
- package/skill/review-pony/SKILL.md +10 -12
- package/src/adapters/cli.ts +11 -52
- package/src/adapters/hooks/index.ts +0 -67
- package/src/adapters/hooks/mv-guard.ts +2 -2
- package/src/analyze/discover.ts +1 -1
- package/src/analyze/index.ts +0 -1
- package/src/commands.ts +8 -39
- package/src/core/config.ts +0 -6
- package/src/core/fapony-dir.ts +71 -0
- package/src/core/hook-helpers.ts +1 -2
- package/src/debt/load.ts +3 -5
- package/src/debt/promotion.ts +2 -2
- package/src/debt/scan.ts +1 -1
- package/src/digest/collect.ts +6 -7
- package/src/fael.ts +139 -0
- package/src/hook.ts +1 -81
- package/src/init.ts +30 -90
- package/src/install/antigravity.ts +8 -99
- package/src/install/claude.ts +66 -200
- package/src/install/codex.ts +6 -170
- package/src/install/detect.ts +4 -14
- package/src/install/opencode.ts +50 -486
- package/src/install/types.ts +1 -39
- package/src/install/zcode.ts +5 -91
- package/src/install.ts +9 -35
- package/src/memory.ts +10 -69
- package/src/plan/index.ts +34 -0
- package/src/plan/next.ts +181 -0
- package/src/plan/store.ts +41 -0
- package/src/{mem/commands/plan.ts → plan/sweep.ts} +66 -46
- package/src/seed/plan-seed.ts +10 -9
- package/src/setup.ts +4 -22
- package/src/update.ts +1 -1
- package/templates/PLAN.md +1 -1
- package/src/adapters/hooks/bug-markers.ts +0 -60
- package/src/adapters/hooks/context-data.ts +0 -139
- package/src/adapters/hooks/edit-hint.ts +0 -215
- package/src/adapters/hooks/read-hint.ts +0 -410
- package/src/adapters/hooks/session-start.ts +0 -112
- package/src/adapters/hooks/stop.ts +0 -768
- package/src/adapters/mcp/tools/index.ts +0 -149
- package/src/adapters/mcp/tools/mem.ts +0 -252
- package/src/adapters/mcp/transport.ts +0 -147
- package/src/analyze/cache.ts +0 -162
- package/src/core/mem-log.ts +0 -375
- package/src/init-mem.ts +0 -143
- package/src/install/cursor.ts +0 -167
- package/src/install/utils.ts +0 -29
- package/src/mem/commands/read.ts +0 -738
- package/src/mem/commands/rotate.ts +0 -59
- package/src/mem/commands/where.ts +0 -56
- package/src/mem/commands/write.ts +0 -273
- package/src/mem/engine.ts +0 -315
- package/src/mem/index.ts +0 -201
- package/src/mem/render.ts +0 -66
- package/src/mem/selectors.ts +0 -160
- package/src/mem/store.ts +0 -356
package/src/install/cursor.ts
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
// src/install/cursor.ts — Cursor install provider
|
|
2
|
-
//
|
|
3
|
-
// Writes mcpServers.fapony to ~/.cursor/mcp.json directly (Cursor has no CLI
|
|
4
|
-
// for MCP config) and appends the fapony stop hook to the stop array in
|
|
5
|
-
// ~/.cursor/hooks.json — append, never replace: other tools (e.g.
|
|
6
|
-
// code-review-graph) register hooks in the same file and Cursor runs every
|
|
7
|
-
// entry in the array.
|
|
8
|
-
|
|
9
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
10
|
-
import { homedir } from "node:os";
|
|
11
|
-
import { join } from "node:path";
|
|
12
|
-
import {
|
|
13
|
-
CURSOR_MCP_ENTRY,
|
|
14
|
-
defaultExit,
|
|
15
|
-
INSTALL_ROOT,
|
|
16
|
-
type InstallDeps,
|
|
17
|
-
MCP_KEY,
|
|
18
|
-
} from "./types.js";
|
|
19
|
-
|
|
20
|
-
/** ~/.cursor itself — the app creates it on first run, often before the user
|
|
21
|
-
* ever adds an MCP server or hook, so dir-exists is the detect signal. */
|
|
22
|
-
export function findCursorDir(getHome: () => string): string | null {
|
|
23
|
-
const dir = join(getHome(), ".cursor");
|
|
24
|
-
return existsSync(dir) ? dir : null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function isFaponyEntry(entry: unknown): boolean {
|
|
28
|
-
if (!entry || typeof entry !== "object") return false;
|
|
29
|
-
const cfg = entry as Record<string, unknown>;
|
|
30
|
-
return (
|
|
31
|
-
cfg.command === CURSOR_MCP_ENTRY.command &&
|
|
32
|
-
JSON.stringify(cfg.args) === JSON.stringify(CURSOR_MCP_ENTRY.args)
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function readJsonObject(path: string): Record<string, unknown> | null {
|
|
37
|
-
try {
|
|
38
|
-
const parsed: unknown = JSON.parse(readFileSync(path, "utf-8"));
|
|
39
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
return parsed as Record<string, unknown>;
|
|
43
|
-
} catch {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function cmdInstallCursor(
|
|
49
|
-
dryRun: boolean,
|
|
50
|
-
deps: InstallDeps = {},
|
|
51
|
-
): void {
|
|
52
|
-
const exitFn = deps.exit ?? defaultExit;
|
|
53
|
-
const getHome = deps.homedir ?? homedir;
|
|
54
|
-
const cursorDir = findCursorDir(getHome);
|
|
55
|
-
|
|
56
|
-
if (!cursorDir) {
|
|
57
|
-
console.error(
|
|
58
|
-
`Cursor not found — open Cursor at least once to create ~/.cursor`,
|
|
59
|
-
);
|
|
60
|
-
exitFn(1);
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// --- 1. MCP server (mcpServers.fapony in ~/.cursor/mcp.json) ---
|
|
65
|
-
const mcpPath = join(cursorDir, "mcp.json");
|
|
66
|
-
let mcp: Record<string, unknown> = {};
|
|
67
|
-
let mcpIsNew = true;
|
|
68
|
-
if (existsSync(mcpPath)) {
|
|
69
|
-
const parsed = readJsonObject(mcpPath);
|
|
70
|
-
if (!parsed) {
|
|
71
|
-
console.error(`failed to parse ${mcpPath} — fix or remove it first`);
|
|
72
|
-
exitFn(1);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
mcp = parsed;
|
|
76
|
-
mcpIsNew = false;
|
|
77
|
-
}
|
|
78
|
-
const servers = (mcp.mcpServers ?? {}) as Record<string, unknown>;
|
|
79
|
-
const existing = servers[MCP_KEY];
|
|
80
|
-
if (existing !== undefined && !isFaponyEntry(existing)) {
|
|
81
|
-
console.error(
|
|
82
|
-
`an MCP server named "${MCP_KEY}" exists but points elsewhere — not overwriting.`,
|
|
83
|
-
);
|
|
84
|
-
console.error(` inspect ${mcpPath} and remove it first`);
|
|
85
|
-
exitFn(1);
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (existing !== undefined) {
|
|
89
|
-
console.error(
|
|
90
|
-
`✓ mcpServers.${MCP_KEY} already configured — no change needed`,
|
|
91
|
-
);
|
|
92
|
-
console.error(` (${mcpPath})`);
|
|
93
|
-
} else {
|
|
94
|
-
const after = {
|
|
95
|
-
...mcp,
|
|
96
|
-
mcpServers: { ...servers, [MCP_KEY]: CURSOR_MCP_ENTRY },
|
|
97
|
-
};
|
|
98
|
-
if (dryRun) {
|
|
99
|
-
console.error(
|
|
100
|
-
`── dry-run: would ${mcpIsNew ? "create" : "write"} ${mcpPath}${mcpIsNew ? "" : ` (mcpServers.${MCP_KEY})`} ──`,
|
|
101
|
-
);
|
|
102
|
-
} else {
|
|
103
|
-
writeFileSync(mcpPath, `${JSON.stringify(after, null, 2)}\n`, "utf-8");
|
|
104
|
-
console.error(`✓ added mcpServers.${MCP_KEY} to ${mcpPath}`);
|
|
105
|
-
console.error(` restart Cursor to load the MCP server`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// --- 2. Stop hook (hooks.stop array in ~/.cursor/hooks.json) ---
|
|
110
|
-
installStopHook(dryRun, cursorDir);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Append `fapony hook-stop` to the stop array in ~/.cursor/hooks.json.
|
|
115
|
-
* Same policy as Claude's installStopHook: never touch a hook someone else
|
|
116
|
-
* registered, never fail the install over it. User hooks run from ~/.cursor/,
|
|
117
|
-
* so the command must be an absolute path.
|
|
118
|
-
*/
|
|
119
|
-
function installStopHook(dryRun: boolean, cursorDir: string): void {
|
|
120
|
-
const hooksPath = join(cursorDir, "hooks.json");
|
|
121
|
-
let config: Record<string, unknown> = {};
|
|
122
|
-
if (existsSync(hooksPath)) {
|
|
123
|
-
const parsed = readJsonObject(hooksPath);
|
|
124
|
-
if (!parsed) {
|
|
125
|
-
console.error(
|
|
126
|
-
` stop hook: ${hooksPath} is unreadable or malformed — skipping`,
|
|
127
|
-
);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
config = parsed;
|
|
131
|
-
}
|
|
132
|
-
const hookMap = config.hooks;
|
|
133
|
-
if (
|
|
134
|
-
hookMap !== undefined &&
|
|
135
|
-
(typeof hookMap !== "object" || Array.isArray(hookMap))
|
|
136
|
-
) {
|
|
137
|
-
console.error(
|
|
138
|
-
` stop hook: ${hooksPath} has an unexpected "hooks" shape — skipping`,
|
|
139
|
-
);
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
const map = (hookMap ?? {}) as Record<string, unknown>;
|
|
143
|
-
const stop = Array.isArray(map.stop) ? (map.stop as unknown[]) : [];
|
|
144
|
-
if (JSON.stringify(stop).includes("hook-stop")) {
|
|
145
|
-
console.error(` stop hook: already configured in hooks.json — no change`);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const command = `bun ${join(INSTALL_ROOT, "fapony.ts")} hook-stop`;
|
|
150
|
-
const after = {
|
|
151
|
-
...config,
|
|
152
|
-
version: typeof config.version === "number" ? config.version : 1,
|
|
153
|
-
hooks: { ...map, stop: [...stop, { command }] },
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
if (!dryRun) {
|
|
157
|
-
try {
|
|
158
|
-
writeFileSync(hooksPath, `${JSON.stringify(after, null, 2)}\n`, "utf-8");
|
|
159
|
-
} catch (e) {
|
|
160
|
-
console.error(` stop hook: failed to write — ${(e as Error).message}`);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
console.error(
|
|
165
|
-
` stop hook: ${dryRun ? "would write" : "wrote"} hooks.stop → ${hooksPath}`,
|
|
166
|
-
);
|
|
167
|
-
}
|
package/src/install/utils.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// src/install/utils.ts — shared utilities for install providers
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Compute a line-by-line JSON diff between two objects.
|
|
5
|
-
* Used by OpenCode and ZCode providers for dry-run output.
|
|
6
|
-
*/
|
|
7
|
-
export function computeDiff(
|
|
8
|
-
before: Record<string, unknown>,
|
|
9
|
-
after: Record<string, unknown>,
|
|
10
|
-
): string {
|
|
11
|
-
const beforeStr = JSON.stringify(before, null, 2);
|
|
12
|
-
const afterStr = JSON.stringify(after, null, 2);
|
|
13
|
-
const beforeLines = beforeStr.split("\n");
|
|
14
|
-
const afterLines = afterStr.split("\n");
|
|
15
|
-
|
|
16
|
-
const diff: string[] = [];
|
|
17
|
-
const max = Math.max(beforeLines.length, afterLines.length);
|
|
18
|
-
for (let i = 0; i < max; i++) {
|
|
19
|
-
const b = beforeLines[i];
|
|
20
|
-
const a = afterLines[i];
|
|
21
|
-
if (b !== a) {
|
|
22
|
-
if (b !== undefined) diff.push(`- ${b}`);
|
|
23
|
-
if (a !== undefined) diff.push(`+ ${a}`);
|
|
24
|
-
} else {
|
|
25
|
-
diff.push(` ${b}`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return diff.join("\n");
|
|
29
|
-
}
|