omegon 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +3 -0
- package/AGENTS.md +16 -0
- package/LICENSE +15 -0
- package/README.md +289 -0
- package/bin/pi.mjs +30 -0
- package/extensions/00-secrets/index.ts +1126 -0
- package/extensions/01-auth/auth.ts +401 -0
- package/extensions/01-auth/index.ts +289 -0
- package/extensions/auto-compact.ts +42 -0
- package/extensions/bootstrap/deps.ts +291 -0
- package/extensions/bootstrap/index.ts +811 -0
- package/extensions/chronos/chronos.sh +487 -0
- package/extensions/chronos/index.ts +148 -0
- package/extensions/cleave/assessment.ts +754 -0
- package/extensions/cleave/bridge.ts +31 -0
- package/extensions/cleave/conflicts.ts +250 -0
- package/extensions/cleave/dispatcher.ts +808 -0
- package/extensions/cleave/guardrails.ts +426 -0
- package/extensions/cleave/index.ts +3121 -0
- package/extensions/cleave/lifecycle-emitter.ts +20 -0
- package/extensions/cleave/openspec.ts +811 -0
- package/extensions/cleave/planner.ts +260 -0
- package/extensions/cleave/review.ts +579 -0
- package/extensions/cleave/skills.ts +355 -0
- package/extensions/cleave/types.ts +261 -0
- package/extensions/cleave/workspace.ts +861 -0
- package/extensions/cleave/worktree.ts +243 -0
- package/extensions/core-renderers.ts +253 -0
- package/extensions/dashboard/context-gauge.ts +58 -0
- package/extensions/dashboard/file-watch.ts +14 -0
- package/extensions/dashboard/footer.ts +1145 -0
- package/extensions/dashboard/git.ts +185 -0
- package/extensions/dashboard/index.ts +478 -0
- package/extensions/dashboard/memory-audit.ts +34 -0
- package/extensions/dashboard/overlay-data.ts +705 -0
- package/extensions/dashboard/overlay.ts +365 -0
- package/extensions/dashboard/render-utils.ts +54 -0
- package/extensions/dashboard/types.ts +191 -0
- package/extensions/dashboard/uri-helper.ts +45 -0
- package/extensions/debug.ts +69 -0
- package/extensions/defaults.ts +282 -0
- package/extensions/design-tree/dashboard-state.ts +161 -0
- package/extensions/design-tree/design-card.ts +362 -0
- package/extensions/design-tree/index.ts +2130 -0
- package/extensions/design-tree/lifecycle-emitter.ts +41 -0
- package/extensions/design-tree/tree.ts +1607 -0
- package/extensions/design-tree/types.ts +163 -0
- package/extensions/distill.ts +127 -0
- package/extensions/effort/index.ts +395 -0
- package/extensions/effort/tiers.ts +146 -0
- package/extensions/effort/types.ts +105 -0
- package/extensions/lib/git-state.ts +227 -0
- package/extensions/lib/local-models.ts +157 -0
- package/extensions/lib/model-preferences.ts +51 -0
- package/extensions/lib/model-routing.ts +720 -0
- package/extensions/lib/operator-fallback.ts +205 -0
- package/extensions/lib/operator-profile.ts +360 -0
- package/extensions/lib/slash-command-bridge.ts +253 -0
- package/extensions/lib/typebox-helpers.ts +16 -0
- package/extensions/local-inference/index.ts +727 -0
- package/extensions/mcp-bridge/README.md +220 -0
- package/extensions/mcp-bridge/index.ts +951 -0
- package/extensions/mcp-bridge/lib.ts +365 -0
- package/extensions/mcp-bridge/mcp.json +3 -0
- package/extensions/mcp-bridge/package.json +11 -0
- package/extensions/model-budget.ts +752 -0
- package/extensions/offline-driver.ts +403 -0
- package/extensions/openspec/archive-gate.ts +164 -0
- package/extensions/openspec/branch-cleanup.ts +64 -0
- package/extensions/openspec/dashboard-state.ts +50 -0
- package/extensions/openspec/index.ts +1917 -0
- package/extensions/openspec/lifecycle-emitter.ts +65 -0
- package/extensions/openspec/lifecycle-files.ts +70 -0
- package/extensions/openspec/lifecycle.ts +50 -0
- package/extensions/openspec/reconcile.ts +187 -0
- package/extensions/openspec/spec.ts +1385 -0
- package/extensions/openspec/types.ts +98 -0
- package/extensions/project-memory/DESIGN-global-mind.md +198 -0
- package/extensions/project-memory/README.md +202 -0
- package/extensions/project-memory/api-types.ts +382 -0
- package/extensions/project-memory/compaction-policy.ts +29 -0
- package/extensions/project-memory/core.ts +164 -0
- package/extensions/project-memory/embeddings.ts +230 -0
- package/extensions/project-memory/extraction-v2.ts +861 -0
- package/extensions/project-memory/factstore.ts +2177 -0
- package/extensions/project-memory/index.ts +3459 -0
- package/extensions/project-memory/injection-metrics.ts +91 -0
- package/extensions/project-memory/jsonl-io.ts +12 -0
- package/extensions/project-memory/lifecycle.ts +331 -0
- package/extensions/project-memory/migration.ts +293 -0
- package/extensions/project-memory/package.json +9 -0
- package/extensions/project-memory/sci-renderers.ts +7 -0
- package/extensions/project-memory/template.ts +103 -0
- package/extensions/project-memory/triggers.ts +52 -0
- package/extensions/project-memory/types.ts +102 -0
- package/extensions/render/composition/fonts/Inter-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Inter-Regular.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Regular.ttf +0 -0
- package/extensions/render/composition/package-lock.json +534 -0
- package/extensions/render/composition/package.json +22 -0
- package/extensions/render/composition/render.mjs +246 -0
- package/extensions/render/composition/test-comp.tsx +87 -0
- package/extensions/render/composition/types.ts +24 -0
- package/extensions/render/excalidraw/UPSTREAM.md +81 -0
- package/extensions/render/excalidraw/elements.ts +764 -0
- package/extensions/render/excalidraw/index.ts +66 -0
- package/extensions/render/excalidraw/types.ts +223 -0
- package/extensions/render/excalidraw-renderer/pyproject.toml +8 -0
- package/extensions/render/excalidraw-renderer/render_excalidraw.py +182 -0
- package/extensions/render/excalidraw-renderer/render_template.html +59 -0
- package/extensions/render/index.ts +830 -0
- package/extensions/render/native-diagrams/index.ts +57 -0
- package/extensions/render/native-diagrams/motifs.ts +542 -0
- package/extensions/render/native-diagrams/raster.ts +8 -0
- package/extensions/render/native-diagrams/scene.ts +75 -0
- package/extensions/render/native-diagrams/spec.ts +204 -0
- package/extensions/render/native-diagrams/svg.ts +116 -0
- package/extensions/sci-ui.ts +304 -0
- package/extensions/session-log.ts +174 -0
- package/extensions/shared-state.ts +146 -0
- package/extensions/spinner-verbs.ts +91 -0
- package/extensions/style.ts +281 -0
- package/extensions/terminal-title.ts +191 -0
- package/extensions/tool-profile/index.ts +291 -0
- package/extensions/tool-profile/profiles.ts +290 -0
- package/extensions/types.d.ts +9 -0
- package/extensions/vault/index.ts +185 -0
- package/extensions/version-check.ts +90 -0
- package/extensions/view/index.ts +859 -0
- package/extensions/view/uri-resolver.ts +148 -0
- package/extensions/web-search/index.ts +182 -0
- package/extensions/web-search/providers.ts +121 -0
- package/extensions/web-ui/index.ts +110 -0
- package/extensions/web-ui/server.ts +265 -0
- package/extensions/web-ui/state.ts +462 -0
- package/extensions/web-ui/static/index.html +145 -0
- package/extensions/web-ui/types.ts +284 -0
- package/package.json +76 -0
- package/prompts/init.md +75 -0
- package/prompts/new-repo.md +54 -0
- package/prompts/oci-login.md +56 -0
- package/prompts/status.md +50 -0
- package/settings.json +4 -0
- package/skills/cleave/SKILL.md +218 -0
- package/skills/git/SKILL.md +209 -0
- package/skills/git/_reference/ci-validation.md +204 -0
- package/skills/oci/SKILL.md +338 -0
- package/skills/openspec/SKILL.md +346 -0
- package/skills/pi-extensions/SKILL.md +191 -0
- package/skills/pi-tui/SKILL.md +517 -0
- package/skills/python/SKILL.md +189 -0
- package/skills/rust/SKILL.md +268 -0
- package/skills/security/SKILL.md +206 -0
- package/skills/style/SKILL.md +264 -0
- package/skills/typescript/SKILL.md +225 -0
- package/skills/vault/SKILL.md +102 -0
- package/themes/alpharius-legacy.json +85 -0
- package/themes/alpharius.conf +59 -0
- package/themes/alpharius.json +88 -0
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cleave/assessment — Pattern library and complexity calculation.
|
|
3
|
+
*
|
|
4
|
+
* Ported from styrene-lab/cleave src/cleave/core/assessment.py.
|
|
5
|
+
* Pure functions with zero runtime dependencies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { AssessmentFlags, AssessmentResult, PatternDefinition, PatternMatch } from "./types.ts";
|
|
9
|
+
|
|
10
|
+
export type AssessStructuredSubcommand = "cleave" | "diff" | "spec" | "complexity" | "session" | "freeform" | "design";
|
|
11
|
+
export type AssessEffect =
|
|
12
|
+
| { type: "view"; content: string; display?: boolean }
|
|
13
|
+
| { type: "follow_up"; content: string }
|
|
14
|
+
| {
|
|
15
|
+
type: "reconcile_hint";
|
|
16
|
+
changeName: string;
|
|
17
|
+
assessmentKind: "spec" | "cleave";
|
|
18
|
+
outcomes: readonly ("pass" | "reopen" | "ambiguous")[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type AssessLifecycleOutcome = "pass" | "reopen" | "ambiguous";
|
|
22
|
+
|
|
23
|
+
export interface AssessLifecycleHint {
|
|
24
|
+
changeName: string;
|
|
25
|
+
assessmentKind: "spec" | "cleave";
|
|
26
|
+
outcomes: readonly AssessLifecycleOutcome[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AssessSnapshotIdentity {
|
|
30
|
+
gitHead: string | null;
|
|
31
|
+
fingerprint: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface AssessReconciliationHints {
|
|
35
|
+
reopen: boolean;
|
|
36
|
+
changedFiles: string[];
|
|
37
|
+
constraints: string[];
|
|
38
|
+
recommendedAction: string | null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AssessLifecycleRecord {
|
|
42
|
+
changeName: string;
|
|
43
|
+
assessmentKind: "spec" | "cleave";
|
|
44
|
+
outcome: AssessLifecycleOutcome;
|
|
45
|
+
timestamp: string;
|
|
46
|
+
snapshot: AssessSnapshotIdentity;
|
|
47
|
+
reconciliation: AssessReconciliationHints;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface AssessCompletion {
|
|
51
|
+
completed: boolean;
|
|
52
|
+
completedInBand: boolean;
|
|
53
|
+
requiresFollowUp: boolean;
|
|
54
|
+
outcome?: AssessLifecycleOutcome;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface AssessSpecScenarioResult {
|
|
58
|
+
domain: string;
|
|
59
|
+
requirement: string;
|
|
60
|
+
scenario: string;
|
|
61
|
+
status: "PASS" | "FAIL" | "UNCLEAR";
|
|
62
|
+
evidence: string[];
|
|
63
|
+
notes?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface AssessSpecSummary {
|
|
67
|
+
total: number;
|
|
68
|
+
pass: number;
|
|
69
|
+
fail: number;
|
|
70
|
+
unclear: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface AssessStructuredResult<TData = unknown> {
|
|
74
|
+
command: "assess";
|
|
75
|
+
subcommand: AssessStructuredSubcommand;
|
|
76
|
+
args: string;
|
|
77
|
+
ok: boolean;
|
|
78
|
+
summary: string;
|
|
79
|
+
humanText: string;
|
|
80
|
+
data: TData;
|
|
81
|
+
effects: AssessEffect[];
|
|
82
|
+
nextSteps: string[];
|
|
83
|
+
completion?: AssessCompletion;
|
|
84
|
+
lifecycle?: AssessLifecycleHint;
|
|
85
|
+
lifecycleRecord?: AssessLifecycleRecord;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
89
|
+
// PATTERN LIBRARY — 12 domain patterns for fast-path assessment
|
|
90
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
91
|
+
|
|
92
|
+
export const PATTERNS: Record<string, PatternDefinition> = {
|
|
93
|
+
full_stack_crud: {
|
|
94
|
+
name: "Full-Stack CRUD",
|
|
95
|
+
description: "Create/read/update/delete operations spanning UI, API, and database layers",
|
|
96
|
+
keywords: [
|
|
97
|
+
"form", "page", "crud", "create", "update", "delete", "add", "edit", "remove",
|
|
98
|
+
"table", "migration", "endpoint", "api", "frontend", "backend", "react", "vue",
|
|
99
|
+
"angular", "express", "django", "rails", "postgres", "mysql", "mongodb",
|
|
100
|
+
],
|
|
101
|
+
requiredAny: ["crud", "create", "add", "update", "delete", "form", "page"],
|
|
102
|
+
expectedComponents: {
|
|
103
|
+
ui_layer: ["react", "vue", "angular", "frontend", "form", "page", "component"],
|
|
104
|
+
api_layer: ["api", "endpoint", "express", "django", "rails", "backend", "route"],
|
|
105
|
+
db_layer: ["postgres", "mysql", "mongodb", "table", "database", "schema"],
|
|
106
|
+
},
|
|
107
|
+
systemsBase: 3,
|
|
108
|
+
modifiersDefault: ["data_migration"],
|
|
109
|
+
splitStrategy: ["Database layer", "API layer", "UI layer"],
|
|
110
|
+
},
|
|
111
|
+
authentication: {
|
|
112
|
+
name: "Authentication System",
|
|
113
|
+
description: "User authentication with credential storage, token management, protected routes",
|
|
114
|
+
keywords: [
|
|
115
|
+
"auth", "authentication", "login", "register", "logout", "jwt", "token", "session",
|
|
116
|
+
"oauth", "saml", "bcrypt", "password", "credential", "sign", "verify", "middleware",
|
|
117
|
+
"guard", "protected", "secure",
|
|
118
|
+
],
|
|
119
|
+
requiredAny: ["auth", "login", "jwt", "oauth", "session", "password"],
|
|
120
|
+
expectedComponents: {
|
|
121
|
+
mechanism: ["jwt", "session", "oauth", "saml", "token"],
|
|
122
|
+
storage: ["postgres", "mysql", "mongodb", "database", "store", "bcrypt", "hash"],
|
|
123
|
+
protection: ["middleware", "guard", "protected", "route", "endpoint"],
|
|
124
|
+
},
|
|
125
|
+
systemsBase: 2,
|
|
126
|
+
modifiersDefault: ["state_coordination", "error_handling", "security_critical"],
|
|
127
|
+
splitStrategy: ["Credential storage", "Token generation/validation", "Route protection"],
|
|
128
|
+
},
|
|
129
|
+
external_integration: {
|
|
130
|
+
name: "External Service Integration",
|
|
131
|
+
description: "Third-party API integration with error handling and state management",
|
|
132
|
+
keywords: [
|
|
133
|
+
"stripe", "twilio", "sendgrid", "aws", "s3", "lambda", "google", "api", "integrate",
|
|
134
|
+
"webhook", "callback", "payment", "email", "sms", "storage", "analytics", "external",
|
|
135
|
+
"third-party", "provider",
|
|
136
|
+
],
|
|
137
|
+
requiredAny: ["stripe", "twilio", "sendgrid", "aws", "s3", "webhook", "integrate"],
|
|
138
|
+
expectedComponents: {
|
|
139
|
+
provider: ["stripe", "twilio", "sendgrid", "aws", "s3", "google", "azure"],
|
|
140
|
+
operation: ["webhook", "callback", "payment", "email", "sms", "upload"],
|
|
141
|
+
error_handling: ["retry", "error", "idempotent", "handle", "fallback"],
|
|
142
|
+
},
|
|
143
|
+
systemsBase: 4,
|
|
144
|
+
modifiersDefault: ["state_coordination", "error_handling", "third_party_api"],
|
|
145
|
+
splitStrategy: ["Database schema", "API client + error handling", "Webhook handlers", "UI integration"],
|
|
146
|
+
},
|
|
147
|
+
database_migration: {
|
|
148
|
+
name: "Database Migration",
|
|
149
|
+
description: "Schema changes, data transformations, or backfill operations",
|
|
150
|
+
keywords: [
|
|
151
|
+
"migrate", "migration", "schema", "column", "table", "index", "alter", "backfill",
|
|
152
|
+
"transform", "constraint", "rollback", "transaction",
|
|
153
|
+
],
|
|
154
|
+
requiredAny: ["migrate", "migration", "schema", "column", "alter", "backfill"],
|
|
155
|
+
expectedComponents: {
|
|
156
|
+
change_type: ["column", "table", "index", "constraint", "alter", "add", "remove"],
|
|
157
|
+
strategy: ["migration", "backfill", "rollback", "transaction"],
|
|
158
|
+
},
|
|
159
|
+
systemsBase: 1,
|
|
160
|
+
modifiersDefault: ["data_migration"],
|
|
161
|
+
splitStrategy: ["Migration script", "Backfill script", "Validation + rollback"],
|
|
162
|
+
},
|
|
163
|
+
performance_optimization: {
|
|
164
|
+
name: "Performance Optimization",
|
|
165
|
+
description: "Caching, query optimization, or performance improvements with SLAs",
|
|
166
|
+
keywords: [
|
|
167
|
+
"optimize", "cache", "redis", "memcached", "cdn", "performance", "latency",
|
|
168
|
+
"throughput", "p95", "p99", "response time", "query", "index", "slow",
|
|
169
|
+
],
|
|
170
|
+
requiredAny: ["optimize", "cache", "redis", "performance", "latency"],
|
|
171
|
+
expectedComponents: {
|
|
172
|
+
technology: ["redis", "memcached", "cdn", "cache", "index"],
|
|
173
|
+
sla: ["p95", "p99", "latency", "ms", "response time", "throughput"],
|
|
174
|
+
invalidation: ["invalidat", "expire", "ttl", "refresh"],
|
|
175
|
+
},
|
|
176
|
+
systemsBase: 3,
|
|
177
|
+
modifiersDefault: ["state_coordination", "concurrency", "performance_critical"],
|
|
178
|
+
splitStrategy: ["Caching layer", "Cache invalidation", "Monitoring + metrics"],
|
|
179
|
+
},
|
|
180
|
+
breaking_api_change: {
|
|
181
|
+
name: "Breaking API Change",
|
|
182
|
+
description: "API contract modifications with versioning or backwards compatibility",
|
|
183
|
+
keywords: [
|
|
184
|
+
"version", "v1", "v2", "deprecate", "deprecated", "breaking", "rename", "endpoint",
|
|
185
|
+
"contract", "backward", "migration", "client",
|
|
186
|
+
],
|
|
187
|
+
requiredAny: ["version", "v2", "deprecate", "breaking", "backward"],
|
|
188
|
+
expectedComponents: {
|
|
189
|
+
change: ["rename", "remove", "modify", "change", "breaking"],
|
|
190
|
+
versioning: ["version", "v1", "v2", "v3", "deprecat"],
|
|
191
|
+
client_plan: ["client", "consumer", "update", "migrat"],
|
|
192
|
+
},
|
|
193
|
+
systemsBase: 2,
|
|
194
|
+
modifiersDefault: ["breaking_changes"],
|
|
195
|
+
splitStrategy: ["New versioned endpoint", "Deprecation + dual-support", "Client migration"],
|
|
196
|
+
},
|
|
197
|
+
simple_refactor: {
|
|
198
|
+
name: "Simple Refactor",
|
|
199
|
+
description: "Code cleanup, renaming, or structural changes without functional modifications",
|
|
200
|
+
keywords: [
|
|
201
|
+
"refactor", "rename", "reorganize", "cleanup", "extract", "inline", "move",
|
|
202
|
+
"restructure", "mechanical", "no functional",
|
|
203
|
+
],
|
|
204
|
+
requiredAny: ["refactor", "rename", "cleanup", "extract", "reorganize"],
|
|
205
|
+
expectedComponents: {
|
|
206
|
+
operation: ["rename", "extract", "inline", "move", "reorganize"],
|
|
207
|
+
scope: ["function", "class", "file", "module", "component", "method"],
|
|
208
|
+
},
|
|
209
|
+
systemsBase: 1,
|
|
210
|
+
modifiersDefault: [],
|
|
211
|
+
splitStrategy: ["By module/scope", "Update tests"],
|
|
212
|
+
},
|
|
213
|
+
bug_fix: {
|
|
214
|
+
name: "Bug Fix",
|
|
215
|
+
description: "Fix broken functionality, crashes, or incorrect behavior",
|
|
216
|
+
keywords: [
|
|
217
|
+
"fix", "bug", "broken", "issue", "error", "crash", "regression", "defect",
|
|
218
|
+
"incorrect", "failing", "problem",
|
|
219
|
+
],
|
|
220
|
+
requiredAny: ["fix", "bug", "broken", "crash", "error", "regression"],
|
|
221
|
+
expectedComponents: {
|
|
222
|
+
problem: ["crash", "error", "broken", "failing", "incorrect", "regression"],
|
|
223
|
+
area: ["auth", "api", "database", "ui", "workflow", "validation"],
|
|
224
|
+
},
|
|
225
|
+
systemsBase: 0.5,
|
|
226
|
+
modifiersDefault: ["error_handling"],
|
|
227
|
+
splitStrategy: ["Reproduce bug", "Fix implementation", "Add regression test"],
|
|
228
|
+
},
|
|
229
|
+
greenfield_project: {
|
|
230
|
+
name: "Greenfield Project",
|
|
231
|
+
description: "New project scaffolding with multiple modules, crates, or packages from scratch",
|
|
232
|
+
keywords: [
|
|
233
|
+
"new project", "greenfield", "scaffold", "bootstrap", "from scratch", "create",
|
|
234
|
+
"initialize", "init", "setup", "starter", "boilerplate", "foundation",
|
|
235
|
+
"workspace", "monorepo", "crate", "package", "module", "library", "app",
|
|
236
|
+
"application", "binary", "build", "architecture", "project structure",
|
|
237
|
+
],
|
|
238
|
+
requiredAny: ["new project", "greenfield", "scaffold", "bootstrap", "from scratch", "initialize", "init"],
|
|
239
|
+
expectedComponents: {
|
|
240
|
+
structure: ["workspace", "monorepo", "crate", "package", "module", "directory", "layout"],
|
|
241
|
+
build: ["cargo", "npm", "pip", "gradle", "cmake", "makefile", "build", "compile"],
|
|
242
|
+
modules: ["library", "app", "binary", "core", "foundation", "model", "render", "engine"],
|
|
243
|
+
},
|
|
244
|
+
systemsBase: 3,
|
|
245
|
+
modifiersDefault: [],
|
|
246
|
+
splitStrategy: ["Foundation/core module", "Domain modules (parallel)", "Application integration"],
|
|
247
|
+
},
|
|
248
|
+
multi_module_library: {
|
|
249
|
+
name: "Multi-Module Library",
|
|
250
|
+
description: "Building a library or framework with interdependent modules/crates/packages",
|
|
251
|
+
keywords: [
|
|
252
|
+
"library", "framework", "crate", "package", "module", "api", "sdk",
|
|
253
|
+
"trait", "interface", "abstraction", "export", "publish", "public api",
|
|
254
|
+
"dependency", "workspace", "monorepo", "multi-crate", "multi-package",
|
|
255
|
+
],
|
|
256
|
+
requiredAny: ["library", "framework", "crate", "sdk", "multi-crate", "multi-package"],
|
|
257
|
+
expectedComponents: {
|
|
258
|
+
modules: ["crate", "package", "module", "workspace", "monorepo"],
|
|
259
|
+
api: ["trait", "interface", "abstraction", "export", "public", "api"],
|
|
260
|
+
build: ["cargo", "npm", "pip", "gradle", "build", "compile", "link"],
|
|
261
|
+
},
|
|
262
|
+
systemsBase: 2,
|
|
263
|
+
modifiersDefault: [],
|
|
264
|
+
splitStrategy: ["Core traits/interfaces", "Module implementations (parallel)", "Integration + public API"],
|
|
265
|
+
},
|
|
266
|
+
application_bootstrap: {
|
|
267
|
+
name: "Application Bootstrap",
|
|
268
|
+
description: "Setting up a new application with its full architecture (UI, document model, rendering, etc.)",
|
|
269
|
+
keywords: [
|
|
270
|
+
"application", "app", "gui", "tui", "cli", "desktop", "web app", "mobile",
|
|
271
|
+
"document model", "rendering", "pipeline", "event loop", "main loop",
|
|
272
|
+
"window", "canvas", "layout", "state management", "architecture",
|
|
273
|
+
"egui", "iced", "tauri", "electron", "react", "svelte",
|
|
274
|
+
],
|
|
275
|
+
requiredAny: ["application", "app", "gui", "tui", "desktop", "web app"],
|
|
276
|
+
expectedComponents: {
|
|
277
|
+
ui: ["gui", "tui", "window", "canvas", "component", "layout", "render", "egui", "iced"],
|
|
278
|
+
model: ["model", "state", "document", "data", "store", "management"],
|
|
279
|
+
infra: ["event", "loop", "pipeline", "architecture", "main", "entry"],
|
|
280
|
+
},
|
|
281
|
+
systemsBase: 3,
|
|
282
|
+
modifiersDefault: ["state_coordination"],
|
|
283
|
+
splitStrategy: ["Core data model", "Rendering/UI layer", "Event handling + state management", "Application shell"],
|
|
284
|
+
},
|
|
285
|
+
refactor: {
|
|
286
|
+
name: "Refactor",
|
|
287
|
+
description: "Replace or rewrite implementation while preserving behavior",
|
|
288
|
+
keywords: [
|
|
289
|
+
"refactor", "replace", "rewrite", "improve", "clean", "restructure", "reorganize",
|
|
290
|
+
"swap", "substitute", "modernize",
|
|
291
|
+
],
|
|
292
|
+
requiredAny: ["replace", "rewrite", "swap", "substitute", "refactor"],
|
|
293
|
+
expectedComponents: {
|
|
294
|
+
operation: ["replace", "rewrite", "swap", "substitute"],
|
|
295
|
+
target: ["implementation", "approach", "library", "framework", "pattern"],
|
|
296
|
+
},
|
|
297
|
+
systemsBase: 1.0,
|
|
298
|
+
modifiersDefault: [],
|
|
299
|
+
splitStrategy: ["Implement replacement", "Update call sites", "Remove old implementation"],
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
304
|
+
// MODIFIERS — complexity multipliers
|
|
305
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
306
|
+
|
|
307
|
+
export const MODIFIERS: Record<string, string[]> = {
|
|
308
|
+
state_coordination: ["transaction", "cache invalidation", "eventual consistency", "sync", "distributed"],
|
|
309
|
+
error_handling: ["rollback", "compensation", "recovery", "retry", "error handling"],
|
|
310
|
+
concurrency: ["concurrent", "lock", "atomic", "race condition", "thread"],
|
|
311
|
+
security_critical: ["auth", "encrypt", "secret", "pii", "credential", "password", "token"],
|
|
312
|
+
breaking_changes: ["breaking", "backward compat", "deprecate", "migration path"],
|
|
313
|
+
data_migration: ["migration", "schema", "backfill", "transform", "alter"],
|
|
314
|
+
third_party_api: ["stripe", "twilio", "sendgrid", "aws", "external api", "webhook"],
|
|
315
|
+
performance_critical: ["sla", "latency", "p95", "p99", "throughput", "<100ms"],
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
319
|
+
// SYSTEM SIGNALS — architectural boundary detection for heuristic fallback
|
|
320
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
321
|
+
|
|
322
|
+
const SYSTEM_SIGNALS: Record<string, string[]> = {
|
|
323
|
+
abstraction: ["interface", "abstraction", "abstract", "protocol", "contract"],
|
|
324
|
+
discovery: ["registry", "discover", "plugin", "loader", "dynamic"],
|
|
325
|
+
observability: ["decorator", "instrument", "monitor", "metrics", "analytics", "telemetry", "profil"],
|
|
326
|
+
streaming: ["stream", "real-time", "realtime", "websocket", "sse", "event-driven"],
|
|
327
|
+
backend_layer: ["backend", "api", "endpoint", "route", "microservice"],
|
|
328
|
+
frontend_layer: ["frontend", "ui", "component", "page", "form", "tui", "terminal"],
|
|
329
|
+
data_layer: ["database", "db", "schema", "migration", "orm", "table"],
|
|
330
|
+
auth_layer: ["auth", "token", "session", "credential", "jwt", "oauth"],
|
|
331
|
+
infrastructure: ["cache", "queue", "worker", "job", "celery"],
|
|
332
|
+
io_layer: ["export", "import", "format", "parse", "serializ"],
|
|
333
|
+
cli_layer: ["cli", "subcommand", "argparse", "click"],
|
|
334
|
+
security_layer: ["security", "validation", "scan", "audit", "policy"],
|
|
335
|
+
config_layer: ["config", "setting", "preference"],
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
339
|
+
// CORE FUNCTIONS
|
|
340
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
341
|
+
|
|
342
|
+
/** Source pattern for file extension detection — shared by the safe wrappers below. */
|
|
343
|
+
const FILE_EXT_SOURCE = String.raw`\w+\.(ts|tsx|js|jsx|py|java|go|rs|rb|php|cpp|c|h|kt|swift|cs|m|scala)\b`;
|
|
344
|
+
|
|
345
|
+
/** Safe wrapper: always returns fresh matches, no global state leakage. */
|
|
346
|
+
function fileExtMatches(text: string): RegExpMatchArray[] {
|
|
347
|
+
return [...text.matchAll(new RegExp(FILE_EXT_SOURCE, "g"))];
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** Safe wrapper for test: uses a non-global regex, no lastIndex mutation. */
|
|
351
|
+
function hasFileExt(text: string): boolean {
|
|
352
|
+
return new RegExp(FILE_EXT_SOURCE).test(text);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Estimate the number of architectural system boundaries in a directive.
|
|
357
|
+
*
|
|
358
|
+
* Single file mention → cap at 1.
|
|
359
|
+
* Multiple file mentions → max(signal count, file count).
|
|
360
|
+
* No file mentions → signal-based estimation.
|
|
361
|
+
*/
|
|
362
|
+
export function estimateSystems(directive: string): number {
|
|
363
|
+
const lower = directive.toLowerCase();
|
|
364
|
+
|
|
365
|
+
const fileMatches = fileExtMatches(lower);
|
|
366
|
+
|
|
367
|
+
const baseCount = Object.values(SYSTEM_SIGNALS).filter((keywords) =>
|
|
368
|
+
keywords.some((kw) => lower.includes(kw)),
|
|
369
|
+
).length;
|
|
370
|
+
|
|
371
|
+
if (fileMatches.length === 1) return 1;
|
|
372
|
+
if (fileMatches.length > 1) return Math.max(1, Math.max(baseCount, fileMatches.length));
|
|
373
|
+
return Math.max(1, baseCount);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Match directive against the 12 core patterns.
|
|
378
|
+
*
|
|
379
|
+
* Confidence scoring:
|
|
380
|
+
* - Base 0.55 for having a required keyword
|
|
381
|
+
* - +0.05 per keyword matched (up to 0.20)
|
|
382
|
+
* - +0.08 per expected component category covered
|
|
383
|
+
* - +0.05 for all components covered
|
|
384
|
+
* - +0.05 for specific technology names
|
|
385
|
+
* - +0.15 for specific file mention (Bug Fix boost)
|
|
386
|
+
* - -0.15 for vague terms
|
|
387
|
+
*/
|
|
388
|
+
export function matchPattern(directive: string): PatternMatch | null {
|
|
389
|
+
const lower = directive.toLowerCase();
|
|
390
|
+
let best: PatternMatch | null = null;
|
|
391
|
+
|
|
392
|
+
for (const [patternId, pattern] of Object.entries(PATTERNS)) {
|
|
393
|
+
const hasRequired = pattern.requiredAny.some((req) => lower.includes(req));
|
|
394
|
+
if (!hasRequired) continue;
|
|
395
|
+
|
|
396
|
+
const keywordsMatched = pattern.keywords.filter((kw) => lower.includes(kw));
|
|
397
|
+
|
|
398
|
+
// Base confidence
|
|
399
|
+
let confidence = 0.55;
|
|
400
|
+
|
|
401
|
+
// +0.05 per keyword (capped at 0.20)
|
|
402
|
+
confidence += Math.min(0.20, keywordsMatched.length * 0.05);
|
|
403
|
+
|
|
404
|
+
// Component coverage
|
|
405
|
+
const expectedEntries = Object.values(pattern.expectedComponents);
|
|
406
|
+
let componentsCovered = 0;
|
|
407
|
+
for (const componentKeywords of expectedEntries) {
|
|
408
|
+
if (componentKeywords.some((kw) => lower.includes(kw))) {
|
|
409
|
+
componentsCovered++;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (expectedEntries.length > 0) {
|
|
413
|
+
confidence += componentsCovered * 0.08;
|
|
414
|
+
if (componentsCovered === expectedEntries.length) {
|
|
415
|
+
confidence += 0.05;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Bug Fix: file-specific boost
|
|
420
|
+
if (patternId === "bug_fix" && hasFileExt(lower)) {
|
|
421
|
+
confidence += 0.15;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Specific technology bonus
|
|
425
|
+
const specificTech = ["postgres", "mysql", "mongodb", "redis", "stripe", "aws", "jwt", "oauth"];
|
|
426
|
+
if (specificTech.some((tech) => lower.includes(tech))) {
|
|
427
|
+
confidence += 0.05;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Vague penalty
|
|
431
|
+
const vagueTerms = ["better", "improve", "fix issues", "make it", "update"];
|
|
432
|
+
if (vagueTerms.some((vague) => lower.includes(vague))) {
|
|
433
|
+
confidence -= 0.15;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
confidence = Math.min(0.98, Math.max(0.0, confidence));
|
|
437
|
+
|
|
438
|
+
const threshold = patternId === "bug_fix" ? 0.65 : 0.80;
|
|
439
|
+
if (confidence < threshold) continue;
|
|
440
|
+
|
|
441
|
+
if (!best || confidence > best.confidence) {
|
|
442
|
+
best = {
|
|
443
|
+
patternId,
|
|
444
|
+
name: pattern.name,
|
|
445
|
+
confidence,
|
|
446
|
+
keywordsMatched,
|
|
447
|
+
systems: pattern.systemsBase,
|
|
448
|
+
modifiers: pattern.modifiersDefault,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return best;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** Detect which complexity modifiers apply to a directive. */
|
|
457
|
+
export function detectModifiers(directive: string): string[] {
|
|
458
|
+
const lower = directive.toLowerCase();
|
|
459
|
+
return Object.entries(MODIFIERS)
|
|
460
|
+
.filter(([, keywords]) => keywords.some((kw) => lower.includes(kw)))
|
|
461
|
+
.map(([name]) => name);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Calculate complexity: (1 + systems) × (1 + 0.5 × modifiers).
|
|
466
|
+
* Capped at 100.0.
|
|
467
|
+
*/
|
|
468
|
+
export function calculateComplexity(systems: number, modifiers: string[]): number {
|
|
469
|
+
const raw = (1 + systems) * (1 + 0.5 * modifiers.length);
|
|
470
|
+
return Math.round(Math.min(raw, 100.0) * 10) / 10;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/** Effective complexity with validation offset for threshold comparison. */
|
|
474
|
+
export function effectiveComplexity(complexity: number, validate = true): number {
|
|
475
|
+
return complexity + (validate ? 1 : 0);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/** Detect special flags in directive text. */
|
|
479
|
+
export function detectFlags(directive: string): AssessmentFlags {
|
|
480
|
+
const lower = directive.toLowerCase();
|
|
481
|
+
return {
|
|
482
|
+
robust: lower.includes("cleave-robust") || lower.includes("cleave_robust"),
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Assess directive complexity using fast-path pattern matching.
|
|
488
|
+
*
|
|
489
|
+
* Returns a structured assessment with complexity, matched pattern,
|
|
490
|
+
* confidence, and decision (execute | cleave | needs_assessment).
|
|
491
|
+
*/
|
|
492
|
+
export function assessDirective(
|
|
493
|
+
directive: string,
|
|
494
|
+
threshold = 2.0,
|
|
495
|
+
validate = true,
|
|
496
|
+
): AssessmentResult {
|
|
497
|
+
if (!directive?.trim()) {
|
|
498
|
+
throw new Error("Directive cannot be empty or whitespace-only");
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const match = matchPattern(directive);
|
|
502
|
+
|
|
503
|
+
if (match) {
|
|
504
|
+
const detectedModifiers = detectModifiers(directive);
|
|
505
|
+
const allModifiers = [...new Set([...match.modifiers, ...detectedModifiers])];
|
|
506
|
+
|
|
507
|
+
// File detection can override pattern systems
|
|
508
|
+
const lower = directive.toLowerCase();
|
|
509
|
+
const fileMatches = fileExtMatches(lower);
|
|
510
|
+
|
|
511
|
+
let systemsForCalc = match.systems;
|
|
512
|
+
let systemsForDisplay = match.systems;
|
|
513
|
+
|
|
514
|
+
if (fileMatches.length === 1) {
|
|
515
|
+
systemsForCalc = Math.max(1, match.systems);
|
|
516
|
+
systemsForDisplay = 1;
|
|
517
|
+
} else if (fileMatches.length > 1) {
|
|
518
|
+
systemsForCalc = Math.max(match.systems, fileMatches.length);
|
|
519
|
+
systemsForDisplay = Math.max(match.systems, fileMatches.length);
|
|
520
|
+
} else {
|
|
521
|
+
systemsForDisplay = Math.max(1, match.systems);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const complexity = calculateComplexity(systemsForCalc, allModifiers);
|
|
525
|
+
const effComplexity = effectiveComplexity(complexity, validate);
|
|
526
|
+
const decision = effComplexity <= threshold ? "execute" : "cleave";
|
|
527
|
+
|
|
528
|
+
const result: AssessmentResult = {
|
|
529
|
+
complexity,
|
|
530
|
+
systems: systemsForDisplay,
|
|
531
|
+
modifiers: allModifiers,
|
|
532
|
+
method: "fast-path",
|
|
533
|
+
pattern: match.name,
|
|
534
|
+
confidence: match.confidence,
|
|
535
|
+
decision,
|
|
536
|
+
reasoning:
|
|
537
|
+
`Pattern '${match.name}' matched with ${(match.confidence * 100).toFixed(0)}% confidence. ` +
|
|
538
|
+
`Systems: ${systemsForDisplay}, Modifiers: ${allModifiers.length}. ` +
|
|
539
|
+
`Formula: (1 + ${systemsForCalc}) × (1 + 0.5 × ${allModifiers.length}) = ${complexity}. ` +
|
|
540
|
+
`Effective (validate=${validate}): ${effComplexity}`,
|
|
541
|
+
skipInterrogation: false,
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
// Tier 0: trivial tasks skip interrogation
|
|
545
|
+
if (
|
|
546
|
+
match.confidence >= 0.90 &&
|
|
547
|
+
effComplexity <= threshold &&
|
|
548
|
+
match.name === "Simple Refactor"
|
|
549
|
+
) {
|
|
550
|
+
result.skipInterrogation = true;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return result;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// No pattern matched — heuristic fallback.
|
|
557
|
+
// If complexity exceeds threshold, recommend cleave rather than
|
|
558
|
+
// needs_assessment. A high-complexity directive without a matching
|
|
559
|
+
// pattern still benefits from decomposition.
|
|
560
|
+
const detectedModifiers = detectModifiers(directive);
|
|
561
|
+
const estimatedSystems = estimateSystems(directive);
|
|
562
|
+
const complexity = calculateComplexity(estimatedSystems, detectedModifiers);
|
|
563
|
+
const effComplexity = effectiveComplexity(complexity, validate);
|
|
564
|
+
|
|
565
|
+
const decision = effComplexity > threshold ? "cleave" : "needs_assessment";
|
|
566
|
+
|
|
567
|
+
return {
|
|
568
|
+
complexity,
|
|
569
|
+
systems: estimatedSystems,
|
|
570
|
+
modifiers: detectedModifiers,
|
|
571
|
+
method: "heuristic",
|
|
572
|
+
pattern: null,
|
|
573
|
+
confidence: 0,
|
|
574
|
+
decision,
|
|
575
|
+
reasoning:
|
|
576
|
+
`No pattern matched with sufficient confidence. ` +
|
|
577
|
+
`Heuristic estimate: ${estimatedSystems} systems, ${detectedModifiers.length} modifiers. ` +
|
|
578
|
+
`Complexity: ${complexity}, effective: ${effComplexity}.` +
|
|
579
|
+
(decision === "cleave" ? ` Exceeds threshold ${threshold} — recommending decomposition.` : ""),
|
|
580
|
+
skipInterrogation: false,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
585
|
+
// DESIGN ASSESSMENT — Structural + LLM evaluation of design-tree nodes
|
|
586
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
587
|
+
|
|
588
|
+
export type DesignFindingType = "scenario" | "falsifiability" | "constraint" | "structural";
|
|
589
|
+
|
|
590
|
+
export interface DesignAssessmentFinding {
|
|
591
|
+
type: DesignFindingType;
|
|
592
|
+
index: number;
|
|
593
|
+
pass: boolean;
|
|
594
|
+
finding: string;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export interface DesignAssessmentResult {
|
|
598
|
+
nodeId: string;
|
|
599
|
+
pass: boolean;
|
|
600
|
+
structuralPass: boolean;
|
|
601
|
+
findings: DesignAssessmentFinding[];
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Run the structural pre-check for a design node.
|
|
606
|
+
* Returns findings[] — empty means structural pass.
|
|
607
|
+
*/
|
|
608
|
+
export function runDesignStructuralCheck(
|
|
609
|
+
nodeId: string,
|
|
610
|
+
sections: {
|
|
611
|
+
openQuestions: string[];
|
|
612
|
+
decisions: Array<{ title: string; status: string; rationale: string }>;
|
|
613
|
+
acceptanceCriteria: {
|
|
614
|
+
scenarios: unknown[];
|
|
615
|
+
falsifiability: unknown[];
|
|
616
|
+
constraints: unknown[];
|
|
617
|
+
};
|
|
618
|
+
},
|
|
619
|
+
): DesignAssessmentFinding[] {
|
|
620
|
+
const findings: DesignAssessmentFinding[] = [];
|
|
621
|
+
|
|
622
|
+
if (sections.openQuestions.length > 0) {
|
|
623
|
+
findings.push({
|
|
624
|
+
type: "structural",
|
|
625
|
+
index: 0,
|
|
626
|
+
pass: false,
|
|
627
|
+
finding: `Node has ${sections.openQuestions.length} unresolved open question(s): ${sections.openQuestions.map((q) => `"${q}"`).join(", ")}. Resolve all open questions before assessing.`,
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (sections.decisions.length === 0) {
|
|
632
|
+
findings.push({
|
|
633
|
+
type: "structural",
|
|
634
|
+
index: 1,
|
|
635
|
+
pass: false,
|
|
636
|
+
finding: "No decisions recorded. Add at least one decision in the Decisions section before assessing.",
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const ac = sections.acceptanceCriteria;
|
|
641
|
+
const hasAnyCriteria =
|
|
642
|
+
ac.scenarios.length > 0 || ac.falsifiability.length > 0 || ac.constraints.length > 0;
|
|
643
|
+
if (!hasAnyCriteria) {
|
|
644
|
+
findings.push({
|
|
645
|
+
type: "structural",
|
|
646
|
+
index: 2,
|
|
647
|
+
pass: false,
|
|
648
|
+
finding: "Acceptance Criteria section is empty. Add at least one scenario, falsifiability condition, or constraint.",
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return findings;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Build the LLM prompt for design assessment.
|
|
657
|
+
* The LLM evaluates each acceptance criterion against the document body.
|
|
658
|
+
*/
|
|
659
|
+
export function buildDesignAssessmentPrompt(
|
|
660
|
+
nodeId: string,
|
|
661
|
+
nodeTitle: string,
|
|
662
|
+
documentBody: string,
|
|
663
|
+
acceptanceCriteria: {
|
|
664
|
+
scenarios: Array<{ title: string; given: string; when: string; then: string }>;
|
|
665
|
+
falsifiability: Array<{ condition: string }>;
|
|
666
|
+
constraints: Array<{ text: string; checked: boolean }>;
|
|
667
|
+
},
|
|
668
|
+
): string {
|
|
669
|
+
const lines: string[] = [
|
|
670
|
+
`You are assessing design node "${nodeId}" (${nodeTitle}) for readiness to be marked as 'decided'.`,
|
|
671
|
+
"",
|
|
672
|
+
"## Document Body",
|
|
673
|
+
"",
|
|
674
|
+
documentBody,
|
|
675
|
+
"",
|
|
676
|
+
"## Assessment Task",
|
|
677
|
+
"",
|
|
678
|
+
"Evaluate each acceptance criterion below against the document body.",
|
|
679
|
+
"For each item, output a JSON object on its own line with exactly these fields:",
|
|
680
|
+
' {"type":"scenario"|"falsifiability"|"constraint","index":N,"pass":true|false,"finding":"<reason>"}',
|
|
681
|
+
"",
|
|
682
|
+
"Rules:",
|
|
683
|
+
"- pass=true if the document body addresses or satisfies the criterion",
|
|
684
|
+
"- pass=false if the criterion is unaddressed, contradicted, or underspecified",
|
|
685
|
+
"- finding must be a concrete, actionable sentence (not just 'yes' or 'no')",
|
|
686
|
+
"- Output ONLY the JSON lines, nothing else",
|
|
687
|
+
"",
|
|
688
|
+
];
|
|
689
|
+
|
|
690
|
+
if (acceptanceCriteria.scenarios.length > 0) {
|
|
691
|
+
lines.push("## Scenarios (Given/When/Then)");
|
|
692
|
+
lines.push("");
|
|
693
|
+
acceptanceCriteria.scenarios.forEach((s, i) => {
|
|
694
|
+
lines.push(`### Scenario ${i}: ${s.title}`);
|
|
695
|
+
lines.push(`- Given: ${s.given}`);
|
|
696
|
+
lines.push(`- When: ${s.when}`);
|
|
697
|
+
lines.push(`- Then: ${s.then}`);
|
|
698
|
+
lines.push("");
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
if (acceptanceCriteria.falsifiability.length > 0) {
|
|
703
|
+
lines.push("## Falsifiability Conditions");
|
|
704
|
+
lines.push("Each condition must be explicitly addressed, ruled out, or acknowledged as a known risk.");
|
|
705
|
+
lines.push("");
|
|
706
|
+
acceptanceCriteria.falsifiability.forEach((f, i) => {
|
|
707
|
+
lines.push(`${i}. ${f.condition}`);
|
|
708
|
+
});
|
|
709
|
+
lines.push("");
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (acceptanceCriteria.constraints.length > 0) {
|
|
713
|
+
lines.push("## Constraints");
|
|
714
|
+
lines.push("Each constraint must be satisfied by the document content.");
|
|
715
|
+
lines.push("");
|
|
716
|
+
acceptanceCriteria.constraints.forEach((c, i) => {
|
|
717
|
+
lines.push(`${i}. [${c.checked ? "x" : " "}] ${c.text}`);
|
|
718
|
+
});
|
|
719
|
+
lines.push("");
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
return lines.join("\n");
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Parse LLM output into DesignAssessmentFinding[].
|
|
727
|
+
* Each line should be a JSON object; non-JSON lines are skipped.
|
|
728
|
+
*/
|
|
729
|
+
export function parseDesignAssessmentFindings(llmOutput: string): DesignAssessmentFinding[] {
|
|
730
|
+
const findings: DesignAssessmentFinding[] = [];
|
|
731
|
+
for (const line of llmOutput.split("\n")) {
|
|
732
|
+
const trimmed = line.trim();
|
|
733
|
+
if (!trimmed.startsWith("{")) continue;
|
|
734
|
+
try {
|
|
735
|
+
const obj = JSON.parse(trimmed) as {
|
|
736
|
+
type: DesignFindingType;
|
|
737
|
+
index: number;
|
|
738
|
+
pass: boolean;
|
|
739
|
+
finding: string;
|
|
740
|
+
};
|
|
741
|
+
if (
|
|
742
|
+
typeof obj.type === "string" &&
|
|
743
|
+
typeof obj.index === "number" &&
|
|
744
|
+
typeof obj.pass === "boolean" &&
|
|
745
|
+
typeof obj.finding === "string"
|
|
746
|
+
) {
|
|
747
|
+
findings.push({ type: obj.type, index: obj.index, pass: obj.pass, finding: obj.finding });
|
|
748
|
+
}
|
|
749
|
+
} catch {
|
|
750
|
+
// skip malformed lines
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return findings;
|
|
754
|
+
}
|