aiwcli 0.10.3 → 0.11.1
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/bin/run.js +1 -1
- package/dist/commands/clear.js +28 -131
- package/dist/commands/init/index.js +3 -3
- package/dist/lib/gitignore-manager.d.ts +32 -0
- package/dist/lib/gitignore-manager.js +141 -2
- package/dist/templates/CLAUDE.md +8 -8
- package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
- package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
- package/dist/templates/_shared/.claude/settings.json +7 -7
- package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
- package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
- package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
- package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
- package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
- package/dist/templates/_shared/hooks-ts/session_end.ts +107 -0
- package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
- package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
- package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
- package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
- package/dist/templates/_shared/lib-ts/base/atomic-write.ts +12 -12
- package/dist/templates/_shared/lib-ts/base/constants.ts +22 -15
- package/dist/templates/_shared/lib-ts/base/git-state.ts +1 -1
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +129 -50
- package/dist/templates/_shared/lib-ts/base/inference.ts +28 -21
- package/dist/templates/_shared/lib-ts/base/logger.ts +15 -2
- package/dist/templates/_shared/lib-ts/base/state-io.ts +9 -7
- package/dist/templates/_shared/lib-ts/base/stop-words.ts +131 -131
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +142 -0
- package/dist/templates/_shared/lib-ts/base/utils.ts +69 -69
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +30 -24
- package/dist/templates/_shared/lib-ts/context/context-selector.ts +50 -32
- package/dist/templates/_shared/lib-ts/context/context-store.ts +76 -48
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +43 -23
- package/dist/templates/_shared/lib-ts/context/task-tracker.ts +10 -6
- package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +11 -10
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +158 -0
- package/dist/templates/_shared/lib-ts/templates/formatters.ts +6 -4
- package/dist/templates/_shared/lib-ts/types.ts +68 -55
- package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
- package/dist/templates/_shared/scripts/resume_handoff.ts +345 -0
- package/dist/templates/_shared/scripts/save_handoff.ts +3 -3
- package/dist/templates/_shared/scripts/status_line.ts +687 -0
- package/dist/templates/cc-native/.claude/settings.json +175 -185
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +0 -2
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +1027 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +156 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +792 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +144 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/corroboration.ts +115 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +120 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +168 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +250 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +275 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +107 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +240 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +385 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +14 -1
- package/oclif.manifest.json +1 -1
- package/package.json +2 -2
- package/dist/templates/_shared/hooks/__init__.py +0 -16
- package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/archive_plan.py +0 -177
- package/dist/templates/_shared/hooks/context_monitor.py +0 -270
- package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
- package/dist/templates/_shared/hooks/pre_compact.py +0 -104
- package/dist/templates/_shared/hooks/session_end.py +0 -173
- package/dist/templates/_shared/hooks/session_start.py +0 -206
- package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
- package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
- package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
- package/dist/templates/_shared/lib/__init__.py +0 -1
- package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__init__.py +0 -65
- package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
- package/dist/templates/_shared/lib/base/constants.py +0 -358
- package/dist/templates/_shared/lib/base/hook_utils.py +0 -339
- package/dist/templates/_shared/lib/base/inference.py +0 -307
- package/dist/templates/_shared/lib/base/logger.py +0 -305
- package/dist/templates/_shared/lib/base/stop_words.py +0 -221
- package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
- package/dist/templates/_shared/lib/base/utils.py +0 -263
- package/dist/templates/_shared/lib/context/__init__.py +0 -102
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_extractor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_formatter.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_selector.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/discovery.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_archive.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
- package/dist/templates/_shared/lib/context/context_selector.py +0 -508
- package/dist/templates/_shared/lib/context/context_store.py +0 -653
- package/dist/templates/_shared/lib/context/plan_manager.py +0 -303
- package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
- package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
- package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
- package/dist/templates/_shared/lib/templates/README.md +0 -206
- package/dist/templates/_shared/lib/templates/__init__.py +0 -36
- package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/formatters.py +0 -146
- package/dist/templates/_shared/lib/templates/plan_context.py +0 -73
- package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/save_handoff.py +0 -357
- package/dist/templates/_shared/scripts/status_line.py +0 -716
- package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/MIGRATION.md +0 -86
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -954
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
- package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/constants.py +0 -45
- package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +0 -215
- package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
- package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
- package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1071
- package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +0 -168
- package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Iteration state management for plan review cycles.
|
|
3
|
+
* State files are stored adjacent to plan files (e.g., foo.md → foo.state.json).
|
|
4
|
+
* See cc-native-plan-review-spec.md §4.7
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from "node:fs";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import { atomicWrite } from "../../_shared/lib-ts/base/atomic-write.js";
|
|
10
|
+
import { logInfo, logWarn, logError } from "../../_shared/lib-ts/base/logger.js";
|
|
11
|
+
import { nowIso } from "../../_shared/lib-ts/base/utils.js";
|
|
12
|
+
import { validatePlanPath } from "./constants.js";
|
|
13
|
+
import type { IterationState, IterationEntry } from "./types.js";
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Constants
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
const STATE_SCHEMA_VERSION = "1.0.0";
|
|
20
|
+
|
|
21
|
+
export const DEFAULT_REVIEW_ITERATIONS: Record<string, number> = {
|
|
22
|
+
simple: 1,
|
|
23
|
+
medium: 3,
|
|
24
|
+
high: 5,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// State File Management
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Derive state file path from plan file path with security validation.
|
|
33
|
+
* Example: ~/.claude/plans/foo.md → ~/.claude/plans/foo.state.json
|
|
34
|
+
*
|
|
35
|
+
* @throws Error if planPath is invalid or insecure
|
|
36
|
+
*/
|
|
37
|
+
export function getStateFilePath(planPath: string): string {
|
|
38
|
+
const validated = validatePlanPath(planPath);
|
|
39
|
+
const parsed = path.parse(validated);
|
|
40
|
+
return path.join(parsed.dir, `${parsed.name}.state.json`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load state file with schema validation and migration.
|
|
45
|
+
*/
|
|
46
|
+
export function loadState(planPath: string): Record<string, unknown> | null {
|
|
47
|
+
try {
|
|
48
|
+
const stateFile = getStateFilePath(planPath);
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(stateFile)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const state = JSON.parse(
|
|
55
|
+
fs.readFileSync(stateFile, "utf-8"),
|
|
56
|
+
) as Record<string, unknown>;
|
|
57
|
+
|
|
58
|
+
// Handle schema version (backward compatible)
|
|
59
|
+
const schemaVersion = state.schema_version as string | undefined;
|
|
60
|
+
|
|
61
|
+
if (schemaVersion === undefined) {
|
|
62
|
+
state.schema_version = STATE_SCHEMA_VERSION;
|
|
63
|
+
logInfo(
|
|
64
|
+
"state",
|
|
65
|
+
`Migrated state file to schema v${STATE_SCHEMA_VERSION}`,
|
|
66
|
+
);
|
|
67
|
+
} else if (schemaVersion !== STATE_SCHEMA_VERSION) {
|
|
68
|
+
logWarn(
|
|
69
|
+
"state",
|
|
70
|
+
`Schema mismatch (expected ${STATE_SCHEMA_VERSION}, got ${schemaVersion})`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return state;
|
|
75
|
+
} catch (e: unknown) {
|
|
76
|
+
if (e instanceof Error && e.message.includes("Invalid plan path")) {
|
|
77
|
+
logError("state", `SECURITY: Invalid plan path: ${e}`);
|
|
78
|
+
} else {
|
|
79
|
+
logError("state", `Failed to load state: ${e}`);
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Save state file with schema version and validation.
|
|
87
|
+
* Returns true on success, false on failure.
|
|
88
|
+
*/
|
|
89
|
+
export function saveStateToPlan(
|
|
90
|
+
planPath: string,
|
|
91
|
+
state: Record<string, unknown>,
|
|
92
|
+
): boolean {
|
|
93
|
+
try {
|
|
94
|
+
const stateFile = getStateFilePath(planPath);
|
|
95
|
+
|
|
96
|
+
const stateWithVersion = {
|
|
97
|
+
schema_version: STATE_SCHEMA_VERSION,
|
|
98
|
+
...state,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const [success, error] = atomicWrite(
|
|
102
|
+
stateFile,
|
|
103
|
+
JSON.stringify(stateWithVersion, null, 2),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
if (!success) {
|
|
107
|
+
logError("state", `Failed to save state: ${error}`);
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return true;
|
|
112
|
+
} catch (e: unknown) {
|
|
113
|
+
if (e instanceof Error && e.message.includes("Invalid plan path")) {
|
|
114
|
+
logError("state", `SECURITY: Invalid plan path: ${e}`);
|
|
115
|
+
} else {
|
|
116
|
+
logError("state", String(e));
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Delete state file after successful archive.
|
|
124
|
+
* Returns true if deleted or didn't exist, false on error.
|
|
125
|
+
*/
|
|
126
|
+
export function deleteState(planPath: string): boolean {
|
|
127
|
+
try {
|
|
128
|
+
const stateFile = getStateFilePath(planPath);
|
|
129
|
+
if (fs.existsSync(stateFile)) {
|
|
130
|
+
fs.unlinkSync(stateFile);
|
|
131
|
+
logInfo("state", `Deleted state file: ${stateFile}`);
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
} catch (e: unknown) {
|
|
135
|
+
if (e instanceof Error && e.message.includes("Invalid plan path")) {
|
|
136
|
+
logError("state", `SECURITY: Invalid plan path in delete: ${e}`);
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
logWarn("state", `Failed to delete state file: ${e}`);
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// Iteration State Management
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get or initialize iteration state based on complexity.
|
|
150
|
+
*/
|
|
151
|
+
export function getIterationState(
|
|
152
|
+
state: Record<string, unknown>,
|
|
153
|
+
complexity: string,
|
|
154
|
+
config?: Record<string, unknown>,
|
|
155
|
+
): IterationState {
|
|
156
|
+
if (state.iteration) {
|
|
157
|
+
return state.iteration as IterationState;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Initialize new iteration state
|
|
161
|
+
const reviewIterations = { ...DEFAULT_REVIEW_ITERATIONS };
|
|
162
|
+
if (config) {
|
|
163
|
+
const overrides = config.reviewIterations as
|
|
164
|
+
| Record<string, number>
|
|
165
|
+
| undefined;
|
|
166
|
+
if (overrides) {
|
|
167
|
+
Object.assign(reviewIterations, overrides);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
current: 1,
|
|
173
|
+
max: reviewIterations[complexity] ?? 1,
|
|
174
|
+
complexity,
|
|
175
|
+
history: [],
|
|
176
|
+
graduated: [],
|
|
177
|
+
passStreaks: {},
|
|
178
|
+
lastPlanHash: "",
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Record review result in iteration history and update state.
|
|
184
|
+
*/
|
|
185
|
+
export function updateIterationState(
|
|
186
|
+
state: Record<string, unknown>,
|
|
187
|
+
iteration: IterationState,
|
|
188
|
+
planHash: string,
|
|
189
|
+
verdict: string,
|
|
190
|
+
): Record<string, unknown> {
|
|
191
|
+
const entry: IterationEntry = {
|
|
192
|
+
hash: planHash,
|
|
193
|
+
verdict,
|
|
194
|
+
timestamp: nowIso(),
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
iteration.history.push(entry);
|
|
198
|
+
state.iteration = iteration;
|
|
199
|
+
return state;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Determine if more review iterations are needed.
|
|
204
|
+
*/
|
|
205
|
+
export function shouldContinueIterating(
|
|
206
|
+
iteration: IterationState,
|
|
207
|
+
verdict: string,
|
|
208
|
+
config?: Record<string, unknown>,
|
|
209
|
+
): boolean {
|
|
210
|
+
const current = iteration.current;
|
|
211
|
+
const maxIter = iteration.max;
|
|
212
|
+
|
|
213
|
+
// At or past max iterations
|
|
214
|
+
if (current >= maxIter) {
|
|
215
|
+
logInfo(
|
|
216
|
+
"state",
|
|
217
|
+
`At max iterations (${current}/${maxIter}), no more iterations`,
|
|
218
|
+
);
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check early exit on all pass
|
|
223
|
+
let earlyExit = true;
|
|
224
|
+
if (config) {
|
|
225
|
+
earlyExit = (config.earlyExitOnAllPass as boolean) ?? true;
|
|
226
|
+
}
|
|
227
|
+
if (earlyExit && verdict === "pass") {
|
|
228
|
+
logInfo(
|
|
229
|
+
"state",
|
|
230
|
+
"All reviewers passed and earlyExitOnAllPass=true, exiting early",
|
|
231
|
+
);
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
logInfo(
|
|
236
|
+
"state",
|
|
237
|
+
`Continuing to next iteration (${current + 1}/${maxIter}), verdict=${verdict}`,
|
|
238
|
+
);
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noUncheckedIndexedAccess": true,
|
|
8
|
+
"noFallthroughCasesInSwitch": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "../..",
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"types": ["bun-types"],
|
|
14
|
+
"skipLibCheck": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["./**/*.ts", "../../_shared/lib-ts/**/*.ts"],
|
|
17
|
+
"exclude": ["../../_shared/lib-ts/__tests__/**"]
|
|
18
|
+
}
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CC-Native plan review type definitions.
|
|
3
|
+
* All interfaces, schemas, and prompt constants for the plan review engine.
|
|
4
|
+
* See cc-native-plan-review-spec.md §3
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Re-export shared types used by cc-native consumers
|
|
8
|
+
export type { ContextState, HookInput, HookOutput } from "../../_shared/lib-ts/types.js";
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Verdict & Decision Types
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
/** Verdict from a single reviewer */
|
|
15
|
+
export type Verdict = "pass" | "warn" | "fail" | "error" | "skip";
|
|
16
|
+
|
|
17
|
+
/** Decision after aggregating all verdicts */
|
|
18
|
+
export type ReviewDecision = "allow" | "deny";
|
|
19
|
+
|
|
20
|
+
/** Complexity level assigned by orchestrator */
|
|
21
|
+
export type ComplexityCategory = "simple" | "medium" | "high";
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Review Data Structures
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
/** Dimension classification for corroboration-based verdict */
|
|
28
|
+
export type IssueDimension =
|
|
29
|
+
| "completeness"
|
|
30
|
+
| "simplicity"
|
|
31
|
+
| "security"
|
|
32
|
+
| "performance"
|
|
33
|
+
| "reliability"
|
|
34
|
+
| "maintainability"
|
|
35
|
+
| "testability"
|
|
36
|
+
| "scope"
|
|
37
|
+
| "feasibility"
|
|
38
|
+
| "clarity";
|
|
39
|
+
|
|
40
|
+
/** A single issue found during review */
|
|
41
|
+
export interface ReviewIssue {
|
|
42
|
+
severity: "high" | "medium" | "low";
|
|
43
|
+
category: string;
|
|
44
|
+
issue: string;
|
|
45
|
+
suggested_fix: string;
|
|
46
|
+
dimension?: IssueDimension;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** A group of issues in one dimension, classified as blocking or solo */
|
|
50
|
+
export interface DimensionGroup {
|
|
51
|
+
dimension: IssueDimension;
|
|
52
|
+
issues: Array<{ agent: string; issue: ReviewIssue }>;
|
|
53
|
+
agentCount: number;
|
|
54
|
+
threshold: number; // 2 × agentCount
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Corroborated (blocking) group — threshold exceeded */
|
|
58
|
+
export type CorroboratedGroup = DimensionGroup;
|
|
59
|
+
|
|
60
|
+
/** Solo finding — below threshold, informational only */
|
|
61
|
+
export type SoloFinding = DimensionGroup;
|
|
62
|
+
|
|
63
|
+
/** Result of corroboration-based verdict computation */
|
|
64
|
+
export interface CorroborationResult {
|
|
65
|
+
blocking: CorroboratedGroup[];
|
|
66
|
+
solo: SoloFinding[];
|
|
67
|
+
unclassified: Array<{ agent: string; issue: ReviewIssue }>;
|
|
68
|
+
verdict: "pass" | "fail";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Normalized review data from any reviewer */
|
|
72
|
+
export interface ReviewData {
|
|
73
|
+
verdict: Verdict;
|
|
74
|
+
summary: string;
|
|
75
|
+
summary_source: "reviewer" | "default";
|
|
76
|
+
issues: ReviewIssue[];
|
|
77
|
+
missing_sections: string[];
|
|
78
|
+
questions: string[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Result from a single plan reviewer (Codex, Gemini, or Claude agent) */
|
|
82
|
+
export interface ReviewerResult {
|
|
83
|
+
name: string;
|
|
84
|
+
ok: boolean;
|
|
85
|
+
verdict: Verdict;
|
|
86
|
+
data: Record<string, unknown>;
|
|
87
|
+
raw: string;
|
|
88
|
+
err: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Result from the plan orchestrator */
|
|
92
|
+
export interface OrchestratorResult {
|
|
93
|
+
complexity: ComplexityCategory;
|
|
94
|
+
category: string;
|
|
95
|
+
selected_agents: string[];
|
|
96
|
+
reasoning: string;
|
|
97
|
+
skip_reason?: string;
|
|
98
|
+
error?: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Combined result from all review phases */
|
|
102
|
+
export interface CombinedReviewResult {
|
|
103
|
+
plan_hash: string;
|
|
104
|
+
overall_verdict: Verdict;
|
|
105
|
+
cli_reviewers: Record<string, ReviewerResult>;
|
|
106
|
+
orchestration: OrchestratorResult | null;
|
|
107
|
+
agents: Record<string, ReviewerResult>;
|
|
108
|
+
timestamp: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Result from verdict aggregation */
|
|
112
|
+
export interface ReviewDecisionResult {
|
|
113
|
+
should_deny: boolean;
|
|
114
|
+
reason: string; // "fail_veto" | "acceptable" | "no_signal"
|
|
115
|
+
score: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Agent & Orchestrator Configuration
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
/** Configuration for a Claude Code review agent */
|
|
123
|
+
export interface AgentConfig {
|
|
124
|
+
name: string;
|
|
125
|
+
model: string;
|
|
126
|
+
provider: string; // e.g. "claude" | "codex" — assigned at runtime by assignModelsToAgents()
|
|
127
|
+
focus: string;
|
|
128
|
+
enabled: boolean;
|
|
129
|
+
categories: string[];
|
|
130
|
+
description: string;
|
|
131
|
+
system_prompt: string; // Markdown body content for --system-prompt
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Configuration for the plan orchestrator */
|
|
135
|
+
export interface OrchestratorConfig {
|
|
136
|
+
enabled: boolean;
|
|
137
|
+
model: string;
|
|
138
|
+
timeout: number;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Configuration for a model provider (Claude, Codex, etc.) */
|
|
142
|
+
export interface ProviderConfig {
|
|
143
|
+
enabled: boolean;
|
|
144
|
+
models: string[];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Model provider pool configuration */
|
|
148
|
+
export interface ModelsConfig {
|
|
149
|
+
providers: Record<string, ProviderConfig>;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// State Interfaces
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
/** A single iteration history entry */
|
|
157
|
+
export interface IterationEntry {
|
|
158
|
+
hash: string;
|
|
159
|
+
verdict: string;
|
|
160
|
+
timestamp: string;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Iteration tracking state (stored adjacent to plan file) */
|
|
164
|
+
export interface IterationState {
|
|
165
|
+
current: number;
|
|
166
|
+
max: number;
|
|
167
|
+
complexity: string;
|
|
168
|
+
history: IterationEntry[];
|
|
169
|
+
graduated: string[];
|
|
170
|
+
passStreaks: Record<string, number>;
|
|
171
|
+
lastPlanHash: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** CC-native state stored in context state.json under cc_native key */
|
|
175
|
+
export interface CcNativeState {
|
|
176
|
+
plan_review?: PlanReviewState;
|
|
177
|
+
questions_asked?: QuestionsAskedState;
|
|
178
|
+
stuck_detection?: StuckDetectionState;
|
|
179
|
+
[key: string]: unknown;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Plan review state within cc_native */
|
|
183
|
+
export interface PlanReviewState {
|
|
184
|
+
plan_hash: string;
|
|
185
|
+
reviewed_at: string;
|
|
186
|
+
decision: string;
|
|
187
|
+
iteration?: {
|
|
188
|
+
current: number;
|
|
189
|
+
max: number;
|
|
190
|
+
complexity: string;
|
|
191
|
+
latest_verdict?: string;
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** Questions-asked tracking state */
|
|
196
|
+
export interface QuestionsAskedState {
|
|
197
|
+
asked: boolean;
|
|
198
|
+
asked_at: string;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Stuck detection state — tracks repeated errors, file edits, and test failures */
|
|
202
|
+
export interface StuckDetectionState {
|
|
203
|
+
error_hashes: Record<string, number>;
|
|
204
|
+
file_edits: Record<string, number>;
|
|
205
|
+
test_failures: number;
|
|
206
|
+
tool_calls_since_suggestion: number;
|
|
207
|
+
suggestion_count: number;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
// Configuration
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
/** Display settings for review output formatting */
|
|
215
|
+
export interface DisplaySettings {
|
|
216
|
+
maxIssues: number;
|
|
217
|
+
maxMissingSections: number;
|
|
218
|
+
maxQuestions: number;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Full plan review configuration (from plan-review.config.json) */
|
|
222
|
+
export interface PlanReviewConfig {
|
|
223
|
+
planReview?: {
|
|
224
|
+
enabled?: boolean;
|
|
225
|
+
reviewers?: {
|
|
226
|
+
codex?: { enabled?: boolean; timeout?: number; model?: string };
|
|
227
|
+
gemini?: { enabled?: boolean; timeout?: number; model?: string };
|
|
228
|
+
};
|
|
229
|
+
agentReview?: {
|
|
230
|
+
enabled?: boolean;
|
|
231
|
+
timeout?: number;
|
|
232
|
+
orchestrator?: { enabled?: boolean; model?: string; timeout?: number };
|
|
233
|
+
agentSelection?: Record<string, unknown>;
|
|
234
|
+
complexityCategories?: string[];
|
|
235
|
+
};
|
|
236
|
+
display?: Partial<DisplaySettings>;
|
|
237
|
+
reviewIterations?: Record<string, number>;
|
|
238
|
+
earlyExitOnAllPass?: boolean;
|
|
239
|
+
};
|
|
240
|
+
display?: Partial<DisplaySettings>;
|
|
241
|
+
[key: string]: unknown;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// Reviewer Interface
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
/** Options passed to reviewer implementations */
|
|
249
|
+
export interface ReviewOptions {
|
|
250
|
+
timeout: number;
|
|
251
|
+
context_path?: string;
|
|
252
|
+
session_name?: string;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** Interface all reviewers must implement */
|
|
256
|
+
export interface Reviewer {
|
|
257
|
+
review(
|
|
258
|
+
plan: string,
|
|
259
|
+
schema: Record<string, unknown>,
|
|
260
|
+
options: ReviewOptions,
|
|
261
|
+
): Promise<ReviewerResult>;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// JSON Schemas
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
/** JSON schema for review structured output */
|
|
269
|
+
export const REVIEW_SCHEMA: Record<string, unknown> = {
|
|
270
|
+
type: "object",
|
|
271
|
+
properties: {
|
|
272
|
+
verdict: { type: "string", enum: ["pass", "warn", "fail"] },
|
|
273
|
+
summary: { type: "string", minLength: 20 },
|
|
274
|
+
issues: {
|
|
275
|
+
type: "array",
|
|
276
|
+
items: {
|
|
277
|
+
type: "object",
|
|
278
|
+
properties: {
|
|
279
|
+
severity: { type: "string", enum: ["high", "medium", "low"] },
|
|
280
|
+
category: { type: "string" },
|
|
281
|
+
issue: { type: "string" },
|
|
282
|
+
suggested_fix: { type: "string" },
|
|
283
|
+
dimension: {
|
|
284
|
+
type: "string",
|
|
285
|
+
enum: [
|
|
286
|
+
"completeness", "simplicity", "security", "performance",
|
|
287
|
+
"reliability", "maintainability", "testability", "scope",
|
|
288
|
+
"feasibility", "clarity",
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
required: ["severity", "category", "issue", "suggested_fix"],
|
|
293
|
+
additionalProperties: false,
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
missing_sections: { type: "array", items: { type: "string" } },
|
|
297
|
+
questions: { type: "array", items: { type: "string" } },
|
|
298
|
+
},
|
|
299
|
+
required: ["verdict", "summary", "issues", "missing_sections", "questions"],
|
|
300
|
+
additionalProperties: false,
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
/** JSON schema for orchestrator structured output */
|
|
304
|
+
export const ORCHESTRATOR_SCHEMA: Record<string, unknown> = {
|
|
305
|
+
type: "object",
|
|
306
|
+
properties: {
|
|
307
|
+
complexity: { type: "string", enum: ["simple", "medium", "high"] },
|
|
308
|
+
category: {
|
|
309
|
+
type: "string",
|
|
310
|
+
enum: [
|
|
311
|
+
"code",
|
|
312
|
+
"infrastructure",
|
|
313
|
+
"documentation",
|
|
314
|
+
"life",
|
|
315
|
+
"business",
|
|
316
|
+
"design",
|
|
317
|
+
"research",
|
|
318
|
+
],
|
|
319
|
+
},
|
|
320
|
+
selectedAgents: { type: "array", items: { type: "string" } },
|
|
321
|
+
reasoning: { type: "string" },
|
|
322
|
+
skipReason: { type: "string" },
|
|
323
|
+
},
|
|
324
|
+
required: ["complexity", "category", "selectedAgents", "reasoning"],
|
|
325
|
+
additionalProperties: false,
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
// Prompt Constants
|
|
330
|
+
// ---------------------------------------------------------------------------
|
|
331
|
+
|
|
332
|
+
export const REVIEW_PROMPT_PREFIX = `You are a senior staff software engineer acting as a strict plan reviewer.
|
|
333
|
+
|
|
334
|
+
Review the PLAN below. Focus on:
|
|
335
|
+
- missing steps, unclear assumptions, edge cases
|
|
336
|
+
- security/privacy concerns
|
|
337
|
+
- testing/rollout/rollback completeness
|
|
338
|
+
- operational concerns (observability, failure modes)
|
|
339
|
+
`;
|
|
340
|
+
|
|
341
|
+
export const AGENT_REVIEW_PROMPT_PREFIX = `# SINGLE-TURN PLAN REVIEW
|
|
342
|
+
|
|
343
|
+
## CRITICAL: ONE TURN ONLY
|
|
344
|
+
You have exactly ONE response to complete this review. Do NOT attempt multi-step workflows, context queries, or phased analysis. Analyze the plan and output your review immediately.
|
|
345
|
+
|
|
346
|
+
## YOUR TASK
|
|
347
|
+
Review the plan below from your area of expertise. Then call StructuredOutput with your assessment.
|
|
348
|
+
|
|
349
|
+
## REQUIRED OUTPUT (all fields must have content)
|
|
350
|
+
Call StructuredOutput with:
|
|
351
|
+
- **verdict**: "pass" (no concerns), "warn" (some concerns), or "fail" (critical issues)
|
|
352
|
+
- **summary**: 2-3 sentences with your overall assessment and key findings (REQUIRED)
|
|
353
|
+
- **issues**: Array of concerns found. Format each as:
|
|
354
|
+
{"severity": "high/medium/low", "category": "...", "issue": "...", "suggested_fix": "...", "dimension": "..."}
|
|
355
|
+
- **dimension**: Classify each issue into exactly one dimension:
|
|
356
|
+
completeness, simplicity, security, performance, reliability,
|
|
357
|
+
maintainability, testability, scope, feasibility, or clarity.
|
|
358
|
+
Examples: "missing error handling" → reliability, "excessive abstraction" → simplicity,
|
|
359
|
+
"no test strategy" → testability, "missing deployment steps" → completeness,
|
|
360
|
+
"unclear interaction between components" → clarity.
|
|
361
|
+
- **missing_sections**: Topics the plan should address but doesn't
|
|
362
|
+
- **questions**: Things that need clarification before implementation
|
|
363
|
+
|
|
364
|
+
## IMPORTANT RULES
|
|
365
|
+
1. A "warn" verdict MUST include at least one issue explaining why
|
|
366
|
+
2. Summary MUST explain your reasoning, not just "looks good" or empty
|
|
367
|
+
3. Focus on your expertise area (architecture, security, performance, etc.)
|
|
368
|
+
4. Output StructuredOutput NOW - no other tools, no questions, no delays
|
|
369
|
+
5. Return ONLY your top 3 most critical issues. Prioritize high-severity over medium/low. Quality over quantity.
|
|
370
|
+
`;
|
|
371
|
+
|
|
372
|
+
// ---------------------------------------------------------------------------
|
|
373
|
+
// Display Defaults
|
|
374
|
+
// ---------------------------------------------------------------------------
|
|
375
|
+
|
|
376
|
+
export const DEFAULT_DISPLAY: DisplaySettings = {
|
|
377
|
+
maxIssues: 12,
|
|
378
|
+
maxMissingSections: 12,
|
|
379
|
+
maxQuestions: 12,
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
export const DEFAULT_SANITIZATION = {
|
|
383
|
+
maxSessionIdLength: 32,
|
|
384
|
+
maxTitleLength: 50,
|
|
385
|
+
};
|