pi-monofold 0.3.3 → 0.4.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 +199 -195
- package/focus-preset.ts +53 -0
- package/index.ts +1874 -1766
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,195 +1,199 @@
|
|
|
1
|
-
# pi-monofold
|
|
2
|
-
|
|
3
|
-
[](https://github.com/eiei114/pi-monofold/actions/workflows/ci.yml)
|
|
4
|
-
[](https://github.com/eiei114/pi-monofold/actions/workflows/publish.yml)
|
|
5
|
-
[](https://www.npmjs.com/package/pi-monofold)
|
|
6
|
-
[](https://www.npmjs.com/package/pi-monofold)
|
|
7
|
-
[](./LICENSE)
|
|
8
|
-
[](https://github.com/eiei114/pi-monofold)
|
|
9
|
-
[](https://docs.npmjs.com/generating-provenance-statements)
|
|
10
|
-
|
|
11
|
-
Pi extension that folds multiple local repositories and folders into a guarded **Virtual Monorepo** for AI agents.
|
|
12
|
-
|
|
13
|
-
## What this is
|
|
14
|
-
|
|
15
|
-
Pi Monofold (`pi-monofold`) keeps repositories physically separate while giving Pi a lightweight manifest, routed writes, workspace-aware reads, guarded commands, and explicit git flows. Documentation, rules, product context, and implementation code can appear as one connected system without migrating everything into a single git repository.
|
|
16
|
-
|
|
17
|
-
See [docs/usage.md](./docs/usage.md) for configuration, commands, agent tools, and guard behavior.
|
|
18
|
-
|
|
19
|
-
## Features
|
|
20
|
-
|
|
21
|
-
- **Virtual monorepo manifest** — declare workspaces and project workspaces in `.pi/monofold.yaml`
|
|
22
|
-
- **Routed Markdown writes** — route PRDs, progress notes, and other doc types to configured folders
|
|
23
|
-
- **Workspace-aware reads** — list, read, search, and tree views scoped to readable workspaces, with bounded previews by default
|
|
24
|
-
- **Capability guard** — block or confirm `read` / `write` / `edit` / `grep` / `find` / `bash` based on workspace tags
|
|
25
|
-
- **Focus presets** — tag-based focus targets for the control workspace
|
|
26
|
-
- **Natural-language commands** — `/monofold:explore`, `/monofold:write`, `/monofold:config`, `/monofold:git`, and more
|
|
27
|
-
- **Strict agent tools** — `monofold_*` tools for programmatic access behind the command surface
|
|
28
|
-
- **Config migration** — upgrade legacy `.pi/monofold.yml` with backups and validation
|
|
29
|
-
|
|
30
|
-
## Install
|
|
31
|
-
|
|
32
|
-
Pi Monofold is a Pi package. Install it with Pi's package installer from git or npm.
|
|
33
|
-
|
|
34
|
-
> Security: Pi packages run with full system access. Review packages before installing third-party code.
|
|
35
|
-
|
|
36
|
-
### From git
|
|
37
|
-
|
|
38
|
-
```powershell
|
|
39
|
-
pi install git:github.com/eiei114/pi-monofold
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Project-local install:
|
|
43
|
-
|
|
44
|
-
```powershell
|
|
45
|
-
pi install -l git:github.com/eiei114/pi-monofold
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Pin a version:
|
|
49
|
-
|
|
50
|
-
```powershell
|
|
51
|
-
pi install git:github.com/eiei114/pi-monofold@v0.3.2
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Try without installing:
|
|
55
|
-
|
|
56
|
-
```powershell
|
|
57
|
-
pi -e git:github.com/eiei114/pi-monofold
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### From npm
|
|
61
|
-
|
|
62
|
-
```powershell
|
|
63
|
-
pi install npm:pi-monofold
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Project-local install:
|
|
67
|
-
|
|
68
|
-
```powershell
|
|
69
|
-
pi install -l npm:pi-monofold
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Pin a version:
|
|
73
|
-
|
|
74
|
-
```powershell
|
|
75
|
-
pi install npm:pi-monofold@0.3.2
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Try without installing:
|
|
79
|
-
|
|
80
|
-
```powershell
|
|
81
|
-
pi -e npm:pi-monofold
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## Quick start
|
|
85
|
-
|
|
86
|
-
1. Install the extension (see [Install](#install)).
|
|
87
|
-
2. In your control repository, create `.pi/monofold.yaml` with at least one workspace entry (or run `/monofold:init`).
|
|
88
|
-
3. Start Pi in the control repository and run `/monofold:explore show the project workspaces`.
|
|
89
|
-
4. Use `/monofold:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
| `/monofold:
|
|
99
|
-
| `/monofold:
|
|
100
|
-
| `/monofold:
|
|
101
|
-
| `/monofold:
|
|
102
|
-
| `/monofold:
|
|
103
|
-
| `/monofold:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
`monofold_read`
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
**
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
├──
|
|
139
|
-
│ ├──
|
|
140
|
-
│ ├──
|
|
141
|
-
│ └──
|
|
142
|
-
├──
|
|
143
|
-
│
|
|
144
|
-
├──
|
|
145
|
-
|
|
146
|
-
├──
|
|
147
|
-
|
|
148
|
-
├──
|
|
149
|
-
├──
|
|
150
|
-
├──
|
|
151
|
-
├──
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
1
|
+
# pi-monofold
|
|
2
|
+
|
|
3
|
+
[](https://github.com/eiei114/pi-monofold/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/eiei114/pi-monofold/actions/workflows/publish.yml)
|
|
5
|
+
[](https://www.npmjs.com/package/pi-monofold)
|
|
6
|
+
[](https://www.npmjs.com/package/pi-monofold)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
[](https://github.com/eiei114/pi-monofold)
|
|
9
|
+
[](https://docs.npmjs.com/generating-provenance-statements)
|
|
10
|
+
|
|
11
|
+
Pi extension that folds multiple local repositories and folders into a guarded **Virtual Monorepo** for AI agents.
|
|
12
|
+
|
|
13
|
+
## What this is
|
|
14
|
+
|
|
15
|
+
Pi Monofold (`pi-monofold`) keeps repositories physically separate while giving Pi a lightweight manifest, routed writes, workspace-aware reads, guarded commands, and explicit git flows. Documentation, rules, product context, and implementation code can appear as one connected system without migrating everything into a single git repository.
|
|
16
|
+
|
|
17
|
+
See [docs/usage.md](./docs/usage.md) for configuration, commands, agent tools, and guard behavior.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **Virtual monorepo manifest** — declare workspaces and project workspaces in `.pi/monofold.yaml`
|
|
22
|
+
- **Routed Markdown writes** — route PRDs, progress notes, and other doc types to configured folders
|
|
23
|
+
- **Workspace-aware reads** — list, read, search, and tree views scoped to readable workspaces, with bounded previews by default
|
|
24
|
+
- **Capability guard** — block or confirm `read` / `write` / `edit` / `grep` / `find` / `bash` based on workspace tags
|
|
25
|
+
- **Focus presets** — tag-based focus targets for the control workspace
|
|
26
|
+
- **Natural-language commands** — `/monofold:explore`, `/monofold:write`, `/monofold:config`, `/monofold:git`, and more
|
|
27
|
+
- **Strict agent tools** — `monofold_*` tools for programmatic access behind the command surface
|
|
28
|
+
- **Config migration** — upgrade legacy `.pi/monofold.yml` with backups and validation
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
Pi Monofold is a Pi package. Install it with Pi's package installer from git or npm.
|
|
33
|
+
|
|
34
|
+
> Security: Pi packages run with full system access. Review packages before installing third-party code.
|
|
35
|
+
|
|
36
|
+
### From git
|
|
37
|
+
|
|
38
|
+
```powershell
|
|
39
|
+
pi install git:github.com/eiei114/pi-monofold
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Project-local install:
|
|
43
|
+
|
|
44
|
+
```powershell
|
|
45
|
+
pi install -l git:github.com/eiei114/pi-monofold
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Pin a version:
|
|
49
|
+
|
|
50
|
+
```powershell
|
|
51
|
+
pi install git:github.com/eiei114/pi-monofold@v0.3.2
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Try without installing:
|
|
55
|
+
|
|
56
|
+
```powershell
|
|
57
|
+
pi -e git:github.com/eiei114/pi-monofold
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### From npm
|
|
61
|
+
|
|
62
|
+
```powershell
|
|
63
|
+
pi install npm:pi-monofold
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Project-local install:
|
|
67
|
+
|
|
68
|
+
```powershell
|
|
69
|
+
pi install -l npm:pi-monofold
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Pin a version:
|
|
73
|
+
|
|
74
|
+
```powershell
|
|
75
|
+
pi install npm:pi-monofold@0.3.2
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Try without installing:
|
|
79
|
+
|
|
80
|
+
```powershell
|
|
81
|
+
pi -e npm:pi-monofold
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Quick start
|
|
85
|
+
|
|
86
|
+
1. Install the extension (see [Install](#install)).
|
|
87
|
+
2. In your control repository, create `.pi/monofold.yaml` with at least one workspace entry (or run `/monofold:init`).
|
|
88
|
+
3. Start Pi in the control repository and run `/monofold:explore show the project workspaces`.
|
|
89
|
+
4. Use `/monofold:focus` or `ctrl+shift+m` to switch focus presets when `focusPresets` are configured.
|
|
90
|
+
5. Use `/monofold:write` for routed Markdown outputs and `/monofold:git` for guarded git workflows.
|
|
91
|
+
|
|
92
|
+
Example command flows: [docs/examples.md](./docs/examples.md).
|
|
93
|
+
|
|
94
|
+
## Usage summary
|
|
95
|
+
|
|
96
|
+
| Surface | Purpose |
|
|
97
|
+
|---------|---------|
|
|
98
|
+
| `/monofold:explore` | List, read, search, or inspect workspace trees |
|
|
99
|
+
| `/monofold:write` | Create routed Markdown outputs |
|
|
100
|
+
| `/monofold:config` | Add or change workspaces and project workspaces |
|
|
101
|
+
| `/monofold:git` | Run guarded git status, commit, push, or commit+push |
|
|
102
|
+
| `/monofold:focus` | Select the active focus preset from a TUI list |
|
|
103
|
+
| `/monofold:guide` | Interactive guide for common flows |
|
|
104
|
+
| `/monofold:init` | Create or update `.pi/monofold.yaml` |
|
|
105
|
+
| `/monofold:update` | Migrate legacy config and optionally request config edits |
|
|
106
|
+
|
|
107
|
+
Default focus shortcut: `ctrl+shift+m` cycles Active Focus forward through `focusPresets` YAML order. No backward focus shortcut ships in the MVP.
|
|
108
|
+
|
|
109
|
+
Agent tools (`monofold_list`, `monofold_read`, `monofold_write`, `monofold_git`, `monofold_init`) sit behind these commands. Full reference: [docs/usage.md](./docs/usage.md).
|
|
110
|
+
|
|
111
|
+
## Safe read defaults
|
|
112
|
+
|
|
113
|
+
`monofold_read` can reach files across multiple configured workspaces. Returning full file bodies or unbounded search/tree output by default would flood the agent chat and can bias later turns. Pi Monofold therefore uses **preview-first, capped-by-default** reads.
|
|
114
|
+
|
|
115
|
+
| `monofold_read` mode | Default output |
|
|
116
|
+
|----------------------|----------------|
|
|
117
|
+
| **file** | Path, size, line/character counts, modified time, then a bounded preview (first **20** lines, up to **2,000** characters). Files that already fit those bounds are shown in full without a truncation marker. |
|
|
118
|
+
| **search** | Up to **50** match lines and **8,000** characters of ripgrep output. |
|
|
119
|
+
| **tree** | Up to **200** entries; traversal depth is capped at **5**. |
|
|
120
|
+
|
|
121
|
+
When output is cut, the tool response includes a **`[truncated]`** marker (file mode) or a **`[truncated: …]`** footer (search/tree) that states what was shown and how to request more.
|
|
122
|
+
|
|
123
|
+
**Request more content intentionally:**
|
|
124
|
+
|
|
125
|
+
| Goal | `monofold_read` parameters |
|
|
126
|
+
|------|----------------------------|
|
|
127
|
+
| Full file body | `mode: "file"`, `includeContent: true` |
|
|
128
|
+
| Larger bounded file slice | `head`, `tail`, and/or `maxChars` (positive integers) |
|
|
129
|
+
| More search results | Higher `maxMatches` and/or `maxChars`, or a narrower `path` / `query` |
|
|
130
|
+
| Larger directory tree | Higher `maxEntries`, lower `depth`, or a narrower `path` |
|
|
131
|
+
|
|
132
|
+
Agents should call **`monofold_read`** (not guess at raw Pi `read`). Humans should use **`/monofold:explore`** with natural language. Legacy slash commands such as `/monofold:read` apply the same caps for compatibility but are **not** the preferred human-facing surface—see [docs/usage.md](./docs/usage.md#safe-read-contract-monofold_read).
|
|
133
|
+
|
|
134
|
+
## Package contents
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
pi-monofold/
|
|
138
|
+
├── .github/workflows/
|
|
139
|
+
│ ├── auto-release.yml # Auto-tag + release on merge to main
|
|
140
|
+
│ ├── ci.yml # Validate on PR / push
|
|
141
|
+
│ └── publish.yml # Publish to npm (Trusted Publishing)
|
|
142
|
+
├── docs/
|
|
143
|
+
│ ├── usage.md # Config, commands, agent API, guard
|
|
144
|
+
│ ├── examples.md # Command examples
|
|
145
|
+
│ └── release.md # Release and publish flow
|
|
146
|
+
├── tests/
|
|
147
|
+
│ └── focus-preset.test.ts
|
|
148
|
+
├── CHANGELOG.md
|
|
149
|
+
├── SECURITY.md
|
|
150
|
+
├── focus-preset.ts
|
|
151
|
+
├── index.ts
|
|
152
|
+
├── LICENSE
|
|
153
|
+
├── package.json
|
|
154
|
+
├── README.md
|
|
155
|
+
├── validation.ts
|
|
156
|
+
└── tsconfig.json
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Development
|
|
160
|
+
|
|
161
|
+
Clone and validate:
|
|
162
|
+
|
|
163
|
+
```powershell
|
|
164
|
+
git clone https://github.com/eiei114/pi-monofold.git
|
|
165
|
+
cd pi-monofold
|
|
166
|
+
npm install
|
|
167
|
+
npm run check
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Try the local checkout without installing:
|
|
171
|
+
|
|
172
|
+
```powershell
|
|
173
|
+
pi -e .
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Release
|
|
177
|
+
|
|
178
|
+
Releases are automated. See [docs/release.md](./docs/release.md) for details.
|
|
179
|
+
|
|
180
|
+
1. Bump `version` in `package.json` and update `CHANGELOG.md`.
|
|
181
|
+
2. Merge to `main`.
|
|
182
|
+
3. **Auto Release** tags `v<version>` and creates a GitHub release when the tag is new.
|
|
183
|
+
4. The tag triggers **Publish**, which publishes to npm with OIDC provenance.
|
|
184
|
+
|
|
185
|
+
## Security
|
|
186
|
+
|
|
187
|
+
Pi Monofold intercepts standard Pi tool calls when monofold config is present. Writes and shell commands are allowed only when the resolved workspace grants the matching capability. Git commit/push via raw `bash` is blocked; use `/monofold:git` or `monofold_git` instead.
|
|
188
|
+
|
|
189
|
+
Report vulnerabilities per [SECURITY.md](./SECURITY.md).
|
|
190
|
+
|
|
191
|
+
## Links
|
|
192
|
+
|
|
193
|
+
- **Repository**: <https://github.com/eiei114/pi-monofold>
|
|
194
|
+
- **npm**: <https://www.npmjs.com/package/pi-monofold>
|
|
195
|
+
- **Issues**: <https://github.com/eiei114/pi-monofold/issues>
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
[MIT](LICENSE)
|
package/focus-preset.ts
CHANGED
|
@@ -120,6 +120,59 @@ export function setActiveFocusPresetId(id: string, focusPresets: FocusPreset[] |
|
|
|
120
120
|
activeFocusInitialized = true;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
export type ActiveFocusPresetPosition = {
|
|
124
|
+
preset: FocusPreset;
|
|
125
|
+
index: number;
|
|
126
|
+
total: number;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export type FocusCycleResult = ActiveFocusPresetPosition & {
|
|
130
|
+
changed: boolean;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/** Returns the current active focus preset and YAML-order position, if any. */
|
|
134
|
+
export function getActiveFocusPresetPosition(
|
|
135
|
+
focusPresets: FocusPreset[] | undefined,
|
|
136
|
+
): ActiveFocusPresetPosition | null {
|
|
137
|
+
if (!focusPresets || focusPresets.length === 0) return null;
|
|
138
|
+
const activeId = getActiveFocusPresetId();
|
|
139
|
+
if (!activeId) return null;
|
|
140
|
+
const index = focusPresets.findIndex((preset) => preset.id === activeId);
|
|
141
|
+
if (index < 0) return null;
|
|
142
|
+
return { preset: focusPresets[index]!, index, total: focusPresets.length };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Selects a focus preset by the user-facing label returned from ctx.ui.select. */
|
|
146
|
+
export function setActiveFocusPresetByLabel(
|
|
147
|
+
label: string,
|
|
148
|
+
focusPresets: FocusPreset[] | undefined,
|
|
149
|
+
): ActiveFocusPresetPosition {
|
|
150
|
+
const presets = focusPresets ?? [];
|
|
151
|
+
const index = presets.findIndex((preset) => preset.label === label);
|
|
152
|
+
if (index < 0) throw new Error(`Unknown focus preset label: ${label}`);
|
|
153
|
+
const preset = presets[index]!;
|
|
154
|
+
setActiveFocusPresetId(preset.id, presets);
|
|
155
|
+
return { preset, index, total: presets.length };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Cycles the active focus preset forward in YAML order. */
|
|
159
|
+
export function cycleActiveFocusPresetForward(focusPresets: FocusPreset[] | undefined): FocusCycleResult | null {
|
|
160
|
+
const presets = focusPresets ?? [];
|
|
161
|
+
if (presets.length === 0) {
|
|
162
|
+
clearActiveFocusPresetId();
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
ensureActiveFocusInitialized(presets);
|
|
167
|
+
const currentId = getActiveFocusPresetId();
|
|
168
|
+
const currentIndex = currentId ? presets.findIndex((preset) => preset.id === currentId) : -1;
|
|
169
|
+
const nextIndex = currentIndex < 0 ? 0 : (currentIndex + 1) % presets.length;
|
|
170
|
+
const preset = presets[nextIndex]!;
|
|
171
|
+
const changed = presets.length > 1 && preset.id !== currentId;
|
|
172
|
+
setActiveFocusPresetId(preset.id, presets);
|
|
173
|
+
return { preset, index: nextIndex, total: presets.length, changed };
|
|
174
|
+
}
|
|
175
|
+
|
|
123
176
|
/** Clears the active focus preset for the current process/session. */
|
|
124
177
|
export function clearActiveFocusPresetId(): void {
|
|
125
178
|
activeFocusPresetId = null;
|