@slowdini/slow-powers-opencode 0.4.2 → 0.4.3

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.
@@ -23,11 +23,18 @@ const bootstrapLeadingPhrase = "<EXTREMELY-IMPORTANT>";
23
23
  // once eliminates redundant fs work on every agent step.
24
24
  let _bootstrapCache; // undefined = not yet loaded, null = file missing
25
25
 
26
- // Tracks plan files we've already sent the hardening prompt for.
27
- // Once we ask the agent to harden a plan, we never ask again for that file.
28
- const hardeningPromptSentFor = new Set();
29
-
30
26
  export const SlowPowersPlugin = async ({ client, directory: _directory }) => {
27
+ // Tracks plan files we've already sent the hardening prompt for, keyed by
28
+ // `${sessionID}:${filePath}` so different sessions with the same plan path
29
+ // still get prompted. Scoped to the plugin instance (one per opencode process).
30
+ const hardeningPromptSentFor = new Set();
31
+
32
+ const log = (level, message) => {
33
+ client.app
34
+ .log({ body: { service: "slow-powers", level, message } })
35
+ .catch(() => {});
36
+ };
37
+
31
38
  // Helper to load bootstrap content (cached after first call)
32
39
  const getBootstrapContent = () => {
33
40
  if (_bootstrapCache !== undefined) return _bootstrapCache;
@@ -46,23 +53,30 @@ export const SlowPowersPlugin = async ({ client, directory: _directory }) => {
46
53
  const filePath = event.properties.file;
47
54
  const sessionID = event.properties.sessionID;
48
55
 
49
- if (!filePath || !sessionID) return;
50
-
51
- if (!filePath.match(/\.opencode\/plans\/.*\.md$/)) return;
56
+ if (!filePath || !sessionID) {
57
+ log("debug", `[hardening] skipped: missing filePath or sessionID`);
58
+ return;
59
+ }
52
60
 
53
- let session;
54
- try {
55
- session = await client.session.get({ path: { id: sessionID } });
56
- } catch {
61
+ if (!filePath.match(/\.opencode\/plans\/.*\.md$/)) {
62
+ log("debug", `[hardening] skipped: ${filePath} not in .opencode/plans/`);
57
63
  return;
58
64
  }
59
- if (session.agent !== "plan") return;
60
65
 
61
- // Only prompt once per plan file. After we've asked the agent to harden
62
- // it, we trust them to do so or not; re-prompting causes loops.
63
- if (hardeningPromptSentFor.has(filePath)) return;
66
+ const promptKey = `${sessionID}:${filePath}`;
67
+
68
+ // Only prompt once per plan file per session. After we've asked the agent
69
+ // to harden it, we trust them to do so or not; re-prompting causes loops.
70
+ if (hardeningPromptSentFor.has(promptKey)) {
71
+ log("debug", `[hardening] skipped: already prompted for ${promptKey}`);
72
+ return;
73
+ }
64
74
 
65
- hardeningPromptSentFor.add(filePath);
75
+ hardeningPromptSentFor.add(promptKey);
76
+ log(
77
+ "info",
78
+ `[hardening] prompting agent to harden ${filePath} in session ${sessionID}`,
79
+ );
66
80
 
67
81
  try {
68
82
  await client.session.prompt({
@@ -78,8 +92,8 @@ export const SlowPowersPlugin = async ({ client, directory: _directory }) => {
78
92
  },
79
93
  });
80
94
  } catch (err) {
81
- hardeningPromptSentFor.delete(filePath);
82
- console.error("[slow-powers] Failed to trigger hardening-plans:", err);
95
+ hardeningPromptSentFor.delete(promptKey);
96
+ log("error", `[hardening] failed to trigger hardening-plans: ${err}`);
83
97
  }
84
98
  };
85
99
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slowdini/slow-powers-opencode",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Slow-powers — structured development workflows for coding agents (TDD, debugging, verification, git hygiene)",
5
5
  "type": "module",
6
6
  "main": "./opencode/plugins/slow-powers.js",