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.
@@ -0,0 +1,15 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Bash",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "/home/aryaniyaps/.local/bin/graphify hook-check"
10
+ }
11
+ ]
12
+ }
13
+ ]
14
+ }
15
+ }
@@ -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
- const imagePath = join(process.cwd(), ".pi", "extensions", "banner.png");
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
- const SYNC_SCRIPT = join(process.cwd(), "scripts", "sentrux-rules-sync.mjs");
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, [SYNC_SCRIPT, ...args], {
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
+ }
@@ -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 it:
9
- ```bash
10
- pip install graphifyy && graphify install
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 . --update`
18
+ - Offer to update: `graphify update .`
23
19
  - Show recent god nodes from GRAPH_REPORT.md