gsd-pi 2.18.0 → 2.20.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/README.md +5 -1
- package/dist/cli.js +3 -3
- package/dist/onboarding.d.ts +3 -1
- package/dist/onboarding.js +77 -3
- package/dist/remote-questions-config.d.ts +1 -1
- package/dist/resources/extensions/google-search/index.ts +164 -47
- package/dist/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +148 -39
- package/dist/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/dist/resources/extensions/gsd/auto.ts +690 -39
- package/dist/resources/extensions/gsd/captures.ts +384 -0
- package/dist/resources/extensions/gsd/commands.ts +654 -36
- package/dist/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/dist/resources/extensions/gsd/context-budget.ts +243 -0
- package/dist/resources/extensions/gsd/context-store.ts +195 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +51 -3
- package/dist/resources/extensions/gsd/db-writer.ts +341 -0
- package/dist/resources/extensions/gsd/debug-logger.ts +178 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/dist/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/dist/resources/extensions/gsd/doctor.ts +283 -2
- package/dist/resources/extensions/gsd/export.ts +81 -2
- package/dist/resources/extensions/gsd/files.ts +39 -9
- package/dist/resources/extensions/gsd/git-service.ts +6 -0
- package/dist/resources/extensions/gsd/gsd-db.ts +752 -0
- package/dist/resources/extensions/gsd/guided-flow.ts +26 -1
- package/dist/resources/extensions/gsd/history.ts +0 -1
- package/dist/resources/extensions/gsd/index.ts +277 -1
- package/dist/resources/extensions/gsd/md-importer.ts +526 -0
- package/dist/resources/extensions/gsd/metrics.ts +84 -0
- package/dist/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/dist/resources/extensions/gsd/model-router.ts +256 -0
- package/dist/resources/extensions/gsd/notifications.ts +0 -1
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +72 -2
- package/dist/resources/extensions/gsd/preferences.ts +198 -150
- package/dist/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/dist/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +48 -0
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/dist/resources/extensions/gsd/prompts/system.md +2 -1
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/dist/resources/extensions/gsd/quick.ts +156 -0
- package/dist/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/dist/resources/extensions/gsd/skill-health.ts +417 -0
- package/dist/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/dist/resources/extensions/gsd/state.ts +30 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +1 -0
- package/dist/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/dist/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/dist/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/dist/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/dist/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/dist/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/dist/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/dist/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/dist/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/dist/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/dist/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/dist/resources/extensions/gsd/tests/metrics.test.ts +197 -0
- package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/dist/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/dist/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/dist/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/dist/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/dist/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/dist/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/dist/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
- package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/dist/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/dist/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
- package/dist/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
- package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/dist/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
- package/dist/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/dist/resources/extensions/gsd/triage-ui.ts +175 -0
- package/dist/resources/extensions/gsd/types.ts +29 -0
- package/dist/resources/extensions/gsd/undo.ts +0 -1
- package/dist/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/dist/resources/extensions/gsd/visualizer-data.ts +505 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +337 -0
- package/dist/resources/extensions/gsd/visualizer-views.ts +755 -0
- package/dist/resources/extensions/gsd/worktree-command.ts +18 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/dist/resources/extensions/remote-questions/config.ts +4 -2
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +35 -4
- package/dist/resources/extensions/remote-questions/format.ts +166 -14
- package/dist/resources/extensions/remote-questions/manager.ts +14 -4
- package/dist/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/dist/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/dist/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/dist/resources/extensions/remote-questions/types.ts +2 -1
- package/dist/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/dist/resources/extensions/voice/index.ts +4 -3
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +12 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +25 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.js +106 -3
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.js +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.js +45 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +43 -11
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +7 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +13 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +26 -0
- package/packages/pi-coding-agent/src/core/lsp/index.ts +157 -2
- package/packages/pi-coding-agent/src/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/src/core/lsp/types.ts +53 -0
- package/packages/pi-coding-agent/src/core/lsp/utils.ts +56 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +41 -11
- package/packages/pi-coding-agent/src/core/system-prompt.ts +7 -1
- package/packages/pi-coding-agent/src/core/tools/edit.ts +3 -0
- package/packages/pi-coding-agent/src/core/tools/write.ts +3 -0
- package/src/resources/extensions/google-search/index.ts +164 -47
- package/src/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +148 -39
- package/src/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/src/resources/extensions/gsd/auto.ts +690 -39
- package/src/resources/extensions/gsd/captures.ts +384 -0
- package/src/resources/extensions/gsd/commands.ts +654 -36
- package/src/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/src/resources/extensions/gsd/context-budget.ts +243 -0
- package/src/resources/extensions/gsd/context-store.ts +195 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +51 -3
- package/src/resources/extensions/gsd/db-writer.ts +341 -0
- package/src/resources/extensions/gsd/debug-logger.ts +178 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/src/resources/extensions/gsd/doctor.ts +283 -2
- package/src/resources/extensions/gsd/export.ts +81 -2
- package/src/resources/extensions/gsd/files.ts +39 -9
- package/src/resources/extensions/gsd/git-service.ts +6 -0
- package/src/resources/extensions/gsd/gsd-db.ts +752 -0
- package/src/resources/extensions/gsd/guided-flow.ts +26 -1
- package/src/resources/extensions/gsd/history.ts +0 -1
- package/src/resources/extensions/gsd/index.ts +277 -1
- package/src/resources/extensions/gsd/md-importer.ts +526 -0
- package/src/resources/extensions/gsd/metrics.ts +84 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/src/resources/extensions/gsd/model-router.ts +256 -0
- package/src/resources/extensions/gsd/notifications.ts +0 -1
- package/src/resources/extensions/gsd/post-unit-hooks.ts +72 -2
- package/src/resources/extensions/gsd/preferences.ts +198 -150
- package/src/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/src/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +48 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/src/resources/extensions/gsd/prompts/system.md +2 -1
- package/src/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/src/resources/extensions/gsd/quick.ts +156 -0
- package/src/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/src/resources/extensions/gsd/skill-health.ts +417 -0
- package/src/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/src/resources/extensions/gsd/state.ts +30 -0
- package/src/resources/extensions/gsd/templates/preferences.md +1 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +197 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
- package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/src/resources/extensions/gsd/triage-ui.ts +175 -0
- package/src/resources/extensions/gsd/types.ts +29 -0
- package/src/resources/extensions/gsd/undo.ts +0 -1
- package/src/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +505 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +337 -0
- package/src/resources/extensions/gsd/visualizer-views.ts +755 -0
- package/src/resources/extensions/gsd/worktree-command.ts +18 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/src/resources/extensions/remote-questions/config.ts +4 -2
- package/src/resources/extensions/remote-questions/discord-adapter.ts +35 -4
- package/src/resources/extensions/remote-questions/format.ts +166 -14
- package/src/resources/extensions/remote-questions/manager.ts +14 -4
- package/src/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/src/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/src/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/src/resources/extensions/remote-questions/types.ts +2 -1
- package/src/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/src/resources/extensions/voice/index.ts +4 -3
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { createTestContext } from './test-helpers.ts';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import {
|
|
6
|
+
openDatabase,
|
|
7
|
+
closeDatabase,
|
|
8
|
+
isDbAvailable,
|
|
9
|
+
getDbProvider,
|
|
10
|
+
insertDecision,
|
|
11
|
+
getDecisionById,
|
|
12
|
+
insertRequirement,
|
|
13
|
+
getRequirementById,
|
|
14
|
+
getActiveDecisions,
|
|
15
|
+
getActiveRequirements,
|
|
16
|
+
transaction,
|
|
17
|
+
_getAdapter,
|
|
18
|
+
_resetProvider,
|
|
19
|
+
} from '../gsd-db.ts';
|
|
20
|
+
|
|
21
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
22
|
+
|
|
23
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
24
|
+
// Helper: create a temp file path for file-backed DB tests
|
|
25
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
26
|
+
|
|
27
|
+
function tempDbPath(): string {
|
|
28
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-db-test-'));
|
|
29
|
+
return path.join(dir, 'test.db');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function cleanup(dbPath: string): void {
|
|
33
|
+
closeDatabase();
|
|
34
|
+
try {
|
|
35
|
+
const dir = path.dirname(dbPath);
|
|
36
|
+
// Remove DB file and WAL/SHM files
|
|
37
|
+
for (const f of fs.readdirSync(dir)) {
|
|
38
|
+
fs.unlinkSync(path.join(dir, f));
|
|
39
|
+
}
|
|
40
|
+
fs.rmdirSync(dir);
|
|
41
|
+
} catch {
|
|
42
|
+
// best effort
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
47
|
+
// gsd-db tests
|
|
48
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
49
|
+
|
|
50
|
+
console.log('\n=== gsd-db: provider detection ===');
|
|
51
|
+
{
|
|
52
|
+
const provider = getDbProvider();
|
|
53
|
+
assertTrue(provider !== null, 'provider should be non-null');
|
|
54
|
+
assertTrue(
|
|
55
|
+
provider === 'node:sqlite' || provider === 'better-sqlite3',
|
|
56
|
+
`provider should be a known name, got: ${provider}`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log('\n=== gsd-db: fresh DB schema init (memory) ===');
|
|
61
|
+
{
|
|
62
|
+
const ok = openDatabase(':memory:');
|
|
63
|
+
assertTrue(ok, 'openDatabase should return true');
|
|
64
|
+
assertTrue(isDbAvailable(), 'isDbAvailable should be true after open');
|
|
65
|
+
|
|
66
|
+
// Check schema_version table
|
|
67
|
+
const adapter = _getAdapter()!;
|
|
68
|
+
const version = adapter.prepare('SELECT version FROM schema_version').get();
|
|
69
|
+
assertEq(version?.['version'], 2, 'schema version should be 2');
|
|
70
|
+
|
|
71
|
+
// Check tables exist by querying them
|
|
72
|
+
const dRows = adapter.prepare('SELECT count(*) as cnt FROM decisions').get();
|
|
73
|
+
assertEq(dRows?.['cnt'], 0, 'decisions table should exist and be empty');
|
|
74
|
+
|
|
75
|
+
const rRows = adapter.prepare('SELECT count(*) as cnt FROM requirements').get();
|
|
76
|
+
assertEq(rRows?.['cnt'], 0, 'requirements table should exist and be empty');
|
|
77
|
+
|
|
78
|
+
closeDatabase();
|
|
79
|
+
assertTrue(!isDbAvailable(), 'isDbAvailable should be false after close');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log('\n=== gsd-db: double-init idempotency ===');
|
|
83
|
+
{
|
|
84
|
+
const dbPath = tempDbPath();
|
|
85
|
+
openDatabase(dbPath);
|
|
86
|
+
|
|
87
|
+
// Insert a decision so we can verify it survives re-init
|
|
88
|
+
insertDecision({
|
|
89
|
+
id: 'D001',
|
|
90
|
+
when_context: 'test',
|
|
91
|
+
scope: 'global',
|
|
92
|
+
decision: 'test decision',
|
|
93
|
+
choice: 'option A',
|
|
94
|
+
rationale: 'because',
|
|
95
|
+
revisable: 'yes',
|
|
96
|
+
superseded_by: null,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
closeDatabase();
|
|
100
|
+
|
|
101
|
+
// Re-open same DB — schema init should be idempotent
|
|
102
|
+
openDatabase(dbPath);
|
|
103
|
+
const d = getDecisionById('D001');
|
|
104
|
+
assertTrue(d !== null, 'decision should survive re-init');
|
|
105
|
+
assertEq(d?.id, 'D001', 'decision ID preserved after re-init');
|
|
106
|
+
|
|
107
|
+
// Schema version should still be 1 (not duplicated)
|
|
108
|
+
const adapter = _getAdapter()!;
|
|
109
|
+
const versions = adapter.prepare('SELECT count(*) as cnt FROM schema_version').get();
|
|
110
|
+
assertEq(versions?.['cnt'], 1, 'schema_version should have exactly 1 row after double-init');
|
|
111
|
+
|
|
112
|
+
cleanup(dbPath);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log('\n=== gsd-db: insert + get decision ===');
|
|
116
|
+
{
|
|
117
|
+
openDatabase(':memory:');
|
|
118
|
+
insertDecision({
|
|
119
|
+
id: 'D042',
|
|
120
|
+
when_context: 'during sprint 3',
|
|
121
|
+
scope: 'M001/S02',
|
|
122
|
+
decision: 'use SQLite for storage',
|
|
123
|
+
choice: 'node:sqlite',
|
|
124
|
+
rationale: 'built-in, zero deps',
|
|
125
|
+
revisable: 'yes, if perf insufficient',
|
|
126
|
+
superseded_by: null,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const d = getDecisionById('D042');
|
|
130
|
+
assertTrue(d !== null, 'should find inserted decision');
|
|
131
|
+
assertEq(d?.id, 'D042', 'decision id');
|
|
132
|
+
assertEq(d?.scope, 'M001/S02', 'decision scope');
|
|
133
|
+
assertEq(d?.choice, 'node:sqlite', 'decision choice');
|
|
134
|
+
assertTrue(typeof d?.seq === 'number' && d.seq > 0, 'seq should be auto-assigned positive number');
|
|
135
|
+
assertEq(d?.superseded_by, null, 'superseded_by should be null');
|
|
136
|
+
|
|
137
|
+
// Non-existent
|
|
138
|
+
const missing = getDecisionById('D999');
|
|
139
|
+
assertEq(missing, null, 'non-existent decision returns null');
|
|
140
|
+
|
|
141
|
+
closeDatabase();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
console.log('\n=== gsd-db: insert + get requirement ===');
|
|
145
|
+
{
|
|
146
|
+
openDatabase(':memory:');
|
|
147
|
+
insertRequirement({
|
|
148
|
+
id: 'R007',
|
|
149
|
+
class: 'functional',
|
|
150
|
+
status: 'active',
|
|
151
|
+
description: 'System must persist decisions',
|
|
152
|
+
why: 'decisions inform future agents',
|
|
153
|
+
source: 'M001-CONTEXT',
|
|
154
|
+
primary_owner: 'S01',
|
|
155
|
+
supporting_slices: 'S02, S03',
|
|
156
|
+
validation: 'insert and query roundtrip',
|
|
157
|
+
notes: 'high priority',
|
|
158
|
+
full_content: 'Full text of requirement...',
|
|
159
|
+
superseded_by: null,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const r = getRequirementById('R007');
|
|
163
|
+
assertTrue(r !== null, 'should find inserted requirement');
|
|
164
|
+
assertEq(r?.id, 'R007', 'requirement id');
|
|
165
|
+
assertEq(r?.class, 'functional', 'requirement class');
|
|
166
|
+
assertEq(r?.status, 'active', 'requirement status');
|
|
167
|
+
assertEq(r?.primary_owner, 'S01', 'requirement primary_owner');
|
|
168
|
+
assertEq(r?.superseded_by, null, 'superseded_by should be null');
|
|
169
|
+
|
|
170
|
+
// Non-existent
|
|
171
|
+
const missing = getRequirementById('R999');
|
|
172
|
+
assertEq(missing, null, 'non-existent requirement returns null');
|
|
173
|
+
|
|
174
|
+
closeDatabase();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.log('\n=== gsd-db: active_decisions view excludes superseded ===');
|
|
178
|
+
{
|
|
179
|
+
openDatabase(':memory:');
|
|
180
|
+
|
|
181
|
+
insertDecision({
|
|
182
|
+
id: 'D001',
|
|
183
|
+
when_context: 'early',
|
|
184
|
+
scope: 'global',
|
|
185
|
+
decision: 'use JSON files',
|
|
186
|
+
choice: 'JSON',
|
|
187
|
+
rationale: 'simple',
|
|
188
|
+
revisable: 'yes',
|
|
189
|
+
superseded_by: 'D002', // superseded!
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
insertDecision({
|
|
193
|
+
id: 'D002',
|
|
194
|
+
when_context: 'later',
|
|
195
|
+
scope: 'global',
|
|
196
|
+
decision: 'use SQLite',
|
|
197
|
+
choice: 'SQLite',
|
|
198
|
+
rationale: 'better querying',
|
|
199
|
+
revisable: 'yes',
|
|
200
|
+
superseded_by: null, // active
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
insertDecision({
|
|
204
|
+
id: 'D003',
|
|
205
|
+
when_context: 'same time',
|
|
206
|
+
scope: 'local',
|
|
207
|
+
decision: 'use WAL mode',
|
|
208
|
+
choice: 'WAL',
|
|
209
|
+
rationale: 'concurrent reads',
|
|
210
|
+
revisable: 'no',
|
|
211
|
+
superseded_by: null, // active
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const active = getActiveDecisions();
|
|
215
|
+
assertEq(active.length, 2, 'active_decisions should return 2 (not the superseded one)');
|
|
216
|
+
const ids = active.map(d => d.id).sort();
|
|
217
|
+
assertEq(ids, ['D002', 'D003'], 'active decisions should be D002 and D003');
|
|
218
|
+
|
|
219
|
+
// Verify D001 is still in the raw table
|
|
220
|
+
const d1 = getDecisionById('D001');
|
|
221
|
+
assertTrue(d1 !== null, 'superseded decision still exists in raw table');
|
|
222
|
+
assertEq(d1?.superseded_by, 'D002', 'superseded_by is set');
|
|
223
|
+
|
|
224
|
+
closeDatabase();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
console.log('\n=== gsd-db: active_requirements view excludes superseded ===');
|
|
228
|
+
{
|
|
229
|
+
openDatabase(':memory:');
|
|
230
|
+
|
|
231
|
+
insertRequirement({
|
|
232
|
+
id: 'R001',
|
|
233
|
+
class: 'functional',
|
|
234
|
+
status: 'active',
|
|
235
|
+
description: 'old requirement',
|
|
236
|
+
why: 'was needed',
|
|
237
|
+
source: 'M001',
|
|
238
|
+
primary_owner: 'S01',
|
|
239
|
+
supporting_slices: '',
|
|
240
|
+
validation: 'test',
|
|
241
|
+
notes: '',
|
|
242
|
+
full_content: '',
|
|
243
|
+
superseded_by: 'R002', // superseded!
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
insertRequirement({
|
|
247
|
+
id: 'R002',
|
|
248
|
+
class: 'functional',
|
|
249
|
+
status: 'active',
|
|
250
|
+
description: 'new requirement',
|
|
251
|
+
why: 'replaces R001',
|
|
252
|
+
source: 'M001',
|
|
253
|
+
primary_owner: 'S01',
|
|
254
|
+
supporting_slices: '',
|
|
255
|
+
validation: 'test',
|
|
256
|
+
notes: '',
|
|
257
|
+
full_content: '',
|
|
258
|
+
superseded_by: null, // active
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const active = getActiveRequirements();
|
|
262
|
+
assertEq(active.length, 1, 'active_requirements should return 1');
|
|
263
|
+
assertEq(active[0]?.id, 'R002', 'only R002 should be active');
|
|
264
|
+
|
|
265
|
+
// R001 still in raw table
|
|
266
|
+
const r1 = getRequirementById('R001');
|
|
267
|
+
assertTrue(r1 !== null, 'superseded requirement still in raw table');
|
|
268
|
+
|
|
269
|
+
closeDatabase();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
console.log('\n=== gsd-db: WAL mode on file-backed DB ===');
|
|
273
|
+
{
|
|
274
|
+
const dbPath = tempDbPath();
|
|
275
|
+
openDatabase(dbPath);
|
|
276
|
+
|
|
277
|
+
const adapter = _getAdapter()!;
|
|
278
|
+
const mode = adapter.prepare('PRAGMA journal_mode').get();
|
|
279
|
+
assertEq(mode?.['journal_mode'], 'wal', 'journal_mode should be wal for file-backed DB');
|
|
280
|
+
|
|
281
|
+
cleanup(dbPath);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
console.log('\n=== gsd-db: transaction rollback on error ===');
|
|
285
|
+
{
|
|
286
|
+
openDatabase(':memory:');
|
|
287
|
+
|
|
288
|
+
// Insert a decision normally
|
|
289
|
+
insertDecision({
|
|
290
|
+
id: 'D010',
|
|
291
|
+
when_context: 'test',
|
|
292
|
+
scope: 'test',
|
|
293
|
+
decision: 'test',
|
|
294
|
+
choice: 'test',
|
|
295
|
+
rationale: 'test',
|
|
296
|
+
revisable: 'test',
|
|
297
|
+
superseded_by: null,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Try a transaction that fails — the insert inside should be rolled back
|
|
301
|
+
let threw = false;
|
|
302
|
+
try {
|
|
303
|
+
transaction(() => {
|
|
304
|
+
insertDecision({
|
|
305
|
+
id: 'D011',
|
|
306
|
+
when_context: 'should be rolled back',
|
|
307
|
+
scope: 'test',
|
|
308
|
+
decision: 'test',
|
|
309
|
+
choice: 'test',
|
|
310
|
+
rationale: 'test',
|
|
311
|
+
revisable: 'test',
|
|
312
|
+
superseded_by: null,
|
|
313
|
+
});
|
|
314
|
+
throw new Error('intentional failure');
|
|
315
|
+
});
|
|
316
|
+
} catch (err) {
|
|
317
|
+
if ((err as Error).message === 'intentional failure') {
|
|
318
|
+
threw = true;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
assertTrue(threw, 'transaction should re-throw the error');
|
|
323
|
+
const d11 = getDecisionById('D011');
|
|
324
|
+
assertEq(d11, null, 'D011 should be rolled back (not found)');
|
|
325
|
+
|
|
326
|
+
// D010 should still be there
|
|
327
|
+
const d10 = getDecisionById('D010');
|
|
328
|
+
assertTrue(d10 !== null, 'D010 should survive the failed transaction');
|
|
329
|
+
|
|
330
|
+
closeDatabase();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
console.log('\n=== gsd-db: query wrappers return null/empty when DB unavailable ===');
|
|
334
|
+
{
|
|
335
|
+
// Ensure DB is closed
|
|
336
|
+
closeDatabase();
|
|
337
|
+
assertTrue(!isDbAvailable(), 'DB should not be available');
|
|
338
|
+
|
|
339
|
+
const d = getDecisionById('D001');
|
|
340
|
+
assertEq(d, null, 'getDecisionById returns null when DB closed');
|
|
341
|
+
|
|
342
|
+
const r = getRequirementById('R001');
|
|
343
|
+
assertEq(r, null, 'getRequirementById returns null when DB closed');
|
|
344
|
+
|
|
345
|
+
const ad = getActiveDecisions();
|
|
346
|
+
assertEq(ad, [], 'getActiveDecisions returns [] when DB closed');
|
|
347
|
+
|
|
348
|
+
const ar = getActiveRequirements();
|
|
349
|
+
assertEq(ar, [], 'getActiveRequirements returns [] when DB closed');
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// ─── Final Report ──────────────────────────────────────────────────────────
|
|
353
|
+
report();
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// gsd-inspect — Tests for /gsd inspect output formatting
|
|
2
|
+
//
|
|
3
|
+
// Tests the pure formatInspectOutput function with known data.
|
|
4
|
+
|
|
5
|
+
import { createTestContext } from './test-helpers.ts';
|
|
6
|
+
import { formatInspectOutput, type InspectData } from '../commands.ts';
|
|
7
|
+
|
|
8
|
+
const { assertEq, assertTrue, assertMatch, report } = createTestContext();
|
|
9
|
+
|
|
10
|
+
// ── formats output with schema version, counts, and recent entries ──
|
|
11
|
+
console.log("# === gsd-inspect: full output formatting ===");
|
|
12
|
+
{
|
|
13
|
+
const data: InspectData = {
|
|
14
|
+
schemaVersion: 2,
|
|
15
|
+
counts: { decisions: 12, requirements: 8, artifacts: 3 },
|
|
16
|
+
recentDecisions: [
|
|
17
|
+
{ id: "D012", decision: "Use SQLite for persistence", choice: "node:sqlite with fallback" },
|
|
18
|
+
{ id: "D011", decision: "Markdown dual-write", choice: "DB-first then regenerate" },
|
|
19
|
+
],
|
|
20
|
+
recentRequirements: [
|
|
21
|
+
{ id: "R015", status: "active", description: "Commands register via pi.registerCommand" },
|
|
22
|
+
{ id: "R014", status: "active", description: "DB writes use upsert pattern" },
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const output = formatInspectOutput(data);
|
|
27
|
+
|
|
28
|
+
assertMatch(output, /=== GSD Database Inspect ===/, "contains header");
|
|
29
|
+
assertMatch(output, /Schema version: 2/, "contains schema version");
|
|
30
|
+
assertMatch(output, /Decisions:\s+12/, "contains decisions count");
|
|
31
|
+
assertMatch(output, /Requirements:\s+8/, "contains requirements count");
|
|
32
|
+
assertMatch(output, /Artifacts:\s+3/, "contains artifacts count");
|
|
33
|
+
assertMatch(output, /Recent decisions:/, "contains recent decisions header");
|
|
34
|
+
assertMatch(output, /D012: Use SQLite for persistence → node:sqlite with fallback/, "contains D012 entry");
|
|
35
|
+
assertMatch(output, /D011: Markdown dual-write → DB-first then regenerate/, "contains D011 entry");
|
|
36
|
+
assertMatch(output, /Recent requirements:/, "contains recent requirements header");
|
|
37
|
+
assertMatch(output, /R015 \[active\]: Commands register via pi\.registerCommand/, "contains R015 entry");
|
|
38
|
+
assertMatch(output, /R014 \[active\]: DB writes use upsert pattern/, "contains R014 entry");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── handles zero counts and no recent entries ──
|
|
42
|
+
console.log("# === gsd-inspect: empty data ===");
|
|
43
|
+
{
|
|
44
|
+
const data: InspectData = {
|
|
45
|
+
schemaVersion: 1,
|
|
46
|
+
counts: { decisions: 0, requirements: 0, artifacts: 0 },
|
|
47
|
+
recentDecisions: [],
|
|
48
|
+
recentRequirements: [],
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const output = formatInspectOutput(data);
|
|
52
|
+
|
|
53
|
+
assertMatch(output, /Schema version: 1/, "contains schema version 1");
|
|
54
|
+
assertMatch(output, /Decisions:\s+0/, "zero decisions");
|
|
55
|
+
assertMatch(output, /Requirements:\s+0/, "zero requirements");
|
|
56
|
+
assertMatch(output, /Artifacts:\s+0/, "zero artifacts");
|
|
57
|
+
assertTrue(!output.includes("Recent decisions:"), "no recent decisions section when empty");
|
|
58
|
+
assertTrue(!output.includes("Recent requirements:"), "no recent requirements section when empty");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── handles null schema version ──
|
|
62
|
+
console.log("# === gsd-inspect: null schema version ===");
|
|
63
|
+
{
|
|
64
|
+
const data: InspectData = {
|
|
65
|
+
schemaVersion: null,
|
|
66
|
+
counts: { decisions: 0, requirements: 0, artifacts: 0 },
|
|
67
|
+
recentDecisions: [],
|
|
68
|
+
recentRequirements: [],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const output = formatInspectOutput(data);
|
|
72
|
+
assertMatch(output, /Schema version: unknown/, "null version shows as unknown");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── formats up to 5 recent entries ──
|
|
76
|
+
console.log("# === gsd-inspect: five recent entries ===");
|
|
77
|
+
{
|
|
78
|
+
const data: InspectData = {
|
|
79
|
+
schemaVersion: 2,
|
|
80
|
+
counts: { decisions: 5, requirements: 5, artifacts: 0 },
|
|
81
|
+
recentDecisions: [
|
|
82
|
+
{ id: "D005", decision: "Dec 5", choice: "C5" },
|
|
83
|
+
{ id: "D004", decision: "Dec 4", choice: "C4" },
|
|
84
|
+
{ id: "D003", decision: "Dec 3", choice: "C3" },
|
|
85
|
+
{ id: "D002", decision: "Dec 2", choice: "C2" },
|
|
86
|
+
{ id: "D001", decision: "Dec 1", choice: "C1" },
|
|
87
|
+
],
|
|
88
|
+
recentRequirements: [
|
|
89
|
+
{ id: "R005", status: "active", description: "Req 5" },
|
|
90
|
+
{ id: "R004", status: "done", description: "Req 4" },
|
|
91
|
+
{ id: "R003", status: "active", description: "Req 3" },
|
|
92
|
+
{ id: "R002", status: "active", description: "Req 2" },
|
|
93
|
+
{ id: "R001", status: "done", description: "Req 1" },
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const output = formatInspectOutput(data);
|
|
98
|
+
|
|
99
|
+
for (let i = 1; i <= 5; i++) {
|
|
100
|
+
assertMatch(output, new RegExp(`D00${i}: Dec ${i} → C${i}`), `contains D00${i}`);
|
|
101
|
+
}
|
|
102
|
+
for (let i = 1; i <= 5; i++) {
|
|
103
|
+
assertMatch(output, new RegExp(`R00${i}`), `contains R00${i}`);
|
|
104
|
+
}
|
|
105
|
+
assertMatch(output, /\[active\]/, "contains active status");
|
|
106
|
+
assertMatch(output, /\[done\]/, "contains done status");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── output is multiline text (not JSON) ──
|
|
110
|
+
console.log("# === gsd-inspect: output format ===");
|
|
111
|
+
{
|
|
112
|
+
const data: InspectData = {
|
|
113
|
+
schemaVersion: 2,
|
|
114
|
+
counts: { decisions: 1, requirements: 1, artifacts: 0 },
|
|
115
|
+
recentDecisions: [{ id: "D001", decision: "Test", choice: "Yes" }],
|
|
116
|
+
recentRequirements: [{ id: "R001", status: "active", description: "Test req" }],
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const output = formatInspectOutput(data);
|
|
120
|
+
const lines = output.split("\n");
|
|
121
|
+
assertTrue(lines.length > 5, "output has multiple lines");
|
|
122
|
+
assertTrue(!output.startsWith("{"), "output is not JSON");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
report();
|