localpi 0.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.
@@ -0,0 +1,129 @@
1
+ ---
2
+ title: Startup Model And Thinking Control Plan
3
+ author: Bob <dutifulbob@gmail.com>
4
+ date: 2026-06-16
5
+ ---
6
+
7
+ # Startup Model And Thinking Control Plan
8
+
9
+ This plan covers the localpi UX for choosing models and controlling thinking without turning localpi into a second interactive app.
10
+
11
+ ## Goal
12
+
13
+ Plain `localpi` should make model choice easy when multiple local models are available, while thinking stays controlled by Pi after startup.
14
+
15
+ Startup selection is for models only. There is no startup thinking picker.
16
+
17
+ ## Target Behavior
18
+
19
+ - `localpi` discovers all usable local providers before Pi starts.
20
+ - If exactly one usable model is available, localpi starts Pi with that model.
21
+ - If multiple usable models are available in an interactive terminal, localpi launches Pi with a bootstrap model and Pi opens its native model selector at startup.
22
+ - If no external model is available, localpi may fall back to a managed `llama-server` model.
23
+ - A concrete `--model` value bypasses the startup model picker. `--provider` alone only scopes the catalog.
24
+ - Explicit `--runtime` values scope discovery but do not disable the startup selector by themselves.
25
+ - Non-interactive runs never show a picker.
26
+ - Pi receives the launch-time model catalog so `/model` can switch across discovered providers and models.
27
+ - Thinking starts as `off` unless `--thinking` or `LOCALPI_THINKING` sets another startup level.
28
+ - In-session thinking changes happen through `/thinking` inside Pi.
29
+
30
+ ## Provider Coverage
31
+
32
+ The startup model catalog should generalize across all local providers that localpi knows how to describe:
33
+
34
+ - LM Studio: externally managed OpenAI-compatible provider.
35
+ - vLLM: externally managed OpenAI-compatible provider.
36
+ - Generic OpenAI-compatible providers from localpi config.
37
+ - Managed `llama-server`: localpi-owned, startable GGUF aliases and the currently served model.
38
+
39
+ Localpi should not start or stop externally managed providers. It should only manage localpi-owned `llama-server` processes.
40
+
41
+ ## Startup Model Selection
42
+
43
+ Selection happens after provider discovery and before Pi config generation.
44
+
45
+ Interactive behavior:
46
+
47
+ - If no explicit model was requested and more than one usable model exists, select a deterministic bootstrap model only so Pi can start.
48
+ - In the same launch, generate a startup extension that opens Pi's native `ModelSelectorComponent`.
49
+ - Label options with provider and model, for example `LM Studio / qwen3.6-35b-a3b-mtp`.
50
+ - Prefer a stable ordering so repeated launches are predictable.
51
+ - Let Enter choose the first ranked model.
52
+
53
+ Non-interactive behavior:
54
+
55
+ - Do not prompt.
56
+ - Use a deterministic default when one exists.
57
+ - If multiple loaded models match, use the first deterministic bootstrap model and let automation pin another model with `--provider` and `--model` when needed.
58
+
59
+ Explicit selection behavior:
60
+
61
+ - `--provider <id> --model <id>` selects an exact catalog entry.
62
+ - `--provider <id>` without `--model` scopes startup discovery and still opens the Pi-native picker when multiple loaded models match.
63
+ - `--runtime lmstudio` and `--runtime vllm` select the built-in external provider.
64
+ - Managed `llama-server` aliases continue to work through `--model <alias>`.
65
+
66
+ ## Pi Model Switching
67
+
68
+ Localpi should pass the full launch-time catalog to Pi instead of creating a localpi-only `/model` extension.
69
+
70
+ Generated Pi config should include:
71
+
72
+ - one provider entry per discovered provider
73
+ - every discovered usable model under its provider
74
+ - selected provider and selected model in `settings.json`
75
+ - context window and max token metadata when known
76
+ - reasoning metadata when known
77
+ - provider compatibility metadata when needed, such as Qwen or DeepSeek thinking format
78
+
79
+ Pi owns the `/model` command and the native startup model selector UI. Localpi's job is to give Pi a complete model catalog at launch and request that native selector when startup model choice is needed.
80
+
81
+ Live model refresh after Pi starts is out of scope for this plan. That should be a future Pi/provider integration.
82
+
83
+ ## Thinking Control
84
+
85
+ Thinking is not selected at startup through a picker.
86
+
87
+ Startup defaults:
88
+
89
+ - `localpi` starts with thinking `off`.
90
+ - `LOCALPI_THINKING=<level>` changes the startup default.
91
+ - `localpi --thinking <level>` overrides the startup default.
92
+ - The chosen startup value is passed to Pi as `--thinking <level>` and written to `settings.json.defaultThinkingLevel`.
93
+
94
+ In-session control:
95
+
96
+ - Localpi provides a Pi extension command named `/thinking`.
97
+ - `/thinking` opens Pi's selector UI.
98
+ - `/thinking high` sets the level directly.
99
+ - The extension calls Pi's thinking API, so Pi owns runtime mutation.
100
+
101
+ Managed `llama-server` caveat:
102
+
103
+ - Server-side reasoning budget is still chosen at startup.
104
+ - Changing the server-side budget later would require restarting the local server process.
105
+
106
+ ## Implementation Checklist
107
+
108
+ - [x] Discover a normalized launch-time model catalog across providers.
109
+ - [x] Add startup model selection when multiple models are available interactively.
110
+ - [x] Use Pi's native model selector UI at startup instead of a localpi-owned terminal prompt.
111
+ - [x] Pass the launch-time catalog into generated Pi model config.
112
+ - [x] Keep `/model` owned by Pi.
113
+ - [x] Add reasoning and thinking-format metadata for known local reasoning models.
114
+ - [x] Add `/thinking` as a Pi extension command.
115
+ - [x] Keep startup thinking non-interactive.
116
+ - [x] Keep `--thinking` and `LOCALPI_THINKING` as automation-safe startup controls.
117
+ - [ ] Manually verify model picker behavior in an interactive terminal with multiple loaded providers.
118
+ - [ ] Manually verify Pi `/model` can switch among generated catalog entries.
119
+ - [ ] Manually verify Pi `/thinking` picker and direct `/thinking <level>` command.
120
+
121
+ ## Out Of Scope
122
+
123
+ - A startup thinking selector.
124
+ - A localpi-owned `/model` extension.
125
+ - Live refresh of models after Pi starts.
126
+ - Starting or stopping LM Studio, vLLM, or other externally managed providers.
127
+ - Guessing random provider ports.
128
+ - Cloud provider authentication.
129
+ - Caller-specific workflows such as classifier routing or schema-constrained output.
@@ -0,0 +1,75 @@
1
+ # Implementation Plan
2
+
3
+ This plan tracks the migration from `localagent` to `localpi` and changes the product from a generic OpenAI-compatible wrapper into a polished local Pi launcher.
4
+
5
+ ## 1. Rename The Public Surface
6
+
7
+ - [x] Rename package metadata from `@dutifuldev/localagent` to `@dutifuldev/localpi`.
8
+ - [x] Rename the installed binary from `localagent` to `localpi`.
9
+ - [x] Rename source namespaces from `localagent` to `localpi`.
10
+ - [x] Rename default state from `~/.local/state/localagent` to `~/.local/state/localpi`.
11
+ - [x] Replace `LOCALAGENT_*` environment variables with `LOCALPI_*`.
12
+ - [x] Do not keep a `localagent` compatibility shim.
13
+
14
+ ## 2. Remove Structured Output
15
+
16
+ - [x] Remove `--final-schema` and `--schema` from option parsing.
17
+ - [x] Remove `LOCALAGENT_FINAL_SCHEMA`.
18
+ - [x] Delete `src/structured/final-schema.ts`.
19
+ - [x] Delete structured-output tests and example schemas.
20
+ - [x] Keep a migration note that schema-constrained classifier runs belong in `localpager-agent`.
21
+ - [x] Confirm the current localpager classifier uses `localpager-agent --final-schema`, not localpi.
22
+
23
+ Older workspace wrappers outside this repository still mention `localagent --final-schema`; those should be migrated separately if they are still used.
24
+
25
+ ## 3. Add Runtime Backends
26
+
27
+ - [x] Add a runtime option with default `llama-server`.
28
+ - [x] Implement a managed `llama-server` backend:
29
+ - [x] model alias resolution
30
+ - [x] custom GGUF path support
31
+ - [x] context window configuration
32
+ - [x] chat template file support
33
+ - [x] pid file and metadata file under localpi state
34
+ - [x] start, reuse, status, and stop
35
+ - [x] Implement explicit `lmstudio` backend:
36
+ - [x] default base URL `http://127.0.0.1:1234/v1`
37
+ - [x] model probing through `/v1/models`
38
+ - [x] clear failures when LM Studio is not running or the model is not loaded
39
+ - [x] Keep a generic `openai-compatible` backend for externally managed servers.
40
+
41
+ ## 4. Add Default Pi Extensions
42
+
43
+ - [x] Add a tool approval extension.
44
+ - [x] Add a token status extension.
45
+ - [x] Generate both extensions under localpi state.
46
+ - [x] Pass them to Pi by default.
47
+ - [x] Add `--no-approval` for trusted sessions.
48
+ - [x] Add `--no-token-status` if the status UI causes problems in print or non-interactive mode.
49
+
50
+ ## 5. Add Default Tooling
51
+
52
+ - [x] Default Pi tool allow list: `read,bash,edit,write,grep,find,ls`.
53
+ - [x] Allow override with `--tools`.
54
+ - [x] Preserve `--` forwarding for raw Pi flags.
55
+ - [x] Keep the system prompt short and generic.
56
+
57
+ ## 6. Memory Safety
58
+
59
+ - [x] Only manage localpi-owned `llama-server` processes.
60
+ - [x] Stop the previous localpi-owned server before starting a different managed model.
61
+ - [x] In `llama-server` mode, detect loaded LM Studio models where possible and warn before starting a large model.
62
+ - [x] Never silently start both LM Studio and managed `llama-server` for the same localpi command.
63
+
64
+ ## 7. Verification
65
+
66
+ - Unit-test option parsing, runtime selection, Pi launch planning, and server lifecycle decisions.
67
+ - Smoke-test:
68
+ - `localpi --list`
69
+ - `localpi --status`
70
+ - `localpi --model gemma-e4b -p "say ok"`
71
+ - `localpi --model gemma-12b`
72
+ - `localpi --runtime lmstudio --model gemma-4-e4b-it -p "say ok"`
73
+ - approval denial in an interactive tool call
74
+ - token status display in an interactive session
75
+ - Run `npm run check` before merging implementation changes.
@@ -0,0 +1,148 @@
1
+ # Runtime Specification
2
+
3
+ Localpi is the local runtime launcher for Pi.
4
+
5
+ It should make the common local-model path one command while keeping the selected provider and model explicit and inspectable.
6
+
7
+ ## Goals
8
+
9
+ - Run Pi against local open-weight models without hand-editing Pi config.
10
+ - Discover local providers by default and select from the loaded model catalog.
11
+ - Support LM Studio and vLLM as built-in OpenAI-compatible providers.
12
+ - Keep managed `llama-server` as the fallback when no external model is loaded.
13
+ - Keep the tool generic: no classifier prompts, topic schemas, dataset generation, or final-schema output.
14
+ - Keep large model memory usage predictable by managing only one localpi-owned `llama-server` process at a time.
15
+
16
+ ## Runtimes
17
+
18
+ ### `auto`
19
+
20
+ Default runtime.
21
+
22
+ Localpi:
23
+
24
+ - probes built-in LM Studio and vLLM endpoints
25
+ - loads configured OpenAI-compatible providers from `--providers-file`, `LOCALPI_PROVIDERS_FILE`, or `LOCALPI_MODELS_FILE`
26
+ - includes the localpi-owned `llama-server` catalog as startable fallback entries
27
+ - selects the only loaded model automatically
28
+ - opens Pi's native model selector when multiple loaded models are available in an interactive TTY
29
+ - never prompts in non-interactive runs; automation can pin a model with concrete `--provider` and `--model` values
30
+ - treats `--provider` without `--model` as catalog scoping, not as a concrete model choice
31
+ - writes Pi config for all launch-time loaded catalog entries so Pi `/model` can switch among them
32
+
33
+ ### `llama-server`
34
+
35
+ Managed runtime.
36
+
37
+ Localpi:
38
+
39
+ - resolves a model alias or GGUF path
40
+ - starts `llama-server` if the selected model is not already served
41
+ - reuses an existing server on the configured port if it is already serving the requested model
42
+ - exposes the server through an OpenAI-compatible `/v1` endpoint
43
+ - writes Pi config that points at that endpoint
44
+ - stops the old localpi-owned server before starting a different managed model
45
+ - reports any detected LM Studio loaded models before starting a large managed model
46
+
47
+ ### LM Studio
48
+
49
+ Built-in external OpenAI-compatible provider.
50
+
51
+ Localpi:
52
+
53
+ - requires `--runtime lmstudio`
54
+ - defaults to `http://127.0.0.1:1234/v1`
55
+ - does not start or stop LM Studio
56
+ - probes `/v1/models` and fails clearly if the requested model is not available
57
+
58
+ ### vLLM
59
+
60
+ Built-in external OpenAI-compatible provider.
61
+
62
+ Localpi:
63
+
64
+ - requires `--runtime vllm`
65
+ - defaults to `http://127.0.0.1:8000/v1`
66
+ - does not start or stop vLLM
67
+ - probes `/v1/models` and fails clearly if the requested model is not available
68
+
69
+ ### Custom OpenAI-Compatible Endpoint
70
+
71
+ Explicit alternate runtime.
72
+
73
+ Localpi:
74
+
75
+ - requires `--runtime openai-compatible`
76
+ - requires `--base-url`
77
+ - can use `--provider <id>` to name the generated Pi provider
78
+ - uses `/v1/models` for discovery
79
+ - avoids assuming it can start, stop, or unload the backend
80
+
81
+ ### Configured Providers
82
+
83
+ Provider registry JSON can define additional OpenAI-compatible providers:
84
+
85
+ ```json
86
+ {
87
+ "providers": {
88
+ "vllm-qwen": {
89
+ "type": "openai-compatible",
90
+ "name": "vLLM Qwen",
91
+ "baseUrl": "http://127.0.0.1:8000/v1",
92
+ "discover": true
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ Set `discover: false` when the endpoint should not be probed during startup. Explicit `--provider <id> --model <id>` can still select that provider and generate Pi config.
99
+
100
+ ## Model Selection
101
+
102
+ `--model` should accept:
103
+
104
+ - a configured alias such as `gemma-12b` or `gemma-e4b`
105
+ - an LM Studio model id
106
+ - a vLLM model id
107
+ - an absolute or relative GGUF path for `llama-server`
108
+ - `auto`, which selects the first model reported by the backend
109
+
110
+ Model aliases are configurable with `LOCALPI_MODELS_FILE`. The built-in defaults cover the local Gemma GGUF paths commonly used on this machine and are easy to override.
111
+
112
+ ## Pi Defaults
113
+
114
+ Localpi passes these defaults to Pi unless the user overrides them:
115
+
116
+ ```text
117
+ tools: read,bash,edit,write,grep,find,ls
118
+ thinking: off
119
+ state dir: ~/.local/state/localpi
120
+ session dir: ~/.local/state/localpi/sessions
121
+ ```
122
+
123
+ Localpi installs two default extensions:
124
+
125
+ - tool approval gate: ask before each tool call, and tell the model clearly when a tool call was blocked
126
+ - token status: show live output token estimate while streaming and final exact token stats when usage data is available
127
+
128
+ ## System Prompt
129
+
130
+ Localpi appends a short system prompt that tells the model:
131
+
132
+ - it is running through Pi on a local model
133
+ - tool calls require user approval
134
+ - blocked tool calls did not run
135
+ - it should not claim to have used a blocked tool
136
+ - it should prefer direct answers when tools are not needed
137
+
138
+ The prompt should be generic and should not mention localpager, OpenClaw, datasets, or classifier labels.
139
+
140
+ ## Out Of Scope
141
+
142
+ - `--final-schema`
143
+ - `final_json`
144
+ - JSON schema validation for final answers
145
+ - classifier retry policy
146
+ - GitHub issue or pull request fetching
147
+ - reposhell-specific behavior
148
+ - dataset generation
@@ -0,0 +1,9 @@
1
+ # Structured Output
2
+
3
+ Structured final output is out of scope for localpi.
4
+
5
+ Localpi focuses on launching Pi against local models with good runtime defaults: managed `llama-server`, optional LM Studio support, default tools, tool approval, and token status.
6
+
7
+ Schema-constrained classifier workflows belong in caller-specific tools such as `localpager-agent`, where the prompt, schema, retries, and output validation belong to the application workflow.
8
+
9
+ The previous localagent `--final-schema` / `--schema` behavior was removed during the localpi rename implementation.
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "localpi",
3
+ "version": "0.1.0",
4
+ "description": "Pi-compatible local model launcher with managed llama-server support.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/dutifuldev/localpi.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/dutifuldev/localpi/issues"
13
+ },
14
+ "homepage": "https://github.com/dutifuldev/localpi#readme",
15
+ "bin": {
16
+ "localpi": "dist/src/cli/main.js"
17
+ },
18
+ "files": [
19
+ "dist/src/**",
20
+ "docs/**",
21
+ "examples/**",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsc -p tsconfig.json",
27
+ "format": "prettier --check .",
28
+ "lint": "eslint .",
29
+ "typecheck": "tsc --noEmit",
30
+ "test": "vitest run",
31
+ "coverage": "vitest run --coverage --coverage.thresholds.lines 85 --coverage.thresholds.functions 85 --coverage.thresholds.branches 85 --coverage.thresholds.statements 85",
32
+ "dry": "slophammer-ts dry .",
33
+ "mutate": "stryker run",
34
+ "mutate:scan": "stryker run --dryRunOnly",
35
+ "slophammer": "slophammer-ts check .",
36
+ "localpi": "tsx src/cli/main.ts",
37
+ "check": "npm run format && npm run lint && npm run typecheck && npm test && npm run build"
38
+ },
39
+ "devDependencies": {
40
+ "@eslint/js": "^9.0.0",
41
+ "@stryker-mutator/core": "^9.6.1",
42
+ "@stryker-mutator/typescript-checker": "^9.6.1",
43
+ "@stryker-mutator/vitest-runner": "^9.6.1",
44
+ "@types/node": "^22.0.0",
45
+ "@vitest/coverage-v8": "^3.0.0",
46
+ "eslint": "^9.0.0",
47
+ "prettier": "^3.0.0",
48
+ "slophammer-ts": "^0.3.0",
49
+ "tsx": "^4.19.4",
50
+ "typescript": "^5.0.0",
51
+ "typescript-eslint": "^8.0.0",
52
+ "vitest": "^3.0.0"
53
+ }
54
+ }