@codewalla_india/openspec 1.3.1 → 1.3.3
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/LICENSE +22 -22
- package/README.md +214 -133
- package/bin/openspec.js +5 -5
- package/dist/cli/index.js +99 -62
- package/dist/commands/change.d.ts +0 -2
- package/dist/commands/change.js +107 -86
- package/dist/commands/config.js +14 -16
- package/dist/commands/doctor.js +9 -4
- package/dist/commands/feedback.js +79 -30
- package/dist/commands/modify.d.ts +26 -0
- package/dist/commands/modify.js +147 -0
- package/dist/commands/schema.js +170 -91
- package/dist/commands/show.js +5 -2
- package/dist/commands/spec.js +49 -29
- package/dist/commands/store.js +15 -21
- package/dist/commands/validate.d.ts +8 -0
- package/dist/commands/validate.js +43 -20
- package/dist/commands/workflow/index.d.ts +2 -2
- package/dist/commands/workflow/index.js +1 -1
- package/dist/commands/workflow/instructions.d.ts +6 -26
- package/dist/commands/workflow/instructions.js +183 -376
- package/dist/commands/workflow/new-change.d.ts +0 -4
- package/dist/commands/workflow/new-change.js +10 -28
- package/dist/commands/workflow/shared.d.ts +34 -19
- package/dist/commands/workflow/shared.js +34 -4
- package/dist/commands/workflow/status.js +10 -32
- package/dist/commands/workflow/templates.js +15 -5
- package/dist/commands/workset.d.ts +2 -2
- package/dist/commands/workset.js +19 -22
- package/dist/comprehension-quiz/fingerprint.d.ts +39 -0
- package/dist/comprehension-quiz/fingerprint.js +84 -0
- package/dist/comprehension-quiz/index.d.ts +42 -0
- package/dist/comprehension-quiz/index.js +47 -0
- package/dist/comprehension-quiz/pass-record.d.ts +44 -0
- package/dist/comprehension-quiz/pass-record.js +98 -0
- package/dist/comprehension-quiz/providers/anthropic.d.ts +35 -0
- package/dist/comprehension-quiz/providers/anthropic.js +69 -0
- package/dist/comprehension-quiz/providers/factory.d.ts +44 -0
- package/dist/comprehension-quiz/providers/factory.js +174 -0
- package/dist/comprehension-quiz/providers/interface.d.ts +98 -0
- package/dist/comprehension-quiz/providers/interface.js +150 -0
- package/dist/comprehension-quiz/providers/local.d.ts +35 -0
- package/dist/comprehension-quiz/providers/local.js +69 -0
- package/dist/comprehension-quiz/providers/ollama.d.ts +35 -0
- package/dist/comprehension-quiz/providers/ollama.js +69 -0
- package/dist/comprehension-quiz/providers/openai.d.ts +35 -0
- package/dist/comprehension-quiz/providers/openai.js +72 -0
- package/dist/comprehension-quiz/question-generator.d.ts +117 -0
- package/dist/comprehension-quiz/question-generator.js +203 -0
- package/dist/comprehension-quiz/quiz-executor.d.ts +75 -0
- package/dist/comprehension-quiz/quiz-executor.js +181 -0
- package/dist/comprehension-quiz/types.d.ts +137 -0
- package/dist/comprehension-quiz/types.js +11 -0
- package/dist/core/archive.d.ts +14 -1
- package/dist/core/archive.js +1350 -190
- package/dist/core/artifact-graph/graph.d.ts +28 -9
- package/dist/core/artifact-graph/graph.js +94 -43
- package/dist/core/artifact-graph/index.d.ts +1 -1
- package/dist/core/artifact-graph/index.js +1 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +30 -3
- package/dist/core/artifact-graph/instruction-loader.js +70 -12
- package/dist/core/artifact-graph/outputs.d.ts +1 -0
- package/dist/core/artifact-graph/outputs.js +67 -5
- package/dist/core/artifact-graph/resolver.d.ts +14 -0
- package/dist/core/artifact-graph/resolver.js +69 -15
- package/dist/core/artifact-graph/types.js +21 -3
- package/dist/core/available-tools.d.ts +2 -2
- package/dist/core/available-tools.js +14 -3
- package/dist/core/change-metadata/schema.d.ts +2 -0
- package/dist/core/change-metadata/schema.js +13 -0
- package/dist/core/change-status-policy.d.ts +10 -1
- package/dist/core/change-status-policy.js +30 -2
- package/dist/core/command-generation/adapters/amazon-q.d.ts +4 -0
- package/dist/core/command-generation/adapters/amazon-q.js +11 -5
- package/dist/core/command-generation/adapters/antigravity.js +6 -5
- package/dist/core/command-generation/adapters/auggie.js +7 -6
- package/dist/core/command-generation/adapters/bob.d.ts +5 -1
- package/dist/core/command-generation/adapters/bob.js +11 -10
- package/dist/core/command-generation/adapters/claude.d.ts +1 -1
- package/dist/core/command-generation/adapters/claude.js +12 -17
- package/dist/core/command-generation/adapters/cline.js +5 -5
- package/dist/core/command-generation/adapters/codebuddy.js +8 -7
- package/dist/core/command-generation/adapters/continue.js +8 -7
- package/dist/core/command-generation/adapters/costrict.js +7 -6
- package/dist/core/command-generation/adapters/crush.js +9 -9
- package/dist/core/command-generation/adapters/cursor.js +8 -8
- package/dist/core/command-generation/adapters/devin.d.ts +19 -0
- package/dist/core/command-generation/adapters/devin.js +36 -0
- package/dist/core/command-generation/adapters/factory.js +7 -6
- package/dist/core/command-generation/adapters/gemini.js +40 -5
- package/dist/core/command-generation/adapters/github-copilot.js +6 -5
- package/dist/core/command-generation/adapters/iflow.js +9 -8
- package/dist/core/command-generation/adapters/index.d.ts +4 -2
- package/dist/core/command-generation/adapters/index.js +4 -2
- package/dist/core/command-generation/adapters/junie.js +6 -5
- package/dist/core/command-generation/adapters/kilocode.js +1 -1
- package/dist/core/command-generation/adapters/kiro.js +6 -5
- package/dist/core/command-generation/adapters/lingma.js +9 -9
- package/dist/core/command-generation/adapters/oh-my-pi.d.ts +21 -0
- package/dist/core/command-generation/adapters/oh-my-pi.js +42 -0
- package/dist/core/command-generation/adapters/opencode.js +6 -8
- package/dist/core/command-generation/adapters/pi.d.ts +2 -2
- package/dist/core/command-generation/adapters/pi.js +7 -10
- package/dist/core/command-generation/adapters/qoder.js +9 -9
- package/dist/core/command-generation/adapters/qwen.d.ts +7 -3
- package/dist/core/command-generation/adapters/qwen.js +14 -9
- package/dist/core/command-generation/adapters/roocode.d.ts +4 -4
- package/dist/core/command-generation/adapters/roocode.js +9 -9
- package/dist/core/command-generation/adapters/trae.d.ts +13 -0
- package/dist/core/command-generation/adapters/trae.js +28 -0
- package/dist/core/command-generation/adapters/zcode.d.ts +16 -0
- package/dist/core/command-generation/adapters/zcode.js +33 -0
- package/dist/core/command-generation/generator.d.ts +8 -0
- package/dist/core/command-generation/generator.js +15 -1
- package/dist/core/command-generation/index.d.ts +1 -1
- package/dist/core/command-generation/index.js +1 -1
- package/dist/core/command-generation/invocation.d.ts +73 -0
- package/dist/core/command-generation/invocation.js +77 -0
- package/dist/core/command-generation/registry.js +8 -4
- package/dist/core/command-generation/types.d.ts +9 -2
- package/dist/core/command-generation/yaml.d.ts +20 -6
- package/dist/core/command-generation/yaml.js +31 -21
- package/dist/core/command-surface.d.ts +15 -0
- package/dist/core/command-surface.js +33 -0
- package/dist/core/completions/command-registry.js +16 -61
- package/dist/core/completions/generators/bash-generator.js +41 -41
- package/dist/core/completions/generators/fish-generator.js +7 -7
- package/dist/core/completions/generators/powershell-generator.js +44 -37
- package/dist/core/completions/generators/zsh-generator.js +33 -33
- package/dist/core/completions/installers/zsh-installer.d.ts +9 -0
- package/dist/core/completions/installers/zsh-installer.js +20 -4
- package/dist/core/completions/templates/bash-templates.js +24 -24
- package/dist/core/completions/templates/fish-templates.js +38 -38
- package/dist/core/completions/templates/powershell-templates.js +28 -28
- package/dist/core/completions/templates/zsh-templates.js +39 -39
- package/dist/core/config-prompts.d.ts +1 -1
- package/dist/core/config-prompts.js +14 -1
- package/dist/core/config-schema.d.ts +10 -1
- package/dist/core/config-schema.js +67 -1
- package/dist/core/config.d.ts +15 -0
- package/dist/core/config.js +46 -5
- package/dist/core/file-state.js +56 -24
- package/dist/core/github-copilot/cloud-agent.d.ts +95 -0
- package/dist/core/github-copilot/cloud-agent.js +497 -0
- package/dist/core/global-config.d.ts +16 -0
- package/dist/core/init.d.ts +58 -0
- package/dist/core/init.js +474 -73
- package/dist/core/legacy-cleanup.d.ts +59 -0
- package/dist/core/legacy-cleanup.js +253 -24
- package/dist/core/list.js +23 -16
- package/dist/core/migration.d.ts +73 -1
- package/dist/core/migration.js +360 -11
- package/dist/core/modify/conflict-detector.d.ts +26 -0
- package/dist/core/modify/conflict-detector.js +62 -0
- package/dist/core/modify/dependency-propagator.d.ts +38 -0
- package/dist/core/modify/dependency-propagator.js +103 -0
- package/dist/core/modify/history-tracker.d.ts +34 -0
- package/dist/core/modify/history-tracker.js +114 -0
- package/dist/core/modify/index.d.ts +7 -0
- package/dist/core/modify/index.js +7 -0
- package/dist/core/onboarding-commands.d.ts +30 -0
- package/dist/core/onboarding-commands.js +38 -0
- package/dist/core/openspec-root.js +18 -15
- package/dist/core/parsers/change-parser.d.ts +17 -2
- package/dist/core/parsers/change-parser.js +35 -21
- package/dist/core/parsers/code-fence.d.ts +15 -0
- package/dist/core/parsers/code-fence.js +49 -0
- package/dist/core/parsers/markdown-parser.d.ts +0 -3
- package/dist/core/parsers/markdown-parser.js +7 -60
- package/dist/core/parsers/requirement-blocks.d.ts +29 -0
- package/dist/core/parsers/requirement-blocks.js +150 -31
- package/dist/core/parsers/requirement-text.d.ts +48 -0
- package/dist/core/parsers/requirement-text.js +98 -0
- package/dist/core/parsers/spec-structure.d.ts +1 -1
- package/dist/core/parsers/spec-structure.js +20 -30
- package/dist/core/profile-sync-drift.d.ts +0 -8
- package/dist/core/profile-sync-drift.js +49 -50
- package/dist/core/profiles.d.ts +2 -2
- package/dist/core/profiles.js +2 -2
- package/dist/core/project-config.d.ts +27 -18
- package/dist/core/project-config.js +101 -34
- package/dist/core/references.js +42 -4
- package/dist/core/relationship-health.d.ts +8 -0
- package/dist/core/relationship-health.js +13 -0
- package/dist/core/root-selection.d.ts +9 -6
- package/dist/core/root-selection.js +42 -5
- package/dist/core/schemas/base.schema.js +7 -2
- package/dist/core/shared/allowed-tools.d.ts +12 -0
- package/dist/core/shared/allowed-tools.js +12 -0
- package/dist/core/shared/index.d.ts +1 -0
- package/dist/core/shared/index.js +1 -0
- package/dist/core/shared/skill-content-equivalence.d.ts +7 -0
- package/dist/core/shared/skill-content-equivalence.js +41 -0
- package/dist/core/shared/skill-generation.js +17 -13
- package/dist/core/shared/skill-paths.d.ts +13 -0
- package/dist/core/shared/skill-paths.js +23 -0
- package/dist/core/shared/tool-detection.d.ts +27 -7
- package/dist/core/shared/tool-detection.js +167 -37
- package/dist/core/shared-skill-target.d.ts +16 -0
- package/dist/core/shared-skill-target.js +156 -0
- package/dist/core/specs-apply.d.ts +80 -33
- package/dist/core/specs-apply.js +667 -145
- package/dist/core/store/git.d.ts +13 -0
- package/dist/core/store/git.js +24 -0
- package/dist/core/store/operations.js +20 -0
- package/dist/core/templates/skill-templates.d.ts +3 -2
- package/dist/core/templates/skill-templates.js +3 -2
- package/dist/core/templates/workflows/apply-change.d.ts +10 -0
- package/dist/core/templates/workflows/apply-change.js +186 -224
- package/dist/core/templates/workflows/archive-change.js +387 -260
- package/dist/core/templates/workflows/bulk-archive-change.js +655 -475
- package/dist/core/templates/workflows/continue-change.js +210 -219
- package/dist/core/templates/workflows/explore.js +479 -443
- package/dist/core/templates/workflows/feedback.js +97 -97
- package/dist/core/templates/workflows/ff-change.js +200 -191
- package/dist/core/templates/workflows/modify-change.d.ts +4 -1
- package/dist/core/templates/workflows/modify-change.js +193 -71
- package/dist/core/templates/workflows/new-change.js +127 -134
- package/dist/core/templates/workflows/onboard.js +548 -542
- package/dist/core/templates/workflows/propose.js +273 -217
- package/dist/core/templates/workflows/store-selection.d.ts +1 -1
- package/dist/core/templates/workflows/store-selection.js +1 -1
- package/dist/core/templates/workflows/sync-specs.js +500 -273
- package/dist/core/templates/workflows/update-change.d.ts +10 -0
- package/dist/core/templates/workflows/update-change.js +177 -0
- package/dist/core/templates/workflows/verify-change.js +324 -326
- package/dist/core/update.d.ts +40 -5
- package/dist/core/update.js +411 -96
- package/dist/core/validation/constants.d.ts +4 -1
- package/dist/core/validation/constants.js +4 -1
- package/dist/core/validation/plan-validator.d.ts +8 -0
- package/dist/core/validation/plan-validator.js +45 -0
- package/dist/core/validation/task-numbering.d.ts +16 -0
- package/dist/core/validation/task-numbering.js +57 -0
- package/dist/core/validation/validator.d.ts +27 -3
- package/dist/core/validation/validator.js +337 -48
- package/dist/core/version-check.d.ts +152 -0
- package/dist/core/version-check.js +731 -0
- package/dist/core/view.js +13 -18
- package/dist/prompts/searchable-multi-select.js +1 -1
- package/dist/telemetry/caller-detection.d.ts +27 -0
- package/dist/telemetry/caller-detection.js +85 -0
- package/dist/telemetry/caller-detection.test.d.ts +5 -0
- package/dist/telemetry/config.d.ts +48 -2
- package/dist/telemetry/config.js +113 -11
- package/dist/telemetry/config.test.d.ts +5 -0
- package/dist/telemetry/deduplication.d.ts +35 -0
- package/dist/telemetry/deduplication.js +82 -0
- package/dist/telemetry/deduplication.test.d.ts +5 -0
- package/dist/telemetry/identity.d.ts +62 -22
- package/dist/telemetry/identity.js +147 -102
- package/dist/telemetry/index.d.ts +126 -15
- package/dist/telemetry/index.js +569 -37
- package/dist/telemetry/sanitization.d.ts +40 -0
- package/dist/telemetry/sanitization.js +114 -0
- package/dist/telemetry/sanitization.test.d.ts +5 -0
- package/dist/telemetry/throttling.d.ts +20 -0
- package/dist/telemetry/throttling.js +70 -0
- package/dist/ui/welcome-screen.d.ts +9 -1
- package/dist/ui/welcome-screen.js +91 -38
- package/dist/utils/change-metadata.d.ts +43 -0
- package/dist/utils/change-metadata.js +123 -7
- package/dist/utils/change-utils.d.ts +10 -4
- package/dist/utils/change-utils.js +20 -13
- package/dist/utils/ci.d.ts +12 -0
- package/dist/utils/ci.js +16 -0
- package/dist/utils/command-references.d.ts +92 -6
- package/dist/utils/command-references.js +180 -7
- package/dist/utils/date.d.ts +8 -0
- package/dist/utils/date.js +13 -0
- package/dist/utils/file-system.d.ts +10 -0
- package/dist/utils/file-system.js +70 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/interactive.d.ts +23 -0
- package/dist/utils/interactive.js +32 -0
- package/dist/utils/item-discovery.d.ts +19 -0
- package/dist/utils/item-discovery.js +30 -48
- package/dist/utils/shell-detection.d.ts +1 -1
- package/dist/utils/shell-detection.js +64 -11
- package/dist/utils/spec-discovery.d.ts +40 -0
- package/dist/utils/spec-discovery.js +115 -0
- package/dist/utils/task-progress.d.ts +29 -1
- package/dist/utils/task-progress.js +109 -15
- package/package.json +96 -84
- package/schemas/spec-driven/schema.yaml +252 -184
- package/schemas/spec-driven/templates/design.md +19 -19
- package/schemas/spec-driven/templates/plan-template.md +91 -0
- package/schemas/spec-driven/templates/proposal.md +29 -24
- package/schemas/spec-driven/templates/spec.md +11 -8
- package/schemas/spec-driven/templates/tasks.md +9 -9
- package/scripts/postinstall.js +83 -83
- package/dist/core/command-generation/adapters/codex.d.ts +0 -16
- package/dist/core/command-generation/adapters/codex.js +0 -39
- package/dist/core/command-generation/adapters/windsurf.d.ts +0 -14
- package/dist/core/command-generation/adapters/windsurf.js +0 -38
- package/dist/core/comprehension/config.d.ts +0 -20
- package/dist/core/comprehension/config.js +0 -23
- package/dist/core/comprehension/fingerprint.d.ts +0 -14
- package/dist/core/comprehension/fingerprint.js +0 -33
- package/dist/core/comprehension/index.d.ts +0 -57
- package/dist/core/comprehension/index.js +0 -87
- package/dist/core/comprehension/pass-record.d.ts +0 -29
- package/dist/core/comprehension/pass-record.js +0 -64
- package/dist/core/comprehension/stats.d.ts +0 -35
- package/dist/core/comprehension/stats.js +0 -133
- package/dist/core/templates/workflows/comprehension-guidance.d.ts +0 -9
- package/dist/core/templates/workflows/comprehension-guidance.js +0 -73
- package/dist/core/templates/workflows/mcp-guidance.d.ts +0 -13
- package/dist/core/templates/workflows/mcp-guidance.js +0 -131
- package/dist/core/templates/workflows/user-prompt-guidance.d.ts +0 -16
- package/dist/core/templates/workflows/user-prompt-guidance.js +0 -48
- package/dist/telemetry/caller.d.ts +0 -5
- package/dist/telemetry/caller.js +0 -29
- package/dist/telemetry/client.d.ts +0 -27
- package/dist/telemetry/client.js +0 -127
- package/dist/telemetry/command-context.d.ts +0 -13
- package/dist/telemetry/command-context.js +0 -59
- package/dist/telemetry/comprehension.d.ts +0 -44
- package/dist/telemetry/comprehension.js +0 -105
- package/dist/telemetry/content.d.ts +0 -10
- package/dist/telemetry/content.js +0 -56
- package/dist/telemetry/git-stats.d.ts +0 -12
- package/dist/telemetry/git-stats.js +0 -69
- package/dist/telemetry/identify-cache.d.ts +0 -7
- package/dist/telemetry/identify-cache.js +0 -47
- package/dist/telemetry/input.d.ts +0 -17
- package/dist/telemetry/input.js +0 -68
- package/dist/telemetry/marker.d.ts +0 -37
- package/dist/telemetry/marker.js +0 -67
- package/dist/telemetry/workflow.d.ts +0 -85
- package/dist/telemetry/workflow.js +0 -318
- package/schemas/spec-driven/templates/plan.md +0 -19
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Question generator for quiz questions
|
|
3
|
+
*
|
|
4
|
+
* This module handles reading artifact content and generating quiz questions
|
|
5
|
+
* using LLM providers. It includes logic for question count scaling based on
|
|
6
|
+
* artifact size and proportional question allocation across artifact types.
|
|
7
|
+
*
|
|
8
|
+
* @module comprehension-quiz/question-generator
|
|
9
|
+
*/
|
|
10
|
+
import { createProviderWithFallback } from './providers/factory.js';
|
|
11
|
+
/**
|
|
12
|
+
* Read artifact content from file
|
|
13
|
+
*
|
|
14
|
+
* @param filePath - Path to the artifact file
|
|
15
|
+
* @returns File content as string
|
|
16
|
+
*/
|
|
17
|
+
async function readArtifactContent(filePath) {
|
|
18
|
+
const { promises: fs } = await import('fs');
|
|
19
|
+
try {
|
|
20
|
+
return await fs.readFile(filePath, 'utf-8');
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
throw new Error(`Failed to read artifact file ${filePath}: ${error}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Determine question count based on artifact size
|
|
28
|
+
*
|
|
29
|
+
* @param totalSize - Total size of all artifacts in bytes
|
|
30
|
+
* @returns Number of questions to generate (5, 7, or 10)
|
|
31
|
+
*/
|
|
32
|
+
export function getQuestionCount(totalSize) {
|
|
33
|
+
if (totalSize < 2048) {
|
|
34
|
+
return 5; // < 2KB
|
|
35
|
+
}
|
|
36
|
+
else if (totalSize < 10240) {
|
|
37
|
+
return 7; // 2-10KB
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
return 10; // > 10KB
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Calculate question allocation per artifact type
|
|
45
|
+
*
|
|
46
|
+
* Distributes questions proportionally to artifact size, ensuring at least
|
|
47
|
+
* one question per artifact type if it has content.
|
|
48
|
+
*
|
|
49
|
+
* @param totalQuestions - Total number of questions to generate
|
|
50
|
+
* @param artifactSizes - Size of each artifact type in bytes
|
|
51
|
+
* @returns Object with question count per artifact type
|
|
52
|
+
*/
|
|
53
|
+
export function calculateQuestionAllocation(totalQuestions, artifactSizes) {
|
|
54
|
+
const totalSize = Object.values(artifactSizes).reduce((sum, size) => sum + size, 0);
|
|
55
|
+
const allocation = {
|
|
56
|
+
proposal: 0,
|
|
57
|
+
design: 0,
|
|
58
|
+
specs: 0,
|
|
59
|
+
plan: 0,
|
|
60
|
+
tasks: 0,
|
|
61
|
+
};
|
|
62
|
+
// Allocate questions proportionally to artifact size
|
|
63
|
+
let allocated = 0;
|
|
64
|
+
for (const [artifactType, size] of Object.entries(artifactSizes)) {
|
|
65
|
+
const proportion = size / totalSize;
|
|
66
|
+
const count = Math.round(proportion * totalQuestions);
|
|
67
|
+
allocation[artifactType] = count;
|
|
68
|
+
allocated += count;
|
|
69
|
+
}
|
|
70
|
+
// Ensure at least 1 question per artifact type if it has content
|
|
71
|
+
for (const [artifactType, size] of Object.entries(artifactSizes)) {
|
|
72
|
+
if (size > 0 && allocation[artifactType] === 0 && allocated < totalQuestions) {
|
|
73
|
+
allocation[artifactType] = 1;
|
|
74
|
+
allocated += 1;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Adjust to match totalQuestions exactly
|
|
78
|
+
const currentTotal = Object.values(allocation).reduce((sum, count) => sum + count, 0);
|
|
79
|
+
if (currentTotal !== totalQuestions) {
|
|
80
|
+
const diff = totalQuestions - currentTotal;
|
|
81
|
+
// Add remaining questions to the largest artifact
|
|
82
|
+
const largestArtifact = Object.entries(artifactSizes).sort((a, b) => b[1] - a[1])[0][0];
|
|
83
|
+
allocation[largestArtifact] += diff;
|
|
84
|
+
}
|
|
85
|
+
return allocation;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Generate questions for a specific artifact type
|
|
89
|
+
*
|
|
90
|
+
* @param artifactContent - Content of the artifact
|
|
91
|
+
* @param artifactType - Type of artifact
|
|
92
|
+
* @param questionCount - Number of questions to generate
|
|
93
|
+
* @param provider - LLM provider to use
|
|
94
|
+
* @returns Array of generated questions
|
|
95
|
+
*/
|
|
96
|
+
export async function generateQuestionsForArtifact(artifactContent, artifactType, questionCount, provider) {
|
|
97
|
+
return provider.generateQuestions(artifactContent, questionCount, artifactType);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* On-demand question stream for the quiz executor.
|
|
101
|
+
*
|
|
102
|
+
* This class resolves the artifact contents and allocation plan upfront, but
|
|
103
|
+
* NEVER pre-generates questions. Each call to `next()` fetches exactly ONE
|
|
104
|
+
* question from the LLM — only after the previous question has been answered.
|
|
105
|
+
*
|
|
106
|
+
* This enforces FR-10: questions are fetched on-demand, never pre-generated.
|
|
107
|
+
* The correct answer (correctOption) is held in process memory; it is never
|
|
108
|
+
* written to stdout, disk, or any external surface.
|
|
109
|
+
*/
|
|
110
|
+
export class QuizQuestionStream {
|
|
111
|
+
provider;
|
|
112
|
+
plan = [];
|
|
113
|
+
totalQuestions = 0;
|
|
114
|
+
fetchedCount = 0;
|
|
115
|
+
askedTexts = [];
|
|
116
|
+
constructor(provider) {
|
|
117
|
+
this.provider = provider;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Create and initialise a QuizQuestionStream.
|
|
121
|
+
* Reads artifact files and builds the question plan, but does NOT generate any questions.
|
|
122
|
+
*
|
|
123
|
+
* @param artifactPaths - Object mapping artifact types to file paths
|
|
124
|
+
* @param totalQuestions - Total number of questions the quiz will ask
|
|
125
|
+
* @param provider - LLM provider (optional, uses fallback if not provided)
|
|
126
|
+
*/
|
|
127
|
+
static async create(artifactPaths, totalQuestions, provider) {
|
|
128
|
+
const llmProvider = provider ?? (await createProviderWithFallback());
|
|
129
|
+
const stream = new QuizQuestionStream(llmProvider);
|
|
130
|
+
// Read artifact contents
|
|
131
|
+
const artifactContents = {};
|
|
132
|
+
const artifactSizes = { proposal: 0, design: 0, specs: 0, plan: 0, tasks: 0 };
|
|
133
|
+
for (const [artifactType, filePath] of Object.entries(artifactPaths)) {
|
|
134
|
+
if (filePath) {
|
|
135
|
+
const content = await readArtifactContent(filePath);
|
|
136
|
+
artifactContents[artifactType] = content;
|
|
137
|
+
artifactSizes[artifactType] = content.length;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Build flat ordered plan: [artifactType, content] pairs for each question slot
|
|
141
|
+
const allocation = calculateQuestionAllocation(totalQuestions, artifactSizes);
|
|
142
|
+
for (const [artifactType, count] of Object.entries(allocation)) {
|
|
143
|
+
if (count > 0 && artifactContents[artifactType]) {
|
|
144
|
+
for (let i = 0; i < count; i++) {
|
|
145
|
+
stream.plan.push({
|
|
146
|
+
artifactType: artifactType,
|
|
147
|
+
content: artifactContents[artifactType],
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
stream.totalQuestions = stream.plan.length;
|
|
153
|
+
return stream;
|
|
154
|
+
}
|
|
155
|
+
/** Total number of questions this quiz will ask. */
|
|
156
|
+
get total() {
|
|
157
|
+
return this.totalQuestions;
|
|
158
|
+
}
|
|
159
|
+
/** Whether there are more questions to fetch. */
|
|
160
|
+
get hasNext() {
|
|
161
|
+
return this.fetchedCount < this.totalQuestions;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Fetch the next question from the LLM.
|
|
165
|
+
* MUST only be called after the user has answered the current question.
|
|
166
|
+
* Only one question is held in memory at a time; this method discards the
|
|
167
|
+
* previous question before fetching the next.
|
|
168
|
+
*
|
|
169
|
+
* @returns The next Question, or null if all questions have been asked.
|
|
170
|
+
*/
|
|
171
|
+
async next() {
|
|
172
|
+
if (!this.hasNext)
|
|
173
|
+
return null;
|
|
174
|
+
const slot = this.plan[this.fetchedCount];
|
|
175
|
+
const question = await this.provider.generateSingleQuestion(slot.content, slot.artifactType, this.fetchedCount, this.totalQuestions, this.askedTexts);
|
|
176
|
+
this.fetchedCount += 1;
|
|
177
|
+
this.askedTexts.push(question.text);
|
|
178
|
+
return question;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Generate complete quiz question set for all artifacts.
|
|
183
|
+
*
|
|
184
|
+
* NOTE: This function pre-generates all questions and should only be used for
|
|
185
|
+
* non-interactive contexts (e.g. testing, pass recording). During live quiz
|
|
186
|
+
* execution use QuizQuestionStream.create() instead.
|
|
187
|
+
*
|
|
188
|
+
* @param artifactPaths - Object mapping artifact types to file paths
|
|
189
|
+
* @param totalQuestions - Total number of questions to generate
|
|
190
|
+
* @param provider - LLM provider to use (optional, uses fallback if not provided)
|
|
191
|
+
* @returns Array of all generated questions
|
|
192
|
+
*/
|
|
193
|
+
export async function generateQuizQuestions(artifactPaths, totalQuestions, provider) {
|
|
194
|
+
const stream = await QuizQuestionStream.create(artifactPaths, totalQuestions, provider);
|
|
195
|
+
const allQuestions = [];
|
|
196
|
+
while (stream.hasNext) {
|
|
197
|
+
const q = await stream.next();
|
|
198
|
+
if (q)
|
|
199
|
+
allQuestions.push(q);
|
|
200
|
+
}
|
|
201
|
+
return allQuestions;
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=question-generator.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quiz executor with state machine and CLI interaction
|
|
3
|
+
*
|
|
4
|
+
* This module implements the quiz execution engine with a strict state machine
|
|
5
|
+
* and timing constraints to enforce human-only participation. It handles question
|
|
6
|
+
* presentation, answer collection, timing validation, and feedback display.
|
|
7
|
+
*
|
|
8
|
+
* @module comprehension-quiz/quiz-executor
|
|
9
|
+
*/
|
|
10
|
+
import type { QuizResult } from './types.js';
|
|
11
|
+
import type { QuizQuestionStream } from './question-generator.js';
|
|
12
|
+
/**
|
|
13
|
+
* Quiz executor class
|
|
14
|
+
*
|
|
15
|
+
* Manages the quiz state machine and CLI interaction. Enforces timing
|
|
16
|
+
* constraints (4-second wait before accepting input, 2-second minimum answer time)
|
|
17
|
+
* to prevent AI agents from programmatically answering questions.
|
|
18
|
+
*/
|
|
19
|
+
export declare class QuizExecutor {
|
|
20
|
+
private state;
|
|
21
|
+
private totalQuestions;
|
|
22
|
+
private correctCount;
|
|
23
|
+
private questionDisplayedAt;
|
|
24
|
+
private changeName;
|
|
25
|
+
private attemptNumber;
|
|
26
|
+
/** Minimum ms the question must be visible before ANY input is accepted (FR-4, NFR-4). */
|
|
27
|
+
private static readonly DISPLAY_LOCK_MS;
|
|
28
|
+
/** Minimum ms between question display and a valid answer (FR-9). */
|
|
29
|
+
private static readonly MIN_ANSWER_MS;
|
|
30
|
+
/**
|
|
31
|
+
* Execute quiz using an on-demand question stream.
|
|
32
|
+
*
|
|
33
|
+
* Questions are fetched one at a time from the stream — the next question is
|
|
34
|
+
* NOT fetched until the user has answered the current one. This means at most
|
|
35
|
+
* one Question object is in memory at any point, and its correctOption never
|
|
36
|
+
* appears in stdout.
|
|
37
|
+
*
|
|
38
|
+
* @param stream - QuizQuestionStream supplying one question per call
|
|
39
|
+
* @param changeName - Name of the change being tested
|
|
40
|
+
* @returns Quiz result with score and pass/fail status
|
|
41
|
+
*/
|
|
42
|
+
executeQuiz(stream: QuizQuestionStream, changeName: string): Promise<QuizResult>;
|
|
43
|
+
private transitionTo;
|
|
44
|
+
/**
|
|
45
|
+
* Display a question to the user.
|
|
46
|
+
* Records the display timestamp for timing enforcement.
|
|
47
|
+
* correctOption is NOT printed — it stays in process memory only.
|
|
48
|
+
*/
|
|
49
|
+
private displayQuestion;
|
|
50
|
+
/**
|
|
51
|
+
* Collect a valid answer from stdin, looping until one is received.
|
|
52
|
+
*
|
|
53
|
+
* Enforces:
|
|
54
|
+
* - 4-second display lock: any input before 4s is silently discarded (FR-4 / NFR-4)
|
|
55
|
+
* - Batch input rejection: multiple newline-separated values in one write → re-prompt
|
|
56
|
+
* - Skip/show-all rejection: any non-option text → "Answer this question to continue."
|
|
57
|
+
* - 2-second minimum read time: answers faster than 2s after display → re-prompt (FR-9)
|
|
58
|
+
*
|
|
59
|
+
* @returns The user's selected option number (1, 2, or 3)
|
|
60
|
+
*/
|
|
61
|
+
private collectValidAnswer;
|
|
62
|
+
/**
|
|
63
|
+
* Read a single line from stdin without echoing the prompt.
|
|
64
|
+
* Uses raw mode to capture the full write in one shot so batch detection works.
|
|
65
|
+
*/
|
|
66
|
+
private readLine;
|
|
67
|
+
/**
|
|
68
|
+
* Show pass/fail feedback for a single answer.
|
|
69
|
+
* Does NOT reveal the correctOption number to stdout if the user was correct —
|
|
70
|
+
* only shows "Correct" or "Incorrect" with the correct option disclosed on wrong answers.
|
|
71
|
+
*/
|
|
72
|
+
private showFeedback;
|
|
73
|
+
private sleep;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=quiz-executor.d.ts.map
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quiz executor with state machine and CLI interaction
|
|
3
|
+
*
|
|
4
|
+
* This module implements the quiz execution engine with a strict state machine
|
|
5
|
+
* and timing constraints to enforce human-only participation. It handles question
|
|
6
|
+
* presentation, answer collection, timing validation, and feedback display.
|
|
7
|
+
*
|
|
8
|
+
* @module comprehension-quiz/quiz-executor
|
|
9
|
+
*/
|
|
10
|
+
import * as readline from 'readline';
|
|
11
|
+
import { trackComprehensionAttempt, trackComprehensionCompletion, trackApplyReady } from '../telemetry/index.js';
|
|
12
|
+
import { getPassRecord } from './pass-record.js';
|
|
13
|
+
/**
|
|
14
|
+
* Quiz executor class
|
|
15
|
+
*
|
|
16
|
+
* Manages the quiz state machine and CLI interaction. Enforces timing
|
|
17
|
+
* constraints (4-second wait before accepting input, 2-second minimum answer time)
|
|
18
|
+
* to prevent AI agents from programmatically answering questions.
|
|
19
|
+
*/
|
|
20
|
+
export class QuizExecutor {
|
|
21
|
+
state = 'START';
|
|
22
|
+
totalQuestions = 0;
|
|
23
|
+
correctCount = 0;
|
|
24
|
+
questionDisplayedAt = 0;
|
|
25
|
+
changeName = '';
|
|
26
|
+
attemptNumber = 1;
|
|
27
|
+
/** Minimum ms the question must be visible before ANY input is accepted (FR-4, NFR-4). */
|
|
28
|
+
static DISPLAY_LOCK_MS = 4000;
|
|
29
|
+
/** Minimum ms between question display and a valid answer (FR-9). */
|
|
30
|
+
static MIN_ANSWER_MS = 2000;
|
|
31
|
+
/**
|
|
32
|
+
* Execute quiz using an on-demand question stream.
|
|
33
|
+
*
|
|
34
|
+
* Questions are fetched one at a time from the stream — the next question is
|
|
35
|
+
* NOT fetched until the user has answered the current one. This means at most
|
|
36
|
+
* one Question object is in memory at any point, and its correctOption never
|
|
37
|
+
* appears in stdout.
|
|
38
|
+
*
|
|
39
|
+
* @param stream - QuizQuestionStream supplying one question per call
|
|
40
|
+
* @param changeName - Name of the change being tested
|
|
41
|
+
* @returns Quiz result with score and pass/fail status
|
|
42
|
+
*/
|
|
43
|
+
async executeQuiz(stream, changeName) {
|
|
44
|
+
this.changeName = changeName;
|
|
45
|
+
this.totalQuestions = stream.total;
|
|
46
|
+
this.correctCount = 0;
|
|
47
|
+
this.state = 'START';
|
|
48
|
+
const existingRecord = await getPassRecord(changeName);
|
|
49
|
+
this.attemptNumber = existingRecord ? existingRecord.attemptCount + 1 : 1;
|
|
50
|
+
await trackComprehensionAttempt(changeName, this.attemptNumber);
|
|
51
|
+
let questionIndex = 0;
|
|
52
|
+
while (stream.hasNext) {
|
|
53
|
+
await this.transitionTo(`WAITING_FOR_QUESTION_${questionIndex + 1}`);
|
|
54
|
+
// Fetch exactly one question — only now, not before
|
|
55
|
+
const question = await stream.next();
|
|
56
|
+
if (!question)
|
|
57
|
+
break;
|
|
58
|
+
this.displayQuestion(question, questionIndex);
|
|
59
|
+
// Loop until a valid answer is received (re-prompt on any invalid/early/batch/skip input)
|
|
60
|
+
const userAnswer = await this.collectValidAnswer(question);
|
|
61
|
+
await this.transitionTo('ACCEPTING_ANSWER');
|
|
62
|
+
if (userAnswer === question.correctOption + 1) {
|
|
63
|
+
this.correctCount += 1;
|
|
64
|
+
}
|
|
65
|
+
await this.transitionTo('FEEDBACK');
|
|
66
|
+
this.showFeedback(question, userAnswer);
|
|
67
|
+
questionIndex += 1;
|
|
68
|
+
}
|
|
69
|
+
await this.transitionTo('DONE');
|
|
70
|
+
const scorePercent = this.totalQuestions > 0
|
|
71
|
+
? Math.round((this.correctCount / this.totalQuestions) * 100)
|
|
72
|
+
: 0;
|
|
73
|
+
const passed = scorePercent >= 80;
|
|
74
|
+
console.log(`\n${'─'.repeat(50)}`);
|
|
75
|
+
console.log(`Result: ${this.correctCount}/${this.totalQuestions} correct (${scorePercent}%)`);
|
|
76
|
+
console.log(passed ? '✓ PASS — apply is now unblocked.' : '✗ FAIL — retry to proceed.');
|
|
77
|
+
console.log('─'.repeat(50));
|
|
78
|
+
await trackComprehensionCompletion(changeName, scorePercent, passed ? 'pass' : 'fail', this.attemptNumber);
|
|
79
|
+
if (passed) {
|
|
80
|
+
await trackApplyReady(changeName);
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
correct: this.correctCount,
|
|
84
|
+
total: this.totalQuestions,
|
|
85
|
+
scorePercent,
|
|
86
|
+
passed,
|
|
87
|
+
attemptNumber: this.attemptNumber,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async transitionTo(newState) {
|
|
91
|
+
this.state = newState;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Display a question to the user.
|
|
95
|
+
* Records the display timestamp for timing enforcement.
|
|
96
|
+
* correctOption is NOT printed — it stays in process memory only.
|
|
97
|
+
*/
|
|
98
|
+
displayQuestion(question, index) {
|
|
99
|
+
console.clear();
|
|
100
|
+
console.log(`Question ${index + 1} of ${this.totalQuestions}\n`);
|
|
101
|
+
console.log(question.text);
|
|
102
|
+
console.log('');
|
|
103
|
+
console.log(` 1) ${question.options[0]}`);
|
|
104
|
+
console.log(` 2) ${question.options[1]}`);
|
|
105
|
+
console.log(` 3) ${question.options[2]}`);
|
|
106
|
+
console.log('');
|
|
107
|
+
this.questionDisplayedAt = Date.now();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Collect a valid answer from stdin, looping until one is received.
|
|
111
|
+
*
|
|
112
|
+
* Enforces:
|
|
113
|
+
* - 4-second display lock: any input before 4s is silently discarded (FR-4 / NFR-4)
|
|
114
|
+
* - Batch input rejection: multiple newline-separated values in one write → re-prompt
|
|
115
|
+
* - Skip/show-all rejection: any non-option text → "Answer this question to continue."
|
|
116
|
+
* - 2-second minimum read time: answers faster than 2s after display → re-prompt (FR-9)
|
|
117
|
+
*
|
|
118
|
+
* @returns The user's selected option number (1, 2, or 3)
|
|
119
|
+
*/
|
|
120
|
+
async collectValidAnswer(question) {
|
|
121
|
+
while (true) {
|
|
122
|
+
const elapsed = Date.now() - this.questionDisplayedAt;
|
|
123
|
+
if (elapsed < QuizExecutor.DISPLAY_LOCK_MS) {
|
|
124
|
+
// Still within the 4-second display lock — wait out the remainder silently
|
|
125
|
+
await this.sleep(QuizExecutor.DISPLAY_LOCK_MS - elapsed);
|
|
126
|
+
}
|
|
127
|
+
const raw = await this.readLine();
|
|
128
|
+
const answerReceivedAt = Date.now();
|
|
129
|
+
// Reject batch input: more than one non-empty line in a single write
|
|
130
|
+
if (raw.includes('\n') && raw.split('\n').filter((l) => l.trim()).length > 1) {
|
|
131
|
+
console.log('Answer this question to continue.');
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const trimmed = raw.trim();
|
|
135
|
+
// Reject empty, skip commands, show-all, or anything not "1" / "2" / "3"
|
|
136
|
+
if (trimmed === '' || !['1', '2', '3'].includes(trimmed)) {
|
|
137
|
+
console.log('Answer this question to continue.');
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
// Enforce minimum 2-second read time after question was displayed
|
|
141
|
+
const timeSinceDisplay = answerReceivedAt - this.questionDisplayedAt;
|
|
142
|
+
if (timeSinceDisplay < QuizExecutor.MIN_ANSWER_MS) {
|
|
143
|
+
console.log('Answer this question to continue.');
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
return parseInt(trimmed, 10);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Read a single line from stdin without echoing the prompt.
|
|
151
|
+
* Uses raw mode to capture the full write in one shot so batch detection works.
|
|
152
|
+
*/
|
|
153
|
+
readLine() {
|
|
154
|
+
return new Promise((resolve) => {
|
|
155
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
156
|
+
rl.question('Enter 1, 2, or 3: ', (answer) => {
|
|
157
|
+
rl.close();
|
|
158
|
+
resolve(answer);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Show pass/fail feedback for a single answer.
|
|
164
|
+
* Does NOT reveal the correctOption number to stdout if the user was correct —
|
|
165
|
+
* only shows "Correct" or "Incorrect" with the correct option disclosed on wrong answers.
|
|
166
|
+
*/
|
|
167
|
+
showFeedback(question, userAnswer) {
|
|
168
|
+
const isCorrect = userAnswer === question.correctOption + 1;
|
|
169
|
+
if (isCorrect) {
|
|
170
|
+
console.log('\n✓ Correct\n');
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
console.log('\n✗ Incorrect');
|
|
174
|
+
console.log(` The correct answer was option ${question.correctOption + 1}.\n`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
sleep(ms) {
|
|
178
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=quiz-executor.js.map
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the comprehension quiz system
|
|
3
|
+
*
|
|
4
|
+
* This module defines all TypeScript interfaces and types used across the
|
|
5
|
+
* comprehension quiz system, including questions, pass records, quiz states,
|
|
6
|
+
* and LLM provider interfaces.
|
|
7
|
+
*
|
|
8
|
+
* @module comprehension-quiz/types
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* A quiz question with multiple-choice options
|
|
12
|
+
*/
|
|
13
|
+
export interface Question {
|
|
14
|
+
/** The question text */
|
|
15
|
+
text: string;
|
|
16
|
+
/** Answer options (exactly 3 for this system) */
|
|
17
|
+
options: string[];
|
|
18
|
+
/** Index of the correct answer (0-2) */
|
|
19
|
+
correctOption: number;
|
|
20
|
+
/** Which artifact this question was generated from */
|
|
21
|
+
artifactSource: 'proposal' | 'design' | 'specs' | 'plan' | 'tasks';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A recorded quiz pass with metadata
|
|
25
|
+
*
|
|
26
|
+
* Pass records are stored in `.openspec/comprehension-quiz-passes.json` and
|
|
27
|
+
* include fingerprint information to detect when artifacts have changed
|
|
28
|
+
* since the quiz was passed.
|
|
29
|
+
*/
|
|
30
|
+
export interface PassRecord {
|
|
31
|
+
/** Change name this pass is for */
|
|
32
|
+
changeName: string;
|
|
33
|
+
/** ISO 8601 timestamp when quiz was passed */
|
|
34
|
+
passedAt: string;
|
|
35
|
+
/** Score percentage (0-100) */
|
|
36
|
+
score: number;
|
|
37
|
+
/** SHA-256 fingerprint of all artifact files at time of pass */
|
|
38
|
+
fingerprint: string;
|
|
39
|
+
/** Total number of quiz attempts for this change */
|
|
40
|
+
attemptCount: number;
|
|
41
|
+
/** Number of failed attempts before passing */
|
|
42
|
+
failureCount?: number;
|
|
43
|
+
/** Optional: LLM provider used */
|
|
44
|
+
provider?: string;
|
|
45
|
+
/** Optional: Model used */
|
|
46
|
+
model?: string;
|
|
47
|
+
/** Optional: Number of questions in the quiz */
|
|
48
|
+
questionCount?: number;
|
|
49
|
+
/** Optional: Duration of quiz in seconds */
|
|
50
|
+
duration?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Quiz state machine states
|
|
54
|
+
*
|
|
55
|
+
* The quiz executes through a strict state machine with timing constraints
|
|
56
|
+
* to enforce human-only participation. States include START, WAITING_FOR_QUESTION_N,
|
|
57
|
+
* ACCEPTING_ANSWER, FEEDBACK, and DONE.
|
|
58
|
+
*/
|
|
59
|
+
export type QuizState = 'START' | 'WAITING_FOR_QUESTION_1' | 'WAITING_FOR_QUESTION_2' | 'WAITING_FOR_QUESTION_3' | 'WAITING_FOR_QUESTION_4' | 'WAITING_FOR_QUESTION_5' | 'WAITING_FOR_QUESTION_6' | 'WAITING_FOR_QUESTION_7' | 'WAITING_FOR_QUESTION_8' | 'WAITING_FOR_QUESTION_9' | 'WAITING_FOR_QUESTION_10' | 'ACCEPTING_ANSWER' | 'FEEDBACK' | 'DONE';
|
|
60
|
+
/**
|
|
61
|
+
* LLM provider interface for question generation
|
|
62
|
+
*
|
|
63
|
+
* All LLM providers (OpenAI, Anthropic, Ollama, local) must implement
|
|
64
|
+
* this interface to enable question generation from artifact content.
|
|
65
|
+
*/
|
|
66
|
+
export interface LLMProvider {
|
|
67
|
+
/**
|
|
68
|
+
* Generate quiz questions from artifact content
|
|
69
|
+
* @param artifactContent - The content of the artifact to generate questions from
|
|
70
|
+
* @param questionCount - Number of questions to generate
|
|
71
|
+
* @param artifactType - Type of artifact (proposal, design, specs, plan, tasks)
|
|
72
|
+
* @returns Array of generated questions
|
|
73
|
+
*/
|
|
74
|
+
generateQuestions(artifactContent: string, questionCount: number, artifactType: 'proposal' | 'design' | 'specs' | 'plan' | 'tasks'): Promise<Question[]>;
|
|
75
|
+
/**
|
|
76
|
+
* Generate a single quiz question on-demand from artifact content.
|
|
77
|
+
*
|
|
78
|
+
* This method MUST be used during quiz execution to fetch one question at a time.
|
|
79
|
+
* The correct answer index is included in the returned Question object so the CLI
|
|
80
|
+
* process can validate the user's answer locally. The correct answer MUST NOT be
|
|
81
|
+
* included in any conversational context or stdout — it stays in process memory only.
|
|
82
|
+
*
|
|
83
|
+
* The LLM prompt used by implementors MUST:
|
|
84
|
+
* 1. Ask for exactly ONE question, not a list or array.
|
|
85
|
+
* 2. Return a JSON object with: text, options (3 strings), correctOption (0-2), artifactSource.
|
|
86
|
+
* 3. NEVER ask the LLM to explain the answer or include the answer text in any prose.
|
|
87
|
+
* 4. Use a system message that forbids the model from listing multiple questions.
|
|
88
|
+
*
|
|
89
|
+
* @param artifactContent - The content of the artifact to generate from
|
|
90
|
+
* @param artifactType - Type of artifact (proposal, design, specs, plan, tasks)
|
|
91
|
+
* @param questionIndex - 0-based index of this question in the quiz (for diversity)
|
|
92
|
+
* @param totalQuestions - Total number of questions in the quiz (for context)
|
|
93
|
+
* @param previousQuestionTexts - Texts of already-asked questions to avoid duplicates
|
|
94
|
+
* @returns A single generated question (text + options + correctOption + artifactSource)
|
|
95
|
+
*/
|
|
96
|
+
generateSingleQuestion(artifactContent: string, artifactType: 'proposal' | 'design' | 'specs' | 'plan' | 'tasks', questionIndex: number, totalQuestions: number, previousQuestionTexts: string[]): Promise<Question>;
|
|
97
|
+
/**
|
|
98
|
+
* Validate provider configuration
|
|
99
|
+
* @throws Error if configuration is invalid
|
|
100
|
+
*/
|
|
101
|
+
validateConfig(): void;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Provider configuration
|
|
105
|
+
*
|
|
106
|
+
* Configuration options for LLM providers, including API keys, model names,
|
|
107
|
+
* and endpoint URLs.
|
|
108
|
+
*/
|
|
109
|
+
export interface ProviderConfig {
|
|
110
|
+
/** API key for cloud providers */
|
|
111
|
+
apiKey?: string;
|
|
112
|
+
/** Model name to use */
|
|
113
|
+
model?: string;
|
|
114
|
+
/** Endpoint URL (for local providers) */
|
|
115
|
+
endpoint?: string;
|
|
116
|
+
/** Model type (for local providers) */
|
|
117
|
+
modelType?: string;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Quiz result from execution
|
|
121
|
+
*
|
|
122
|
+
* Contains the final score, pass/fail status, and attempt information
|
|
123
|
+
* after a quiz has been completed.
|
|
124
|
+
*/
|
|
125
|
+
export interface QuizResult {
|
|
126
|
+
/** Number of correct answers */
|
|
127
|
+
correct: number;
|
|
128
|
+
/** Total number of questions */
|
|
129
|
+
total: number;
|
|
130
|
+
/** Score percentage (0-100) */
|
|
131
|
+
scorePercent: number;
|
|
132
|
+
/** Whether the quiz was passed (≥80%) */
|
|
133
|
+
passed: boolean;
|
|
134
|
+
/** Attempt number */
|
|
135
|
+
attemptNumber: number;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the comprehension quiz system
|
|
3
|
+
*
|
|
4
|
+
* This module defines all TypeScript interfaces and types used across the
|
|
5
|
+
* comprehension quiz system, including questions, pass records, quiz states,
|
|
6
|
+
* and LLM provider interfaces.
|
|
7
|
+
*
|
|
8
|
+
* @module comprehension-quiz/types
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=types.js.map
|
package/dist/core/archive.d.ts
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when the ONLY thing wrong with a rebuilt spec is that it has no
|
|
3
|
+
* requirements. That is the exact failure retiring a capability replaces
|
|
4
|
+
* (#1302); anything else means the spec is broken in a way the author still has
|
|
5
|
+
* to fix, so archive must abort exactly as it always did instead of retiring.
|
|
6
|
+
*
|
|
7
|
+
* Asking the validator - rather than counting requirement blocks a second time -
|
|
8
|
+
* is what makes "this spec could not have been written anyway" true by
|
|
9
|
+
* construction. The two counts genuinely disagree: `MarkdownParser` accepts any
|
|
10
|
+
* `###` heading under `## Requirements` as a requirement, while the delta block
|
|
11
|
+
* parser only indexes canonical `### Requirement:` headers and sweeps the rest
|
|
12
|
+
* into the preamble, which survives into the rebuilt spec.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isRetirableSpec(specName: string, rebuilt: string): Promise<boolean>;
|
|
1
15
|
export interface ArchiveOptions {
|
|
2
16
|
yes?: boolean;
|
|
3
17
|
skipSpecs?: boolean;
|
|
@@ -17,6 +31,5 @@ export declare class ArchiveCommand {
|
|
|
17
31
|
*/
|
|
18
32
|
private run;
|
|
19
33
|
private selectChange;
|
|
20
|
-
private getArchiveDate;
|
|
21
34
|
}
|
|
22
35
|
//# sourceMappingURL=archive.d.ts.map
|