pi-soly 1.6.0 → 1.7.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 (3) hide show
  1. package/core.ts +30 -0
  2. package/index.ts +26 -11
  3. package/package.json +1 -1
package/core.ts CHANGED
@@ -1614,3 +1614,33 @@ export function buildDriftReminder(turnsSinceLastVerb: number): string | null {
1614
1614
  const when = turnsSinceLastVerb === 1 ? "1 turn" : `${turnsSinceLastVerb} turns`;
1615
1615
  return `soly drift hint: ${when} since last soly verb. Consider \`${verb}\` to sync state (pause saves HANDOFF for resume across compactions).`;
1616
1616
  }
1617
+
1618
+ // =============================================================================
1619
+ // Post-work rules check: which rules apply to a set of edited files?
1620
+ // =============================================================================
1621
+ //
1622
+ // Used by the turn_end hook to surface a checklist of rules that SHOULD have
1623
+ // been followed during this turn. Honest post-hook — does not claim to detect
1624
+ // violations, just lists what was applicable so the user can verify.
1625
+
1626
+ export function rulesApplicableToFiles(
1627
+ rules: RuleFile[],
1628
+ editedFiles: string[],
1629
+ ): string[] {
1630
+ const applicable = new Set<string>();
1631
+ for (const filePath of editedFiles) {
1632
+ for (const rule of rules) {
1633
+ if (!rule.enabled) continue;
1634
+ const globs = rule.meta.globs;
1635
+ const always = rule.meta.always === true;
1636
+ if (always) {
1637
+ applicable.add(rule.relPath);
1638
+ continue;
1639
+ }
1640
+ if (globs && globs.some((g) => matchesGlob(filePath, g))) {
1641
+ applicable.add(rule.relPath);
1642
+ }
1643
+ }
1644
+ }
1645
+ return [...applicable];
1646
+ }
package/index.ts CHANGED
@@ -36,6 +36,8 @@ import {
36
36
  loadAllRules,
37
37
  loadPhaseRules,
38
38
  loadProjectState,
39
+ matchesGlob,
40
+ rulesApplicableToFiles,
39
41
  STATUS_ID,
40
42
  solyDirFor,
41
43
  isLegacySolyDir,
@@ -125,7 +127,7 @@ export default function solyExtension(pi: ExtensionAPI) {
125
127
 
126
128
  // Behavioral nudge state
127
129
  let nudgeActiveForTask = false;
128
- let rulesEditNotifyShown = false;
130
+ let editedFilesThisTurn = new Set<string>();
129
131
  let lastNudgePromptKey = "";
130
132
 
131
133
  // Git context (cached, refreshed on hot reload + before_agent_start)
@@ -427,7 +429,6 @@ export default function solyExtension(pi: ExtensionAPI) {
427
429
  rulesLoaded = [];
428
430
  lastRulesTokens = 0;
429
431
  nudgeActiveForTask = false;
430
- rulesEditNotifyShown = false;
431
432
  lastNudgePromptKey = "";
432
433
  sessionStats = { turns: 0, tokensEstimate: 0 };
433
434
 
@@ -711,21 +712,35 @@ export default function solyExtension(pi: ExtensionAPI) {
711
712
  if (rulesChanged || stateChanged) {
712
713
  updateStatus(ctx);
713
714
  }
715
+
716
+ // Post-work rules check: surface applicable rules for files edited
717
+ // in this turn. Honest post-hook — doesn't pretend to detect violations,
718
+ // just reminds the user which rules SHOULD have been followed.
719
+ if (editedFilesThisTurn.size > 0) {
720
+ const applicable = rulesApplicableToFiles(
721
+ combinedRules(),
722
+ [...editedFilesThisTurn],
723
+ );
724
+ if (applicable.length > 0) {
725
+ ctx.ui.notify(
726
+ `📋 Rules check: edited ${editedFilesThisTurn.size} file(s), ${applicable.length} rule(s) applied:\n • ${applicable.join("\n • ")}`,
727
+ "info",
728
+ );
729
+ }
730
+ editedFilesThisTurn = new Set();
731
+ }
714
732
  });
715
733
 
716
734
  // ============================================================================
717
- // tool_call: rules reinforcement fire a brief notify to the user when
718
- // the LLM is about to edit/write a file that has applicable rules.
719
- // This doesn't block the tool — it's a visibility signal so the user
720
- // can spot when the LLM is editing without checking rules.
735
+ // tool_call: track files edited in this turn. Used at turn_end to surface
736
+ // applicable rules as a post-work checklist ("did the agent follow them?").
721
737
  // ============================================================================
722
738
  pi.on("tool_call", async (event, _ctx) => {
723
739
  if (event.toolName !== "edit" && event.toolName !== "write") return;
724
- const activeRules = combinedRules();
725
- if (activeRules.length === 0) return;
726
- // Don't spam — only notify once per session
727
- if (rulesEditNotifyShown) return;
728
- rulesEditNotifyShown = true;
740
+ const input = event.input as { path?: string };
741
+ if (input?.path) {
742
+ editedFilesThisTurn.add(input.path);
743
+ }
729
744
  });
730
745
 
731
746
  // Mount built-in sub-features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-soly",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Project management framework for pi-coding-agent. Workflows, planning, multi-question picker, agent switcher, live task list — one npm install, zero config.",
5
5
  "type": "module",
6
6
  "main": "index.ts",