ralph-lisa-loop 3.0.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.
@@ -0,0 +1,5 @@
1
+ /**
2
+ * ralph-lisa-loop - public API
3
+ */
4
+ export * from "./state.js";
5
+ export * from "./policy.js";
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /**
3
+ * ralph-lisa-loop - public API
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ __exportStar(require("./state.js"), exports);
21
+ __exportStar(require("./policy.js"), exports);
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Policy layer for Ralph-Lisa Loop.
3
+ * Checks submissions for required content (warn or block mode).
4
+ *
5
+ * Modes (RL_POLICY_MODE env):
6
+ * off - no checks (default)
7
+ * warn - print warnings, don't block
8
+ * block - print warnings AND exit(1) if violations found
9
+ */
10
+ export type PolicyMode = "off" | "warn" | "block";
11
+ export interface PolicyViolation {
12
+ rule: string;
13
+ message: string;
14
+ }
15
+ export declare function getPolicyMode(): PolicyMode;
16
+ /**
17
+ * Check Ralph's submission for policy violations.
18
+ */
19
+ export declare function checkRalph(tag: string, content: string): PolicyViolation[];
20
+ /**
21
+ * Check Lisa's submission for policy violations.
22
+ */
23
+ export declare function checkLisa(tag: string, content: string): PolicyViolation[];
24
+ /**
25
+ * Run policy checks and handle output/exit based on mode.
26
+ * Returns true if submission should proceed, false if blocked.
27
+ */
28
+ export declare function runPolicyCheck(role: "ralph" | "lisa", tag: string, content: string): boolean;
package/dist/policy.js ADDED
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ /**
3
+ * Policy layer for Ralph-Lisa Loop.
4
+ * Checks submissions for required content (warn or block mode).
5
+ *
6
+ * Modes (RL_POLICY_MODE env):
7
+ * off - no checks (default)
8
+ * warn - print warnings, don't block
9
+ * block - print warnings AND exit(1) if violations found
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.getPolicyMode = getPolicyMode;
13
+ exports.checkRalph = checkRalph;
14
+ exports.checkLisa = checkLisa;
15
+ exports.runPolicyCheck = runPolicyCheck;
16
+ function getPolicyMode() {
17
+ const mode = process.env.RL_POLICY_MODE || "off";
18
+ if (mode === "warn" || mode === "block" || mode === "off")
19
+ return mode;
20
+ return "off";
21
+ }
22
+ /**
23
+ * Check Ralph's submission for policy violations.
24
+ */
25
+ function checkRalph(tag, content) {
26
+ const violations = [];
27
+ // [CODE] or [FIX] must include Test Results
28
+ if (tag === "CODE" || tag === "FIX") {
29
+ if (!content.includes("Test Results") &&
30
+ !content.includes("test results") &&
31
+ !content.includes("Test results")) {
32
+ violations.push({
33
+ rule: "test-results",
34
+ message: `[${tag}] submission missing "Test Results" section.`,
35
+ });
36
+ }
37
+ }
38
+ // [RESEARCH] must have substance
39
+ if (tag === "RESEARCH") {
40
+ // 4 distinct field groups, each with Chinese + English variants
41
+ const fieldGroups = [
42
+ ["参考实现", "reference"],
43
+ ["关键类型", "key type"],
44
+ ["数据格式", "data format", "数据结构", "data structure"],
45
+ ["验证方式", "verification"],
46
+ ];
47
+ const lc = content.toLowerCase();
48
+ const matchedFields = fieldGroups.filter((variants) => variants.some((v) => lc.includes(v.toLowerCase()))).length;
49
+ const hasSubstantialContent = content.split("\n").length > 3;
50
+ if (matchedFields < 2 && !hasSubstantialContent) {
51
+ violations.push({
52
+ rule: "research-content",
53
+ message: "[RESEARCH] submission needs at least 2 fields (参考实现/关键类型/数据结构/验证方式) or equivalent summary with evidence.",
54
+ });
55
+ }
56
+ }
57
+ return violations;
58
+ }
59
+ /**
60
+ * Check Lisa's submission for policy violations.
61
+ */
62
+ function checkLisa(tag, content) {
63
+ const violations = [];
64
+ // [PASS] or [NEEDS_WORK] must include at least 1 reason
65
+ if (tag === "PASS" || tag === "NEEDS_WORK") {
66
+ // Content after the first line (tag+summary) should have substance
67
+ const lines = content.split("\n");
68
+ const bodyLines = lines.slice(1).filter((l) => l.trim().length > 0);
69
+ if (bodyLines.length === 0) {
70
+ violations.push({
71
+ rule: "reason-required",
72
+ message: `[${tag}] submission must include at least 1 reason.`,
73
+ });
74
+ }
75
+ }
76
+ return violations;
77
+ }
78
+ /**
79
+ * Run policy checks and handle output/exit based on mode.
80
+ * Returns true if submission should proceed, false if blocked.
81
+ */
82
+ function runPolicyCheck(role, tag, content) {
83
+ const mode = getPolicyMode();
84
+ if (mode === "off")
85
+ return true;
86
+ const violations = role === "ralph" ? checkRalph(tag, content) : checkLisa(tag, content);
87
+ if (violations.length === 0)
88
+ return true;
89
+ console.error("");
90
+ console.error("⚠️ Policy warnings:");
91
+ for (const v of violations) {
92
+ console.error(` - ${v.message}`);
93
+ }
94
+ console.error("");
95
+ if (mode === "block") {
96
+ console.error("Policy mode is 'block'. Submission rejected.");
97
+ return false;
98
+ }
99
+ // warn mode: print but continue
100
+ return true;
101
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * State management for Ralph-Lisa Loop.
3
+ * Manages .dual-agent/ directory and all state files.
4
+ */
5
+ export declare const STATE_DIR = ".dual-agent";
6
+ export declare const ARCHIVE_DIR = ".dual-agent-archive";
7
+ export declare const VALID_TAGS = "PLAN|RESEARCH|CODE|FIX|PASS|NEEDS_WORK|CHALLENGE|DISCUSS|QUESTION|CONSENSUS";
8
+ export declare function stateDir(projectDir?: string): string;
9
+ export declare function checkSession(projectDir?: string): void;
10
+ export declare function readFile(filePath: string): string;
11
+ export declare function writeFile(filePath: string, content: string): void;
12
+ export declare function appendFile(filePath: string, content: string): void;
13
+ export declare function getTurn(projectDir?: string): string;
14
+ export declare function setTurn(turn: string, projectDir?: string): void;
15
+ export declare function getRound(projectDir?: string): string;
16
+ export declare function setRound(round: number, projectDir?: string): void;
17
+ export declare function getStep(projectDir?: string): string;
18
+ export declare function setStep(step: string, projectDir?: string): void;
19
+ export declare function extractTag(content: string): string;
20
+ export declare function extractSummary(content: string): string;
21
+ export declare function timestamp(): string;
22
+ export declare function timeShort(): string;
23
+ export declare function appendHistory(role: string, content: string, projectDir?: string): void;
24
+ export declare function updateLastAction(role: string, content: string, projectDir?: string): void;
package/dist/state.js ADDED
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ /**
3
+ * State management for Ralph-Lisa Loop.
4
+ * Manages .dual-agent/ directory and all state files.
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.VALID_TAGS = exports.ARCHIVE_DIR = exports.STATE_DIR = void 0;
41
+ exports.stateDir = stateDir;
42
+ exports.checkSession = checkSession;
43
+ exports.readFile = readFile;
44
+ exports.writeFile = writeFile;
45
+ exports.appendFile = appendFile;
46
+ exports.getTurn = getTurn;
47
+ exports.setTurn = setTurn;
48
+ exports.getRound = getRound;
49
+ exports.setRound = setRound;
50
+ exports.getStep = getStep;
51
+ exports.setStep = setStep;
52
+ exports.extractTag = extractTag;
53
+ exports.extractSummary = extractSummary;
54
+ exports.timestamp = timestamp;
55
+ exports.timeShort = timeShort;
56
+ exports.appendHistory = appendHistory;
57
+ exports.updateLastAction = updateLastAction;
58
+ const fs = __importStar(require("node:fs"));
59
+ const path = __importStar(require("node:path"));
60
+ exports.STATE_DIR = ".dual-agent";
61
+ exports.ARCHIVE_DIR = ".dual-agent-archive";
62
+ exports.VALID_TAGS = "PLAN|RESEARCH|CODE|FIX|PASS|NEEDS_WORK|CHALLENGE|DISCUSS|QUESTION|CONSENSUS";
63
+ const TAG_RE = new RegExp(`^\\[(${exports.VALID_TAGS})\\]`);
64
+ function stateDir(projectDir = process.cwd()) {
65
+ return path.join(projectDir, exports.STATE_DIR);
66
+ }
67
+ function checkSession(projectDir = process.cwd()) {
68
+ const dir = stateDir(projectDir);
69
+ if (!fs.existsSync(dir)) {
70
+ console.error('Error: Session not initialized. Run: ralph-lisa init "task description"');
71
+ process.exit(1);
72
+ }
73
+ }
74
+ function readFile(filePath) {
75
+ try {
76
+ return fs.readFileSync(filePath, "utf-8").trim();
77
+ }
78
+ catch {
79
+ return "";
80
+ }
81
+ }
82
+ function writeFile(filePath, content) {
83
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
84
+ fs.writeFileSync(filePath, content, "utf-8");
85
+ }
86
+ function appendFile(filePath, content) {
87
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
88
+ fs.appendFileSync(filePath, content, "utf-8");
89
+ }
90
+ function getTurn(projectDir = process.cwd()) {
91
+ return readFile(path.join(stateDir(projectDir), "turn.txt")) || "ralph";
92
+ }
93
+ function setTurn(turn, projectDir = process.cwd()) {
94
+ writeFile(path.join(stateDir(projectDir), "turn.txt"), turn);
95
+ }
96
+ function getRound(projectDir = process.cwd()) {
97
+ return readFile(path.join(stateDir(projectDir), "round.txt")) || "?";
98
+ }
99
+ function setRound(round, projectDir = process.cwd()) {
100
+ writeFile(path.join(stateDir(projectDir), "round.txt"), String(round));
101
+ }
102
+ function getStep(projectDir = process.cwd()) {
103
+ return readFile(path.join(stateDir(projectDir), "step.txt")) || "?";
104
+ }
105
+ function setStep(step, projectDir = process.cwd()) {
106
+ writeFile(path.join(stateDir(projectDir), "step.txt"), step);
107
+ }
108
+ function extractTag(content) {
109
+ const firstLine = content.split("\n")[0] || "";
110
+ const match = firstLine.match(TAG_RE);
111
+ return match ? match[1] : "";
112
+ }
113
+ function extractSummary(content) {
114
+ const firstLine = content.split("\n")[0] || "";
115
+ return firstLine.replace(TAG_RE, "").trim();
116
+ }
117
+ function timestamp() {
118
+ const d = new Date();
119
+ const pad = (n) => String(n).padStart(2, "0");
120
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
121
+ }
122
+ function timeShort() {
123
+ const d = new Date();
124
+ const pad = (n) => String(n).padStart(2, "0");
125
+ return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
126
+ }
127
+ function appendHistory(role, content, projectDir = process.cwd()) {
128
+ const tag = extractTag(content);
129
+ const summary = extractSummary(content);
130
+ const round = getRound(projectDir);
131
+ const step = getStep(projectDir);
132
+ const ts = timestamp();
133
+ const entry = `
134
+ ---
135
+
136
+ ## [${role}] [${tag}] Round ${round} | Step: ${step}
137
+ **Time**: ${ts}
138
+ **Summary**: ${summary}
139
+
140
+ ${content}
141
+
142
+ `;
143
+ appendFile(path.join(stateDir(projectDir), "history.md"), entry);
144
+ }
145
+ function updateLastAction(role, content, projectDir = process.cwd()) {
146
+ const tag = extractTag(content);
147
+ const summary = extractSummary(content);
148
+ const ts = timeShort();
149
+ writeFile(path.join(stateDir(projectDir), "last_action.txt"), `[${tag}] ${summary} (by ${role}, ${ts})`);
150
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const node_test_1 = require("node:test");
37
+ const assert = __importStar(require("node:assert"));
38
+ const fs = __importStar(require("node:fs"));
39
+ const path = __importStar(require("node:path"));
40
+ const node_child_process_1 = require("node:child_process");
41
+ const CLI = path.join(__dirname, "..", "cli.js");
42
+ const TMP = path.join(__dirname, "..", "..", ".test-tmp");
43
+ function run(...args) {
44
+ try {
45
+ const stdout = (0, node_child_process_1.execFileSync)(process.execPath, [CLI, ...args], {
46
+ cwd: TMP,
47
+ encoding: "utf-8",
48
+ env: { ...process.env, RL_POLICY_MODE: "off" },
49
+ });
50
+ return { stdout, exitCode: 0 };
51
+ }
52
+ catch (e) {
53
+ return { stdout: (e.stdout || "") + (e.stderr || ""), exitCode: e.status };
54
+ }
55
+ }
56
+ (0, node_test_1.describe)("CLI: policy check-consensus", () => {
57
+ (0, node_test_1.beforeEach)(() => {
58
+ fs.rmSync(TMP, { recursive: true, force: true });
59
+ fs.mkdirSync(TMP, { recursive: true });
60
+ run("init", "--minimal");
61
+ });
62
+ (0, node_test_1.afterEach)(() => {
63
+ fs.rmSync(TMP, { recursive: true, force: true });
64
+ });
65
+ (0, node_test_1.it)("passes when both agents submit CONSENSUS", () => {
66
+ run("submit-ralph", "[CONSENSUS] Agreed on plan");
67
+ run("submit-lisa", "[CONSENSUS] Agreed\n\n- All good");
68
+ const r = run("policy", "check-consensus");
69
+ assert.strictEqual(r.exitCode, 0);
70
+ assert.ok(r.stdout.includes("Consensus reached"));
71
+ });
72
+ (0, node_test_1.it)("fails when neither agent has CONSENSUS", () => {
73
+ run("submit-ralph", "[CODE] Done\n\nTest Results\n- pass");
74
+ run("submit-lisa", "[PASS] OK\n\n- Clean code");
75
+ const r = run("policy", "check-consensus");
76
+ assert.strictEqual(r.exitCode, 1);
77
+ assert.ok(r.stdout.includes("NOT reached"));
78
+ });
79
+ (0, node_test_1.it)("fails when only Ralph has CONSENSUS", () => {
80
+ run("submit-ralph", "[CONSENSUS] I agree");
81
+ run("submit-lisa", "[PASS] OK\n\n- Looks good");
82
+ const r = run("policy", "check-consensus");
83
+ assert.strictEqual(r.exitCode, 1);
84
+ assert.ok(r.stdout.includes("Lisa"));
85
+ });
86
+ });
87
+ (0, node_test_1.describe)("CLI: policy check-next-step", () => {
88
+ (0, node_test_1.beforeEach)(() => {
89
+ fs.rmSync(TMP, { recursive: true, force: true });
90
+ fs.mkdirSync(TMP, { recursive: true });
91
+ run("init", "--minimal");
92
+ });
93
+ (0, node_test_1.afterEach)(() => {
94
+ fs.rmSync(TMP, { recursive: true, force: true });
95
+ });
96
+ (0, node_test_1.it)("passes when both CONSENSUS and policy OK", () => {
97
+ run("submit-ralph", "[CONSENSUS] Agreed on approach");
98
+ run("submit-lisa", "[CONSENSUS] Confirmed\n\n- Sound plan");
99
+ const r = run("policy", "check-next-step");
100
+ assert.strictEqual(r.exitCode, 0);
101
+ assert.ok(r.stdout.includes("Ready to proceed"));
102
+ });
103
+ (0, node_test_1.it)("fails with comprehensive issues", () => {
104
+ run("submit-ralph", "[CODE] Done");
105
+ run("submit-lisa", "[PASS] OK");
106
+ const r = run("policy", "check-next-step");
107
+ assert.strictEqual(r.exitCode, 1);
108
+ // Should report: no consensus + missing test results + missing reason
109
+ assert.ok(r.stdout.includes("not [CONSENSUS]"));
110
+ });
111
+ });
112
+ (0, node_test_1.describe)("CLI: init --minimal", () => {
113
+ (0, node_test_1.beforeEach)(() => {
114
+ fs.rmSync(TMP, { recursive: true, force: true });
115
+ fs.mkdirSync(TMP, { recursive: true });
116
+ });
117
+ (0, node_test_1.afterEach)(() => {
118
+ fs.rmSync(TMP, { recursive: true, force: true });
119
+ });
120
+ (0, node_test_1.it)("creates only .dual-agent/, no project files", () => {
121
+ run("init", "--minimal");
122
+ assert.ok(fs.existsSync(path.join(TMP, ".dual-agent", "turn.txt")));
123
+ assert.ok(!fs.existsSync(path.join(TMP, "CLAUDE.md")));
124
+ assert.ok(!fs.existsSync(path.join(TMP, "CODEX.md")));
125
+ assert.ok(!fs.existsSync(path.join(TMP, ".claude")));
126
+ assert.ok(!fs.existsSync(path.join(TMP, ".codex")));
127
+ });
128
+ (0, node_test_1.it)("allows submit after minimal init", () => {
129
+ run("init", "--minimal");
130
+ const r = run("submit-ralph", "[PLAN] Test plan");
131
+ assert.strictEqual(r.exitCode, 0);
132
+ assert.ok(r.stdout.includes("Submitted"));
133
+ });
134
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const node_test_1 = require("node:test");
37
+ const assert = __importStar(require("node:assert"));
38
+ const policy_js_1 = require("../policy.js");
39
+ (0, node_test_1.describe)("checkRalph", () => {
40
+ (0, node_test_1.it)("warns when CODE missing Test Results", () => {
41
+ const violations = (0, policy_js_1.checkRalph)("CODE", "[CODE] Done\n\nImplemented it.");
42
+ assert.strictEqual(violations.length, 1);
43
+ assert.strictEqual(violations[0].rule, "test-results");
44
+ });
45
+ (0, node_test_1.it)("passes when CODE includes Test Results", () => {
46
+ const violations = (0, policy_js_1.checkRalph)("CODE", "[CODE] Done\n\nTest Results\n- Passed");
47
+ assert.strictEqual(violations.length, 0);
48
+ });
49
+ (0, node_test_1.it)("warns when FIX missing Test Results", () => {
50
+ const violations = (0, policy_js_1.checkRalph)("FIX", "[FIX] Fixed\n\nChanged code.");
51
+ assert.strictEqual(violations.length, 1);
52
+ });
53
+ (0, node_test_1.it)("no warnings for PLAN", () => {
54
+ const violations = (0, policy_js_1.checkRalph)("PLAN", "[PLAN] Plan\n\nDetails");
55
+ assert.strictEqual(violations.length, 0);
56
+ });
57
+ (0, node_test_1.it)("warns when RESEARCH has no substance", () => {
58
+ const violations = (0, policy_js_1.checkRalph)("RESEARCH", "[RESEARCH] Done");
59
+ assert.strictEqual(violations.length, 1);
60
+ assert.strictEqual(violations[0].rule, "research-content");
61
+ });
62
+ (0, node_test_1.it)("passes RESEARCH with 2+ fields (Chinese)", () => {
63
+ const violations = (0, policy_js_1.checkRalph)("RESEARCH", "[RESEARCH] Done\n\n参考实现: file.ts\n关键类型: MyType\n验证方式: tested");
64
+ assert.strictEqual(violations.length, 0);
65
+ });
66
+ (0, node_test_1.it)("passes RESEARCH with exactly 2 fields", () => {
67
+ // Bug fix: previously ceil(2/2)=1 incorrectly warned
68
+ const violations = (0, policy_js_1.checkRalph)("RESEARCH", "[RESEARCH] Done\n\n参考实现: file.ts\n验证方式: curl tested");
69
+ assert.strictEqual(violations.length, 0);
70
+ });
71
+ (0, node_test_1.it)("passes RESEARCH with 2 English fields", () => {
72
+ const violations = (0, policy_js_1.checkRalph)("RESEARCH", "[RESEARCH] Done\n\nReference: file.ts\nVerification: tested");
73
+ assert.strictEqual(violations.length, 0);
74
+ });
75
+ (0, node_test_1.it)("passes RESEARCH with mixed Chinese-English fields", () => {
76
+ const violations = (0, policy_js_1.checkRalph)("RESEARCH", "[RESEARCH] Done\n\n参考实现: file.ts\nVerification: tested");
77
+ assert.strictEqual(violations.length, 0);
78
+ });
79
+ (0, node_test_1.it)("warns RESEARCH with only 1 field", () => {
80
+ const violations = (0, policy_js_1.checkRalph)("RESEARCH", "[RESEARCH] Done\n\n参考实现: file.ts");
81
+ // 1 field + only 2 lines = warns
82
+ assert.strictEqual(violations.length, 1);
83
+ });
84
+ (0, node_test_1.it)("passes RESEARCH with substantial content (>3 lines) even without fields", () => {
85
+ const violations = (0, policy_js_1.checkRalph)("RESEARCH", "[RESEARCH] API analysis\n\nLine 1\nLine 2\nLine 3\nLine 4");
86
+ assert.strictEqual(violations.length, 0);
87
+ });
88
+ });
89
+ (0, node_test_1.describe)("checkLisa", () => {
90
+ (0, node_test_1.it)("warns when PASS has no reason", () => {
91
+ const violations = (0, policy_js_1.checkLisa)("PASS", "[PASS] Looks good");
92
+ assert.strictEqual(violations.length, 1);
93
+ assert.strictEqual(violations[0].rule, "reason-required");
94
+ });
95
+ (0, node_test_1.it)("passes when PASS has reason", () => {
96
+ const violations = (0, policy_js_1.checkLisa)("PASS", "[PASS] Looks good\n\n- Clean code\n- Tests pass");
97
+ assert.strictEqual(violations.length, 0);
98
+ });
99
+ (0, node_test_1.it)("warns when NEEDS_WORK has no reason", () => {
100
+ const violations = (0, policy_js_1.checkLisa)("NEEDS_WORK", "[NEEDS_WORK] Fix it");
101
+ assert.strictEqual(violations.length, 1);
102
+ });
103
+ (0, node_test_1.it)("no warnings for DISCUSS", () => {
104
+ const violations = (0, policy_js_1.checkLisa)("DISCUSS", "[DISCUSS] About this");
105
+ assert.strictEqual(violations.length, 0);
106
+ });
107
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const node_test_1 = require("node:test");
37
+ const assert = __importStar(require("node:assert"));
38
+ const state_js_1 = require("../state.js");
39
+ (0, node_test_1.describe)("extractTag", () => {
40
+ (0, node_test_1.it)("extracts known tags", () => {
41
+ assert.strictEqual((0, state_js_1.extractTag)("[PLAN] My plan"), "PLAN");
42
+ assert.strictEqual((0, state_js_1.extractTag)("[CODE] Implementation done"), "CODE");
43
+ assert.strictEqual((0, state_js_1.extractTag)("[RESEARCH] Results"), "RESEARCH");
44
+ assert.strictEqual((0, state_js_1.extractTag)("[CHALLENGE] I disagree"), "CHALLENGE");
45
+ assert.strictEqual((0, state_js_1.extractTag)("[PASS] Looks good"), "PASS");
46
+ assert.strictEqual((0, state_js_1.extractTag)("[NEEDS_WORK] Fix this"), "NEEDS_WORK");
47
+ assert.strictEqual((0, state_js_1.extractTag)("[DISCUSS] Let's talk"), "DISCUSS");
48
+ assert.strictEqual((0, state_js_1.extractTag)("[QUESTION] What about?"), "QUESTION");
49
+ assert.strictEqual((0, state_js_1.extractTag)("[CONSENSUS] Agreed"), "CONSENSUS");
50
+ assert.strictEqual((0, state_js_1.extractTag)("[FIX] Fixed the bug"), "FIX");
51
+ });
52
+ (0, node_test_1.it)("returns empty for invalid tags", () => {
53
+ assert.strictEqual((0, state_js_1.extractTag)("No tag here"), "");
54
+ assert.strictEqual((0, state_js_1.extractTag)("[INVALID] Nope"), "");
55
+ assert.strictEqual((0, state_js_1.extractTag)(""), "");
56
+ });
57
+ });
58
+ (0, node_test_1.describe)("extractSummary", () => {
59
+ (0, node_test_1.it)("extracts summary after tag", () => {
60
+ assert.strictEqual((0, state_js_1.extractSummary)("[PLAN] My plan"), "My plan");
61
+ assert.strictEqual((0, state_js_1.extractSummary)("[CODE] Implementation done"), "Implementation done");
62
+ });
63
+ (0, node_test_1.it)("returns full line if no tag", () => {
64
+ assert.strictEqual((0, state_js_1.extractSummary)("No tag here"), "No tag here");
65
+ });
66
+ });
67
+ (0, node_test_1.describe)("VALID_TAGS", () => {
68
+ (0, node_test_1.it)("contains all expected tags", () => {
69
+ const tags = state_js_1.VALID_TAGS.split("|");
70
+ assert.ok(tags.includes("PLAN"));
71
+ assert.ok(tags.includes("RESEARCH"));
72
+ assert.ok(tags.includes("CODE"));
73
+ assert.ok(tags.includes("FIX"));
74
+ assert.ok(tags.includes("PASS"));
75
+ assert.ok(tags.includes("NEEDS_WORK"));
76
+ assert.ok(tags.includes("CHALLENGE"));
77
+ assert.ok(tags.includes("DISCUSS"));
78
+ assert.ok(tags.includes("QUESTION"));
79
+ assert.ok(tags.includes("CONSENSUS"));
80
+ assert.strictEqual(tags.length, 10);
81
+ });
82
+ });