pi-soly 1.5.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 +33 -1
  2. package/index.ts +32 -0
  3. package/package.json +1 -1
package/core.ts CHANGED
@@ -758,7 +758,9 @@ export function buildRulesSection(
758
758
 
759
759
  const section = `
760
760
 
761
- ## soly project rules
761
+ ## ⚠️ MANDATORY: soly project rules
762
+
763
+ **These rules are NON-NEGOTIABLE. Before writing or editing ANY code, re-read the rules above that apply to the file path you are about to modify. If a rule contradicts your instinct, the rule wins.**
762
764
 
763
765
  ${headerHint}
764
766
 
@@ -1612,3 +1614,33 @@ export function buildDriftReminder(turnsSinceLastVerb: number): string | null {
1612
1614
  const when = turnsSinceLastVerb === 1 ? "1 turn" : `${turnsSinceLastVerb} turns`;
1613
1615
  return `soly drift hint: ${when} since last soly verb. Consider \`${verb}\` to sync state (pause saves HANDOFF for resume across compactions).`;
1614
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,6 +127,7 @@ export default function solyExtension(pi: ExtensionAPI) {
125
127
 
126
128
  // Behavioral nudge state
127
129
  let nudgeActiveForTask = false;
130
+ let editedFilesThisTurn = new Set<string>();
128
131
  let lastNudgePromptKey = "";
129
132
 
130
133
  // Git context (cached, refreshed on hot reload + before_agent_start)
@@ -709,6 +712,35 @@ export default function solyExtension(pi: ExtensionAPI) {
709
712
  if (rulesChanged || stateChanged) {
710
713
  updateStatus(ctx);
711
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
+ }
732
+ });
733
+
734
+ // ============================================================================
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?").
737
+ // ============================================================================
738
+ pi.on("tool_call", async (event, _ctx) => {
739
+ if (event.toolName !== "edit" && event.toolName !== "write") return;
740
+ const input = event.input as { path?: string };
741
+ if (input?.path) {
742
+ editedFilesThisTurn.add(input.path);
743
+ }
712
744
  });
713
745
 
714
746
  // Mount built-in sub-features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-soly",
3
- "version": "1.5.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",