@pi-unipi/unipi 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +143 -0
  2. package/package.json +70 -0
  3. package/packages/btw/README.md +95 -0
  4. package/packages/btw/extensions/btw.ts +1972 -0
  5. package/packages/btw/skills/btw/SKILL.md +149 -0
  6. package/packages/core/README.md +74 -0
  7. package/packages/core/index.ts +10 -0
  8. package/packages/info-screen/README.md +115 -0
  9. package/packages/info-screen/index.ts +165 -0
  10. package/packages/memory/README.md +94 -0
  11. package/packages/memory/index.ts +209 -0
  12. package/packages/memory/skills/memory/SKILL.md +151 -0
  13. package/packages/ralph/README.md +101 -0
  14. package/packages/ralph/index.ts +548 -0
  15. package/packages/subagents/README.md +114 -0
  16. package/packages/unipi/index.ts +28 -0
  17. package/packages/workflow/README.md +129 -0
  18. package/packages/workflow/index.ts +155 -0
  19. package/packages/workflow/skills/brainstorm/SKILL.md +202 -0
  20. package/packages/workflow/skills/consolidate/SKILL.md +142 -0
  21. package/packages/workflow/skills/consultant/SKILL.md +97 -0
  22. package/packages/workflow/skills/document/SKILL.md +120 -0
  23. package/packages/workflow/skills/gather-context/SKILL.md +122 -0
  24. package/packages/workflow/skills/plan/SKILL.md +169 -0
  25. package/packages/workflow/skills/quick-work/SKILL.md +110 -0
  26. package/packages/workflow/skills/review-work/SKILL.md +131 -0
  27. package/packages/workflow/skills/scan-issues/SKILL.md +183 -0
  28. package/packages/workflow/skills/work/SKILL.md +144 -0
  29. package/packages/workflow/skills/worktree-create/SKILL.md +69 -0
  30. package/packages/workflow/skills/worktree-list/SKILL.md +67 -0
  31. package/packages/workflow/skills/worktree-merge/SKILL.md +79 -0
