oh-my-claude-sisyphus 3.7.0 → 3.7.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/README.md +24 -1
- package/commands/hud.md +37 -5
- package/commands/omc-setup.md +105 -0
- package/dist/__tests__/compatibility-security.test.d.ts +13 -0
- package/dist/__tests__/compatibility-security.test.d.ts.map +1 -0
- package/dist/__tests__/compatibility-security.test.js +403 -0
- package/dist/__tests__/compatibility-security.test.js.map +1 -0
- package/dist/__tests__/compatibility.test.d.ts +7 -0
- package/dist/__tests__/compatibility.test.d.ts.map +1 -0
- package/dist/__tests__/compatibility.test.js +484 -0
- package/dist/__tests__/compatibility.test.js.map +1 -0
- package/dist/__tests__/hud/analytics-display.test.js +141 -1
- package/dist/__tests__/hud/analytics-display.test.js.map +1 -1
- package/dist/__tests__/hud-windows.test.d.ts +2 -0
- package/dist/__tests__/hud-windows.test.d.ts.map +1 -0
- package/dist/__tests__/hud-windows.test.js +91 -0
- package/dist/__tests__/hud-windows.test.js.map +1 -0
- package/dist/cli/analytics.js +0 -0
- package/dist/cli/index.js +0 -0
- package/dist/compatibility/discovery.d.ts +58 -0
- package/dist/compatibility/discovery.d.ts.map +1 -0
- package/dist/compatibility/discovery.js +619 -0
- package/dist/compatibility/discovery.js.map +1 -0
- package/dist/compatibility/index.d.ts +51 -0
- package/dist/compatibility/index.d.ts.map +1 -0
- package/dist/compatibility/index.js +72 -0
- package/dist/compatibility/index.js.map +1 -0
- package/dist/compatibility/mcp-bridge.d.ts +138 -0
- package/dist/compatibility/mcp-bridge.d.ts.map +1 -0
- package/dist/compatibility/mcp-bridge.js +524 -0
- package/dist/compatibility/mcp-bridge.js.map +1 -0
- package/dist/compatibility/permission-adapter.d.ts +79 -0
- package/dist/compatibility/permission-adapter.d.ts.map +1 -0
- package/dist/compatibility/permission-adapter.js +369 -0
- package/dist/compatibility/permission-adapter.js.map +1 -0
- package/dist/compatibility/registry.d.ts +161 -0
- package/dist/compatibility/registry.d.ts.map +1 -0
- package/dist/compatibility/registry.js +389 -0
- package/dist/compatibility/registry.js.map +1 -0
- package/dist/compatibility/types.d.ts +249 -0
- package/dist/compatibility/types.d.ts.map +1 -0
- package/dist/compatibility/types.js +8 -0
- package/dist/compatibility/types.js.map +1 -0
- package/dist/features/rate-limit-wait/daemon.d.ts.map +1 -1
- package/dist/features/rate-limit-wait/daemon.js +44 -1
- package/dist/features/rate-limit-wait/daemon.js.map +1 -1
- package/dist/features/state-manager/index.d.ts.map +1 -1
- package/dist/features/state-manager/index.js +4 -1
- package/dist/features/state-manager/index.js.map +1 -1
- package/dist/hooks/permission-handler/__tests__/index.test.js +47 -0
- package/dist/hooks/permission-handler/__tests__/index.test.js.map +1 -1
- package/dist/hooks/permission-handler/index.d.ts +1 -1
- package/dist/hooks/permission-handler/index.d.ts.map +1 -1
- package/dist/hooks/permission-handler/index.js +11 -15
- package/dist/hooks/permission-handler/index.js.map +1 -1
- package/dist/hooks/plugin-patterns/index.d.ts +5 -0
- package/dist/hooks/plugin-patterns/index.d.ts.map +1 -1
- package/dist/hooks/plugin-patterns/index.js +26 -1
- package/dist/hooks/plugin-patterns/index.js.map +1 -1
- package/dist/hooks/session-end/index.d.ts +0 -8
- package/dist/hooks/session-end/index.d.ts.map +1 -1
- package/dist/hooks/session-end/index.js +5 -12
- package/dist/hooks/session-end/index.js.map +1 -1
- package/dist/hooks/subagent-tracker/index.d.ts.map +1 -1
- package/dist/hooks/subagent-tracker/index.js +32 -17
- package/dist/hooks/subagent-tracker/index.js.map +1 -1
- package/dist/hud/analytics-display.d.ts +17 -1
- package/dist/hud/analytics-display.d.ts.map +1 -1
- package/dist/hud/analytics-display.js +55 -12
- package/dist/hud/analytics-display.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +49 -18
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/types.d.ts +2 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js +14 -0
- package/dist/hud/types.js.map +1 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +3 -2
- package/dist/installer/index.js.map +1 -1
- package/docs/COMPATIBILITY.md +1051 -0
- package/hooks/keyword-detector.sh +4 -4
- package/hooks/persistent-mode.sh +10 -10
- package/hooks/session-start.sh +4 -4
- package/package.json +3 -1
- package/scripts/keyword-detector.mjs +4 -4
- package/scripts/persistent-mode.mjs +6 -6
- package/scripts/persistent-mode.sh +10 -10
- package/scripts/session-start.mjs +4 -4
- package/skills/hud/SKILL.md +37 -5
- package/skills/omc-setup/SKILL.md +162 -4
- package/skills/writer-memory/SKILL.md +443 -0
- package/skills/writer-memory/lib/character-tracker.ts +338 -0
- package/skills/writer-memory/lib/memory-manager.ts +804 -0
- package/skills/writer-memory/lib/relationship-graph.ts +400 -0
- package/skills/writer-memory/lib/scene-organizer.ts +544 -0
- package/skills/writer-memory/lib/synopsis-builder.ts +339 -0
- package/skills/writer-memory/templates/synopsis-template.md +46 -0
- package/templates/hooks/keyword-detector.sh +4 -4
- package/templates/hooks/persistent-mode.sh +10 -10
- package/templates/hooks/session-start.sh +4 -4
- package/dist/__tests__/analytics/analytics-summary.test.d.ts +0 -2
- package/dist/__tests__/analytics/analytics-summary.test.d.ts.map +0 -1
- package/dist/__tests__/analytics/analytics-summary.test.js +0 -267
- package/dist/__tests__/analytics/analytics-summary.test.js.map +0 -1
- package/dist/__tests__/analytics/cost-estimator.test.d.ts +0 -2
- package/dist/__tests__/analytics/cost-estimator.test.d.ts.map +0 -1
- package/dist/__tests__/analytics/cost-estimator.test.js +0 -212
- package/dist/__tests__/analytics/cost-estimator.test.js.map +0 -1
- package/dist/__tests__/hooks/auto-slash-command/executor.test.d.ts +0 -7
- package/dist/__tests__/hooks/auto-slash-command/executor.test.d.ts.map +0 -1
- package/dist/__tests__/hooks/auto-slash-command/executor.test.js +0 -374
- package/dist/__tests__/hooks/auto-slash-command/executor.test.js.map +0 -1
- package/dist/__tests__/hud/auto-tracking.integration.test.d.ts +0 -2
- package/dist/__tests__/hud/auto-tracking.integration.test.d.ts.map +0 -1
- package/dist/__tests__/hud/auto-tracking.integration.test.js +0 -12
- package/dist/__tests__/hud/auto-tracking.integration.test.js.map +0 -1
- package/dist/__tests__/learned-skills/config.test.d.ts +0 -2
- package/dist/__tests__/learned-skills/config.test.d.ts.map +0 -1
- package/dist/__tests__/learned-skills/config.test.js +0 -37
- package/dist/__tests__/learned-skills/config.test.js.map +0 -1
- package/dist/__tests__/learned-skills/detector.test.d.ts +0 -2
- package/dist/__tests__/learned-skills/detector.test.d.ts.map +0 -1
- package/dist/__tests__/learned-skills/detector.test.js +0 -99
- package/dist/__tests__/learned-skills/detector.test.js.map +0 -1
- package/dist/__tests__/learned-skills/finder.test.d.ts +0 -2
- package/dist/__tests__/learned-skills/finder.test.d.ts.map +0 -1
- package/dist/__tests__/learned-skills/finder.test.js +0 -59
- package/dist/__tests__/learned-skills/finder.test.js.map +0 -1
- package/dist/__tests__/learned-skills/loader.test.d.ts +0 -2
- package/dist/__tests__/learned-skills/loader.test.d.ts.map +0 -1
- package/dist/__tests__/learned-skills/loader.test.js +0 -69
- package/dist/__tests__/learned-skills/loader.test.js.map +0 -1
- package/dist/__tests__/learned-skills/parser.test.d.ts +0 -2
- package/dist/__tests__/learned-skills/parser.test.d.ts.map +0 -1
- package/dist/__tests__/learned-skills/parser.test.js +0 -81
- package/dist/__tests__/learned-skills/parser.test.js.map +0 -1
- package/dist/__tests__/learned-skills/validator.test.d.ts +0 -2
- package/dist/__tests__/learned-skills/validator.test.d.ts.map +0 -1
- package/dist/__tests__/learned-skills/validator.test.js +0 -85
- package/dist/__tests__/learned-skills/validator.test.js.map +0 -1
- package/dist/agents/codex-agents.d.ts +0 -20
- package/dist/agents/codex-agents.d.ts.map +0 -1
- package/dist/agents/codex-agents.js +0 -36
- package/dist/agents/codex-agents.js.map +0 -1
- package/dist/agents/document-writer.d.ts +0 -11
- package/dist/agents/document-writer.d.ts.map +0 -1
- package/dist/agents/document-writer.js +0 -209
- package/dist/agents/document-writer.js.map +0 -1
- package/dist/agents/frontend-engineer.d.ts +0 -11
- package/dist/agents/frontend-engineer.d.ts.map +0 -1
- package/dist/agents/frontend-engineer.js +0 -115
- package/dist/agents/frontend-engineer.js.map +0 -1
- package/dist/agents/librarian.d.ts +0 -12
- package/dist/agents/librarian.d.ts.map +0 -1
- package/dist/agents/librarian.js +0 -103
- package/dist/agents/librarian.js.map +0 -1
- package/dist/agents/metis.d.ts +0 -12
- package/dist/agents/metis.d.ts.map +0 -1
- package/dist/agents/metis.js +0 -117
- package/dist/agents/metis.js.map +0 -1
- package/dist/agents/momus.d.ts +0 -12
- package/dist/agents/momus.d.ts.map +0 -1
- package/dist/agents/momus.js +0 -128
- package/dist/agents/momus.js.map +0 -1
- package/dist/agents/multimodal-looker.d.ts +0 -11
- package/dist/agents/multimodal-looker.d.ts.map +0 -1
- package/dist/agents/multimodal-looker.js +0 -70
- package/dist/agents/multimodal-looker.js.map +0 -1
- package/dist/agents/oracle.d.ts +0 -13
- package/dist/agents/oracle.d.ts.map +0 -1
- package/dist/agents/oracle.js +0 -191
- package/dist/agents/oracle.js.map +0 -1
- package/dist/agents/orchestrator-sisyphus.d.ts +0 -11
- package/dist/agents/orchestrator-sisyphus.d.ts.map +0 -1
- package/dist/agents/orchestrator-sisyphus.js +0 -115
- package/dist/agents/orchestrator-sisyphus.js.map +0 -1
- package/dist/agents/prometheus.d.ts +0 -12
- package/dist/agents/prometheus.d.ts.map +0 -1
- package/dist/agents/prometheus.js +0 -195
- package/dist/agents/prometheus.js.map +0 -1
- package/dist/agents/sisyphus-junior.d.ts +0 -12
- package/dist/agents/sisyphus-junior.d.ts.map +0 -1
- package/dist/agents/sisyphus-junior.js +0 -93
- package/dist/agents/sisyphus-junior.js.map +0 -1
- package/dist/cli/components/CostDashboard.d.ts +0 -15
- package/dist/cli/components/CostDashboard.d.ts.map +0 -1
- package/dist/cli/components/CostDashboard.js +0 -15
- package/dist/cli/components/CostDashboard.js.map +0 -1
- package/dist/cli/components/LiveStats.d.ts +0 -16
- package/dist/cli/components/LiveStats.d.ts.map +0 -1
- package/dist/cli/components/LiveStats.js +0 -16
- package/dist/cli/components/LiveStats.js.map +0 -1
- package/dist/cli/components/SessionBrowser.d.ts +0 -14
- package/dist/cli/components/SessionBrowser.d.ts.map +0 -1
- package/dist/cli/components/SessionBrowser.js +0 -14
- package/dist/cli/components/SessionBrowser.js.map +0 -1
- package/dist/cli/tui.d.ts +0 -21
- package/dist/cli/tui.d.ts.map +0 -1
- package/dist/cli/tui.js +0 -21
- package/dist/cli/tui.js.map +0 -1
- package/dist/hooks/autopilot/signals.d.ts +0 -20
- package/dist/hooks/autopilot/signals.d.ts.map +0 -1
- package/dist/hooks/autopilot/signals.js +0 -75
- package/dist/hooks/autopilot/signals.js.map +0 -1
- package/dist/hooks/autopilot/summary.d.ts +0 -27
- package/dist/hooks/autopilot/summary.d.ts.map +0 -1
- package/dist/hooks/autopilot/summary.js +0 -160
- package/dist/hooks/autopilot/summary.js.map +0 -1
- package/dist/hooks/autopilot/transition.d.ts +0 -39
- package/dist/hooks/autopilot/transition.d.ts.map +0 -1
- package/dist/hooks/autopilot/transition.js +0 -216
- package/dist/hooks/autopilot/transition.js.map +0 -1
- package/dist/hooks/context-window-limit-recovery/constants.d.ts +0 -28
- package/dist/hooks/context-window-limit-recovery/constants.d.ts.map +0 -1
- package/dist/hooks/context-window-limit-recovery/constants.js +0 -85
- package/dist/hooks/context-window-limit-recovery/constants.js.map +0 -1
- package/dist/hooks/context-window-limit-recovery/index.d.ts +0 -62
- package/dist/hooks/context-window-limit-recovery/index.d.ts.map +0 -1
- package/dist/hooks/context-window-limit-recovery/index.js +0 -201
- package/dist/hooks/context-window-limit-recovery/index.js.map +0 -1
- package/dist/hooks/context-window-limit-recovery/parser.d.ts +0 -31
- package/dist/hooks/context-window-limit-recovery/parser.d.ts.map +0 -1
- package/dist/hooks/context-window-limit-recovery/parser.js +0 -241
- package/dist/hooks/context-window-limit-recovery/parser.js.map +0 -1
- package/dist/hooks/context-window-limit-recovery/types.d.ts +0 -84
- package/dist/hooks/context-window-limit-recovery/types.d.ts.map +0 -1
- package/dist/hooks/context-window-limit-recovery/types.js +0 -34
- package/dist/hooks/context-window-limit-recovery/types.js.map +0 -1
- package/dist/hooks/edit-error-recovery/index.d.ts +0 -62
- package/dist/hooks/edit-error-recovery/index.d.ts.map +0 -1
- package/dist/hooks/edit-error-recovery/index.js +0 -89
- package/dist/hooks/edit-error-recovery/index.js.map +0 -1
- package/dist/hooks/learned-skills/config.d.ts +0 -53
- package/dist/hooks/learned-skills/config.d.ts.map +0 -1
- package/dist/hooks/learned-skills/config.js +0 -103
- package/dist/hooks/learned-skills/config.js.map +0 -1
- package/dist/hooks/learned-skills/constants.d.ts +0 -24
- package/dist/hooks/learned-skills/constants.d.ts.map +0 -1
- package/dist/hooks/learned-skills/constants.js +0 -26
- package/dist/hooks/learned-skills/constants.js.map +0 -1
- package/dist/hooks/learned-skills/detection-hook.d.ts +0 -39
- package/dist/hooks/learned-skills/detection-hook.d.ts.map +0 -1
- package/dist/hooks/learned-skills/detection-hook.js +0 -83
- package/dist/hooks/learned-skills/detection-hook.js.map +0 -1
- package/dist/hooks/learned-skills/detector.d.ts +0 -30
- package/dist/hooks/learned-skills/detector.d.ts.map +0 -1
- package/dist/hooks/learned-skills/detector.js +0 -150
- package/dist/hooks/learned-skills/detector.js.map +0 -1
- package/dist/hooks/learned-skills/finder.d.ts +0 -21
- package/dist/hooks/learned-skills/finder.d.ts.map +0 -1
- package/dist/hooks/learned-skills/finder.js +0 -117
- package/dist/hooks/learned-skills/finder.js.map +0 -1
- package/dist/hooks/learned-skills/index.d.ts +0 -62
- package/dist/hooks/learned-skills/index.d.ts.map +0 -1
- package/dist/hooks/learned-skills/index.js +0 -137
- package/dist/hooks/learned-skills/index.js.map +0 -1
- package/dist/hooks/learned-skills/loader.d.ts +0 -20
- package/dist/hooks/learned-skills/loader.d.ts.map +0 -1
- package/dist/hooks/learned-skills/loader.js +0 -107
- package/dist/hooks/learned-skills/loader.js.map +0 -1
- package/dist/hooks/learned-skills/parser.d.ts +0 -21
- package/dist/hooks/learned-skills/parser.d.ts.map +0 -1
- package/dist/hooks/learned-skills/parser.js +0 -190
- package/dist/hooks/learned-skills/parser.js.map +0 -1
- package/dist/hooks/learned-skills/promotion.d.ts +0 -29
- package/dist/hooks/learned-skills/promotion.d.ts.map +0 -1
- package/dist/hooks/learned-skills/promotion.js +0 -87
- package/dist/hooks/learned-skills/promotion.js.map +0 -1
- package/dist/hooks/learned-skills/types.d.ts +0 -109
- package/dist/hooks/learned-skills/types.d.ts.map +0 -1
- package/dist/hooks/learned-skills/types.js +0 -8
- package/dist/hooks/learned-skills/types.js.map +0 -1
- package/dist/hooks/learned-skills/validator.d.ts +0 -15
- package/dist/hooks/learned-skills/validator.d.ts.map +0 -1
- package/dist/hooks/learned-skills/validator.js +0 -87
- package/dist/hooks/learned-skills/validator.js.map +0 -1
- package/dist/hooks/learned-skills/writer.d.ts +0 -27
- package/dist/hooks/learned-skills/writer.d.ts.map +0 -1
- package/dist/hooks/learned-skills/writer.js +0 -126
- package/dist/hooks/learned-skills/writer.js.map +0 -1
- package/dist/hooks/mnemosyne/config.d.ts +0 -53
- package/dist/hooks/mnemosyne/config.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/config.js +0 -103
- package/dist/hooks/mnemosyne/config.js.map +0 -1
- package/dist/hooks/mnemosyne/constants.d.ts +0 -24
- package/dist/hooks/mnemosyne/constants.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/constants.js +0 -26
- package/dist/hooks/mnemosyne/constants.js.map +0 -1
- package/dist/hooks/mnemosyne/detection-hook.d.ts +0 -39
- package/dist/hooks/mnemosyne/detection-hook.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/detection-hook.js +0 -83
- package/dist/hooks/mnemosyne/detection-hook.js.map +0 -1
- package/dist/hooks/mnemosyne/detector.d.ts +0 -30
- package/dist/hooks/mnemosyne/detector.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/detector.js +0 -150
- package/dist/hooks/mnemosyne/detector.js.map +0 -1
- package/dist/hooks/mnemosyne/finder.d.ts +0 -21
- package/dist/hooks/mnemosyne/finder.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/finder.js +0 -117
- package/dist/hooks/mnemosyne/finder.js.map +0 -1
- package/dist/hooks/mnemosyne/index.d.ts +0 -62
- package/dist/hooks/mnemosyne/index.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/index.js +0 -137
- package/dist/hooks/mnemosyne/index.js.map +0 -1
- package/dist/hooks/mnemosyne/loader.d.ts +0 -20
- package/dist/hooks/mnemosyne/loader.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/loader.js +0 -113
- package/dist/hooks/mnemosyne/loader.js.map +0 -1
- package/dist/hooks/mnemosyne/parser.d.ts +0 -21
- package/dist/hooks/mnemosyne/parser.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/parser.js +0 -190
- package/dist/hooks/mnemosyne/parser.js.map +0 -1
- package/dist/hooks/mnemosyne/promotion.d.ts +0 -29
- package/dist/hooks/mnemosyne/promotion.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/promotion.js +0 -87
- package/dist/hooks/mnemosyne/promotion.js.map +0 -1
- package/dist/hooks/mnemosyne/types.d.ts +0 -109
- package/dist/hooks/mnemosyne/types.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/types.js +0 -8
- package/dist/hooks/mnemosyne/types.js.map +0 -1
- package/dist/hooks/mnemosyne/validator.d.ts +0 -15
- package/dist/hooks/mnemosyne/validator.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/validator.js +0 -87
- package/dist/hooks/mnemosyne/validator.js.map +0 -1
- package/dist/hooks/mnemosyne/writer.d.ts +0 -27
- package/dist/hooks/mnemosyne/writer.d.ts.map +0 -1
- package/dist/hooks/mnemosyne/writer.js +0 -126
- package/dist/hooks/mnemosyne/writer.js.map +0 -1
- package/dist/hooks/ralph-loop/index.d.ts +0 -116
- package/dist/hooks/ralph-loop/index.d.ts.map +0 -1
- package/dist/hooks/ralph-loop/index.js +0 -322
- package/dist/hooks/ralph-loop/index.js.map +0 -1
- package/dist/hooks/ralph-prd/index.d.ts +0 -130
- package/dist/hooks/ralph-prd/index.d.ts.map +0 -1
- package/dist/hooks/ralph-prd/index.js +0 -310
- package/dist/hooks/ralph-prd/index.js.map +0 -1
- package/dist/hooks/ralph-progress/index.d.ts +0 -102
- package/dist/hooks/ralph-progress/index.d.ts.map +0 -1
- package/dist/hooks/ralph-progress/index.js +0 -408
- package/dist/hooks/ralph-progress/index.js.map +0 -1
- package/dist/hooks/ralph-verifier/index.d.ts +0 -72
- package/dist/hooks/ralph-verifier/index.d.ts.map +0 -1
- package/dist/hooks/ralph-verifier/index.js +0 -223
- package/dist/hooks/ralph-verifier/index.js.map +0 -1
- package/dist/hooks/session-recovery/constants.d.ts +0 -56
- package/dist/hooks/session-recovery/constants.d.ts.map +0 -1
- package/dist/hooks/session-recovery/constants.js +0 -78
- package/dist/hooks/session-recovery/constants.js.map +0 -1
- package/dist/hooks/session-recovery/index.d.ts +0 -53
- package/dist/hooks/session-recovery/index.d.ts.map +0 -1
- package/dist/hooks/session-recovery/index.js +0 -321
- package/dist/hooks/session-recovery/index.js.map +0 -1
- package/dist/hooks/session-recovery/storage.d.ts +0 -76
- package/dist/hooks/session-recovery/storage.d.ts.map +0 -1
- package/dist/hooks/session-recovery/storage.js +0 -383
- package/dist/hooks/session-recovery/storage.js.map +0 -1
- package/dist/hooks/session-recovery/types.d.ts +0 -145
- package/dist/hooks/session-recovery/types.d.ts.map +0 -1
- package/dist/hooks/session-recovery/types.js +0 -8
- package/dist/hooks/session-recovery/types.js.map +0 -1
- package/dist/hooks/sisyphus-orchestrator/constants.d.ts +0 -23
- package/dist/hooks/sisyphus-orchestrator/constants.d.ts.map +0 -1
- package/dist/hooks/sisyphus-orchestrator/constants.js +0 -142
- package/dist/hooks/sisyphus-orchestrator/constants.js.map +0 -1
- package/dist/hooks/sisyphus-orchestrator/index.d.ts +0 -113
- package/dist/hooks/sisyphus-orchestrator/index.d.ts.map +0 -1
- package/dist/hooks/sisyphus-orchestrator/index.js +0 -309
- package/dist/hooks/sisyphus-orchestrator/index.js.map +0 -1
- package/dist/hooks/ultraqa-loop/index.d.ts +0 -94
- package/dist/hooks/ultraqa-loop/index.d.ts.map +0 -1
- package/dist/hooks/ultraqa-loop/index.js +0 -216
- package/dist/hooks/ultraqa-loop/index.js.map +0 -1
- package/dist/hooks/ultrawork-state/index.d.ts +0 -62
- package/dist/hooks/ultrawork-state/index.d.ts.map +0 -1
- package/dist/hooks/ultrawork-state/index.js +0 -208
- package/dist/hooks/ultrawork-state/index.js.map +0 -1
- package/dist/hud/sisyphus-state.d.ts +0 -31
- package/dist/hud/sisyphus-state.d.ts.map +0 -1
- package/dist/hud/sisyphus-state.js +0 -163
- package/dist/hud/sisyphus-state.js.map +0 -1
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* memory-manager.ts
|
|
3
|
+
*
|
|
4
|
+
* Core memory management module for the Writer Memory System.
|
|
5
|
+
* Handles all CRUD operations for .writer-memory/ storage.
|
|
6
|
+
*
|
|
7
|
+
* This is a REFERENCE IMPLEMENTATION that Claude reads when the skill
|
|
8
|
+
* is activated. Written as real, runnable TypeScript with proper types,
|
|
9
|
+
* error handling, and atomic operations.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync, renameSync, readdirSync } from "fs";
|
|
13
|
+
import { join, dirname } from "path";
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Types
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
export type SpeechLevel = "반말" | "존댓말" | "해체" | "혼합";
|
|
20
|
+
|
|
21
|
+
export type RelationshipType =
|
|
22
|
+
| "romantic"
|
|
23
|
+
| "familial"
|
|
24
|
+
| "friendship"
|
|
25
|
+
| "antagonistic"
|
|
26
|
+
| "professional"
|
|
27
|
+
| "mentor"
|
|
28
|
+
| "complex";
|
|
29
|
+
|
|
30
|
+
export interface EmotionPoint {
|
|
31
|
+
timestamp: string;
|
|
32
|
+
sceneId?: string;
|
|
33
|
+
/** Korean emotion word, e.g. "그리움" */
|
|
34
|
+
emotion: string;
|
|
35
|
+
/** What caused this emotion */
|
|
36
|
+
trigger: string;
|
|
37
|
+
intensity: 1 | 2 | 3 | 4 | 5;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface Character {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
aliases: string[];
|
|
44
|
+
/** Arc summary, e.g. "체념->욕망자각->선택" */
|
|
45
|
+
arc: string;
|
|
46
|
+
/** Tone summary, e.g. "담백, 현재충실" */
|
|
47
|
+
tone: string;
|
|
48
|
+
speechLevel: SpeechLevel;
|
|
49
|
+
/** Characteristic phrases/words */
|
|
50
|
+
keywords: string[];
|
|
51
|
+
/** Attitude summary (태도 요약) */
|
|
52
|
+
attitude: string;
|
|
53
|
+
timeline: EmotionPoint[];
|
|
54
|
+
notes: string;
|
|
55
|
+
created: string;
|
|
56
|
+
updated: string;
|
|
57
|
+
/** Words/patterns the character would NEVER say */
|
|
58
|
+
taboo?: string[];
|
|
59
|
+
/** Default emotional state */
|
|
60
|
+
emotional_baseline?: string;
|
|
61
|
+
/** What triggers emotional changes */
|
|
62
|
+
triggers?: string[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface WorldRule {
|
|
66
|
+
id: string;
|
|
67
|
+
category: string;
|
|
68
|
+
description: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface Location {
|
|
72
|
+
id: string;
|
|
73
|
+
name: string;
|
|
74
|
+
description: string;
|
|
75
|
+
atmosphere: string;
|
|
76
|
+
/** Other location IDs */
|
|
77
|
+
connectedTo: string[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface WorldMemory {
|
|
81
|
+
name: string;
|
|
82
|
+
era: string;
|
|
83
|
+
atmosphere: string;
|
|
84
|
+
rules: WorldRule[];
|
|
85
|
+
locations: Location[];
|
|
86
|
+
culturalNotes: string[];
|
|
87
|
+
notes: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface RelationshipEvent {
|
|
91
|
+
timestamp: string;
|
|
92
|
+
sceneId?: string;
|
|
93
|
+
change: string;
|
|
94
|
+
catalyst: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface Relationship {
|
|
98
|
+
id: string;
|
|
99
|
+
/** Character ID */
|
|
100
|
+
from: string;
|
|
101
|
+
/** Character ID */
|
|
102
|
+
to: string;
|
|
103
|
+
type: RelationshipType;
|
|
104
|
+
/** e.g. "일방적 짝사랑 -> 상호 이해" */
|
|
105
|
+
dynamic: string;
|
|
106
|
+
speechLevel?: SpeechLevel;
|
|
107
|
+
evolution: RelationshipEvent[];
|
|
108
|
+
notes?: string;
|
|
109
|
+
created: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface Cut {
|
|
113
|
+
order: number;
|
|
114
|
+
type: "dialogue" | "narration" | "action" | "internal";
|
|
115
|
+
content: string;
|
|
116
|
+
character?: string;
|
|
117
|
+
emotionTag?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface Scene {
|
|
121
|
+
id: string;
|
|
122
|
+
title: string;
|
|
123
|
+
chapter?: string;
|
|
124
|
+
order: number;
|
|
125
|
+
characters: string[];
|
|
126
|
+
emotionTags: string[];
|
|
127
|
+
cuts: Cut[];
|
|
128
|
+
narrationTone?: string;
|
|
129
|
+
notes?: string;
|
|
130
|
+
created: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface Theme {
|
|
134
|
+
id: string;
|
|
135
|
+
name: string;
|
|
136
|
+
description: string;
|
|
137
|
+
keywords: string[];
|
|
138
|
+
relatedCharacters: string[];
|
|
139
|
+
relatedScenes: string[];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface SynopsisState {
|
|
143
|
+
/** 주인공 태도 요약 */
|
|
144
|
+
protagonistAttitude: string;
|
|
145
|
+
/** 관계 핵심 구도 */
|
|
146
|
+
coreRelationships: string;
|
|
147
|
+
/** 정서적 테마 */
|
|
148
|
+
emotionalTheme: string;
|
|
149
|
+
/** 장르 vs 실제감정 대비 */
|
|
150
|
+
genreVsRealEmotion: string;
|
|
151
|
+
/** 엔딩 정서 잔상 */
|
|
152
|
+
endingAftertaste: string;
|
|
153
|
+
lastGenerated?: string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface ProjectMeta {
|
|
157
|
+
name: string;
|
|
158
|
+
genre: string;
|
|
159
|
+
/** ISO timestamp */
|
|
160
|
+
created: string;
|
|
161
|
+
/** ISO timestamp */
|
|
162
|
+
updated: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface WriterMemory {
|
|
166
|
+
version: "1.0";
|
|
167
|
+
project: ProjectMeta;
|
|
168
|
+
characters: Record<string, Character>;
|
|
169
|
+
world: WorldMemory;
|
|
170
|
+
relationships: Relationship[];
|
|
171
|
+
scenes: Scene[];
|
|
172
|
+
themes: Theme[];
|
|
173
|
+
synopsis: SynopsisState;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface MemoryStats {
|
|
177
|
+
characterCount: number;
|
|
178
|
+
relationshipCount: number;
|
|
179
|
+
sceneCount: number;
|
|
180
|
+
themeCount: number;
|
|
181
|
+
totalEmotionPoints: number;
|
|
182
|
+
lastUpdated: string;
|
|
183
|
+
storageSizeKB: number;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface SearchResult {
|
|
187
|
+
type: "character" | "relationship" | "scene" | "theme" | "world";
|
|
188
|
+
id: string;
|
|
189
|
+
title: string;
|
|
190
|
+
relevance: string;
|
|
191
|
+
snippet: string;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface ValidationResult {
|
|
195
|
+
valid: boolean;
|
|
196
|
+
errors: string[];
|
|
197
|
+
warnings: string[];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// Constants
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
const MEMORY_DIR = ".writer-memory";
|
|
205
|
+
const MEMORY_FILE = "memory.json";
|
|
206
|
+
const BACKUP_DIR = "backups";
|
|
207
|
+
const MAX_BACKUPS = 20;
|
|
208
|
+
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
// Path Helpers
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
|
|
213
|
+
/** Returns the path to the main memory JSON file. */
|
|
214
|
+
export function getMemoryPath(): string {
|
|
215
|
+
return join(MEMORY_DIR, MEMORY_FILE);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Returns the path to the backups directory. */
|
|
219
|
+
export function getBackupPath(): string {
|
|
220
|
+
return join(MEMORY_DIR, BACKUP_DIR);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
// ID Generation
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Generate a prefixed unique ID using unix timestamp + random suffix.
|
|
229
|
+
* @param prefix - e.g. "char", "rel", "scene"
|
|
230
|
+
* @returns e.g. "char_1706123456_a3f"
|
|
231
|
+
*/
|
|
232
|
+
export function generateId(prefix: string): string {
|
|
233
|
+
const ts = Math.floor(Date.now() / 1000);
|
|
234
|
+
const rand = Math.random().toString(36).slice(2, 5);
|
|
235
|
+
return `${prefix}_${ts}_${rand}`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
// Timestamps
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
|
|
242
|
+
/** Returns the current time as an ISO 8601 string. */
|
|
243
|
+
export function now(): string {
|
|
244
|
+
return new Date().toISOString();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Format an ISO timestamp into Korean date format.
|
|
249
|
+
* @param iso - ISO 8601 string
|
|
250
|
+
* @returns e.g. "2024년 1월 24일"
|
|
251
|
+
*/
|
|
252
|
+
export function formatKoreanDate(iso: string): string {
|
|
253
|
+
const d = new Date(iso);
|
|
254
|
+
if (isNaN(d.getTime())) {
|
|
255
|
+
return iso; // fallback for invalid dates
|
|
256
|
+
}
|
|
257
|
+
const year = d.getFullYear();
|
|
258
|
+
const month = d.getMonth() + 1;
|
|
259
|
+
const day = d.getDate();
|
|
260
|
+
return `${year}년 ${month}월 ${day}일`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
// Initialization
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Create a fresh WriterMemory structure for a new project.
|
|
269
|
+
* Also ensures the .writer-memory/ directory tree exists on disk.
|
|
270
|
+
*
|
|
271
|
+
* @param projectName - e.g. "이별의 온도"
|
|
272
|
+
* @param genre - e.g. "멜로 / 성장 드라마"
|
|
273
|
+
*/
|
|
274
|
+
export function initMemory(projectName: string, genre: string): WriterMemory {
|
|
275
|
+
const timestamp = now();
|
|
276
|
+
|
|
277
|
+
// Ensure directory structure
|
|
278
|
+
const memDir = MEMORY_DIR;
|
|
279
|
+
const backDir = getBackupPath();
|
|
280
|
+
if (!existsSync(memDir)) {
|
|
281
|
+
mkdirSync(memDir, { recursive: true });
|
|
282
|
+
}
|
|
283
|
+
if (!existsSync(backDir)) {
|
|
284
|
+
mkdirSync(backDir, { recursive: true });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const memory: WriterMemory = {
|
|
288
|
+
version: "1.0",
|
|
289
|
+
project: {
|
|
290
|
+
name: projectName,
|
|
291
|
+
genre,
|
|
292
|
+
created: timestamp,
|
|
293
|
+
updated: timestamp,
|
|
294
|
+
},
|
|
295
|
+
characters: {},
|
|
296
|
+
world: {
|
|
297
|
+
name: "",
|
|
298
|
+
era: "",
|
|
299
|
+
atmosphere: "",
|
|
300
|
+
rules: [],
|
|
301
|
+
locations: [],
|
|
302
|
+
culturalNotes: [],
|
|
303
|
+
notes: "",
|
|
304
|
+
},
|
|
305
|
+
relationships: [],
|
|
306
|
+
scenes: [],
|
|
307
|
+
themes: [],
|
|
308
|
+
synopsis: {
|
|
309
|
+
protagonistAttitude: "",
|
|
310
|
+
coreRelationships: "",
|
|
311
|
+
emotionalTheme: "",
|
|
312
|
+
genreVsRealEmotion: "",
|
|
313
|
+
endingAftertaste: "",
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
saveMemory(memory);
|
|
318
|
+
return memory;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ---------------------------------------------------------------------------
|
|
322
|
+
// Core CRUD
|
|
323
|
+
// ---------------------------------------------------------------------------
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Load the writer memory from disk.
|
|
327
|
+
* @returns The parsed WriterMemory, or null if the file does not exist or is corrupt.
|
|
328
|
+
*/
|
|
329
|
+
export function loadMemory(): WriterMemory | null {
|
|
330
|
+
const memPath = getMemoryPath();
|
|
331
|
+
try {
|
|
332
|
+
if (!existsSync(memPath)) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
const raw = readFileSync(memPath, "utf-8");
|
|
336
|
+
const parsed = JSON.parse(raw) as WriterMemory;
|
|
337
|
+
return parsed;
|
|
338
|
+
} catch (err) {
|
|
339
|
+
console.error(`[writer-memory] Failed to load memory from ${memPath}:`, err);
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Persist memory to disk using an atomic write (write to temp, then rename).
|
|
346
|
+
* Automatically updates the project.updated timestamp and creates a backup
|
|
347
|
+
* of the previous state.
|
|
348
|
+
*
|
|
349
|
+
* @returns true on success, false on failure
|
|
350
|
+
*/
|
|
351
|
+
export function saveMemory(memory: WriterMemory): boolean {
|
|
352
|
+
const memPath = getMemoryPath();
|
|
353
|
+
const memDir = dirname(memPath);
|
|
354
|
+
|
|
355
|
+
try {
|
|
356
|
+
// Ensure directory exists
|
|
357
|
+
if (!existsSync(memDir)) {
|
|
358
|
+
mkdirSync(memDir, { recursive: true });
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Backup existing file before overwriting
|
|
362
|
+
if (existsSync(memPath)) {
|
|
363
|
+
try {
|
|
364
|
+
const existing = readFileSync(memPath, "utf-8");
|
|
365
|
+
const existingMemory = JSON.parse(existing) as WriterMemory;
|
|
366
|
+
createBackup(existingMemory);
|
|
367
|
+
} catch {
|
|
368
|
+
// If backup fails, continue with save anyway
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Update timestamp
|
|
373
|
+
memory.project.updated = now();
|
|
374
|
+
|
|
375
|
+
// Atomic write: write to temp file, then rename
|
|
376
|
+
const tmpPath = memPath + ".tmp";
|
|
377
|
+
const json = JSON.stringify(memory, null, 2);
|
|
378
|
+
writeFileSync(tmpPath, json, "utf-8");
|
|
379
|
+
renameSync(tmpPath, memPath);
|
|
380
|
+
|
|
381
|
+
return true;
|
|
382
|
+
} catch (err) {
|
|
383
|
+
console.error(`[writer-memory] Failed to save memory to ${memPath}:`, err);
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Create a timestamped backup of the given memory state.
|
|
390
|
+
* Old backups beyond MAX_BACKUPS are pruned automatically.
|
|
391
|
+
*
|
|
392
|
+
* @returns The backup file path, or empty string on failure.
|
|
393
|
+
*/
|
|
394
|
+
export function createBackup(memory: WriterMemory): string {
|
|
395
|
+
const backDir = getBackupPath();
|
|
396
|
+
|
|
397
|
+
try {
|
|
398
|
+
if (!existsSync(backDir)) {
|
|
399
|
+
mkdirSync(backDir, { recursive: true });
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
403
|
+
const backupFile = join(backDir, `memory-${ts}.json`);
|
|
404
|
+
const json = JSON.stringify(memory, null, 2);
|
|
405
|
+
writeFileSync(backupFile, json, "utf-8");
|
|
406
|
+
|
|
407
|
+
// Prune old backups
|
|
408
|
+
pruneBackups(backDir);
|
|
409
|
+
|
|
410
|
+
return backupFile;
|
|
411
|
+
} catch (err) {
|
|
412
|
+
console.error("[writer-memory] Failed to create backup:", err);
|
|
413
|
+
return "";
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Remove oldest backup files when count exceeds MAX_BACKUPS.
|
|
419
|
+
*/
|
|
420
|
+
function pruneBackups(backDir: string): void {
|
|
421
|
+
try {
|
|
422
|
+
const files = readdirSync(backDir)
|
|
423
|
+
.filter((f) => f.startsWith("memory-") && f.endsWith(".json"))
|
|
424
|
+
.sort(); // lexicographic sort works because filenames contain ISO timestamps
|
|
425
|
+
|
|
426
|
+
while (files.length > MAX_BACKUPS) {
|
|
427
|
+
const oldest = files.shift()!;
|
|
428
|
+
const fullPath = join(backDir, oldest);
|
|
429
|
+
// Use writeFileSync trick: overwrite then unlink is not needed;
|
|
430
|
+
// simply use fs.unlinkSync
|
|
431
|
+
require("fs").unlinkSync(fullPath);
|
|
432
|
+
}
|
|
433
|
+
} catch {
|
|
434
|
+
// Non-critical; ignore pruning errors
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// ---------------------------------------------------------------------------
|
|
439
|
+
// Memory Stats
|
|
440
|
+
// ---------------------------------------------------------------------------
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Compute aggregate statistics about the memory store.
|
|
444
|
+
*/
|
|
445
|
+
export function getMemoryStats(memory: WriterMemory): MemoryStats {
|
|
446
|
+
const characters = Object.values(memory.characters);
|
|
447
|
+
const totalEmotionPoints = characters.reduce(
|
|
448
|
+
(sum, c) => sum + c.timeline.length,
|
|
449
|
+
0
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
let storageSizeKB = 0;
|
|
453
|
+
try {
|
|
454
|
+
const memPath = getMemoryPath();
|
|
455
|
+
if (existsSync(memPath)) {
|
|
456
|
+
const stat = statSync(memPath);
|
|
457
|
+
storageSizeKB = Math.round((stat.size / 1024) * 100) / 100;
|
|
458
|
+
}
|
|
459
|
+
} catch {
|
|
460
|
+
// If stat fails, leave at 0
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return {
|
|
464
|
+
characterCount: characters.length,
|
|
465
|
+
relationshipCount: memory.relationships.length,
|
|
466
|
+
sceneCount: memory.scenes.length,
|
|
467
|
+
themeCount: memory.themes.length,
|
|
468
|
+
totalEmotionPoints,
|
|
469
|
+
lastUpdated: memory.project.updated,
|
|
470
|
+
storageSizeKB,
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// ---------------------------------------------------------------------------
|
|
475
|
+
// Search / Query Helpers
|
|
476
|
+
// ---------------------------------------------------------------------------
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Find a character by exact name match (case-sensitive).
|
|
480
|
+
* @param name - e.g. "서연"
|
|
481
|
+
*/
|
|
482
|
+
export function findCharacterByName(
|
|
483
|
+
memory: WriterMemory,
|
|
484
|
+
name: string
|
|
485
|
+
): Character | null {
|
|
486
|
+
for (const char of Object.values(memory.characters)) {
|
|
487
|
+
if (char.name === name) {
|
|
488
|
+
return char;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Find a character by one of their aliases.
|
|
496
|
+
* @param alias - e.g. "연이" (nickname for 서연)
|
|
497
|
+
*/
|
|
498
|
+
export function findCharacterByAlias(
|
|
499
|
+
memory: WriterMemory,
|
|
500
|
+
alias: string
|
|
501
|
+
): Character | null {
|
|
502
|
+
for (const char of Object.values(memory.characters)) {
|
|
503
|
+
if (char.aliases.includes(alias)) {
|
|
504
|
+
return char;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Find a relationship between two characters (in either direction).
|
|
512
|
+
* @param char1 - Character ID
|
|
513
|
+
* @param char2 - Character ID
|
|
514
|
+
*/
|
|
515
|
+
export function findRelationship(
|
|
516
|
+
memory: WriterMemory,
|
|
517
|
+
char1: string,
|
|
518
|
+
char2: string
|
|
519
|
+
): Relationship | null {
|
|
520
|
+
return (
|
|
521
|
+
memory.relationships.find(
|
|
522
|
+
(r) =>
|
|
523
|
+
(r.from === char1 && r.to === char2) ||
|
|
524
|
+
(r.from === char2 && r.to === char1)
|
|
525
|
+
) ?? null
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Find a scene by its unique ID.
|
|
531
|
+
*/
|
|
532
|
+
export function findSceneById(
|
|
533
|
+
memory: WriterMemory,
|
|
534
|
+
id: string
|
|
535
|
+
): Scene | null {
|
|
536
|
+
return memory.scenes.find((s) => s.id === id) ?? null;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Find all scenes that include a given character.
|
|
541
|
+
* @param characterId - Character ID to search for
|
|
542
|
+
*/
|
|
543
|
+
export function findScenesByCharacter(
|
|
544
|
+
memory: WriterMemory,
|
|
545
|
+
characterId: string
|
|
546
|
+
): Scene[] {
|
|
547
|
+
return memory.scenes.filter((s) => s.characters.includes(characterId));
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Full-text search across all memory domains.
|
|
552
|
+
* Matches query substring (case-insensitive) against names, descriptions,
|
|
553
|
+
* notes, keywords, and content fields.
|
|
554
|
+
*
|
|
555
|
+
* @param query - Search string, e.g. "그리움" or "카페"
|
|
556
|
+
* @returns Matching results sorted by domain priority
|
|
557
|
+
*/
|
|
558
|
+
export function searchMemory(
|
|
559
|
+
memory: WriterMemory,
|
|
560
|
+
query: string
|
|
561
|
+
): SearchResult[] {
|
|
562
|
+
const results: SearchResult[] = [];
|
|
563
|
+
const q = query.toLowerCase();
|
|
564
|
+
|
|
565
|
+
const matches = (text: string | undefined): boolean =>
|
|
566
|
+
text != null && text.toLowerCase().includes(q);
|
|
567
|
+
|
|
568
|
+
// Search characters
|
|
569
|
+
for (const char of Object.values(memory.characters)) {
|
|
570
|
+
if (
|
|
571
|
+
matches(char.name) ||
|
|
572
|
+
matches(char.arc) ||
|
|
573
|
+
matches(char.tone) ||
|
|
574
|
+
matches(char.attitude) ||
|
|
575
|
+
matches(char.notes) ||
|
|
576
|
+
char.aliases.some(matches) ||
|
|
577
|
+
char.keywords.some(matches)
|
|
578
|
+
) {
|
|
579
|
+
results.push({
|
|
580
|
+
type: "character",
|
|
581
|
+
id: char.id,
|
|
582
|
+
title: char.name,
|
|
583
|
+
relevance: matches(char.name) ? "name" : "content",
|
|
584
|
+
snippet: truncate(
|
|
585
|
+
[char.arc, char.tone, char.attitude].filter(Boolean).join(" | "),
|
|
586
|
+
120
|
|
587
|
+
),
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Search relationships
|
|
593
|
+
for (const rel of memory.relationships) {
|
|
594
|
+
if (matches(rel.dynamic) || matches(rel.notes)) {
|
|
595
|
+
const fromChar = memory.characters[rel.from];
|
|
596
|
+
const toChar = memory.characters[rel.to];
|
|
597
|
+
const fromName = fromChar?.name ?? rel.from;
|
|
598
|
+
const toName = toChar?.name ?? rel.to;
|
|
599
|
+
results.push({
|
|
600
|
+
type: "relationship",
|
|
601
|
+
id: rel.id,
|
|
602
|
+
title: `${fromName} <-> ${toName}`,
|
|
603
|
+
relevance: "content",
|
|
604
|
+
snippet: truncate(rel.dynamic, 120),
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Search scenes
|
|
610
|
+
for (const scene of memory.scenes) {
|
|
611
|
+
if (
|
|
612
|
+
matches(scene.title) ||
|
|
613
|
+
matches(scene.narrationTone) ||
|
|
614
|
+
matches(scene.notes) ||
|
|
615
|
+
scene.emotionTags.some(matches) ||
|
|
616
|
+
scene.cuts.some((c) => matches(c.content))
|
|
617
|
+
) {
|
|
618
|
+
results.push({
|
|
619
|
+
type: "scene",
|
|
620
|
+
id: scene.id,
|
|
621
|
+
title: scene.title,
|
|
622
|
+
relevance: matches(scene.title) ? "title" : "content",
|
|
623
|
+
snippet: truncate(
|
|
624
|
+
scene.cuts
|
|
625
|
+
.slice(0, 2)
|
|
626
|
+
.map((c) => c.content)
|
|
627
|
+
.join(" / "),
|
|
628
|
+
120
|
|
629
|
+
),
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Search themes
|
|
635
|
+
for (const theme of memory.themes) {
|
|
636
|
+
if (
|
|
637
|
+
matches(theme.name) ||
|
|
638
|
+
matches(theme.description) ||
|
|
639
|
+
theme.keywords.some(matches)
|
|
640
|
+
) {
|
|
641
|
+
results.push({
|
|
642
|
+
type: "theme",
|
|
643
|
+
id: theme.id,
|
|
644
|
+
title: theme.name,
|
|
645
|
+
relevance: matches(theme.name) ? "name" : "content",
|
|
646
|
+
snippet: truncate(theme.description, 120),
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Search world
|
|
652
|
+
const world = memory.world;
|
|
653
|
+
if (
|
|
654
|
+
matches(world.name) ||
|
|
655
|
+
matches(world.era) ||
|
|
656
|
+
matches(world.atmosphere) ||
|
|
657
|
+
matches(world.notes) ||
|
|
658
|
+
world.culturalNotes.some(matches) ||
|
|
659
|
+
world.locations.some(
|
|
660
|
+
(l) => matches(l.name) || matches(l.description) || matches(l.atmosphere)
|
|
661
|
+
)
|
|
662
|
+
) {
|
|
663
|
+
// Find the most relevant location if applicable
|
|
664
|
+
const matchedLoc = world.locations.find(
|
|
665
|
+
(l) => matches(l.name) || matches(l.description)
|
|
666
|
+
);
|
|
667
|
+
results.push({
|
|
668
|
+
type: "world",
|
|
669
|
+
id: matchedLoc?.id ?? "world",
|
|
670
|
+
title: matchedLoc?.name ?? (world.name || "World"),
|
|
671
|
+
relevance: "content",
|
|
672
|
+
snippet: truncate(
|
|
673
|
+
matchedLoc?.description ?? world.atmosphere ?? world.notes,
|
|
674
|
+
120
|
|
675
|
+
),
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return results;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/** Truncate a string to maxLen, appending ellipsis if needed. */
|
|
683
|
+
function truncate(text: string | undefined, maxLen: number): string {
|
|
684
|
+
if (!text) return "";
|
|
685
|
+
if (text.length <= maxLen) return text;
|
|
686
|
+
return text.slice(0, maxLen - 1) + "\u2026";
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// ---------------------------------------------------------------------------
|
|
690
|
+
// Validation
|
|
691
|
+
// ---------------------------------------------------------------------------
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Validate the structural integrity of a WriterMemory object.
|
|
695
|
+
* Checks for required fields, dangling references, and data consistency.
|
|
696
|
+
*/
|
|
697
|
+
export function validateMemory(memory: WriterMemory): ValidationResult {
|
|
698
|
+
const errors: string[] = [];
|
|
699
|
+
const warnings: string[] = [];
|
|
700
|
+
|
|
701
|
+
// Version check
|
|
702
|
+
if (memory.version !== "1.0") {
|
|
703
|
+
errors.push(`Unsupported version: "${memory.version}" (expected "1.0")`);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Project meta
|
|
707
|
+
if (!memory.project.name) {
|
|
708
|
+
errors.push("Project name is empty");
|
|
709
|
+
}
|
|
710
|
+
if (!memory.project.genre) {
|
|
711
|
+
warnings.push("Project genre is empty");
|
|
712
|
+
}
|
|
713
|
+
if (!memory.project.created) {
|
|
714
|
+
errors.push("Project created timestamp is missing");
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// Characters
|
|
718
|
+
const charIds = new Set(Object.keys(memory.characters));
|
|
719
|
+
for (const [id, char] of Object.entries(memory.characters)) {
|
|
720
|
+
if (char.id !== id) {
|
|
721
|
+
errors.push(
|
|
722
|
+
`Character key "${id}" does not match character.id "${char.id}"`
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
if (!char.name) {
|
|
726
|
+
errors.push(`Character "${id}" has no name`);
|
|
727
|
+
}
|
|
728
|
+
for (const ep of char.timeline) {
|
|
729
|
+
if (ep.intensity < 1 || ep.intensity > 5) {
|
|
730
|
+
warnings.push(
|
|
731
|
+
`Character "${char.name}" has emotion point with intensity ${ep.intensity} (expected 1-5)`
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
if (ep.sceneId && !memory.scenes.some((s) => s.id === ep.sceneId)) {
|
|
735
|
+
warnings.push(
|
|
736
|
+
`Character "${char.name}" references non-existent scene "${ep.sceneId}" in timeline`
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Relationships
|
|
743
|
+
for (const rel of memory.relationships) {
|
|
744
|
+
if (!charIds.has(rel.from)) {
|
|
745
|
+
errors.push(
|
|
746
|
+
`Relationship "${rel.id}" references non-existent character "${rel.from}"`
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
if (!charIds.has(rel.to)) {
|
|
750
|
+
errors.push(
|
|
751
|
+
`Relationship "${rel.id}" references non-existent character "${rel.to}"`
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
if (rel.from === rel.to) {
|
|
755
|
+
warnings.push(
|
|
756
|
+
`Relationship "${rel.id}" is self-referential (from === to === "${rel.from}")`
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Scenes
|
|
762
|
+
const sceneIds = new Set<string>();
|
|
763
|
+
for (const scene of memory.scenes) {
|
|
764
|
+
if (sceneIds.has(scene.id)) {
|
|
765
|
+
errors.push(`Duplicate scene ID: "${scene.id}"`);
|
|
766
|
+
}
|
|
767
|
+
sceneIds.add(scene.id);
|
|
768
|
+
|
|
769
|
+
for (const charId of scene.characters) {
|
|
770
|
+
if (!charIds.has(charId)) {
|
|
771
|
+
warnings.push(
|
|
772
|
+
`Scene "${scene.title}" references non-existent character "${charId}"`
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (scene.cuts.length === 0) {
|
|
777
|
+
warnings.push(`Scene "${scene.title}" has no cuts`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Themes
|
|
782
|
+
for (const theme of memory.themes) {
|
|
783
|
+
for (const charId of theme.relatedCharacters) {
|
|
784
|
+
if (!charIds.has(charId)) {
|
|
785
|
+
warnings.push(
|
|
786
|
+
`Theme "${theme.name}" references non-existent character "${charId}"`
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
for (const sid of theme.relatedScenes) {
|
|
791
|
+
if (!sceneIds.has(sid)) {
|
|
792
|
+
warnings.push(
|
|
793
|
+
`Theme "${theme.name}" references non-existent scene "${sid}"`
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
return {
|
|
800
|
+
valid: errors.length === 0,
|
|
801
|
+
errors,
|
|
802
|
+
warnings,
|
|
803
|
+
};
|
|
804
|
+
}
|