@normful/picadillo 2.0.3 → 2.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/CHANGELOG.md +18 -0
- package/README.md +28 -8
- package/extensions/gastown.ts +160 -0
- package/extensions/mulch.ts +75 -0
- package/package.json +1 -1
- package/skills/mulch/SKILL.md +148 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
|
+
## [2.1.0] - 2026-02-17
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- *(extension)* Add mulch extension for project learning management
|
|
6
|
+
- *(extension)* Add gastown extension for gt integration
|
|
7
|
+
|
|
8
|
+
### 🚜 Refactor
|
|
9
|
+
|
|
10
|
+
- *(test)* Replace `vi.fn()` with `mock` from `bun:test` in parrot tests
|
|
11
|
+
|
|
12
|
+
### 📚 Documentation
|
|
13
|
+
|
|
14
|
+
- Update README with reorganized skills and extensions sections
|
|
1
15
|
## [2.0.3] - 2026-02-16
|
|
2
16
|
|
|
3
17
|
### 📚 Documentation
|
|
4
18
|
|
|
5
19
|
- *(README)* Add parrot extension demo screenshot to README
|
|
20
|
+
|
|
21
|
+
### ⚙️ Miscellaneous Tasks
|
|
22
|
+
|
|
23
|
+
- Release 2.0.3
|
|
6
24
|
## [2.0.2] - 2026-02-16
|
|
7
25
|
|
|
8
26
|
### 🚀 Features
|
package/README.md
CHANGED
|
@@ -27,7 +27,8 @@ repeatedly poke reality until it tastes better.”
|
|
|
27
27
|
pi install https://github.com/normful/picadillo
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Configure what you don't want with `pi config`. It will modify
|
|
30
|
+
Configure what you don't want with `pi config`. It will modify
|
|
31
|
+
`~/.pi/agent/settings.json`
|
|
31
32
|
|
|
32
33
|
## Dependencies
|
|
33
34
|
|
|
@@ -35,19 +36,38 @@ Configure what you don't want with `pi config`. It will modify `~/.pi/agent/sett
|
|
|
35
36
|
|
|
36
37
|
## Skills
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|-------|-------------|
|
|
40
|
-
| [run-in-tmux](skills/run-in-tmux/) | Run commands in a new tmux session with split panes. Useful for dev environments, parallel processes, and persistent background tasks. |
|
|
39
|
+
### run-in-tmux
|
|
41
40
|
|
|
41
|
+
Run commands in a new tmux session with split panes. Useful for dev
|
|
42
|
+
environments, parallel processes, and persistent background tasks.
|
|
43
|
+
|
|
44
|
+
### mulch
|
|
45
|
+
|
|
46
|
+
Usage examples showing how to use [mulch](https://github.com/jayminwest/mulch)
|
|
47
|
+
to record and retrieve structured project
|
|
48
|
+
learnings.
|
|
42
49
|
|
|
43
50
|
## Extensions
|
|
44
51
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
### parrot
|
|
53
|
+
|
|
54
|
+
Opens the last AI response in an external text editor (respects `$VISUAL` or
|
|
55
|
+
`$EDITOR`), then sends your edited content back to the chat. Useful for editing
|
|
56
|
+
AI responses before re-sending, copying output to a full-featured editor, or
|
|
57
|
+
iterating with custom edits.
|
|
58
|
+
|
|
59
|
+
Usage: `/parrot` or `Alt+R`
|
|
60
|
+
|
|
61
|
+
### gastown
|
|
62
|
+
|
|
63
|
+
Hooks to automatically run `gt prime` and `gt mail` from [gastown](https://github.com/steveyegge/gastown)
|
|
64
|
+
|
|
65
|
+
### mulch
|
|
48
66
|
|
|
67
|
+
Hooks to automatically run [mulch](https://github.com/jayminwest/mulch) for
|
|
68
|
+
recording and retrieving structured project learnings.
|
|
49
69
|
|
|
50
|
-
|
|
70
|
+
## Parrot Extension Demo
|
|
51
71
|
|
|
52
72
|
[](https://asciinema.org/a/788693)
|
|
53
73
|
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import type { ExecResult } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
|
|
4
|
+
export type { ExecResult };
|
|
5
|
+
export const GASTOWN_MESSAGE_TYPE = "gastown";
|
|
6
|
+
export const AUTONOMOUS_ROLES = new Set(["polecat", "witness", "refinery", "deacon"]);
|
|
7
|
+
|
|
8
|
+
export interface GastownConfig {
|
|
9
|
+
role?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Run `gt prime --hook` to get prime text.
|
|
14
|
+
*/
|
|
15
|
+
export async function gastownPrime(execFn: ExtensionAPI["exec"]): Promise<string> {
|
|
16
|
+
let primeText = "";
|
|
17
|
+
try {
|
|
18
|
+
const { stdout } = await execFn("gt", ["prime", "--hook"]);
|
|
19
|
+
primeText = stdout;
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error("[gastown] gt prime failed:", e);
|
|
22
|
+
}
|
|
23
|
+
return primeText;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Run `gt mail check --inject` to get mail text.
|
|
28
|
+
*/
|
|
29
|
+
export async function gastownMailCheck(execFn: ExtensionAPI["exec"]): Promise<string> {
|
|
30
|
+
let mailText = "";
|
|
31
|
+
try {
|
|
32
|
+
const { stdout } = await execFn("gt", ["mail", "check", "--inject"]);
|
|
33
|
+
mailText = stdout;
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.error("[gastown] gt mail check --inject failed:", e);
|
|
36
|
+
}
|
|
37
|
+
return mailText;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Record session costs using `gt costs record --session <sessionId>`.
|
|
42
|
+
*/
|
|
43
|
+
export async function recordSessionCosts(
|
|
44
|
+
execFn: ExtensionAPI["exec"],
|
|
45
|
+
sessionId: string,
|
|
46
|
+
): Promise<void> {
|
|
47
|
+
try {
|
|
48
|
+
await execFn("gt", ["costs", "record", "--session", sessionId]);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.error("[gastown] gt costs record failed:", e);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Check if the given role is an autonomous role.
|
|
56
|
+
*/
|
|
57
|
+
export function isAutonomousRole(role: string): boolean {
|
|
58
|
+
return AUTONOMOUS_ROLES.has(role.toLowerCase());
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Handle session_start event.
|
|
63
|
+
* Fetches prime text and mail, sends combined message.
|
|
64
|
+
*/
|
|
65
|
+
export async function handleSessionStart(
|
|
66
|
+
execFn: ExtensionAPI["exec"],
|
|
67
|
+
sendMessage: ExtensionAPI["sendMessage"],
|
|
68
|
+
): Promise<void> {
|
|
69
|
+
const primeText = await gastownPrime(execFn);
|
|
70
|
+
if (!primeText) return;
|
|
71
|
+
|
|
72
|
+
const mailText = await gastownMailCheck(execFn);
|
|
73
|
+
sendMessage(
|
|
74
|
+
{
|
|
75
|
+
customType: GASTOWN_MESSAGE_TYPE,
|
|
76
|
+
content: primeText + "\n\n" + mailText,
|
|
77
|
+
display: true,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
deliverAs: "steer",
|
|
81
|
+
triggerTurn: true,
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Handle before_agent_start event.
|
|
88
|
+
* Sends mail text only for autonomous roles.
|
|
89
|
+
*/
|
|
90
|
+
export function handleBeforeAgentStart(
|
|
91
|
+
role: string,
|
|
92
|
+
mailText: string,
|
|
93
|
+
): { message: { customType: string; content: string; display: boolean } } | undefined {
|
|
94
|
+
if (!mailText || !isAutonomousRole(role)) return undefined;
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
message: {
|
|
98
|
+
customType: GASTOWN_MESSAGE_TYPE,
|
|
99
|
+
content: mailText,
|
|
100
|
+
display: true,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Handle session_compact event.
|
|
107
|
+
* Fetches prime text and sends as follow-up.
|
|
108
|
+
*/
|
|
109
|
+
export async function handleSessionCompact(
|
|
110
|
+
execFn: ExtensionAPI["exec"],
|
|
111
|
+
sendMessage: ExtensionAPI["sendMessage"],
|
|
112
|
+
): Promise<void> {
|
|
113
|
+
const primeText = await gastownPrime(execFn);
|
|
114
|
+
if (!primeText) return;
|
|
115
|
+
|
|
116
|
+
sendMessage(
|
|
117
|
+
{
|
|
118
|
+
customType: GASTOWN_MESSAGE_TYPE,
|
|
119
|
+
content: primeText,
|
|
120
|
+
display: true,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
deliverAs: "followUp",
|
|
124
|
+
triggerTurn: true,
|
|
125
|
+
},
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Handle session_shutdown event.
|
|
131
|
+
* Records session costs.
|
|
132
|
+
*/
|
|
133
|
+
export async function handleSessionShutdown(
|
|
134
|
+
execFn: ExtensionAPI["exec"],
|
|
135
|
+
sessionId: string,
|
|
136
|
+
): Promise<void> {
|
|
137
|
+
await recordSessionCosts(execFn, sessionId);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export default function (pi: ExtensionAPI) {
|
|
141
|
+
const role = (process.env.GT_ROLE || "").toLowerCase();
|
|
142
|
+
|
|
143
|
+
pi.on("session_start", async (_event, _ctx) => {
|
|
144
|
+
await handleSessionStart(pi.exec, pi.sendMessage);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
pi.on("before_agent_start", async (_event, _ctx) => {
|
|
148
|
+
const mailText = await gastownMailCheck(pi.exec);
|
|
149
|
+
return handleBeforeAgentStart(role, mailText);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
pi.on("session_compact", async (_event, _ctx) => {
|
|
153
|
+
await handleSessionCompact(pi.exec, pi.sendMessage);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
pi.on("session_shutdown", async (_event, ctx) => {
|
|
157
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
158
|
+
await handleSessionShutdown(pi.exec, sessionId);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import type { ExecResult } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
|
|
4
|
+
export type { ExecResult };
|
|
5
|
+
export const MULCH_MESSAGE_TYPE = "mulch";
|
|
6
|
+
|
|
7
|
+
const APPEND_TO_MULCH_PRIME = `
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
But you should NOT run any above mulch commands right now.
|
|
11
|
+
I am just letting you know the session close protocol, so that you remember to do it later.
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
export async function mulchPrime(
|
|
15
|
+
execFn: ExtensionAPI["exec"],
|
|
16
|
+
): Promise<string> {
|
|
17
|
+
let primeText = "";
|
|
18
|
+
try {
|
|
19
|
+
const { stdout } = await execFn("mulch", ["prime"]);
|
|
20
|
+
primeText = stdout;
|
|
21
|
+
} catch (e) {
|
|
22
|
+
console.error("mulch prime failed:", e);
|
|
23
|
+
}
|
|
24
|
+
return primeText;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function handleSessionStart(
|
|
28
|
+
execFn: ExtensionAPI["exec"],
|
|
29
|
+
sendMessage: ExtensionAPI["sendMessage"],
|
|
30
|
+
): Promise<void> {
|
|
31
|
+
const primeText = await mulchPrime(execFn);
|
|
32
|
+
if (!primeText) return;
|
|
33
|
+
|
|
34
|
+
sendMessage(
|
|
35
|
+
{
|
|
36
|
+
customType: MULCH_MESSAGE_TYPE,
|
|
37
|
+
content: primeText + APPEND_TO_MULCH_PRIME,
|
|
38
|
+
display: true,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
deliverAs: "steer",
|
|
42
|
+
triggerTurn: true,
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function handleSessionCompact(
|
|
48
|
+
execFn: ExtensionAPI["exec"],
|
|
49
|
+
sendMessage: ExtensionAPI["sendMessage"],
|
|
50
|
+
): Promise<void> {
|
|
51
|
+
const primeText = await mulchPrime(execFn);
|
|
52
|
+
if (!primeText) return;
|
|
53
|
+
|
|
54
|
+
sendMessage(
|
|
55
|
+
{
|
|
56
|
+
customType: MULCH_MESSAGE_TYPE,
|
|
57
|
+
content: primeText + APPEND_TO_MULCH_PRIME,
|
|
58
|
+
display: true,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
deliverAs: "followUp",
|
|
62
|
+
triggerTurn: true,
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default function (pi: ExtensionAPI) {
|
|
68
|
+
pi.on("session_start", async (_event, _ctx) => {
|
|
69
|
+
await handleSessionStart(pi.exec, pi.sendMessage);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
pi.on("session_compact", async (_event, _ctx) => {
|
|
73
|
+
await handleSessionCompact(pi.exec, pi.sendMessage);
|
|
74
|
+
});
|
|
75
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mulch
|
|
3
|
+
description: Record and retrieve structured project learnings using `mulch` CLI. Use when working on code and wanting to capture patterns, failures, decisions, conventions, references, or guides for future sessions.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Mulch Usage
|
|
7
|
+
|
|
8
|
+
Mulch captures project learnings as structured "records" organized by **domain**. Each domain represents a category of knowledge:
|
|
9
|
+
|
|
10
|
+
| Domain | Description |
|
|
11
|
+
|--------|-------------|
|
|
12
|
+
| `purpose` | Why the project exists |
|
|
13
|
+
| `identity` | Brand, naming, tone |
|
|
14
|
+
| `public-interface` | APIs, CLI, UI surfaces |
|
|
15
|
+
| `internal-structure` | Code organization |
|
|
16
|
+
| `dependencies` | External libraries |
|
|
17
|
+
| `data` | Database, schemas, models |
|
|
18
|
+
| `configuration` | Env vars, settings |
|
|
19
|
+
| `observability` | Logging, metrics, tracing |
|
|
20
|
+
| `glossary` | Domain-specific terminology |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# When to Run Commands
|
|
25
|
+
|
|
26
|
+
## 1. At Session Start
|
|
27
|
+
|
|
28
|
+
Load all project expertise into your context:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
mulch prime # load all project learnings
|
|
32
|
+
mulch prime --files src/foo.ts # load only records relevant to specific files
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This gives you awareness of existing patterns, decisions, and conventions before you start working.
|
|
36
|
+
|
|
37
|
+
## 2. While Working
|
|
38
|
+
|
|
39
|
+
Record insights as you discover them:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Create a new domain if needed
|
|
43
|
+
mulch add purpose
|
|
44
|
+
|
|
45
|
+
# Record different types of learnings
|
|
46
|
+
mulch record <domain> --type pattern --name "Auth Pattern" --description "Use middleware for auth checks before route handlers"
|
|
47
|
+
mulch record <domain> --type failure --name "Missing Validation" --description "Form field not validated on client" --resolution "Add Zod schema validation"
|
|
48
|
+
mulch record <domain> --type decision --title "Chose PostgreSQL" --rationale "Needed relational queries and ACID compliance"
|
|
49
|
+
mulch record <domain> --type convention --content "API routes go in src/routes/"
|
|
50
|
+
mulch record <domain> --type reference --name "Error Handling" --description "See docs/error patterns"
|
|
51
|
+
mulch record <domain> --type guide --name "Setup Guide" --description "Run npm install then npm run dev"
|
|
52
|
+
|
|
53
|
+
# Add evidence, files, tags
|
|
54
|
+
mulch record <domain> --type pattern --name "..." --description "..." --evidence-commit abc123
|
|
55
|
+
mulch record <domain> --type pattern --name "..." --description "..." --evidence-bead fix-42 # link to bd issue
|
|
56
|
+
mulch record <domain> --type pattern --name "..." --description "..." --files "src/auth.ts,src/middleware.ts"
|
|
57
|
+
mulch record <domain> --type pattern --name "..." --description "..." --tags "security,auth"
|
|
58
|
+
|
|
59
|
+
# Link related records
|
|
60
|
+
mulch record <domain> --type pattern --name "..." --description "..." --relates-to mx-abc123
|
|
61
|
+
mulch record <domain> --type pattern --name "..." --description "..." --supersedes mx-old123
|
|
62
|
+
|
|
63
|
+
# Batch record from JSON file
|
|
64
|
+
mulch record <domain> --batch records.json
|
|
65
|
+
mulch record <domain> --batch records.json --dry-run # preview first
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 3. Before Finishing Your Task
|
|
69
|
+
|
|
70
|
+
After all file editing is done (but before committing), run `mulch learn` to see what changed and get recording suggestions:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
mulch learn
|
|
74
|
+
mulch learn --since HEAD~3 # diff against specific commit
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This shows which files were modified and can suggest what to record based on your changes.
|
|
78
|
+
|
|
79
|
+
Then record any learnings and sync to git:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
mulch record <domain> ... # record learnings from this session
|
|
83
|
+
mulch sync # validates, stages, and commits .mulch/ changes
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Note:** `mulch sync` runs `mulch validate` automatically before committing, so you don't need a separate validate step.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
# Querying & Searching
|
|
91
|
+
|
|
92
|
+
**Tip:** `--json` is a global flag that works with all commands below.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Check domain status
|
|
96
|
+
mulch status --json
|
|
97
|
+
mulch status --json | jq '.'
|
|
98
|
+
|
|
99
|
+
# Query records in a domain
|
|
100
|
+
mulch query <domain> --json
|
|
101
|
+
mulch query <domain> --type failure --json # filter by type
|
|
102
|
+
|
|
103
|
+
# Search across all domains
|
|
104
|
+
mulch search "auth" --json
|
|
105
|
+
mulch search "auth" --type pattern --json # filter by type
|
|
106
|
+
mulch search "auth" --tag security --json # filter by tag
|
|
107
|
+
mulch search "auth" --domain <domain> --json # limit to domain
|
|
108
|
+
|
|
109
|
+
# Show recent additions
|
|
110
|
+
mulch ready --json
|
|
111
|
+
|
|
112
|
+
# Show changes since git ref
|
|
113
|
+
mulch diff --json
|
|
114
|
+
mulch diff --since v1.0.0 --json
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Other Useful Commands
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# Edit a record
|
|
121
|
+
mulch edit <domain> mx-abc123 --description "Updated description"
|
|
122
|
+
|
|
123
|
+
# Delete a record
|
|
124
|
+
mulch delete <domain> mx-abc123
|
|
125
|
+
|
|
126
|
+
# Compact similar records
|
|
127
|
+
mulch compact <domain> --analyze
|
|
128
|
+
mulch compact <domain> --apply --type pattern --name "Auth Patterns" --description "Combined from 5 records"
|
|
129
|
+
|
|
130
|
+
# Generate onboarding snippet for AGENTS.md
|
|
131
|
+
mulch onboard
|
|
132
|
+
mulch onboard --check # verify it's installed
|
|
133
|
+
|
|
134
|
+
# Health check
|
|
135
|
+
mulch doctor # check for issues with --fix option
|
|
136
|
+
|
|
137
|
+
# Check for updates
|
|
138
|
+
mulch update
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## JSON Output
|
|
142
|
+
|
|
143
|
+
All commands support `--json` for scripting:
|
|
144
|
+
```bash
|
|
145
|
+
mulch status --json | jq '.'
|
|
146
|
+
mulch query <domain> --json | jq '.[] | select(.type == "failure")'
|
|
147
|
+
mulch search "auth" --json | jq '.[] | .name'
|
|
148
|
+
```
|