@sangyaahub/pi-marg 1.0.7 → 1.0.8

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.
@@ -7,14 +7,14 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "PiMarg — a governed development path for Pi Agent and Oh My Pi.",
10
- "version": "1.0.7"
10
+ "version": "1.0.8"
11
11
  },
12
12
  "plugins": [
13
13
  {
14
14
  "name": "pi-marg",
15
15
  "description": "Intent-led development workflow with live A/B/C/D routing, capability detection, verification, and approval gates.",
16
16
  "source": "./",
17
- "version": "1.0.7",
17
+ "version": "1.0.8",
18
18
  "author": {
19
19
  "name": "Sangyaa",
20
20
  "email": "contact@sangyaa.co"
package/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
1
  # Changelog
2
+ ## 1.0.8 — 2026-10-26
3
+
4
+ - A provider reporting 95% usage pauses while its stage context is kept. The saved high then low backup resumes the stage only when it is a different provider, fits the same stage, and is itself under 95%. Hard quota errors keep the existing failover.
5
+ - Provider lines in the catalog show remaining usage when the host reports `percentUsed`, `percentRemaining`, or `used`/`limit`, and say usage remaining is unknown otherwise.
6
+ - An empty OMP catalog can add the optional Kimi Code preset through the same custom-provider writer. It stores the `KIMI_API_KEY` env var name, writes no API key, and is not needed for a logged-in (OAuth) `kimi-code` provider.
7
+
2
8
 
3
9
  ## 1.0.7 — 2026-10-03
4
10
 
package/MODEL-SETUP.md CHANGED
@@ -16,7 +16,7 @@ LIVE AUTHENTICATED CATALOG
16
16
  high backup ──► low backup
17
17
  ```
18
18
 
19
- If the live catalog is empty, `/pi-marg` offers three actions: sign in with `/login`, add a custom OpenAI-compatible provider, or cancel. The custom path writes `apiKey: ENV_VAR_NAME` into `~/.omp/agent/models.yml` and the secret only into `~/.omp/agent/.env`. Cancelling writes neither file.
19
+ If the live catalog is empty, `/pi-marg` offers four actions: sign in with `/login`, add a custom OpenAI-compatible provider, add the optional Kimi Code preset, or cancel. The custom path writes `apiKey: ENV_VAR_NAME` into `~/.omp/agent/models.yml` and the secret only into `~/.omp/agent/.env`. The Kimi preset uses that same writer with provider id `kimi-code`, display name `Kimi Code`, base URL `https://api.kimi.com/coding/v1`, env var `KIMI_API_KEY`, and the model ids from OMP's Kimi Code provider rule. It does not write an API key. Cancelling writes neither file. A logged-in Kimi provider is listed with every other authenticated provider; the preset is not required for that.
20
20
 
21
21
  ## Roles
22
22
 
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- <a href="https://github.com/sangyaahub/pi-marg/releases"><img alt="Version 1.0.7" src="https://img.shields.io/badge/version-1.0.7-0451F7"></a>
6
+ <a href="https://github.com/sangyaahub/pi-marg/releases"><img alt="Version 1.0.8" src="https://img.shields.io/badge/version-1.0.8-0451F7"></a>
7
7
  <a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/license-MIT-C27B1A"></a>
8
8
  <a href="https://pi.dev/packages?name=pi-marg"><img alt="Pi package" src="https://img.shields.io/badge/Pi-package-0338A8"></a>
9
9
  <a href="https://omp.sh"><img alt="Oh My Pi plugin" src="https://img.shields.io/badge/OMP-plugin-021E5C"></a>
@@ -11,7 +11,7 @@
11
11
 
12
12
  # PiMarg
13
13
 
14
- **Version 1.0.7 · MIT licensed · Pi Agent + Oh My Pi · by [Sangyaa](https://sangyaa.co)**
14
+ **Version 1.0.8 · MIT licensed · Pi Agent + Oh My Pi · by [Sangyaa](https://sangyaa.co)**
15
15
 
16
16
  PiMarg turns an ordinary prompt into a governed development path. It identifies the work boundary, selects one engineering method, discovers the models and tools the active runtime really provides, separates thinking from execution, records evidence, and stops before protected actions.
17
17
 
@@ -10,8 +10,13 @@ import {
10
10
  executeAutomaticFailover,
11
11
  executeModelRoute,
12
12
  failedModelSelector,
13
+ chooseUsageResume,
14
+ executeUsageResume,
13
15
  findTerminalUsageLimitFailure,
14
16
  formatFailoverRecoveryMessage,
17
+ formatUsagePauseMessage,
18
+ providerReachedUsagePause,
19
+ usageSnapshotsFromMessages,
15
20
  hasConfiguredFallback,
16
21
  modelFailureKey,
17
22
  modelToolUsageLimitFailure,
@@ -370,6 +375,29 @@ export default function modelRouter(pi: any) {
370
375
 
371
376
  pi.on("agent_end", async (event: any, ctx: any) => {
372
377
  if (event?.willContinue || event?.isTerminal === false) return;
378
+ const liveModels = (ctx.models?.list?.() ?? []) as ModelLike[];
379
+ const current = ctx.models?.current?.() ?? ctx.model;
380
+ const snapshots = usageSnapshotsFromMessages(event?.messages ?? [], liveModels);
381
+ const pause = typeof current?.provider === "string" ? providerReachedUsagePause(snapshots, current.provider) : undefined;
382
+ if (pause && typeof current?.id === "string" && hasConfiguredFallback(state)) {
383
+ const pausedSelector = `${current.provider}/${current.id}`;
384
+ const resume = chooseUsageResume(state, snapshots, current.provider);
385
+ if (resume) {
386
+ const execution = await executeUsageResume(state, pausedSelector, resume, {
387
+ runtimeName: "OMP",
388
+ models: liveModels,
389
+ hasExternalDevin: hasExternalDevin(pi),
390
+ activate: (model) => pi.setModel(model),
391
+ persist: (nextState) => persistRoute(nextState, ctx),
392
+ });
393
+ state = execution.state;
394
+ if (execution.switched) {
395
+ ctx.ui?.notify?.(`PiMarg paused ${pausedSelector} at ${pause.percentUsed}% usage and resumed on ${execution.switched.selection.selector}.`, "warning");
396
+ pi.sendUserMessage(formatUsagePauseMessage(pausedSelector, pause, execution.switched.selection.selector), { deliverAs: "aside" });
397
+ return;
398
+ }
399
+ }
400
+ }
373
401
  const failed = findTerminalUsageLimitFailure(event?.messages ?? []);
374
402
  const failure = failed
375
403
  ? {
@@ -2,6 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
4
  import {
5
+ kimiCodeProviderPreset,
5
6
  probeModelIds,
6
7
  validateCustomProvider,
7
8
  writeCustomProvider,
@@ -17,6 +18,7 @@ import { agentDirectory } from "../../../core/user-route";
17
18
  const SETUP_ITEMS = [
18
19
  "Sign in to a provider",
19
20
  "Add a custom OpenAI-compatible provider",
21
+ "Add the Kimi Code preset",
20
22
  "Cancel",
21
23
  ] as const;
22
24
 
@@ -157,9 +159,34 @@ export async function addCustomProvider(ctx: any, home: string | undefined): Pro
157
159
  return { status: "ready", models };
158
160
  }
159
161
 
162
+ export async function addKimiCodePreset(ctx: any, home: string | undefined): Promise<CatalogSetup> {
163
+ const preset = kimiCodeProviderPreset();
164
+ try {
165
+ const directory = agentDirectory("OMP", home);
166
+ writeCustomProvider({
167
+ modelsPath: join(directory, "models.yml"),
168
+ envPath: join(directory, ".env"),
169
+ }, preset);
170
+ } catch (error) {
171
+ ctx.ui.notify(error instanceof Error ? error.message : "Could not write the Kimi Code preset.", "error");
172
+ return { status: "cancelled" };
173
+ }
174
+ ctx.ui.notify(
175
+ `Kimi Code was added to models.yml. Set ${preset.envVar} in the environment; the API key was not stored.`,
176
+ "info",
177
+ );
178
+ if (typeof ctx.models?.reload === "function") {
179
+ await ctx.models.reload();
180
+ }
181
+ const models = (ctx.models.list() ?? []) as ModelLike[];
182
+ if (models.length === 0) return { status: "restart" };
183
+ return { status: "ready", models };
184
+ }
185
+
160
186
  export async function configureEmptyCatalog(ctx: any, home: string | undefined): Promise<CatalogSetup> {
161
187
  const choice = await askItems(ctx, "Model setup", SETUP_ITEMS);
162
188
  if (!choice || choice === "Cancel") return { status: "cancelled" };
189
+ if (choice === "Add the Kimi Code preset") return addKimiCodePreset(ctx, home);
163
190
  if (choice === "Sign in to a provider") {
164
191
  if (typeof ctx.openLogin === "function") await ctx.openLogin();
165
192
  else ctx.ui.notify("Sign in with /login, verify with `omp models`, then run /pi-marg again.", "warning");
@@ -7,13 +7,20 @@ import {
7
7
  executeAutomaticFailover,
8
8
  executeModelRoute,
9
9
  failedModelSelector,
10
+ chooseUsageResume,
11
+ executeUsageResume,
10
12
  findTerminalUsageLimitFailure,
11
13
  formatFailoverRecoveryMessage,
14
+ formatUsagePauseMessage,
15
+ providerReachedUsagePause,
16
+ usageSnapshotsFromMessages,
12
17
  hasConfiguredFallback,
13
18
  modelFailureKey,
14
19
  modelToolUsageLimitFailure,
15
20
  type ModelLike,
16
21
  type ModelRouteParams,
22
+ type ModelSelection,
23
+ type ProviderUsageSnapshot,
17
24
  } from "../../../core/model-routing";
18
25
  import { loadRuntimeRoute, rememberRuntimeRoute } from "../../../core/user-route";
19
26
 
@@ -36,6 +43,7 @@ export default function modelRouter(pi: any) {
36
43
  let state = emptyModelRouteState();
37
44
  let recoveryToken: symbol | undefined;
38
45
  let sessionGeneration = 0;
46
+ let pendingUsagePause: { selector: string; snapshot: ProviderUsageSnapshot; resume: ModelSelection } | undefined;
39
47
  let pendingModelFailure: { selector: string; key: string } | undefined;
40
48
  let pendingToolFailure: { selector: string; key: string } | undefined;
41
49
  const routeHome = (ctx: any) => typeof ctx?.agentHome === "string" ? ctx.agentHome : undefined;
@@ -50,6 +58,7 @@ export default function modelRouter(pi: any) {
50
58
  const restore = (_event: unknown, ctx: any) => {
51
59
  sessionGeneration += 1;
52
60
  recoveryToken = undefined;
61
+ pendingUsagePause = undefined;
53
62
  pendingModelFailure = undefined;
54
63
  pendingToolFailure = undefined;
55
64
  state = loadRuntimeRoute("Pi", ctx.sessionManager.getBranch(), routeHome(ctx));
@@ -128,6 +137,17 @@ export default function modelRouter(pi: any) {
128
137
  };
129
138
 
130
139
  pi.on("agent_end", (event: any, ctx: any) => {
140
+ const liveModels = availableModels(ctx);
141
+ const current = ctx.model as ModelLike | undefined;
142
+ const snapshots = usageSnapshotsFromMessages(event?.messages ?? [], liveModels);
143
+ const pause = current?.provider ? providerReachedUsagePause(snapshots, current.provider) : undefined;
144
+ if (pause && current?.id && hasConfiguredFallback(state)) {
145
+ const pausedSelector = `${current.provider}/${current.id}`;
146
+ const resume = chooseUsageResume(state, snapshots, current.provider);
147
+ if (resume) {
148
+ pendingUsagePause = { selector: pausedSelector, snapshot: pause, resume };
149
+ }
150
+ }
131
151
  const failed = findTerminalUsageLimitFailure(event?.messages ?? []);
132
152
  pendingModelFailure = failed
133
153
  ? { selector: failedModelSelector(failed, ctx.model), key: modelFailureKey(failed) }
@@ -139,6 +159,25 @@ export default function modelRouter(pi: any) {
139
159
  });
140
160
 
141
161
  pi.on("agent_settled", async (_event: unknown, ctx: any) => {
162
+ const usagePause = pendingUsagePause;
163
+ pendingUsagePause = undefined;
164
+ if (usagePause && !pendingModelFailure && !pendingToolFailure) {
165
+ const execution = await executeUsageResume(state, usagePause.selector, usagePause.resume, {
166
+ runtimeName: "Pi",
167
+ models: availableModels(ctx),
168
+ hasExternalDevin: hasExternalDevin(pi),
169
+ activate: (model) => pi.setModel(model),
170
+ persist: (nextState) => persistRoute(nextState, ctx),
171
+ });
172
+ state = execution.state;
173
+ if (execution.switched) {
174
+ ctx.ui?.notify?.(`PiMarg paused ${usagePause.selector} at ${usagePause.snapshot.percentUsed}% usage and resumed on ${execution.switched.selection.selector}.`, "warning");
175
+ pi.sendUserMessage(formatUsagePauseMessage(usagePause.selector, usagePause.snapshot, execution.switched.selection.selector), { deliverAs: "followUp" });
176
+ pendingModelFailure = undefined;
177
+ pendingToolFailure = undefined;
178
+ return;
179
+ }
180
+ }
142
181
  const failure = pendingModelFailure ?? pendingToolFailure;
143
182
  pendingModelFailure = undefined;
144
183
  pendingToolFailure = undefined;
@@ -3,13 +3,29 @@ export type FallbackLevel = "high" | "low";
3
3
  export type CandidateTier = "frontier" | "execution" | "general";
4
4
  export type RuntimeName = "OMP" | "Pi";
5
5
 
6
+ export interface ModelUsage {
7
+ percentUsed?: number;
8
+ percentRemaining?: number;
9
+ used?: number;
10
+ limit?: number;
11
+ }
12
+
6
13
  export interface ModelLike {
7
14
  provider: string;
8
15
  id: string;
9
16
  name?: string;
10
17
  cost?: Record<string, unknown>;
18
+ usage?: ModelUsage;
19
+ }
20
+
21
+ export interface ProviderUsageSnapshot {
22
+ provider: string;
23
+ percentUsed: number;
24
+ percentRemaining: number;
11
25
  }
12
26
 
27
+ export const USAGE_PAUSE_PERCENT = 95;
28
+
13
29
  export interface ModelCandidate {
14
30
  selector: string;
15
31
  identity: string;
@@ -158,6 +174,108 @@ export function emptyModelRouteState(): ModelRouteState {
158
174
  return { version: 2, selections: {}, fallbacks: {} };
159
175
  }
160
176
 
177
+
178
+ function finitePercent(value: unknown): number | undefined {
179
+ if (typeof value !== "number" || !Number.isFinite(value)) return undefined;
180
+ return Math.min(100, Math.max(0, value));
181
+ }
182
+
183
+ export function percentUsedFromUsage(usage: ModelUsage | undefined): number | undefined {
184
+ if (!usage) return undefined;
185
+ const used = finitePercent(usage.percentUsed);
186
+ if (used !== undefined) return used;
187
+ const remaining = finitePercent(usage.percentRemaining);
188
+ if (remaining !== undefined) return 100 - remaining;
189
+ if (typeof usage.used === "number" && typeof usage.limit === "number" && usage.limit > 0 && Number.isFinite(usage.used)) {
190
+ return finitePercent((usage.used / usage.limit) * 100);
191
+ }
192
+ return undefined;
193
+ }
194
+
195
+ export function providerUsageSnapshots(models: readonly ModelLike[]): ProviderUsageSnapshot[] {
196
+ const byProvider = new Map<string, number>();
197
+ for (const model of models) {
198
+ const percentUsed = percentUsedFromUsage(model.usage);
199
+ if (percentUsed === undefined) continue;
200
+ const current = byProvider.get(model.provider);
201
+ if (current === undefined || percentUsed > current) byProvider.set(model.provider, percentUsed);
202
+ }
203
+ return [...byProvider.entries()]
204
+ .map(([provider, percentUsed]) => ({
205
+ provider,
206
+ percentUsed,
207
+ percentRemaining: Math.max(0, 100 - percentUsed),
208
+ }))
209
+ .sort((left, right) => left.provider.localeCompare(right.provider));
210
+ }
211
+
212
+ export function usageSnapshotsFromMessages(
213
+ messages: readonly unknown[],
214
+ models: readonly ModelLike[] = [],
215
+ ): ProviderUsageSnapshot[] {
216
+ const reported: ModelLike[] = [...models];
217
+ for (const message of messages) {
218
+ const record = asRecord(message);
219
+ if (!record || typeof record.provider !== "string") continue;
220
+ const usage = asRecord(record.usage);
221
+ if (!usage) continue;
222
+ reported.push({
223
+ provider: record.provider,
224
+ id: typeof record.model === "string" ? record.model : "message",
225
+ usage: {
226
+ percentUsed: typeof usage.percentUsed === "number" ? usage.percentUsed : undefined,
227
+ percentRemaining: typeof usage.percentRemaining === "number" ? usage.percentRemaining : undefined,
228
+ used: typeof usage.used === "number" ? usage.used : undefined,
229
+ limit: typeof usage.limit === "number" ? usage.limit : undefined,
230
+ },
231
+ });
232
+ }
233
+ return providerUsageSnapshots(reported);
234
+ }
235
+
236
+ export function providerReachedUsagePause(
237
+ snapshots: readonly ProviderUsageSnapshot[],
238
+ provider: string,
239
+ ): ProviderUsageSnapshot | undefined {
240
+ return snapshots.find((snapshot) => snapshot.provider === provider && snapshot.percentUsed >= USAGE_PAUSE_PERCENT);
241
+ }
242
+
243
+ function fallbackFitsStage(stage: AutoStage | undefined, selection: ModelSelection): boolean {
244
+ if (!stage || selection.tier === "general") return true;
245
+ if (stage === "A" || stage === "C") return selection.tier === "frontier";
246
+ return selection.tier === "execution";
247
+ }
248
+
249
+ export function chooseUsageResume(
250
+ state: ModelRouteState,
251
+ snapshots: readonly ProviderUsageSnapshot[],
252
+ pausedProvider: string,
253
+ ): ModelSelection | undefined {
254
+ for (const level of FALLBACK_LEVELS) {
255
+ const selection = state.fallbacks[level];
256
+ if (!selection || selection.access !== "runtime-model") continue;
257
+ if (selection.provider === pausedProvider) continue;
258
+ if (!fallbackFitsStage(state.activeStage, selection)) continue;
259
+ const usage = snapshots.find((snapshot) => snapshot.provider === selection.provider);
260
+ if (usage && usage.percentUsed >= USAGE_PAUSE_PERCENT) continue;
261
+ return selection;
262
+ }
263
+ return undefined;
264
+ }
265
+
266
+ export function formatUsagePauseMessage(
267
+ pausedSelector: string,
268
+ snapshot: ProviderUsageSnapshot,
269
+ resumeSelector: string,
270
+ ): string {
271
+ return [
272
+ `[PiMarg usage pause] ${pausedSelector} is at ${snapshot.percentUsed}% usage (${snapshot.percentRemaining}% remaining).`,
273
+ "Paused and kept the current stage context.",
274
+ `Resume on ${resumeSelector}, a saved backup of the same stage capability.`,
275
+ ].join("\n");
276
+ }
277
+
278
+
161
279
  export function isFallbackLevel(value: unknown): value is FallbackLevel {
162
280
  return typeof value === "string" && FALLBACK_LEVELS.includes(value as FallbackLevel);
163
281
  }
@@ -471,6 +589,7 @@ export function formatCatalogPresentation(
471
589
  catalog: Record<AutoStage, ModelCandidate[]>,
472
590
  requiredStages: AutoStage[],
473
591
  providerFilter?: string,
592
+ usage: readonly ProviderUsageSnapshot[] = [],
474
593
  ) {
475
594
  const notes = catalogNotes(runtimeName, catalog);
476
595
  const orderedStages = ascendingStages(requiredStages);
@@ -520,7 +639,9 @@ export function formatCatalogPresentation(
520
639
  lines.push("Providers:");
521
640
  for (const provider of providers) {
522
641
  const marker = providerFilter === provider.name ? " ← selected" : "";
523
- lines.push(` ${provider.index}. ${provider.name} (${provider.count})${marker}`);
642
+ const snapshot = usage.find((item) => item.provider === provider.name);
643
+ const remaining = snapshot ? ` — ${snapshot.percentRemaining}% remaining` : " — usage remaining unknown";
644
+ lines.push(` ${provider.index}. ${provider.name} (${provider.count})${remaining}${marker}`);
524
645
  }
525
646
  if (!providerFilter) {
526
647
  lines.push("Call catalog again with provider=<name> to list every model for that provider.");
@@ -583,6 +704,45 @@ export function restoreModelRouteState(entries: readonly any[]): ModelRouteState
583
704
  return restored;
584
705
  }
585
706
 
707
+
708
+ export async function executeUsageResume(
709
+ currentState: ModelRouteState,
710
+ pausedSelector: string,
711
+ selection: ModelSelection,
712
+ port: ModelRoutePort,
713
+ ): Promise<AutomaticFailoverExecution> {
714
+ const level = (FALLBACK_LEVELS.find((item) => currentState.fallbacks[item]?.selector === selection.selector) ?? "high");
715
+ const model = port.models.find((item) => `${item.provider}/${item.id}` === selection.selector);
716
+ if (!model) return { state: currentState, exhausted: true, stateChanged: false };
717
+ let activated: boolean | void;
718
+ try {
719
+ activated = await port.activate(model);
720
+ } catch {
721
+ return { state: currentState, exhausted: false, stateChanged: false };
722
+ }
723
+ if (activated === false) return { state: currentState, exhausted: false, stateChanged: false };
724
+ const nextState: ModelRouteState = {
725
+ ...currentState,
726
+ version: 2,
727
+ activeFallback: level,
728
+ lastFailover: {
729
+ stage: currentState.activeStage,
730
+ failedSelector: pausedSelector,
731
+ fallbackLevel: level,
732
+ activatedSelector: selection.selector,
733
+ switchedAt: port.now?.() ?? new Date().toISOString(),
734
+ },
735
+ };
736
+ const persistenceError = persistFailoverState(port, nextState);
737
+ return {
738
+ state: nextState,
739
+ switched: { level, selection },
740
+ exhausted: false,
741
+ stateChanged: true,
742
+ ...(persistenceError ? { persistenceError } : {}),
743
+ };
744
+ }
745
+
586
746
  export async function executeAutomaticFailover(
587
747
  currentState: ModelRouteState,
588
748
  failedSelector: string,
@@ -780,7 +940,8 @@ export async function executeModelRoute(
780
940
  };
781
941
  }
782
942
  }
783
- const formatted = formatCatalogPresentation(port.runtimeName, catalog, requiredStages, providerFilter);
943
+ const usage = providerUsageSnapshots(port.models);
944
+ const formatted = formatCatalogPresentation(port.runtimeName, catalog, requiredStages, providerFilter, usage);
784
945
  return {
785
946
  state: currentState,
786
947
  response: toolResult(formatted.text, {
@@ -15,6 +15,28 @@ export interface ProviderFiles {
15
15
  envPath: string;
16
16
  }
17
17
 
18
+ const KIMI_CODE_MODEL_IDS = [
19
+ "kimi-for-coding",
20
+ "k3",
21
+ "k3-256k",
22
+ "kimi-for-coding-highspeed",
23
+ "kimi-k2",
24
+ "kimi-k2.5",
25
+ "kimi-k2-turbo-preview",
26
+ ] as const;
27
+
28
+ /** Optional Kimi Code models.yml input. It names the env var and does not carry an API key. */
29
+ export function kimiCodeProviderPreset(): CustomProviderInput {
30
+ return {
31
+ providerId: "kimi-code",
32
+ displayName: "Kimi Code",
33
+ baseUrl: "https://api.kimi.com/coding/v1",
34
+ envVar: "KIMI_API_KEY",
35
+ modelIds: [...KIMI_CODE_MODEL_IDS],
36
+ allowLocalHttp: false,
37
+ };
38
+ }
39
+
18
40
  const PROVIDER_ID = /^[a-z0-9_-]+$/;
19
41
  const ENV_VAR = /^[A-Z][A-Z0-9_]*$/;
20
42
  const MODEL_ID = /^[A-Za-z0-9._:-]+$/;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sangyaahub/pi-marg",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "A governed, model-routed development path for Pi Agent and Oh My Pi.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -71,4 +71,4 @@ if [[ "${runtime}" == "omp" || "${runtime}" == "both" ]]; then
71
71
  omp plugin install --scope user pi-marg@pi-marg-marketplace
72
72
  fi
73
73
 
74
- echo "PiMarg v1.0.6 installed for ${runtime}. Start a fresh runtime session, then run /pi-marg."
74
+ echo "PiMarg v1.0.8 installed for ${runtime}. Start a fresh runtime session, then run /pi-marg."
@@ -45,7 +45,7 @@ bun -e '
45
45
  const packageJson = await Bun.file("package.json").json();
46
46
  const marketplace = await Bun.file(".omp-plugin/marketplace.json").json();
47
47
  if (packageJson.name !== "@sangyaahub/pi-marg") throw new Error("Unexpected package name");
48
- if (packageJson.version !== "1.0.7") throw new Error("package.json must be v1.0.7");
48
+ if (packageJson.version !== "1.0.8") throw new Error("package.json must be v1.0.8");
49
49
  if (packageJson.license !== "MIT") throw new Error("package.json must declare MIT");
50
50
  if (packageJson.author !== "Sangyaa") throw new Error("Unexpected package author");
51
51
  if (!packageJson.keywords?.includes("pi-package")) throw new Error("Pi catalog keyword is missing");