@sangyaahub/pi-marg 1.0.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.
Files changed (71) hide show
  1. package/.mcp.json +12 -0
  2. package/.omp-plugin/marketplace.json +42 -0
  3. package/BRAND.md +25 -0
  4. package/CHANGELOG.md +11 -0
  5. package/CODE_OF_CONDUCT.md +9 -0
  6. package/CONTRIBUTING.md +11 -0
  7. package/LICENSE +21 -0
  8. package/MODEL-SETUP.md +73 -0
  9. package/PUBLISHING.md +53 -0
  10. package/README.md +191 -0
  11. package/SECURITY.md +11 -0
  12. package/SUPPORT.md +7 -0
  13. package/adapters/omp/extensions/approval-guard.ts +43 -0
  14. package/adapters/omp/extensions/model-router.ts +56 -0
  15. package/adapters/omp/extensions/runtime-capabilities.ts +24 -0
  16. package/adapters/pi/extensions/approval-guard.ts +39 -0
  17. package/adapters/pi/extensions/model-router.ts +61 -0
  18. package/adapters/pi/extensions/runtime-capabilities.ts +25 -0
  19. package/adapters/pi/extensions/workflow-bootstrap.ts +7 -0
  20. package/adapters/pi/prompts/auto-close.md +11 -0
  21. package/adapters/pi/prompts/auto-commit.md +7 -0
  22. package/adapters/pi/prompts/auto-models.md +12 -0
  23. package/adapters/pi/prompts/auto-reset.md +8 -0
  24. package/adapters/pi/prompts/auto-review.md +11 -0
  25. package/adapters/pi/prompts/auto-runtime.md +13 -0
  26. package/adapters/pi/prompts/auto-status.md +17 -0
  27. package/adapters/pi/prompts/auto.md +12 -0
  28. package/agents/auto-builder.md +10 -0
  29. package/agents/auto-business-analyst.md +9 -0
  30. package/agents/auto-debugger.md +10 -0
  31. package/agents/auto-intake.md +10 -0
  32. package/agents/auto-lessons-writer.md +10 -0
  33. package/agents/auto-qa.md +9 -0
  34. package/agents/auto-reviewer.md +9 -0
  35. package/agents/auto-security-reviewer.md +9 -0
  36. package/agents/auto-strategist.md +10 -0
  37. package/assets/brand/sangyaa-mark.png +0 -0
  38. package/assets/pi-marg-banner-source.html +106 -0
  39. package/assets/pi-marg-banner.png +0 -0
  40. package/commands/auto-close.md +11 -0
  41. package/commands/auto-commit.md +7 -0
  42. package/commands/auto-models.md +12 -0
  43. package/commands/auto-reset.md +8 -0
  44. package/commands/auto-review.md +11 -0
  45. package/commands/auto-runtime.md +13 -0
  46. package/commands/auto-status.md +17 -0
  47. package/commands/auto.md +12 -0
  48. package/core/approval-policy.ts +109 -0
  49. package/core/model-routing.ts +348 -0
  50. package/core/runtime-capabilities.ts +77 -0
  51. package/core/workflow-policy.ts +3 -0
  52. package/package.json +84 -0
  53. package/rules/auto-mode.md +32 -0
  54. package/scripts/check-prerequisites.sh +37 -0
  55. package/scripts/install.sh +74 -0
  56. package/scripts/setup-codegraph.sh +62 -0
  57. package/scripts/validate.sh +75 -0
  58. package/skills/auto-mode-addons/SKILL.md +15 -0
  59. package/skills/auto-mode-addons/references/cost-control.md +24 -0
  60. package/skills/auto-mode-addons/references/hindsight.md +54 -0
  61. package/skills/auto-mode-addons/references/language-skills.md +54 -0
  62. package/skills/auto-mode-addons/references/ponytail.md +41 -0
  63. package/skills/auto-mode-router/SKILL.md +22 -0
  64. package/skills/auto-mode-router/references/intake-and-state.md +124 -0
  65. package/skills/auto-mode-router/references/models-and-tools.md +74 -0
  66. package/skills/auto-mode-router/references/native-omp-runtime.md +88 -0
  67. package/skills/auto-mode-router/references/native-pi-runtime.md +52 -0
  68. package/skills/auto-mode-router/references/routing-matrix.md +99 -0
  69. package/skills/auto-mode-router/references/session-ledger.md +92 -0
  70. package/skills/codegraph-first/SKILL.md +34 -0
  71. package/skills/session-lessons/SKILL.md +34 -0
