@drewpayment/mink 0.1.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/README.md +347 -0
- package/package.json +32 -0
- package/src/cli.ts +176 -0
- package/src/commands/bug-search.ts +32 -0
- package/src/commands/config.ts +109 -0
- package/src/commands/cron.ts +295 -0
- package/src/commands/daemon.ts +46 -0
- package/src/commands/dashboard.ts +21 -0
- package/src/commands/designqc.ts +160 -0
- package/src/commands/detect-waste.ts +81 -0
- package/src/commands/framework-advisor.ts +52 -0
- package/src/commands/init.ts +159 -0
- package/src/commands/post-read.ts +123 -0
- package/src/commands/post-write.ts +157 -0
- package/src/commands/pre-read.ts +109 -0
- package/src/commands/pre-write.ts +136 -0
- package/src/commands/reflect.ts +39 -0
- package/src/commands/restore.ts +31 -0
- package/src/commands/scan.ts +101 -0
- package/src/commands/session-start.ts +21 -0
- package/src/commands/session-stop.ts +115 -0
- package/src/commands/status.ts +152 -0
- package/src/commands/update.ts +121 -0
- package/src/core/action-log.ts +341 -0
- package/src/core/backup.ts +122 -0
- package/src/core/bug-memory.ts +223 -0
- package/src/core/cron-parser.ts +94 -0
- package/src/core/daemon.ts +152 -0
- package/src/core/dashboard-api.ts +280 -0
- package/src/core/dashboard-server.ts +580 -0
- package/src/core/description.ts +232 -0
- package/src/core/design-eval/capture.ts +269 -0
- package/src/core/design-eval/route-detect.ts +165 -0
- package/src/core/design-eval/server-detect.ts +91 -0
- package/src/core/framework-advisor/catalog.ts +360 -0
- package/src/core/framework-advisor/decision-tree.ts +287 -0
- package/src/core/framework-advisor/generate.ts +132 -0
- package/src/core/framework-advisor/migration-prompts.ts +502 -0
- package/src/core/framework-advisor/validate.ts +137 -0
- package/src/core/fs-utils.ts +30 -0
- package/src/core/global-config.ts +74 -0
- package/src/core/index-store.ts +72 -0
- package/src/core/learning-memory.ts +120 -0
- package/src/core/paths.ts +86 -0
- package/src/core/pattern-engine.ts +108 -0
- package/src/core/project-id.ts +19 -0
- package/src/core/project-registry.ts +64 -0
- package/src/core/reflection.ts +256 -0
- package/src/core/scanner.ts +99 -0
- package/src/core/scheduler.ts +352 -0
- package/src/core/seed.ts +239 -0
- package/src/core/session.ts +128 -0
- package/src/core/stdin.ts +13 -0
- package/src/core/task-registry.ts +202 -0
- package/src/core/token-estimate.ts +36 -0
- package/src/core/token-ledger.ts +185 -0
- package/src/core/waste-detection.ts +214 -0
- package/src/core/write-exclusions.ts +24 -0
- package/src/types/action-log.ts +20 -0
- package/src/types/backup.ts +6 -0
- package/src/types/bug-memory.ts +24 -0
- package/src/types/config.ts +59 -0
- package/src/types/dashboard.ts +104 -0
- package/src/types/design-eval.ts +64 -0
- package/src/types/file-index.ts +38 -0
- package/src/types/framework-advisor.ts +97 -0
- package/src/types/hook-input.ts +27 -0
- package/src/types/learning-memory.ts +36 -0
- package/src/types/scheduler.ts +82 -0
- package/src/types/session.ts +50 -0
- package/src/types/token-ledger.ts +43 -0
- package/src/types/waste-detection.ts +21 -0
package/README.md
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# Mink
|
|
2
|
+
|
|
3
|
+
A hidden presence that moves alongside the developer.
|
|
4
|
+
|
|
5
|
+
Mink is a lightweight companion for AI coding assistants like [Claude Code](https://claude.ai/code). It hooks into the assistant's lifecycle events to reduce token waste, enforce learned rules, and build a portable knowledge base across all your projects.
|
|
6
|
+
|
|
7
|
+
## Why Mink?
|
|
8
|
+
|
|
9
|
+
AI coding assistants consume tokens every time they read a file, write code, or reason about your project. Much of this is redundant: re-reading files already seen, repeating mistakes that were already corrected, and lacking context that was available in a previous session.
|
|
10
|
+
|
|
11
|
+
Mink intercepts these lifecycle events and maintains structured state so the assistant can work smarter:
|
|
12
|
+
|
|
13
|
+
- **Track what was already read** and warn before redundant re-reads
|
|
14
|
+
- **Remember past mistakes** and surface them before they're repeated
|
|
15
|
+
- **Enforce learned rules** extracted from corrections you've already given
|
|
16
|
+
- **Log every action** with token cost estimates so you can see where tokens go
|
|
17
|
+
- **Detect token waste** and surface patterns of inefficiency
|
|
18
|
+
- **Run background tasks** on a schedule to keep state fresh
|
|
19
|
+
- **Visualize everything** in a real-time web dashboard
|
|
20
|
+
- **Evaluate UI designs** with automated multi-viewport screenshots
|
|
21
|
+
- **Advise on frameworks** with a decision tree and migration guides
|
|
22
|
+
- **Build a cross-project wiki** that accumulates knowledge across all your projects
|
|
23
|
+
|
|
24
|
+
## How It Works
|
|
25
|
+
|
|
26
|
+
Mink registers as a set of [Claude Code hooks](https://docs.anthropic.com/en/docs/claude-code/hooks) that fire on key lifecycle events. Each hook is a lightweight CLI call that reads and updates JSON state files stored in `~/.mink/`.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
Session Start Read a File Write a File Session Stop
|
|
30
|
+
| | | |
|
|
31
|
+
v v v v
|
|
32
|
+
Create fresh Check file index Check learning Build summary
|
|
33
|
+
session state Track read count memory rules Calculate savings
|
|
34
|
+
Log to action log Warn on repeats Surface past bugs Append to ledger
|
|
35
|
+
Estimate tokens Estimate tokens Emit reminders
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
All state lives in `~/.mink/` -- nothing is stored in your project repository.
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
### Core State Management
|
|
43
|
+
- **Session Lifecycle** — Tracks session start/stop, token counts, and file operations
|
|
44
|
+
- **File Index** — Scans and indexes project files with descriptions and metadata
|
|
45
|
+
- **Learning Memory** — Four-section knowledge store: preferences, learnings, do-not-repeat, and decision log
|
|
46
|
+
- **Token Ledger** — Persistent usage history with per-session breakdowns and savings calculations
|
|
47
|
+
|
|
48
|
+
### Intelligent Hooks
|
|
49
|
+
- **Read Intelligence** — Tracks file reads, warns on redundant re-reads, estimates token cost
|
|
50
|
+
- **Write Enforcement** — Enforces learned rules on writes, surfaces past bugs for relevant files
|
|
51
|
+
|
|
52
|
+
### Knowledge & Analytics
|
|
53
|
+
- **Bug Memory** — Tracks bugs, fixes, root causes, and tags for searchable history
|
|
54
|
+
- **Action Log** — Human-readable chronological log of all session activity
|
|
55
|
+
- **Waste Detection** — Identifies patterns of token waste (repeated reads, large file scans, etc.)
|
|
56
|
+
|
|
57
|
+
### Automation
|
|
58
|
+
- **Background Scheduler** — Daemon process with cron-based task scheduling, retry logic with exponential backoff, and a dead letter queue for failed tasks
|
|
59
|
+
- **Built-in Tasks** — File index rescan, action log consolidation, waste detection, learning memory reflection, and project suggestions — all on configurable schedules
|
|
60
|
+
|
|
61
|
+
### Interfaces
|
|
62
|
+
- **CLI** — 20+ commands covering lifecycle hooks, state management, scheduling, configuration, backup/restore, and more
|
|
63
|
+
- **Real-time Dashboard** — Web UI with 10 panels, SSE live updates, light/dark themes, virtual scrolling, and interactive charts
|
|
64
|
+
|
|
65
|
+
### Advanced
|
|
66
|
+
- **Design Evaluation** — Automated multi-viewport screenshot capture with server and route detection (uses Puppeteer)
|
|
67
|
+
- **Framework Advisor** — Decision tree, framework catalog, comparison matrix, and migration prompts for UI framework selection
|
|
68
|
+
|
|
69
|
+
## Current Status
|
|
70
|
+
|
|
71
|
+
Specs 1–14 are fully implemented and tested. Remaining specs (wiki, test plan) are designed and documented in `specs/`.
|
|
72
|
+
|
|
73
|
+
| Domain | Specs | Status |
|
|
74
|
+
|--------|-------|--------|
|
|
75
|
+
| Core | Session Lifecycle, File Index, Learning Memory, Token Ledger | Implemented |
|
|
76
|
+
| Hooks | Read Intelligence, Write Enforcement | Implemented |
|
|
77
|
+
| Knowledge | Bug Memory, Action Log | Implemented |
|
|
78
|
+
| Analytics | Waste Detection | Implemented |
|
|
79
|
+
| Automation | Background Scheduler | Implemented |
|
|
80
|
+
| Interfaces | CLI Commands, Dashboard | Implemented |
|
|
81
|
+
| Advanced | Design Evaluation, Framework Advisor | Implemented |
|
|
82
|
+
| Wiki | Cross-Project Wiki | Designed |
|
|
83
|
+
| Quality | Test Plan | Designed |
|
|
84
|
+
|
|
85
|
+
## Installation
|
|
86
|
+
|
|
87
|
+
### Prerequisites
|
|
88
|
+
|
|
89
|
+
- [Node.js](https://nodejs.org/) 18+ or [Bun](https://bun.sh/) (recommended for faster hook execution)
|
|
90
|
+
- [Claude Code](https://claude.ai/code)
|
|
91
|
+
|
|
92
|
+
### Install
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# With Bun (recommended)
|
|
96
|
+
bun add -g mink
|
|
97
|
+
|
|
98
|
+
# With npm
|
|
99
|
+
npm install -g mink
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Initialize in a project
|
|
103
|
+
|
|
104
|
+
From your project root:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
mink init
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This will:
|
|
111
|
+
|
|
112
|
+
1. Detect your runtime (Bun if available, otherwise Node.js)
|
|
113
|
+
2. Create your project's state directory at `~/.mink/projects/<project-slug>/`
|
|
114
|
+
3. Register Mink's hooks in `.claude/settings.json`
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
[mink] initialized
|
|
118
|
+
project: my-project-a3f2b1
|
|
119
|
+
state: /Users/you/.mink/projects/my-project-a3f2b1
|
|
120
|
+
runtime: bun
|
|
121
|
+
hooks: /Users/you/dev/my-project/.claude/settings.json
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
That's it. Mink runs automatically in the background during your Claude Code sessions.
|
|
125
|
+
|
|
126
|
+
### Verify it's working
|
|
127
|
+
|
|
128
|
+
Start a new Claude Code session in your project. Mink will create a `session.json` in your project's state directory:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
cat ~/.mink/projects/*/session.json
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
You should see a fresh session state with a unique ID, timestamp, and zeroed counters.
|
|
135
|
+
|
|
136
|
+
## Architecture
|
|
137
|
+
|
|
138
|
+
### State Directory
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
~/.mink/
|
|
142
|
+
├── config.json # Global user configuration
|
|
143
|
+
├── projects/
|
|
144
|
+
│ └── my-project-a3f2b1/
|
|
145
|
+
│ ├── session.json # Ephemeral session state
|
|
146
|
+
│ ├── file-index.json # File descriptions and metadata
|
|
147
|
+
│ ├── learning-memory.md # Accumulated project knowledge (4 sections)
|
|
148
|
+
│ ├── token-ledger.json # Persistent usage history
|
|
149
|
+
│ ├── action-log.md # Human-readable action history
|
|
150
|
+
│ ├── bug-memory.json # Past bugs, fixes, and root causes
|
|
151
|
+
│ ├── scheduler.json # Scheduler manifest and task state
|
|
152
|
+
│ ├── daemon.pid # Background daemon PID
|
|
153
|
+
│ ├── backups/ # State backups for restore
|
|
154
|
+
│ └── screenshots/ # Design evaluation captures
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Project Identification
|
|
158
|
+
|
|
159
|
+
Each project gets a deterministic, human-readable identifier: the slugified directory name plus a 6-character hash of the absolute path. This means:
|
|
160
|
+
|
|
161
|
+
- `my-project` in `/Users/drew/dev/` becomes `my-project-a3f2b1`
|
|
162
|
+
- `my-project` in `/Users/drew/work/` gets a different hash, avoiding collisions
|
|
163
|
+
- Moving a project changes its ID (re-run `mink init`)
|
|
164
|
+
|
|
165
|
+
### Crash Safety
|
|
166
|
+
|
|
167
|
+
All JSON writes use atomic temp-file-then-rename. If the process dies mid-write, only the `.tmp` file is affected -- the original state file remains intact.
|
|
168
|
+
|
|
169
|
+
### Hook Integration
|
|
170
|
+
|
|
171
|
+
Mink hooks into Claude Code via `.claude/settings.json`:
|
|
172
|
+
|
|
173
|
+
| Claude Code Event | Mink Command | Purpose |
|
|
174
|
+
|-------------------|--------------|---------|
|
|
175
|
+
| `SessionStart` | `mink session-start` | Create fresh session state |
|
|
176
|
+
| `Stop` | `mink session-stop` | Finalize session, calculate savings |
|
|
177
|
+
| `PreToolUse` (Read) | `mink pre-read` | Check file index, warn on repeat reads |
|
|
178
|
+
| `PostToolUse` (Read) | `mink post-read` | Track read, estimate tokens |
|
|
179
|
+
| `PreToolUse` (Write/Edit) | `mink pre-write` | Enforce learned rules, surface past bugs |
|
|
180
|
+
| `PostToolUse` (Write/Edit) | `mink post-write` | Log write, update file index |
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
### Setup
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
git clone git@github.com:drewpayment/mink.git
|
|
188
|
+
cd mink
|
|
189
|
+
bun install
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Run tests
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Run all tests
|
|
196
|
+
bun test
|
|
197
|
+
|
|
198
|
+
# Watch mode
|
|
199
|
+
bun test --watch
|
|
200
|
+
|
|
201
|
+
# Run a specific test file
|
|
202
|
+
bun test tests/unit/session.test.ts
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Project structure
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
mink/
|
|
209
|
+
├── src/
|
|
210
|
+
│ ├── cli.ts # Entry point, command routing (20+ commands)
|
|
211
|
+
│ ├── commands/ # CLI command implementations
|
|
212
|
+
│ │ ├── init.ts # mink init — runtime detection, hook wiring
|
|
213
|
+
│ │ ├── session-start.ts # Hook: create fresh session state
|
|
214
|
+
│ │ ├── session-stop.ts # Hook: finalize session, emit reminders
|
|
215
|
+
│ │ ├── pre-read.ts # Hook: file read intelligence
|
|
216
|
+
│ │ ├── post-read.ts # Hook: post-read tracking
|
|
217
|
+
│ │ ├── pre-write.ts # Hook: write enforcement
|
|
218
|
+
│ │ ├── post-write.ts # Hook: post-write tracking
|
|
219
|
+
│ │ ├── status.ts # Project health display
|
|
220
|
+
│ │ ├── scan.ts # Force full file index rescan
|
|
221
|
+
│ │ ├── config.ts # Global configuration management
|
|
222
|
+
│ │ ├── cron.ts # Scheduled task management
|
|
223
|
+
│ │ ├── daemon.ts # Background daemon control
|
|
224
|
+
│ │ ├── dashboard.ts # Real-time web dashboard
|
|
225
|
+
│ │ ├── designqc.ts # Design evaluation screenshots
|
|
226
|
+
│ │ ├── framework-advisor.ts # Framework advisor CLI
|
|
227
|
+
│ │ ├── detect-waste.ts # Token waste analysis
|
|
228
|
+
│ │ ├── bug-search.ts # Bug log search
|
|
229
|
+
│ │ ├── reflect.ts # Learning memory reflection
|
|
230
|
+
│ │ ├── update.ts # Cross-project update
|
|
231
|
+
│ │ └── restore.ts # State restoration from backup
|
|
232
|
+
│ ├── core/ # Core library modules
|
|
233
|
+
│ │ ├── session.ts # Session state CRUD, summary, savings
|
|
234
|
+
│ │ ├── paths.ts # ~/.mink path resolution
|
|
235
|
+
│ │ ├── project-id.ts # Slug + hash project ID generation
|
|
236
|
+
│ │ ├── fs-utils.ts # Atomic JSON write, safe read
|
|
237
|
+
│ │ ├── index-store.ts # File index management
|
|
238
|
+
│ │ ├── scanner.ts # Project file scanner
|
|
239
|
+
│ │ ├── learning-memory.ts # Learning memory operations
|
|
240
|
+
│ │ ├── token-ledger.ts # Token usage tracking
|
|
241
|
+
│ │ ├── action-log.ts # Action log management
|
|
242
|
+
│ │ ├── bug-memory.ts # Bug memory operations
|
|
243
|
+
│ │ ├── waste-detection.ts # Waste pattern detection
|
|
244
|
+
│ │ ├── pattern-engine.ts # Learned pattern matching
|
|
245
|
+
│ │ ├── scheduler.ts # Cron-based task scheduler
|
|
246
|
+
│ │ ├── daemon.ts # Daemon process management
|
|
247
|
+
│ │ ├── cron-parser.ts # Cron expression parsing
|
|
248
|
+
│ │ ├── task-registry.ts # Built-in task definitions
|
|
249
|
+
│ │ ├── dashboard-server.ts # Dashboard HTTP server
|
|
250
|
+
│ │ ├── dashboard-api.ts # Dashboard REST API + SSE
|
|
251
|
+
│ │ ├── design-eval/ # Screenshot capture, route/server detection
|
|
252
|
+
│ │ ├── framework-advisor/ # Catalog, decision tree, migration prompts
|
|
253
|
+
│ │ └── ... # Global config, backup, reflection, etc.
|
|
254
|
+
│ ├── dashboard/ # Embedded dashboard UI (HTML/CSS/JS generation)
|
|
255
|
+
│ │ ├── get-dashboard-html.ts # Main HTML assembly
|
|
256
|
+
│ │ ├── panel-*.ts # 10 panel implementations
|
|
257
|
+
│ │ ├── css-*.ts # Base styles and themes
|
|
258
|
+
│ │ └── js-*.ts # Charts, SSE, virtual scroll, search
|
|
259
|
+
│ └── types/ # TypeScript interfaces
|
|
260
|
+
├── tests/
|
|
261
|
+
│ ├── unit/ # 35+ unit test files
|
|
262
|
+
│ └── integration/ # 15+ integration test files
|
|
263
|
+
├── specs/ # Feature specifications (technology-agnostic)
|
|
264
|
+
└── docs/ # Design docs and implementation plans
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Contributing
|
|
268
|
+
|
|
269
|
+
### Specs-first development
|
|
270
|
+
|
|
271
|
+
Mink follows a specs-first approach. All feature specifications live in `specs/` and describe **what** to build, not how. Each spec follows a consistent format: Overview, Capabilities, Acceptance Criteria (Given/When/Then), Edge Cases, and Test Requirements.
|
|
272
|
+
|
|
273
|
+
Before implementing a new feature:
|
|
274
|
+
|
|
275
|
+
1. Read the relevant spec in `specs/`
|
|
276
|
+
2. Check if a design doc exists in `docs/superpowers/specs/`
|
|
277
|
+
3. Check if an implementation plan exists in `docs/superpowers/plans/`
|
|
278
|
+
|
|
279
|
+
### Guidelines
|
|
280
|
+
|
|
281
|
+
- **TypeScript** with strict mode enabled
|
|
282
|
+
- **Bun** as runtime, test runner, and package manager
|
|
283
|
+
- **TDD** -- write failing tests first, then implement
|
|
284
|
+
- **Atomic commits** -- one logical change per commit
|
|
285
|
+
- **No state in project repos** -- all Mink state goes in `~/.mink/`
|
|
286
|
+
- **Crash-safe I/O** -- use `atomicWriteJson` from `src/core/fs-utils.ts` for all JSON writes
|
|
287
|
+
- **Graceful degradation** -- missing or corrupt state files should log warnings, not crash
|
|
288
|
+
|
|
289
|
+
### Running the full lifecycle locally
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Initialize mink for this repo
|
|
293
|
+
bun src/cli.ts init
|
|
294
|
+
|
|
295
|
+
# Simulate a session
|
|
296
|
+
bun src/cli.ts session-start
|
|
297
|
+
cat ~/.mink/projects/mink-*/session.json
|
|
298
|
+
|
|
299
|
+
bun src/cli.ts session-stop
|
|
300
|
+
cat ~/.mink/projects/mink-*/session.json
|
|
301
|
+
|
|
302
|
+
# Start the dashboard
|
|
303
|
+
bun src/cli.ts dashboard --port 3333
|
|
304
|
+
|
|
305
|
+
# Start the background daemon
|
|
306
|
+
bun src/cli.ts daemon start
|
|
307
|
+
|
|
308
|
+
# Check project status
|
|
309
|
+
bun src/cli.ts status
|
|
310
|
+
|
|
311
|
+
# Run a waste detection scan
|
|
312
|
+
bun src/cli.ts detect-waste
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Adding a new spec implementation
|
|
316
|
+
|
|
317
|
+
Each spec follows this workflow:
|
|
318
|
+
|
|
319
|
+
1. **Design** -- Brainstorm approaches, document decisions in `docs/superpowers/specs/`
|
|
320
|
+
2. **Plan** -- Break down into bite-sized tasks in `docs/superpowers/plans/`
|
|
321
|
+
3. **Implement** -- Follow the plan task by task using TDD
|
|
322
|
+
4. **Review** -- Verify spec compliance and code quality
|
|
323
|
+
|
|
324
|
+
## Specifications
|
|
325
|
+
|
|
326
|
+
| # | Spec | Domain | Status |
|
|
327
|
+
|---|------|--------|--------|
|
|
328
|
+
| 01 | [Session Lifecycle](./specs/01-session-lifecycle.md) | Core | Implemented |
|
|
329
|
+
| 02 | [File Index](./specs/02-file-index.md) | Core | Implemented |
|
|
330
|
+
| 03 | [Learning Memory](./specs/03-learning-memory.md) | Core | Implemented |
|
|
331
|
+
| 04 | [Token Ledger](./specs/04-token-ledger.md) | Core | Implemented |
|
|
332
|
+
| 05 | [Read Intelligence](./specs/05-read-intelligence.md) | Hooks | Implemented |
|
|
333
|
+
| 06 | [Write Enforcement](./specs/06-write-enforcement.md) | Hooks | Implemented |
|
|
334
|
+
| 07 | [Bug Memory](./specs/07-bug-memory.md) | Knowledge | Implemented |
|
|
335
|
+
| 08 | [Action Log](./specs/08-action-log.md) | Knowledge | Implemented |
|
|
336
|
+
| 09 | [Waste Detection](./specs/09-waste-detection.md) | Analytics | Implemented |
|
|
337
|
+
| 10 | [Background Scheduler](./specs/10-background-scheduler.md) | Automation | Implemented |
|
|
338
|
+
| 11 | [CLI Interface](./specs/11-cli-interface.md) | Interface | Implemented |
|
|
339
|
+
| 12 | [Dashboard](./specs/12-dashboard.md) | Interface | Implemented |
|
|
340
|
+
| 13 | [Design Evaluation](./specs/13-design-evaluation.md) | Advanced | Implemented |
|
|
341
|
+
| 14 | [Framework Advisor](./specs/14-framework-advisor.md) | Advanced | Implemented |
|
|
342
|
+
| 15 | [Cross-Project Wiki](./specs/15-cross-project-wiki.md) | Wiki | Designed |
|
|
343
|
+
| 16 | [Test Plan](./specs/16-test-plan.md) | Quality | Designed |
|
|
344
|
+
|
|
345
|
+
## License
|
|
346
|
+
|
|
347
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@drewpayment/mink",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A hidden presence that moves alongside the developer — token efficiency and cross-project wiki for AI coding assistants",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mink": "./src/cli.ts"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"typecheck": "bunx tsc --noEmit",
|
|
11
|
+
"test": "bun test",
|
|
12
|
+
"test:watch": "bun test --watch",
|
|
13
|
+
"dashboard:dev": "cd dashboard && bun run dev",
|
|
14
|
+
"dashboard:build": "cd dashboard && bun run build"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"src/**/*.ts"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"puppeteer-core": "^24.0.0"
|
|
25
|
+
},
|
|
26
|
+
"optionalDependencies": {
|
|
27
|
+
"@puppeteer/browsers": "^2.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"bun-types": "^1.3.12"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { sessionStart } from "./commands/session-start";
|
|
3
|
+
import { sessionStop } from "./commands/session-stop";
|
|
4
|
+
import { sessionPath } from "./core/paths";
|
|
5
|
+
|
|
6
|
+
const command = process.argv[2];
|
|
7
|
+
const cwd = process.cwd();
|
|
8
|
+
|
|
9
|
+
switch (command) {
|
|
10
|
+
case "session-start":
|
|
11
|
+
sessionStart(cwd);
|
|
12
|
+
break;
|
|
13
|
+
|
|
14
|
+
case "session-stop":
|
|
15
|
+
sessionStop(sessionPath(cwd));
|
|
16
|
+
break;
|
|
17
|
+
|
|
18
|
+
case "init": {
|
|
19
|
+
const { init } = await import("./commands/init");
|
|
20
|
+
await init(cwd);
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
case "status": {
|
|
25
|
+
const { status } = await import("./commands/status");
|
|
26
|
+
status(cwd);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
case "scan": {
|
|
31
|
+
const { scan } = await import("./commands/scan");
|
|
32
|
+
const check = process.argv.includes("--check");
|
|
33
|
+
scan(cwd, { check });
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
case "reflect": {
|
|
38
|
+
const { reflect } = await import("./commands/reflect");
|
|
39
|
+
const { learningMemoryPath, configPath } = await import("./core/paths");
|
|
40
|
+
reflect(cwd, learningMemoryPath(cwd), configPath(cwd));
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
case "pre-read": {
|
|
45
|
+
const { preRead } = await import("./commands/pre-read");
|
|
46
|
+
await preRead(cwd);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
case "post-read": {
|
|
51
|
+
const { postRead } = await import("./commands/post-read");
|
|
52
|
+
await postRead(cwd);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
case "pre-write": {
|
|
57
|
+
const { preWrite } = await import("./commands/pre-write");
|
|
58
|
+
await preWrite(cwd);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
case "post-write": {
|
|
63
|
+
const { postWrite } = await import("./commands/post-write");
|
|
64
|
+
await postWrite(cwd);
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
case "detect-waste": {
|
|
69
|
+
const { detectWaste } = await import("./commands/detect-waste");
|
|
70
|
+
detectWaste(cwd);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
case "cron": {
|
|
75
|
+
const { cron } = await import("./commands/cron");
|
|
76
|
+
await cron(cwd, process.argv.slice(3));
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
case "dashboard": {
|
|
81
|
+
const { dashboard } = await import("./commands/dashboard");
|
|
82
|
+
await dashboard(cwd, process.argv.slice(3));
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
case "daemon": {
|
|
87
|
+
const { daemon } = await import("./commands/daemon");
|
|
88
|
+
await daemon(cwd, process.argv.slice(3));
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case "config": {
|
|
93
|
+
const { config } = await import("./commands/config");
|
|
94
|
+
await config(process.argv.slice(3));
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
case "update": {
|
|
99
|
+
const { update } = await import("./commands/update");
|
|
100
|
+
await update(cwd, process.argv.slice(3));
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
case "restore": {
|
|
105
|
+
const { restore } = await import("./commands/restore");
|
|
106
|
+
restore(cwd, process.argv.slice(3));
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
case "designqc": {
|
|
111
|
+
const { designqc } = await import("./commands/designqc");
|
|
112
|
+
designqc(cwd, process.argv.slice(3));
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
case "framework-advisor": {
|
|
117
|
+
const { frameworkAdvisor } = await import("./commands/framework-advisor");
|
|
118
|
+
await frameworkAdvisor(cwd, process.argv.slice(3));
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case "bug-search": {
|
|
123
|
+
const { bugSearch } = await import("./commands/bug-search");
|
|
124
|
+
bugSearch(cwd, process.argv.slice(3).join(" "));
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
case "bug": {
|
|
129
|
+
if (process.argv[3] === "search") {
|
|
130
|
+
const { bugSearch } = await import("./commands/bug-search");
|
|
131
|
+
bugSearch(cwd, process.argv.slice(4).join(" "));
|
|
132
|
+
} else {
|
|
133
|
+
console.error(
|
|
134
|
+
`[mink] unknown bug subcommand: ${process.argv[3] ?? "(none)"}`
|
|
135
|
+
);
|
|
136
|
+
console.error("Usage: mink bug search <term>");
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
case "help":
|
|
143
|
+
case "--help":
|
|
144
|
+
case "-h":
|
|
145
|
+
console.log("mink — a hidden presence that moves alongside the developer");
|
|
146
|
+
console.log();
|
|
147
|
+
console.log("Usage: mink <command> [options]");
|
|
148
|
+
console.log();
|
|
149
|
+
console.log("Commands:");
|
|
150
|
+
console.log(" init Initialize Mink in the current project");
|
|
151
|
+
console.log(" status Display project health at a glance");
|
|
152
|
+
console.log(" scan [--check] Force a full file index rescan");
|
|
153
|
+
console.log(" config [key] [value] Manage global user settings");
|
|
154
|
+
console.log(" dashboard [--port=N] Open the real-time web dashboard");
|
|
155
|
+
console.log(" daemon <cmd> Manage the background daemon (start|stop|restart|logs)");
|
|
156
|
+
console.log(" cron <cmd> [id] Manage scheduled tasks (list|run|retry)");
|
|
157
|
+
console.log(" update [options] Update Mink across registered projects");
|
|
158
|
+
console.log(" restore [backup] Restore state from a backup");
|
|
159
|
+
console.log(" bug search <term> Search the bug log");
|
|
160
|
+
console.log(" detect-waste Detect and flag wasteful patterns");
|
|
161
|
+
console.log(" reflect Generate learning memory reflections");
|
|
162
|
+
console.log(" designqc [target] Capture design screenshots (spec 13)");
|
|
163
|
+
console.log(" framework-advisor Generate framework advisor knowledge file (spec 14)");
|
|
164
|
+
console.log();
|
|
165
|
+
console.log("Lifecycle hooks (internal):");
|
|
166
|
+
console.log(" session-start Start session tracking");
|
|
167
|
+
console.log(" session-stop Finalize session and log data");
|
|
168
|
+
console.log(" pre-read / post-read File read hooks");
|
|
169
|
+
console.log(" pre-write / post-write File write hooks");
|
|
170
|
+
break;
|
|
171
|
+
|
|
172
|
+
default:
|
|
173
|
+
console.error(`[mink] unknown command: ${command ?? "(none)"}`);
|
|
174
|
+
console.error("Run 'mink help' for usage information.");
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { loadBugMemory, searchBugs } from "../core/bug-memory";
|
|
2
|
+
import { bugMemoryPath } from "../core/paths";
|
|
3
|
+
|
|
4
|
+
export function bugSearch(cwd: string, query: string): void {
|
|
5
|
+
if (!query) {
|
|
6
|
+
console.error("Usage: mink bug search <query>");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const memory = loadBugMemory(bugMemoryPath(cwd));
|
|
11
|
+
const results = searchBugs(memory, query);
|
|
12
|
+
|
|
13
|
+
if (results.length === 0) {
|
|
14
|
+
console.log("No matching bugs found.");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
for (const match of results) {
|
|
19
|
+
const e = match.entry;
|
|
20
|
+
console.log(
|
|
21
|
+
`${e.id} (score: ${match.score.toFixed(2)}) — ${e.errorMessage}`
|
|
22
|
+
);
|
|
23
|
+
console.log(
|
|
24
|
+
` File: ${e.filePath}${e.lineNumber ? `:${e.lineNumber}` : ""}`
|
|
25
|
+
);
|
|
26
|
+
console.log(` Root cause: ${e.rootCause}`);
|
|
27
|
+
console.log(` Fix: ${e.fixDescription}`);
|
|
28
|
+
if (e.tags.length > 0) console.log(` Tags: ${e.tags.join(", ")}`);
|
|
29
|
+
if (e.occurrenceCount > 1) console.log(` Seen ${e.occurrenceCount} times`);
|
|
30
|
+
console.log();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONFIG_KEYS,
|
|
3
|
+
isValidConfigKey,
|
|
4
|
+
} from "../types/config";
|
|
5
|
+
import {
|
|
6
|
+
resolveConfigValue,
|
|
7
|
+
resolveAllConfig,
|
|
8
|
+
setConfigValue,
|
|
9
|
+
resetConfigKey,
|
|
10
|
+
resetAllConfig,
|
|
11
|
+
} from "../core/global-config";
|
|
12
|
+
|
|
13
|
+
function printValidKeys(): void {
|
|
14
|
+
console.error("Valid keys:");
|
|
15
|
+
for (const meta of CONFIG_KEYS) {
|
|
16
|
+
console.error(` ${meta.key} — ${meta.description}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function readLineFromStdin(): Promise<string> {
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
const chunks: Buffer[] = [];
|
|
23
|
+
process.stdin.resume();
|
|
24
|
+
process.stdin.setEncoding("utf-8");
|
|
25
|
+
process.stdin.once("data", (data) => {
|
|
26
|
+
process.stdin.pause();
|
|
27
|
+
resolve(String(data).trim());
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function config(args: string[]): Promise<void> {
|
|
33
|
+
// mink config --reset-all
|
|
34
|
+
if (args.includes("--reset-all")) {
|
|
35
|
+
process.stdout.write(
|
|
36
|
+
"[mink] reset all settings to defaults? (yes/no): "
|
|
37
|
+
);
|
|
38
|
+
const answer = await readLineFromStdin();
|
|
39
|
+
if (answer === "yes" || answer === "y") {
|
|
40
|
+
resetAllConfig();
|
|
41
|
+
console.log("[mink] all settings reset to defaults");
|
|
42
|
+
} else {
|
|
43
|
+
console.log("[mink] cancelled");
|
|
44
|
+
}
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// mink config --reset <key>
|
|
49
|
+
const resetIdx = args.indexOf("--reset");
|
|
50
|
+
if (resetIdx !== -1) {
|
|
51
|
+
const key = args[resetIdx + 1];
|
|
52
|
+
if (!key) {
|
|
53
|
+
console.error("Usage: mink config --reset <key>");
|
|
54
|
+
printValidKeys();
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
if (!isValidConfigKey(key)) {
|
|
58
|
+
console.error(`[mink] unknown config key: ${key}`);
|
|
59
|
+
printValidKeys();
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
resetConfigKey(key);
|
|
63
|
+
console.log(`[mink] ${key} reset to default`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// mink config (no args) — show all
|
|
68
|
+
if (args.length === 0) {
|
|
69
|
+
const all = resolveAllConfig();
|
|
70
|
+
console.log("[mink] configuration:");
|
|
71
|
+
for (const entry of all) {
|
|
72
|
+
let line = ` ${entry.key} = ${entry.value} (source: ${entry.source})`;
|
|
73
|
+
if (
|
|
74
|
+
entry.source === "environment variable" &&
|
|
75
|
+
entry.configFileValue !== undefined
|
|
76
|
+
) {
|
|
77
|
+
line += ` [config file value: ${entry.configFileValue} — overridden]`;
|
|
78
|
+
}
|
|
79
|
+
console.log(line);
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const key = args[0];
|
|
85
|
+
if (!isValidConfigKey(key)) {
|
|
86
|
+
console.error(`[mink] unknown config key: ${key}`);
|
|
87
|
+
printValidKeys();
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// mink config <key> <value> — set
|
|
92
|
+
if (args.length >= 2) {
|
|
93
|
+
const value = args.slice(1).join(" ");
|
|
94
|
+
setConfigValue(key, value);
|
|
95
|
+
console.log(`[mink] ${key} = ${value}`);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// mink config <key> — show one
|
|
100
|
+
const resolved = resolveConfigValue(key);
|
|
101
|
+
let line = `${key} = ${resolved.value} (source: ${resolved.source})`;
|
|
102
|
+
if (
|
|
103
|
+
resolved.source === "environment variable" &&
|
|
104
|
+
resolved.configFileValue !== undefined
|
|
105
|
+
) {
|
|
106
|
+
line += `\n note: config file value (${resolved.configFileValue}) is overridden`;
|
|
107
|
+
}
|
|
108
|
+
console.log(line);
|
|
109
|
+
}
|