guardian-framework 0.1.52 → 0.1.54

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.
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
3
+ * Last Sync: 2026-05-31
4
+
2
5
  * Goal Loop Extension for pi
3
6
  *
4
7
  * Persistent standing goals with validator-backed judge.
@@ -151,7 +154,10 @@ const BUILTIN_VALIDATORS: Record<string, string> = {
151
154
  integration: ".pi/scripts/validate-integration.sh",
152
155
  };
153
156
 
154
- /** Discover custom validators from `.pi/scripts/validate-*.sh` */
157
+ /**
158
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
159
+ * Last Sync: 2026-05-31
160
+ Discover custom validators from `.pi/scripts/validate-*.sh` */
155
161
  function discoverCustomValidators(cwd: string): Record<string, string> {
156
162
  const scriptsDir = join(cwd, ".pi/scripts");
157
163
  if (!existsSync(scriptsDir)) return {};
@@ -230,15 +236,16 @@ async function runLLMJudge(
230
236
 
231
237
  const prompt = `Goal:\n${truncatedGoal}\n${subgoalsBlock}\n\nAgent's most recent response:\n${truncatedResponse}\n\nIs the goal satisfied?`;
232
238
 
233
- // Check if git diff is accessible (for heuristic judgment)
239
+ // Use guardian_coordinate to run an LLM-based judgment
234
240
  try {
235
- await ctx.shell.execute("git rev-parse --git-dir 2>/dev/null", {});
241
+ const result = await ctx.tools.execute("guardian_scope", {});
242
+ // We can't directly call LLM from extension — instead return a heuristic judgment
243
+ // The LLM judge runs via the agent itself being prompted to self-assess
244
+ // For now, return continue to let the agent decide
245
+ return { done: false, reason: "LLM judge requires agent self-assessment turn" };
236
246
  } catch {
237
- // Not a git repo continue anyway
247
+ return { done: false, reason: "judge unavailable, continuing" };
238
248
  }
239
- // The LLM judge runs via the agent itself being prompted to self-assess
240
- // For now, return continue to let the agent decide
241
- return { done: false, reason: "LLM judge requires agent self-assessment turn" };
242
249
  }
243
250
 
244
251
  function truncate(text: string, limit: number): string {
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
3
+ * Last Sync: 2026-05-31
4
+
2
5
  * Shell Hooks Extension for pi
3
6
  *
4
7
  * Declarative shell-script hooks that fire on lifecycle events.
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
3
+ * Last Sync: 2026-05-31
4
+
2
5
  * Kanban Extension for pi
3
6
  *
4
7
  * Durable JSON-backed task board for multi-agent collaboration.
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
3
+ * Last Sync: 2026-05-31
4
+
2
5
  * Plan Mode Extension for pi
3
6
  *
4
7
  * Intercepts mutating tool calls (edit, write_file, create_directory) and queues
@@ -20,8 +20,7 @@
20
20
  */
21
21
 
22
22
  import { type ExecSyncOptions, execSync } from "node:child_process";
23
- import { existsSync, readFileSync, readdirSync } from "node:fs";
24
- import * as path from "node:path";
23
+ import { existsSync, readdirSync } from "node:fs";
25
24
  import { join } from "node:path";
26
25
 
27
26
  // ── Types ──
@@ -151,19 +150,7 @@ export default function (pi: ExtensionAPI) {
151
150
 
152
151
  const lang = String(parsed.lang || parsed.language || "").toLowerCase();
153
152
  const buildTool = String(parsed.buildTool || "").toLowerCase() || undefined;
154
-
155
- // Read groupId from manifest if not passed via CLI (matches guardian CLI behavior)
156
- let groupId = String(parsed.groupId || parsed.group || "");
157
- if (!groupId) {
158
- try {
159
- const manifestPath = path.join(ctx.cwd, "guardian-manifest.json");
160
- if (fs.existsSync(manifestPath)) {
161
- const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
162
- groupId = manifest.groupId || "";
163
- }
164
- } catch { /* ignore malformed manifest */ }
165
- }
166
- if (!groupId) groupId = "com.example";
153
+ const groupId = String(parsed.groupId || parsed.group || "com.example");
167
154
  const validators = String(parsed.validators || "ci,tests");
168
155
  const dryRun = parsed.dryRun === true || parsed["dry-run"] === true || parsed.d === true;
169
156
  const force = parsed.force === true || parsed.f === true;
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
3
+ * Last Sync: 2026-05-31
4
+
2
5
  * Read-Only Mode Extension for pi
3
6
  *
4
7
  * Hard-enforces a tiny tool allowlist: read, grep, find, ls.
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
3
+ * Last Sync: 2026-05-31
4
+
2
5
  * Redaction Extension for pi
3
6
  *
4
7
  * Automatically redacts sensitive data from tool results, terminal output,
@@ -40,6 +43,9 @@ const PATTERNS: Array<{ kind: string; re: RegExp }> = [
40
43
  ];
41
44
 
42
45
  /**
46
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
47
+ * Last Sync: 2026-05-31
48
+
43
49
  * Redact sensitive data from text. Returns the redacted string.
44
50
  */
45
51
  export function redactSensitive(text: string): string {
@@ -58,6 +64,9 @@ export function redactSensitive(text: string): string {
58
64
  }
59
65
 
60
66
  /**
67
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
68
+ * Last Sync: 2026-05-31
69
+
61
70
  * Count redactions made (for logging/telemetry-free reporting).
62
71
  */
63
72
  export function countRedactions(text: string): number {
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
3
+ * Last Sync: 2026-05-31
4
+
2
5
  * Session Persistence Extension for pi
3
6
  *
4
7
  * Provides structured session lifecycle with:
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
3
+ * Last Sync: 2026-05-31
4
+
2
5
  * Slash Commands Extension for pi
3
6
  *
4
7
  * Intercepts user input starting with `/` and transforms slash commands into
@@ -1,4 +1,7 @@
1
1
  /**
2
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
3
+ * Last Sync: 2026-05-31
4
+
2
5
  * Snippets Extension for pi
3
6
  *
4
7
  * Manages reusable prompt fragments (snippets) that can be referenced via
@@ -57,7 +60,10 @@ function normalizeHandle(raw: string): string {
57
60
  .replace(/^-+|-+$/g, "");
58
61
  }
59
62
 
60
- /** Expand #handle tokens in text, returning the body with tokens stripped and snippet XML blocks. */
63
+ /**
64
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
65
+ * Last Sync: 2026-05-31
66
+ Expand #handle tokens in text, returning the body with tokens stripped and snippet XML blocks. */
61
67
  function expandSnippetTokens(
62
68
  text: string,
63
69
  snippets: readonly Snippet[],
@@ -1,10 +1,17 @@
1
1
  /**
2
+ * Canonical Reference: .pi/architecture/modules/core-libraries.md
3
+ * Last Sync: 2026-06-02
4
+
2
5
  * Validation Runner Extension for pi
3
6
  *
4
7
  * Runs validation scripts as pi tools and commands.
8
+ * Supports language-specific validators from scripts/languages/{lang}/.
5
9
  * See coordinator.ts for guardian_scope and guardian_validate tools.
6
10
  */
7
11
 
12
+ import * as fs from "node:fs";
13
+ import * as path from "node:path";
14
+
8
15
  type ShellResult = {
9
16
  exitCode: number;
10
17
  stdout: string;
@@ -12,6 +19,7 @@ type ShellResult = {
12
19
  };
13
20
 
14
21
  type ExtensionContext = {
22
+ cwd: string;
15
23
  ui: { notify(message: string, level?: string): void };
16
24
  shell: { execute(command: string, options?: { signal?: AbortSignal }): Promise<ShellResult> };
17
25
  tools: { execute(name: string, params: Record<string, unknown>): Promise<unknown> };
@@ -41,7 +49,7 @@ type ExtensionAPI = {
41
49
  ): void;
42
50
  };
43
51
 
44
- const VALIDATORS = {
52
+ const BASE_VALIDATORS: Record<string, string> = {
45
53
  architecture: ".pi/scripts/validate-architecture.sh",
46
54
  canonical: ".pi/scripts/validate-canonical.sh",
47
55
  ci: ".pi/scripts/validate-ci.sh",
@@ -49,34 +57,82 @@ const VALIDATORS = {
49
57
  operations: ".pi/scripts/validate-operations.sh",
50
58
  security: ".pi/scripts/validate-security.sh",
51
59
  tests: ".pi/scripts/validate-tests.sh",
52
- } as const;
60
+ };
53
61
 
54
- type ValidatorName = keyof typeof VALIDATORS;
62
+ type ValidatorName = string;
55
63
 
56
- function isValidatorName(value: string): value is ValidatorName {
57
- return Object.hasOwn(VALIDATORS, value);
64
+ /**
65
+ * Get the project language from the manifest, if available.
66
+ */
67
+ function getProjectLanguage(cwd: string): string | null {
68
+ try {
69
+ const manifestPath = path.join(cwd, "guardian-manifest.json");
70
+ if (fs.existsSync(manifestPath)) {
71
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
72
+ return manifest.language || null;
73
+ }
74
+ } catch {
75
+ // Ignore parse errors
76
+ }
77
+ return null;
78
+ }
79
+
80
+ /**
81
+ * Discover language-specific validators from scripts/languages/{lang}/ directory.
82
+ */
83
+ function getLanguageValidators(cwd: string, language: string | null): Record<string, string> {
84
+ if (!language) return {};
85
+
86
+ const langDir = path.join(cwd, `.pi/scripts/languages/${language}`);
87
+ if (!fs.existsSync(langDir)) return {};
88
+
89
+ const validators: Record<string, string> = {};
90
+ try {
91
+ const files = fs.readdirSync(langDir);
92
+ for (const file of files) {
93
+ if (file.endsWith(".sh")) {
94
+ const name = file.replace("validate-", "").replace(".sh", "");
95
+ validators[`${language}:${name}`] = `.pi/scripts/languages/${language}/${file}`;
96
+ }
97
+ }
98
+ } catch {
99
+ // Ignore read errors
100
+ }
101
+ return validators;
58
102
  }
59
103
 
60
104
  export default function (pi: ExtensionAPI) {
61
- // Session initialization
105
+ let language: string | null = null;
106
+
107
+ // Session initialization — detect project language
62
108
  pi.on("session_start", async (_event, ctx) => {
63
- ctx.ui.notify("Guardian validation runner initialized", "info");
109
+ language = getProjectLanguage(ctx.cwd);
110
+ if (language) {
111
+ ctx.ui.notify(`Guardian validation runner initialized (language: ${language})`, "info");
112
+ } else {
113
+ ctx.ui.notify("Guardian validation runner initialized", "info");
114
+ }
64
115
  });
65
116
 
66
117
  // Register validate command — runs scripts directly via shell
67
118
  pi.registerCommand("validate", {
68
119
  description: "Run all or specific validators",
69
120
  handler: async (args, ctx) => {
121
+ // Build full validator map (base + language-specific)
122
+ const langValidators = getLanguageValidators(ctx.cwd, language);
123
+ const allValidators = { ...BASE_VALIDATORS, ...langValidators };
124
+
70
125
  const validators =
71
126
  args.length > 0
72
- ? args.filter(isValidatorName)
73
- : ["ci", "tests", "operations", "security", "canonical"];
127
+ ? args.filter((v) => Object.hasOwn(allValidators, v))
128
+ : Object.keys(allValidators);
129
+
74
130
  ctx.ui.notify(`Running validators: ${validators.join(", ")}`, "info");
75
131
 
76
132
  const results: Record<string, { passed: boolean; output: string }> = {};
77
133
 
78
134
  for (const validator of validators) {
79
- const scriptPath = VALIDATORS[validator];
135
+ const scriptPath = allValidators[validator];
80
136
  try {
81
137
  const result = await ctx.shell.execute(`bash ${scriptPath}`);
82
138
  results[validator] = {
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-07-04T14:56:34Z",
2
+ "timestamp": "2026-07-08T11:39:04Z",
3
3
  "mode": "all",
4
4
  "stages_run": [],
5
5
  "summary": {
@@ -178,6 +178,17 @@ keep_lines_matching = ["FAILED", "error", "timeout", "connection refused", "asse
178
178
  max_lines = 80
179
179
  on_empty = "✅ All integration tests passed"
180
180
 
181
+ # ── Ubiquitous Language Validator ────────────────────────────────────────────
182
+
183
+ [filters.ubiquitous-language]
184
+ command = "validate-ubiquitous-language"
185
+ description = "Detect code drift from .pi/domain/ubiquitous-language.md glossary"
186
+ # Run: bash .pi/scripts/validate-ubiquitous-language.sh [src_dir]
187
+ strip_ansi = false
188
+ keep_lines_matching = ["❌ FAIL", "⚠️ WARN", "Drift:", "alias", "canonical", "Variation"]
189
+ max_lines = 80
190
+ on_empty = "✅ No drift detected"
191
+
181
192
  # ── Git Status Validator ────────────────────────────────────────────────────
182
193
 
183
194
  [filters.git-status]
@@ -199,11 +210,3 @@ strip_ansi = true
199
210
  head_lines = 1
200
211
  truncate_lines_at = 120
201
212
  max_lines = 30
202
-
203
- # ── Ubiquitous Language Validator ────────────────────────────────────────────
204
-
205
- [filters.ubiquitous-language]
206
- command = "validate-ubiquitous-language"
207
- description = "Detect code drift from .pi/domain/ubiquitous-language.md glossary"
208
- strip_ansi = false
209
- keep_lines_matching = ["FAIL", "WARN", "Drift:", "alias", "canonical"]