hivemind-context-governance 1.3.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 (139) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/LICENSE +21 -0
  3. package/README.md +218 -0
  4. package/dist/cli/init.d.ts +23 -0
  5. package/dist/cli/init.d.ts.map +1 -0
  6. package/dist/cli/init.js +175 -0
  7. package/dist/cli/init.js.map +1 -0
  8. package/dist/cli.d.ts +14 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +162 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/dashboard/i18n/en.d.ts +43 -0
  13. package/dist/dashboard/i18n/en.d.ts.map +1 -0
  14. package/dist/dashboard/i18n/en.js +43 -0
  15. package/dist/dashboard/i18n/en.js.map +1 -0
  16. package/dist/dashboard/i18n/index.d.ts +10 -0
  17. package/dist/dashboard/i18n/index.d.ts.map +1 -0
  18. package/dist/dashboard/i18n/index.js +14 -0
  19. package/dist/dashboard/i18n/index.js.map +1 -0
  20. package/dist/dashboard/i18n/vi.d.ts +43 -0
  21. package/dist/dashboard/i18n/vi.d.ts.map +1 -0
  22. package/dist/dashboard/i18n/vi.js +43 -0
  23. package/dist/dashboard/i18n/vi.js.map +1 -0
  24. package/dist/dashboard/server.d.ts +23 -0
  25. package/dist/dashboard/server.d.ts.map +1 -0
  26. package/dist/dashboard/server.js +282 -0
  27. package/dist/dashboard/server.js.map +1 -0
  28. package/dist/hooks/compaction.d.ts +21 -0
  29. package/dist/hooks/compaction.d.ts.map +1 -0
  30. package/dist/hooks/compaction.js +79 -0
  31. package/dist/hooks/compaction.js.map +1 -0
  32. package/dist/hooks/index.d.ts +8 -0
  33. package/dist/hooks/index.d.ts.map +1 -0
  34. package/dist/hooks/index.js +7 -0
  35. package/dist/hooks/index.js.map +1 -0
  36. package/dist/hooks/session-lifecycle.d.ts +28 -0
  37. package/dist/hooks/session-lifecycle.d.ts.map +1 -0
  38. package/dist/hooks/session-lifecycle.js +94 -0
  39. package/dist/hooks/session-lifecycle.js.map +1 -0
  40. package/dist/hooks/tool-gate.d.ts +28 -0
  41. package/dist/hooks/tool-gate.d.ts.map +1 -0
  42. package/dist/hooks/tool-gate.js +152 -0
  43. package/dist/hooks/tool-gate.js.map +1 -0
  44. package/dist/index.d.ts +19 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +159 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/lib/complexity.d.ts +41 -0
  49. package/dist/lib/complexity.d.ts.map +1 -0
  50. package/dist/lib/complexity.js +67 -0
  51. package/dist/lib/complexity.js.map +1 -0
  52. package/dist/lib/context-refresh.d.ts +46 -0
  53. package/dist/lib/context-refresh.d.ts.map +1 -0
  54. package/dist/lib/context-refresh.js +77 -0
  55. package/dist/lib/context-refresh.js.map +1 -0
  56. package/dist/lib/index.d.ts +10 -0
  57. package/dist/lib/index.d.ts.map +1 -0
  58. package/dist/lib/index.js +10 -0
  59. package/dist/lib/index.js.map +1 -0
  60. package/dist/lib/logging.d.ts +13 -0
  61. package/dist/lib/logging.d.ts.map +1 -0
  62. package/dist/lib/logging.js +40 -0
  63. package/dist/lib/logging.js.map +1 -0
  64. package/dist/lib/persistence.d.ts +15 -0
  65. package/dist/lib/persistence.d.ts.map +1 -0
  66. package/dist/lib/persistence.js +62 -0
  67. package/dist/lib/persistence.js.map +1 -0
  68. package/dist/lib/planning-fs.d.ts +34 -0
  69. package/dist/lib/planning-fs.d.ts.map +1 -0
  70. package/dist/lib/planning-fs.js +134 -0
  71. package/dist/lib/planning-fs.js.map +1 -0
  72. package/dist/lib/sentiment.d.ts +31 -0
  73. package/dist/lib/sentiment.d.ts.map +1 -0
  74. package/dist/lib/sentiment.js +158 -0
  75. package/dist/lib/sentiment.js.map +1 -0
  76. package/dist/schemas/brain-state.d.ts +60 -0
  77. package/dist/schemas/brain-state.d.ts.map +1 -0
  78. package/dist/schemas/brain-state.js +176 -0
  79. package/dist/schemas/brain-state.js.map +1 -0
  80. package/dist/schemas/config.d.ts +51 -0
  81. package/dist/schemas/config.d.ts.map +1 -0
  82. package/dist/schemas/config.js +128 -0
  83. package/dist/schemas/config.js.map +1 -0
  84. package/dist/schemas/hierarchy.d.ts +22 -0
  85. package/dist/schemas/hierarchy.d.ts.map +1 -0
  86. package/dist/schemas/hierarchy.js +27 -0
  87. package/dist/schemas/hierarchy.js.map +1 -0
  88. package/dist/schemas/index.d.ts +7 -0
  89. package/dist/schemas/index.d.ts.map +1 -0
  90. package/dist/schemas/index.js +7 -0
  91. package/dist/schemas/index.js.map +1 -0
  92. package/dist/tools/compact-session.d.ts +15 -0
  93. package/dist/tools/compact-session.d.ts.map +1 -0
  94. package/dist/tools/compact-session.js +76 -0
  95. package/dist/tools/compact-session.js.map +1 -0
  96. package/dist/tools/declare-intent.d.ts +15 -0
  97. package/dist/tools/declare-intent.d.ts.map +1 -0
  98. package/dist/tools/declare-intent.js +83 -0
  99. package/dist/tools/declare-intent.js.map +1 -0
  100. package/dist/tools/index.d.ts +14 -0
  101. package/dist/tools/index.d.ts.map +1 -0
  102. package/dist/tools/index.js +14 -0
  103. package/dist/tools/index.js.map +1 -0
  104. package/dist/tools/map-context.d.ts +15 -0
  105. package/dist/tools/map-context.d.ts.map +1 -0
  106. package/dist/tools/map-context.js +85 -0
  107. package/dist/tools/map-context.js.map +1 -0
  108. package/dist/tools/self-rate.d.ts +15 -0
  109. package/dist/tools/self-rate.d.ts.map +1 -0
  110. package/dist/tools/self-rate.js +76 -0
  111. package/dist/tools/self-rate.js.map +1 -0
  112. package/package.json +71 -0
  113. package/src/cli/init.ts +214 -0
  114. package/src/cli.ts +178 -0
  115. package/src/dashboard/i18n/en.ts +43 -0
  116. package/src/dashboard/i18n/index.ts +20 -0
  117. package/src/dashboard/i18n/vi.ts +43 -0
  118. package/src/dashboard/server.ts +372 -0
  119. package/src/hooks/compaction.ts +104 -0
  120. package/src/hooks/index.ts +8 -0
  121. package/src/hooks/session-lifecycle.ts +133 -0
  122. package/src/hooks/tool-gate.ts +205 -0
  123. package/src/index.ts +206 -0
  124. package/src/lib/complexity.ts +96 -0
  125. package/src/lib/context-refresh.ts +107 -0
  126. package/src/lib/index.ts +10 -0
  127. package/src/lib/logging.ts +53 -0
  128. package/src/lib/persistence.ts +83 -0
  129. package/src/lib/planning-fs.ts +187 -0
  130. package/src/lib/sentiment.ts +183 -0
  131. package/src/schemas/brain-state.ts +257 -0
  132. package/src/schemas/config.ts +184 -0
  133. package/src/schemas/hierarchy.ts +58 -0
  134. package/src/schemas/index.ts +7 -0
  135. package/src/tools/compact-session.ts +97 -0
  136. package/src/tools/declare-intent.ts +104 -0
  137. package/src/tools/index.ts +14 -0
  138. package/src/tools/map-context.ts +105 -0
  139. package/src/tools/self-rate.ts +86 -0
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Tool Gate Hook — Configurable governance enforcement.
3
+ *
4
+ * Intercepts tool calls and applies governance based on mode:
5
+ * - strict: blocks writes until declare_intent called
6
+ * - assisted: warns but allows
7
+ * - permissive: silently allows, tracks for metrics
8
+ *
9
+ * P3: try/catch — never break tool execution
10
+ * P5: Config cached in closure — single disk read on first call
11
+ */
12
+ import { isSessionLocked, incrementTurnCount, shouldTriggerDriftWarning, addFileTouched, calculateDriftScore, setComplexityNudgeShown, } from "../schemas/brain-state.js";
13
+ import { createStateManager } from "../lib/persistence.js";
14
+ import { checkComplexity } from "../lib/complexity.js";
15
+ /** Tools that are always allowed regardless of governance state */
16
+ const EXEMPT_TOOLS = new Set([
17
+ "declare_intent",
18
+ "map_context",
19
+ "compact_session",
20
+ "read",
21
+ "grep",
22
+ "glob",
23
+ "find",
24
+ "list",
25
+ "search",
26
+ ]);
27
+ /** Write/edit tools that governance should gate */
28
+ const WRITE_TOOLS = new Set([
29
+ "write",
30
+ "edit",
31
+ "create",
32
+ "delete",
33
+ "rename",
34
+ "move",
35
+ "patch",
36
+ ]);
37
+ function isExemptTool(toolName) {
38
+ // Check full name match
39
+ if (EXEMPT_TOOLS.has(toolName))
40
+ return true;
41
+ // Check if tool name starts with an exempt prefix
42
+ for (const exempt of EXEMPT_TOOLS) {
43
+ if (toolName.startsWith(exempt))
44
+ return true;
45
+ }
46
+ return false;
47
+ }
48
+ function isWriteTool(toolName) {
49
+ for (const w of WRITE_TOOLS) {
50
+ if (toolName.includes(w))
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ /**
56
+ * Creates the tool gate hook.
57
+ *
58
+ * Hook factory pattern — captures config + logger + directory.
59
+ */
60
+ export function createToolGateHook(log, directory, config) {
61
+ const stateManager = createStateManager(directory);
62
+ return async (input) => {
63
+ try {
64
+ const { tool: toolName } = input;
65
+ // Always allow exempt tools (governance tools + read-only)
66
+ if (isExemptTool(toolName)) {
67
+ return { allowed: true };
68
+ }
69
+ // Load brain state
70
+ let state = await stateManager.load();
71
+ // No state = no session initialized yet
72
+ if (!state) {
73
+ switch (config.governance_mode) {
74
+ case "strict":
75
+ return {
76
+ allowed: false,
77
+ error: "SESSION NOT INITIALIZED. Use 'declare_intent' to start a session.",
78
+ };
79
+ case "assisted":
80
+ await log.warn(`Tool "${toolName}" called without session. Consider using declare_intent.`);
81
+ return {
82
+ allowed: true,
83
+ warning: "No session initialized. Consider calling declare_intent.",
84
+ };
85
+ case "permissive":
86
+ return { allowed: true };
87
+ }
88
+ }
89
+ // Session is locked — governance gate
90
+ if (state && isSessionLocked(state)) {
91
+ switch (config.governance_mode) {
92
+ case "strict":
93
+ if (isWriteTool(toolName)) {
94
+ return {
95
+ allowed: false,
96
+ error: "SESSION LOCKED. Use 'declare_intent' to unlock before writing.",
97
+ };
98
+ }
99
+ return { allowed: true };
100
+ case "assisted":
101
+ if (isWriteTool(toolName)) {
102
+ await log.warn(`Write tool "${toolName}" used while session locked.`);
103
+ return {
104
+ allowed: true,
105
+ warning: "Session locked. Declare your intent for better tracking.",
106
+ };
107
+ }
108
+ return { allowed: true };
109
+ case "permissive":
110
+ return { allowed: true };
111
+ }
112
+ }
113
+ // Session is open — track activity
114
+ if (state) {
115
+ state = incrementTurnCount(state);
116
+ // Track file touches for write tools (tool name used as proxy)
117
+ if (isWriteTool(toolName)) {
118
+ state = addFileTouched(state, `[via ${toolName}]`);
119
+ }
120
+ // Update drift score
121
+ state.metrics.drift_score = calculateDriftScore(state);
122
+ // Save updated state
123
+ await stateManager.save(state);
124
+ // Check drift warning
125
+ if (shouldTriggerDriftWarning(state, config.max_turns_before_warning)) {
126
+ await log.warn(`Drift warning: ${state.metrics.turn_count} turns, score: ${state.metrics.drift_score}/100. Consider using map_context to re-focus.`);
127
+ if (config.governance_mode !== "permissive") {
128
+ return {
129
+ allowed: true,
130
+ warning: `Drift detected (${state.metrics.drift_score}/100). Use map_context to re-focus.`,
131
+ };
132
+ }
133
+ }
134
+ // Check complexity and show nudge (once per session)
135
+ const complexityCheck = checkComplexity(state);
136
+ if (complexityCheck.isComplex && !state.complexity_nudge_shown) {
137
+ await log.warn(`[Nudge] ${complexityCheck.message}`);
138
+ // Mark nudge as shown
139
+ state = setComplexityNudgeShown(state);
140
+ await stateManager.save(state);
141
+ }
142
+ }
143
+ return { allowed: true };
144
+ }
145
+ catch (error) {
146
+ // P3: Never break tool execution
147
+ await log.error(`Tool gate error: ${error}`);
148
+ return { allowed: true };
149
+ }
150
+ };
151
+ }
152
+ //# sourceMappingURL=tool-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-gate.js","sourceRoot":"","sources":["../../src/hooks/tool-gate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * HiveMind Context Governance — OpenCode Plugin Entry Point
3
+ *
4
+ * Context-aware governance layer that prevents drift and manages
5
+ * session state across lifecycles. 3 tools, 3 hooks, configurable modes.
6
+ *
7
+ * CRITICAL: NO console.log anywhere — breaks TUI rendering.
8
+ */
9
+ import type { Plugin } from "@opencode-ai/plugin";
10
+ /**
11
+ * Plugin factory — hook factory pattern with captured state.
12
+ *
13
+ * GUARD: If .opencode/planning/ doesn't exist, the plugin was not initialized.
14
+ * Return empty hooks to avoid zombie directory creation.
15
+ * The user must run `hivemind init` first.
16
+ */
17
+ declare const hivemind: Plugin;
18
+ export default hivemind;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAqBjD;;;;;;GAMG;AACH,QAAA,MAAM,QAAQ,EAAE,MAsKf,CAAA;AAED,eAAe,QAAQ,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,159 @@
1
+ /**
2
+ * HiveMind Context Governance — OpenCode Plugin Entry Point
3
+ *
4
+ * Context-aware governance layer that prevents drift and manages
5
+ * session state across lifecycles. 3 tools, 3 hooks, configurable modes.
6
+ *
7
+ * CRITICAL: NO console.log anywhere — breaks TUI rendering.
8
+ */
9
+ import { existsSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { createLogger } from "./lib/logging.js";
12
+ import { loadConfig } from "./lib/persistence.js";
13
+ import { createToolGateHook, createSessionLifecycleHook, createCompactionHook, } from "./hooks/index.js";
14
+ import { createDeclareIntentTool, createMapContextTool, createCompactSessionTool, createSelfRateTool, } from "./tools/index.js";
15
+ import { createStateManager } from "./lib/persistence.js";
16
+ import { trackToolCall, addSentimentSignals } from "./schemas/brain-state.js";
17
+ import { detectSentiment } from "./lib/sentiment.js";
18
+ import { checkRefreshTrigger } from "./lib/context-refresh.js";
19
+ /**
20
+ * Plugin factory — hook factory pattern with captured state.
21
+ *
22
+ * GUARD: If .opencode/planning/ doesn't exist, the plugin was not initialized.
23
+ * Return empty hooks to avoid zombie directory creation.
24
+ * The user must run `hivemind init` first.
25
+ */
26
+ const hivemind = async ({ directory }) => {
27
+ // Init guard: skip if not initialized
28
+ const planningDir = join(directory, ".opencode", "planning");
29
+ if (!existsSync(planningDir)) {
30
+ return {};
31
+ }
32
+ const log = await createLogger(join(directory, ".opencode", "planning", "logs"), "hivemind-core");
33
+ await log.info(`HiveMind loaded for ${directory}`);
34
+ // Load config
35
+ const config = await loadConfig(directory);
36
+ // Create hook instances
37
+ const toolGateHook = createToolGateHook(log, directory, config);
38
+ const sessionLifecycleHook = createSessionLifecycleHook(log, directory, config);
39
+ const compactionHook = createCompactionHook(log, directory);
40
+ // Create tool instances with directory bound via closure
41
+ const declare_intent = createDeclareIntentTool(directory);
42
+ const map_context = createMapContextTool(directory);
43
+ const compact_session = createCompactSessionTool(directory);
44
+ const self_rate = createSelfRateTool(directory);
45
+ return {
46
+ /**
47
+ * Session lifecycle events.
48
+ */
49
+ event: async ({ event }) => {
50
+ try {
51
+ await log.info(`event: ${event.type}`);
52
+ }
53
+ catch {
54
+ // P3: Never crash on event handling
55
+ }
56
+ },
57
+ /**
58
+ * Tool gate — configurable governance enforcement.
59
+ * Logs warnings/errors based on governance mode.
60
+ * Uses soft enforcement — log, don't hard-block.
61
+ */
62
+ "tool.execute.before": async (input) => {
63
+ const result = await toolGateHook({
64
+ sessionID: input.sessionID,
65
+ tool: input.tool,
66
+ });
67
+ if (!result.allowed) {
68
+ await log.warn(`GOVERNANCE BLOCK: ${result.error ?? "Operation blocked."} (tool: ${input.tool})`);
69
+ }
70
+ if (result.warning) {
71
+ await log.warn(result.warning);
72
+ }
73
+ },
74
+ /**
75
+ * Tool execution tracking — auto-rating on completion.
76
+ * Tracks successful tool calls for auto-health calculation.
77
+ */
78
+ "tool.execute.after": async (_input) => {
79
+ try {
80
+ const stateManager = createStateManager(directory);
81
+ let state = await stateManager.load();
82
+ if (state) {
83
+ // Track successful tool call
84
+ state = trackToolCall(state, true);
85
+ await stateManager.save(state);
86
+ // Log health score periodically (every 10 calls)
87
+ if (state.metrics.total_tool_calls % 10 === 0) {
88
+ await log.info(`[AutoHealth] Score: ${state.metrics.auto_health_score}/100 (${state.metrics.successful_tool_calls}/${state.metrics.total_tool_calls} successful)`);
89
+ }
90
+ }
91
+ }
92
+ catch {
93
+ // P3: Never crash on tracking
94
+ }
95
+ },
96
+ /**
97
+ * System prompt — injects hierarchy + governance status.
98
+ * Budget: ≤250 tokens. ADD, not REPLACE.
99
+ */
100
+ "experimental.chat.system.transform": async (input, output) => {
101
+ await sessionLifecycleHook(input, output);
102
+ },
103
+ /**
104
+ * Chat message hook — sentiment detection on user messages.
105
+ * Detects negative signals (frustration, confusion) and updates drift score.
106
+ */
107
+ "chat.message": async (_input, output) => {
108
+ try {
109
+ // Extract text from user message parts
110
+ const messageText = output.parts
111
+ .filter((p) => p.type === "text")
112
+ .map((p) => p.text ?? p.content ?? "")
113
+ .join(" ");
114
+ if (!messageText)
115
+ return;
116
+ const stateManager = createStateManager(directory);
117
+ let state = await stateManager.load();
118
+ if (state) {
119
+ const signals = detectSentiment(messageText, state.metrics.turn_count, "user");
120
+ if (signals.length > 0) {
121
+ state = addSentimentSignals(state, signals);
122
+ await stateManager.save(state);
123
+ await log.info(`[Sentiment] Detected ${signals.length} signals: ${signals.map((s) => s.type).join(", ")}`);
124
+ // Check if refresh trigger threshold reached
125
+ const refreshCheck = checkRefreshTrigger(state);
126
+ if (refreshCheck.shouldRefresh) {
127
+ await log.warn(refreshCheck.message);
128
+ }
129
+ }
130
+ }
131
+ }
132
+ catch {
133
+ // P3: Never crash on sentiment detection
134
+ }
135
+ },
136
+ /**
137
+ * Compaction — preserves hierarchy context across LLM compaction.
138
+ * Budget: ≤500 tokens.
139
+ */
140
+ "experimental.session.compacting": async (input, output) => {
141
+ await compactionHook(input, output);
142
+ },
143
+ /**
144
+ * 4 lifecycle tools:
145
+ * declare_intent — unlock session, set mode/focus
146
+ * map_context — update hierarchy level
147
+ * compact_session — archive + reset
148
+ * self_rate — agent self-assessment
149
+ */
150
+ tool: {
151
+ declare_intent,
152
+ map_context,
153
+ compact_session,
154
+ self_rate,
155
+ },
156
+ };
157
+ };
158
+ export default hivemind;
159
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACjD,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,oBAAoB,GACrB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D;;;;;;GAMG;AACH,MAAM,QAAQ,GAAW,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;IAC/C,sCAAsC;IACtC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,UAAU,CAAC,CAAA;IAC5D,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,YAAY,CAC5B,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,EAChD,eAAe,CAChB,CAAA;IAED,MAAM,GAAG,CAAC,IAAI,CAAC,uBAAuB,SAAS,EAAE,CAAC,CAAA;IAElD,cAAc;IACd,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,SAAS,CAAC,CAAA;IAE1C,wBAAwB;IACxB,MAAM,YAAY,GAAG,kBAAkB,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;IAC/D,MAAM,oBAAoB,GAAG,0BAA0B,CACrD,GAAG,EACH,SAAS,EACT,MAAM,CACP,CAAA;IACD,MAAM,cAAc,GAAG,oBAAoB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAA;IAE3D,yDAAyD;IACzD,MAAM,cAAc,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAA;IACzD,MAAM,WAAW,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAA;IACnD,MAAM,eAAe,GAAG,wBAAwB,CAAC,SAAS,CAAC,CAAA;IAC3D,MAAM,SAAS,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;IAE/C,OAAO;QACL;;WAEG;QACH,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,oCAAoC;YACtC,CAAC;QACH,CAAC;QAED;;;;WAIG;QACH,qBAAqB,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;gBAChC,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,IAAI,EAAE,KAAK,CAAC,IAAI;aACjB,CAAC,CAAA;YAEF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,GAAG,CAAC,IAAI,CACZ,qBAAqB,MAAM,CAAC,KAAK,IAAI,oBAAoB,WAAW,KAAK,CAAC,IAAI,GAAG,CAClF,CAAA;YACH,CAAC;YAED,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;YAChC,CAAC;QACH,CAAC;QAED;;;WAGG;QACH,oBAAoB,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;gBAClD,IAAI,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,CAAA;gBAErC,IAAI,KAAK,EAAE,CAAC;oBACV,6BAA6B;oBAC7B,KAAK,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;oBAClC,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;oBAE9B,iDAAiD;oBACjD,IAAI,KAAK,CAAC,OAAO,CAAC,gBAAgB,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC;wBAC9C,MAAM,GAAG,CAAC,IAAI,CACZ,uBAAuB,KAAK,CAAC,OAAO,CAAC,iBAAiB,SAAS,KAAK,CAAC,OAAO,CAAC,qBAAqB,IAAI,KAAK,CAAC,OAAO,CAAC,gBAAgB,cAAc,CACnJ,CAAA;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,8BAA8B;YAChC,CAAC;QACH,CAAC;QAED;;;WAGG;QACH,oCAAoC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YAC5D,MAAM,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QAC3C,CAAC;QAED;;;WAGG;QACH,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACvC,IAAI,CAAC;gBACH,uCAAuC;gBACvC,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK;qBAC7B,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;qBACrC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC;qBAC1C,IAAI,CAAC,GAAG,CAAC,CAAA;gBAEZ,IAAI,CAAC,WAAW;oBAAE,OAAM;gBAExB,MAAM,YAAY,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAA;gBAClD,IAAI,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,CAAA;gBAErC,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,OAAO,GAAG,eAAe,CAC7B,WAAW,EACX,KAAK,CAAC,OAAO,CAAC,UAAU,EACxB,MAAM,CACP,CAAA;oBAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACvB,KAAK,GAAG,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;wBAC3C,MAAM,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;wBAE9B,MAAM,GAAG,CAAC,IAAI,CACZ,wBAAwB,OAAO,CAAC,MAAM,aAAa,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3F,CAAA;wBAED,6CAA6C;wBAC7C,MAAM,YAAY,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAA;wBAC/C,IAAI,YAAY,CAAC,aAAa,EAAE,CAAC;4BAC/B,MAAM,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAA;wBACtC,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,yCAAyC;YAC3C,CAAC;QACH,CAAC;QAED;;;WAGG;QACH,iCAAiC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YACzD,MAAM,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QACrC,CAAC;QAED;;;;;;WAMG;QACH,IAAI,EAAE;YACJ,cAAc;YACd,WAAW;YACX,eAAe;YACf,SAAS;SACV;KACF,CAAA;AACH,CAAC,CAAA;AAED,eAAe,QAAQ,CAAA"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Complexity Detection Module
3
+ *
4
+ * Detects when sessions get complex based on:
5
+ * - Files touched count (unique file paths)
6
+ * - Turn count (total tool calls since last declare_intent)
7
+ *
8
+ * Configurable thresholds (default: 3 files OR 5 turns)
9
+ */
10
+ import type { BrainState } from "../schemas/brain-state.js";
11
+ export interface ComplexityThreshold {
12
+ maxFiles: number;
13
+ maxTurns: number;
14
+ }
15
+ export interface ComplexityCheckResult {
16
+ isComplex: boolean;
17
+ filesCount: number;
18
+ turnsCount: number;
19
+ threshold: ComplexityThreshold;
20
+ message: string;
21
+ }
22
+ /** Default complexity threshold */
23
+ export declare const DEFAULT_COMPLEXITY_THRESHOLD: ComplexityThreshold;
24
+ /**
25
+ * Check if current session complexity exceeds thresholds
26
+ */
27
+ export declare function checkComplexity(state: BrainState, threshold?: ComplexityThreshold): ComplexityCheckResult;
28
+ /**
29
+ * Check if nudge should be shown (only once per session)
30
+ */
31
+ export declare function shouldShowComplexityNudge(state: BrainState, nudgeShown: boolean): boolean;
32
+ /**
33
+ * Format complexity for display
34
+ */
35
+ export declare function formatComplexity(state: BrainState): string;
36
+ /**
37
+ * Get complexity percentage (0-100)
38
+ * Based on how close to thresholds
39
+ */
40
+ export declare function getComplexityPercentage(state: BrainState, threshold?: ComplexityThreshold): number;
41
+ //# sourceMappingURL=complexity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complexity.d.ts","sourceRoot":"","sources":["../../src/lib/complexity.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,mCAAmC;AACnC,eAAO,MAAM,4BAA4B,EAAE,mBAG1C,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,UAAU,EACjB,SAAS,GAAE,mBAAkD,GAC5D,qBAAqB,CAuBvB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,OAAO,GAClB,OAAO,CAKT;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAG1D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,UAAU,EACjB,SAAS,GAAE,mBAAkD,GAC5D,MAAM,CAKR"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Complexity Detection Module
3
+ *
4
+ * Detects when sessions get complex based on:
5
+ * - Files touched count (unique file paths)
6
+ * - Turn count (total tool calls since last declare_intent)
7
+ *
8
+ * Configurable thresholds (default: 3 files OR 5 turns)
9
+ */
10
+ /** Default complexity threshold */
11
+ export const DEFAULT_COMPLEXITY_THRESHOLD = {
12
+ maxFiles: 3,
13
+ maxTurns: 5,
14
+ };
15
+ /**
16
+ * Check if current session complexity exceeds thresholds
17
+ */
18
+ export function checkComplexity(state, threshold = DEFAULT_COMPLEXITY_THRESHOLD) {
19
+ const filesCount = state.metrics.files_touched.length;
20
+ const turnsCount = state.metrics.turn_count;
21
+ const filesExceeded = filesCount >= threshold.maxFiles;
22
+ const turnsExceeded = turnsCount >= threshold.maxTurns;
23
+ const isComplex = filesExceeded || turnsExceeded;
24
+ let message = "Complexity normal";
25
+ if (isComplex) {
26
+ const reasons = [];
27
+ if (filesExceeded)
28
+ reasons.push(`${filesCount} files touched`);
29
+ if (turnsExceeded)
30
+ reasons.push(`${turnsCount} turns`);
31
+ message = `Complexity rising (${reasons.join(", ")}). Consider declare_intent.`;
32
+ }
33
+ return {
34
+ isComplex,
35
+ filesCount,
36
+ turnsCount,
37
+ threshold,
38
+ message,
39
+ };
40
+ }
41
+ /**
42
+ * Check if nudge should be shown (only once per session)
43
+ */
44
+ export function shouldShowComplexityNudge(state, nudgeShown) {
45
+ if (nudgeShown)
46
+ return false;
47
+ const check = checkComplexity(state);
48
+ return check.isComplex;
49
+ }
50
+ /**
51
+ * Format complexity for display
52
+ */
53
+ export function formatComplexity(state) {
54
+ const check = checkComplexity(state);
55
+ return `${check.filesCount} files, ${check.turnsCount} turns`;
56
+ }
57
+ /**
58
+ * Get complexity percentage (0-100)
59
+ * Based on how close to thresholds
60
+ */
61
+ export function getComplexityPercentage(state, threshold = DEFAULT_COMPLEXITY_THRESHOLD) {
62
+ const filesRatio = state.metrics.files_touched.length / threshold.maxFiles;
63
+ const turnsRatio = state.metrics.turn_count / threshold.maxTurns;
64
+ const maxRatio = Math.max(filesRatio, turnsRatio);
65
+ return Math.min(100, Math.round(maxRatio * 100));
66
+ }
67
+ //# sourceMappingURL=complexity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complexity.js","sourceRoot":"","sources":["../../src/lib/complexity.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiBH,mCAAmC;AACnC,MAAM,CAAC,MAAM,4BAA4B,GAAwB;IAC/D,QAAQ,EAAE,CAAC;IACX,QAAQ,EAAE,CAAC;CACZ,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAiB,EACjB,YAAiC,4BAA4B;IAE7D,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC;IACtD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;IAE5C,MAAM,aAAa,GAAG,UAAU,IAAI,SAAS,CAAC,QAAQ,CAAC;IACvD,MAAM,aAAa,GAAG,UAAU,IAAI,SAAS,CAAC,QAAQ,CAAC;IACvD,MAAM,SAAS,GAAG,aAAa,IAAI,aAAa,CAAC;IAEjD,IAAI,OAAO,GAAG,mBAAmB,CAAC;IAClC,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,IAAI,aAAa;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,gBAAgB,CAAC,CAAC;QAC/D,IAAI,aAAa;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,UAAU,QAAQ,CAAC,CAAC;QACvD,OAAO,GAAG,sBAAsB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,6BAA6B,CAAC;IAClF,CAAC;IAED,OAAO;QACL,SAAS;QACT,UAAU;QACV,UAAU;QACV,SAAS;QACT,OAAO;KACR,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB,CACvC,KAAiB,EACjB,UAAmB;IAEnB,IAAI,UAAU;QAAE,OAAO,KAAK,CAAC;IAE7B,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACrC,OAAO,KAAK,CAAC,SAAS,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAiB;IAChD,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACrC,OAAO,GAAG,KAAK,CAAC,UAAU,WAAW,KAAK,CAAC,UAAU,QAAQ,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAiB,EACjB,YAAiC,4BAA4B;IAE7D,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC;IAC3E,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,QAAQ,CAAC;IACjE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAClD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Context Refresh Trigger Module
3
+ *
4
+ * Triggers context refresh recommendations when negative sentiment
5
+ * signals exceed thresholds.
6
+ *
7
+ * Threshold: 2 negative signals within 5 turns triggers refresh warning
8
+ */
9
+ import type { BrainState } from "../schemas/brain-state.js";
10
+ import type { SentimentSignal } from "./sentiment.js";
11
+ export interface RefreshTriggerResult {
12
+ shouldRefresh: boolean;
13
+ signalCount: number;
14
+ windowSize: number;
15
+ message: string;
16
+ }
17
+ /** Default threshold configuration */
18
+ export declare const DEFAULT_REFRESH_THRESHOLD: {
19
+ signalCount: number;
20
+ windowSize: number;
21
+ };
22
+ /**
23
+ * Check if context refresh should be triggered
24
+ * Based on signal count within recent turn window
25
+ */
26
+ export declare function checkRefreshTrigger(state: BrainState, threshold?: {
27
+ signalCount: number;
28
+ windowSize: number;
29
+ }): RefreshTriggerResult;
30
+ /**
31
+ * Get recent signals for display
32
+ */
33
+ export declare function getRecentSignals(state: BrainState, windowSize?: number): SentimentSignal[];
34
+ /**
35
+ * Format signals for logging
36
+ */
37
+ export declare function formatSignalsForLog(signals: SentimentSignal[]): string;
38
+ /**
39
+ * Check if drift warning should be added to active.md
40
+ */
41
+ export declare function shouldAddDriftWarning(state: BrainState): boolean;
42
+ /**
43
+ * Generate drift warning content for active.md
44
+ */
45
+ export declare function generateDriftWarning(state: BrainState): string;
46
+ //# sourceMappingURL=context-refresh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-refresh.d.ts","sourceRoot":"","sources":["../../src/lib/context-refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sCAAsC;AACtC,eAAO,MAAM,yBAAyB;;;CAGrC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,UAAU,EACjB,SAAS;;;CAA4B,GACpC,oBAAoB,CAmBtB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,UAAU,EACjB,UAAU,GAAE,MAAU,GACrB,eAAe,EAAE,CAOnB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,MAAM,CAMtE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAGhE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAgB9D"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Context Refresh Trigger Module
3
+ *
4
+ * Triggers context refresh recommendations when negative sentiment
5
+ * signals exceed thresholds.
6
+ *
7
+ * Threshold: 2 negative signals within 5 turns triggers refresh warning
8
+ */
9
+ /** Default threshold configuration */
10
+ export const DEFAULT_REFRESH_THRESHOLD = {
11
+ signalCount: 2,
12
+ windowSize: 5, // turns
13
+ };
14
+ /**
15
+ * Check if context refresh should be triggered
16
+ * Based on signal count within recent turn window
17
+ */
18
+ export function checkRefreshTrigger(state, threshold = DEFAULT_REFRESH_THRESHOLD) {
19
+ const currentTurn = state.metrics.turn_count;
20
+ const windowStart = Math.max(0, currentTurn - threshold.windowSize);
21
+ // Count signals within the window
22
+ const recentSignals = state.sentiment_signals.filter((signal) => signal.turn_number >= windowStart && signal.turn_number <= currentTurn);
23
+ const shouldRefresh = recentSignals.length >= threshold.signalCount;
24
+ return {
25
+ shouldRefresh,
26
+ signalCount: recentSignals.length,
27
+ windowSize: threshold.windowSize,
28
+ message: shouldRefresh
29
+ ? `[ContextRefresh] Drift detected (${recentSignals.length} negative signals in last ${threshold.windowSize} turns). Consider compact_session.`
30
+ : `Context stable (${recentSignals.length} signals in last ${threshold.windowSize} turns)`,
31
+ };
32
+ }
33
+ /**
34
+ * Get recent signals for display
35
+ */
36
+ export function getRecentSignals(state, windowSize = 5) {
37
+ const currentTurn = state.metrics.turn_count;
38
+ const windowStart = Math.max(0, currentTurn - windowSize);
39
+ return state.sentiment_signals.filter((signal) => signal.turn_number >= windowStart && signal.turn_number <= currentTurn);
40
+ }
41
+ /**
42
+ * Format signals for logging
43
+ */
44
+ export function formatSignalsForLog(signals) {
45
+ if (signals.length === 0)
46
+ return "No recent signals";
47
+ return signals
48
+ .map((s) => `[Turn ${s.turn_number}] ${s.type}: "${s.matched_text}"`)
49
+ .join("\n ");
50
+ }
51
+ /**
52
+ * Check if drift warning should be added to active.md
53
+ */
54
+ export function shouldAddDriftWarning(state) {
55
+ const result = checkRefreshTrigger(state);
56
+ return result.shouldRefresh;
57
+ }
58
+ /**
59
+ * Generate drift warning content for active.md
60
+ */
61
+ export function generateDriftWarning(state) {
62
+ const recentSignals = getRecentSignals(state);
63
+ return [
64
+ "",
65
+ "## ⚠️ Drift Warning",
66
+ "",
67
+ `**Detected**: ${recentSignals.length} negative signals in recent turns`,
68
+ "",
69
+ "**Recommendation**: Consider using `compact_session` to reset context,",
70
+ "or `map_context` to refocus on current objectives.",
71
+ "",
72
+ "**Recent Signals**:",
73
+ ...recentSignals.map((s) => `- Turn ${s.turn_number}: ${s.type} (${s.matched_text})`),
74
+ "",
75
+ ].join("\n");
76
+ }
77
+ //# sourceMappingURL=context-refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-refresh.js","sourceRoot":"","sources":["../../src/lib/context-refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAYH,sCAAsC;AACtC,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,WAAW,EAAE,CAAC;IACd,UAAU,EAAE,CAAC,EAAE,QAAQ;CACxB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAiB,EACjB,SAAS,GAAG,yBAAyB;IAErC,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;IAEpE,kCAAkC;IAClC,MAAM,aAAa,GAAG,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAClD,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,IAAI,WAAW,IAAI,MAAM,CAAC,WAAW,IAAI,WAAW,CACnF,CAAC;IAEF,MAAM,aAAa,GAAG,aAAa,CAAC,MAAM,IAAI,SAAS,CAAC,WAAW,CAAC;IAEpE,OAAO;QACL,aAAa;QACb,WAAW,EAAE,aAAa,CAAC,MAAM;QACjC,UAAU,EAAE,SAAS,CAAC,UAAU;QAChC,OAAO,EAAE,aAAa;YACpB,CAAC,CAAC,oCAAoC,aAAa,CAAC,MAAM,6BAA6B,SAAS,CAAC,UAAU,oCAAoC;YAC/I,CAAC,CAAC,mBAAmB,aAAa,CAAC,MAAM,oBAAoB,SAAS,CAAC,UAAU,SAAS;KAC7F,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAiB,EACjB,aAAqB,CAAC;IAEtB,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC,CAAC;IAE1D,OAAO,KAAK,CAAC,iBAAiB,CAAC,MAAM,CACnC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,IAAI,WAAW,IAAI,MAAM,CAAC,WAAW,IAAI,WAAW,CACnF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA0B;IAC5D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,mBAAmB,CAAC;IAErD,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,YAAY,GAAG,CAAC;SACpE,IAAI,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAiB;IACrD,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC,aAAa,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAiB;IACpD,MAAM,aAAa,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAE9C,OAAO;QACL,EAAE;QACF,qBAAqB;QACrB,EAAE;QACF,iBAAiB,aAAa,CAAC,MAAM,mCAAmC;QACxE,EAAE;QACF,wEAAwE;QACxE,oDAAoD;QACpD,EAAE;QACF,qBAAqB;QACrB,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,YAAY,GAAG,CAAC;QACrF,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Lib barrel exports
3
+ */
4
+ export * from "./logging.js";
5
+ export * from "./persistence.js";
6
+ export * from "./planning-fs.js";
7
+ export * from "./sentiment.js";
8
+ export * from "./context-refresh.js";
9
+ export * from "./complexity.js";
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Lib barrel exports
3
+ */
4
+ export * from "./logging.js";
5
+ export * from "./persistence.js";
6
+ export * from "./planning-fs.js";
7
+ export * from "./sentiment.js";
8
+ export * from "./context-refresh.js";
9
+ export * from "./complexity.js";
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * TUI-safe logging
3
+ * File-based logging only - never use console.log
4
+ */
5
+ export interface Logger {
6
+ debug: (message: string) => Promise<void>;
7
+ info: (message: string) => Promise<void>;
8
+ warn: (message: string) => Promise<void>;
9
+ error: (message: string) => Promise<void>;
10
+ }
11
+ export declare function createLogger(logDir: string, service: string): Promise<Logger>;
12
+ export declare const noopLogger: Logger;
13
+ //# sourceMappingURL=logging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../src/lib/logging.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CA0BjB;AAGD,eAAO,MAAM,UAAU,EAAE,MAKxB,CAAC"}