@drewpayment/mink 0.13.0-beta.1 → 0.13.0-beta.3

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.
Files changed (105) hide show
  1. package/README.md +4 -20
  2. package/dashboard/out/404.html +1 -1
  3. package/dashboard/out/_next/static/U9AeObddt4LmJkKRZpEfy/_buildManifest.js +1 -0
  4. package/dashboard/out/_next/static/chunks/app/(panels)/activity/page-c285fb9f63d9a82a.js +1 -0
  5. package/dashboard/out/_next/static/chunks/app/(panels)/bugs/page-f3ba7d8f50a96568.js +1 -0
  6. package/dashboard/out/_next/static/chunks/app/(panels)/capture/page-e004bec9af99a244.js +1 -0
  7. package/dashboard/out/_next/static/chunks/app/(panels)/compression/page-21e1af119b3f81ff.js +1 -0
  8. package/dashboard/out/_next/static/chunks/app/(panels)/config/page-d47fb6f588ccfd4b.js +1 -0
  9. package/dashboard/out/_next/static/chunks/app/(panels)/daemon/page-52f913e751416717.js +1 -0
  10. package/dashboard/out/_next/static/chunks/app/(panels)/design/page-53a76719b9af5830.js +1 -0
  11. package/dashboard/out/_next/static/chunks/app/(panels)/discord/page-04502d12c4a96cf7.js +1 -0
  12. package/dashboard/out/_next/static/chunks/app/(panels)/file-index/page-a1bd10e04bb219d9.js +1 -0
  13. package/dashboard/out/_next/static/chunks/app/(panels)/insights/page-7367274963571b6b.js +1 -0
  14. package/dashboard/out/_next/static/chunks/app/(panels)/learning/{page-b766adc79099adb4.js → page-4a03cf7b9a6106fd.js} +1 -1
  15. package/dashboard/out/_next/static/chunks/app/(panels)/overview/page-38b8430b5c56e807.js +1 -0
  16. package/dashboard/out/_next/static/chunks/app/(panels)/scheduler/page-510b78c9b0a61012.js +1 -0
  17. package/dashboard/out/_next/static/chunks/app/(panels)/sync/page-b7215c2a29a7d7a7.js +1 -0
  18. package/dashboard/out/_next/static/chunks/app/(panels)/tokens/page-1be7ed35a5c9bd39.js +1 -0
  19. package/dashboard/out/_next/static/chunks/app/(panels)/waste/page-24a726e6d63f771a.js +1 -0
  20. package/dashboard/out/_next/static/chunks/app/(panels)/wiki/page-230d2d1cae6507a8.js +1 -0
  21. package/dashboard/out/_next/static/chunks/app/layout-70a6d18f8e464960.js +1 -0
  22. package/dashboard/out/action-log.html +1 -1
  23. package/dashboard/out/action-log.txt +4 -4
  24. package/dashboard/out/activity.html +1 -1
  25. package/dashboard/out/activity.txt +5 -5
  26. package/dashboard/out/bugs.html +1 -1
  27. package/dashboard/out/bugs.txt +5 -5
  28. package/dashboard/out/capture.html +1 -1
  29. package/dashboard/out/capture.txt +5 -5
  30. package/dashboard/out/compression.html +1 -0
  31. package/dashboard/out/compression.txt +24 -0
  32. package/dashboard/out/config.html +1 -1
  33. package/dashboard/out/config.txt +5 -5
  34. package/dashboard/out/daemon.html +1 -1
  35. package/dashboard/out/daemon.txt +5 -5
  36. package/dashboard/out/design.html +1 -1
  37. package/dashboard/out/design.txt +5 -5
  38. package/dashboard/out/discord.html +1 -1
  39. package/dashboard/out/discord.txt +5 -5
  40. package/dashboard/out/file-index.html +1 -1
  41. package/dashboard/out/file-index.txt +5 -5
  42. package/dashboard/out/index.html +1 -1
  43. package/dashboard/out/index.txt +4 -4
  44. package/dashboard/out/insights.html +1 -1
  45. package/dashboard/out/insights.txt +5 -5
  46. package/dashboard/out/learning.html +1 -1
  47. package/dashboard/out/learning.txt +5 -5
  48. package/dashboard/out/overview.html +1 -1
  49. package/dashboard/out/overview.txt +5 -5
  50. package/dashboard/out/scheduler.html +1 -1
  51. package/dashboard/out/scheduler.txt +5 -5
  52. package/dashboard/out/sync.html +1 -1
  53. package/dashboard/out/sync.txt +5 -5
  54. package/dashboard/out/tokens.html +1 -1
  55. package/dashboard/out/tokens.txt +5 -5
  56. package/dashboard/out/waste.html +1 -1
  57. package/dashboard/out/waste.txt +5 -5
  58. package/dashboard/out/wiki.html +1 -1
  59. package/dashboard/out/wiki.txt +5 -5
  60. package/dist/cli.bun.js +1300 -908
  61. package/dist/cli.node.js +1319 -928
  62. package/package.json +1 -1
  63. package/src/cli.ts +17 -20
  64. package/src/commands/init.ts +14 -123
  65. package/src/commands/post-read.ts +18 -0
  66. package/src/commands/post-tool.ts +48 -0
  67. package/src/commands/retrieve.ts +32 -0
  68. package/src/commands/status.ts +13 -1
  69. package/src/core/code-skeleton.ts +108 -0
  70. package/src/core/compress-tool-output.ts +127 -0
  71. package/src/core/compression.ts +81 -0
  72. package/src/core/dashboard-api.ts +20 -1
  73. package/src/core/dashboard-server.ts +3 -0
  74. package/src/core/hook-output.ts +42 -0
  75. package/src/core/output-compression.ts +252 -0
  76. package/src/core/token-estimate.ts +40 -0
  77. package/src/repositories/compression-cache-repo.ts +97 -0
  78. package/src/repositories/token-ledger-repo.ts +142 -0
  79. package/src/storage/schema.ts +50 -1
  80. package/src/types/compression.ts +29 -0
  81. package/src/types/config.ts +40 -0
  82. package/src/types/dashboard.ts +22 -1
  83. package/src/types/hook-input.ts +4 -0
  84. package/src/types/token-ledger.ts +55 -0
  85. package/dashboard/out/_next/static/UWfkbJY4zr9fSt7O-CAge/_buildManifest.js +0 -1
  86. package/dashboard/out/_next/static/chunks/app/(panels)/activity/page-096a97ba539d5323.js +0 -1
  87. package/dashboard/out/_next/static/chunks/app/(panels)/bugs/page-449d31c133432458.js +0 -1
  88. package/dashboard/out/_next/static/chunks/app/(panels)/capture/page-c6617aa0a8a7333e.js +0 -1
  89. package/dashboard/out/_next/static/chunks/app/(panels)/config/page-aa0a0623b3fdd0d8.js +0 -1
  90. package/dashboard/out/_next/static/chunks/app/(panels)/daemon/page-7cd3fac2f5d87a0d.js +0 -1
  91. package/dashboard/out/_next/static/chunks/app/(panels)/design/page-5304675c96b6793b.js +0 -1
  92. package/dashboard/out/_next/static/chunks/app/(panels)/discord/page-9940dde80ba2a69e.js +0 -1
  93. package/dashboard/out/_next/static/chunks/app/(panels)/file-index/page-ecd8a753614e981e.js +0 -1
  94. package/dashboard/out/_next/static/chunks/app/(panels)/insights/page-7909d8beb8d8ef7a.js +0 -1
  95. package/dashboard/out/_next/static/chunks/app/(panels)/overview/page-7a9e86dcde67d6a9.js +0 -1
  96. package/dashboard/out/_next/static/chunks/app/(panels)/scheduler/page-a88f93204c9742a1.js +0 -1
  97. package/dashboard/out/_next/static/chunks/app/(panels)/sync/page-8a9ad4c36aa6cb65.js +0 -1
  98. package/dashboard/out/_next/static/chunks/app/(panels)/tokens/page-8dac7d50d4db2756.js +0 -1
  99. package/dashboard/out/_next/static/chunks/app/(panels)/waste/page-bcf56144faf7d133.js +0 -1
  100. package/dashboard/out/_next/static/chunks/app/(panels)/wiki/page-a32fdbd0bf58b30b.js +0 -1
  101. package/dashboard/out/_next/static/chunks/app/layout-782cd26e0ccc4514.js +0 -1
  102. package/src/core/agent-detect.ts +0 -88
  103. package/src/core/agent-pi.ts +0 -314
  104. package/src/core/prompt.ts +0 -27
  105. /package/dashboard/out/_next/static/{UWfkbJY4zr9fSt7O-CAge → U9AeObddt4LmJkKRZpEfy}/_ssgManifest.js +0 -0
