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.
- package/core.ts +30 -0
- package/index.ts +26 -11
- 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
|
|
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:
|
|
718
|
-
//
|
|
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
|
|
725
|
-
if (
|
|
726
|
-
|
|
727
|
-
|
|
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.
|
|
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",
|