@jals1212/pi-spaces 0.6.0 → 0.7.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 +16 -0
- package/README.md +20 -17
- package/dist/command.js +5 -3
- package/dist/config.js +12 -6
- package/dist/context.js +1 -1
- package/package.json +1 -1
- package/skills/pi-spaces/SKILL.md +15 -10
- package/src/command.ts +5 -3
- package/src/config.ts +12 -6
- package/src/context.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,22 @@ commands and options and a patch version fixes.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 0.7.0
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- The setup hint of `/space new` names the space with its profile, as
|
|
14
|
+
`pi-space` now requires: `pi-space setup <profile>/<name>`, where the
|
|
15
|
+
profile is the one of the running Pi (`pi` when no profile matches its base
|
|
16
|
+
home). `/space` itself still takes the bare name.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- The built-in `pi` profile no longer takes another profile's home: inside
|
|
21
|
+
the Pi of a program whose profile names its `home`, `PI_CODING_AGENT_DIR`
|
|
22
|
+
holds that home, and `pi` now keeps `~/.pi/agent` (its spaces in
|
|
23
|
+
`~/.pi/spaces`) instead of pointing at that program's home and spaces.
|
|
24
|
+
|
|
9
25
|
## 0.6.0
|
|
10
26
|
|
|
11
27
|
### Added
|
package/README.md
CHANGED
|
@@ -45,10 +45,10 @@ From your shell:
|
|
|
45
45
|
```sh
|
|
46
46
|
pi --space work # start Pi in "work"
|
|
47
47
|
pi --space work "review this branch" # with a prompt
|
|
48
|
-
pi-space new work
|
|
49
|
-
pi-space remove work
|
|
50
|
-
pi-space update --every # update the packages of base and of every space (optional CLI)
|
|
51
|
-
pi-space # pick
|
|
48
|
+
pi-space new pi/work # create a space of the profile "pi" (optional CLI)
|
|
49
|
+
pi-space remove pi/work # delete it, after asking; keeps a backup unless told not to (optional CLI)
|
|
50
|
+
pi-space update --every # update the packages of base and of every space of every profile (optional CLI)
|
|
51
|
+
pi-space # pick a space of any profile: arrows, Enter, type to filter (optional CLI)
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
A new space starts empty except for a `settings.json` that declares this package, so `/space` works inside it. Sign in and install packages there as you would in a fresh Pi.
|
|
@@ -59,7 +59,7 @@ A new space starts empty except for a `settings.json` that declares this package
|
|
|
59
59
|
| --- | --- |
|
|
60
60
|
| `/space` | Opens a list of the other spaces and restarts Pi in the one you pick. Without the interactive terminal it lists them instead. |
|
|
61
61
|
| `/space list` | Lists `base` and every space, with `*` on the active one. |
|
|
62
|
-
| `/space new <name>` | Creates an empty space. When a setup command is set (`PI_SPACES_SETUP`), it names `pi-space setup <name>` to run from a shell. |
|
|
62
|
+
| `/space new <name>` | Creates an empty space. When a setup command is set (the profile's `setup`, or `PI_SPACES_SETUP`), it names `pi-space setup <profile>/<name>` to run from a shell, with this Pi's profile (`pi` when none matches). |
|
|
63
63
|
| `/space switch <name>` | Restarts Pi in that space. `base` is the home Pi started with. |
|
|
64
64
|
| `/space switch` | Same as `/space`. |
|
|
65
65
|
| `/space rename <old> <new>` | Renames a space. The active space cannot be renamed. |
|
|
@@ -67,7 +67,7 @@ A new space starts empty except for a `settings.json` that declares this package
|
|
|
67
67
|
| `pi --space <name>` | Starts Pi directly in a space, keeping the prompt and every other option. |
|
|
68
68
|
| `pi --space <name> <command> [args...]` | Runs one of Pi's package commands (`install`, `remove`, `uninstall`, `update`, `list`) in a space, or in the base home with `base`: `pi --space work update --extensions`, `pi --space work install npm:<package>`. |
|
|
69
69
|
|
|
70
|
-
The status line shows `◆ <name>` while a space is active. In the base home it shows `◆ base` when pi-spaces opened it (`pi --space base`, `pi-space base`, or `/space switch base`), and nothing when Pi was started plainly.
|
|
70
|
+
The status line shows `◆ <name>` while a space is active. In the base home it shows `◆ base` when pi-spaces opened it (`pi --space base`, `pi-space pi/base`, or `/space switch base`), and nothing when Pi was started plainly.
|
|
71
71
|
|
|
72
72
|
Choosing an argument such as `switch` from the `/space ` suggestions only fills the line; press Enter again to run it. `/space` and Enter opens the picker in one step.
|
|
73
73
|
|
|
@@ -75,13 +75,13 @@ Choosing an argument such as `switch` from the `/space ` suggestions only fills
|
|
|
75
75
|
|
|
76
76
|
## The `pi-space` command
|
|
77
77
|
|
|
78
|
-
The optional `@jals1212/pi-spaces-cli` package adds a `pi-space` command to your shell: `pi-space`
|
|
78
|
+
The optional `@jals1212/pi-spaces-cli` package adds a `pi-space` command to your shell. It names every space as `<profile>/<name>` (`pi/work`, `my/base`): the profile is an entry of the [config file](#profiles), and `pi` (plain Pi) is always there. `pi-space` opens a picker of the spaces of every profile and starts Pi in the one you choose, `pi-space <profile>/<name> [pi args...]` starts Pi in a named space, `pi-space new <profile>/<name>` creates one, `pi-space rename <profile>/<old> <new>` renames one, `pi-space remove <profile>/<name>` deletes one, and `pi-space list` lists every profile's spaces. `pi-space list|update|install|uninstall <profile>/<name|--every> ...` runs Pi's package commands in a space, in a profile's spaces, or (`--every` alone) in every profile's. The profile says which program to run, where its spaces live and how to prepare a new space; `--command`, `--dir` and `--setup` override those for one call, for one named profile. `pi-space setup <profile>/<name|--every>` runs the setup command again. The command does not read `PI_SPACES_COMMAND`, `PI_SPACES_DIR` or `PI_SPACES_SETUP` as settings: they are what it hands to the Pi it starts. It only runs `pi --space <name> [command]`, so this extension must be installed in the home it opens. See [`cli/README.md`](cli/README.md) for the commands, the options and the exit status.
|
|
79
79
|
|
|
80
80
|
## Profiles
|
|
81
81
|
|
|
82
82
|
A **profile** describes one program that runs Pi (plain Pi is one too): where its agent home is, the command that starts it, where its spaces live, how to prepare a new space, and the variables it needs per space. Profiles live in an optional JSON file, so you do not need shell variables or functions to tell pi-spaces about a program. Without the file, nothing changes: this is exactly how pi-spaces behaves without any configuration.
|
|
83
83
|
|
|
84
|
-
The extension uses the profile whose `home` is the base home Pi started with (the one `base` goes back to); inside a space that is the home the space came from. The optional `pi-space` command ([`cli/README.md`](cli/README.md)) reads the same file and
|
|
84
|
+
The extension uses the profile whose `home` is the base home Pi started with (the one `base` goes back to); inside a space that is the home the space came from. The optional `pi-space` command ([`cli/README.md`](cli/README.md)) reads the same file, and the profile is the first word of every space it takes: `pi-space ml/work`. It does not use `default`: the profile is always named.
|
|
85
85
|
|
|
86
86
|
### The file
|
|
87
87
|
|
|
@@ -97,7 +97,6 @@ A complete example for a program called `my-launcher`, which keeps its own confi
|
|
|
97
97
|
```json
|
|
98
98
|
{
|
|
99
99
|
"schema": "pi-spaces.config/v1",
|
|
100
|
-
"default": "ml",
|
|
101
100
|
"profiles": {
|
|
102
101
|
"ml": {
|
|
103
102
|
"home": "~/.my-launcher/agent",
|
|
@@ -109,7 +108,7 @@ A complete example for a program called `my-launcher`, which keeps its own confi
|
|
|
109
108
|
}
|
|
110
109
|
```
|
|
111
110
|
|
|
112
|
-
With that file, a space of `my-launcher` lives in `~/.my-launcher/spaces/<name>`, `/space new <name>` points you at `pi-space setup
|
|
111
|
+
With that file, a space of `my-launcher` lives in `~/.my-launcher/spaces/<name>`, `/space new <name>` points you at `pi-space setup ml/<name>`, and entering the space `work` starts Pi with `MY_LAUNCHER_CONFIG_HOME=~/.my-launcher/spaces/work/config`, while the base home gets no such variable from pi-spaces. From a shell, `pi-space ml/work` starts that space, and an alias such as `alias mlw='pi-space ml/work'` is optional sugar on top.
|
|
113
112
|
|
|
114
113
|
### Schema `pi-spaces.config/v1`
|
|
115
114
|
|
|
@@ -118,7 +117,7 @@ Top level (every other key is refused):
|
|
|
118
117
|
| Key | Type | Required | Meaning |
|
|
119
118
|
| --- | --- | --- | --- |
|
|
120
119
|
| `schema` | string | yes | Exactly `"pi-spaces.config/v1"`. |
|
|
121
|
-
| `default` | string | no |
|
|
120
|
+
| `default` | string | no | Accepted and validated (it must name a profile of the file, or `pi`), but not used: neither the extension nor `pi-space` falls back to it. `pi-space` warns when it reads a file that has it; remove the key. |
|
|
122
121
|
| `profiles` | object | no | Profiles by name. Names: lowercase letters, digits, `-` and `_`, starting with a letter or digit, at most 64 characters. |
|
|
123
122
|
|
|
124
123
|
A profile (every other key is refused; every key is optional):
|
|
@@ -128,7 +127,7 @@ A profile (every other key is refused; every key is optional):
|
|
|
128
127
|
| `home` | string, not empty | `PI_CODING_AGENT_DIR`, else `~/.pi/agent` | The base agent home of the program: the home it starts Pi with, which `base` goes back to. `~` is your home folder; a relative path is taken from it. |
|
|
129
128
|
| `command` | string, not empty | `pi` for the profile `pi`; none for the others | The command that starts the program (`my-launcher --`), in front of `--space <name>`. |
|
|
130
129
|
| `dir` | string, not empty | `spaces` next to `home` (`~/.my-launcher/agent` gives `~/.my-launcher/spaces`) | The folder that holds the profile's spaces. `~` and relative paths as in `home`. |
|
|
131
|
-
| `setup` | string, not empty | none | The command that prepares a new space; `{home}` and `{name}` become the space's folder and name. With it, `/space new` names `pi-space setup <name>`. |
|
|
130
|
+
| `setup` | string, not empty | none | The command that prepares a new space; `{home}` and `{name}` become the space's folder and name. With it, `/space new` names `pi-space setup <profile>/<name>`. |
|
|
132
131
|
| `spaceEnv` | object of strings | none | Variables that exist only in spaces; see below. Names: letters, digits and `_`, not starting with a digit; not `PI_CODING_AGENT_DIR`, `PI_SPACE`, `PI_SPACE_HOME` or any `PI_SPACES_*`, which pi-spaces sets itself. |
|
|
133
132
|
|
|
134
133
|
All strings are trimmed. `{home}` and `{name}` are replaced everywhere they appear in a `spaceEnv` value (and `setup`), and `$` in a value stays literal.
|
|
@@ -148,7 +147,11 @@ pi-spaces does not create what a variable points to; `setup` (or the program its
|
|
|
148
147
|
|
|
149
148
|
### The built-in `pi` profile
|
|
150
149
|
|
|
151
|
-
Plain Pi is always a profile named `pi`: command `pi`,
|
|
150
|
+
Plain Pi is always a profile named `pi`: command `pi`, no setup, no `spaceEnv`. Its home is `PI_SPACES_BASE_HOME` (set inside a space), else `PI_CODING_AGENT_DIR`, else `~/.pi/agent`.
|
|
151
|
+
|
|
152
|
+
Inside another program's Pi those variables hold that program's home. When a profile of the file names that folder as its `home`, `pi` does not take it and uses `~/.pi/agent`, so `pi/<name>` is still plain Pi there.
|
|
153
|
+
|
|
154
|
+
Naming `pi` in `profiles` replaces it as a whole; what that entry leaves out still takes the defaults above for `home` and `command`, never anything from the old built-in's other fields.
|
|
152
155
|
|
|
153
156
|
### Schema versions
|
|
154
157
|
|
|
@@ -162,7 +165,7 @@ Unknown keys, wrong types, bad names, a `default` that names no profile, an unre
|
|
|
162
165
|
|
|
163
166
|
### Where spaces live
|
|
164
167
|
|
|
165
|
-
In a `spaces` folder next to the home Pi starts with: `~/.pi/agent` keeps its spaces in `~/.pi/spaces/<name>/`. A program that runs Pi with its own home gets its own folder too, for example `~/.tool/agent` → `~/.tool/spaces`. The folder stays the same while you switch between spaces. A [profile](#profiles)'s `dir` replaces it for that profile's home, and `PI_SPACES_DIR` replaces both.
|
|
168
|
+
In a `spaces` folder next to the home Pi starts with: `~/.pi/agent` keeps its spaces in `~/.pi/spaces/<name>/`. A program that runs Pi with its own home gets its own folder too, for example `~/.tool/agent` → `~/.tool/spaces`. The folder stays the same while you switch between spaces. A [profile](#profiles)'s `dir` replaces it for that profile's home, and `PI_SPACES_DIR` replaces both. The `pi-space` command does not read `PI_SPACES_DIR` (or `PI_SPACES_COMMAND` and `PI_SPACES_SETUP`) as a setting: it takes the folder from the profile or `--dir`, and sets the variable for the Pi it starts.
|
|
166
169
|
|
|
167
170
|
### Environment variables
|
|
168
171
|
|
|
@@ -172,8 +175,8 @@ Read:
|
|
|
172
175
|
| --- | --- |
|
|
173
176
|
| `PI_SPACES_DIR` | The spaces folder. A relative value is taken from your home folder. Default: the matching profile's `dir`, else `spaces` next to the base home. |
|
|
174
177
|
| `PI_SPACES_CONFIG` | The [config file](#profiles) with the profiles. A leading `~` is your home folder and a relative value is taken from it. Default: `~/.pi-spaces/config.json`. |
|
|
175
|
-
| `PI_SPACES_COMMAND` |
|
|
176
|
-
| `PI_SPACES_SETUP` |
|
|
178
|
+
| `PI_SPACES_COMMAND` | The program that starts Pi (default `pi`): told to the model and exported (see below). A switch starts Pi again with the command line it already has, so this is information, not a setting. |
|
|
179
|
+
| `PI_SPACES_SETUP` | The command that prepares a new space. `/space new` names `pi-space setup <profile>/<name>` when it is set. The `pi-space` command sets it for the Pi it starts, from the profile or `--setup`; it does not read it from your shell. |
|
|
177
180
|
| `PI_CODING_AGENT_DIR` | Pi's agent home; its parent holds the default spaces folder. Default: `~/.pi/agent`. |
|
|
178
181
|
|
|
179
182
|
Set for the Pi a switch starts:
|
|
@@ -191,7 +194,7 @@ Set in every Pi this extension runs in, even one started without a switch, for i
|
|
|
191
194
|
| --- | --- |
|
|
192
195
|
| `PI_SPACE` | The active space's name, unset in the base home: the same variable a switch sets. |
|
|
193
196
|
| `PI_SPACE_HOME` | The agent home this Pi runs with: the space's folder, or the base home. |
|
|
194
|
-
| `PI_SPACES_COMMAND` | Kept when it has a value; set to `pi` when it is unset or blank. |
|
|
197
|
+
| `PI_SPACES_COMMAND` | Kept when it has a value; set to `pi` when it is unset or blank. The `pi-space` command sets it for the Pi it starts (the profile's `command`, or `--command`), and the extension reads it to tell the model and the tools how Pi was started (see below); `pi-space` itself does not read it from your shell. |
|
|
195
198
|
|
|
196
199
|
### What the model is told
|
|
197
200
|
|
package/dist/command.js
CHANGED
|
@@ -58,11 +58,13 @@ export async function runSpaceCommand(args , deps )
|
|
|
58
58
|
try {
|
|
59
59
|
if (sub === "list" && rest.length === 0) return list(deps);
|
|
60
60
|
if (sub === "new" && rest.length === 1) {
|
|
61
|
-
const
|
|
62
|
-
const
|
|
61
|
+
const [name] = rest;
|
|
62
|
+
const path = createSpace(deps.dir, name, deps.declaration());
|
|
63
|
+
// pi-space names a space with its profile; without a matching profile this Pi is plain pi.
|
|
64
|
+
const qualified = `${deps.profile?.name ?? "pi"}/${name}`;
|
|
63
65
|
ctx.ui.notify(
|
|
64
66
|
hasSetupCommand(deps.env) || Boolean(deps.profile?.setup)
|
|
65
|
-
? `Created space "${name}" at ${path}. Prepare it from a shell with: pi-space setup ${
|
|
67
|
+
? `Created space "${name}" at ${path}. Prepare it from a shell with: pi-space setup ${qualified}. Then enter it with /space switch ${name}.`
|
|
66
68
|
: `Created space "${name}" at ${path}. Enter it with /space switch ${name}.`,
|
|
67
69
|
"info",
|
|
68
70
|
);
|
package/dist/config.js
CHANGED
|
@@ -170,15 +170,21 @@ export function profileNames(config ) {
|
|
|
170
170
|
return names.includes(BUILT_IN) ? names : [...names, BUILT_IN];
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
/**
|
|
174
|
-
|
|
173
|
+
/**
|
|
174
|
+
* The built-in pi home: the base home inside a space (PI_SPACES_BASE_HOME), else PI_CODING_AGENT_DIR, else ~/.pi/agent.
|
|
175
|
+
* A home that a profile of the file names is that profile's, not pi's: inside the Pi of another program those
|
|
176
|
+
* variables hold that program's home, so pi keeps ~/.pi/agent.
|
|
177
|
+
*/
|
|
178
|
+
function builtInHome(config , env , home ) {
|
|
175
179
|
const fromEnv = env.PI_SPACES_BASE_HOME || env.PI_CODING_AGENT_DIR;
|
|
176
|
-
|
|
180
|
+
const found = fromEnv ? resolve(home, expandTilde(fromEnv, home)) : undefined;
|
|
181
|
+
const claimed = (path ) => config?.profiles.some((spec) => spec.home !== undefined && samePath(resolve(home, expandTilde(spec.home, home)), path));
|
|
182
|
+
return found !== undefined && !claimed(found) ? found : join(home, ".pi", "agent");
|
|
177
183
|
}
|
|
178
184
|
|
|
179
185
|
/** Resolves a profile; what a profile omits for `home` falls back to the built-in pi's, and `pi` also gets its `command`. */
|
|
180
|
-
function resolveProfile(spec , env , home ) {
|
|
181
|
-
const base = spec.home === undefined ? builtInHome(env, home) : resolve(home, expandTilde(spec.home, home));
|
|
186
|
+
function resolveProfile(config , spec , env , home ) {
|
|
187
|
+
const base = spec.home === undefined ? builtInHome(config, env, home) : resolve(home, expandTilde(spec.home, home));
|
|
182
188
|
const profile = {
|
|
183
189
|
name: spec.name,
|
|
184
190
|
home: base,
|
|
@@ -195,7 +201,7 @@ function resolveProfile(spec , env , home ) {
|
|
|
195
201
|
export function profileByName(config , name , env , home ) {
|
|
196
202
|
const spec = config?.profiles.find((profile) => profile.name === name) ?? (name === BUILT_IN ? { name } : undefined);
|
|
197
203
|
if (spec === undefined) throw new ConfigError(`Unknown profile "${name}"; known profiles: ${profileNames(config).join(", ")}.`);
|
|
198
|
-
return resolveProfile(spec, env, home);
|
|
204
|
+
return resolveProfile(config, spec, env, home);
|
|
199
205
|
}
|
|
200
206
|
|
|
201
207
|
/** The profile whose home is the base home (the same place, real paths compared): the first in file order, then built-in `pi`. */
|
package/dist/context.js
CHANGED
|
@@ -20,7 +20,7 @@ export function spaceInfo(dir , agentHome , env ) {
|
|
|
20
20
|
return {
|
|
21
21
|
space: activeSpace(dir, agentHome),
|
|
22
22
|
home: resolve(agentHome),
|
|
23
|
-
// A blank value counts as unset, so the default is pi
|
|
23
|
+
// A blank value counts as unset, so the default is pi (pi-space leaves it unset for a profile without a command).
|
|
24
24
|
command: env.PI_SPACES_COMMAND?.trim() || "pi",
|
|
25
25
|
};
|
|
26
26
|
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ description: "Trigger: pi-spaces, space, spaces, /space, pi-space, pi --space, P
|
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: jals1212
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.3"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
## Activation Contract
|
|
@@ -14,10 +14,13 @@ Use when a task depends on which Pi agent home this session uses (settings, sign
|
|
|
14
14
|
## Hard Rules
|
|
15
15
|
|
|
16
16
|
- Read where you run from the `<pi_space>` system prompt section; in a shell, from `PI_SPACE` (unset in the base home), `PI_SPACE_HOME` (this Pi's agent home) and `PI_SPACES_COMMAND` (the program that starts Pi, default `pi`). Never guess from `~/.pi/agent`.
|
|
17
|
+
- The `pi-space` CLI names every space `<profile>/<name>` (`base` included, as `<profile>/base`); a bare name is refused. Nothing in the environment or the `<pi_space>` section names your own profile. To find it, take your base home (`PI_SPACES_BASE_HOME` when set, else `PI_SPACE_HOME`) and look in the config file for the profile whose `home` is that folder; no match means the built-in `pi`.
|
|
17
18
|
- Files of this session's home (`settings.json`, `extensions/`, `sessions/`, installed packages) live under `PI_SPACE_HOME`; `pi install` and settings changes apply to this space only.
|
|
19
|
+
- The config's `default` is not used by `pi-space` (it warns and ignores it); the profile is always named. `pi-space` does not read `PI_SPACES_COMMAND`, `PI_SPACES_DIR` or `PI_SPACES_SETUP` from the shell: a profile or `--command`, `--dir`, `--setup` (for one named profile) gives them.
|
|
18
20
|
- Profiles live in `~/.pi-spaces/config.json`, or the file in `PI_SPACES_CONFIG`; its `schema` must be `pi-spaces.config/v1`. Read it to answer questions about profiles; edit it only when the user asks, and keep every key to the documented ones (unknown keys make the file invalid and ignored).
|
|
19
21
|
- Never read `auth.json` or other credential files in any home.
|
|
20
|
-
- You cannot move this session to another space. Ask the user to run `/space switch <name>` (interactive terminal only; Pi restarts), or start another Pi with `$PI_SPACES_COMMAND --space <name
|
|
22
|
+
- You cannot move this session to another space. Ask the user to run `/space switch <name>` (interactive terminal only; Pi restarts), or start another Pi with `$PI_SPACES_COMMAND --space <name>` (the space name there is bare: `work`; `pi-space` takes `<profile>/work`).
|
|
23
|
+
- The Pi that `pi-space` starts runs as if started from a plain terminal for the named profile, even when you run it from inside another Pi or a space (it unwinds your space and sets `PI_CODING_AGENT_DIR` to that profile's home).
|
|
21
24
|
- Never set `PI_CODING_AGENT_DIR` yourself, and never create, rename or delete space folders with file commands; use `/space` or `pi-space`.
|
|
22
25
|
- Removing a space deletes everything in it: only on the user's explicit request, and keep the backup unless they say otherwise.
|
|
23
26
|
|
|
@@ -26,16 +29,18 @@ Use when a task depends on which Pi agent home this session uses (settings, sign
|
|
|
26
29
|
| Need | Action |
|
|
27
30
|
| --- | --- |
|
|
28
31
|
| Which space, home, command | `<pi_space>` section, or `echo "$PI_SPACE|$PI_SPACE_HOME|$PI_SPACES_COMMAND"` |
|
|
29
|
-
| List spaces from a shell | `pi-space list` (optional `@jals1212/pi-spaces-cli`) |
|
|
32
|
+
| List spaces from a shell | `pi-space list` (optional `@jals1212/pi-spaces-cli`): every profile, each space as `<profile>/<name>` |
|
|
30
33
|
| Spaces folder | `PI_SPACES_DIR`, else the `dir` of the profile whose `home` is the base home, else `spaces` next to the base home (`PI_SPACES_BASE_HOME`, else this home) |
|
|
31
34
|
| Profiles, or why a variable differs per space | The config file (`PI_SPACES_CONFIG`, else `~/.pi-spaces/config.json`): a profile's `spaceEnv` variables are set only inside spaces (`{home}` and `{name}` filled in) and removed in base; without `spaceEnv` whatever a program keeps outside its agent home is shared with base |
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
| Pick a space from a shell | bare `pi-space` on a terminal opens a picker of every profile's spaces (`<profile>/<name>`, `base` included) and starts Pi in the one chosen |
|
|
36
|
+
| Rename a space | User request only: `pi-space rename <profile>/<old> <new>`; the new name alone or with the same profile (`<profile>/<new>`), a space never changes profile; refused for base and for the space you are in |
|
|
37
|
+
| Create a space | `pi-space new <profile>/<name>`, or ask the user for `/space new <name>` (this Pi's profile); `pi-space new` also runs the profile's setup command when there is one (or `--setup <cmd>` for one call) |
|
|
38
|
+
| Prepare a space for the program that runs Pi | `pi-space setup <profile>/<name>` (or `<profile>/--every`, or `--every` for every profile with a setup): runs the setup command (`{home}` and `{name}` become the space's folder and name) with the space as `PI_CODING_AGENT_DIR`; never in base |
|
|
39
|
+
| Work in another space | User runs `/space switch <name>`, or `$PI_SPACES_COMMAND --space <name> "<prompt>"`, or `pi-space <profile>/<name>` from a shell |
|
|
40
|
+
| Remove a space | User request only: `pi-space remove <profile>/<name> --yes` keeps a backup in `.backups` |
|
|
41
|
+
| Packages of a space | `pi-space update <profile>/<name|base|--every> --extensions` (`--every` alone: every profile), `pi-space install <profile>/<name> <source>`, `pi-space uninstall <profile>/<name> <source>`, `pi-space list <profile>/<name>`; without the CLI, `$PI_SPACES_COMMAND --space <name|base> update --extensions` |
|
|
42
|
+
|
|
43
|
+
Names (the part after `<profile>/`; a profile name follows the same rule): lowercase letters, digits, `-` and `_`, starting with a letter or digit, at most 64 characters; `base` (the base home) and the command words (`list`, `new`, `remove`, `rename`, `switch`, `update`, `install`, `uninstall`, `setup`, `completion`) are reserved.
|
|
39
44
|
|
|
40
45
|
## Execution Steps
|
|
41
46
|
|
package/src/command.ts
CHANGED
|
@@ -58,11 +58,13 @@ export async function runSpaceCommand(args: string, deps: SpaceCommandDeps): Pro
|
|
|
58
58
|
try {
|
|
59
59
|
if (sub === "list" && rest.length === 0) return list(deps);
|
|
60
60
|
if (sub === "new" && rest.length === 1) {
|
|
61
|
-
const
|
|
62
|
-
const
|
|
61
|
+
const [name] = rest;
|
|
62
|
+
const path = createSpace(deps.dir, name, deps.declaration());
|
|
63
|
+
// pi-space names a space with its profile; without a matching profile this Pi is plain pi.
|
|
64
|
+
const qualified = `${deps.profile?.name ?? "pi"}/${name}`;
|
|
63
65
|
ctx.ui.notify(
|
|
64
66
|
hasSetupCommand(deps.env) || Boolean(deps.profile?.setup)
|
|
65
|
-
? `Created space "${name}" at ${path}. Prepare it from a shell with: pi-space setup ${
|
|
67
|
+
? `Created space "${name}" at ${path}. Prepare it from a shell with: pi-space setup ${qualified}. Then enter it with /space switch ${name}.`
|
|
66
68
|
: `Created space "${name}" at ${path}. Enter it with /space switch ${name}.`,
|
|
67
69
|
"info",
|
|
68
70
|
);
|
package/src/config.ts
CHANGED
|
@@ -170,15 +170,21 @@ export function profileNames(config: Config | undefined): string[] {
|
|
|
170
170
|
return names.includes(BUILT_IN) ? names : [...names, BUILT_IN];
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
/**
|
|
174
|
-
|
|
173
|
+
/**
|
|
174
|
+
* The built-in pi home: the base home inside a space (PI_SPACES_BASE_HOME), else PI_CODING_AGENT_DIR, else ~/.pi/agent.
|
|
175
|
+
* A home that a profile of the file names is that profile's, not pi's: inside the Pi of another program those
|
|
176
|
+
* variables hold that program's home, so pi keeps ~/.pi/agent.
|
|
177
|
+
*/
|
|
178
|
+
function builtInHome(config: Config | undefined, env: Env, home: string): string {
|
|
175
179
|
const fromEnv = env.PI_SPACES_BASE_HOME || env.PI_CODING_AGENT_DIR;
|
|
176
|
-
|
|
180
|
+
const found = fromEnv ? resolve(home, expandTilde(fromEnv, home)) : undefined;
|
|
181
|
+
const claimed = (path: string) => config?.profiles.some((spec) => spec.home !== undefined && samePath(resolve(home, expandTilde(spec.home, home)), path));
|
|
182
|
+
return found !== undefined && !claimed(found) ? found : join(home, ".pi", "agent");
|
|
177
183
|
}
|
|
178
184
|
|
|
179
185
|
/** Resolves a profile; what a profile omits for `home` falls back to the built-in pi's, and `pi` also gets its `command`. */
|
|
180
|
-
function resolveProfile(spec: ProfileSpec, env: Env, home: string): Profile {
|
|
181
|
-
const base = spec.home === undefined ? builtInHome(env, home) : resolve(home, expandTilde(spec.home, home));
|
|
186
|
+
function resolveProfile(config: Config | undefined, spec: ProfileSpec, env: Env, home: string): Profile {
|
|
187
|
+
const base = spec.home === undefined ? builtInHome(config, env, home) : resolve(home, expandTilde(spec.home, home));
|
|
182
188
|
const profile: Profile = {
|
|
183
189
|
name: spec.name,
|
|
184
190
|
home: base,
|
|
@@ -195,7 +201,7 @@ function resolveProfile(spec: ProfileSpec, env: Env, home: string): Profile {
|
|
|
195
201
|
export function profileByName(config: Config | undefined, name: string, env: Env, home: string): Profile {
|
|
196
202
|
const spec = config?.profiles.find((profile) => profile.name === name) ?? (name === BUILT_IN ? { name } : undefined);
|
|
197
203
|
if (spec === undefined) throw new ConfigError(`Unknown profile "${name}"; known profiles: ${profileNames(config).join(", ")}.`);
|
|
198
|
-
return resolveProfile(spec, env, home);
|
|
204
|
+
return resolveProfile(config, spec, env, home);
|
|
199
205
|
}
|
|
200
206
|
|
|
201
207
|
/** The profile whose home is the base home (the same place, real paths compared): the first in file order, then built-in `pi`. */
|
package/src/context.ts
CHANGED
|
@@ -20,7 +20,7 @@ export function spaceInfo(dir: string, agentHome: string, env: Env): SpaceInfo {
|
|
|
20
20
|
return {
|
|
21
21
|
space: activeSpace(dir, agentHome),
|
|
22
22
|
home: resolve(agentHome),
|
|
23
|
-
// A blank value counts as unset, so the default is pi
|
|
23
|
+
// A blank value counts as unset, so the default is pi (pi-space leaves it unset for a profile without a command).
|
|
24
24
|
command: env.PI_SPACES_COMMAND?.trim() || "pi",
|
|
25
25
|
};
|
|
26
26
|
}
|