pi-para 0.6.0 → 0.6.1

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 (208) hide show
  1. package/ARCHITECTURE.md +0 -9
  2. package/CHANGELOG.md +13 -0
  3. package/CONFIGURATION.md +0 -23
  4. package/README.md +1 -29
  5. package/SETUP.md +1 -1
  6. package/STRUCTURE.md +0 -9
  7. package/dist/atomic-write.d.ts +2 -0
  8. package/dist/atomic-write.d.ts.map +1 -0
  9. package/dist/atomic-write.js +18 -0
  10. package/dist/atomic-write.js.map +1 -0
  11. package/dist/capture.d.ts +34 -0
  12. package/dist/capture.d.ts.map +1 -0
  13. package/dist/capture.js +461 -0
  14. package/dist/capture.js.map +1 -0
  15. package/dist/cli-legacy.d.ts +4 -0
  16. package/dist/cli-legacy.d.ts.map +1 -0
  17. package/dist/cli-legacy.js +41 -0
  18. package/dist/cli-legacy.js.map +1 -0
  19. package/dist/cli.d.ts +8 -0
  20. package/dist/cli.d.ts.map +1 -0
  21. package/dist/cli.js +427 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/commands.d.ts +15 -0
  24. package/dist/commands.d.ts.map +1 -0
  25. package/dist/commands.js +719 -0
  26. package/dist/commands.js.map +1 -0
  27. package/dist/config.d.ts +94 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +228 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/context.d.ts +63 -0
  32. package/dist/context.d.ts.map +1 -0
  33. package/dist/context.js +311 -0
  34. package/dist/context.js.map +1 -0
  35. package/dist/credentials.d.ts +27 -0
  36. package/dist/credentials.d.ts.map +1 -0
  37. package/dist/credentials.js +85 -0
  38. package/dist/credentials.js.map +1 -0
  39. package/dist/daemon.d.ts +49 -0
  40. package/dist/daemon.d.ts.map +1 -0
  41. package/dist/daemon.js +226 -0
  42. package/dist/daemon.js.map +1 -0
  43. package/dist/doctor.d.ts +20 -0
  44. package/dist/doctor.d.ts.map +1 -0
  45. package/dist/doctor.js +176 -0
  46. package/dist/doctor.js.map +1 -0
  47. package/dist/frontmatter.d.ts +59 -0
  48. package/dist/frontmatter.d.ts.map +1 -0
  49. package/dist/frontmatter.js +170 -0
  50. package/dist/frontmatter.js.map +1 -0
  51. package/dist/index.d.ts +21 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +342 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/ingest.d.ts +88 -0
  56. package/dist/ingest.d.ts.map +1 -0
  57. package/dist/ingest.js +105 -0
  58. package/dist/ingest.js.map +1 -0
  59. package/dist/link-discovery.d.ts +40 -0
  60. package/dist/link-discovery.d.ts.map +1 -0
  61. package/dist/link-discovery.js +122 -0
  62. package/dist/link-discovery.js.map +1 -0
  63. package/dist/link-utils.d.ts +30 -0
  64. package/dist/link-utils.d.ts.map +1 -0
  65. package/dist/link-utils.js +145 -0
  66. package/dist/link-utils.js.map +1 -0
  67. package/dist/lint.d.ts +39 -0
  68. package/dist/lint.d.ts.map +1 -0
  69. package/dist/lint.js +827 -0
  70. package/dist/lint.js.map +1 -0
  71. package/dist/maintainer.d.ts +24 -0
  72. package/dist/maintainer.d.ts.map +1 -0
  73. package/dist/maintainer.js +358 -0
  74. package/dist/maintainer.js.map +1 -0
  75. package/dist/model-resolver.d.ts +30 -0
  76. package/dist/model-resolver.d.ts.map +1 -0
  77. package/dist/model-resolver.js +71 -0
  78. package/dist/model-resolver.js.map +1 -0
  79. package/dist/paths.d.ts +20 -0
  80. package/dist/paths.d.ts.map +1 -0
  81. package/dist/paths.js +33 -0
  82. package/dist/paths.js.map +1 -0
  83. package/dist/processor.d.ts +14 -0
  84. package/dist/processor.d.ts.map +1 -0
  85. package/dist/processor.js +361 -0
  86. package/dist/processor.js.map +1 -0
  87. package/dist/qmd-providers.d.ts +12 -0
  88. package/dist/qmd-providers.d.ts.map +1 -0
  89. package/dist/qmd-providers.js +45 -0
  90. package/dist/qmd-providers.js.map +1 -0
  91. package/dist/query.d.ts +69 -0
  92. package/dist/query.d.ts.map +1 -0
  93. package/dist/query.js +120 -0
  94. package/dist/query.js.map +1 -0
  95. package/dist/raw.d.ts +57 -0
  96. package/dist/raw.d.ts.map +1 -0
  97. package/dist/raw.js +219 -0
  98. package/dist/raw.js.map +1 -0
  99. package/dist/redact.d.ts +21 -0
  100. package/dist/redact.d.ts.map +1 -0
  101. package/dist/redact.js +143 -0
  102. package/dist/redact.js.map +1 -0
  103. package/dist/repair.d.ts +11 -0
  104. package/dist/repair.d.ts.map +1 -0
  105. package/dist/repair.js +41 -0
  106. package/dist/repair.js.map +1 -0
  107. package/dist/scheduler/controls.d.ts +35 -0
  108. package/dist/scheduler/controls.d.ts.map +1 -0
  109. package/dist/scheduler/controls.js +98 -0
  110. package/dist/scheduler/controls.js.map +1 -0
  111. package/dist/scheduler/index.d.ts +48 -0
  112. package/dist/scheduler/index.d.ts.map +1 -0
  113. package/dist/scheduler/index.js +193 -0
  114. package/dist/scheduler/index.js.map +1 -0
  115. package/dist/scheduler/leases.d.ts +22 -0
  116. package/dist/scheduler/leases.d.ts.map +1 -0
  117. package/dist/scheduler/leases.js +74 -0
  118. package/dist/scheduler/leases.js.map +1 -0
  119. package/dist/scheduler/session-capture.d.ts +33 -0
  120. package/dist/scheduler/session-capture.d.ts.map +1 -0
  121. package/dist/scheduler/session-capture.js +108 -0
  122. package/dist/scheduler/session-capture.js.map +1 -0
  123. package/dist/scheduler/state.d.ts +56 -0
  124. package/dist/scheduler/state.d.ts.map +1 -0
  125. package/dist/scheduler/state.js +210 -0
  126. package/dist/scheduler/state.js.map +1 -0
  127. package/dist/scope.d.ts +54 -0
  128. package/dist/scope.d.ts.map +1 -0
  129. package/dist/scope.js +161 -0
  130. package/dist/scope.js.map +1 -0
  131. package/dist/scripts/bulk-link-fix.d.ts +9 -0
  132. package/dist/scripts/bulk-link-fix.d.ts.map +1 -0
  133. package/dist/scripts/bulk-link-fix.js +106 -0
  134. package/dist/scripts/bulk-link-fix.js.map +1 -0
  135. package/dist/session-tools.d.ts +33 -0
  136. package/dist/session-tools.d.ts.map +1 -0
  137. package/dist/session-tools.js +211 -0
  138. package/dist/session-tools.js.map +1 -0
  139. package/dist/settings.d.ts +19 -0
  140. package/dist/settings.d.ts.map +1 -0
  141. package/dist/settings.js +106 -0
  142. package/dist/settings.js.map +1 -0
  143. package/dist/setup.d.ts +29 -0
  144. package/dist/setup.d.ts.map +1 -0
  145. package/dist/setup.js +90 -0
  146. package/dist/setup.js.map +1 -0
  147. package/dist/state.d.ts +54 -0
  148. package/dist/state.d.ts.map +1 -0
  149. package/dist/state.js +176 -0
  150. package/dist/state.js.map +1 -0
  151. package/dist/status.d.ts +22 -0
  152. package/dist/status.d.ts.map +1 -0
  153. package/dist/status.js +93 -0
  154. package/dist/status.js.map +1 -0
  155. package/dist/store.d.ts +71 -0
  156. package/dist/store.d.ts.map +1 -0
  157. package/dist/store.js +282 -0
  158. package/dist/store.js.map +1 -0
  159. package/dist/summarize.d.ts +60 -0
  160. package/dist/summarize.d.ts.map +1 -0
  161. package/dist/summarize.js +266 -0
  162. package/dist/summarize.js.map +1 -0
  163. package/dist/tag-registry.d.ts +56 -0
  164. package/dist/tag-registry.d.ts.map +1 -0
  165. package/dist/tag-registry.js +146 -0
  166. package/dist/tag-registry.js.map +1 -0
  167. package/dist/templates/prompts.d.ts +51 -0
  168. package/dist/templates/prompts.d.ts.map +1 -0
  169. package/dist/templates/prompts.js +203 -0
  170. package/dist/templates/prompts.js.map +1 -0
  171. package/dist/tools.d.ts +26 -0
  172. package/dist/tools.d.ts.map +1 -0
  173. package/dist/tools.js +1198 -0
  174. package/dist/tools.js.map +1 -0
  175. package/dist/watcher.d.ts +25 -0
  176. package/dist/watcher.d.ts.map +1 -0
  177. package/dist/watcher.js +84 -0
  178. package/dist/watcher.js.map +1 -0
  179. package/dist/webui/client/dist/index.html +1885 -0
  180. package/dist/webui/server.d.ts +31 -0
  181. package/dist/webui/server.d.ts.map +1 -0
  182. package/dist/webui/server.js +566 -0
  183. package/dist/webui/server.js.map +1 -0
  184. package/dist/wiki-tool-guidance.d.ts +6 -0
  185. package/dist/wiki-tool-guidance.d.ts.map +1 -0
  186. package/dist/wiki-tool-guidance.js +70 -0
  187. package/dist/wiki-tool-guidance.js.map +1 -0
  188. package/dist/wiki.d.ts +108 -0
  189. package/dist/wiki.d.ts.map +1 -0
  190. package/dist/wiki.js +497 -0
  191. package/dist/wiki.js.map +1 -0
  192. package/package.json +2 -1
  193. package/scripts/build-assets.mjs +1 -13
  194. package/skills/setup/SKILL.md +1 -1
  195. package/src/cli.ts +0 -70
  196. package/src/config.ts +0 -25
  197. package/src/index.ts +1 -38
  198. package/src/templates/prompts.ts +0 -56
  199. package/scripts/gepa/dataset.py +0 -192
  200. package/scripts/gepa/lm_providers.py +0 -472
  201. package/scripts/gepa/metric.py +0 -279
  202. package/scripts/gepa/optimize.py +0 -367
  203. package/scripts/gepa/program.py +0 -99
  204. package/scripts/gepa/pyproject.toml +0 -19
  205. package/scripts/gepa/run-all.sh +0 -53
  206. package/scripts/gepa/wiki_reader.py +0 -113
  207. package/src/gepa/index.ts +0 -287
  208. package/src/gepa/optimized/capture-prompt.txt +0 -65