@@ -0,0 +1,348 @@
1
+ export type AutoStage = "A" | "B" | "C" | "D";
2
+ export type CandidateTier = "frontier" | "execution";
3
+ export type RuntimeName = "OMP" | "Pi";
4
+
5
+ export interface ModelLike {
6
+ provider: string;
7
+ id: string;
8
+ name?: string;
9
+ cost?: Record<string, unknown>;
10
+ }
11
+
12
+ export interface ModelCandidate {
13
+ selector: string;
14
+ identity: string;
15
+ label: string;
16
+ provider: string;
17
+ tier: CandidateTier;
18
+ choice:
19
+ | "claude-frontier"
20
+ | "openai-frontier"
21
+ | "grok-frontier"
22
+ | "claude-mid"
23
+ | "openai-mid"
24
+ | "cursor-composer"
25
+ | "included-cursor"
26
+ | "devin-swe"
27
+ | "included-devin"
28
+ | "devin-external";
29
+ access: "runtime-model" | "external-agent";
30
+ billingNote?: string;
31
+ }
32
+
33
+ export interface ModelSelection extends ModelCandidate {
34
+ selectedAt: string;
35
+ }
36
+
37
+ export interface ModelRouteState {
38
+ version: 1;
39
+ workType?: number;
40
+ selections: Partial<Record<AutoStage, ModelSelection>>;
41
+ }
42
+
43
+ export interface ModelRouteParams {
44
+ action: "catalog" | "select" | "activate" | "status";
45
+ stage?: AutoStage;
46
+ target?: string;
47
+ workType?: number;
48
+ }
49
+
50
+ export interface ModelRoutePort {
51
+ runtimeName: RuntimeName;
52
+ models: ModelLike[];
53
+ hasExternalDevin: boolean;
54
+ activate(model: ModelLike): Promise<boolean | void>;
55
+ persist(state: ModelRouteState): void;
56
+ now?(): string;
57
+ }
58
+
59
+ export interface ModelRouteExecution {
60
+ state: ModelRouteState;
61
+ response: ReturnType<typeof toolResult>;
62
+ }
63
+
64
+ export const MODEL_STATE_TYPE = "co.sangyaa.pi-marg.model-routing.v1";
65
+ export const STAGES: AutoStage[] = ["A", "B", "C", "D"];
66
+
67
+ const FRONTIER_PATTERNS: Array<[ModelCandidate["choice"], RegExp]> = [
68
+ ["claude-frontier", /(?:claude[^\n]*(?:fable|opus)|(?:fable|opus)[^\n]*claude)/i],
69
+ ["openai-frontier", /(?:gpt[^\n]*(?:astra|\bsol\b)|(?:astra|\bsol\b)[^\n]*gpt)/i],
70
+ ["grok-frontier", /grok[^\n]*4[._ -](?:[6-9]|\d{2,})(?:\b|[-_])/i],
71
+ ];
72
+
73
+ const EXECUTION_PATTERNS: Array<[ModelCandidate["choice"], RegExp]> = [
74
+ ["claude-mid", /claude[^\n]*sonnet/i],
75
+ ["openai-mid", /(?:gpt|codex)[^\n]*(?:terra|luna|mini|codex)/i],
76
+ ["cursor-composer", /composer/i],
77
+ ];
78
+
79
+ export function emptyModelRouteState(): ModelRouteState {
80
+ return { version: 1, selections: {} };
81
+ }
82
+
83
+ export function isStage(value: unknown): value is AutoStage {
84
+ return typeof value === "string" && STAGES.includes(value as AutoStage);
85
+ }
86
+
87
+ export function requiredStagesForWorkType(workType: number): AutoStage[] {
88
+ switch (workType) {
89
+ case 2:
90
+ case 3:
91
+ return ["A", "C", "B", "D"];
92
+ case 4:
93
+ return ["A", "B", "D"];
94
+ case 1:
95
+ case 5:
96
+ case 6:
97
+ case 7:
98
+ return ["A", "C"];
99
+ default:
100
+ return [];
101
+ }
102
+ }
103
+
104
+ export function normalizeModelIdentity(value: string): string {
105
+ let normalized = value
106
+ .toLowerCase()
107
+ .replace(/^[^/]+\//, "")
108
+ .replace(/(?:-latest|:latest)$/g, "")
109
+ .replace(/[^a-z0-9.]+/g, "-")
110
+ .replace(/^-|-$/g, "");
111
+
112
+ normalized = normalized
113
+ .replace(/^cursor-/, "")
114
+ .replace(/-\d{4}-(?:non-)?reasoning$/, "")
115
+ .replace(/-multi-agent-\d{4}$/, "")
116
+ .replace(/-\d{8}$/, "")
117
+ .replace(/(\d)-(?=\d)/g, "$1.")
118
+ .replace(/^claude-(\d+(?:\.\d+)?)-(opus|sonnet)/, "claude-$2-$1")
119
+ .replace(/-1m$/, "");
120
+
121
+ let previous: string;
122
+ do {
123
+ previous = normalized;
124
+ normalized = normalized.replace(
125
+ /-(?:thinking-)?(?:minimal|low|medium|high|xhigh|max|fast|lightning)$/,
126
+ "",
127
+ );
128
+ } while (normalized !== previous);
129
+
130
+ return normalized;
131
+ }
132
+
133
+ export function classifyModel(model: ModelLike): ModelCandidate | undefined {
134
+ const selector = `${model.provider}/${model.id}`;
135
+ const haystack = `${model.provider} ${model.id} ${model.name ?? ""}`;
136
+
137
+ for (const [choice, pattern] of FRONTIER_PATTERNS) {
138
+ if (pattern.test(haystack)) return candidate(model, selector, choice, "frontier");
139
+ }
140
+ for (const [choice, pattern] of EXECUTION_PATTERNS) {
141
+ if (pattern.test(haystack)) return candidate(model, selector, choice, "execution");
142
+ }
143
+
144
+ if (/^devin$/i.test(model.provider) && /^swe[-_. ]/i.test(model.id)) {
145
+ return candidate(model, selector, "devin-swe", "execution");
146
+ }
147
+ if (/cursor/i.test(model.provider) && isZeroMetered(model.cost)) {
148
+ return {
149
+ ...candidate(model, selector, "included-cursor", "execution"),
150
+ billingNote: "The runtime reports zero metered token cost; plan quotas or pool limits may still apply.",
151
+ };
152
+ }
153
+ if (/^devin$/i.test(model.provider) && isZeroMetered(model.cost)) {
154
+ return {
155
+ ...candidate(model, selector, "included-devin", "execution"),
156
+ billingNote: "The runtime reports zero metered token cost; Devin plan quotas or pool limits may still apply.",
157
+ };
158
+ }
159
+ return undefined;
160
+ }
161
+
162
+ export function buildModelCatalog(models: ModelLike[], hasExternalDevin: boolean) {
163
+ const candidates = models
164
+ .map(classifyModel)
165
+ .filter((item): item is ModelCandidate => Boolean(item))
166
+ .sort(compareCandidates);
167
+
168
+ if (hasExternalDevin) {
169
+ candidates.push({
170
+ selector: "devin/external-session",
171
+ identity: "devin-external-session",
172
+ label: "Devin external session",
173
+ provider: "devin",
174
+ tier: "execution",
175
+ choice: "devin-external",
176
+ access: "external-agent",
177
+ billingNote: "Uses Devin quota rather than a runtime model slot; session creation requires post-web approval.",
178
+ });
179
+ }
180
+
181
+ const frontier = candidates.filter((item) => item.tier === "frontier");
182
+ const execution = candidates.filter((item) => item.tier === "execution");
183
+ return { A: frontier, B: execution, C: frontier, D: execution } satisfies Record<AutoStage, ModelCandidate[]>;
184
+ }
185
+
186
+ export function validateDistinctSelection(
187
+ state: ModelRouteState,
188
+ stage: AutoStage,
189
+ candidate: ModelCandidate,
190
+ ): string | undefined {
191
+ const opposite: Record<AutoStage, AutoStage> = { A: "C", C: "A", B: "D", D: "B" };
192
+ const otherStage = opposite[stage];
193
+ const other = state.selections[otherStage];
194
+ if (other && other.identity === candidate.identity) {
195
+ return `Stage ${stage} cannot use ${candidate.label}; Stage ${otherStage} already uses the same model. Choose a different model.`;
196
+ }
197
+ return undefined;
198
+ }
199
+
200
+ export function restoreModelRouteState(entries: readonly any[]): ModelRouteState {
201
+ let restored = emptyModelRouteState();
202
+ for (const entry of entries) {
203
+ if (entry?.type === "custom" && entry.customType === MODEL_STATE_TYPE && entry.data?.version === 1) {
204
+ restored = entry.data as ModelRouteState;
205
+ }
206
+ }
207
+ return restored;
208
+ }
209
+
210
+ export async function executeModelRoute(
211
+ currentState: ModelRouteState,
212
+ params: ModelRouteParams,
213
+ port: ModelRoutePort,
214
+ ): Promise<ModelRouteExecution> {
215
+ const catalog = buildModelCatalog(port.models, port.hasExternalDevin);
216
+
217
+ if (params.action === "catalog") {
218
+ const workType = Number(params.workType);
219
+ const requiredStages = Number.isInteger(workType) ? requiredStagesForWorkType(workType) : STAGES;
220
+ return {
221
+ state: currentState,
222
+ response: toolResult(
223
+ JSON.stringify({ runtime: port.runtimeName, requiredStages, catalog, notes: catalogNotes(port.runtimeName, catalog) }, null, 2),
224
+ { runtime: port.runtimeName, requiredStages, catalog, state: currentState },
225
+ ),
226
+ };
227
+ }
228
+
229
+ if (params.action === "status") {
230
+ return { state: currentState, response: toolResult(JSON.stringify(currentState, null, 2), { state: currentState }) };
231
+ }
232
+
233
+ if (!isStage(params.stage)) {
234
+ return { state: currentState, response: toolResult("A valid stage A, B, C, or D is required.", { state: currentState }, true) };
235
+ }
236
+
237
+ const stage = params.stage;
238
+ if (params.action === "select") {
239
+ if (typeof params.target !== "string") {
240
+ return { state: currentState, response: toolResult("Select an exact selector from the current catalog.", { catalog, state: currentState }, true) };
241
+ }
242
+ const selected = catalog[stage].find((item) => item.selector === params.target);
243
+ if (!selected) {
244
+ return { state: currentState, response: toolResult(`Target ${params.target} is not an available Stage ${stage} choice. Refresh the catalog.`, { catalog, state: currentState }, true) };
245
+ }
246
+ const conflict = validateDistinctSelection(currentState, stage, selected);
247
+ if (conflict) return { state: currentState, response: toolResult(conflict, { catalog, state: currentState }, true) };
248
+
249
+ if (selected.access === "runtime-model") {
250
+ const model = port.models.find((item) => `${item.provider}/${item.id}` === selected.selector);
251
+ if (!model) return { state: currentState, response: toolResult("The selected model disappeared; refresh the catalog.", { state: currentState }, true) };
252
+ const activated = await port.activate(model);
253
+ if (activated === false) return { state: currentState, response: toolResult(`Could not activate ${selected.selector}; verify provider authentication.`, { state: currentState }, true) };
254
+ }
255
+
256
+ const workType = Number(params.workType);
257
+ const nextState: ModelRouteState = {
258
+ version: 1,
259
+ workType: Number.isInteger(workType) && workType >= 1 && workType <= 7 ? workType : currentState.workType,
260
+ selections: {
261
+ ...currentState.selections,
262
+ [stage]: { ...selected, selectedAt: port.now?.() ?? new Date().toISOString() },
263
+ },
264
+ };
265
+ port.persist(nextState);
266
+ return {
267
+ state: nextState,
268
+ response: toolResult(
269
+ selected.access === "external-agent"
270
+ ? `Selected ${selected.label} for Stage ${stage}. Obtain post-web approval before creating its session.`
271
+ : `Selected and activated ${selected.label} (${selected.selector}) for Stage ${stage}.`,
272
+ { state: nextState, selected },
273
+ ),
274
+ };
275
+ }
276
+
277
+ const selected = currentState.selections[stage];
278
+ if (!selected) {
279
+ return { state: currentState, response: toolResult(`No model is selected for Stage ${stage}. Run catalog, then select first.`, { state: currentState }, true) };
280
+ }
281
+ if (selected.access === "external-agent") {
282
+ if (!port.hasExternalDevin) return { state: currentState, response: toolResult("Devin was selected but no external Devin tool is available.", { state: currentState }, true) };
283
+ return { state: currentState, response: toolResult(`Stage ${stage} uses an external Devin session. Delegate only after post-web approval.`, { state: currentState, selected }) };
284
+ }
285
+ const model = port.models.find((item) => `${item.provider}/${item.id}` === selected.selector);
286
+ if (!model) return { state: currentState, response: toolResult(`Saved model ${selected.selector} is unavailable. Select a replacement.`, { state: currentState }, true) };
287
+ const activated = await port.activate(model);
288
+ if (activated === false) return { state: currentState, response: toolResult(`Could not activate ${selected.selector}; verify provider authentication.`, { state: currentState }, true) };
289
+ return { state: currentState, response: toolResult(`Activated ${selected.label} (${selected.selector}) for Stage ${stage}.`, { state: currentState, selected }) };
290
+ }
291
+
292
+ function candidate(
293
+ model: ModelLike,
294
+ selector: string,
295
+ choice: ModelCandidate["choice"],
296
+ tier: CandidateTier,
297
+ ): ModelCandidate {
298
+ return {
299
+ selector,
300
+ identity: normalizeModelIdentity(model.id),
301
+ label: model.name?.trim() || model.id,
302
+ provider: model.provider,
303
+ tier,
304
+ choice,
305
+ access: "runtime-model",
306
+ };
307
+ }
308
+
309
+ function isZeroMetered(cost: Record<string, unknown> | undefined): boolean {
310
+ if (!cost) return false;
311
+ const values = Object.values(cost).filter((value): value is number => typeof value === "number");
312
+ return values.length > 0 && values.every((value) => value === 0);
313
+ }
314
+
315
+ function versionParts(candidate: ModelCandidate): number[] {
316
+ return `${candidate.label} ${candidate.selector}`.match(/\d+(?:\.\d+)*/g)?.flatMap((part) => part.split(".").map(Number)) ?? [];
317
+ }
318
+
319
+ function compareCandidates(left: ModelCandidate, right: ModelCandidate): number {
320
+ if (left.choice !== right.choice) return left.choice.localeCompare(right.choice);
321
+ const a = versionParts(left);
322
+ const b = versionParts(right);
323
+ const length = Math.max(a.length, b.length);
324
+ for (let index = 0; index < length; index += 1) {
325
+ const difference = (b[index] ?? 0) - (a[index] ?? 0);
326
+ if (difference !== 0) return difference;
327
+ }
328
+ return left.selector.localeCompare(right.selector);
329
+ }
330
+
331
+ function toolResult(text: string, details: unknown, isError = false) {
332
+ return {
333
+ content: [{ type: "text" as const, text }],
334
+ details,
335
+ ...(isError ? { isError: true } : {}),
336
+ };
337
+ }
338
+
339
+ function catalogNotes(runtimeName: RuntimeName, catalog: Record<AutoStage, ModelCandidate[]>): string[] {
340
+ const notes = [
341
+ `The catalog is built from ${runtimeName}'s authenticated model registry at call time; selectors are never guessed or pinned.`,
342
+ "A and C use frontier candidates. B and D use execution candidates, including eligible Cursor or Devin routes when the runtime exposes them.",
343
+ "Zero-metered describes the runtime's reported token price; it does not promise unlimited subscription usage.",
344
+ ];
345
+ if (catalog.A.length === 0) notes.push("No frontier matches are authenticated. Connect a provider, then refresh.");
346
+ if (catalog.B.length === 0) notes.push("No execution matches are authenticated and no external Devin tool was detected.");
347
+ return notes;
348
+ }
@@ -0,0 +1,77 @@
1
+ import type { RuntimeName } from "./model-routing";
2
+
3
+ export interface RuntimeCapabilityReport {
4
+ runtime: RuntimeName;
5
+ tools: Record<string, boolean>;
6
+ commands: Record<string, boolean>;
7
+ memory: {
8
+ configured: boolean;
9
+ recall: boolean;
10
+ retain: boolean;
11
+ reflect: boolean;
12
+ learn: boolean;
13
+ };
14
+ notes: string[];
15
+ }
16
+
17
+ export const TOOL_CAPABILITIES = [
18
+ "codegraph_explore",
19
+ "lsp",
20
+ "ast_grep",
21
+ "ast_edit",
22
+ "task",
23
+ "checkpoint",
24
+ "rewind",
25
+ "debug",
26
+ "security_scan",
27
+ "eval",
28
+ "browser",
29
+ "computer",
30
+ "recall",
31
+ "retain",
32
+ "reflect",
33
+ "learn",
34
+ ] as const;
35
+
36
+ export const COMMAND_CAPABILITIES = ["advisor", "review", "memory", "collab", "share"] as const;
37
+
38
+ export function normalizeCapabilityNames(items: unknown[]): string[] {
39
+ return items
40
+ .map((item: any) => (typeof item === "string" ? item : String(item?.name ?? "")))
41
+ .filter(Boolean);
42
+ }
43
+
44
+ export function buildRuntimeCapabilityReport(
45
+ runtime: RuntimeName,
46
+ toolNames: string[],
47
+ commandNames: string[],
48
+ memoryConfigured: boolean,
49
+ ): RuntimeCapabilityReport {
50
+ const tools = new Set(toolNames.map((name) => name.trim().toLowerCase()));
51
+ const commands = new Set(commandNames.map((name) => name.trim().toLowerCase().replace(/^\//, "")));
52
+ const toolStatus = Object.fromEntries(TOOL_CAPABILITIES.map((name) => [name, tools.has(name)])) as Record<string, boolean>;
53
+ const commandStatus = Object.fromEntries(COMMAND_CAPABILITIES.map((name) => [name, commands.has(name)])) as Record<string, boolean>;
54
+ const notes: string[] = [];
55
+
56
+ if (!toolStatus.codegraph_explore) notes.push("CodeGraph tool integration is unavailable. Use an installed CodeGraph CLI through bash, or pause repository-wide architecture claims.");
57
+ if (!toolStatus.lsp) notes.push("Native LSP is unavailable. Use repository-native language tooling and label exact symbol analysis as limited.");
58
+ if (!toolStatus.checkpoint || !toolStatus.rewind) notes.push("Checkpoint/rewind is unavailable. Preserve decisions in the Markdown ledger and use normal compaction when needed.");
59
+ if (!toolStatus.security_scan) notes.push("Native security_scan is unavailable. Use repository-native scanners plus an independent read-only security review.");
60
+ if (!memoryConfigured) notes.push("No approved persistent-memory integration was detected. Write lessons.md locally and leave retention off.");
61
+ if (!commandStatus.advisor) notes.push(runtime === "Pi" ? "OMP Advisor is not native to Pi. Keep the required independent C/D stages instead." : "Advisor is unavailable. Keep the required independent C/D stages.");
62
+ if (!commandStatus.review) notes.push("Native review command is unavailable. Run the recorded C/D review contract directly.");
63
+
64
+ return {
65
+ runtime,
66
+ tools: toolStatus,
67
+ commands: commandStatus,
68
+ memory: {
69
+ configured: memoryConfigured,
70
+ recall: toolStatus.recall,
71
+ retain: toolStatus.retain,
72
+ reflect: toolStatus.reflect,
73
+ learn: toolStatus.learn,
74
+ },
75
+ notes,
76
+ };
77
+ }
@@ -0,0 +1,3 @@
1
+ export const UNIVERSAL_WORKFLOW_POLICY = `PiMarg is active.
2
+
3
+ For each new substantive request, load and follow the auto-mode-router skill before solution work. Infer the likely intent first; confirm or ask only for missing choices that materially change the route. Establish job/client versus personal context, classify one of the seven work types, choose a dedicated skill spine or Option 4, discover live model choices, and enforce A different from C plus B different from D. Resume an active session for ordinary follow-ups instead of restarting intake. Keep the Markdown plan, decisions, evidence, and one session lesson. Use CodeGraph first for repository-wide architecture and impact when available. Never bypass PiMarg protected-action approvals.`;
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@sangyaahub/pi-marg",
3
+ "version": "1.0.0",
4
+ "description": "A governed, model-routed development path for Pi Agent and Oh My Pi.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Sangyaa",
8
+ "homepage": "https://github.com/sangyaahub/pi-marg#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/sangyaahub/pi-marg.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/sangyaahub/pi-marg/issues"
15
+ },
16
+ "keywords": [
17
+ "pi-package",
18
+ "omp-plugin",
19
+ "pi-agent",
20
+ "oh-my-pi",
21
+ "agent-workflow",
22
+ "model-routing",
23
+ "approval-gates",
24
+ "multi-agent",
25
+ "sangyaa"
26
+ ],
27
+ "files": [
28
+ ".mcp.json",
29
+ ".omp-plugin",
30
+ "BRAND.md",
31
+ "CHANGELOG.md",
32
+ "CODE_OF_CONDUCT.md",
33
+ "CONTRIBUTING.md",
34
+ "LICENSE",
35
+ "README.md",
36
+ "MODEL-SETUP.md",
37
+ "PUBLISHING.md",
38
+ "SECURITY.md",
39
+ "SUPPORT.md",
40
+ "adapters",
41
+ "agents",
42
+ "assets",
43
+ "commands",
44
+ "core",
45
+ "rules",
46
+ "scripts",
47
+ "skills"
48
+ ],
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "engines": {
53
+ "node": ">=20"
54
+ },
55
+ "scripts": {
56
+ "test": "bun test tests",
57
+ "validate": "./scripts/validate.sh"
58
+ },
59
+ "pi": {
60
+ "extensions": [
61
+ "./adapters/pi/extensions/approval-guard.ts",
62
+ "./adapters/pi/extensions/model-router.ts",
63
+ "./adapters/pi/extensions/runtime-capabilities.ts",
64
+ "./adapters/pi/extensions/workflow-bootstrap.ts"
65
+ ],
66
+ "skills": [
67
+ "./skills"
68
+ ],
69
+ "prompts": [
70
+ "./adapters/pi/prompts"
71
+ ],
72
+ "image": "https://raw.githubusercontent.com/sangyaahub/pi-marg/main/assets/pi-marg-banner.png"
73
+ },
74
+ "omp": {
75
+ "extensions": [
76
+ "./adapters/omp/extensions/approval-guard.ts",
77
+ "./adapters/omp/extensions/model-router.ts",
78
+ "./adapters/omp/extensions/runtime-capabilities.ts"
79
+ ]
80
+ },
81
+ "peerDependencies": {
82
+ "typebox": "*"
83
+ }
84
+ }
@@ -0,0 +1,32 @@
1
+ ---
2
+ description: Start and enforce PiMarg for each new substantive task.
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # PiMarg global rule
7
+
8
+ For every new substantive user task, load and follow the `auto-mode-router` skill before doing the work.
9
+
10
+ - Do not restart intake for acknowledgements, clarifications, approval replies, or follow-ups that belong to the active task.
11
+ - If the conversation already contains an active Auto Mode session record, reuse it. Start a new intake only when the user starts a materially different task or invokes `/auto-reset`.
12
+ - The first intake question is always whether the work is for a job/client or personal.
13
+ - Confirm one of work types 1–7, then confirm skill mode 1–4 before execution.
14
+ - After skill mode, use the live `auto_model_route` catalog and record required A/B/C/D choices. A and C must use different underlying models; B and D must use different underlying models.
15
+ - Activate the saved model before each A/B/C/D stage. Never guess or pin a version absent from the active Pi or OMP registry.
16
+ - Call `auto_runtime_status` during intake and route only through capabilities it reports available.
17
+ - Use exactly one primary workflow spine for the task. Do not let multiple skill families maintain competing plans or state.
18
+ - For work types 2–6, load `codegraph-first` and require a healthy initialized CodeGraph before repository-wide architecture or impact claims. Use detected LSP/AST or repository-native tools for precise questions. Never conceal missing graph coverage behind a broad grep/glob crawl.
19
+ - Keep C and D as independent recorded review stages. A detected advisor may watch continuously, but it cannot approve actions or replace the final C/D verdict.
20
+ - Use OMP `workflowz`/`orchestrate` or a detected `task` fan-out only when work is genuinely independent. Isolate parallel writes, integrate them into one canonical tree, and verify again before D.
21
+ - Run conditional verification after B: repository checks always, DAP debugging for supported runtime defects, browser/desktop QA for affected user flows, and security review for type 5 or high-risk changes.
22
+ - Use checkpoint/rewind only when the runtime exposes those tools with the documented disposable-investigation semantics. They are not filesystem checkpoints or stage-completion markers.
23
+ - Ponytail, Hindsight, and language skills are supporting add-ons only. Verify they are discovered, isolate job/client memory, and never let them create a competing plan.
24
+ - Prefer the runtime's detected memory integration. Recalled memory is heuristic; job/client retention stays off until policy permits it.
25
+ - Track the plan, decisions, evidence, approval events, and lessons status in Markdown as defined by the router.
26
+ - For job/client work, never add Sangyaa branding, names, logos, metadata, promotional copy, or footers unless the user explicitly approves that exact artifact. Preserve the existing client/employer brand.
27
+ - For personal work, do not add Sangyaa branding automatically. Use it only when requested or already established by the project.
28
+ - Never claim an unavailable skill, model, plugin, or tool was used. Offer a compatible route when a requested family is unavailable.
29
+ - Before final completion, run `session-lessons` exactly once for the active Auto Mode session and record it in the session ledger.
30
+ - After review passes, offer a local commit through the runtime or repository-native path. Never combine that step with push or merge.
31
+ - Keep `/collab` and `/share` off by default when present. Both require one-use `post-web` approval; job/client sharing should default to view-only.
32
+ - Delete/remove actions, pushing to main, merging a PR, and posting or publishing to a web platform require explicit one-use user approval. The runtime guard is authoritative when it blocks a tool.
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ found_runtime="false"
5
+
6
+ if command -v pi >/dev/null 2>&1; then
7
+ echo "[ok] Pi: $(pi --version 2>/dev/null | head -n 1)"
8
+ found_runtime="true"
9
+ else
10
+ echo "[optional] Pi is not on PATH"
11
+ fi
12
+
13
+ if command -v omp >/dev/null 2>&1; then
14
+ echo "[ok] OMP: $(omp --version 2>/dev/null | head -n 1)"
15
+ found_runtime="true"
16
+ else
17
+ echo "[optional] OMP is not on PATH"
18
+ fi
19
+
20
+ if [[ "${found_runtime}" != "true" ]]; then
21
+ echo "Install Pi or OMP before installing PiMarg." >&2
22
+ exit 1
23
+ fi
24
+
25
+ if command -v codegraph >/dev/null 2>&1; then
26
+ echo "[ok] CodeGraph CLI is available"
27
+ else
28
+ echo "[optional] CodeGraph is missing; use setup-codegraph.sh for existing repositories"
29
+ fi
30
+
31
+ for tool in git bun; do
32
+ if command -v "${tool}" >/dev/null 2>&1; then
33
+ echo "[ok] ${tool} is available"
34
+ else
35
+ echo "[optional] ${tool} is not on PATH"
36
+ fi
37
+ done
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ runtime=""
5
+ mode="dry-run"
6
+
7
+ usage() {
8
+ echo "Usage: $0 --runtime <pi|omp|both> [--apply]" >&2
9
+ }
10
+
11
+ while [[ $# -gt 0 ]]; do
12
+ case "$1" in
13
+ --runtime)
14
+ [[ $# -ge 2 ]] || { usage; exit 2; }
15
+ runtime="$2"
16
+ shift 2
17
+ ;;
18
+ --apply)
19
+ mode="apply"
20
+ shift
21
+ ;;
22
+ -h|--help)
23
+ usage
24
+ exit 0
25
+ ;;
26
+ *)
27
+ echo "Unknown argument: $1" >&2
28
+ usage
29
+ exit 2
30
+ ;;
31
+ esac
32
+ done
33
+
34
+ case "${runtime}" in
35
+ pi|omp|both) ;;
36
+ *)
37
+ echo "--runtime must be pi, omp, or both." >&2
38
+ usage
39
+ exit 2
40
+ ;;
41
+ esac
42
+
43
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
44
+ package_root="$(cd "${script_dir}/.." && pwd)"
45
+
46
+ print_pi() {
47
+ echo "[Pi] pi install ${package_root}"
48
+ }
49
+
50
+ print_omp() {
51
+ echo "[OMP] omp plugin marketplace add ${package_root}"
52
+ echo "[OMP] omp plugin install --scope user pi-marg@pi-marg-marketplace"
53
+ }
54
+
55
+ if [[ "${mode}" == "dry-run" ]]; then
56
+ echo "Preview only; no configuration will be changed."
57
+ [[ "${runtime}" == "pi" || "${runtime}" == "both" ]] && print_pi
58
+ [[ "${runtime}" == "omp" || "${runtime}" == "both" ]] && print_omp
59
+ echo "Run again with --apply to install."
60
+ exit 0
61
+ fi
62
+
63
+ if [[ "${runtime}" == "pi" || "${runtime}" == "both" ]]; then
64
+ command -v pi >/dev/null 2>&1 || { echo "Pi is not available on PATH." >&2; exit 1; }
65
+ pi install "${package_root}"
66
+ fi
67
+
68
+ if [[ "${runtime}" == "omp" || "${runtime}" == "both" ]]; then
69
+ command -v omp >/dev/null 2>&1 || { echo "OMP is not available on PATH." >&2; exit 1; }
70
+ omp plugin marketplace add "${package_root}"
71
+ omp plugin install --scope user pi-marg@pi-marg-marketplace
72
+ fi
73
+
74
+ echo "PiMarg v1.0.0 installed for ${runtime}. Start a fresh runtime session, then run /auto-runtime status."