@mainahq/core 1.0.2 → 1.1.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/package.json +1 -1
- package/src/ai/__tests__/availability.test.ts +131 -0
- package/src/ai/__tests__/delegation.test.ts +55 -1
- package/src/ai/availability.ts +23 -0
- package/src/ai/delegation.ts +5 -3
- package/src/context/__tests__/budget.test.ts +29 -6
- package/src/context/__tests__/engine.test.ts +1 -0
- package/src/context/__tests__/selector.test.ts +23 -3
- package/src/context/__tests__/wiki.test.ts +349 -0
- package/src/context/budget.ts +12 -8
- package/src/context/engine.ts +37 -0
- package/src/context/selector.ts +30 -4
- package/src/context/wiki.ts +296 -0
- package/src/db/index.ts +12 -0
- package/src/feedback/__tests__/capture.test.ts +166 -0
- package/src/feedback/__tests__/signals.test.ts +144 -0
- package/src/feedback/__tests__/tmp-capture-1775575256633-lah0etnzlj/feedback.db +0 -0
- package/src/feedback/__tests__/tmp-capture-1775575256640-2xmjme4qraa/feedback.db +0 -0
- package/src/feedback/capture.ts +102 -0
- package/src/feedback/signals.ts +68 -0
- package/src/index.ts +108 -1
- package/src/init/__tests__/init.test.ts +477 -18
- package/src/init/index.ts +419 -13
- package/src/language/__tests__/__fixtures__/detect/composer.lock +1 -0
- package/src/prompts/defaults/index.ts +3 -1
- package/src/prompts/defaults/wiki-compile.md +20 -0
- package/src/prompts/defaults/wiki-query.md +18 -0
- package/src/stats/__tests__/tool-usage.test.ts +133 -0
- package/src/stats/tracker.ts +92 -0
- package/src/verify/__tests__/builtin.test.ts +270 -0
- package/src/verify/__tests__/pipeline.test.ts +11 -8
- package/src/verify/builtin.ts +350 -0
- package/src/verify/pipeline.ts +32 -2
- package/src/verify/tools/__tests__/wiki-lint.test.ts +784 -0
- package/src/verify/tools/wiki-lint-runner.ts +38 -0
- package/src/verify/tools/wiki-lint.ts +898 -0
- package/src/wiki/__tests__/compiler.test.ts +389 -0
- package/src/wiki/__tests__/extractors/code.test.ts +99 -0
- package/src/wiki/__tests__/extractors/decision.test.ts +323 -0
- package/src/wiki/__tests__/extractors/feature.test.ts +186 -0
- package/src/wiki/__tests__/extractors/workflow.test.ts +131 -0
- package/src/wiki/__tests__/graph.test.ts +344 -0
- package/src/wiki/__tests__/hooks.test.ts +119 -0
- package/src/wiki/__tests__/indexer.test.ts +285 -0
- package/src/wiki/__tests__/linker.test.ts +230 -0
- package/src/wiki/__tests__/louvain.test.ts +229 -0
- package/src/wiki/__tests__/query.test.ts +316 -0
- package/src/wiki/__tests__/schema.test.ts +114 -0
- package/src/wiki/__tests__/signals.test.ts +474 -0
- package/src/wiki/__tests__/state.test.ts +168 -0
- package/src/wiki/__tests__/tracking.test.ts +118 -0
- package/src/wiki/__tests__/types.test.ts +387 -0
- package/src/wiki/compiler.ts +1075 -0
- package/src/wiki/extractors/code.ts +90 -0
- package/src/wiki/extractors/decision.ts +217 -0
- package/src/wiki/extractors/feature.ts +206 -0
- package/src/wiki/extractors/workflow.ts +112 -0
- package/src/wiki/graph.ts +445 -0
- package/src/wiki/hooks.ts +49 -0
- package/src/wiki/indexer.ts +105 -0
- package/src/wiki/linker.ts +117 -0
- package/src/wiki/louvain.ts +190 -0
- package/src/wiki/prompts/compile-architecture.md +59 -0
- package/src/wiki/prompts/compile-decision.md +66 -0
- package/src/wiki/prompts/compile-entity.md +56 -0
- package/src/wiki/prompts/compile-feature.md +60 -0
- package/src/wiki/prompts/compile-module.md +42 -0
- package/src/wiki/prompts/wiki-query.md +25 -0
- package/src/wiki/query.ts +338 -0
- package/src/wiki/schema.ts +111 -0
- package/src/wiki/signals.ts +368 -0
- package/src/wiki/state.ts +89 -0
- package/src/wiki/tracking.ts +30 -0
- package/src/wiki/types.ts +169 -0
- package/src/workflow/context.ts +26 -0
package/package.json
CHANGED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
+
import { checkAIAvailability } from "../availability";
|
|
3
|
+
|
|
4
|
+
describe("checkAIAvailability", () => {
|
|
5
|
+
const originalEnv = { ...process.env };
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
// Clear all relevant env vars before each test
|
|
9
|
+
delete process.env.MAINA_API_KEY;
|
|
10
|
+
delete process.env.OPENROUTER_API_KEY;
|
|
11
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
12
|
+
delete process.env.MAINA_HOST_MODE;
|
|
13
|
+
delete process.env.CLAUDECODE;
|
|
14
|
+
delete process.env.CLAUDE_CODE_ENTRYPOINT;
|
|
15
|
+
delete process.env.CURSOR;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
// Restore original env
|
|
20
|
+
for (const key of [
|
|
21
|
+
"MAINA_API_KEY",
|
|
22
|
+
"OPENROUTER_API_KEY",
|
|
23
|
+
"ANTHROPIC_API_KEY",
|
|
24
|
+
"MAINA_HOST_MODE",
|
|
25
|
+
"CLAUDECODE",
|
|
26
|
+
"CLAUDE_CODE_ENTRYPOINT",
|
|
27
|
+
"CURSOR",
|
|
28
|
+
]) {
|
|
29
|
+
if (originalEnv[key] !== undefined) {
|
|
30
|
+
process.env[key] = originalEnv[key];
|
|
31
|
+
} else {
|
|
32
|
+
delete process.env[key];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("returns api-key method when MAINA_API_KEY is set", () => {
|
|
38
|
+
process.env.MAINA_API_KEY = "test-key-123";
|
|
39
|
+
|
|
40
|
+
const result = checkAIAvailability();
|
|
41
|
+
|
|
42
|
+
expect(result.available).toBe(true);
|
|
43
|
+
expect(result.method).toBe("api-key");
|
|
44
|
+
expect(result.reason).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns api-key method when OPENROUTER_API_KEY is set", () => {
|
|
48
|
+
process.env.OPENROUTER_API_KEY = "or-key-456";
|
|
49
|
+
|
|
50
|
+
const result = checkAIAvailability();
|
|
51
|
+
|
|
52
|
+
expect(result.available).toBe(true);
|
|
53
|
+
expect(result.method).toBe("api-key");
|
|
54
|
+
expect(result.reason).toBeUndefined();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("returns api-key method when ANTHROPIC_API_KEY is set", () => {
|
|
58
|
+
process.env.ANTHROPIC_API_KEY = "sk-ant-test";
|
|
59
|
+
|
|
60
|
+
const result = checkAIAvailability();
|
|
61
|
+
|
|
62
|
+
expect(result.available).toBe(true);
|
|
63
|
+
expect(result.method).toBe("api-key");
|
|
64
|
+
expect(result.reason).toBeUndefined();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("returns host-delegation when CLAUDECODE env is set", () => {
|
|
68
|
+
process.env.CLAUDECODE = "1";
|
|
69
|
+
|
|
70
|
+
const result = checkAIAvailability();
|
|
71
|
+
|
|
72
|
+
expect(result.available).toBe(true);
|
|
73
|
+
expect(result.method).toBe("host-delegation");
|
|
74
|
+
expect(result.reason).toBeUndefined();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("returns host-delegation when CLAUDE_CODE_ENTRYPOINT is set", () => {
|
|
78
|
+
process.env.CLAUDE_CODE_ENTRYPOINT = "cli";
|
|
79
|
+
|
|
80
|
+
const result = checkAIAvailability();
|
|
81
|
+
|
|
82
|
+
expect(result.available).toBe(true);
|
|
83
|
+
expect(result.method).toBe("host-delegation");
|
|
84
|
+
expect(result.reason).toBeUndefined();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("returns host-delegation when CURSOR is set", () => {
|
|
88
|
+
process.env.CURSOR = "1";
|
|
89
|
+
|
|
90
|
+
const result = checkAIAvailability();
|
|
91
|
+
|
|
92
|
+
expect(result.available).toBe(true);
|
|
93
|
+
expect(result.method).toBe("host-delegation");
|
|
94
|
+
expect(result.reason).toBeUndefined();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("returns host-delegation when MAINA_HOST_MODE is true", () => {
|
|
98
|
+
process.env.MAINA_HOST_MODE = "true";
|
|
99
|
+
|
|
100
|
+
const result = checkAIAvailability();
|
|
101
|
+
|
|
102
|
+
expect(result.available).toBe(true);
|
|
103
|
+
expect(result.method).toBe("host-delegation");
|
|
104
|
+
expect(result.reason).toBeUndefined();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("returns none when no key and no host environment", () => {
|
|
108
|
+
const result = checkAIAvailability();
|
|
109
|
+
|
|
110
|
+
expect(result.available).toBe(false);
|
|
111
|
+
expect(result.method).toBe("none");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("includes a reason message when method is none", () => {
|
|
115
|
+
const result = checkAIAvailability();
|
|
116
|
+
|
|
117
|
+
expect(result.reason).toBeDefined();
|
|
118
|
+
expect(result.reason).toContain("No API key found");
|
|
119
|
+
expect(result.reason).toContain("maina init");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("prefers api-key over host-delegation when both available", () => {
|
|
123
|
+
process.env.MAINA_API_KEY = "test-key";
|
|
124
|
+
process.env.CLAUDECODE = "1";
|
|
125
|
+
|
|
126
|
+
const result = checkAIAvailability();
|
|
127
|
+
|
|
128
|
+
expect(result.available).toBe(true);
|
|
129
|
+
expect(result.method).toBe("api-key");
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
type DelegationRequest,
|
|
4
4
|
formatDelegationRequest,
|
|
5
|
+
outputDelegationRequest,
|
|
5
6
|
parseDelegationRequest,
|
|
6
7
|
} from "../delegation";
|
|
7
8
|
|
|
@@ -103,3 +104,56 @@ Some output after`;
|
|
|
103
104
|
expect(parsed?.prompt).toBe("hello");
|
|
104
105
|
});
|
|
105
106
|
});
|
|
107
|
+
|
|
108
|
+
describe("outputDelegationRequest", () => {
|
|
109
|
+
let stderrChunks: string[];
|
|
110
|
+
let stdoutChunks: string[];
|
|
111
|
+
let originalStderrWrite: typeof process.stderr.write;
|
|
112
|
+
let originalStdoutWrite: typeof process.stdout.write;
|
|
113
|
+
|
|
114
|
+
beforeEach(() => {
|
|
115
|
+
stderrChunks = [];
|
|
116
|
+
stdoutChunks = [];
|
|
117
|
+
originalStderrWrite = process.stderr.write;
|
|
118
|
+
originalStdoutWrite = process.stdout.write;
|
|
119
|
+
|
|
120
|
+
process.stderr.write = ((chunk: string | Uint8Array) => {
|
|
121
|
+
stderrChunks.push(
|
|
122
|
+
typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk),
|
|
123
|
+
);
|
|
124
|
+
return true;
|
|
125
|
+
}) as typeof process.stderr.write;
|
|
126
|
+
|
|
127
|
+
process.stdout.write = ((chunk: string | Uint8Array) => {
|
|
128
|
+
stdoutChunks.push(
|
|
129
|
+
typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk),
|
|
130
|
+
);
|
|
131
|
+
return true;
|
|
132
|
+
}) as typeof process.stdout.write;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
afterEach(() => {
|
|
136
|
+
process.stderr.write = originalStderrWrite;
|
|
137
|
+
process.stdout.write = originalStdoutWrite;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("writes to stderr, not stdout (prevents MCP protocol corruption)", () => {
|
|
141
|
+
const req: DelegationRequest = {
|
|
142
|
+
task: "review",
|
|
143
|
+
context: "test context",
|
|
144
|
+
prompt: "test prompt",
|
|
145
|
+
expectedFormat: "text",
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
outputDelegationRequest(req);
|
|
149
|
+
|
|
150
|
+
// Must write to stderr
|
|
151
|
+
expect(stderrChunks.length).toBeGreaterThan(0);
|
|
152
|
+
const stderrOutput = stderrChunks.join("");
|
|
153
|
+
expect(stderrOutput).toContain("---MAINA_AI_REQUEST---");
|
|
154
|
+
expect(stderrOutput).toContain("task: review");
|
|
155
|
+
|
|
156
|
+
// Must NOT write to stdout (stdout is reserved for JSON-RPC in MCP)
|
|
157
|
+
expect(stdoutChunks.length).toBe(0);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getApiKey, isHostMode } from "../config/index";
|
|
2
|
+
|
|
3
|
+
export interface AIAvailability {
|
|
4
|
+
available: boolean;
|
|
5
|
+
method: "api-key" | "host-delegation" | "none";
|
|
6
|
+
reason?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function checkAIAvailability(): AIAvailability {
|
|
10
|
+
const apiKey = getApiKey();
|
|
11
|
+
if (apiKey !== null) {
|
|
12
|
+
return { available: true, method: "api-key" };
|
|
13
|
+
}
|
|
14
|
+
if (isHostMode()) {
|
|
15
|
+
return { available: true, method: "host-delegation" };
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
available: false,
|
|
19
|
+
method: "none",
|
|
20
|
+
reason:
|
|
21
|
+
"No API key found and not running inside an AI agent. Run `maina init` to set up or run inside Claude Code/Cursor.",
|
|
22
|
+
};
|
|
23
|
+
}
|
package/src/ai/delegation.ts
CHANGED
|
@@ -102,10 +102,12 @@ export function parseDelegationRequest(text: string): DelegationRequest | null {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
|
-
* Output a delegation request to
|
|
106
|
-
*
|
|
105
|
+
* Output a delegation request to stderr.
|
|
106
|
+
* Uses stderr so that MCP stdio transport (which uses stdout for JSON-RPC)
|
|
107
|
+
* is never corrupted by delegation text.
|
|
108
|
+
* In CLI mode, stderr is still visible in the terminal.
|
|
107
109
|
*/
|
|
108
110
|
export function outputDelegationRequest(req: DelegationRequest): void {
|
|
109
111
|
const formatted = formatDelegationRequest(req);
|
|
110
|
-
process.
|
|
112
|
+
process.stderr.write(`\n${formatted}\n`);
|
|
111
113
|
}
|
|
@@ -42,12 +42,13 @@ describe("getBudgetRatio", () => {
|
|
|
42
42
|
describe("assembleBudget", () => {
|
|
43
43
|
test("allocations sum correctly for default mode with default context window", () => {
|
|
44
44
|
const allocation = assembleBudget("default");
|
|
45
|
-
// working + episodic + semantic + retrieval + headroom === total
|
|
45
|
+
// working + episodic + semantic + retrieval + wiki + headroom === total
|
|
46
46
|
const layerSum =
|
|
47
47
|
allocation.working +
|
|
48
48
|
allocation.episodic +
|
|
49
49
|
allocation.semantic +
|
|
50
50
|
allocation.retrieval +
|
|
51
|
+
allocation.wiki +
|
|
51
52
|
allocation.headroom;
|
|
52
53
|
expect(layerSum).toBe(allocation.total);
|
|
53
54
|
});
|
|
@@ -75,7 +76,8 @@ describe("assembleBudget", () => {
|
|
|
75
76
|
allocation.working +
|
|
76
77
|
allocation.episodic +
|
|
77
78
|
allocation.semantic +
|
|
78
|
-
allocation.retrieval
|
|
79
|
+
allocation.retrieval +
|
|
80
|
+
allocation.wiki;
|
|
79
81
|
expect(layerSum).toBe(budget);
|
|
80
82
|
});
|
|
81
83
|
|
|
@@ -83,15 +85,35 @@ describe("assembleBudget", () => {
|
|
|
83
85
|
const modelContext = 200_000;
|
|
84
86
|
const allocationDefault = assembleBudget("default", modelContext);
|
|
85
87
|
const allocationFocused = assembleBudget("focused", modelContext);
|
|
86
|
-
// working = ~
|
|
87
|
-
// working = floor(80_000 * 0.
|
|
88
|
+
// working = ~12% of budget; for focused: budget = 0.4 * 200_000 = 80_000
|
|
89
|
+
// working = floor(80_000 * 0.12) = 9_600
|
|
88
90
|
expect(allocationFocused.working).toBe(
|
|
89
|
-
Math.floor(Math.floor(modelContext * 0.4) * 0.
|
|
91
|
+
Math.floor(Math.floor(modelContext * 0.4) * 0.12),
|
|
90
92
|
);
|
|
91
93
|
expect(allocationDefault.working).toBe(
|
|
92
|
-
Math.floor(Math.floor(modelContext * 0.6) * 0.
|
|
94
|
+
Math.floor(Math.floor(modelContext * 0.6) * 0.12),
|
|
93
95
|
);
|
|
94
96
|
});
|
|
97
|
+
|
|
98
|
+
test("wiki allocation is ~12% of usable budget", () => {
|
|
99
|
+
const modelContext = 200_000;
|
|
100
|
+
const allocation = assembleBudget("default", modelContext);
|
|
101
|
+
const budget = Math.floor(modelContext * 0.6);
|
|
102
|
+
expect(allocation.wiki).toBe(Math.floor(budget * 0.12));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("all 5 layers sum to budget for default mode", () => {
|
|
106
|
+
const modelContext = 200_000;
|
|
107
|
+
const allocation = assembleBudget("default", modelContext);
|
|
108
|
+
const budget = Math.floor(modelContext * 0.6);
|
|
109
|
+
const layerSum =
|
|
110
|
+
allocation.working +
|
|
111
|
+
allocation.episodic +
|
|
112
|
+
allocation.semantic +
|
|
113
|
+
allocation.retrieval +
|
|
114
|
+
allocation.wiki;
|
|
115
|
+
expect(layerSum).toBe(budget);
|
|
116
|
+
});
|
|
95
117
|
});
|
|
96
118
|
|
|
97
119
|
describe("truncateToFit", () => {
|
|
@@ -109,6 +131,7 @@ describe("truncateToFit", () => {
|
|
|
109
131
|
semantic: 200,
|
|
110
132
|
episodic: 300,
|
|
111
133
|
retrieval: 400,
|
|
134
|
+
wiki: 0,
|
|
112
135
|
headroom: 0,
|
|
113
136
|
total: 1000,
|
|
114
137
|
...overrides,
|
|
@@ -156,6 +156,7 @@ describe("assembleContext", () => {
|
|
|
156
156
|
expect(typeof result.budget.episodic).toBe("number");
|
|
157
157
|
expect(typeof result.budget.semantic).toBe("number");
|
|
158
158
|
expect(typeof result.budget.retrieval).toBe("number");
|
|
159
|
+
expect(typeof result.budget.wiki).toBe("number");
|
|
159
160
|
expect(typeof result.budget.total).toBe("number");
|
|
160
161
|
expect(typeof result.budget.headroom).toBe("number");
|
|
161
162
|
expect(result.budget.total).toBeGreaterThan(0);
|
|
@@ -3,28 +3,31 @@ import type { BudgetMode } from "../budget.ts";
|
|
|
3
3
|
import { getBudgetMode, getContextNeeds, needsLayer } from "../selector.ts";
|
|
4
4
|
|
|
5
5
|
describe("getContextNeeds", () => {
|
|
6
|
-
it("commit needs only working + conventions", () => {
|
|
6
|
+
it("commit needs only working + conventions + wiki", () => {
|
|
7
7
|
const needs = getContextNeeds("commit");
|
|
8
8
|
expect(needs.working).toBe(true);
|
|
9
9
|
expect(needs.episodic).toBe(false);
|
|
10
10
|
expect(needs.semantic).toEqual(["conventions"]);
|
|
11
11
|
expect(needs.retrieval).toBe(false);
|
|
12
|
+
expect(needs.wiki).toBe(true);
|
|
12
13
|
});
|
|
13
14
|
|
|
14
|
-
it("context command needs all
|
|
15
|
+
it("context command needs all 5 layers", () => {
|
|
15
16
|
const needs = getContextNeeds("context");
|
|
16
17
|
expect(needs.working).toBe(true);
|
|
17
18
|
expect(needs.episodic).toBe(true);
|
|
18
19
|
expect(needs.semantic).toBe(true);
|
|
19
20
|
expect(needs.retrieval).toBe(true);
|
|
21
|
+
expect(needs.wiki).toBe(true);
|
|
20
22
|
});
|
|
21
23
|
|
|
22
|
-
it("verify needs working + recent-reviews + adrs + conventions", () => {
|
|
24
|
+
it("verify needs working + recent-reviews + adrs + conventions + wiki", () => {
|
|
23
25
|
const needs = getContextNeeds("verify");
|
|
24
26
|
expect(needs.working).toBe(true);
|
|
25
27
|
expect(needs.episodic).toEqual(["recent-reviews"]);
|
|
26
28
|
expect(needs.semantic).toEqual(["adrs", "conventions"]);
|
|
27
29
|
expect(needs.retrieval).toBe(false);
|
|
30
|
+
expect(needs.wiki).toBe(true);
|
|
28
31
|
});
|
|
29
32
|
|
|
30
33
|
it("review returns correct context needs", () => {
|
|
@@ -33,6 +36,7 @@ describe("getContextNeeds", () => {
|
|
|
33
36
|
expect(needs.episodic).toEqual(["past-reviews"]);
|
|
34
37
|
expect(needs.semantic).toEqual(["adrs"]);
|
|
35
38
|
expect(needs.retrieval).toBe(false);
|
|
39
|
+
expect(needs.wiki).toBe(true);
|
|
36
40
|
});
|
|
37
41
|
|
|
38
42
|
it("plan returns correct context needs", () => {
|
|
@@ -41,6 +45,7 @@ describe("getContextNeeds", () => {
|
|
|
41
45
|
expect(needs.semantic).toEqual(["adrs", "conventions"]);
|
|
42
46
|
expect(needs.episodic).toBe(false);
|
|
43
47
|
expect(needs.retrieval).toBe(false);
|
|
48
|
+
expect(needs.wiki).toBe(true);
|
|
44
49
|
});
|
|
45
50
|
|
|
46
51
|
it("explain returns correct context needs", () => {
|
|
@@ -49,6 +54,7 @@ describe("getContextNeeds", () => {
|
|
|
49
54
|
expect(needs.episodic).toBe(false);
|
|
50
55
|
expect(needs.semantic).toBe(true);
|
|
51
56
|
expect(needs.retrieval).toBe(true);
|
|
57
|
+
expect(needs.wiki).toBe(true);
|
|
52
58
|
});
|
|
53
59
|
|
|
54
60
|
it("design returns correct context needs", () => {
|
|
@@ -57,6 +63,7 @@ describe("getContextNeeds", () => {
|
|
|
57
63
|
expect(needs.episodic).toBe(false);
|
|
58
64
|
expect(needs.semantic).toEqual(["adrs"]);
|
|
59
65
|
expect(needs.retrieval).toBe(false);
|
|
66
|
+
expect(needs.wiki).toBe(true);
|
|
60
67
|
});
|
|
61
68
|
|
|
62
69
|
it("ticket returns correct context needs", () => {
|
|
@@ -65,6 +72,7 @@ describe("getContextNeeds", () => {
|
|
|
65
72
|
expect(needs.episodic).toBe(false);
|
|
66
73
|
expect(needs.semantic).toEqual(["modules"]);
|
|
67
74
|
expect(needs.retrieval).toBe(false);
|
|
75
|
+
expect(needs.wiki).toBe(false);
|
|
68
76
|
});
|
|
69
77
|
|
|
70
78
|
it("analyze returns correct context needs", () => {
|
|
@@ -73,6 +81,7 @@ describe("getContextNeeds", () => {
|
|
|
73
81
|
expect(needs.episodic).toBe(true);
|
|
74
82
|
expect(needs.semantic).toBe(true);
|
|
75
83
|
expect(needs.retrieval).toBe(false);
|
|
84
|
+
expect(needs.wiki).toBe(true);
|
|
76
85
|
});
|
|
77
86
|
|
|
78
87
|
it("pr returns correct context needs", () => {
|
|
@@ -81,6 +90,7 @@ describe("getContextNeeds", () => {
|
|
|
81
90
|
expect(needs.episodic).toEqual(["past-reviews"]);
|
|
82
91
|
expect(needs.semantic).toBe(true);
|
|
83
92
|
expect(needs.retrieval).toBe(true);
|
|
93
|
+
expect(needs.wiki).toBe(true);
|
|
84
94
|
});
|
|
85
95
|
});
|
|
86
96
|
|
|
@@ -129,6 +139,16 @@ describe("needsLayer", () => {
|
|
|
129
139
|
const needs = getContextNeeds("commit");
|
|
130
140
|
expect(needsLayer(needs, "retrieval")).toBe(false);
|
|
131
141
|
});
|
|
142
|
+
|
|
143
|
+
it("correctly identifies when wiki layer is needed", () => {
|
|
144
|
+
const needs = getContextNeeds("commit");
|
|
145
|
+
expect(needsLayer(needs, "wiki")).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("correctly identifies when wiki layer is not needed", () => {
|
|
149
|
+
const needs = getContextNeeds("ticket");
|
|
150
|
+
expect(needsLayer(needs, "wiki")).toBe(false);
|
|
151
|
+
});
|
|
132
152
|
});
|
|
133
153
|
|
|
134
154
|
describe("getBudgetMode", () => {
|