@@ -1,99 +0,0 @@
1
- """DSPy Module proxy for prompt optimization.
2
-
3
- Each optimization target is wrapped in a lightweight proxy module.
4
- The Signature's docstring (instruction) IS the prompt being optimized.
5
- GEPA evolves the instruction to maximize the metric.
6
-
7
- One LLM call per eval (not 10-50 like the full agent loop).
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- import dspy
13
-
14
-
15
- class WikiTaskOutput(dspy.Signature):
16
- """You are a wiki knowledge management agent for a PARA-structured knowledge base."""
17
-
18
- task_context: str = dspy.InputField(
19
- desc="Context describing the wiki task to perform"
20
- )
21
- output: str = dspy.OutputField(
22
- desc="Structured wiki output following PARA conventions: "
23
- "Topic, Key Facts, Insights, Connections with [[wikilinks]], Sources"
24
- )
25
-
26
-
27
- class PromptProxy(dspy.Module):
28
- """Lightweight proxy for prompt/instruction optimization.
29
-
30
- The Signature's instruction text is what GEPA evolves.
31
- forward() runs a single dspy.Predict call (1 LLM call).
32
-
33
- Usage:
34
- proxy = PromptProxy.from_instruction(current_prompt_text)
35
- # GEPA optimizes proxy → evolved instruction = optimized prompt
36
- """
37
-
38
- def __init__(self, instruction: str | None = None):
39
- super().__init__()
40
-
41
- if instruction:
42
- # Create a signature class with the instruction as docstring
43
- class CustomSig(WikiTaskOutput):
44
- pass
45
- CustomSig.__doc__ = instruction
46
- self.predict = dspy.Predict(CustomSig)
47
- else:
48
- self.predict = dspy.Predict(WikiTaskOutput)
49
-
50
- def forward(self, task_context: str) -> dspy.Prediction:
51
- return self.predict(task_context=task_context)
52
-
53
- @classmethod
54
- def from_instruction(cls, instruction: str) -> "PromptProxy":
55
- """Create a proxy from a prompt/instruction string."""
56
- return cls(instruction=instruction)
57
-
58
- def get_instruction(self) -> str | None:
59
- """Get the current instruction text."""
60
- sig = getattr(self.predict, "signature", None)
61
- if sig:
62
- inst = getattr(sig, "instructions", None)
63
- if inst and not inst.startswith("str(object"):
64
- return inst
65
- doc = getattr(sig, "__doc__", None)
66
- if doc and not doc.startswith("str(object"):
67
- return doc
68
- return None
69
-
70
-
71
- def extract_evolved_instruction(optimized_proxy: PromptProxy) -> str | None:
72
- """Extract the GEPA-evolved instruction from an optimized proxy.
73
-
74
- After GEPA optimization, the proxy's Predict signature contains
75
- the evolved instruction text. This extracts it.
76
- """
77
- # Check predict.signature first
78
- if hasattr(optimized_proxy, "predict"):
79
- pred = optimized_proxy.predict
80
- for attr_name in ("extended_signature", "signature"):
81
- sig = getattr(pred, attr_name, None)
82
- if sig:
83
- for field in ("instructions", "__doc__"):
84
- text = getattr(sig, field, None)
85
- if text and len(text) > 50 and not text.startswith("str(object"):
86
- return text
87
-
88
- # Fallback to named_predictors
89
- try:
90
- for name, predictor in optimized_proxy.named_predictors():
91
- sig = getattr(predictor, "signature", None)
92
- if sig:
93
- inst = getattr(sig, "instructions", None) or getattr(sig, "__doc__", None)
94
- if inst and len(inst) > 50 and not inst.startswith("str(object"):
95
- return inst
96
- except Exception:
97
- pass
98
-
99
- return None
@@ -1,19 +0,0 @@
1
- [project]
2
- name = "pi-para-gepa"
3
- version = "0.1.0"
4
- description = "GEPA optimizer for pi-para prompts, tools, and skills"
5
- requires-python = ">=3.11"
6
- dependencies = [
7
- "dspy>=2.6.0",
8
- "anthropic>=0.52.0",
9
- "openai>=1.30.0",
10
- "pyyaml>=6.0.0",
11
- ]
12
-
13
- [tool.hatch.build.targets.wheel]
14
- packages = ["lm_providers", "wiki_reader", "dataset", "program", "metric", "optimize"]
15
- include = ["*.py"]
16
-
17
- [build-system]
18
- requires = ["hatchling"]
19
- build-backend = "hatchling.build"
@@ -1,53 +0,0 @@
1
- #!/bin/bash
2
- # Run GEPA optimization one target at a time.
3
- # Default: Sonnet for task/judge (fast, cheap), Opus for reflection (smart mutations).
4
- # Each target invocation re-reads auth.json for fresh token.
5
- set -e
6
-
7
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
8
- PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
9
- MODEL="${1:-anthropic/claude-sonnet-4-20250514}"
10
- REFLECTION_MODEL="${2:-anthropic/claude-opus-4-6}"
11
- AUTO="${3:-light}"
12
-
13
- echo "[gepa-all] Model: $MODEL | Reflection: $REFLECTION_MODEL | Budget: $AUTO"
14
- echo "[gepa-all] Started at $(date)"
15
-
16
- # Get target list
17
- TARGETS=$(cd "$PROJECT_DIR" && node dist/cli.js gepa targets 2>/dev/null | awk '{print $1}')
18
-
19
- TOTAL=$(echo "$TARGETS" | wc -l)
20
- CURRENT=0
21
- SUCCEEDED=0
22
- FAILED=0
23
-
24
- for TARGET in $TARGETS; do
25
- CURRENT=$((CURRENT + 1))
26
- echo ""
27
- echo "================================================================"
28
- echo "[gepa-all] [$CURRENT/$TOTAL] Optimizing: $TARGET"
29
- echo "[gepa-all] $(date)"
30
- echo "================================================================"
31
-
32
- cd "$PROJECT_DIR"
33
- if node dist/cli.js gepa optimize \
34
- --target "$TARGET" \
35
- --model "$MODEL" \
36
- --reflection-model "$REFLECTION_MODEL" \
37
- --auto "$AUTO" 2>&1; then
38
- SUCCEEDED=$((SUCCEEDED + 1))
39
- echo "[gepa-all] ✅ $TARGET done"
40
- else
41
- FAILED=$((FAILED + 1))
42
- echo "[gepa-all] ❌ $TARGET failed"
43
- fi
44
-
45
- # Brief pause between targets to avoid rate limits
46
- sleep 5
47
- done
48
-
49
- echo ""
50
- echo "================================================================"
51
- echo "[gepa-all] ALL DONE at $(date)"
52
- echo "[gepa-all] Succeeded: $SUCCEEDED / $TOTAL, Failed: $FAILED"
53
- echo "================================================================"
@@ -1,113 +0,0 @@
1
- """Read wiki pages from disk for training data.
2
-
3
- Parses YAML frontmatter + markdown body from ~/.pi/wiki/{category}/{slug}.md.
4
- """
5
-
6
- from __future__ import annotations
7
-
8
- import os
9
- import re
10
- from dataclasses import dataclass, field
11
- from pathlib import Path
12
-
13
- import yaml
14
-
15
-
16
- PARA_CATEGORIES = ("projects", "areas", "resources", "archives")
17
-
18
-
19
- @dataclass
20
- class WikiPage:
21
- """A parsed wiki page."""
22
- category: str
23
- slug: str
24
- title: str
25
- scope: list[str] = field(default_factory=list)
26
- tags: list[str] = field(default_factory=list)
27
- links: list[str] = field(default_factory=list)
28
- body: str = ""
29
- created: str = ""
30
- updated: str = ""
31
-
32
- @property
33
- def path(self) -> str:
34
- return f"{self.category}/{self.slug}"
35
-
36
-
37
- def parse_page(filepath: str) -> WikiPage | None:
38
- """Parse a wiki markdown file into a WikiPage."""
39
- try:
40
- with open(filepath, "r", encoding="utf-8") as f:
41
- content = f.read()
42
- except (IOError, OSError):
43
- return None
44
-
45
- # Split frontmatter from body
46
- fm_match = re.match(r"^---\s*\n(.*?\n)---\s*\n(.*)", content, re.DOTALL)
47
- if not fm_match:
48
- return None
49
-
50
- try:
51
- fm = yaml.safe_load(fm_match.group(1))
52
- except yaml.YAMLError:
53
- return None
54
-
55
- if not isinstance(fm, dict):
56
- return None
57
-
58
- body = fm_match.group(2).strip()
59
- path = Path(filepath)
60
- slug = path.stem
61
- category = path.parent.name
62
-
63
- return WikiPage(
64
- category=category,
65
- slug=slug,
66
- title=fm.get("title", slug),
67
- scope=fm.get("scope", []) or [],
68
- tags=fm.get("tags", []) or [],
69
- links=fm.get("links", []) or [],
70
- body=body,
71
- created=str(fm.get("created", "")),
72
- updated=str(fm.get("updated", "")),
73
- )
74
-
75
-
76
- def load_all_pages(wiki_dir: str) -> list[WikiPage]:
77
- """Load all wiki pages from disk."""
78
- pages = []
79
- for cat in PARA_CATEGORIES:
80
- cat_dir = os.path.join(wiki_dir, cat)
81
- if not os.path.isdir(cat_dir):
82
- continue
83
- for fname in sorted(os.listdir(cat_dir)):
84
- if not fname.endswith(".md"):
85
- continue
86
- page = parse_page(os.path.join(cat_dir, fname))
87
- if page:
88
- pages.append(page)
89
- return pages
90
-
91
-
92
- def get_wiki_stats(wiki_dir: str) -> dict:
93
- """Get summary stats about the wiki."""
94
- pages = load_all_pages(wiki_dir)
95
- cat_counts = {}
96
- all_scopes = set()
97
- all_tags = set()
98
- total_links = 0
99
-
100
- for p in pages:
101
- cat_counts[p.category] = cat_counts.get(p.category, 0) + 1
102
- all_scopes.update(p.scope)
103
- all_tags.update(p.tags)
104
- total_links += len(p.links)
105
-
106
- return {
107
- "total_pages": len(pages),
108
- "categories": cat_counts,
109
- "unique_scopes": sorted(all_scopes),
110
- "unique_tags": sorted(all_tags)[:20], # top 20
111
- "total_links": total_links,
112
- "avg_links_per_page": round(total_links / max(len(pages), 1), 1),
113
- }
package/src/gepa/index.ts DELETED
@@ -1,287 +0,0 @@
1
- /**
2
- * GEPA optimizer orchestrator — TypeScript side.
3
- *
4
- * 1. Extracts all prompt/tool/skill instruction texts from source
5
- * 2. Writes targets.json for the Python DSPy GEPA script
6
- * 3. Reads auth.json → passes API keys as env vars to uv subprocess
7
- * 4. Calls `uv run scripts/gepa/optimize.py`
8
- * 5. Reads results.json → saves optimized prompts to disk
9
- */
10
-
11
- import { spawn } from "node:child_process";
12
- import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
13
- import { homedir } from "node:os";
14
- import { dirname, join, resolve } from "node:path";
15
-
16
- // -- Types -------------------------------------------------------------------
17
-
18
- interface GEPATarget {
19
- name: string;
20
- type: "prompt" | "tool" | "skill";
21
- content: string;
22
- sourceFile: string;
23
- }
24
-
25
- interface GEPAResult {
26
- target: string;
27
- status: "success" | "error";
28
- baseline_score?: number;
29
- optimized_score?: number;
30
- improvement_pct?: number;
31
- optimized_instruction?: string;
32
- original_instruction?: string;
33
- model?: string;
34
- auto?: string;
35
- error?: string;
36
- timestamp?: string;
37
- }
38
-
39
- interface GEPAOutput {
40
- results: GEPAResult[];
41
- model: string;
42
- auto: string;
43
- wiki_dir: string;
44
- timestamp: string;
45
- }
46
-
47
- export interface GEPAOptions {
48
- target?: string;
49
- /** Student model — runs the proxy (fast, cheap). */
50
- studentModel?: string;
51
- /** Teacher/reflection model — proposes mutations (smart, creative). */
52
- teacherModel?: string;
53
- /** Judge model — scores outputs (fast, cheap). Default: same as student. */
54
- judgeModel?: string;
55
- /** Shorthand: sets studentModel (backward compat with --model flag) */
56
- model?: string;
57
- /** Shorthand: sets teacherModel (backward compat with --reflection-model flag) */
58
- reflectionModel?: string;
59
- auto?: "light" | "medium" | "heavy";
60
- maxMetricCalls?: number;
61
- threads?: number;
62
- seed?: number;
63
- }
64
-
65
- // -- Paths -------------------------------------------------------------------
66
-
67
- const WIKI_DIR = join(homedir(), ".pi", "wiki");
68
- const GEPA_DIR = join(WIKI_DIR, "gepa");
69
- const INPUT_DIR = join(GEPA_DIR, "input");
70
- const OUTPUT_DIR = join(GEPA_DIR, "output");
71
- const OPTIMIZED_DIR = join(GEPA_DIR, "optimized");
72
- const HISTORY_DIR = join(GEPA_DIR, "history");
73
-
74
- // -- Package root ------------------------------------------------------------
75
-
76
- function findPackageRoot(): string {
77
- let dir = dirname(new URL(import.meta.url).pathname);
78
- for (let i = 0; i < 5; i++) {
79
- const pkgPath = join(dir, "package.json");
80
- if (existsSync(pkgPath)) {
81
- try {
82
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
83
- if (pkg.name === "pi-para" || pkg.name === "@picassio/pi-para") return dir;
84
- } catch {}
85
- }
86
- dir = dirname(dir);
87
- }
88
- return resolve(dirname(new URL(import.meta.url).pathname), "../..");
89
- }
90
-
91
- // -- Helpers: extract strings from TS source ---------------------------------
92
-
93
- function extractTSConst(source: string, name: string): string | null {
94
- const m = source.match(new RegExp(`(?:export\\s+)?const\\s+${name}\\s*=\\s*\`([\\s\\S]*?)\`;`));
95
- return m ? m[1].trim() : null;
96
- }
97
-
98
- function extractToolInstructions(source: string): Record<string, string> {
99
- const tools: Record<string, string> = {};
100
- const blocks = [...source.matchAll(
101
- /registerTool\(\{[\s\S]*?name:\s*"(wiki_\w+)"[\s\S]*?(?=registerTool\(\{|$)/g,
102
- )];
103
- for (const block of blocks) {
104
- const name = block[1].replace("wiki_", "wiki-");
105
- const text = block[0];
106
- const descStrs = [...(text.match(/description:\s*\n?\s*([\s\S]*?)(?:promptSnippet:|promptGuidelines:|parameters:)/)?.[1] ?? "").matchAll(/["'`]([^"'`]+)["'`]/g)];
107
- const desc = descStrs.map(s => s[1]).join(" ");
108
- const snippet = text.match(/promptSnippet:\s*["'`]([^"'`]+)["'`]/)?.[1] ?? "";
109
- const guideMatch = text.match(/promptGuidelines:\s*\[([\s\S]*?)\]/);
110
- const guides = guideMatch ? [...guideMatch[1].matchAll(/["'`]([^"'`]+)["'`]/g)].map(s => s[1]).join("\n") : "";
111
- const combined = [`Tool: ${name}`, desc && `Description: ${desc.trim()}`, snippet && `Snippet: ${snippet}`, guides && `Guidelines:\n${guides}`].filter(Boolean).join("\n");
112
- if (combined.length > 50) tools[name] = combined;
113
- }
114
- return tools;
115
- }
116
-
117
- function toKebab(name: string): string {
118
- return name.replace(/_/g, "-").replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
119
- }
120
-
121
- // -- Target extraction -------------------------------------------------------
122
-
123
- export function extractTargets(packageRoot?: string): GEPATarget[] {
124
- const root = packageRoot ?? findPackageRoot();
125
- const targets: GEPATarget[] = [];
126
-
127
- // Prompt templates
128
- const promptsFile = join(root, "src", "templates", "prompts.ts");
129
- if (existsSync(promptsFile)) {
130
- const src = readFileSync(promptsFile, "utf-8");
131
- for (const n of ["WIKI_SYSTEM_PROMPT","INGEST_PROMPT","QUERY_PROMPT","CAPTURE_SYSTEM_PROMPT","CAPTURE_PROMPT","EXPLICIT_CAPTURE_PROMPT","SUMMARIZE_SYSTEM_PROMPT","ITERATIVE_UPDATE_PROMPT","OVERVIEW_PROMPT","LINT_PROMPT"]) {
132
- const c = extractTSConst(src, n);
133
- if (c) targets.push({ name: toKebab(n), type: "prompt", content: c, sourceFile: promptsFile });
134
- }
135
- }
136
-
137
- // Maintainer + processor prompts
138
- for (const [file, constName, targetName] of [
139
- ["src/maintainer.ts", "MAINTENANCE_SYSTEM_PROMPT", "maintenance-system-prompt"],
140
- ["src/processor.ts", "CAPTURE_SYSTEM_PROMPT", "processor-capture-prompt"],
141
- ] as const) {
142
- const p = join(root, file);
143
- if (existsSync(p)) {
144
- const c = extractTSConst(readFileSync(p, "utf-8"), constName);
145
- if (c) targets.push({ name: targetName, type: "prompt", content: c, sourceFile: p });
146
- }
147
- }
148
-
149
- // Tool instructions
150
- const toolsFile = join(root, "src", "tools.ts");
151
- if (existsSync(toolsFile)) {
152
- for (const [n, c] of Object.entries(extractToolInstructions(readFileSync(toolsFile, "utf-8")))) {
153
- targets.push({ name: `tool-${n}`, type: "tool", content: c, sourceFile: toolsFile });
154
- }
155
- }
156
-
157
- // Skills
158
- for (const s of ["para", "setup"]) {
159
- const p = join(root, "skills", s, "SKILL.md");
160
- if (existsSync(p)) {
161
- const raw = readFileSync(p, "utf-8");
162
- const body = raw.match(/^---\s*\n[\s\S]*?\n---\s*\n([\s\S]*)$/)?.[1]?.trim() ?? raw;
163
- targets.push({ name: `skill-${s}`, type: "skill", content: body, sourceFile: p });
164
- }
165
- }
166
-
167
- return targets;
168
- }
169
-
170
- // -- Auth env vars -----------------------------------------------------------
171
-
172
- function getAuthEnvVars(): Record<string, string> {
173
- const env: Record<string, string> = {};
174
- try {
175
- const auth = JSON.parse(readFileSync(join(homedir(), ".pi", "agent", "auth.json"), "utf-8"));
176
- if (auth.anthropic?.access) { env.ANTHROPIC_OAUTH_TOKEN = auth.anthropic.access; env.ANTHROPIC_API_KEY = auth.anthropic.access; }
177
- else if (auth.anthropic?.key) env.ANTHROPIC_API_KEY = auth.anthropic.key;
178
- if (auth.minimax?.key) env.MINIMAX_API_KEY = auth.minimax.key;
179
- if (auth.openrouter?.key) env.OPENROUTER_API_KEY = auth.openrouter.key;
180
- else if (auth.openrouter?.openrouter_key) env.OPENROUTER_API_KEY = auth.openrouter.openrouter_key;
181
- } catch {
182
- try {
183
- const raw = readFileSync(join(homedir(), ".config", "qmd", "index.yml"), "utf-8");
184
- const m = raw.match(/chat:[\s\S]*?key:\s*(.+)/);
185
- if (m) { const k = m[1].trim(); if (raw.includes("minimaxi.com")) env.MINIMAX_CN_API_KEY = k; else env.MINIMAX_API_KEY = k; }
186
- } catch {}
187
- }
188
- return env;
189
- }
190
-
191
- // -- Main orchestrator -------------------------------------------------------
192
-
193
- export async function runGEPA(options: GEPAOptions = {}): Promise<GEPAOutput> {
194
- const root = findPackageRoot();
195
- const scriptsDir = join(root, "scripts", "gepa");
196
- for (const d of [INPUT_DIR, OUTPUT_DIR, OPTIMIZED_DIR, HISTORY_DIR]) mkdirSync(d, { recursive: true });
197
-
198
- // Load GEPA config from config.json (user-configurable defaults)
199
- let gepaConfig: Record<string, unknown> = {};
200
- try {
201
- const cfg = JSON.parse(readFileSync(join(WIKI_DIR, "config.json"), "utf-8"));
202
- gepaConfig = cfg.gepa ?? {};
203
- } catch {}
204
-
205
- // Resolution: CLI flag → options → config.json → default
206
- const studentModel = options.studentModel ?? options.model
207
- ?? (gepaConfig.studentModel as string) ?? "anthropic/claude-sonnet-4-20250514";
208
- const teacherModel = options.teacherModel ?? options.reflectionModel
209
- ?? (gepaConfig.teacherModel as string) ?? "anthropic/claude-opus-4-6";
210
- const judgeModel = options.judgeModel
211
- ?? (gepaConfig.judgeModel as string) ?? studentModel;
212
- const auto = options.auto ?? (gepaConfig.auto as "light" | "medium" | "heavy") ?? "light";
213
- const threads = options.threads ?? (gepaConfig.threads as number) ?? 2;
214
- const seed = options.seed ?? (gepaConfig.seed as number) ?? 42;
215
-
216
- const allTargets = extractTargets(root);
217
- let targets = options.target ? allTargets.filter(t => t.name === options.target) : allTargets;
218
- if (options.target && targets.length === 0) throw new Error(`Target '${options.target}' not found. Available: ${allTargets.map(t => t.name).join(", ")}`);
219
-
220
- console.log(`[gepa] Found ${targets.length} target(s) to optimize`);
221
- console.log(`[gepa] Student: ${studentModel}, Teacher: ${teacherModel}, Judge: ${judgeModel}, Budget: ${auto}`);
222
-
223
- const targetsFile = join(INPUT_DIR, "targets.json");
224
- writeFileSync(targetsFile, JSON.stringify({ targets: targets.map(t => ({ name: t.name, type: t.type, content: t.content })) }, null, 2));
225
-
226
- const outputFile = join(OUTPUT_DIR, "results.json");
227
- const uvArgs = [
228
- "run", "--project", scriptsDir, join(scriptsDir, "optimize.py"),
229
- "--targets-file", targetsFile, "--wiki-dir", WIKI_DIR, "--output", outputFile,
230
- "--model", studentModel,
231
- "--reflection-model", teacherModel,
232
- "--judge-model", judgeModel,
233
- "--auto", auto,
234
- "--threads", String(threads),
235
- "--seed", String(seed),
236
- ...(options.maxMetricCalls ? ["--max-metric-calls", String(options.maxMetricCalls)] : []),
237
- ...(options.target ? ["--target", options.target] : []),
238
- ];
239
-
240
- console.log(`[gepa] Running: uv ${uvArgs.slice(0, 6).join(" ")} ...`);
241
-
242
- await new Promise<void>((resolve, reject) => {
243
- const proc = spawn("uv", uvArgs, { cwd: scriptsDir, env: { ...process.env, ...getAuthEnvVars() }, stdio: ["pipe", "inherit", "inherit"] });
244
- proc.on("close", c => c === 0 ? resolve() : reject(new Error(`uv exited with code ${c}`)));
245
- proc.on("error", e => reject(new Error(`Failed to spawn uv: ${e.message}`)));
246
- });
247
-
248
- if (!existsSync(outputFile)) throw new Error("Output file not created");
249
- const output: GEPAOutput = JSON.parse(readFileSync(outputFile, "utf-8"));
250
-
251
- for (const r of output.results) {
252
- if (r.status === "success" && r.optimized_instruction) {
253
- mkdirSync(OPTIMIZED_DIR, { recursive: true });
254
- writeFileSync(join(OPTIMIZED_DIR, `${r.target}.txt`), r.optimized_instruction, "utf-8");
255
- const hDir = join(HISTORY_DIR, r.target); mkdirSync(hDir, { recursive: true });
256
- writeFileSync(join(hDir, `${new Date().toISOString().replace(/[:.]/g, "-")}.json`), JSON.stringify(r, null, 2));
257
- console.log(`[gepa] Saved optimized: ${r.target}`);
258
- }
259
- }
260
- return output;
261
- }
262
-
263
- // -- Query functions ---------------------------------------------------------
264
-
265
- export function listOptimized(): { name: string; score: number; timestamp: string; model: string }[] {
266
- if (!existsSync(OPTIMIZED_DIR)) return [];
267
- const results: { name: string; score: number; timestamp: string; model: string }[] = [];
268
- for (const f of readdirSync(OPTIMIZED_DIR).filter(f => f.endsWith(".txt"))) {
269
- const name = f.replace(".txt", "");
270
- let score = 0, timestamp = "", model = "";
271
- try {
272
- const entries = readdirSync(join(HISTORY_DIR, name)).filter(f => f.endsWith(".json")).sort().reverse();
273
- if (entries[0]) { const d = JSON.parse(readFileSync(join(HISTORY_DIR, name, entries[0]), "utf-8")); score = d.optimized_score ?? 0; timestamp = d.timestamp ?? ""; model = d.model ?? ""; }
274
- } catch {}
275
- results.push({ name, score, timestamp, model });
276
- }
277
- return results;
278
- }
279
-
280
- export function compareTarget(targetName: string): { original: string; optimized: string } | null {
281
- const target = extractTargets().find(t => t.name === targetName);
282
- if (!target) return null;
283
- try {
284
- const opt = readFileSync(join(OPTIMIZED_DIR, `${targetName}.txt`), "utf-8");
285
- return { original: target.content, optimized: opt };
286
- } catch { return null; }
287
- }
@@ -1,65 +0,0 @@
1
- You are a knowledge extraction specialist for technical project wikis. Your task is to analyze coding session conversations and capture ALL project-relevant knowledge into wiki pages.
2
-
3
- ## Input Format
4
- You will receive:
5
- - task_context: Brief description of the coding session topic and project
6
- - session-conversation: The actual conversation content to analyze
7
-
8
- ## Wiki Page Structure Requirements
9
- Create wiki pages using this exact structure:
10
-
11
- ```
12
- # Page Title (kebab-case for technical terms)
13
-
14
- ## Topic
15
- Brief description of what this page covers (1-2 sentences)
16
-
17
- ## Key Facts
18
- - Bullet points with specific technical details
19
- - Include paths, configs, server details, commands
20
- - Use **bold** for important terms/concepts
21
- - Capture operational facts - they're often most valuable
22
-
23
- ## Insights (Optional)
24
- - Analysis of implications, patterns, or broader context
25
- - How this connects to larger architectural patterns
26
- - Lessons learned or best practices identified
27
-
28
- ## Connections
29
- - [[Related Wiki Page]] - Brief description of relationship
30
- - Use kebab-case for technical project names
31
- - Include both specific project pages and general concept pages
32
-
33
- ## Sources
34
- - Session file path or conversation source
35
- ```
36
-
37
- ## PARA Compliance Requirements
38
- - Use kebab-case for all project names and technical terms in page titles and connections
39
- - Technical reference material should be categorized as Resources
40
- - Include scope indicators through consistent naming and cross-references
41
-
42
- ## Knowledge Capture Bias
43
- **Capture bias: when in doubt, capture it.**
44
- - Small operational facts (paths, configs, commands) are often most valuable
45
- - Technical architecture details, even if seemingly minor
46
- - API findings, verification results, compatibility issues
47
- - Extension systems, plugin architectures, integration points
48
- - Security considerations and safeguards
49
-
50
- ## Wiki Management Strategy
51
- 1. **Search first**: Use wiki_query to find existing pages on the topic
52
- 2. **Update over create**: If matching page exists, use wiki_read then wiki_write with exact slug to update
53
- 3. **Create only if needed**: Only make new pages if wiki_query finds no relevant results
54
- 4. **Never pass indexContent**: The index auto-rebuilds from pages on disk
55
- 5. **Always include sources**: Add session file path to Sources section
56
-
57
- ## Special Project Context
58
- Based on the examples, you're working with:
59
- - **pi-mono/pi-para projects**: Modular knowledge management and agent systems
60
- - **Extension architectures**: Plugin-based systems for AI agents and knowledge tools
61
- - **Quarto integration**: .qmd files as first-class knowledge artifacts
62
- - **API verification**: Testing and validation processes for extensions
63
- - **Security considerations**: Prompt injection safeguards in agent systems
64
-
65
- Only respond with "nothing to capture" if the session contains purely greetings or completely off-topic chitchat with zero project knowledge.