ultimate-pi 0.2.3 → 0.2.4
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/.codex/hooks.json +15 -0
- package/.pi/extensions/custom-header.ts +26 -2
- package/.pi/extensions/lib/harness-paths.ts +47 -0
- package/.pi/extensions/model-router-bootstrap.ts +174 -0
- package/.pi/extensions/sentrux-rules-sync.ts +33 -2
- package/.pi/harness/browser.json +1 -0
- package/.pi/model-router.example.json +27 -0
- package/.pi/prompts/graphify.md +4 -8
- package/.pi/prompts/harness-setup.md +143 -92
- package/.pi/settings.json +0 -2
- package/.sentrux/.harness-rules-meta.json +1 -1
- package/AGENTS.md +12 -0
- package/CHANGELOG.md +13 -0
- package/README.md +39 -350
- package/package.json +4 -2
- package/scripts/harness-cli-verify.sh +294 -0
- package/scripts/harness-graphify-bootstrap.sh +151 -0
- package/.pi/model-router.json +0 -95
|
@@ -6,12 +6,19 @@
|
|
|
6
6
|
* doubling vertical resolution in the same terminal footprint.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { join } from "node:path";
|
|
10
9
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
11
10
|
import { truncateToWidth } from "@mariozechner/pi-tui";
|
|
12
11
|
import * as JimpModule from "jimp";
|
|
12
|
+
import { resolveHarnessAsset } from "./lib/harness-paths.js";
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/** Shipped next to this extension in the npm package — not the host project's .pi dir. */
|
|
15
|
+
const imagePath = resolveHarnessAsset(
|
|
16
|
+
// @ts-expect-error pi extensions run as ESM
|
|
17
|
+
import.meta.url,
|
|
18
|
+
".pi",
|
|
19
|
+
"extensions",
|
|
20
|
+
"banner.png",
|
|
21
|
+
);
|
|
15
22
|
|
|
16
23
|
// Terminal footprint — keep a safety margin so we never crash on narrow terminals
|
|
17
24
|
const SAFETY_MARGIN = 2;
|
|
@@ -83,6 +90,23 @@ function ansiCell(
|
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
async function loadBanner(): Promise<string[]> {
|
|
93
|
+
// #region agent log
|
|
94
|
+
fetch("http://127.0.0.1:7928/ingest/a5d40896-34cb-4f12-97db-df7ada0b22f0", {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: {
|
|
97
|
+
"Content-Type": "application/json",
|
|
98
|
+
"X-Debug-Session-Id": "7737a8",
|
|
99
|
+
},
|
|
100
|
+
body: JSON.stringify({
|
|
101
|
+
sessionId: "7737a8",
|
|
102
|
+
hypothesisId: "B",
|
|
103
|
+
location: "custom-header.ts:loadBanner",
|
|
104
|
+
message: "banner path",
|
|
105
|
+
data: { imagePath, cwd: process.cwd() },
|
|
106
|
+
timestamp: Date.now(),
|
|
107
|
+
}),
|
|
108
|
+
}).catch(() => {});
|
|
109
|
+
// #endregion
|
|
86
110
|
const Jimp = getJimpRuntime();
|
|
87
111
|
const image = await Jimp.read(imagePath);
|
|
88
112
|
resizeImageCompat(image, PIXEL_WIDTH, PIXEL_HEIGHT);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const rootByModuleUrl = new Map<string, string>();
|
|
6
|
+
|
|
7
|
+
/** Resolve ultimate-pi package root from the calling extension module URL. */
|
|
8
|
+
export function getHarnessPackageRoot(moduleUrl: string): string {
|
|
9
|
+
const cached = rootByModuleUrl.get(moduleUrl);
|
|
10
|
+
if (cached) {
|
|
11
|
+
return cached;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let dir = dirname(fileURLToPath(moduleUrl));
|
|
15
|
+
for (let depth = 0; depth < 8; depth++) {
|
|
16
|
+
const pkgPath = join(dir, "package.json");
|
|
17
|
+
if (existsSync(pkgPath)) {
|
|
18
|
+
try {
|
|
19
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as {
|
|
20
|
+
name?: string;
|
|
21
|
+
};
|
|
22
|
+
if (pkg.name === "ultimate-pi") {
|
|
23
|
+
rootByModuleUrl.set(moduleUrl, dir);
|
|
24
|
+
return dir;
|
|
25
|
+
}
|
|
26
|
+
} catch {
|
|
27
|
+
/* try parent */
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const parent = dirname(dir);
|
|
31
|
+
if (parent === dir) {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
dir = parent;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const fallback = join(dirname(fileURLToPath(moduleUrl)), "..", "..");
|
|
38
|
+
rootByModuleUrl.set(moduleUrl, fallback);
|
|
39
|
+
return fallback;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveHarnessAsset(
|
|
43
|
+
moduleUrl: string,
|
|
44
|
+
...segments: string[]
|
|
45
|
+
): string {
|
|
46
|
+
return join(getHarnessPackageRoot(moduleUrl), ...segments);
|
|
47
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensures .pi/model-router.json exists before pi-model-router reads config at
|
|
3
|
+
* extension init (which otherwise falls back to openai/gpt-5.4-pro).
|
|
4
|
+
*
|
|
5
|
+
* Runs synchronously in the extension factory so dotenv-loader can run first
|
|
6
|
+
* (alphabetically: dotenv-loader < model-router-bootstrap < sentrux / router pkg).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
12
|
+
|
|
13
|
+
const ROUTER_PATH = ".pi/model-router.json";
|
|
14
|
+
|
|
15
|
+
function model(prefix: string, name: string): string {
|
|
16
|
+
return `${prefix}/${name}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function buildRouterConfig(): Record<string, unknown> | null {
|
|
20
|
+
const hasOpenCode = process.env.OPENAI_API_BASE?.includes("opencode.ai");
|
|
21
|
+
const hasOpenAI = !!process.env.OPENAI_API_KEY;
|
|
22
|
+
const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
|
|
23
|
+
const hasGoogle = !!process.env.GOOGLE_API_KEY;
|
|
24
|
+
|
|
25
|
+
if (!hasOpenCode && !hasOpenAI && !hasAnthropic && !hasGoogle) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const highModel = hasOpenCode
|
|
30
|
+
? model("opencode-go", "deepseek-v4-pro")
|
|
31
|
+
: hasAnthropic
|
|
32
|
+
? "anthropic/claude-sonnet-4-20250514"
|
|
33
|
+
: hasGoogle
|
|
34
|
+
? "google/gemini-2.5-flash-001"
|
|
35
|
+
: hasOpenAI
|
|
36
|
+
? model("openai", "gpt-4o")
|
|
37
|
+
: null;
|
|
38
|
+
|
|
39
|
+
const mediumModel = hasOpenCode
|
|
40
|
+
? model("opencode-go", "qwen3.6-plus")
|
|
41
|
+
: hasAnthropic
|
|
42
|
+
? "anthropic/claude-sonnet-4-20250514"
|
|
43
|
+
: hasGoogle
|
|
44
|
+
? "google/gemini-flash-latest"
|
|
45
|
+
: hasOpenAI
|
|
46
|
+
? model("openai", "gpt-4o-mini")
|
|
47
|
+
: null;
|
|
48
|
+
|
|
49
|
+
const lowModel = hasOpenCode
|
|
50
|
+
? model("opencode-go", "deepseek-v4-flash")
|
|
51
|
+
: hasAnthropic
|
|
52
|
+
? "anthropic/claude-3-5-haiku-20241022"
|
|
53
|
+
: hasGoogle
|
|
54
|
+
? "google/gemini-flash-lite-latest"
|
|
55
|
+
: hasOpenAI
|
|
56
|
+
? model("openai", "gpt-4o-mini")
|
|
57
|
+
: null;
|
|
58
|
+
|
|
59
|
+
if (!highModel || !mediumModel || !lowModel) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const fallbacks: string[] = [];
|
|
64
|
+
if (hasAnthropic && !highModel.startsWith("anthropic/")) {
|
|
65
|
+
fallbacks.push("anthropic/claude-sonnet-4-20250514");
|
|
66
|
+
}
|
|
67
|
+
if (hasGoogle && !highModel.startsWith("google/")) {
|
|
68
|
+
fallbacks.push("google/gemini-flash-latest");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
defaultProfile: "auto",
|
|
73
|
+
debug: false,
|
|
74
|
+
classifierModel: mediumModel,
|
|
75
|
+
phaseBias: 0.5,
|
|
76
|
+
maxSessionBudget: 1.0,
|
|
77
|
+
largeContextThreshold: 100000,
|
|
78
|
+
rules: [
|
|
79
|
+
{
|
|
80
|
+
matches: ["deploy", "production", "release"],
|
|
81
|
+
tier: "high",
|
|
82
|
+
reason: "Safety check for production tasks",
|
|
83
|
+
},
|
|
84
|
+
{ matches: "changelog", tier: "low" },
|
|
85
|
+
],
|
|
86
|
+
profiles: {
|
|
87
|
+
auto: {
|
|
88
|
+
high: { model: highModel, thinking: "high", fallbacks },
|
|
89
|
+
medium: { model: mediumModel, thinking: "medium" },
|
|
90
|
+
low: { model: lowModel, thinking: "low" },
|
|
91
|
+
},
|
|
92
|
+
cheap: {
|
|
93
|
+
high: { model: mediumModel, thinking: "low" },
|
|
94
|
+
medium: { model: lowModel, thinking: "off" },
|
|
95
|
+
low: { model: lowModel, thinking: "off" },
|
|
96
|
+
},
|
|
97
|
+
deep: {
|
|
98
|
+
high: { model: highModel, thinking: "xhigh", fallbacks },
|
|
99
|
+
medium: { model: mediumModel, thinking: "medium" },
|
|
100
|
+
low: { model: lowModel, thinking: "low" },
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function ensureModelRouterConfig(cwd: string): boolean {
|
|
107
|
+
const projectPath = join(cwd, ROUTER_PATH);
|
|
108
|
+
// #region agent log
|
|
109
|
+
fetch("http://127.0.0.1:7928/ingest/a5d40896-34cb-4f12-97db-df7ada0b22f0", {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers: {
|
|
112
|
+
"Content-Type": "application/json",
|
|
113
|
+
"X-Debug-Session-Id": "7737a8",
|
|
114
|
+
},
|
|
115
|
+
body: JSON.stringify({
|
|
116
|
+
sessionId: "7737a8",
|
|
117
|
+
hypothesisId: "A",
|
|
118
|
+
location: "model-router-bootstrap.ts:ensure",
|
|
119
|
+
message: "router bootstrap check",
|
|
120
|
+
data: {
|
|
121
|
+
projectPath,
|
|
122
|
+
exists: existsSync(projectPath),
|
|
123
|
+
hasOpenCode: !!process.env.OPENAI_API_BASE?.includes("opencode.ai"),
|
|
124
|
+
hasOpenAI: !!process.env.OPENAI_API_KEY,
|
|
125
|
+
},
|
|
126
|
+
timestamp: Date.now(),
|
|
127
|
+
}),
|
|
128
|
+
}).catch(() => {});
|
|
129
|
+
// #endregion
|
|
130
|
+
|
|
131
|
+
if (existsSync(projectPath)) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const config = buildRouterConfig();
|
|
136
|
+
if (!config) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
mkdirSync(join(cwd, ".pi"), { recursive: true });
|
|
141
|
+
writeFileSync(projectPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
142
|
+
|
|
143
|
+
// #region agent log
|
|
144
|
+
fetch("http://127.0.0.1:7928/ingest/a5d40896-34cb-4f12-97db-df7ada0b22f0", {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: {
|
|
147
|
+
"Content-Type": "application/json",
|
|
148
|
+
"X-Debug-Session-Id": "7737a8",
|
|
149
|
+
},
|
|
150
|
+
body: JSON.stringify({
|
|
151
|
+
sessionId: "7737a8",
|
|
152
|
+
hypothesisId: "A",
|
|
153
|
+
location: "model-router-bootstrap.ts:write",
|
|
154
|
+
message: "wrote model-router.json",
|
|
155
|
+
data: {
|
|
156
|
+
high: (config.profiles as { auto: { high: { model: string } } }).auto
|
|
157
|
+
.high.model,
|
|
158
|
+
},
|
|
159
|
+
timestamp: Date.now(),
|
|
160
|
+
}),
|
|
161
|
+
}).catch(() => {});
|
|
162
|
+
// #endregion
|
|
163
|
+
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export default function modelRouterBootstrap(_pi: ExtensionAPI) {
|
|
168
|
+
const wrote = ensureModelRouterConfig(process.cwd());
|
|
169
|
+
if (wrote) {
|
|
170
|
+
console.warn(
|
|
171
|
+
"[ultimate-pi] Created .pi/model-router.json from detected providers (avoids gpt-5.4-pro fallback). Run /reload if router was already loaded.",
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -3,14 +3,45 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
9
|
+
import { resolveHarnessAsset } from "./lib/harness-paths.js";
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
function resolveSyncScript(): string {
|
|
12
|
+
const packaged = resolveHarnessAsset(
|
|
13
|
+
// @ts-expect-error pi extensions run as ESM
|
|
14
|
+
import.meta.url,
|
|
15
|
+
"scripts",
|
|
16
|
+
"sentrux-rules-sync.mjs",
|
|
17
|
+
);
|
|
18
|
+
if (existsSync(packaged)) {
|
|
19
|
+
return packaged;
|
|
20
|
+
}
|
|
21
|
+
return join(process.cwd(), "scripts", "sentrux-rules-sync.mjs");
|
|
22
|
+
}
|
|
10
23
|
|
|
11
24
|
function runSync(args: string[]): Promise<{ code: number; output: string }> {
|
|
25
|
+
const syncScript = resolveSyncScript();
|
|
26
|
+
// #region agent log
|
|
27
|
+
fetch("http://127.0.0.1:7928/ingest/a5d40896-34cb-4f12-97db-df7ada0b22f0", {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
"X-Debug-Session-Id": "7737a8",
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify({
|
|
34
|
+
sessionId: "7737a8",
|
|
35
|
+
hypothesisId: "C",
|
|
36
|
+
location: "sentrux-rules-sync.ts:runSync",
|
|
37
|
+
message: "sync script path",
|
|
38
|
+
data: { syncScript, cwd: process.cwd(), exists: existsSync(syncScript) },
|
|
39
|
+
timestamp: Date.now(),
|
|
40
|
+
}),
|
|
41
|
+
}).catch(() => {});
|
|
42
|
+
// #endregion
|
|
12
43
|
return new Promise((resolve) => {
|
|
13
|
-
const child = spawn(process.execPath, [
|
|
44
|
+
const child = spawn(process.execPath, [syncScript, ...args], {
|
|
14
45
|
cwd: process.cwd(),
|
|
15
46
|
stdio: ["ignore", "pipe", "pipe"],
|
|
16
47
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"headless": true, "timeout": 30000, "viewport": {"width": 1280, "height": 720}}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"defaultProfile": "auto",
|
|
3
|
+
"debug": false,
|
|
4
|
+
"classifierModel": "opencode-go/qwen3.6-plus",
|
|
5
|
+
"phaseBias": 0.5,
|
|
6
|
+
"maxSessionBudget": 1.0,
|
|
7
|
+
"largeContextThreshold": 100000,
|
|
8
|
+
"rules": [
|
|
9
|
+
{
|
|
10
|
+
"matches": ["deploy", "production", "release"],
|
|
11
|
+
"tier": "high",
|
|
12
|
+
"reason": "Safety check for production tasks"
|
|
13
|
+
},
|
|
14
|
+
{ "matches": "changelog", "tier": "low" }
|
|
15
|
+
],
|
|
16
|
+
"profiles": {
|
|
17
|
+
"auto": {
|
|
18
|
+
"high": {
|
|
19
|
+
"model": "opencode-go/deepseek-v4-pro",
|
|
20
|
+
"thinking": "high",
|
|
21
|
+
"fallbacks": ["opencode-go/qwen3.6-plus"]
|
|
22
|
+
},
|
|
23
|
+
"medium": { "model": "opencode-go/qwen3.6-plus", "thinking": "medium" },
|
|
24
|
+
"low": { "model": "opencode-go/deepseek-v4-flash", "thinking": "low" }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
package/.pi/prompts/graphify.md
CHANGED
|
@@ -5,13 +5,9 @@ argument-hint: "[directory]"
|
|
|
5
5
|
|
|
6
6
|
Read the `graphify` skill. Then run the setup workflow:
|
|
7
7
|
|
|
8
|
-
1. Check if Graphify is installed (`pip show graphifyy`). If not, install
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
2. Check if a graph already exists (`graphify-out/graph.json`). If yes, report
|
|
13
|
-
current graph stats (nodes, edges, communities, last built).
|
|
14
|
-
3. If no graph exists, build one: `graphify ${ARGUMENTS:-.} --wiki`
|
|
8
|
+
1. Check if Graphify is installed (`pip`/`pip3 show graphifyy`, `uv tool list`, or `command -v graphify`). If not, install (`uv tool install graphifyy` preferred) and `graphify install --platform pi`.
|
|
9
|
+
2. Check if a valid graph exists (`graphify-out/graph.json` with ≥1 node and `GRAPH_REPORT.md`). If yes, report stats.
|
|
10
|
+
3. If no valid graph, build: `GRAPHIFY_VIZ_NODE_LIMIT=200000 graphify update ${ARGUMENTS:-.}` (never `graphify . --wiki` — invalid CLI). For full semantic extraction when API keys exist: `graphify extract ${ARGUMENTS:-.}`.
|
|
15
11
|
4. Read and summarize `graphify-out/GRAPH_REPORT.md` — show god nodes,
|
|
16
12
|
surprising connections, and suggested questions.
|
|
17
13
|
5. Tell user: "Graph built. Open `graphify-out/graph.html` for interactive
|
|
@@ -19,5 +15,5 @@ Read the `graphify` skill. Then run the setup workflow:
|
|
|
19
15
|
|
|
20
16
|
If the graph already exists:
|
|
21
17
|
- Report graph stats from `graph.json`
|
|
22
|
-
- Offer to update: `graphify
|
|
18
|
+
- Offer to update: `graphify update .`
|
|
23
19
|
- Show recent god nodes from GRAPH_REPORT.md
|