@a-company/paradigm 5.4.0 → 5.6.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.
@@ -15,7 +15,7 @@ import {
15
15
  updateExpertiseFromAssessment,
16
16
  updateExpertiseFromLore,
17
17
  verifyIntegrity
18
- } from "./chunk-A2L4TSLZ.js";
18
+ } from "./chunk-ITPJJIHG.js";
19
19
  import "./chunk-7N7GSU6K.js";
20
20
  init_agent_loader();
21
21
  export {
@@ -4,6 +4,7 @@
4
4
  import * as fs from "fs";
5
5
  import * as path from "path";
6
6
  import * as os from "os";
7
+ import * as crypto from "crypto";
7
8
  import { exec } from "child_process";
8
9
  var CLAUDE_PLUGINS_DIR = path.join(os.homedir(), ".claude", "plugins");
9
10
  var CHECK_STATE_PATH = path.join(os.homedir(), ".paradigm", "plugin-update-check.json");
@@ -40,6 +41,39 @@ function isThrottled() {
40
41
  const elapsed = Date.now() - new Date(state.lastCheck).getTime();
41
42
  return elapsed < THROTTLE_HOURS * 3600 * 1e3;
42
43
  }
44
+ function computePluginContentHash(pluginDir) {
45
+ const hash = crypto.createHash("sha256");
46
+ const relevantExts = /* @__PURE__ */ new Set([".md", ".json", ".sh", ".yaml", ".yml", ".ts", ".js"]);
47
+ const skipDirs = /* @__PURE__ */ new Set([".git", "node_modules", ".cache"]);
48
+ function walkDir(dir) {
49
+ let entries;
50
+ try {
51
+ entries = fs.readdirSync(dir, { withFileTypes: true });
52
+ } catch {
53
+ return;
54
+ }
55
+ entries.sort((a, b) => a.name.localeCompare(b.name));
56
+ for (const entry of entries) {
57
+ if (skipDirs.has(entry.name)) continue;
58
+ const fullPath = path.join(dir, entry.name);
59
+ if (entry.isDirectory()) {
60
+ walkDir(fullPath);
61
+ } else if (entry.isFile()) {
62
+ const ext = path.extname(entry.name).toLowerCase();
63
+ if (relevantExts.has(ext)) {
64
+ try {
65
+ const content = fs.readFileSync(fullPath, "utf8");
66
+ const relPath = path.relative(pluginDir, fullPath);
67
+ hash.update(relPath + "\0" + content);
68
+ } catch {
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ walkDir(pluginDir);
75
+ return hash.digest("hex").slice(0, 12);
76
+ }
43
77
  function discoverPlugins() {
44
78
  const plugins = [];
45
79
  const marketplacesDir = path.join(CLAUDE_PLUGINS_DIR, "marketplaces");
@@ -114,7 +148,9 @@ function discoverPlugins() {
114
148
  installedVersion,
115
149
  installedSha,
116
150
  marketplacePath,
117
- localVersion
151
+ localVersion,
152
+ pluginSourceDir: path.join(pluginsSubdir, pluginName),
153
+ pluginCacheDir: fs.existsSync(pluginCacheDir) && installedVersion !== "unknown" ? path.join(pluginCacheDir, installedVersion) : ""
118
154
  });
119
155
  }
120
156
  }
@@ -140,6 +176,9 @@ async function checkPlugin(plugin) {
140
176
  }
141
177
  const hasRemoteUpdate = remoteSha !== null && remoteSha !== localSha;
142
178
  const hasCacheStale = plugin.localVersion !== plugin.installedVersion && plugin.installedVersion !== "unknown";
179
+ const marketplaceContentHash = computePluginContentHash(plugin.pluginSourceDir);
180
+ const cachedContentHash = plugin.pluginCacheDir ? computePluginContentHash(plugin.pluginCacheDir) : "";
181
+ const hasContentDrift = cachedContentHash !== "" && marketplaceContentHash !== cachedContentHash;
143
182
  return {
144
183
  repo: plugin.repo,
145
184
  plugin: plugin.plugin,
@@ -149,7 +188,10 @@ async function checkPlugin(plugin) {
149
188
  remoteSha,
150
189
  marketplacePath: plugin.marketplacePath,
151
190
  hasRemoteUpdate,
152
- hasCacheStale
191
+ hasCacheStale: hasCacheStale || hasContentDrift,
192
+ marketplaceContentHash,
193
+ cachedContentHash,
194
+ hasContentDrift
153
195
  };
154
196
  }
155
197
  function getPluginUpdateNotice() {
@@ -168,6 +210,9 @@ function getPluginUpdateNotice() {
168
210
  lines.push(` - ${r.plugin} (${r.repo}): ${versionInfo}`);
169
211
  pullCmds.push(`git -C ${r.marketplacePath} pull origin main`);
170
212
  reinstallCmds.push(`/plugin marketplace add ${r.repo}`);
213
+ } else if (r.hasContentDrift) {
214
+ lines.push(` - ${r.plugin} (${r.repo}): content changed (hash ${r.cachedContentHash} \u2192 ${r.marketplaceContentHash}, reinstall needed)`);
215
+ reinstallCmds.push(`/plugin marketplace add ${r.repo}`);
171
216
  } else if (r.hasCacheStale) {
172
217
  lines.push(` - ${r.plugin} (${r.repo}): ${r.installedVersion} \u2192 ${r.localVersion} (reinstall needed)`);
173
218
  reinstallCmds.push(`/plugin marketplace add ${r.repo}`);
@@ -4,7 +4,7 @@ import {
4
4
  loadAgentProfile,
5
5
  loadAllAgentProfiles,
6
6
  saveAgentProfile
7
- } from "./chunk-A2L4TSLZ.js";
7
+ } from "./chunk-ITPJJIHG.js";
8
8
  import {
9
9
  init_journal_loader,
10
10
  journal_loader_exports
@@ -686,7 +686,7 @@ function loadDebates(rootDir) {
686
686
  return [];
687
687
  }
688
688
  }
689
- function engageNomination(rootDir, nominationId, response) {
689
+ function engageNomination(rootDir, nominationId, response, reason) {
690
690
  const filePath = getNominationsPath(rootDir);
691
691
  if (!fs4.existsSync(filePath)) return false;
692
692
  try {
@@ -699,6 +699,7 @@ function engageNomination(rootDir, nominationId, response) {
699
699
  if (nom.id === nominationId) {
700
700
  nom.engaged = true;
701
701
  nom.response = response;
702
+ if (reason) nom.reason = reason;
702
703
  found = true;
703
704
  return JSON.stringify(nom);
704
705
  }
@@ -847,8 +848,12 @@ function adjustAttentionFromFeedback(rootDir, agentId) {
847
848
  return { adjusted: false, oldThreshold: 0.6, newThreshold: 0.6, reason: "No attention config" };
848
849
  }
849
850
  const oldThreshold = profile.attention.threshold ?? 0.6;
850
- const nominations = loadNominations(rootDir, { agent: agentId });
851
- const engaged = nominations.filter((n) => n.engaged);
851
+ const STALE_THRESHOLD_MS = 7 * 24 * 60 * 60 * 1e3;
852
+ const allNominations = loadNominations(rootDir, { agent: agentId });
853
+ const active = allNominations.filter(
854
+ (n) => n.engaged || Date.now() - new Date(n.timestamp).getTime() < STALE_THRESHOLD_MS
855
+ );
856
+ const engaged = active.filter((n) => n.engaged);
852
857
  if (engaged.length < 5) {
853
858
  return { adjusted: false, oldThreshold, newThreshold: oldThreshold, reason: `Insufficient data (${engaged.length}/5 engaged nominations)` };
854
859
  }
@@ -882,7 +887,11 @@ function adjustAttentionFromFeedback(rootDir, agentId) {
882
887
  return { adjusted: true, oldThreshold, newThreshold, reason };
883
888
  }
884
889
  function getNominationStats(rootDir, agentId) {
885
- const nominations = loadNominations(rootDir, { agent: agentId });
890
+ const STALE_THRESHOLD_MS = 7 * 24 * 60 * 60 * 1e3;
891
+ const allNominations = loadNominations(rootDir, { agent: agentId });
892
+ const nominations = allNominations.filter(
893
+ (n) => n.engaged || Date.now() - new Date(n.timestamp).getTime() < STALE_THRESHOLD_MS
894
+ );
886
895
  const accepted = nominations.filter((n) => n.response === "accepted").length;
887
896
  const dismissed = nominations.filter((n) => n.response === "dismissed").length;
888
897
  const deferred = nominations.filter((n) => n.response === "deferred").length;
@@ -987,10 +996,9 @@ function autoPromoteJournalEntries(rootDir, agentId) {
987
996
  } catch {
988
997
  return { promoted: 0, entries: [] };
989
998
  }
990
- const journal = loadJournalEntries(agentId, {
991
- trigger: "pattern_discovered",
992
- limit: 100
993
- });
999
+ const patternEntries = loadJournalEntries(agentId, { trigger: "pattern_discovered", limit: 100 });
1000
+ const feedbackEntries = loadJournalEntries(agentId, { trigger: "human_feedback", limit: 100 });
1001
+ const journal = [...patternEntries, ...feedbackEntries];
994
1002
  const promoted = [];
995
1003
  for (const entry of journal) {
996
1004
  if (entry.promoted_to_notebook) continue;
@@ -13,21 +13,24 @@ var init_agents = __esm({
13
13
  builder: { style: "rapid", risk: "balanced", verbosity: "concise" },
14
14
  tester: { style: "methodical", risk: "conservative", verbosity: "concise" },
15
15
  reviewer: { style: "deliberate", risk: "conservative", verbosity: "detailed" },
16
- security: { style: "methodical", risk: "conservative", verbosity: "detailed" }
16
+ security: { style: "methodical", risk: "conservative", verbosity: "detailed" },
17
+ documentor: { style: "methodical", risk: "conservative", verbosity: "concise" }
17
18
  };
18
19
  DEFAULT_ATTENTION = {
19
20
  architect: { symbols: ["$*", "#*"], concepts: ["architecture", "design", "pattern", "refactor"], signals: [{ type: "flow-modified" }, { type: "compliance-violation" }], threshold: 0.5 },
20
21
  builder: { paths: ["src/**", "lib/**", "packages/**"], signals: [{ type: "file-modified" }, { type: "error-encountered" }], threshold: 0.7 },
21
22
  reviewer: { concepts: ["code quality", "bug", "smell", "convention"], signals: [{ type: "compliance-violation" }], threshold: 0.6 },
22
23
  tester: { paths: ["**/*.test.*", "**/*.spec.*"], concepts: ["test", "coverage", "assertion"], signals: [{ type: "error-encountered" }, { type: "test-result" }], threshold: 0.5 },
23
- security: { symbols: ["^*", "#*-auth", "#*-middleware"], paths: ["auth/**", "middleware/**", "guards/**"], concepts: ["permission", "JWT", "session", "RBAC", "XSS", "injection"], signals: [{ type: "gate-added" }, { type: "route-created" }, { type: "gate-checked" }, { type: "compliance-violation" }], threshold: 0.4 }
24
+ security: { symbols: ["^*", "#*-auth", "#*-middleware"], paths: ["auth/**", "middleware/**", "guards/**"], concepts: ["permission", "JWT", "session", "RBAC", "XSS", "injection"], signals: [{ type: "gate-added" }, { type: "route-created" }, { type: "gate-checked" }, { type: "compliance-violation" }], threshold: 0.4 },
25
+ documentor: { paths: ["**/.purpose", "**/portal.yaml", ".paradigm/**"], concepts: ["purpose", "portal", "symbol", "documentation", "component", "gate", "flow"], signals: [{ type: "file-modified" }, { type: "compliance-violation" }, { type: "work-completed" }], threshold: 0.3 }
24
26
  };
25
27
  DEFAULT_COLLABORATION = {
26
28
  architect: { stance: "lead", debate: { will_challenge: true, evidence_required: true, escalate_to_human: true } },
27
29
  builder: { stance: "supportive", with: { architect: { stance: "supportive", can_contradict: false } } },
28
30
  reviewer: { stance: "advisory", debate: { will_challenge: true, evidence_required: true, escalate_to_human: true } },
29
31
  tester: { stance: "supportive", debate: { will_challenge: false, evidence_required: true, escalate_to_human: false } },
30
- security: { stance: "advisory", with: { architect: { stance: "peer", can_contradict: true }, builder: { stance: "advisory", review_output: true } }, debate: { will_challenge: true, evidence_required: true, escalate_to_human: true } }
32
+ security: { stance: "advisory", with: { architect: { stance: "peer", can_contradict: true }, builder: { stance: "advisory", review_output: true } }, debate: { will_challenge: true, evidence_required: true, escalate_to_human: true } },
33
+ documentor: { stance: "supportive", with: { architect: { stance: "supportive" }, builder: { stance: "supportive" }, reviewer: { stance: "supportive" }, security: { stance: "supportive" } }, debate: { will_challenge: false, evidence_required: false, escalate_to_human: false } }
31
34
  };
32
35
  }
33
36
  });
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ __esm,
4
+ __export
5
+ } from "./chunk-7N7GSU6K.js";
6
+
7
+ // ../paradigm-mcp/src/utils/session-work-log.ts
8
+ var session_work_log_exports = {};
9
+ __export(session_work_log_exports, {
10
+ appendSessionWorkEntry: () => appendSessionWorkEntry,
11
+ clearSessionWorkLog: () => clearSessionWorkLog,
12
+ getAgentEntries: () => getAgentEntries,
13
+ getAgentVerdicts: () => getAgentVerdicts,
14
+ getContributingAgents: () => getContributingAgents,
15
+ readSessionWorkLog: () => readSessionWorkLog
16
+ });
17
+ import * as fs from "fs";
18
+ import * as path from "path";
19
+ function appendSessionWorkEntry(rootDir, entry) {
20
+ try {
21
+ const filePath = path.join(rootDir, SESSION_LOG_FILE);
22
+ const dir = path.dirname(filePath);
23
+ if (!fs.existsSync(dir)) {
24
+ fs.mkdirSync(dir, { recursive: true });
25
+ }
26
+ if (fs.existsSync(filePath)) {
27
+ const content = fs.readFileSync(filePath, "utf8");
28
+ const lineCount = content.trim().split("\n").filter((l) => l.trim()).length;
29
+ if (lineCount >= MAX_ENTRIES) return;
30
+ }
31
+ const line = JSON.stringify(entry) + "\n";
32
+ fs.appendFileSync(filePath, line, "utf8");
33
+ } catch {
34
+ }
35
+ }
36
+ function readSessionWorkLog(rootDir) {
37
+ try {
38
+ const filePath = path.join(rootDir, SESSION_LOG_FILE);
39
+ if (!fs.existsSync(filePath)) return [];
40
+ return fs.readFileSync(filePath, "utf8").trim().split("\n").filter((line) => line.trim()).map((line) => {
41
+ try {
42
+ return JSON.parse(line);
43
+ } catch {
44
+ return null;
45
+ }
46
+ }).filter((e) => e !== null);
47
+ } catch {
48
+ return [];
49
+ }
50
+ }
51
+ function clearSessionWorkLog(rootDir) {
52
+ try {
53
+ const filePath = path.join(rootDir, SESSION_LOG_FILE);
54
+ if (fs.existsSync(filePath)) {
55
+ fs.writeFileSync(filePath, "", "utf8");
56
+ }
57
+ } catch {
58
+ }
59
+ }
60
+ function getContributingAgents(rootDir) {
61
+ const entries = readSessionWorkLog(rootDir);
62
+ const agents = /* @__PURE__ */ new Set();
63
+ for (const entry of entries) {
64
+ if (entry.agent) agents.add(entry.agent);
65
+ }
66
+ return Array.from(agents);
67
+ }
68
+ function getAgentEntries(rootDir, agentId) {
69
+ return readSessionWorkLog(rootDir).filter((e) => e.agent === agentId);
70
+ }
71
+ function getAgentVerdicts(rootDir, agentId) {
72
+ const entries = getAgentEntries(rootDir, agentId);
73
+ const contributions = entries.filter((e) => e.type === "agent-contribution");
74
+ const verdicts = entries.filter((e) => e.type === "user-verdict");
75
+ const pairs = [];
76
+ for (const contrib of contributions) {
77
+ pairs.push({ contribution: contrib });
78
+ }
79
+ for (const verdict of verdicts) {
80
+ const unpaired = pairs.find((p) => !p.verdict && p.contribution);
81
+ if (unpaired) {
82
+ unpaired.verdict = verdict;
83
+ } else {
84
+ pairs.push({ verdict });
85
+ }
86
+ }
87
+ return pairs;
88
+ }
89
+ var SESSION_LOG_FILE, MAX_ENTRIES;
90
+ var init_session_work_log = __esm({
91
+ "../paradigm-mcp/src/utils/session-work-log.ts"() {
92
+ SESSION_LOG_FILE = ".paradigm/events/session-log.jsonl";
93
+ MAX_ENTRIES = 200;
94
+ }
95
+ });
96
+
97
+ export {
98
+ appendSessionWorkEntry,
99
+ readSessionWorkLog,
100
+ clearSessionWorkLog,
101
+ getContributingAgents,
102
+ getAgentEntries,
103
+ getAgentVerdicts,
104
+ session_work_log_exports,
105
+ init_session_work_log
106
+ };
@@ -4,6 +4,10 @@ import {
4
4
  checkIntegrity,
5
5
  checkPurposeHealth
6
6
  } from "./chunk-L27I3CPZ.js";
7
+ import {
8
+ init_session_work_log,
9
+ session_work_log_exports
10
+ } from "./chunk-SDDCVUCV.js";
7
11
  import {
8
12
  init_lore_loader,
9
13
  loadLoreEntries,
@@ -2113,6 +2117,11 @@ var SessionTracker = class {
2113
2117
  */
2114
2118
  setRootDir(rootDir) {
2115
2119
  this.rootDir = rootDir;
2120
+ try {
2121
+ const { clearSessionWorkLog } = (init_session_work_log(), __toCommonJS(session_work_log_exports));
2122
+ clearSessionWorkLog(rootDir);
2123
+ } catch {
2124
+ }
2116
2125
  }
2117
2126
  createNewSession() {
2118
2127
  return {
@@ -3223,7 +3232,7 @@ async function buildRecoveryPreamble(rootDir) {
3223
3232
  } catch {
3224
3233
  }
3225
3234
  try {
3226
- const { loadNominations } = await import("./nomination-engine-HDWMN4IO.js");
3235
+ const { loadNominations } = await import("./nomination-engine-LPLCCDW2.js");
3227
3236
  const urgent = loadNominations(rootDir, { pending_only: true }).filter((n) => n.urgency === "critical" || n.urgency === "high");
3228
3237
  if (urgent.length > 0) {
3229
3238
  lines.push("");