@@ -0,0 +1,209 @@
1
+ /**
2
+ * @unipi/memory — Extension entry
3
+ *
4
+ * Persistent cross-session memory with vector search.
5
+ * All storage is project-scoped. "Global" tools search across all projects.
6
+ * Injects memory titles at session start.
7
+ * Auto-consolidates on compaction.
8
+ */
9
+
10
+ import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
11
+ import {
12
+ UNIPI_EVENTS,
13
+ MODULES,
14
+ emitEvent,
15
+ getPackageVersion,
16
+ } from "@pi-unipi/core";
17
+
18
+ // Get info registry from global (avoids direct import issues with pi's extension loading)
19
+ function getInfoRegistry() {
20
+ const g = globalThis as any;
21
+ return g.__unipi_info_registry;
22
+ }
23
+ import {
24
+ MemoryStorage,
25
+ getProjectName,
26
+ searchAllProjects,
27
+ listAllProjects,
28
+ } from "./storage.js";
29
+ import { registerMemoryTools, MEMORY_TOOLS } from "./tools.js";
30
+ import { registerMemoryCommands } from "./commands.js";
31
+
32
+ /** Package version */
33
+ const VERSION = getPackageVersion(new URL(".", import.meta.url).pathname);
34
+
35
+ /** Storage instance for current project */
36
+ let projectStorage: MemoryStorage | null = null;
37
+
38
+ /**
39
+ * Get storage for the current project.
40
+ */
41
+ function getStorage(): MemoryStorage {
42
+ if (!projectStorage) {
43
+ // Fallback: create new instance (shouldn't happen after session_start)
44
+ return new MemoryStorage("unknown");
45
+ }
46
+ return projectStorage;
47
+ }
48
+
49
+ export default function (pi: ExtensionAPI) {
50
+ // Register skills directory
51
+ const skillsDir = new URL("./skills", import.meta.url).pathname;
52
+ pi.on("resources_discover", async (_event, _ctx) => {
53
+ return {
54
+ skillPaths: [skillsDir],
55
+ };
56
+ });
57
+
58
+ // Register tools and commands
59
+ registerMemoryTools(pi, getStorage);
60
+ registerMemoryCommands(pi, getStorage);
61
+
62
+ // Session lifecycle
63
+ pi.on("session_start", async (_event, ctx) => {
64
+ // Initialize project storage
65
+ const projectName = getProjectName(ctx.cwd);
66
+ projectStorage = new MemoryStorage(projectName);
67
+ projectStorage.init();
68
+
69
+
70
+ // Announce module
71
+ emitEvent(pi, UNIPI_EVENTS.MODULE_READY, {
72
+ name: MODULES.MEMORY,
73
+ version: VERSION,
74
+ commands: [
75
+ "unipi:memory-process",
76
+ "unipi:memory-search",
77
+ "unipi:memory-consolidate",
78
+ "unipi:memory-forget",
79
+ "unipi:global-memory-search",
80
+ "unipi:global-memory-list",
81
+ ],
82
+ tools: [
83
+ MEMORY_TOOLS.STORE,
84
+ MEMORY_TOOLS.SEARCH,
85
+ MEMORY_TOOLS.DELETE,
86
+ MEMORY_TOOLS.LIST,
87
+ MEMORY_TOOLS.GLOBAL_SEARCH,
88
+ MEMORY_TOOLS.GLOBAL_LIST,
89
+ ],
90
+ });
91
+
92
+ // Register info group
93
+ const registry = getInfoRegistry();
94
+ if (registry) {
95
+ console.debug("[memory] Registering info group");
96
+ registry.registerGroup({
97
+ id: "memory",
98
+ name: "Memory",
99
+ icon: "🧠",
100
+ priority: 60,
101
+ config: {
102
+ showByDefault: true,
103
+ stats: [
104
+ { id: "projectCount", label: "Project Memories", show: true },
105
+ { id: "totalCount", label: "Total Memories", show: true },
106
+ { id: "projects", label: "Projects", show: true },
107
+ { id: "recent", label: "Recent Memories", show: true },
108
+ ],
109
+ },
110
+ dataProvider: async () => {
111
+ if (!projectStorage) {
112
+ return {
113
+ projectCount: { value: "0" },
114
+ totalCount: { value: "0" },
115
+ projects: { value: "none" },
116
+ recent: { value: "none" },
117
+ };
118
+ }
119
+
120
+ const projectMemories = projectStorage.listAll();
121
+ const allMemories = listAllProjects();
122
+ const uniqueProjects = [...new Set(allMemories.map((m) => m.project))];
123
+
124
+ // Get 3 most recent memories (sorted by updated DESC in listAll)
125
+ const recentMemories = projectMemories.slice(0, 3);
126
+ const recentList = recentMemories.map(m => m.title).join("\n");
127
+
128
+ return {
129
+ projectCount: { value: String(projectMemories.length) },
130
+ totalCount: { value: String(allMemories.length) },
131
+ projects: {
132
+ value: uniqueProjects.length.toString(),
133
+ detail: uniqueProjects.slice(0, 5).join(", ") + (uniqueProjects.length > 5 ? ` +${uniqueProjects.length - 5} more` : ""),
134
+ },
135
+ recent: {
136
+ value: recentMemories.length > 0 ? recentMemories[0].title : "none",
137
+ detail: recentMemories.length > 1 ? recentMemories.slice(1).map(m => m.title).join("\n") : undefined,
138
+ },
139
+ };
140
+ },
141
+ });
142
+ }
143
+
144
+ // Show memory status in UI
145
+ if (ctx.hasUI) {
146
+ const projectCount = projectStorage.listAll().length;
147
+ const allMemories = listAllProjects();
148
+ const projectCountAll = allMemories.length;
149
+ ctx.ui.setStatus(
150
+ "unipi-memory",
151
+ `🧠 memory ${projectCount}p/${projectCountAll}all`
152
+ );
153
+ }
154
+ });
155
+
156
+ // Inject memory titles at session start
157
+ pi.on("before_agent_start", async (event, ctx) => {
158
+ if (!projectStorage) return;
159
+
160
+ const projectName = getProjectName(ctx.cwd);
161
+ const projectMemories = projectStorage.listAll();
162
+
163
+ if (projectMemories.length === 0) {
164
+ return; // No memories to inject
165
+ }
166
+
167
+ let injection = "\n\n<memory>\n";
168
+ injection += `Available memories for project "${projectName}":\n\n`;
169
+
170
+ // Project memories
171
+ for (const m of projectMemories) {
172
+ injection += `- ${m.title}\n`;
173
+ }
174
+
175
+ injection += "\nUse memory_search to retrieve full content. Use memory_store to save new memories.\n";
176
+ injection += "Use global_memory_search to search across ALL projects.\n";
177
+ injection += "</memory>";
178
+
179
+ return {
180
+ systemPrompt: event.systemPrompt + injection,
181
+ };
182
+ });
183
+
184
+ // Auto-consolidation on compaction
185
+ pi.on("session_before_compact", async (event, ctx) => {
186
+ const { preparation } = event;
187
+
188
+ // Extract summary text
189
+ const summary = preparation.previousSummary || "";
190
+
191
+ if (!summary || summary.length < 100) {
192
+ // Summary too short to extract memories from
193
+ return;
194
+ }
195
+
196
+ // For now, just log that consolidation would happen
197
+ // Future: Use LLM to extract memories
198
+ console.log("[unipi/memory] Auto-consolidation triggered, summary length:", summary.length);
199
+
200
+ // Don't modify the compaction summary - return unchanged
201
+ return {};
202
+ });
203
+
204
+ // Cleanup on shutdown
205
+ pi.on("session_shutdown", async () => {
206
+ projectStorage?.close();
207
+ projectStorage = null;
208
+ });
209
+ }
@@ -0,0 +1,151 @@
1
+ ---
2
+ name: memory
3
+ description: >
4
+ Persistent cross-session memory management. Store and retrieve user preferences,
5
+ project decisions, code patterns, and conversation summaries across sessions.
6
+ Use when you need to remember something important or recall past context.
7
+ allowed-tools:
8
+ - memory_store
9
+ - memory_search
10
+ - memory_delete
11
+ - memory_list
12
+ - global_memory_store
13
+ - global_memory_search
14
+ - global_memory_list
15
+ - read
16
+ ---
17
+
18
+ # Memory
19
+
20
+ Persistent cross-session memory for Pi coding agent. Memories survive session restarts, compaction, and context resets.
21
+
22
+ ## When to Store Memory
23
+
24
+ Store memory when you encounter:
25
+
26
+ | Type | Examples | Title Format |
27
+ |------|----------|--------------|
28
+ | **Preference** | User likes tabs, prefers functional style | `style_typescript_prefer_tabs` |
29
+ | **Decision** | Chose PostgreSQL over MySQL, JWT over sessions | `db_postgres_chosen_over_mysql` |
30
+ | **Pattern** | How auth is structured, API naming conventions | `api_rest_versioning_v2` |
31
+ | **Summary** | Key findings from debugging, research results | `perf_slow_query_root_cause` |
32
+
33
+ ## Naming Convention
34
+
35
+ **Format:** `<most_important>_<less_important>_<lesser>`
36
+
37
+ **Rules:**
38
+ - Use underscores, not hyphens
39
+ - Start with category (style, db, auth, api, arch, etc.)
40
+ - Be specific: `auth_jwt_prefer_refresh_tokens` not `auth_tokens`
41
+ - Keep under 60 characters
42
+
43
+ **Good titles:**
44
+ - `style_typescript_strict_mode_always`
45
+ - `db_postgres_use_connection_pooling`
46
+ - `arch_api_versioning_v2_breaking`
47
+ - `perf_cache_redis_for_sessions`
48
+
49
+ **Bad titles:**
50
+ - `auth` (too vague)
51
+ - `User prefers tabs` (not snake_case)
52
+ - `auth-jwt-refresh` (hyphens, not underscores)
53
+
54
+ ## When to Search Memory
55
+
56
+ Search memory when:
57
+
58
+ 1. **User references past work:** "Remember when we fixed the auth bug?"
59
+ 2. **Making similar decisions:** "What did we decide about database choice?"
60
+ 3. **Setting up new features:** "What's the user's coding style?"
61
+ 4. **Debugging recurring issues:** "Have we seen this error before?"
62
+
63
+ ## How to Use Tools
64
+
65
+ ### Store a memory:
66
+ ```
67
+ memory_store(
68
+ title: "auth_jwt_prefer_refresh_tokens",
69
+ content: "User prefers short-lived access tokens (15min) with long-lived refresh tokens (30d). Always implement token rotation on refresh.",
70
+ tags: ["auth", "jwt", "preferences"],
71
+ type: "preference"
72
+ )
73
+ ```
74
+
75
+ ### Search memories:
76
+ ```
77
+ memory_search(query: "auth tokens")
78
+ ```
79
+
80
+ ### List all project memories:
81
+ ```
82
+ memory_list()
83
+ ```
84
+
85
+ ### Delete a memory:
86
+ ```
87
+ memory_delete(title: "auth_jwt_prefer_refresh_tokens")
88
+ ```
89
+
90
+ ## Project vs Cross-Project Search
91
+
92
+ | Action | Scope | Tools |
93
+ |--------|-------|-------|
94
+ | **Store** | Always project-scoped | `memory_store` |
95
+ | **Search this project** | Current project only | `memory_search` |
96
+ | **Search all projects** | Cross-project | `global_memory_search` |
97
+ | **List all** | Cross-project | `global_memory_list` |
98
+
99
+ **All memories are project-scoped.** When you store a memory, it belongs to the current project. Use `global_memory_search` to search across ALL projects when looking for past work or user preferences.
100
+
101
+ ## Update-First Principle
102
+
103
+ **Always check before creating.** Before storing a new memory:
104
+
105
+ 1. Search for similar memories
106
+ 2. If found and relevant → UPDATE the existing memory
107
+ 3. If not found → CREATE new memory
108
+
109
+ This prevents memory duplication and keeps memory clean.
110
+
111
+ ## Consolidation
112
+
113
+ When the user runs `/unipi:memory-consolidate` or during compaction:
114
+
115
+ 1. Review the session for memory-worthy items
116
+ 2. For each item:
117
+ - Search for existing similar memory
118
+ - Update if found, create if not
119
+ 3. Report what was stored/updated
120
+
121
+ ## Reading Memory Files
122
+
123
+ Memory files are stored in `~/.unipi/memory/` as markdown with YAML frontmatter:
124
+
125
+ ```markdown
126
+ ---
127
+ title: auth_jwt_prefer_refresh_tokens
128
+ tags: [auth, jwt, preferences]
129
+ project: my-app
130
+ created: 2026-04-26T10:00:00Z
131
+ updated: 2026-04-26T15:30:00Z
132
+ type: preference
133
+ ---
134
+
135
+ # Auth: Prefer Refresh Tokens
136
+
137
+ User prefers short-lived access tokens (15min) with long-lived refresh tokens (30d).
138
+ Always implement token rotation on refresh.
139
+ ```
140
+
141
+ You can read these files directly with the `read` tool for full context.
142
+
143
+ ## Anti-Patterns
144
+
145
+ | Don't | Do Instead |
146
+ |-------|------------|
147
+ | Store everything | Only store decisions, preferences, patterns, summaries |
148
+ | Create duplicate memories | Search first, update existing |
149
+ | Use vague titles | Use specific `<category>_<detail>` format |
150
+ | Store in wrong scope | Project-specific = project scope, universal = global |
151
+ | Forget to update | When context changes, update the memory |
@@ -0,0 +1,101 @@
1
+ # @pi-unipi/ralph
2
+
3
+ Long-running iterative development loops for [Pi coding agent](https://github.com/badlogic/pi-mono).
4
+
5
+ Start a loop, the agent works through iterations, reflects periodically, and completes when done. State persists across sessions.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pi install npm:@pi-unipi/ralph
11
+ ```
12
+
13
+ Or as part of the full suite:
14
+ ```bash
15
+ pi install npm:unipi
16
+ ```
17
+
18
+ ## Commands
19
+
20
+ | Command | Description |
21
+ |---------|-------------|
22
+ | `/unipi:ralph start <name\|path>` | Start a new loop |
23
+ | `/unipi:ralph stop` | Pause current loop |
24
+ | `/unipi:ralph resume <name>` | Resume a paused loop |
25
+ | `/unipi:ralph status` | Show all loops |
26
+ | `/unipi:ralph cancel <name>` | Delete loop state |
27
+ | `/unipi:ralph archive <name>` | Move loop to archive |
28
+ | `/unipi:ralph clean [--all]` | Clean completed loops |
29
+ | `/unipi:ralph list --archived` | Show archived loops |
30
+ | `/unipi:ralph nuke [--yes]` | Delete all ralph data |
31
+
32
+ ### Options
33
+
34
+ | Flag | Description |
35
+ |------|-------------|
36
+ | `--max-iterations N` | Stop after N iterations (default: 50) |
37
+ | `--items-per-iteration N` | Suggest N items per turn (prompt hint) |
38
+ | `--reflect-every N` | Reflect every N iterations |
39
+
40
+ ## Tools
41
+
42
+ | Tool | Description |
43
+ |------|-------------|
44
+ | `ralph_start` | Start a ralph loop (called by agent) |
45
+ | `ralph_done` | Signal iteration complete (called by agent) |
46
+
47
+ ## How It Works
48
+
49
+ 1. **Start** — creates a task file in `.unipi/ralph/` and begins iteration loop
50
+ 2. **Iterate** — agent works on task, updates progress, calls `ralph_done`
51
+ 3. **Reflect** — optional reflection prompts at configurable intervals
52
+ 4. **Complete** — agent emits `COMPLETE` marker or max iterations reached
53
+ 5. **Persist** — state saved to disk, survives session restarts
54
+
55
+ ```
56
+ start → iterate → done → iterate → ... → complete
57
+ ↑ │
58
+ └──── resume ────────────────────────────┘
59
+ ```
60
+
61
+ ## Task File Format
62
+
63
+ Loops operate on markdown task files:
64
+
65
+ ```markdown
66
+ # Task
67
+
68
+ Redesign the authentication system.
69
+
70
+ ## Goals
71
+ - Migrate to JWT refresh tokens
72
+ - Add role-based access control
73
+
74
+ ## Checklist
75
+ - [x] Design token rotation
76
+ - [ ] Implement refresh endpoint
77
+ - [ ] Add RBAC middleware
78
+ - [ ] Write tests
79
+
80
+ ## Notes
81
+ Session 1: Completed token rotation design.
82
+ ```
83
+
84
+ ## State Storage
85
+
86
+ ```
87
+ .unipi/ralph/
88
+ ├── loop-name.state.json ← active/paused state
89
+ ├── loop-name.md ← task file
90
+ └── archive/
91
+ └── loop-name.* ← archived loops
92
+ ```
93
+
94
+ ## Integration
95
+
96
+ - **@pi-unipi/core** — event types, constants, utilities
97
+ - **@pi-unipi/info-screen** — registers info group showing loop status
98
+
99
+ ## License
100
+
101
+ MIT