@miphamai/cli 0.81.2 → 0.81.5
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/package.json +4 -2
- package/skills/standard/superpower.SKILL.md +27 -12
- package/src/core/engine.ts +4 -0
- package/src/daemon/auth.ts +4 -0
- package/src/daemon/cors.ts +18 -0
- package/src/daemon/server.ts +34 -3
- package/src/daemon/workspace-guard.ts +28 -0
- package/src/index.tsx +5 -0
- package/src/shared/package-info.ts +1 -1
- package/src/skills/bundled-skills.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miphamai/cli",
|
|
3
|
-
"version": "0.81.
|
|
3
|
+
"version": "0.81.5",
|
|
4
4
|
"description": "Mipham Code — Multi-model open-core intelligent coding terminal by MiphamAI",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"build": "bun run scripts/generate-bundled-skills.ts && bun build --compile --minify ./bin/mipham.ts --outfile dist/mipham",
|
|
40
40
|
"typecheck": "tsc --noEmit",
|
|
41
41
|
"test": "vitest run",
|
|
42
|
-
"knip": "knip --production --no-progress --no-exit-code"
|
|
42
|
+
"knip": "knip --production --no-progress --no-exit-code",
|
|
43
|
+
"coverage": "vitest run --coverage --coverage.provider=v8 --coverage.include='src/**/*.{ts,tsx}' --coverage.reporter=text-summary --coverage.reporter=json-summary"
|
|
43
44
|
},
|
|
44
45
|
"dependencies": {
|
|
45
46
|
"@larksuiteoapi/node-sdk": "^1.73.0",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"@types/bun": "^1.4.2",
|
|
56
57
|
"@types/node": "^22.19.19",
|
|
57
58
|
"@types/react": "^19.3.0",
|
|
59
|
+
"@vitest/coverage-v8": "^5.0.0",
|
|
58
60
|
"ink-testing-library": "^4.0.0",
|
|
59
61
|
"vitest": "^5.0.0"
|
|
60
62
|
}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: superpower
|
|
3
3
|
description: Skill discovery and invocation system — find and use skills before any response or action
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
<SUBAGENT-STOP>
|
|
8
|
+
If you were dispatched as a subagent to execute a specific task, ignore this skill.
|
|
9
|
+
</SUBAGENT-STOP>
|
|
10
|
+
|
|
7
11
|
# Superpowers — Using Skills
|
|
8
12
|
|
|
9
13
|
## The Rule
|
|
10
14
|
|
|
11
15
|
**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means you should invoke it to check.
|
|
12
16
|
|
|
17
|
+
Then announce "Using [skill] to [purpose]" and follow the skill exactly. If it has a checklist, create a todo per item.
|
|
18
|
+
|
|
13
19
|
## How to Access Skills
|
|
14
20
|
|
|
15
21
|
Use the `Skill` tool to invoke skills by name. When you invoke a skill, its content is loaded — follow it directly.
|
|
@@ -30,28 +36,37 @@ Skills are listed in `<system-reminder>` messages. Scan this list when receiving
|
|
|
30
36
|
|
|
31
37
|
### Priority Order
|
|
32
38
|
|
|
33
|
-
1. **Process skills first** —
|
|
34
|
-
2. **Implementation skills second** —
|
|
39
|
+
1. **Process skills first** — to-spec, debug-loop, tdd. These determine HOW to approach
|
|
40
|
+
2. **Implementation skills second** — implement, codebase-design. These guide execution
|
|
35
41
|
|
|
36
42
|
## Red Flags
|
|
37
43
|
|
|
38
44
|
These thoughts mean STOP — you're rationalizing:
|
|
39
45
|
|
|
40
|
-
| Thought | Reality
|
|
41
|
-
| ----------------------------------- |
|
|
42
|
-
| "This is just a simple question" | Questions are tasks. Check skills.
|
|
43
|
-
| "I need more context first" | Skill check comes BEFORE clarifying questions.
|
|
44
|
-
| "Let me explore the codebase first" | Skills tell you HOW to explore.
|
|
45
|
-
| "I
|
|
46
|
-
| "
|
|
46
|
+
| Thought | Reality |
|
|
47
|
+
| ----------------------------------- | ------------------------------------------------------ |
|
|
48
|
+
| "This is just a simple question" | Questions are tasks. Check skills. |
|
|
49
|
+
| "I need more context first" | Skill check comes BEFORE clarifying questions. |
|
|
50
|
+
| "Let me explore the codebase first" | Skills tell you HOW to explore. Check first. |
|
|
51
|
+
| "I can check git/files quickly" | Files lack conversation context. Check for skills. |
|
|
52
|
+
| "Let me gather information first" | Skills tell you HOW to gather information. |
|
|
53
|
+
| "This doesn't need a formal skill" | If a skill exists, use it. |
|
|
54
|
+
| "I remember this skill" | Skills evolve. Read current version. |
|
|
55
|
+
| "This doesn't count as a task" | Action = task. Check for skills. |
|
|
56
|
+
| "The skill is overkill" | Simple things become complex. Use it. |
|
|
57
|
+
| "I'll just do this one thing first" | Check BEFORE doing anything. |
|
|
58
|
+
| "This feels productive" | Undisciplined action wastes time. Skills prevent this. |
|
|
59
|
+
| "I know what that means" | Knowing the concept ≠ using the skill. Invoke it. |
|
|
47
60
|
|
|
48
61
|
## Skill Types
|
|
49
62
|
|
|
50
|
-
- **Rigid** (
|
|
63
|
+
- **Rigid** (tdd, debug-loop): Follow exactly. Don't adapt away discipline.
|
|
51
64
|
- **Flexible** (patterns): Adapt principles to context.
|
|
52
65
|
|
|
53
66
|
The skill itself tells you which type it is.
|
|
54
67
|
|
|
55
68
|
## User Instructions
|
|
56
69
|
|
|
57
|
-
|
|
70
|
+
User instructions (CLAUDE.md, AGENTS.md, MIPHAM.md, direct requests) take precedence over skills, which in turn override default behavior.
|
|
71
|
+
|
|
72
|
+
Instructions say WHAT, not HOW. "Add X" or "Fix Y" doesn't mean skip workflows. Only skip a skill workflow when the user has explicitly told you to.
|
package/src/core/engine.ts
CHANGED
|
@@ -1038,6 +1038,10 @@ export class QueryEngine {
|
|
|
1038
1038
|
})
|
|
1039
1039
|
this.context.addToolResult(toolUse.id, result)
|
|
1040
1040
|
}
|
|
1041
|
+
|
|
1042
|
+
// Path-scoped rules for files touched in this round: process() injects for
|
|
1043
|
+
// the first tool round only, so the multi-turn rounds need their own call.
|
|
1044
|
+
this.injectRules()
|
|
1041
1045
|
}
|
|
1042
1046
|
// Max turns reached — safety limit, stop gracefully
|
|
1043
1047
|
}
|
package/src/daemon/auth.ts
CHANGED
|
@@ -72,6 +72,10 @@ export function listTokens(tokenPath: string): string[] {
|
|
|
72
72
|
* In the default 127.0.0.1-only configuration, all requests are implicitly
|
|
73
73
|
* trusted and auth is bypassed. Auth enforcement activates when
|
|
74
74
|
* MIPHAM_BIND=0.0.0.0 for remote access.
|
|
75
|
+
*
|
|
76
|
+
* Loopback says *where* a request came from, not *who* sent it — a web page in
|
|
77
|
+
* the user's own browser is also loopback. Two guards close that gap:
|
|
78
|
+
* `originMiddleware` in cors.ts and the session cwd check in workspace-guard.ts.
|
|
75
79
|
*/
|
|
76
80
|
export function authMiddleware(
|
|
77
81
|
request: Request,
|
package/src/daemon/cors.ts
CHANGED
|
@@ -27,6 +27,24 @@ function isAllowedOrigin(origin: string): boolean {
|
|
|
27
27
|
return getAllowedOrigins().includes(origin)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Reject any request whose Origin is present but not allow-listed.
|
|
32
|
+
*
|
|
33
|
+
* The CORS headers above only stop a page from *reading* a reply — they do not
|
|
34
|
+
* stop it from *sending* the request (a `text/plain` body is a CORS-safelisted
|
|
35
|
+
* content type, so no preflight is triggered at all), and WebSocket handshakes
|
|
36
|
+
* are not subject to the same-origin policy in the first place. Origin is the
|
|
37
|
+
* one signal that distinguishes a foreign page from the CLI, which sends none:
|
|
38
|
+
* absent Origin passes through unchanged, any other Origin must be listed in
|
|
39
|
+
* MIPHAM_CORS_ORIGINS. Note that `isLocalhostOrigin` is deliberately NOT used
|
|
40
|
+
* here — its substring match would accept `https://localhost.evil.example`.
|
|
41
|
+
*/
|
|
42
|
+
export function originMiddleware(request: Request): Response | null {
|
|
43
|
+
const origin = request.headers.get('origin')
|
|
44
|
+
if (!origin || isAllowedOrigin(origin)) return null
|
|
45
|
+
return Response.json({ ok: false, error: 'Origin not allowed' }, { status: 403 })
|
|
46
|
+
}
|
|
47
|
+
|
|
30
48
|
/**
|
|
31
49
|
* Handle CORS preflight (OPTIONS) requests.
|
|
32
50
|
*
|
package/src/daemon/server.ts
CHANGED
|
@@ -9,7 +9,9 @@ import type { GoalManager } from './goal-manager'
|
|
|
9
9
|
import type { ScheduleManager } from './schedule-manager'
|
|
10
10
|
import { authMiddleware, rotateToken } from './auth'
|
|
11
11
|
import { RateLimiter } from './rate-limiter'
|
|
12
|
-
import { corsMiddleware, addCorsHeaders } from './cors'
|
|
12
|
+
import { corsMiddleware, originMiddleware, addCorsHeaders } from './cors'
|
|
13
|
+
import { isCwdAllowed } from './workspace-guard'
|
|
14
|
+
import { getWorkspaceTrust } from '../core/workspace-trust'
|
|
13
15
|
import { PACKAGE_VERSION } from '../shared/package-info'
|
|
14
16
|
import { WorkerPool } from './worker-pool'
|
|
15
17
|
import { logger } from './logger'
|
|
@@ -139,6 +141,11 @@ export function createServer(config: ServerConfig): Server<WsData> {
|
|
|
139
141
|
|
|
140
142
|
const wsClients = new Map<string, Set<ServerWebSocket<WsData>>>()
|
|
141
143
|
|
|
144
|
+
// Captured once: a later chdir must not move the boundary that callers'
|
|
145
|
+
// session cwd values are validated against.
|
|
146
|
+
const daemonRoot = process.cwd()
|
|
147
|
+
const isTrustedWorkspace = (dir: string) => getWorkspaceTrust().isTrusted(dir)
|
|
148
|
+
|
|
142
149
|
function broadcast(sessionId: string, data: unknown): void {
|
|
143
150
|
const clients = wsClients.get(sessionId)
|
|
144
151
|
if (!clients) return
|
|
@@ -361,6 +368,13 @@ export function createServer(config: ServerConfig): Server<WsData> {
|
|
|
361
368
|
return await feishuAdapter.handleEvent(req)
|
|
362
369
|
}
|
|
363
370
|
|
|
371
|
+
// ── Origin gate ─────────────────────────────────
|
|
372
|
+
// Runs before the WebSocket upgrade below, which is what makes the
|
|
373
|
+
// upgrade path covered by it: browsers attach Origin to a WS handshake
|
|
374
|
+
// but never block the connection themselves.
|
|
375
|
+
const originError = originMiddleware(req)
|
|
376
|
+
if (originError) return originError
|
|
377
|
+
|
|
364
378
|
// ── Rate limiting (skip health endpoint) ──────────
|
|
365
379
|
if (path !== '/api/v1/health') {
|
|
366
380
|
const ip = server.requestIP(req)?.address || 'unknown'
|
|
@@ -420,9 +434,21 @@ export function createServer(config: ServerConfig): Server<WsData> {
|
|
|
420
434
|
// ── Sessions CRUD ───────────────────────────────
|
|
421
435
|
if (method === 'POST' && path === '/api/v1/sessions') {
|
|
422
436
|
const body = await jsonBody()
|
|
437
|
+
// The session cwd is the read containment boundary — it may only be a
|
|
438
|
+
// trusted workspace or somewhere inside the daemon's own directory.
|
|
439
|
+
const cwd = typeof body.cwd === 'string' && body.cwd ? body.cwd : daemonRoot
|
|
440
|
+
if (!isCwdAllowed(cwd, isTrustedWorkspace, daemonRoot)) {
|
|
441
|
+
return json(
|
|
442
|
+
{
|
|
443
|
+
ok: false,
|
|
444
|
+
error: 'cwd must be a trusted workspace or inside the daemon directory',
|
|
445
|
+
},
|
|
446
|
+
{ status: 403 },
|
|
447
|
+
)
|
|
448
|
+
}
|
|
423
449
|
const session = sm.createSession(
|
|
424
450
|
(body.name as string) || 'unnamed',
|
|
425
|
-
|
|
451
|
+
cwd,
|
|
426
452
|
(body.provider as string) || 'unknown',
|
|
427
453
|
(body.model as string) || 'unknown',
|
|
428
454
|
)
|
|
@@ -673,7 +699,12 @@ export function createServer(config: ServerConfig): Server<WsData> {
|
|
|
673
699
|
description: 'Health check and daemon stats',
|
|
674
700
|
},
|
|
675
701
|
{ method: 'GET', path: '/api/v1/stats', description: 'Database statistics' },
|
|
676
|
-
{
|
|
702
|
+
{
|
|
703
|
+
method: 'POST',
|
|
704
|
+
path: '/api/v1/sessions',
|
|
705
|
+
description:
|
|
706
|
+
'Create a new session (cwd must be a trusted workspace or inside the daemon directory)',
|
|
707
|
+
},
|
|
677
708
|
{ method: 'GET', path: '/api/v1/sessions', description: 'List sessions' },
|
|
678
709
|
{ method: 'GET', path: '/api/v1/sessions/:id', description: 'Get session details' },
|
|
679
710
|
{ method: 'DELETE', path: '/api/v1/sessions/:id', description: 'Close a session' },
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// apps/cli/src/daemon/workspace-guard.ts
|
|
2
|
+
//
|
|
3
|
+
// Guard for a caller-supplied session `cwd` on the daemon's external API.
|
|
4
|
+
//
|
|
5
|
+
// The session cwd *is* the containment boundary: `read` / `glob` / `grep` are
|
|
6
|
+
// confined to it by `resolveSafe`, and the blocked-path list covers system
|
|
7
|
+
// directories but not home directories. So a caller free to name any cwd turns
|
|
8
|
+
// "read inside the project" into "read anything" — the value is therefore only
|
|
9
|
+
// accepted from the user's trusted-workspace list, or from inside the directory
|
|
10
|
+
// the daemon was started in.
|
|
11
|
+
|
|
12
|
+
import { resolve, sep } from 'node:path'
|
|
13
|
+
|
|
14
|
+
/** True when `dir` is `root` itself or nested beneath it. `..` cannot escape. */
|
|
15
|
+
function isWithin(dir: string, root: string): boolean {
|
|
16
|
+
const d = resolve(dir)
|
|
17
|
+
const r = resolve(root)
|
|
18
|
+
return d === r || d.startsWith(r.endsWith(sep) ? r : r + sep)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function isCwdAllowed(
|
|
22
|
+
cwd: string,
|
|
23
|
+
isTrusted: (dir: string) => boolean,
|
|
24
|
+
daemonRoot: string = process.cwd(),
|
|
25
|
+
): boolean {
|
|
26
|
+
if (typeof cwd !== 'string' || !cwd) return false
|
|
27
|
+
return isWithin(cwd, daemonRoot) || isTrusted(cwd)
|
|
28
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -26,6 +26,7 @@ import { loadSessionMemories, getMemoryManager } from './core/memory/memory-load
|
|
|
26
26
|
import { ContextManager } from './core/context'
|
|
27
27
|
import { PrefixCacheTracker } from './core/context-token'
|
|
28
28
|
import { QueryEngine } from './core/engine'
|
|
29
|
+
import { RulesLoader } from './core/rules-loader'
|
|
29
30
|
import { generateSessionName } from './core/session-name'
|
|
30
31
|
import { ExperienceRuleEngine } from './core/rule-engine.js'
|
|
31
32
|
import { SessionLog } from './core/session-log'
|
|
@@ -535,6 +536,10 @@ export async function runApp(options: RunOptions): Promise<void> {
|
|
|
535
536
|
engine.setSkills(vajraContext.get(SKILLS_KEY)!)
|
|
536
537
|
engine.setLlm(vajraContext.get(LLM_KEY)!)
|
|
537
538
|
|
|
539
|
+
// Path-scoped rules (.mipham/rules/*.md) — injected when the AI touches a
|
|
540
|
+
// matching file. Loaded once at startup; setRulesLoader performs the load.
|
|
541
|
+
engine.setRulesLoader(new RulesLoader(process.cwd()))
|
|
542
|
+
|
|
538
543
|
// Wire inference hooks (DLP) configuration
|
|
539
544
|
const inferenceHookConfig = loadInferenceHookConfig()
|
|
540
545
|
engine.setInferenceHookConfig(inferenceHookConfig)
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
export const PACKAGE_NAME = '@miphamai/cli' as const
|
|
10
10
|
|
|
11
11
|
/** 当前发布版本 */
|
|
12
|
-
export const PACKAGE_VERSION = '0.81.
|
|
12
|
+
export const PACKAGE_VERSION = '0.81.5' as const
|
|
13
13
|
|
|
14
14
|
/** npm install 全局安装命令 */
|
|
15
15
|
export const NPM_INSTALL_COMMAND = `npm install -g ${PACKAGE_NAME}` as const
|
|
@@ -23,7 +23,7 @@ export const BUNDLED_SKILLS: ReadonlyArray<BundledSkill> = [
|
|
|
23
23
|
{ type: 'standard', raw: "---\nname: safe-coding\ndescription: Safe coding rules for code generation — validate external/user input before use and throw RangeError on invalid input\nversion: 1.0.0\n---\n\n# Safe Coding\n\n处理外部/用户输入前必须校验:`null`、`undefined`、空字符串、格式非法时,抛出 `RangeError`,消息为 `'invalid input'`。\n" },
|
|
24
24
|
{ type: 'standard', raw: "---\nname: security-review\ndescription: Security audit skill — vulnerability scanning, OWASP Top 10, secrets detection, supply chain analysis, and compliance checking\nversion: 1.0.0\n---\n\n# Security Review\n\nComprehensive security audit for codebases. Covers vulnerability detection, compliance, and hardening recommendations.\n\n## Audit Checklist\n\n### 1. Secrets & Credentials\n\n- [ ] No hardcoded API keys, tokens, or passwords in source files\n- [ ] `.env` and `*.pem` files in `.gitignore`\n- [ ] API keys use environment variables or secret managers\n- [ ] No credentials in git history (check `git log -p`)\n- [ ] CI/CD secrets stored securely (not in workflow files)\n\n### 2. OWASP Top 10\n\n- [ ] **Injection**: SQL, NoSQL, OS command, LDAP injection points\n- [ ] **Broken Authentication**: Weak password policies, missing MFA\n- [ ] **Sensitive Data Exposure**: Unencrypted PII, missing TLS\n- [ ] **XXE**: XML external entity processing\n- [ ] **Broken Access Control**: Missing authorization checks\n- [ ] **Security Misconfiguration**: Default credentials, verbose errors\n- [ ] **XSS**: Reflected, stored, DOM-based cross-site scripting\n- [ ] **Insecure Deserialization**: Untrusted data deserialization\n- [ ] **Using Vulnerable Components**: Outdated dependencies with CVEs\n- [ ] **Insufficient Logging**: Missing audit trails for auth events\n\n### 3. Supply Chain\n\n- [ ] All dependencies have known licenses (no copyleft/GPL)\n- [ ] No dependencies with critical CVEs\n- [ ] Lock files committed (pnpm-lock.yaml, package-lock.json)\n- [ ] Dependency update policy in place\n- [ ] SBOM (Software Bill of Materials) available\n\n### 4. Network & API Security\n\n- [ ] TLS 1.3 enforced for all external communications\n- [ ] API endpoints have rate limiting\n- [ ] CORS configured with explicit origins (not `*`)\n- [ ] SSRF protections in place (URL validation, IP filtering)\n- [ ] WebSocket connections use WSS\n- [ ] GraphQL endpoints have query depth limits\n\n### 5. File System & Path Security\n\n- [ ] Path traversal protections (no `../../../etc/passwd`)\n- [ ] File upload validation (type, size, content inspection)\n- [ ] Symlink attacks prevented\n- [ ] Sensitive directories blocked (`/etc`, `/proc`, `/sys`)\n- [ ] Temporary files cleaned up after use\n\n### 6. Code-Level Security\n\n- [ ] No `eval()` or `Function()` with user input\n- [ ] No `child_process.exec()` with unsanitized input\n- [ ] Regex patterns safe from ReDoS\n- [ ] Prototype pollution prevented\n- [ ] No `dangerouslySetInnerHTML` without sanitization (React)\n- [ ] SQL queries use parameterized statements\n\n### 7. Authentication & Sessions\n\n- [ ] Passwords hashed with bcrypt/argon2 (not MD5/SHA1)\n- [ ] Session tokens use `httpOnly`, `secure`, `SameSite=Strict`\n- [ ] JWT tokens have reasonable expiration\n- [ ] Account lockout after failed attempts\n- [ ] Password reset tokens expire and are single-use\n\n### 8. Data Protection\n\n- [ ] PII data encrypted at rest (AES-256-GCM)\n- [ ] Data encrypted in transit (TLS 1.3)\n- [ ] Logs do not contain sensitive data\n- [ ] Database backups encrypted\n- [ ] Data retention policies defined\n\n### 9. Infrastructure\n\n- [ ] Infrastructure as Code (Terraform/Pulumi) used\n- [ ] Cloud resources not publicly exposed unless intended\n- [ ] Security groups / firewalls restrict inbound traffic\n- [ ] Container images scanned for vulnerabilities\n- [ ] Kubernetes pods run as non-root\n\n### 10. Logging & Monitoring\n\n- [ ] Authentication events logged\n- [ ] Failed access attempts logged and alerted\n- [ ] Structured logging format (JSON)\n- [ ] No PII in log messages\n- [ ] Alert thresholds configured for critical events\n\n## Report Format\n\n```\nSecurity Review Report\n======================\nDate: YYYY-MM-DD\nSeverity: Critical | High | Medium | Low\n\nFinding #N: [Title]\nSeverity: Critical/High/Medium/Low\nLocation: file:line\nDescription: [What was found]\nRisk: [What could happen]\nFix: [How to resolve]\n```\n\n## Compliance Standards\n\n- OWASP ASVS Level 2\n- PCI DSS (if handling payment data)\n- GDPR (if handling EU personal data)\n- SOC 2 Type II\n- ISO 27001\n" },
|
|
25
25
|
{ type: 'standard', raw: "---\nname: self-review\ndescription: Self-review of staged or recently changed code — reuse, simplification, efficiency, and architectural alignment\nversion: 2.0.0\n---\n\n# Self Review\n\nReview your own code changes before committing or merging. Focus on quality improvements, not bug hunting.\n\n## When to Run\n\n- Before committing changes\n- After completing a feature or fix\n- Before requesting a peer review\n- As the final step before merging\n\n## Review Passes\n\n### Pass 1: Reuse\n\n- Is there existing code that does the same thing?\n- Are there utility functions or shared libraries you missed?\n- Could this be solved with a standard library method?\n- Are you reimplementing something the framework provides?\n\n### Pass 2: Simplification\n\n- Can a complex function be split into smaller, named functions?\n- Are there unnecessary abstractions (interfaces with one impl, unused generics)?\n- Can nested conditionals be flattened with early returns?\n- Is there dead code, unused imports, or commented-out blocks?\n\n### Pass 3: Efficiency\n\n- Are you looping over data multiple times when once would suffice?\n- Are large objects being copied unnecessarily?\n- Could a synchronous operation be made async/non-blocking?\n- Are regex patterns compiled once or on every call?\n\n### Pass 4: Altitude (Architectural Alignment)\n\n- Does this code belong where it is?\n- Is it in the right layer (UI / business logic / data access)?\n- Does it follow existing patterns in the codebase?\n- Would a new developer understand where to find this?\n\n## Output\n\nAfter each pass, either:\n\n- Apply the improvement directly (for clear wins)\n- Note the observation with a recommendation (for trade-off decisions)\n\n## Anti-Patterns\n\n- ❌ Rewriting working code for style preference\n- ❌ Adding abstractions \"just in case\"\n- ❌ Changing code outside the scope of your changes\n- ❌ \"This could be a microservice\" — no it couldn't\n" },
|
|
26
|
-
{ type: 'standard', raw: "---\nname: superpower\ndescription: Skill discovery and invocation system — find and use skills before any response or action\nversion: 2.
|
|
26
|
+
{ type: 'standard', raw: "---\nname: superpower\ndescription: Skill discovery and invocation system — find and use skills before any response or action\nversion: 2.1.0\n---\n\n<SUBAGENT-STOP>\nIf you were dispatched as a subagent to execute a specific task, ignore this skill.\n</SUBAGENT-STOP>\n\n# Superpowers — Using Skills\n\n## The Rule\n\n**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means you should invoke it to check.\n\nThen announce \"Using [skill] to [purpose]\" and follow the skill exactly. If it has a checklist, create a todo per item.\n\n## How to Access Skills\n\nUse the `Skill` tool to invoke skills by name. When you invoke a skill, its content is loaded — follow it directly.\n\n## Skill Discovery\n\n### Check Available Skills\n\nSkills are listed in `<system-reminder>` messages. Scan this list when receiving a task.\n\n### Matching Algorithm\n\n1. Parse the user's request for intent keywords\n2. Scan skill names and descriptions for matches\n3. If ANY skill matches at ≥1% probability → invoke it\n4. Multiple matches → invoke all that may apply\n5. Invoked skill doesn't fit → that's fine, don't use it\n\n### Priority Order\n\n1. **Process skills first** — to-spec, debug-loop, tdd. These determine HOW to approach\n2. **Implementation skills second** — implement, codebase-design. These guide execution\n\n## Red Flags\n\nThese thoughts mean STOP — you're rationalizing:\n\n| Thought | Reality |\n| ----------------------------------- | ------------------------------------------------------ |\n| \"This is just a simple question\" | Questions are tasks. Check skills. |\n| \"I need more context first\" | Skill check comes BEFORE clarifying questions. |\n| \"Let me explore the codebase first\" | Skills tell you HOW to explore. Check first. |\n| \"I can check git/files quickly\" | Files lack conversation context. Check for skills. |\n| \"Let me gather information first\" | Skills tell you HOW to gather information. |\n| \"This doesn't need a formal skill\" | If a skill exists, use it. |\n| \"I remember this skill\" | Skills evolve. Read current version. |\n| \"This doesn't count as a task\" | Action = task. Check for skills. |\n| \"The skill is overkill\" | Simple things become complex. Use it. |\n| \"I'll just do this one thing first\" | Check BEFORE doing anything. |\n| \"This feels productive\" | Undisciplined action wastes time. Skills prevent this. |\n| \"I know what that means\" | Knowing the concept ≠ using the skill. Invoke it. |\n\n## Skill Types\n\n- **Rigid** (tdd, debug-loop): Follow exactly. Don't adapt away discipline.\n- **Flexible** (patterns): Adapt principles to context.\n\nThe skill itself tells you which type it is.\n\n## User Instructions\n\nUser instructions (CLAUDE.md, AGENTS.md, MIPHAM.md, direct requests) take precedence over skills, which in turn override default behavior.\n\nInstructions say WHAT, not HOW. \"Add X\" or \"Fix Y\" doesn't mean skip workflows. Only skip a skill workflow when the user has explicitly told you to.\n" },
|
|
27
27
|
{ type: 'standard', raw: "---\nname: tdd\ndescription: Test-Driven Development — red-green-refactor cycle with language-specific guidance and test design rules\nversion: 2.0.0\n---\n\n# Test-Driven Development (TDD)\n\n## The Cycle\n\n```\nRED → GREEN → REFACTOR → repeat\n```\n\n### 1. RED — Write a Failing Test\n\nWrite the smallest test that captures the behavior you want:\n\n- Name the test descriptively: `it('should return 0 for empty string')`\n- Use the AAA pattern: **A**rrange → **A**ct → **A**ssert\n- Run to confirm it **fails** (not errors — fails)\n- If it passes before implementation, your test is wrong\n\n### 2. GREEN — Make It Pass\n\nWrite the **minimum** code to make the test pass:\n\n- Don't optimize, don't generalize, don't add features\n- A hardcoded return is fine if it passes the test\n- Run all tests — the new one should pass, old ones should still pass\n\n### 3. REFACTOR — Clean Up\n\nImprove the code while tests stay green:\n\n- Remove duplication (test code and production code)\n- Improve names, extract helpers\n- Simplify logic\n- Run tests after each change\n\n## Test Design Rules\n\n- **Deterministic**: No `Date.now()`, `Math.random()`, or network calls in test bodies\n- **Isolated**: Each test sets up its own state; no test-order dependency\n- **Fast**: Unit tests should run in milliseconds, not seconds\n- **Readable**: Test output should explain what broke without reading source\n\n## Language-Specific Guidance\n\n### TypeScript / JavaScript (Vitest)\n\n```ts\nimport { describe, it, expect } from 'vitest'\n\ndescribe('sum', () => {\n it('should add two positive numbers', () => {\n expect(sum(2, 3)).toBe(5)\n })\n it('should handle zero', () => {\n expect(sum(0, 5)).toBe(5)\n })\n})\n```\n\nFile naming: `src/foo.ts` → `test/foo.test.ts`\n\n### Python (pytest)\n\n```python\ndef test_sum_positive():\n assert sum(2, 3) == 5\n\ndef test_sum_zero():\n assert sum(0, 5) == 5\n```\n\n### Go (testing package)\n\n```go\nfunc TestSumPositive(t *testing.T) {\n got := Sum(2, 3)\n want := 5\n if got != want {\n t.Errorf(\"Sum(2,3) = %d; want %d\", got, want)\n }\n}\n```\n\n## When NOT to TDD\n\n- Exploratory spikes (throw away after learning)\n- Configuration files and types (compile-time enforced)\n- Generated code\n" },
|
|
28
28
|
{ type: 'standard', raw: "---\nname: to-spec\ndescription: Turn a conversation into a structured specification document. Use after a grill-with-docs session or any requirements discussion to capture decisions in a durable, shareable format.\nversion: 1.0.0\nuser-invocable: true\nallowed-tools:\n - Read\n - Write\n - Edit\n - Bash\n---\n\n# To Spec — Conversation → Specification\n\nTurn the output of a requirements discussion into a structured specification document. This is the bridge between `/grill-with-docs` (alignment) and `/triage` (task decomposition).\n\n## When to Use\n\n- After a `/grill-with-docs` session — capture what was decided\n- After any requirements discussion — before starting implementation\n- User asks: \"write this up\", \"create a spec\", \"document the plan\"\n- Before handing off work to another session or person\n\n## When NOT to Use\n\n- The requirements are a single sentence and obvious\n- You're in the middle of a grill session — finish the interview first\n- The scope is so small that the spec would be longer than the implementation\n\n---\n\n## Spec Format\n\nWrite to `docs/specs/YYYY-MM-DD-slug.md`:\n\n```markdown\n---\nstatus: draft | approved | implemented\ncreated: 2026-08-10\n---\n\n# {Title}\n\n## Problem\n\n{What problem are we solving? Why now? 1-3 sentences.}\n\n## Scope\n\n### In Scope\n\n- {What we're building}\n\n### Out of Scope (Explicit)\n\n- {What we're NOT building — prevents scope creep}\n\n## Requirements\n\n### Functional\n\n- **{Requirement}**: {Description}. Acceptance: {measurable criterion}.\n\n### Non-Functional\n\n- **Performance**: {latency, throughput targets}\n- **Security**: {auth, data protection, threat model}\n- **Scale**: {expected volume, growth projections}\n\n## Design Decisions\n\n- **Decision**: {What we decided}. Because: {why}. Alternatives considered: {options + reasons rejected}.\n\n## Domain Model\n\n{Key terms and their definitions — from CONTEXT.md or the grill session.}\n\n## Edge Cases\n\n- **{Scenario}**: {Expected behavior}\n- **{Scenario}**: {Expected behavior}\n\n## Open Questions\n\n- {Question} — {who needs to answer / when needed}\n```\n\n---\n\n## The Spec Workflow\n\n### Step 1: Extract from Conversation\n\nScan the conversation history for:\n\n- Decisions made (explicit and implicit)\n- Terms defined (candidates for CONTEXT.md)\n- Edge cases discussed\n- Alternatives rejected (and why)\n- Open questions that remain\n\n### Step 2: Fill Gaps\n\nFor each gap you find:\n\n- Edge cases not discussed → flag as Open Questions\n- Terms used but not defined → propose definitions\n- Assumptions not stated → make them explicit\n\n### Step 3: Validate with User\n\nPresent the spec and ask:\n\n1. \"Does this match your understanding?\"\n2. \"What's missing?\"\n3. \"What's wrong?\"\n4. \"What surprised you?\"\n\n### Step 4: Feed Into Triage\n\nOnce approved, the spec's functional requirements become tickets in `/triage`. Non-functional requirements become acceptance criteria.\n\n---\n\n## Anti-Patterns\n\n- **Waterfall trap**: Don't try to spec everything upfront. Spec the next increment. Specs are living documents, not contracts.\n- **Premature detail**: Don't spec API signatures or DB schemas in the spec — those are implementation details.\n- **Vague acceptance**: \"Works well\" is not acceptance criteria. \"Returns 200 with valid JWT within 500ms\" is.\n\n---\n\n## Integration With Mipham Code\n\n- **grill-with-docs**: Input — the grill session produces the raw material\n- **triage**: Output — the spec feeds into ticket decomposition\n- **domain-modeling**: Terms discovered during spec writing go to CONTEXT.md\n- **Memory System**: The spec file persists as project reference across sessions\n" },
|
|
29
29
|
{ type: 'standard', raw: "---\nname: triage\ndescription: Structured task decomposition and tracking across sessions. Use for breaking complex plans into trackable tickets with dependency graphs, checking task status, or continuing work from a previous session.\nversion: 1.0.0\nuser-invocable: true\nallowed-tools:\n - Read\n - Write\n - Edit\n - Bash\n - Glob\n - Grep\n---\n\n# Triage — Cross-Session Task Tracking\n\nTurn plans into trackable tickets with dependency management. Inspired by Matt Pocock's `triage` + `to-tickets` + `wayfinder` skills, consolidated into one Mipham Code skill.\n\n## When to Use\n\n- Breaking a large plan into actionable tickets\n- Tracking work across multiple sessions\n- User asks: \"what's next?\", \"where did I leave off?\", \"what's the status?\"\n- Complex tasks with dependencies between them\n\n---\n\n## The Ticket Format\n\nTickets live in `.mipham/tickets/` as individual Markdown files:\n\n```markdown\n---\nid: T-001\ntitle: Add user authentication\nstatus: in-progress\npriority: P0\ndepends_on: []\nblocks: [T-003]\ncreated: 2026-08-10\ntags:\n - auth\n - backend\n---\n\n## Description\n\nAdd JWT-based authentication with refresh token rotation.\n\n## Acceptance Criteria\n\n- [ ] Login endpoint returns access + refresh tokens\n- [ ] Refresh endpoint rotates tokens\n- [ ] Invalid tokens return 401\n- [ ] Rate limiting on login attempts\n\n## Notes\n\n- OAuth not in scope for T-001 (punted to T-005)\n```\n\n### Status Values\n\n| Status | Meaning |\n| ------------- | ------------------------------------------ |\n| `backlog` | Not yet planned for any session |\n| `planned` | Scoped and ready to work |\n| `in-progress` | Currently being worked on |\n| `review` | Implementation done, awaiting verification |\n| `done` | Verified and merged |\n| `blocked` | Cannot proceed due to dependency |\n| `wontfix` | Decided not to do |\n\n---\n\n## The Triage Workflow\n\n### Phase 1: Decompose (Plan → Tickets)\n\nGiven a plan or feature request:\n\n1. **Identify the smallest independently-valuable units of work**\n - Each ticket should deliver value on its own\n - If a ticket requires 3+ files touched, it's probably too big\n - If a ticket can be done in < 15 minutes, it's probably too small\n\n2. **Map dependencies**\n - What must be done first? (hard dependency)\n - What would be easier after something else? (soft dependency)\n - What blocks other work? (reverse dependency)\n\n3. **Assign priorities**\n - **P0**: Blocks other work, must do first\n - **P1**: High value, should do soon\n - **P2**: Nice to have, can defer\n - **P3**: Optional, do if time permits\n\n4. **Write acceptance criteria**\n - Specific, testable, unambiguous\n - \"Login works\" is bad. \"POST /auth/login with valid credentials returns 200 + JWT\" is good.\n\n### Phase 2: Status Check\n\nWhen the user asks \"what's next?\" or \"what's the status?\":\n\n1. Read `.mipham/tickets/` directory\n2. Report:\n - Currently in-progress tickets\n - Blocked tickets (and what's blocking them)\n - Next unblocked P0/P1 tickets ready to work\n - Recently completed tickets (for context)\n\n### Phase 3: Session Handoff\n\nWhen starting a new session, check for continuity:\n\n1. Read the previous session's context from the session store\n2. Check ticket statuses — any that were `in-progress` last session?\n3. Present: \"Last session you were working on T-004 (Add rate limiting). Continue from there, or start on T-007 (API docs) which is next in the P1 queue?\"\n\n### Phase 4: Ticket Lifecycle\n\nWhen working on a ticket:\n\n- Mark it `in-progress` when you start\n- Mark it `review` when implementation is done\n- Mark it `done` after verification (tests pass, typecheck clean)\n- If you discover new dependencies, add them to `blocks`/`depends_on`\n\n---\n\n## Dependency Graph\n\nFor tickets with complex dependencies, generate a visual summary:\n\n```\nT-001 (Auth) ──blocks──→ T-003 (Dashboard)\n │ │\n └──blocks──→ T-002 (API) ─┘\n │\n └──soft-dep──→ T-004 (Rate Limiting)\n\nReady to work: T-001 (no dependencies)\nBlocked: T-002 (waiting on T-001), T-003 (waiting on T-001, T-002)\n```\n\n---\n\n## Integration With Mipham Code\n\n- **Session Store**: Ticket status persists across sessions via `.mipham/tickets/`\n- **Memory System**: Active tickets are loaded as project memory for context\n- **grill-with-docs**: The output of a grill session feeds directly into ticket decomposition\n- **Background Agents**: Long-running work on a ticket can be spawned as a background agent\n- **Critical Thinking Layer**: When decomposing, ask \"what's the smallest thing that delivers value?\" — don't over-decompose\n" },
|