@@ -1,88 +0,0 @@
1
- import { execSync } from "child_process";
2
- import { existsSync } from "fs";
3
- import { join } from "path";
4
- import { homedir } from "os";
5
-
6
- // Supported host coding assistants Mink can attach to. Adding a new host is a
7
- // matter of appending an entry here plus an installer in init.ts.
8
- export type AgentId = "claude" | "pi";
9
-
10
- export interface AgentMeta {
11
- id: AgentId;
12
- label: string;
13
- /** Project-local config directory that signals the host is used here. */
14
- projectDir: string;
15
- /** Per-user global config directory that signals the host is installed. */
16
- globalDir: string;
17
- /** Executable name to probe on PATH. */
18
- bin: string;
19
- }
20
-
21
- export const AGENTS: AgentMeta[] = [
22
- {
23
- id: "claude",
24
- label: "Claude Code",
25
- projectDir: ".claude",
26
- globalDir: join(homedir(), ".claude"),
27
- bin: "claude",
28
- },
29
- {
30
- id: "pi",
31
- label: "Pi",
32
- projectDir: ".pi",
33
- globalDir: join(homedir(), ".pi"),
34
- bin: "pi",
35
- },
36
- ];
37
-
38
- export interface AgentInfo extends AgentMeta {
39
- detected: boolean;
40
- /** Human-readable reasons the host was (or was not) detected. */
41
- signals: string[];
42
- }
43
-
44
- function commandExists(bin: string): boolean {
45
- try {
46
- const probe = process.platform === "win32" ? `where ${bin}` : `command -v ${bin}`;
47
- execSync(probe, { stdio: "ignore" });
48
- return true;
49
- } catch {
50
- return false;
51
- }
52
- }
53
-
54
- /**
55
- * Inspect a single host's footprint relative to `cwd`. Detection is best-effort
56
- * and layered, strongest signal first: a project-local config directory is the
57
- * clearest sign the host is actually used here; a global config directory or a
58
- * binary on PATH only prove the host is installed somewhere.
59
- */
60
- export function detectAgent(meta: AgentMeta, cwd: string): AgentInfo {
61
- const signals: string[] = [];
62
- if (existsSync(join(cwd, meta.projectDir))) {
63
- signals.push(`project config (${meta.projectDir}/)`);
64
- }
65
- if (existsSync(meta.globalDir)) {
66
- signals.push("global config");
67
- }
68
- if (commandExists(meta.bin)) {
69
- signals.push("on PATH");
70
- }
71
- return { ...meta, detected: signals.length > 0, signals };
72
- }
73
-
74
- export function detectAgents(cwd: string): AgentInfo[] {
75
- return AGENTS.map((m) => detectAgent(m, cwd));
76
- }
77
-
78
- export function resolveTargetsFromFlag(flag: string): AgentId[] {
79
- const normalized = flag.trim().toLowerCase();
80
- if (normalized === "all") return AGENTS.map((a) => a.id);
81
- const ids = normalized
82
- .split(",")
83
- .map((s) => s.trim())
84
- .filter(Boolean);
85
- const valid = AGENTS.map((a) => a.id) as string[];
86
- const resolved = ids.filter((id): id is AgentId => valid.includes(id));
87
- return resolved;
88
- }
@@ -1,314 +0,0 @@
1
- import { join, resolve, dirname } from "path";
2
- import { existsSync, mkdirSync, copyFileSync, rmSync } from "fs";
3
- import { atomicWriteText } from "./fs-utils";
4
-
5
- // ── Paths ───────────────────────────────────────────────────────────────────
6
- // Pi auto-discovers extensions from `.pi/extensions/*.ts` and skills from
7
- // `.pi/skills/*/SKILL.md`, so simply writing these files wires Mink in — no
8
- // `.pi/settings.json` edit required, which keeps us from clobbering the user's
9
- // own Pi configuration.
10
-
11
- export function piExtensionPath(cwd: string): string {
12
- return join(cwd, ".pi", "extensions", "mink.ts");
13
- }
14
-
15
- export function piGuidanceSkillPath(cwd: string): string {
16
- return join(cwd, ".pi", "skills", "mink", "SKILL.md");
17
- }
18
-
19
- export function piNoteSkillPath(cwd: string): string {
20
- return join(cwd, ".pi", "skills", "mink-note", "SKILL.md");
21
- }
22
-
23
- // ── Extension source ─────────────────────────────────────────────────────────
24
-
25
- /**
26
- * Generate the Pi adapter extension. Like the Claude hook wiring, the `mink`
27
- * invocation is templated: an installed package resolves the portable `mink`
28
- * bin shim (so a committed `.pi/` works across machines), while source-dev mode
29
- * falls back to `bun run <abs cli.ts>`.
30
- *
31
- * The adapter shells out to the same `mink` lifecycle commands every other host
32
- * uses, translating Pi's event/tool shapes into Mink's canonical payload. It is
33
- * deliberately defensive: it never throws into Pi, never blocks a tool, and
34
- * times out fast — matching the safety contract of the Claude hooks.
35
- */
36
- export function buildPiExtension(cliPath: string): string {
37
- const isTsSource = cliPath.endsWith(".ts");
38
- const cmd = isTsSource ? "bun" : "mink";
39
- const baseArgs = isTsSource ? JSON.stringify(["run", cliPath]) : "[]";
40
-
41
- return `// AUTO-GENERATED by \`mink init\`. Do not edit — re-run \`mink init\` to refresh.
42
- //
43
- // Mink adapter for the Pi coding agent. Routes Pi lifecycle and tool events
44
- // into the \`mink\` CLI so Pi shares the same ~/.mink state, file index, ledger,
45
- // and wiki as every other assistant wired to this project.
46
- //
47
- // Field shapes confirmed against pi source (earendil-works/pi,
48
- // packages/coding-agent): read params {path, offset, limit}; write {path,
49
- // content}; edit {path, edits:[{oldText,newText}]} (with legacy file_path /
50
- // top-level oldText,newText). tool_call/tool_result events expose toolName,
51
- // toolCallId, input; tool_result also exposes content (a content-block array),
52
- // details, isError. Advisories are surfaced by returning a modified result from
53
- // the tool_result handler — the documented mechanism. pi.exec has no stdin, so
54
- // the canonical payload is piped to \`mink\` via a spawned child process. Every
55
- // host-API access is defensive: the adapter never throws into Pi or blocks a
56
- // tool — a failure degrades to "no advisory".
57
-
58
- import { spawn } from "node:child_process";
59
-
60
- const MINK_CMD = ${JSON.stringify(cmd)};
61
- const MINK_BASE_ARGS = ${baseArgs};
62
- const TIMEOUT_MS = 5000;
63
-
64
- function runMink(sub, payload, cwd) {
65
- return new Promise((res) => {
66
- let done = false;
67
- const finish = (out) => {
68
- if (done) return;
69
- done = true;
70
- res(out);
71
- };
72
- try {
73
- const child = spawn(MINK_CMD, [...MINK_BASE_ARGS, sub], {
74
- cwd,
75
- stdio: ["pipe", "ignore", "pipe"],
76
- });
77
- let stderr = "";
78
- child.stderr?.on("data", (d) => {
79
- stderr += d.toString();
80
- });
81
- child.on("error", () => finish(""));
82
- child.on("close", () => finish(stderr.trim()));
83
- const timer = setTimeout(() => {
84
- try {
85
- child.kill();
86
- } catch {}
87
- finish("");
88
- }, TIMEOUT_MS);
89
- timer.unref?.();
90
- try {
91
- child.stdin?.end(payload ? JSON.stringify(payload) : "");
92
- } catch {}
93
- } catch {
94
- finish("");
95
- }
96
- });
97
- }
98
-
99
- // Pi's edit tool takes an array of { oldText, newText } replacements (legacy
100
- // inputs may put oldText/newText/new_string at the top level). Concatenate the
101
- // replacement text so Mink's write-enforcement sees everything being written.
102
- function editNewText(input) {
103
- if (Array.isArray(input.edits)) {
104
- return input.edits
105
- .map((e) => e?.newText ?? e?.new_string ?? "")
106
- .filter(Boolean)
107
- .join("\\n");
108
- }
109
- return input.newText ?? input.new_string ?? input.replacement ?? "";
110
- }
111
-
112
- // Resolve Pi's tool name + arguments to Mink's canonical operation. Only the
113
- // three file operations matter; anything else returns null and is ignored.
114
- function toolInfo(event) {
115
- const name = String(event?.toolName ?? event?.tool ?? event?.name ?? "").toLowerCase();
116
- const input = event?.input ?? event?.arguments ?? {};
117
- const filePath = input.path ?? input.file_path ?? input.filePath;
118
- if (!filePath) return null;
119
- if (name === "read") return { op: "read", filePath };
120
- if (name === "write") return { op: "write", filePath, content: input.content ?? input.text ?? "" };
121
- if (name === "edit") return { op: "edit", filePath, newString: editNewText(input) };
122
- return null;
123
- }
124
-
125
- // A tool_result's content is an array of content blocks ({ type, text }); pull
126
- // the text out so Mink's post-read can estimate tokens from real content.
127
- function resultContent(event) {
128
- const c = event?.content;
129
- if (typeof c === "string") return c;
130
- if (Array.isArray(c)) {
131
- const text = c
132
- .map((b) => (typeof b === "string" ? b : b?.text ?? ""))
133
- .filter(Boolean)
134
- .join("\\n");
135
- return text || null;
136
- }
137
- return null;
138
- }
139
-
140
- function prePayload(info) {
141
- if (info.op === "read")
142
- return { sub: "pre-read", payload: { tool_name: "Read", tool_input: { file_path: info.filePath } } };
143
- if (info.op === "write")
144
- return {
145
- sub: "pre-write",
146
- payload: { tool_name: "Write", tool_input: { file_path: info.filePath, content: info.content } },
147
- };
148
- return {
149
- sub: "pre-write",
150
- payload: { tool_name: "Edit", tool_input: { file_path: info.filePath, new_string: info.newString } },
151
- };
152
- }
153
-
154
- function postPayload(info, content) {
155
- if (info.op === "read")
156
- return {
157
- sub: "post-read",
158
- payload: {
159
- tool_name: "Read",
160
- tool_input: { file_path: info.filePath },
161
- tool_output: content == null ? undefined : { content },
162
- },
163
- };
164
- if (info.op === "write")
165
- return {
166
- sub: "post-write",
167
- payload: { tool_name: "Write", tool_input: { file_path: info.filePath, content: info.content } },
168
- };
169
- return {
170
- sub: "post-write",
171
- payload: { tool_name: "Edit", tool_input: { file_path: info.filePath, new_string: info.newString } },
172
- };
173
- }
174
-
175
- export default function (pi) {
176
- const cwd = pi?.ctx?.cwd || process.cwd();
177
- const pending = new Map();
178
- const keyOf = (event) => event?.toolCallId ?? event?.id ?? event?.callId ?? "";
179
-
180
- pi.on?.("session_start", (event) => {
181
- // Skip hot-reloads so an extension reload mid-task doesn't reset the
182
- // ephemeral session state; every genuinely new/resumed session starts fresh.
183
- if (event?.reason === "reload") return;
184
- void runMink("session-start", null, cwd);
185
- });
186
- pi.on?.("agent_end", () => {
187
- void runMink("session-stop", null, cwd);
188
- });
189
- pi.on?.("session_shutdown", () => {
190
- void runMink("session-stop", null, cwd);
191
- });
192
-
193
- pi.on?.("tool_call", async (event) => {
194
- const info = toolInfo(event);
195
- if (!info) return;
196
- const { sub, payload } = prePayload(info);
197
- const advisory = await runMink(sub, payload, cwd);
198
- if (advisory) pending.set(keyOf(event), advisory);
199
- });
200
-
201
- pi.on?.("tool_result", async (event) => {
202
- const info = toolInfo(event);
203
- if (!info) return;
204
- const { sub, payload } = postPayload(info, resultContent(event));
205
- const post = await runMink(sub, payload, cwd);
206
- const pre = pending.get(keyOf(event)) ?? "";
207
- pending.delete(keyOf(event));
208
- const advisory = [pre, post].filter(Boolean).join("\\n");
209
- if (!advisory) return;
210
-
211
- // Surface Mink's advisory into the model's context by appending a text
212
- // block to the tool result — the parity of Claude feeding hook stderr back.
213
- const base = Array.isArray(event.content)
214
- ? event.content
215
- : typeof event.content === "string"
216
- ? [{ type: "text", text: event.content }]
217
- : [];
218
- return {
219
- content: [...base, { type: "text", text: advisory }],
220
- details: event.details,
221
- isError: event.isError,
222
- };
223
- });
224
- }
225
- `;
226
- }
227
-
228
- // ── Guidance skill ───────────────────────────────────────────────────────────
229
- // Pi has no automatic project-rules file (CLAUDE.md / AGENTS.md equivalent), so
230
- // the guidance Mink gives the assistant is delivered as a Pi skill instead.
231
-
232
- const PI_GUIDANCE_SKILL = `---
233
- name: mink
234
- description: Mink context management is active in this project. Read this to understand how Mink memory, write enforcement, and note capture work under Pi.
235
- ---
236
-
237
- # Mink — context management for this project
238
-
239
- This project uses **Mink** (\`@drewpayment/mink\`) for cross-session context management.
240
-
241
- ## How it works
242
- - Mink runs automatically through a Pi extension at \`.pi/extensions/mink.ts\` that hooks session start/stop and every read/edit/write tool call.
243
- - All state lives in \`~/.mink/\` on the user's machine — **not** in this repository. Do not create or write to any in-repo state directory (no \`.wolf/\`, \`.mink/\`, etc.).
244
- - Read intelligence, write enforcement, bug memory, and the token ledger are handled by the extension. You do not need to manually read or update any state files.
245
- - Mink shares one \`~/.mink/\` state across every assistant wired to this project, so history is unified whether the user runs Pi or another assistant.
246
-
247
- ## When to act on Mink
248
- - If the user asks to "save a note", "remember this", "log this to my wiki", or similar, use the \`mink-note\` skill (\`/skill:mink-note\`) — it captures into the user's \`~/.mink/\` vault.
249
- - If the extension surfaces a learning, past bug, or repeat-read warning in context, treat that as authoritative project memory and follow it.
250
- - The \`mink dashboard\` and \`mink agent\` commands are user tools — do not invoke them on the user's behalf.
251
- `;
252
-
253
- function resolveSkillsSourceDir(): string {
254
- // Walk up until we find a package root that contains skills/ — works from
255
- // src/core/agent-pi.ts (dev) and dist/cli.js (installed), which sit at
256
- // different depths relative to the package root.
257
- let dir = dirname(new URL(import.meta.url).pathname);
258
- while (true) {
259
- if (existsSync(join(dir, "package.json")) && existsSync(join(dir, "skills"))) {
260
- return join(dir, "skills");
261
- }
262
- const parent = dirname(dir);
263
- if (parent === dir) break;
264
- dir = parent;
265
- }
266
- return resolve(dirname(new URL(import.meta.url).pathname), "../../skills");
267
- }
268
-
269
- export interface PiInstallResult {
270
- extensionPath: string;
271
- guidancePath: string;
272
- notePath: string | null;
273
- }
274
-
275
- export function installPi(cwd: string, cliPath: string): PiInstallResult {
276
- const extensionPath = piExtensionPath(cwd);
277
- mkdirSync(dirname(extensionPath), { recursive: true });
278
- atomicWriteText(extensionPath, buildPiExtension(cliPath));
279
-
280
- const guidancePath = piGuidanceSkillPath(cwd);
281
- mkdirSync(dirname(guidancePath), { recursive: true });
282
- atomicWriteText(guidancePath, PI_GUIDANCE_SKILL);
283
-
284
- // Mirror the note-capture skill into Pi's skill directory so the single
285
- // source of truth (skills/mink-note) is reused rather than duplicated.
286
- let notePath: string | null = null;
287
- try {
288
- const src = join(resolveSkillsSourceDir(), "mink-note", "SKILL.md");
289
- if (existsSync(src)) {
290
- notePath = piNoteSkillPath(cwd);
291
- mkdirSync(dirname(notePath), { recursive: true });
292
- copyFileSync(src, notePath);
293
- }
294
- } catch {
295
- // Note skill is non-critical — the extension and guidance still work.
296
- notePath = null;
297
- }
298
-
299
- return { extensionPath, guidancePath, notePath };
300
- }
301
-
302
- export function removePi(cwd: string): void {
303
- for (const p of [
304
- piExtensionPath(cwd),
305
- join(cwd, ".pi", "skills", "mink"),
306
- join(cwd, ".pi", "skills", "mink-note"),
307
- ]) {
308
- try {
309
- rmSync(p, { recursive: true, force: true });
310
- } catch {
311
- // best-effort
312
- }
313
- }
314
- }
@@ -1,27 +0,0 @@
1
- import { createInterface } from "readline";
2
-
3
- /**
4
- * Whether the current process can safely prompt the user. We require a real
5
- * interactive TTY on both stdin and stdout and honor the usual escape hatches
6
- * (CI, an explicit opt-out) so `mink init` never blocks a script or pipeline.
7
- */
8
- export function stdinIsInteractive(): boolean {
9
- const stdin = process.stdin as NodeJS.ReadStream & { isTTY?: boolean };
10
- const stdout = process.stdout as NodeJS.WriteStream & { isTTY?: boolean };
11
- return (
12
- Boolean(stdin.isTTY) &&
13
- Boolean(stdout.isTTY) &&
14
- process.env.MINK_NO_PROMPT !== "1" &&
15
- !process.env.CI
16
- );
17
- }
18
-
19
- export function ask(question: string): Promise<string> {
20
- return new Promise((resolve) => {
21
- const rl = createInterface({ input: process.stdin, output: process.stdout });
22
- rl.question(question, (answer) => {
23
- rl.close();
24
- resolve(answer);
25
- });
26
- });
27
- }