principles-disciple 1.8.0 → 1.8.2
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/ADVANCED_CONFIG_ZH.md +97 -0
- package/AGENT_INSTALL.md +173 -0
- package/AGENT_INSTALL_EN.md +173 -0
- package/INSTALL.md +256 -0
- package/SKILL.md +63 -0
- package/docs/COMMAND_REFERENCE.md +76 -0
- package/docs/COMMAND_REFERENCE_EN.md +79 -0
- package/esbuild.config.js +75 -0
- package/openclaw.plugin.json +6 -1
- package/package.json +13 -15
- package/scripts/build-web.mjs +46 -0
- package/scripts/install-dependencies.cjs +47 -0
- package/scripts/sync-plugin.mjs +802 -0
- package/scripts/verify-build.mjs +109 -0
- package/src/agents/nocturnal-dreamer.md +152 -0
- package/src/agents/nocturnal-philosopher.md +138 -0
- package/src/agents/nocturnal-reflector.md +126 -0
- package/src/agents/nocturnal-scribe.md +164 -0
- package/src/commands/capabilities.ts +85 -0
- package/{dist/commands/context.js → src/commands/context.ts} +78 -38
- package/src/commands/evolution-status.ts +146 -0
- package/src/commands/export.ts +111 -0
- package/src/commands/focus.ts +533 -0
- package/src/commands/nocturnal-review.ts +311 -0
- package/src/commands/nocturnal-rollout.ts +763 -0
- package/src/commands/nocturnal-train.ts +1002 -0
- package/{dist/commands/pain.js → src/commands/pain.ts} +68 -49
- package/src/commands/principle-rollback.ts +27 -0
- package/{dist/commands/rollback.js → src/commands/rollback.ts} +44 -12
- package/src/commands/samples.ts +60 -0
- package/src/commands/strategy.ts +38 -0
- package/{dist/commands/thinking-os.js → src/commands/thinking-os.ts} +59 -36
- package/src/commands/workflow-debug.ts +128 -0
- package/{dist/config/defaults/runtime.js → src/config/defaults/runtime.ts} +12 -5
- package/src/config/errors.ts +163 -0
- package/{dist/config/index.d.ts → src/config/index.ts} +2 -1
- package/src/constants/diagnostician.ts +66 -0
- package/src/constants/tools.ts +62 -0
- package/src/core/adaptive-thresholds.ts +476 -0
- package/{dist/core/config-service.js → src/core/config-service.ts} +7 -4
- package/{dist/core/config.js → src/core/config.ts} +158 -46
- package/src/core/control-ui-db.ts +435 -0
- package/{dist/core/detection-funnel.js → src/core/detection-funnel.ts} +36 -21
- package/{dist/core/detection-service.js → src/core/detection-service.ts} +7 -4
- package/{dist/core/dictionary-service.js → src/core/dictionary-service.ts} +7 -4
- package/{dist/core/dictionary.js → src/core/dictionary.ts} +57 -34
- package/src/core/empathy-keyword-matcher.ts +327 -0
- package/src/core/empathy-types.ts +218 -0
- package/src/core/event-log.ts +544 -0
- package/src/core/evolution-engine.ts +612 -0
- package/src/core/evolution-logger.ts +353 -0
- package/src/core/evolution-migration.ts +77 -0
- package/src/core/evolution-reducer.ts +731 -0
- package/src/core/evolution-types.ts +456 -0
- package/src/core/external-training-contract.ts +527 -0
- package/src/core/focus-history.ts +1458 -0
- package/src/core/hygiene/tracker.ts +117 -0
- package/{dist/core/init.js → src/core/init.ts} +39 -26
- package/src/core/local-worker-routing.ts +617 -0
- package/{dist/core/migration.js → src/core/migration.ts} +18 -11
- package/src/core/model-deployment-registry.ts +722 -0
- package/src/core/model-training-registry.ts +813 -0
- package/src/core/nocturnal-arbiter.ts +706 -0
- package/src/core/nocturnal-candidate-scoring.ts +392 -0
- package/src/core/nocturnal-compliance.ts +1075 -0
- package/src/core/nocturnal-dataset.ts +668 -0
- package/src/core/nocturnal-executability.ts +428 -0
- package/src/core/nocturnal-export.ts +390 -0
- package/{dist/core/nocturnal-paths.js → src/core/nocturnal-paths.ts} +49 -23
- package/src/core/nocturnal-trajectory-extractor.ts +484 -0
- package/src/core/nocturnal-trinity.ts +1384 -0
- package/src/core/pain.ts +122 -0
- package/{dist/core/path-resolver.js → src/core/path-resolver.ts} +157 -36
- package/{dist/core/paths.js → src/core/paths.ts} +13 -4
- package/src/core/principle-training-state.ts +450 -0
- package/src/core/profile.ts +226 -0
- package/src/core/promotion-gate.ts +822 -0
- package/{dist/core/risk-calculator.js → src/core/risk-calculator.ts} +42 -16
- package/{dist/core/session-tracker.js → src/core/session-tracker.ts} +185 -63
- package/src/core/shadow-observation-registry.ts +534 -0
- package/{dist/core/system-logger.js → src/core/system-logger.ts} +9 -5
- package/src/core/thinking-models.ts +217 -0
- package/src/core/training-program.ts +630 -0
- package/src/core/trajectory-types.ts +243 -0
- package/src/core/trajectory.ts +1673 -0
- package/{dist/core/workspace-context.js → src/core/workspace-context.ts} +57 -32
- package/src/hooks/bash-risk.ts +171 -0
- package/src/hooks/edit-verification.ts +295 -0
- package/src/hooks/gate-block-helper.ts +160 -0
- package/src/hooks/gate.ts +210 -0
- package/src/hooks/gfi-gate.ts +177 -0
- package/src/hooks/lifecycle.ts +326 -0
- package/{dist/hooks/llm.js → src/hooks/llm.ts} +166 -139
- package/src/hooks/message-sanitize.ts +45 -0
- package/src/hooks/pain.ts +384 -0
- package/src/hooks/progressive-trust-gate.ts +174 -0
- package/src/hooks/prompt.ts +920 -0
- package/src/hooks/subagent.ts +207 -0
- package/src/hooks/thinking-checkpoint.ts +73 -0
- package/src/hooks/trajectory-collector.ts +290 -0
- package/src/http/principles-console-route.ts +716 -0
- package/src/i18n/commands.ts +117 -0
- package/src/index.ts +694 -0
- package/src/service/central-database.ts +831 -0
- package/src/service/control-ui-query-service.ts +888 -0
- package/src/service/evolution-query-service.ts +405 -0
- package/src/service/evolution-worker.ts +1646 -0
- package/src/service/health-query-service.ts +836 -0
- package/{dist/service/nocturnal-runtime.js → src/service/nocturnal-runtime.ts} +263 -36
- package/src/service/nocturnal-service.ts +1015 -0
- package/src/service/nocturnal-target-selector.ts +532 -0
- package/src/service/phase3-input-filter.ts +237 -0
- package/src/service/runtime-summary-service.ts +757 -0
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +513 -0
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +603 -0
- package/src/service/subagent-workflow/index.ts +51 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +856 -0
- package/src/service/subagent-workflow/runtime-direct-driver.ts +166 -0
- package/src/service/subagent-workflow/types.ts +378 -0
- package/src/service/subagent-workflow/workflow-store.ts +328 -0
- package/src/service/trajectory-service.ts +15 -0
- package/{dist/tools/critique-prompt.js → src/tools/critique-prompt.ts} +25 -8
- package/src/tools/deep-reflect.ts +349 -0
- package/{dist/tools/model-index.js → src/tools/model-index.ts} +33 -17
- package/src/types/event-types.ts +453 -0
- package/src/types/hygiene-types.ts +31 -0
- package/src/types/principle-tree-schema.ts +244 -0
- package/src/types/runtime-summary.ts +49 -0
- package/src/types.ts +74 -0
- package/src/utils/file-lock.ts +391 -0
- package/{dist/utils/glob-match.js → src/utils/glob-match.ts} +21 -20
- package/{dist/utils/hashing.js → src/utils/hashing.ts} +6 -4
- package/src/utils/io.ts +110 -0
- package/{dist/utils/nlp.js → src/utils/nlp.ts} +19 -12
- package/{dist/utils/plugin-logger.js → src/utils/plugin-logger.ts} +33 -8
- package/src/utils/subagent-probe.ts +94 -0
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +70 -1
- package/templates/pain_settings.json +2 -1
- package/tests/README.md +120 -0
- package/tests/build-artifacts.test.ts +111 -0
- package/tests/commands/evolution-status.test.ts +222 -0
- package/tests/commands/evolver.test.ts +22 -0
- package/tests/commands/export.test.ts +78 -0
- package/tests/commands/nocturnal-review.test.ts +448 -0
- package/tests/commands/nocturnal-train.test.ts +97 -0
- package/tests/commands/pain.test.ts +108 -0
- package/tests/commands/samples.test.ts +65 -0
- package/tests/commands/strategy.test.ts +34 -0
- package/tests/commands/thinking-os.test.ts +88 -0
- package/tests/core/adaptive-thresholds.test.ts +261 -0
- package/tests/core/config-service.test.ts +89 -0
- package/tests/core/config.test.ts +90 -0
- package/tests/core/control-ui-db.test.ts +75 -0
- package/tests/core/core-template-guidance.test.ts +21 -0
- package/tests/core/detection-funnel.test.ts +63 -0
- package/tests/core/detection-service.test.ts +50 -0
- package/tests/core/dictionary-service.test.ts +116 -0
- package/tests/core/dictionary.test.ts +168 -0
- package/tests/core/empathy-keyword-matcher.test.ts +209 -0
- package/tests/core/event-log.test.ts +181 -0
- package/tests/core/evolution-e2e.test.ts +58 -0
- package/tests/core/evolution-engine-gate-integration.test.ts +543 -0
- package/tests/core/evolution-engine.test.ts +562 -0
- package/tests/core/evolution-logger.test.ts +148 -0
- package/tests/core/evolution-migration.test.ts +50 -0
- package/tests/core/evolution-paths.test.ts +21 -0
- package/tests/core/evolution-reducer.detector-metadata.test.ts +602 -0
- package/tests/core/evolution-reducer.test.ts +180 -0
- package/tests/core/evolution-types-loop.test.ts +48 -0
- package/tests/core/evolution-user-stories.e2e.test.ts +249 -0
- package/tests/core/external-training-contract.test.ts +463 -0
- package/tests/core/focus-history.test.ts +682 -0
- package/tests/core/init-flatten.test.ts +69 -0
- package/tests/core/init-refactor.test.ts +87 -0
- package/tests/core/init-v1.3.test.ts +46 -0
- package/tests/core/init.test.ts +190 -0
- package/tests/core/local-worker-routing.test.ts +757 -0
- package/tests/core/migration.test.ts +84 -0
- package/tests/core/model-deployment-registry.test.ts +845 -0
- package/tests/core/model-training-registry.test.ts +889 -0
- package/tests/core/nocturnal-arbiter.test.ts +494 -0
- package/tests/core/nocturnal-candidate-scoring.test.ts +400 -0
- package/tests/core/nocturnal-compliance.test.ts +646 -0
- package/tests/core/nocturnal-dataset.test.ts +892 -0
- package/tests/core/nocturnal-executability.test.ts +357 -0
- package/tests/core/nocturnal-export.test.ts +462 -0
- package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +428 -0
- package/tests/core/nocturnal-trajectory-extractor.test.ts +634 -0
- package/tests/core/nocturnal-trinity.test.ts +953 -0
- package/tests/core/pain.test.ts +33 -0
- package/tests/core/path-resolver.test.ts +57 -0
- package/tests/core/paths-refactor.test.ts +42 -0
- package/tests/core/phase7-rollout-integration.test.ts +477 -0
- package/tests/core/principle-training-state.test.ts +712 -0
- package/tests/core/profile.test.ts +56 -0
- package/tests/core/promotion-gate.test.ts +556 -0
- package/tests/core/risk-calculator.test.ts +168 -0
- package/tests/core/session-tracker.test.ts +191 -0
- package/tests/core/training-program.test.ts +472 -0
- package/tests/core/trajectory.test.ts +265 -0
- package/tests/core/workspace-context-factory.test.ts +18 -0
- package/tests/core/workspace-context.test.ts +134 -0
- package/tests/fixtures/nocturnal-reviewed-subset.json +183 -0
- package/tests/fixtures/production-compatibility.test.ts +147 -0
- package/tests/fixtures/production-mock-generator.ts +282 -0
- package/tests/hooks/bash-risk-integration.test.ts +137 -0
- package/tests/hooks/bash-risk.test.ts +81 -0
- package/tests/hooks/edit-verification.test.ts +678 -0
- package/tests/hooks/gate-edit-verification-p1.test.ts +632 -0
- package/tests/hooks/gate-edit-verification.test.ts +435 -0
- package/tests/hooks/gate-pipeline-integration.test.ts +404 -0
- package/tests/hooks/gate.test.ts +271 -0
- package/tests/hooks/gfi-gate-unit.test.ts +422 -0
- package/tests/hooks/gfi-gate.test.ts +669 -0
- package/tests/hooks/lifecycle.test.ts +248 -0
- package/tests/hooks/llm.test.ts +308 -0
- package/tests/hooks/message-sanitize.test.ts +36 -0
- package/tests/hooks/pain.test.ts +141 -0
- package/tests/hooks/progressive-trust-gate.test.ts +277 -0
- package/tests/hooks/prompt.test.ts +1411 -0
- package/tests/hooks/subagent.test.ts +467 -0
- package/tests/hooks/thinking-gate.test.ts +313 -0
- package/tests/http/principles-console-route.test.ts +140 -0
- package/tests/hygiene-tracker.test.ts +77 -0
- package/tests/index.integration.test.ts +179 -0
- package/tests/index.shadow-routing.integration.test.ts +140 -0
- package/tests/index.test.ts +9 -0
- package/tests/integration/empathy-workflow-integration.test.ts +627 -0
- package/tests/service/control-ui-query-service.test.ts +121 -0
- package/tests/service/empathy-observer-workflow-manager.test.ts +176 -0
- package/tests/service/evolution-worker.test.ts +585 -0
- package/tests/service/nocturnal-runtime.test.ts +470 -0
- package/tests/service/nocturnal-service.test.ts +577 -0
- package/tests/service/nocturnal-target-selector.test.ts +615 -0
- package/tests/service/nocturnal-workflow-manager.test.ts +439 -0
- package/tests/service/phase3-input-filter.test.ts +289 -0
- package/tests/service/runtime-summary-service.test.ts +919 -0
- package/tests/task-compliance.test.ts +166 -0
- package/tests/test-utils.ts +48 -0
- package/tests/tools/critique-prompt.test.ts +260 -0
- package/tests/tools/deep-reflect.test.ts +232 -0
- package/tests/tools/model-index.test.ts +246 -0
- package/tests/ui/app.test.tsx +114 -0
- package/tests/utils/file-lock.test.ts +407 -0
- package/tests/utils/hashing.test.ts +32 -0
- package/tests/utils/io.test.ts +39 -0
- package/tests/utils/nlp.test.ts +53 -0
- package/tests/utils/plugin-logger.test.ts +156 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/ui/src/App.tsx +45 -0
- package/ui/src/api.ts +216 -0
- package/ui/src/charts.tsx +586 -0
- package/ui/src/components/ErrorState.tsx +6 -0
- package/ui/src/components/Loading.tsx +13 -0
- package/ui/src/components/ProtectedRoute.tsx +12 -0
- package/ui/src/components/Shell.tsx +91 -0
- package/ui/src/components/WorkspaceConfig.tsx +146 -0
- package/ui/src/components/index.ts +5 -0
- package/ui/src/context/auth.tsx +80 -0
- package/ui/src/context/theme.tsx +66 -0
- package/ui/src/hooks/useAutoRefresh.ts +39 -0
- package/ui/src/i18n/ui.ts +363 -0
- package/ui/src/main.tsx +16 -0
- package/ui/src/pages/EvolutionPage.tsx +352 -0
- package/ui/src/pages/FeedbackPage.tsx +140 -0
- package/ui/src/pages/GateMonitorPage.tsx +136 -0
- package/ui/src/pages/LoginPage.tsx +88 -0
- package/ui/src/pages/OverviewPage.tsx +238 -0
- package/ui/src/pages/SamplesPage.tsx +174 -0
- package/ui/src/pages/ThinkingModelsPage.tsx +127 -0
- package/ui/src/styles.css +1661 -0
- package/ui/src/types.ts +368 -0
- package/ui/src/utils/format.ts +15 -0
- package/vitest.config.ts +23 -0
- package/dist/commands/capabilities.d.ts +0 -3
- package/dist/commands/capabilities.js +0 -73
- package/dist/commands/context.d.ts +0 -5
- package/dist/commands/evolution-status.d.ts +0 -4
- package/dist/commands/evolution-status.js +0 -117
- package/dist/commands/evolver.d.ts +0 -9
- package/dist/commands/evolver.js +0 -26
- package/dist/commands/export.d.ts +0 -2
- package/dist/commands/export.js +0 -98
- package/dist/commands/focus.d.ts +0 -14
- package/dist/commands/focus.js +0 -457
- package/dist/commands/nocturnal-review.d.ts +0 -24
- package/dist/commands/nocturnal-review.js +0 -265
- package/dist/commands/nocturnal-rollout.d.ts +0 -27
- package/dist/commands/nocturnal-rollout.js +0 -671
- package/dist/commands/nocturnal-train.d.ts +0 -25
- package/dist/commands/nocturnal-train.js +0 -919
- package/dist/commands/pain.d.ts +0 -5
- package/dist/commands/principle-rollback.d.ts +0 -4
- package/dist/commands/principle-rollback.js +0 -22
- package/dist/commands/rollback.d.ts +0 -19
- package/dist/commands/samples.d.ts +0 -2
- package/dist/commands/samples.js +0 -55
- package/dist/commands/strategy.d.ts +0 -3
- package/dist/commands/strategy.js +0 -29
- package/dist/commands/thinking-os.d.ts +0 -2
- package/dist/config/defaults/runtime.d.ts +0 -40
- package/dist/config/errors.d.ts +0 -84
- package/dist/config/errors.js +0 -94
- package/dist/config/index.js +0 -7
- package/dist/constants/diagnostician.d.ts +0 -12
- package/dist/constants/diagnostician.js +0 -56
- package/dist/constants/tools.d.ts +0 -17
- package/dist/constants/tools.js +0 -54
- package/dist/core/adaptive-thresholds.d.ts +0 -186
- package/dist/core/adaptive-thresholds.js +0 -300
- package/dist/core/config-service.d.ts +0 -15
- package/dist/core/config.d.ts +0 -127
- package/dist/core/control-ui-db.d.ts +0 -95
- package/dist/core/control-ui-db.js +0 -292
- package/dist/core/detection-funnel.d.ts +0 -33
- package/dist/core/detection-service.d.ts +0 -15
- package/dist/core/dictionary-service.d.ts +0 -15
- package/dist/core/dictionary.d.ts +0 -38
- package/dist/core/event-log.d.ts +0 -82
- package/dist/core/event-log.js +0 -463
- package/dist/core/evolution-engine.d.ts +0 -118
- package/dist/core/evolution-engine.js +0 -464
- package/dist/core/evolution-logger.d.ts +0 -137
- package/dist/core/evolution-logger.js +0 -256
- package/dist/core/evolution-migration.d.ts +0 -5
- package/dist/core/evolution-migration.js +0 -65
- package/dist/core/evolution-reducer.d.ts +0 -98
- package/dist/core/evolution-reducer.js +0 -465
- package/dist/core/evolution-types.d.ts +0 -287
- package/dist/core/evolution-types.js +0 -78
- package/dist/core/external-training-contract.d.ts +0 -276
- package/dist/core/external-training-contract.js +0 -269
- package/dist/core/focus-history.d.ts +0 -210
- package/dist/core/focus-history.js +0 -1185
- package/dist/core/hygiene/tracker.d.ts +0 -22
- package/dist/core/hygiene/tracker.js +0 -106
- package/dist/core/init.d.ts +0 -12
- package/dist/core/local-worker-routing.d.ts +0 -175
- package/dist/core/local-worker-routing.js +0 -525
- package/dist/core/migration.d.ts +0 -6
- package/dist/core/model-deployment-registry.d.ts +0 -218
- package/dist/core/model-deployment-registry.js +0 -503
- package/dist/core/model-training-registry.d.ts +0 -295
- package/dist/core/model-training-registry.js +0 -475
- package/dist/core/nocturnal-arbiter.d.ts +0 -159
- package/dist/core/nocturnal-arbiter.js +0 -534
- package/dist/core/nocturnal-candidate-scoring.d.ts +0 -137
- package/dist/core/nocturnal-candidate-scoring.js +0 -266
- package/dist/core/nocturnal-compliance.d.ts +0 -175
- package/dist/core/nocturnal-compliance.js +0 -824
- package/dist/core/nocturnal-dataset.d.ts +0 -224
- package/dist/core/nocturnal-dataset.js +0 -443
- package/dist/core/nocturnal-executability.d.ts +0 -85
- package/dist/core/nocturnal-executability.js +0 -331
- package/dist/core/nocturnal-export.d.ts +0 -124
- package/dist/core/nocturnal-export.js +0 -275
- package/dist/core/nocturnal-paths.d.ts +0 -124
- package/dist/core/nocturnal-trajectory-extractor.d.ts +0 -242
- package/dist/core/nocturnal-trajectory-extractor.js +0 -307
- package/dist/core/nocturnal-trinity.d.ts +0 -311
- package/dist/core/nocturnal-trinity.js +0 -880
- package/dist/core/pain.d.ts +0 -4
- package/dist/core/pain.js +0 -70
- package/dist/core/path-resolver.d.ts +0 -46
- package/dist/core/paths.d.ts +0 -65
- package/dist/core/principle-training-state.d.ts +0 -121
- package/dist/core/principle-training-state.js +0 -321
- package/dist/core/profile.d.ts +0 -62
- package/dist/core/profile.js +0 -210
- package/dist/core/promotion-gate.d.ts +0 -238
- package/dist/core/promotion-gate.js +0 -529
- package/dist/core/risk-calculator.d.ts +0 -22
- package/dist/core/session-tracker.d.ts +0 -99
- package/dist/core/shadow-observation-registry.d.ts +0 -217
- package/dist/core/shadow-observation-registry.js +0 -308
- package/dist/core/system-logger.d.ts +0 -8
- package/dist/core/thinking-models.d.ts +0 -38
- package/dist/core/thinking-models.js +0 -170
- package/dist/core/training-program.d.ts +0 -233
- package/dist/core/training-program.js +0 -433
- package/dist/core/trajectory.d.ts +0 -411
- package/dist/core/trajectory.js +0 -1307
- package/dist/core/workspace-context.d.ts +0 -71
- package/dist/hooks/bash-risk.d.ts +0 -57
- package/dist/hooks/bash-risk.js +0 -137
- package/dist/hooks/edit-verification.d.ts +0 -62
- package/dist/hooks/edit-verification.js +0 -256
- package/dist/hooks/gate-block-helper.d.ts +0 -44
- package/dist/hooks/gate-block-helper.js +0 -119
- package/dist/hooks/gate.d.ts +0 -24
- package/dist/hooks/gate.js +0 -173
- package/dist/hooks/gfi-gate.d.ts +0 -40
- package/dist/hooks/gfi-gate.js +0 -113
- package/dist/hooks/lifecycle.d.ts +0 -5
- package/dist/hooks/lifecycle.js +0 -284
- package/dist/hooks/llm.d.ts +0 -12
- package/dist/hooks/message-sanitize.d.ts +0 -3
- package/dist/hooks/message-sanitize.js +0 -37
- package/dist/hooks/pain.d.ts +0 -5
- package/dist/hooks/pain.js +0 -301
- package/dist/hooks/progressive-trust-gate.d.ts +0 -51
- package/dist/hooks/progressive-trust-gate.js +0 -89
- package/dist/hooks/prompt.d.ts +0 -47
- package/dist/hooks/prompt.js +0 -884
- package/dist/hooks/subagent.d.ts +0 -10
- package/dist/hooks/subagent.js +0 -387
- package/dist/hooks/thinking-checkpoint.d.ts +0 -37
- package/dist/hooks/thinking-checkpoint.js +0 -51
- package/dist/hooks/trajectory-collector.d.ts +0 -32
- package/dist/hooks/trajectory-collector.js +0 -256
- package/dist/http/principles-console-route.d.ts +0 -9
- package/dist/http/principles-console-route.js +0 -567
- package/dist/i18n/commands.d.ts +0 -26
- package/dist/i18n/commands.js +0 -116
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -581
- package/dist/service/central-database.d.ts +0 -104
- package/dist/service/central-database.js +0 -649
- package/dist/service/control-ui-query-service.d.ts +0 -221
- package/dist/service/control-ui-query-service.js +0 -543
- package/dist/service/empathy-observer-manager.d.ts +0 -52
- package/dist/service/empathy-observer-manager.js +0 -229
- package/dist/service/evolution-query-service.d.ts +0 -155
- package/dist/service/evolution-query-service.js +0 -258
- package/dist/service/evolution-worker.d.ts +0 -101
- package/dist/service/evolution-worker.js +0 -974
- package/dist/service/nocturnal-runtime.d.ts +0 -183
- package/dist/service/nocturnal-service.d.ts +0 -163
- package/dist/service/nocturnal-service.js +0 -787
- package/dist/service/nocturnal-target-selector.d.ts +0 -145
- package/dist/service/nocturnal-target-selector.js +0 -315
- package/dist/service/phase3-input-filter.d.ts +0 -73
- package/dist/service/phase3-input-filter.js +0 -172
- package/dist/service/runtime-summary-service.d.ts +0 -122
- package/dist/service/runtime-summary-service.js +0 -485
- package/dist/service/trajectory-service.d.ts +0 -2
- package/dist/service/trajectory-service.js +0 -15
- package/dist/tools/critique-prompt.d.ts +0 -14
- package/dist/tools/deep-reflect.d.ts +0 -39
- package/dist/tools/deep-reflect.js +0 -350
- package/dist/tools/model-index.d.ts +0 -9
- package/dist/types/event-types.d.ts +0 -306
- package/dist/types/event-types.js +0 -106
- package/dist/types/hygiene-types.d.ts +0 -20
- package/dist/types/hygiene-types.js +0 -12
- package/dist/types/runtime-summary.d.ts +0 -47
- package/dist/types/runtime-summary.js +0 -1
- package/dist/types.d.ts +0 -50
- package/dist/types.js +0 -22
- package/dist/utils/file-lock.d.ts +0 -71
- package/dist/utils/file-lock.js +0 -309
- package/dist/utils/glob-match.d.ts +0 -28
- package/dist/utils/hashing.d.ts +0 -9
- package/dist/utils/io.d.ts +0 -6
- package/dist/utils/io.js +0 -106
- package/dist/utils/nlp.d.ts +0 -9
- package/dist/utils/plugin-logger.d.ts +0 -39
- package/dist/utils/subagent-probe.d.ts +0 -34
- package/dist/utils/subagent-probe.js +0 -81
|
@@ -1,1185 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CURRENT_FOCUS 历史版本管理
|
|
3
|
-
*
|
|
4
|
-
* 功能:
|
|
5
|
-
* - 压缩时备份当前版本到历史目录
|
|
6
|
-
* - 清理过期历史版本
|
|
7
|
-
* - 读取历史版本(用于 full 模式)
|
|
8
|
-
* - 工作记忆提取与合并(压缩后恢复上下文)
|
|
9
|
-
*/
|
|
10
|
-
import * as fs from 'fs';
|
|
11
|
-
import * as path from 'path';
|
|
12
|
-
/**
|
|
13
|
-
* 简单的日志记录器
|
|
14
|
-
*/
|
|
15
|
-
function logError(message, error) {
|
|
16
|
-
const timestamp = new Date().toISOString();
|
|
17
|
-
const errorStr = error instanceof Error ? error.message : String(error);
|
|
18
|
-
console.error(`[focus-history] ${timestamp} ERROR: ${message}${errorStr ? ' - ' + errorStr : ''}`);
|
|
19
|
-
}
|
|
20
|
-
/** 历史版本保留数量 */
|
|
21
|
-
const MAX_HISTORY_FILES = 10;
|
|
22
|
-
/** full 模式读取的历史版本数 */
|
|
23
|
-
const FULL_MODE_HISTORY_COUNT = 3;
|
|
24
|
-
/**
|
|
25
|
-
* 获取历史目录路径
|
|
26
|
-
*/
|
|
27
|
-
export function getHistoryDir(focusPath) {
|
|
28
|
-
return path.join(path.dirname(focusPath), '.history');
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* 从 CURRENT_FOCUS.md 提取版本号
|
|
32
|
-
* 支持整数和小数版本(如 v1, v1.1, v1.2)
|
|
33
|
-
*/
|
|
34
|
-
export function extractVersion(content) {
|
|
35
|
-
const match = content.match(/\*\*版本\*\*:\s*v([\d.]+)/i);
|
|
36
|
-
return match ? match[1] : '1';
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* 从 CURRENT_FOCUS.md 提取更新日期
|
|
40
|
-
*/
|
|
41
|
-
export function extractDate(content) {
|
|
42
|
-
const match = content.match(/\*\*更新\*\*:\s*(\d{4}-\d{2}-\d{2})/);
|
|
43
|
-
return match ? match[1] : new Date().toISOString().split('T')[0];
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* 备份当前版本到历史目录
|
|
47
|
-
*
|
|
48
|
-
* @param focusPath CURRENT_FOCUS.md 的完整路径
|
|
49
|
-
* @param content 当前内容
|
|
50
|
-
* @returns 备份文件路径,失败返回 null
|
|
51
|
-
*/
|
|
52
|
-
export function backupToHistory(focusPath, content) {
|
|
53
|
-
try {
|
|
54
|
-
const historyDir = getHistoryDir(focusPath);
|
|
55
|
-
// 确保历史目录存在
|
|
56
|
-
if (!fs.existsSync(historyDir)) {
|
|
57
|
-
try {
|
|
58
|
-
fs.mkdirSync(historyDir, { recursive: true });
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
logError(`Failed to create history directory: ${historyDir}`, error);
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
const version = extractVersion(content);
|
|
66
|
-
const date = extractDate(content);
|
|
67
|
-
// 使用时间戳作为唯一标识,避免同名冲突
|
|
68
|
-
const timestamp = Date.now();
|
|
69
|
-
const backupName = `CURRENT_FOCUS.v${version}.${date}.${timestamp}.md`;
|
|
70
|
-
const backupPath = path.join(historyDir, backupName);
|
|
71
|
-
// 如果备份已存在,跳过
|
|
72
|
-
if (fs.existsSync(backupPath)) {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
try {
|
|
76
|
-
fs.writeFileSync(backupPath, content, 'utf-8');
|
|
77
|
-
return backupPath;
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
logError(`Failed to write backup file: ${backupPath}`, error);
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
logError('Unexpected error in backupToHistory', error);
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* 清理过期历史版本
|
|
91
|
-
*
|
|
92
|
-
* @param focusPath CURRENT_FOCUS.md 的完整路径
|
|
93
|
-
* @param maxFiles 最大保留数量
|
|
94
|
-
*/
|
|
95
|
-
export function cleanupHistory(focusPath, maxFiles = MAX_HISTORY_FILES) {
|
|
96
|
-
try {
|
|
97
|
-
const historyDir = getHistoryDir(focusPath);
|
|
98
|
-
if (!fs.existsSync(historyDir)) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
// 获取所有历史文件并按修改时间排序(最新的在前)
|
|
102
|
-
const files = fs.readdirSync(historyDir)
|
|
103
|
-
.filter(f => f.startsWith('CURRENT_FOCUS.v') && f.endsWith('.md'))
|
|
104
|
-
.map(f => ({
|
|
105
|
-
name: f,
|
|
106
|
-
path: path.join(historyDir, f),
|
|
107
|
-
mtime: fs.statSync(path.join(historyDir, f)).mtime.getTime()
|
|
108
|
-
}))
|
|
109
|
-
.sort((a, b) => b.mtime - a.mtime);
|
|
110
|
-
// 删除超出数量的文件
|
|
111
|
-
const toDelete = files.slice(maxFiles);
|
|
112
|
-
for (const file of toDelete) {
|
|
113
|
-
try {
|
|
114
|
-
fs.unlinkSync(file.path);
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
// 单个文件删除失败不应中断整个清理过程
|
|
118
|
-
logError(`Failed to delete history file: ${file.path}`, error);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
logError('Unexpected error in cleanupHistory', error);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* 获取历史版本列表
|
|
128
|
-
*
|
|
129
|
-
* @param focusPath CURRENT_FOCUS.md 的完整路径
|
|
130
|
-
* @param count 获取数量
|
|
131
|
-
* @returns 历史版本内容数组(按时间倒序)
|
|
132
|
-
*/
|
|
133
|
-
export function getHistoryVersions(focusPath, count = FULL_MODE_HISTORY_COUNT) {
|
|
134
|
-
const historyDir = getHistoryDir(focusPath);
|
|
135
|
-
if (!fs.existsSync(historyDir)) {
|
|
136
|
-
return [];
|
|
137
|
-
}
|
|
138
|
-
// 获取所有历史文件并按修改时间排序(最新的在前)
|
|
139
|
-
const files = fs.readdirSync(historyDir)
|
|
140
|
-
.filter(f => f.startsWith('CURRENT_FOCUS.v') && f.endsWith('.md'))
|
|
141
|
-
.map(f => ({
|
|
142
|
-
path: path.join(historyDir, f),
|
|
143
|
-
mtime: fs.statSync(path.join(historyDir, f)).mtime.getTime()
|
|
144
|
-
}))
|
|
145
|
-
.sort((a, b) => b.mtime - a.mtime)
|
|
146
|
-
.slice(0, count);
|
|
147
|
-
return files.map(f => fs.readFileSync(f.path, 'utf-8'));
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* 压缩 CURRENT_FOCUS.md
|
|
151
|
-
*
|
|
152
|
-
* @param focusPath CURRENT_FOCUS.md 的完整路径
|
|
153
|
-
* @param newContent 新内容
|
|
154
|
-
* @returns 压缩后的信息
|
|
155
|
-
*/
|
|
156
|
-
export function compressFocus(focusPath, newContent) {
|
|
157
|
-
// 读取当前内容
|
|
158
|
-
let oldContent = '';
|
|
159
|
-
if (fs.existsSync(focusPath)) {
|
|
160
|
-
oldContent = fs.readFileSync(focusPath, 'utf-8');
|
|
161
|
-
}
|
|
162
|
-
// 备份当前版本
|
|
163
|
-
const backupPath = oldContent ? backupToHistory(focusPath, oldContent) : null;
|
|
164
|
-
// 递增版本号(支持小数版本)
|
|
165
|
-
const oldVersion = extractVersion(oldContent);
|
|
166
|
-
// 解析版本号并递增
|
|
167
|
-
const versionParts = oldVersion.split('.');
|
|
168
|
-
const majorVersion = parseInt(versionParts[0], 10) || 1;
|
|
169
|
-
const newVersion = `${majorVersion + 1}`;
|
|
170
|
-
const today = new Date().toISOString().split('T')[0];
|
|
171
|
-
// 更新版本号和日期
|
|
172
|
-
const updatedContent = newContent
|
|
173
|
-
.replace(/\*\*版本\*\*:\s*v[\d.]+/i, `**版本**: v${newVersion}`)
|
|
174
|
-
.replace(/\*\*更新\*\*:\s*\d{4}-\d{2}-\d{2}/, `**更新**: ${today}`);
|
|
175
|
-
// 写入新内容
|
|
176
|
-
fs.writeFileSync(focusPath, updatedContent, 'utf-8');
|
|
177
|
-
// 清理过期历史
|
|
178
|
-
const historyDir = getHistoryDir(focusPath);
|
|
179
|
-
const beforeCount = fs.existsSync(historyDir)
|
|
180
|
-
? fs.readdirSync(historyDir).filter(f => f.startsWith('CURRENT_FOCUS.v')).length
|
|
181
|
-
: 0;
|
|
182
|
-
cleanupHistory(focusPath);
|
|
183
|
-
const afterCount = fs.existsSync(historyDir)
|
|
184
|
-
? fs.readdirSync(historyDir).filter(f => f.startsWith('CURRENT_FOCUS.v')).length
|
|
185
|
-
: 0;
|
|
186
|
-
return {
|
|
187
|
-
backupPath,
|
|
188
|
-
cleanedCount: beforeCount - afterCount
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* 智能摘要提取
|
|
193
|
-
*
|
|
194
|
-
* 优先提取关键章节,确保不丢失重要信息
|
|
195
|
-
* 对于非结构化内容,回退到简单的行截取
|
|
196
|
-
*
|
|
197
|
-
* @param content CURRENT_FOCUS.md 内容
|
|
198
|
-
* @param maxLines 最大行数
|
|
199
|
-
*/
|
|
200
|
-
export function extractSummary(content, maxLines = 30) {
|
|
201
|
-
const lines = content.split('\n');
|
|
202
|
-
const sections = {
|
|
203
|
-
header: [], // 标题和元数据
|
|
204
|
-
snapshot: [], // 状态快照
|
|
205
|
-
current: [], // 当前任务
|
|
206
|
-
nextSteps: [], // 下一步
|
|
207
|
-
reference: [] // 参考
|
|
208
|
-
};
|
|
209
|
-
let currentSection = 'header';
|
|
210
|
-
let hasStructuredSections = false;
|
|
211
|
-
for (const line of lines) {
|
|
212
|
-
// 识别章节(使用更宽松的匹配,支持不同格式)
|
|
213
|
-
const trimmedLine = line.trim();
|
|
214
|
-
// 使用正则匹配,支持 h1-h3 和多种格式
|
|
215
|
-
if (/^#{1,3}\s*.*状态快照|📍/.test(trimmedLine)) {
|
|
216
|
-
currentSection = 'snapshot';
|
|
217
|
-
hasStructuredSections = true;
|
|
218
|
-
}
|
|
219
|
-
else if (/^#{1,3}\s*.*当前任务|🔄/.test(trimmedLine)) {
|
|
220
|
-
currentSection = 'current';
|
|
221
|
-
hasStructuredSections = true;
|
|
222
|
-
}
|
|
223
|
-
else if (/^#{1,3}\s*.*下一步|➡️/.test(trimmedLine)) {
|
|
224
|
-
currentSection = 'nextSteps';
|
|
225
|
-
hasStructuredSections = true;
|
|
226
|
-
}
|
|
227
|
-
else if (/^#{1,3}\s*.*参考|📎/.test(trimmedLine)) {
|
|
228
|
-
currentSection = 'reference';
|
|
229
|
-
hasStructuredSections = true;
|
|
230
|
-
}
|
|
231
|
-
else if (trimmedLine === '---') {
|
|
232
|
-
continue; // 跳过分隔线
|
|
233
|
-
}
|
|
234
|
-
else if (line.startsWith('<!--')) {
|
|
235
|
-
continue; // 跳过注释
|
|
236
|
-
}
|
|
237
|
-
sections[currentSection].push(line);
|
|
238
|
-
}
|
|
239
|
-
// 如果没有结构化章节,回退到简单的行截取
|
|
240
|
-
if (!hasStructuredSections) {
|
|
241
|
-
const result = lines.slice(0, maxLines);
|
|
242
|
-
if (lines.length > maxLines) {
|
|
243
|
-
result.push('');
|
|
244
|
-
result.push('...[truncated, see CURRENT_FOCUS.md for full context]');
|
|
245
|
-
}
|
|
246
|
-
return result.join('\n');
|
|
247
|
-
}
|
|
248
|
-
// 按优先级拼接
|
|
249
|
-
const result = [
|
|
250
|
-
...sections.header.slice(0, 5), // 标题 + 元数据
|
|
251
|
-
'',
|
|
252
|
-
'---',
|
|
253
|
-
'',
|
|
254
|
-
...sections.snapshot.slice(0, 10), // 状态快照
|
|
255
|
-
'',
|
|
256
|
-
...sections.nextSteps.slice(0, 10), // 下一步(优先级高)
|
|
257
|
-
'',
|
|
258
|
-
...sections.current.slice(0, 15), // 当前任务
|
|
259
|
-
];
|
|
260
|
-
// 限制总行数
|
|
261
|
-
const trimmed = result.slice(0, maxLines);
|
|
262
|
-
if (result.length > maxLines) {
|
|
263
|
-
trimmed.push('');
|
|
264
|
-
trimmed.push('...[truncated, see CURRENT_FOCUS.md for full context]');
|
|
265
|
-
}
|
|
266
|
-
return trimmed.join('\n');
|
|
267
|
-
}
|
|
268
|
-
// ============================================================================
|
|
269
|
-
// 工作记忆管理
|
|
270
|
-
// ============================================================================
|
|
271
|
-
/** Working Memory 章节标记 */
|
|
272
|
-
const WORKING_MEMORY_SECTION = '## 🧠 Working Memory';
|
|
273
|
-
/** 最大保留的文件记录数 */
|
|
274
|
-
const MAX_ARTIFACTS = 20;
|
|
275
|
-
/** 最大保留的问题数 */
|
|
276
|
-
const MAX_PROBLEMS = 5;
|
|
277
|
-
/** 最大保留下一步行动数 */
|
|
278
|
-
const MAX_NEXT_ACTIONS = 5;
|
|
279
|
-
/**
|
|
280
|
-
* 从会话消息中提取工作记忆
|
|
281
|
-
*
|
|
282
|
-
* @param messages 会话消息数组(OpenClaw 格式)
|
|
283
|
-
* @param workspaceDir 工作区目录(用于生成相对路径)
|
|
284
|
-
* @returns 提取的工作记忆快照
|
|
285
|
-
*/
|
|
286
|
-
export function extractWorkingMemory(messages, workspaceDir) {
|
|
287
|
-
const snapshot = {
|
|
288
|
-
lastUpdated: new Date().toISOString(),
|
|
289
|
-
artifacts: [],
|
|
290
|
-
activeProblems: [],
|
|
291
|
-
nextActions: []
|
|
292
|
-
};
|
|
293
|
-
// 只处理最近的 assistant 消息
|
|
294
|
-
const recentMessages = messages
|
|
295
|
-
.filter(m => m.role === 'assistant')
|
|
296
|
-
.slice(-10);
|
|
297
|
-
for (const msg of recentMessages) {
|
|
298
|
-
let text = '';
|
|
299
|
-
const toolUses = [];
|
|
300
|
-
if (typeof msg.content === 'string') {
|
|
301
|
-
text = msg.content;
|
|
302
|
-
}
|
|
303
|
-
else if (Array.isArray(msg.content)) {
|
|
304
|
-
const textParts = [];
|
|
305
|
-
for (const c of msg.content) {
|
|
306
|
-
if (!c || typeof c !== 'object')
|
|
307
|
-
continue;
|
|
308
|
-
const obj = c;
|
|
309
|
-
// 提取文本内容
|
|
310
|
-
if (obj.type === 'text' && typeof obj.text === 'string') {
|
|
311
|
-
textParts.push(obj.text);
|
|
312
|
-
}
|
|
313
|
-
// 提取工具调用(关键:文件操作在这里!)
|
|
314
|
-
if (obj.type === 'tool_use' && typeof obj.name === 'string' && typeof obj.input === 'object') {
|
|
315
|
-
toolUses.push({
|
|
316
|
-
name: obj.name,
|
|
317
|
-
input: obj.input
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
text = textParts.join('\n');
|
|
322
|
-
}
|
|
323
|
-
// 从工具调用中提取文件路径(这是最可靠的方式)
|
|
324
|
-
for (const toolUse of toolUses) {
|
|
325
|
-
if (['write_file', 'replace', 'create_file'].includes(toolUse.name)) {
|
|
326
|
-
const filePath = toolUse.input.file_path || toolUse.input.absolute_path || toolUse.input.path;
|
|
327
|
-
if (typeof filePath === 'string' && filePath.trim()) {
|
|
328
|
-
// 跳过不需要的文件
|
|
329
|
-
if (filePath.includes('node_modules') ||
|
|
330
|
-
filePath.endsWith('.d.ts') ||
|
|
331
|
-
filePath.includes('.config.')) {
|
|
332
|
-
continue;
|
|
333
|
-
}
|
|
334
|
-
// 生成相对路径
|
|
335
|
-
const displayPath = workspaceDir && filePath.startsWith(workspaceDir)
|
|
336
|
-
? path.relative(workspaceDir, filePath)
|
|
337
|
-
: filePath;
|
|
338
|
-
// 判断操作类型
|
|
339
|
-
const action = toolUse.name === 'write_file' || toolUse.name === 'create_file' ? 'created' : 'modified';
|
|
340
|
-
// 尝试从文本中提取描述
|
|
341
|
-
const description = extractDescription(text, filePath);
|
|
342
|
-
snapshot.artifacts.push({
|
|
343
|
-
path: displayPath,
|
|
344
|
-
action,
|
|
345
|
-
description
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
if (!text)
|
|
351
|
-
continue;
|
|
352
|
-
// 从文本中提取文件操作(备用方式)
|
|
353
|
-
extractFileArtifacts(text, snapshot.artifacts, workspaceDir);
|
|
354
|
-
// 提取问题
|
|
355
|
-
extractProblems(text, snapshot.activeProblems);
|
|
356
|
-
// 提取下一步
|
|
357
|
-
extractNextActions(text, snapshot.nextActions);
|
|
358
|
-
}
|
|
359
|
-
// 去重和限制数量
|
|
360
|
-
snapshot.artifacts = deduplicateArtifacts(snapshot.artifacts).slice(-MAX_ARTIFACTS);
|
|
361
|
-
snapshot.activeProblems = snapshot.activeProblems.slice(-MAX_PROBLEMS);
|
|
362
|
-
snapshot.nextActions = snapshot.nextActions.slice(-MAX_NEXT_ACTIONS);
|
|
363
|
-
return snapshot;
|
|
364
|
-
}
|
|
365
|
-
/**
|
|
366
|
-
* 从文本中提取文件操作记录
|
|
367
|
-
*/
|
|
368
|
-
function extractFileArtifacts(text, artifacts, workspaceDir) {
|
|
369
|
-
// 匹配 write_file, replace 工具调用
|
|
370
|
-
// 格式: file_path: "/path/to/file" 或 absolute_path: "/path/to/file"
|
|
371
|
-
const filePathRegex = /(?:file_path|absolute_path)["']?\s*[:=]\s*["']([^"']+\.(ts|js|json|md|yaml|yml|py|sh|mjs|cjs))["']/gi;
|
|
372
|
-
let match;
|
|
373
|
-
while ((match = filePathRegex.exec(text)) !== null) {
|
|
374
|
-
const filePath = match[1];
|
|
375
|
-
// 跳过 node_modules 和配置文件
|
|
376
|
-
if (filePath.includes('node_modules') ||
|
|
377
|
-
filePath.endsWith('.d.ts') ||
|
|
378
|
-
filePath.includes('.config.')) {
|
|
379
|
-
continue;
|
|
380
|
-
}
|
|
381
|
-
// 生成相对路径(如果有 workspaceDir)
|
|
382
|
-
const displayPath = workspaceDir && filePath.startsWith(workspaceDir)
|
|
383
|
-
? path.relative(workspaceDir, filePath)
|
|
384
|
-
: filePath;
|
|
385
|
-
// 判断操作类型 - 根据上下文关键词
|
|
386
|
-
let action = 'modified';
|
|
387
|
-
const contextBefore = text.substring(Math.max(0, match.index - 200), match.index);
|
|
388
|
-
if (contextBefore.toLowerCase().includes('created') ||
|
|
389
|
-
contextBefore.includes('新建') ||
|
|
390
|
-
contextBefore.includes('创建')) {
|
|
391
|
-
action = 'created';
|
|
392
|
-
}
|
|
393
|
-
else if (contextBefore.toLowerCase().includes('deleted') ||
|
|
394
|
-
contextBefore.includes('删除')) {
|
|
395
|
-
action = 'deleted';
|
|
396
|
-
}
|
|
397
|
-
// 尝试提取描述(从附近的文本)
|
|
398
|
-
const description = extractDescription(text, filePath);
|
|
399
|
-
artifacts.push({
|
|
400
|
-
path: displayPath,
|
|
401
|
-
action,
|
|
402
|
-
description
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
// 匹配更通用的文件路径格式(如代码块中的路径)
|
|
406
|
-
// 格式: `path/to/file.ts` 或 "path/to/file.ts"
|
|
407
|
-
// 只匹配明确的代码相关路径
|
|
408
|
-
const genericPathRegex = /[`"']([a-zA-Z0-9_\-\/]+\.(ts|js|mjs|cjs|py))[`"']/g;
|
|
409
|
-
while ((match = genericPathRegex.exec(text)) !== null) {
|
|
410
|
-
const filePath = match[1];
|
|
411
|
-
// 跳过太短、node_modules、配置文件
|
|
412
|
-
if (filePath.length < 10 ||
|
|
413
|
-
filePath.includes('node_modules') ||
|
|
414
|
-
filePath.includes('.config.') ||
|
|
415
|
-
filePath.endsWith('.d.ts') ||
|
|
416
|
-
filePath.endsWith('.test.ts') ||
|
|
417
|
-
filePath.endsWith('.spec.ts')) {
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
// 检查是否已经存在(避免重复)
|
|
421
|
-
if (artifacts.some(a => a.path === filePath || a.path.endsWith(filePath) || filePath.endsWith(a.path))) {
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
424
|
-
const description = extractDescription(text, filePath);
|
|
425
|
-
artifacts.push({
|
|
426
|
-
path: filePath,
|
|
427
|
-
action: 'modified',
|
|
428
|
-
description
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* 尝试从文本中提取文件描述
|
|
434
|
-
*/
|
|
435
|
-
function extractDescription(text, filePath) {
|
|
436
|
-
// 在文件路径附近查找描述性文字
|
|
437
|
-
const pathIndex = text.indexOf(filePath);
|
|
438
|
-
if (pathIndex === -1)
|
|
439
|
-
return '';
|
|
440
|
-
// 向前查找 100 个字符
|
|
441
|
-
const before = text.substring(Math.max(0, pathIndex - 100), pathIndex);
|
|
442
|
-
// 匹配描述模式
|
|
443
|
-
const descPatterns = [
|
|
444
|
-
/(?:description|说明|描述|功能|purpose)[::]\s*([^\n]{5,50})/i,
|
|
445
|
-
/\/\/\s*(.{5,50})/,
|
|
446
|
-
/\*\s*(.{5,50})\s*$/
|
|
447
|
-
];
|
|
448
|
-
for (const pattern of descPatterns) {
|
|
449
|
-
const match = before.match(pattern);
|
|
450
|
-
if (match) {
|
|
451
|
-
return match[1].trim().substring(0, 50);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
return '';
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* 从文本中提取问题
|
|
458
|
-
*/
|
|
459
|
-
function extractProblems(text, problems) {
|
|
460
|
-
// 问题模式(匹配问题描述)
|
|
461
|
-
const problemPattern = /(?:问题|problem|error|错误|失败|failed)[::]\s*([^\n]{5,100})/gi;
|
|
462
|
-
let match;
|
|
463
|
-
while ((match = problemPattern.exec(text)) !== null) {
|
|
464
|
-
const content = match[1].trim();
|
|
465
|
-
if (content.length > 5) {
|
|
466
|
-
problems.push({
|
|
467
|
-
problem: content,
|
|
468
|
-
approach: undefined
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
// 解决方案模式(匹配问题和解决方案)
|
|
473
|
-
const solutionPattern = /(?:解决|solution|方案|修复|fix)[::]\s*([^\n]{5,100})/gi;
|
|
474
|
-
while ((match = solutionPattern.exec(text)) !== null) {
|
|
475
|
-
const content = match[1].trim();
|
|
476
|
-
if (content.length > 5) {
|
|
477
|
-
// 尝试关联到最近的问题
|
|
478
|
-
const lastProblem = problems[problems.length - 1];
|
|
479
|
-
if (lastProblem && !lastProblem.approach) {
|
|
480
|
-
lastProblem.approach = content;
|
|
481
|
-
}
|
|
482
|
-
else {
|
|
483
|
-
// 作为独立问题记录
|
|
484
|
-
problems.push({
|
|
485
|
-
problem: content,
|
|
486
|
-
approach: content
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* 从文本中提取下一步行动
|
|
494
|
-
*/
|
|
495
|
-
function extractNextActions(text, actions) {
|
|
496
|
-
// 匹配下一步模式
|
|
497
|
-
const patterns = [
|
|
498
|
-
/(?:下一步|next|接下来|todo|待办)[::]?\s*\n?\s*[-\d]+\s*[.)]?\s*([^\n]{5,80})/gi,
|
|
499
|
-
/[-\d]+\s*[.)]\s*([^\n]{5,80})/g
|
|
500
|
-
];
|
|
501
|
-
for (const pattern of patterns) {
|
|
502
|
-
let match;
|
|
503
|
-
while ((match = pattern.exec(text)) !== null) {
|
|
504
|
-
const action = match[1].trim();
|
|
505
|
-
if (action.length > 5 && !actions.includes(action)) {
|
|
506
|
-
actions.push(action);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
/**
|
|
512
|
-
* 去重文件记录
|
|
513
|
-
*/
|
|
514
|
-
function deduplicateArtifacts(artifacts) {
|
|
515
|
-
const seen = new Map();
|
|
516
|
-
for (const artifact of artifacts) {
|
|
517
|
-
const key = artifact.path;
|
|
518
|
-
const existing = seen.get(key);
|
|
519
|
-
if (!existing) {
|
|
520
|
-
seen.set(key, artifact);
|
|
521
|
-
}
|
|
522
|
-
else {
|
|
523
|
-
// 合并描述(保留更长的)
|
|
524
|
-
if (artifact.description.length > existing.description.length) {
|
|
525
|
-
existing.description = artifact.description;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
return Array.from(seen.values());
|
|
530
|
-
}
|
|
531
|
-
/**
|
|
532
|
-
* 解析 CURRENT_FOCUS.md 中的 Working Memory 章节
|
|
533
|
-
*/
|
|
534
|
-
export function parseWorkingMemorySection(content) {
|
|
535
|
-
const wmIndex = content.indexOf(WORKING_MEMORY_SECTION);
|
|
536
|
-
if (wmIndex === -1)
|
|
537
|
-
return null;
|
|
538
|
-
const wmContent = content.substring(wmIndex);
|
|
539
|
-
const snapshot = {
|
|
540
|
-
lastUpdated: new Date().toISOString(),
|
|
541
|
-
artifacts: [],
|
|
542
|
-
activeProblems: [],
|
|
543
|
-
nextActions: []
|
|
544
|
-
};
|
|
545
|
-
// 解析 last updated
|
|
546
|
-
const updatedMatch = wmContent.match(/Last updated:\s*([^\n]+)/i);
|
|
547
|
-
if (updatedMatch) {
|
|
548
|
-
snapshot.lastUpdated = updatedMatch[1].trim();
|
|
549
|
-
}
|
|
550
|
-
// 解析文件记录表格
|
|
551
|
-
// | 文件路径 | 操作 | 描述 |
|
|
552
|
-
const tableRegex = /\|\s*`?([^`|\n]+)`?\s*\|\s*(created|modified|deleted)\s*\|\s*([^|\n]*)\s*\|/gi;
|
|
553
|
-
let match;
|
|
554
|
-
while ((match = tableRegex.exec(wmContent)) !== null) {
|
|
555
|
-
snapshot.artifacts.push({
|
|
556
|
-
path: match[1].trim(),
|
|
557
|
-
action: match[2].toLowerCase(),
|
|
558
|
-
description: match[3].trim()
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
// 解析问题列表
|
|
562
|
-
const problemRegex = /[-*]\s*(.+?)\s*(?:→|->)\s*(.+)/g;
|
|
563
|
-
while ((match = problemRegex.exec(wmContent)) !== null) {
|
|
564
|
-
snapshot.activeProblems.push({
|
|
565
|
-
problem: match[1].trim(),
|
|
566
|
-
approach: match[2].trim()
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
// 解析下一步行动
|
|
570
|
-
const actionRegex = /^\s*[\d]+\.\s*(.+)$/gm;
|
|
571
|
-
while ((match = actionRegex.exec(wmContent)) !== null) {
|
|
572
|
-
snapshot.nextActions.push(match[1].trim());
|
|
573
|
-
}
|
|
574
|
-
return snapshot;
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* 将工作记忆合并到 CURRENT_FOCUS.md 内容中
|
|
578
|
-
*
|
|
579
|
-
* @param content 原始内容
|
|
580
|
-
* @param snapshot 工作记忆快照
|
|
581
|
-
* @returns 合并后的内容
|
|
582
|
-
*/
|
|
583
|
-
export function mergeWorkingMemory(content, snapshot) {
|
|
584
|
-
const wmIndex = content.indexOf(WORKING_MEMORY_SECTION);
|
|
585
|
-
// 生成 Working Memory 章节
|
|
586
|
-
const wmSection = generateWorkingMemorySection(snapshot);
|
|
587
|
-
if (wmIndex === -1) {
|
|
588
|
-
// 没有 Working Memory 章节,追加到末尾
|
|
589
|
-
return content.trimEnd() + '\n\n' + WORKING_MEMORY_SECTION + '\n' + wmSection;
|
|
590
|
-
}
|
|
591
|
-
else {
|
|
592
|
-
// 替换现有的 Working Memory 章节
|
|
593
|
-
const beforeWm = content.substring(0, wmIndex);
|
|
594
|
-
// 查找下一个 ## 标题(如果有的话)
|
|
595
|
-
const afterWm = content.substring(wmIndex);
|
|
596
|
-
const nextSectionMatch = afterWm.substring(WORKING_MEMORY_SECTION.length).match(/\n##\s/);
|
|
597
|
-
if (nextSectionMatch && nextSectionMatch.index !== undefined) {
|
|
598
|
-
const nextSectionStart = WORKING_MEMORY_SECTION.length + nextSectionMatch.index;
|
|
599
|
-
return beforeWm + WORKING_MEMORY_SECTION + '\n' + wmSection + '\n' + afterWm.substring(nextSectionStart);
|
|
600
|
-
}
|
|
601
|
-
else {
|
|
602
|
-
return beforeWm + WORKING_MEMORY_SECTION + '\n' + wmSection;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
/**
|
|
607
|
-
* 生成 Working Memory 章节内容
|
|
608
|
-
*/
|
|
609
|
-
function generateWorkingMemorySection(snapshot) {
|
|
610
|
-
const lines = [`> Last updated: ${snapshot.lastUpdated}`, ''];
|
|
611
|
-
// 文件输出记录
|
|
612
|
-
if (snapshot.artifacts.length > 0) {
|
|
613
|
-
lines.push('### 📁 文件输出记录');
|
|
614
|
-
lines.push('');
|
|
615
|
-
lines.push('| 文件路径 | 操作 | 描述 |');
|
|
616
|
-
lines.push('|----------|------|------|');
|
|
617
|
-
for (const artifact of snapshot.artifacts) {
|
|
618
|
-
lines.push(`| \`${artifact.path}\` | ${artifact.action} | ${artifact.description || '-'} |`);
|
|
619
|
-
}
|
|
620
|
-
lines.push('');
|
|
621
|
-
}
|
|
622
|
-
// 当前任务
|
|
623
|
-
if (snapshot.currentTask) {
|
|
624
|
-
lines.push('### 🎯 当前任务');
|
|
625
|
-
lines.push(`- **描述**: ${snapshot.currentTask.description}`);
|
|
626
|
-
lines.push(`- **状态**: ${snapshot.currentTask.status}`);
|
|
627
|
-
lines.push(`- **进度**: ${snapshot.currentTask.progress}%`);
|
|
628
|
-
lines.push('');
|
|
629
|
-
}
|
|
630
|
-
// 活动问题
|
|
631
|
-
if (snapshot.activeProblems.length > 0) {
|
|
632
|
-
lines.push('### ⚠️ 活动问题');
|
|
633
|
-
for (const p of snapshot.activeProblems) {
|
|
634
|
-
if (p.approach) {
|
|
635
|
-
lines.push(`- ${p.problem} → ${p.approach}`);
|
|
636
|
-
}
|
|
637
|
-
else {
|
|
638
|
-
lines.push(`- ${p.problem}`);
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
lines.push('');
|
|
642
|
-
}
|
|
643
|
-
// 下一步行动
|
|
644
|
-
if (snapshot.nextActions.length > 0) {
|
|
645
|
-
lines.push('### ➡️ 下一步行动');
|
|
646
|
-
for (let i = 0; i < snapshot.nextActions.length; i++) {
|
|
647
|
-
lines.push(`${i + 1}. ${snapshot.nextActions[i]}`);
|
|
648
|
-
}
|
|
649
|
-
lines.push('');
|
|
650
|
-
}
|
|
651
|
-
return lines.join('\n');
|
|
652
|
-
}
|
|
653
|
-
/**
|
|
654
|
-
* 生成工作记忆注入字符串(用于 prompt 注入)
|
|
655
|
-
*/
|
|
656
|
-
export function workingMemoryToInjection(snapshot) {
|
|
657
|
-
if (!snapshot)
|
|
658
|
-
return '';
|
|
659
|
-
if (snapshot.artifacts.length === 0 &&
|
|
660
|
-
snapshot.activeProblems.length === 0 &&
|
|
661
|
-
snapshot.nextActions.length === 0) {
|
|
662
|
-
return '';
|
|
663
|
-
}
|
|
664
|
-
const lines = ['<working_memory preserved="true">'];
|
|
665
|
-
lines.push('以下是你压缩前的工作记忆,请继续完成未完成的任务:');
|
|
666
|
-
lines.push('');
|
|
667
|
-
if (snapshot.artifacts.length > 0) {
|
|
668
|
-
lines.push('### 已输出的文件');
|
|
669
|
-
for (const a of snapshot.artifacts.slice(-10)) {
|
|
670
|
-
lines.push(`- [${a.action.toUpperCase()}] \`${a.path}\`${a.description ? ` - ${a.description}` : ''}`);
|
|
671
|
-
}
|
|
672
|
-
lines.push('');
|
|
673
|
-
}
|
|
674
|
-
if (snapshot.activeProblems.length > 0) {
|
|
675
|
-
lines.push('### 活动问题');
|
|
676
|
-
for (const p of snapshot.activeProblems) {
|
|
677
|
-
if (p.approach) {
|
|
678
|
-
lines.push(`- ${p.problem} → ${p.approach}`);
|
|
679
|
-
}
|
|
680
|
-
else {
|
|
681
|
-
lines.push(`- ${p.problem}`);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
lines.push('');
|
|
685
|
-
}
|
|
686
|
-
if (snapshot.nextActions.length > 0) {
|
|
687
|
-
lines.push('### 下一步行动');
|
|
688
|
-
for (let i = 0; i < snapshot.nextActions.length; i++) {
|
|
689
|
-
lines.push(`${i + 1}. ${snapshot.nextActions[i]}`);
|
|
690
|
-
}
|
|
691
|
-
lines.push('');
|
|
692
|
-
}
|
|
693
|
-
lines.push('</working_memory>');
|
|
694
|
-
return lines.join('\n');
|
|
695
|
-
}
|
|
696
|
-
// ============================================================================
|
|
697
|
-
// 自动压缩与维护
|
|
698
|
-
// ============================================================================
|
|
699
|
-
/** 默认压缩配置 */
|
|
700
|
-
const DEFAULT_COMPRESSION_CONFIG = {
|
|
701
|
-
lineThreshold: 100,
|
|
702
|
-
sizeThreshold: 15 * 1024, // 15KB
|
|
703
|
-
intervalMs: 24 * 60 * 60 * 1000, // 24 hours
|
|
704
|
-
keepCompletedTasks: 3,
|
|
705
|
-
maxWorkingMemoryArtifacts: 10,
|
|
706
|
-
};
|
|
707
|
-
/** 压缩时间记录文件名 */
|
|
708
|
-
const LAST_COMPRESS_FILE = '.last_compress';
|
|
709
|
-
/**
|
|
710
|
-
* 从 pain_settings.json 加载压缩配置
|
|
711
|
-
*
|
|
712
|
-
* @param stateDir state 目录路径
|
|
713
|
-
* @returns 压缩配置
|
|
714
|
-
*/
|
|
715
|
-
function loadCompressionConfig(stateDir) {
|
|
716
|
-
if (!stateDir) {
|
|
717
|
-
return DEFAULT_COMPRESSION_CONFIG;
|
|
718
|
-
}
|
|
719
|
-
try {
|
|
720
|
-
const configPath = path.join(stateDir, 'pain_settings.json');
|
|
721
|
-
if (!fs.existsSync(configPath)) {
|
|
722
|
-
return DEFAULT_COMPRESSION_CONFIG;
|
|
723
|
-
}
|
|
724
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
725
|
-
const compression = config?.compression || {};
|
|
726
|
-
return {
|
|
727
|
-
lineThreshold: compression.line_threshold ?? DEFAULT_COMPRESSION_CONFIG.lineThreshold,
|
|
728
|
-
sizeThreshold: (compression.size_threshold_kb ?? 15) * 1024,
|
|
729
|
-
intervalMs: (compression.interval_hours ?? 24) * 60 * 60 * 1000,
|
|
730
|
-
keepCompletedTasks: compression.keep_completed_tasks ?? DEFAULT_COMPRESSION_CONFIG.keepCompletedTasks,
|
|
731
|
-
maxWorkingMemoryArtifacts: compression.max_working_memory_artifacts ?? DEFAULT_COMPRESSION_CONFIG.maxWorkingMemoryArtifacts,
|
|
732
|
-
};
|
|
733
|
-
}
|
|
734
|
-
catch {
|
|
735
|
-
return DEFAULT_COMPRESSION_CONFIG;
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
/**
|
|
739
|
-
* 检查是否可以进行自动压缩(频率限制)
|
|
740
|
-
*
|
|
741
|
-
* @param stateDir state 目录路径
|
|
742
|
-
* @returns 是否可以进行压缩
|
|
743
|
-
*/
|
|
744
|
-
function canAutoCompress(stateDir) {
|
|
745
|
-
const lastCompressPath = path.join(stateDir, LAST_COMPRESS_FILE);
|
|
746
|
-
if (!fs.existsSync(lastCompressPath)) {
|
|
747
|
-
return true;
|
|
748
|
-
}
|
|
749
|
-
try {
|
|
750
|
-
const config = loadCompressionConfig(stateDir);
|
|
751
|
-
const lastCompressTime = parseInt(fs.readFileSync(lastCompressPath, 'utf-8'), 10);
|
|
752
|
-
const now = Date.now();
|
|
753
|
-
return (now - lastCompressTime) >= config.intervalMs;
|
|
754
|
-
}
|
|
755
|
-
catch {
|
|
756
|
-
return true;
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
/**
|
|
760
|
-
* 记录压缩时间
|
|
761
|
-
*
|
|
762
|
-
* @param stateDir state 目录路径
|
|
763
|
-
*/
|
|
764
|
-
function recordCompressTime(stateDir) {
|
|
765
|
-
try {
|
|
766
|
-
if (!fs.existsSync(stateDir)) {
|
|
767
|
-
fs.mkdirSync(stateDir, { recursive: true });
|
|
768
|
-
}
|
|
769
|
-
fs.writeFileSync(path.join(stateDir, LAST_COMPRESS_FILE), Date.now().toString(), 'utf-8');
|
|
770
|
-
}
|
|
771
|
-
catch (error) {
|
|
772
|
-
logError('Failed to record compress time', error);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
/**
|
|
776
|
-
* 提取已完成任务作为里程碑
|
|
777
|
-
*/
|
|
778
|
-
export function extractMilestones(content) {
|
|
779
|
-
const completedTasks = [];
|
|
780
|
-
const fileArtifacts = [];
|
|
781
|
-
const lines = content.split('\n');
|
|
782
|
-
let inTaskSection = false;
|
|
783
|
-
let inWorkingMemory = false;
|
|
784
|
-
for (const line of lines) {
|
|
785
|
-
const trimmed = line.trim();
|
|
786
|
-
// 识别章节
|
|
787
|
-
if (/^#{1,3}\s*.*当前任务|🔄/.test(trimmed)) {
|
|
788
|
-
inTaskSection = true;
|
|
789
|
-
inWorkingMemory = false;
|
|
790
|
-
}
|
|
791
|
-
else if (/^#{1,3}\s*.*下一步|➡️/.test(trimmed)) {
|
|
792
|
-
inTaskSection = false;
|
|
793
|
-
inWorkingMemory = false;
|
|
794
|
-
}
|
|
795
|
-
else if (/^##\s*🧠\s*Working Memory/.test(trimmed)) {
|
|
796
|
-
inWorkingMemory = true;
|
|
797
|
-
inTaskSection = false;
|
|
798
|
-
}
|
|
799
|
-
// 提取已完成任务
|
|
800
|
-
if (inTaskSection && /^-\s*\[x\]/i.test(trimmed)) {
|
|
801
|
-
completedTasks.push(trimmed.replace(/^-\s*\[x\]\s*/i, ''));
|
|
802
|
-
}
|
|
803
|
-
// 提取文件引用(从工作记忆)
|
|
804
|
-
if (inWorkingMemory) {
|
|
805
|
-
const fileMatch = trimmed.match(/^\|\s*`?([^`|\n]+)`?\s*\|/);
|
|
806
|
-
if (fileMatch && !fileMatch[1].includes('文件路径')) {
|
|
807
|
-
fileArtifacts.push(fileMatch[1].trim());
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
return {
|
|
812
|
-
completedTasks: completedTasks.slice(-10), // 最多 10 个
|
|
813
|
-
fileArtifacts: fileArtifacts.slice(-10)
|
|
814
|
-
};
|
|
815
|
-
}
|
|
816
|
-
/**
|
|
817
|
-
* 归档里程碑到 daily memory 文件
|
|
818
|
-
*/
|
|
819
|
-
export function archiveMilestonesToDaily(workspaceDir, milestones, version) {
|
|
820
|
-
if (milestones.completedTasks.length === 0 && milestones.fileArtifacts.length === 0) {
|
|
821
|
-
return null;
|
|
822
|
-
}
|
|
823
|
-
const dateStr = new Date().toISOString().split('T')[0];
|
|
824
|
-
const memoryDir = path.join(workspaceDir, 'memory');
|
|
825
|
-
const dailyLogPath = path.join(memoryDir, `${dateStr}.md`);
|
|
826
|
-
const timestamp = new Date().toISOString();
|
|
827
|
-
// 确保目录存在
|
|
828
|
-
if (!fs.existsSync(memoryDir)) {
|
|
829
|
-
fs.mkdirSync(memoryDir, { recursive: true });
|
|
830
|
-
}
|
|
831
|
-
// 构建里程碑内容
|
|
832
|
-
const lines = [];
|
|
833
|
-
lines.push(`\n## 🏆 里程碑 [CURRENT_FOCUS v${version} 压缩]`);
|
|
834
|
-
lines.push(`> 时间: ${timestamp}`);
|
|
835
|
-
lines.push('');
|
|
836
|
-
if (milestones.completedTasks.length > 0) {
|
|
837
|
-
lines.push('### 已完成任务');
|
|
838
|
-
for (const task of milestones.completedTasks) {
|
|
839
|
-
lines.push(`- [x] ${task}`);
|
|
840
|
-
}
|
|
841
|
-
lines.push('');
|
|
842
|
-
}
|
|
843
|
-
if (milestones.fileArtifacts.length > 0) {
|
|
844
|
-
lines.push('### 相关文件');
|
|
845
|
-
for (const file of milestones.fileArtifacts) {
|
|
846
|
-
lines.push(`- \`${file}\``);
|
|
847
|
-
}
|
|
848
|
-
lines.push('');
|
|
849
|
-
}
|
|
850
|
-
lines.push('---');
|
|
851
|
-
lines.push('');
|
|
852
|
-
// 追加到 daily log
|
|
853
|
-
try {
|
|
854
|
-
fs.appendFileSync(dailyLogPath, lines.join('\n'), 'utf-8');
|
|
855
|
-
return dailyLogPath;
|
|
856
|
-
}
|
|
857
|
-
catch (error) {
|
|
858
|
-
logError(`Failed to archive milestones to ${dailyLogPath}`, error);
|
|
859
|
-
return null;
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
/**
|
|
863
|
-
* 清理过期信息和验证文件引用
|
|
864
|
-
*/
|
|
865
|
-
export function cleanupStaleInfo(content, workspaceDir, config) {
|
|
866
|
-
const effectiveConfig = config || DEFAULT_COMPRESSION_CONFIG;
|
|
867
|
-
const lines = content.split('\n');
|
|
868
|
-
const result = [];
|
|
869
|
-
let inWorkingMemory = false;
|
|
870
|
-
let inFileTable = false;
|
|
871
|
-
let completedCount = 0;
|
|
872
|
-
let artifactCount = 0;
|
|
873
|
-
for (let i = 0; i < lines.length; i++) {
|
|
874
|
-
const line = lines[i];
|
|
875
|
-
const trimmed = line.trim();
|
|
876
|
-
// 检测 Working Memory 章节
|
|
877
|
-
if (/^##\s*🧠\s*Working Memory/.test(trimmed)) {
|
|
878
|
-
inWorkingMemory = true;
|
|
879
|
-
inFileTable = false;
|
|
880
|
-
}
|
|
881
|
-
else if (/^##\s/.test(trimmed) && !trimmed.includes('Working Memory')) {
|
|
882
|
-
inWorkingMemory = false;
|
|
883
|
-
inFileTable = false;
|
|
884
|
-
}
|
|
885
|
-
// 检测文件表格
|
|
886
|
-
if (inWorkingMemory && /^\|\s*文件路径/.test(trimmed)) {
|
|
887
|
-
inFileTable = true;
|
|
888
|
-
result.push(line);
|
|
889
|
-
continue;
|
|
890
|
-
}
|
|
891
|
-
if (inFileTable && /^\|[^|]+\|[^|]+\|[^|]+\|/.test(trimmed)) {
|
|
892
|
-
// 检查是否是表格分隔行
|
|
893
|
-
if (/^\|[-\s|:]+\|$/.test(trimmed)) {
|
|
894
|
-
result.push(line);
|
|
895
|
-
continue;
|
|
896
|
-
}
|
|
897
|
-
// 提取文件路径
|
|
898
|
-
const match = trimmed.match(/^\|\s*`?([^`|\n]+)`?\s*\|/);
|
|
899
|
-
if (match) {
|
|
900
|
-
const filePath = match[1].trim();
|
|
901
|
-
artifactCount++;
|
|
902
|
-
// 限制条数
|
|
903
|
-
if (artifactCount > effectiveConfig.maxWorkingMemoryArtifacts) {
|
|
904
|
-
continue; // 跳过超出限制的条目
|
|
905
|
-
}
|
|
906
|
-
// 可选:验证文件是否存在
|
|
907
|
-
if (workspaceDir) {
|
|
908
|
-
const fullPath = path.join(workspaceDir, filePath);
|
|
909
|
-
if (!fs.existsSync(fullPath)) {
|
|
910
|
-
continue; // 文件不存在,跳过
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
result.push(line);
|
|
914
|
-
continue;
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
// 处理已完成任务
|
|
918
|
-
if (/^-\s*\[x\]/i.test(trimmed)) {
|
|
919
|
-
completedCount++;
|
|
920
|
-
if (completedCount > effectiveConfig.keepCompletedTasks) {
|
|
921
|
-
continue; // 跳过超出限制的已完成任务
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
result.push(line);
|
|
925
|
-
}
|
|
926
|
-
return result.join('\n');
|
|
927
|
-
}
|
|
928
|
-
/**
|
|
929
|
-
* 自动压缩 CURRENT_FOCUS.md
|
|
930
|
-
*
|
|
931
|
-
* @param focusPath CURRENT_FOCUS.md 的完整路径
|
|
932
|
-
* @param workspaceDir 工作区目录(可选,用于验证文件引用)
|
|
933
|
-
* @param stateDir state 目录路径(可选,用于频率限制)
|
|
934
|
-
* @returns 压缩结果信息,如果不需要压缩则返回 null
|
|
935
|
-
*/
|
|
936
|
-
export function autoCompressFocus(focusPath, workspaceDir, stateDir) {
|
|
937
|
-
// 检查文件是否存在
|
|
938
|
-
if (!fs.existsSync(focusPath)) {
|
|
939
|
-
return {
|
|
940
|
-
compressed: false,
|
|
941
|
-
oldLines: 0,
|
|
942
|
-
newLines: 0,
|
|
943
|
-
milestonesArchived: false,
|
|
944
|
-
backupPath: null,
|
|
945
|
-
reason: 'File not found'
|
|
946
|
-
};
|
|
947
|
-
}
|
|
948
|
-
// 加载配置
|
|
949
|
-
const config = loadCompressionConfig(stateDir);
|
|
950
|
-
const oldContent = fs.readFileSync(focusPath, 'utf-8');
|
|
951
|
-
const oldLines = oldContent.split('\n').length;
|
|
952
|
-
const oldSize = Buffer.byteLength(oldContent, 'utf-8');
|
|
953
|
-
// 检查是否需要压缩(行数或大小阈值)
|
|
954
|
-
const needsCompression = oldLines > config.lineThreshold ||
|
|
955
|
-
oldSize > config.sizeThreshold;
|
|
956
|
-
if (!needsCompression) {
|
|
957
|
-
return {
|
|
958
|
-
compressed: false,
|
|
959
|
-
oldLines,
|
|
960
|
-
newLines: oldLines,
|
|
961
|
-
milestonesArchived: false,
|
|
962
|
-
backupPath: null,
|
|
963
|
-
reason: 'Below threshold'
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
// 检查频率限制
|
|
967
|
-
if (stateDir && !canAutoCompress(stateDir)) {
|
|
968
|
-
return {
|
|
969
|
-
compressed: false,
|
|
970
|
-
oldLines,
|
|
971
|
-
newLines: oldLines,
|
|
972
|
-
milestonesArchived: false,
|
|
973
|
-
backupPath: null,
|
|
974
|
-
reason: 'Rate limited (24h interval)'
|
|
975
|
-
};
|
|
976
|
-
}
|
|
977
|
-
// 1. 提取里程碑
|
|
978
|
-
const version = extractVersion(oldContent);
|
|
979
|
-
const milestones = extractMilestones(oldContent);
|
|
980
|
-
// 2. 归档里程碑到 daily memory
|
|
981
|
-
let milestonesArchived = false;
|
|
982
|
-
if (workspaceDir) {
|
|
983
|
-
const archivePath = archiveMilestonesToDaily(workspaceDir, milestones, version);
|
|
984
|
-
milestonesArchived = archivePath !== null;
|
|
985
|
-
}
|
|
986
|
-
// 3. 清理过期信息(传入配置)
|
|
987
|
-
let newContent = cleanupStaleInfo(oldContent, workspaceDir, config);
|
|
988
|
-
// 4. 压缩内容(使用 extractSummary)
|
|
989
|
-
newContent = extractSummary(newContent, 50);
|
|
990
|
-
// 5. 递增版本号和日期
|
|
991
|
-
const newVersion = `${parseInt(version, 10) + 1}`;
|
|
992
|
-
const today = new Date().toISOString().split('T')[0];
|
|
993
|
-
newContent = newContent
|
|
994
|
-
.replace(/\*\*版本\*\*:\s*v[\d.]+/i, `**版本**: v${newVersion}`)
|
|
995
|
-
.replace(/\*\*更新\*\*:\s*\d{4}-\d{2}-\d{2}/, `**更新**: ${today}`);
|
|
996
|
-
// 6. 备份原版本
|
|
997
|
-
const backupPath = backupToHistory(focusPath, oldContent);
|
|
998
|
-
// 7. 清理过期历史
|
|
999
|
-
cleanupHistory(focusPath);
|
|
1000
|
-
// 8. 写入新内容
|
|
1001
|
-
fs.writeFileSync(focusPath, newContent, 'utf-8');
|
|
1002
|
-
// 9. 记录压缩时间
|
|
1003
|
-
if (stateDir) {
|
|
1004
|
-
recordCompressTime(stateDir);
|
|
1005
|
-
}
|
|
1006
|
-
const newLines = newContent.split('\n').length;
|
|
1007
|
-
return {
|
|
1008
|
-
compressed: true,
|
|
1009
|
-
oldLines,
|
|
1010
|
-
newLines,
|
|
1011
|
-
milestonesArchived,
|
|
1012
|
-
backupPath,
|
|
1013
|
-
reason: `Auto-compressed: ${oldLines} → ${newLines} lines`
|
|
1014
|
-
};
|
|
1015
|
-
}
|
|
1016
|
-
/**
|
|
1017
|
-
* 检查是否需要自动压缩
|
|
1018
|
-
*/
|
|
1019
|
-
export function needsAutoCompression(focusPath, stateDir) {
|
|
1020
|
-
if (!fs.existsSync(focusPath)) {
|
|
1021
|
-
return false;
|
|
1022
|
-
}
|
|
1023
|
-
try {
|
|
1024
|
-
const config = stateDir ? loadCompressionConfig(stateDir) : DEFAULT_COMPRESSION_CONFIG;
|
|
1025
|
-
const content = fs.readFileSync(focusPath, 'utf-8');
|
|
1026
|
-
const lines = content.split('\n').length;
|
|
1027
|
-
const size = Buffer.byteLength(content, 'utf-8');
|
|
1028
|
-
return lines > config.lineThreshold || size > config.sizeThreshold;
|
|
1029
|
-
}
|
|
1030
|
-
catch {
|
|
1031
|
-
return false;
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
// ============================================================================
|
|
1035
|
-
// 格式验证与模板恢复
|
|
1036
|
-
// ============================================================================
|
|
1037
|
-
/** CURRENT_FOCUS 模板路径(相对于插件根目录) */
|
|
1038
|
-
const CURRENT_FOCUS_TEMPLATE_PATH = 'templates/workspace/okr/CURRENT_FOCUS.md';
|
|
1039
|
-
/**
|
|
1040
|
-
* 验证 CURRENT_FOCUS.md 格式
|
|
1041
|
-
*
|
|
1042
|
-
* 仅校验会导致程序崩溃的核心问题,不过度校验
|
|
1043
|
-
*
|
|
1044
|
-
* @param content 文件内容
|
|
1045
|
-
* @returns 验证结果
|
|
1046
|
-
*/
|
|
1047
|
-
export function validateCurrentFocus(content) {
|
|
1048
|
-
const errors = [];
|
|
1049
|
-
const warnings = [];
|
|
1050
|
-
// 仅检查会导致程序崩溃的核心问题
|
|
1051
|
-
// 1. 文件为空
|
|
1052
|
-
if (!content || !content.trim()) {
|
|
1053
|
-
errors.push('文件为空');
|
|
1054
|
-
return { valid: false, errors, warnings };
|
|
1055
|
-
}
|
|
1056
|
-
// 2. 检查是否是有效的文本(排除二进制乱码)
|
|
1057
|
-
// 如果有超过 50% 的非打印字符,认为是乱码
|
|
1058
|
-
const nonPrintable = content.split('').filter(c => {
|
|
1059
|
-
const code = c.charCodeAt(0);
|
|
1060
|
-
// 允许:换行、制表符、中文、英文、数字、标点
|
|
1061
|
-
return code < 32 && code !== 10 && code !== 13 && code !== 9;
|
|
1062
|
-
}).length;
|
|
1063
|
-
if (nonPrintable > content.length * 0.5) {
|
|
1064
|
-
errors.push('文件内容损坏(可能是二进制乱码)');
|
|
1065
|
-
return { valid: false, errors, warnings };
|
|
1066
|
-
}
|
|
1067
|
-
// 以下仅作为警告,不触发恢复
|
|
1068
|
-
// 提示缺少建议的章节(不影响程序运行)
|
|
1069
|
-
if (!content.includes('下一步') && !content.includes('Next')) {
|
|
1070
|
-
warnings.push('缺少下一步章节(建议保留)');
|
|
1071
|
-
}
|
|
1072
|
-
return {
|
|
1073
|
-
valid: true, // 只要不崩溃就认为是 valid
|
|
1074
|
-
errors,
|
|
1075
|
-
warnings
|
|
1076
|
-
};
|
|
1077
|
-
}
|
|
1078
|
-
/**
|
|
1079
|
-
* 从模板恢复 CURRENT_FOCUS.md
|
|
1080
|
-
*
|
|
1081
|
-
* @param focusPath CURRENT_FOCUS.md 路径
|
|
1082
|
-
* @param extensionRoot 插件根目录
|
|
1083
|
-
* @returns 恢复是否成功
|
|
1084
|
-
*/
|
|
1085
|
-
export function recoverFromTemplate(focusPath, extensionRoot) {
|
|
1086
|
-
try {
|
|
1087
|
-
// 查找模板文件
|
|
1088
|
-
const templatePath = path.join(extensionRoot, CURRENT_FOCUS_TEMPLATE_PATH);
|
|
1089
|
-
if (!fs.existsSync(templatePath)) {
|
|
1090
|
-
return {
|
|
1091
|
-
success: false,
|
|
1092
|
-
error: `Template not found: ${templatePath}`
|
|
1093
|
-
};
|
|
1094
|
-
}
|
|
1095
|
-
// 读取模板
|
|
1096
|
-
let template = fs.readFileSync(templatePath, 'utf-8');
|
|
1097
|
-
// 替换日期占位符
|
|
1098
|
-
const today = new Date().toISOString().split('T')[0];
|
|
1099
|
-
template = template.replace(/{YYYY-MM-DD}/g, today);
|
|
1100
|
-
// 备份损坏的文件(如果存在)
|
|
1101
|
-
if (fs.existsSync(focusPath)) {
|
|
1102
|
-
const backupPath = `${focusPath}.corrupted.${Date.now()}.md`;
|
|
1103
|
-
fs.copyFileSync(focusPath, backupPath);
|
|
1104
|
-
}
|
|
1105
|
-
// 确保目录存在
|
|
1106
|
-
const focusDir = path.dirname(focusPath);
|
|
1107
|
-
if (!fs.existsSync(focusDir)) {
|
|
1108
|
-
fs.mkdirSync(focusDir, { recursive: true });
|
|
1109
|
-
}
|
|
1110
|
-
// 写入恢复的内容
|
|
1111
|
-
fs.writeFileSync(focusPath, template, 'utf-8');
|
|
1112
|
-
return {
|
|
1113
|
-
success: true,
|
|
1114
|
-
templatePath
|
|
1115
|
-
};
|
|
1116
|
-
}
|
|
1117
|
-
catch (error) {
|
|
1118
|
-
return {
|
|
1119
|
-
success: false,
|
|
1120
|
-
error: String(error)
|
|
1121
|
-
};
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
/**
|
|
1125
|
-
* 安全读取 CURRENT_FOCUS.md(自动验证 + 恢复)
|
|
1126
|
-
*
|
|
1127
|
-
* 仅在文件为空或损坏时才恢复,其他情况正常使用
|
|
1128
|
-
*
|
|
1129
|
-
* @param focusPath CURRENT_FOCUS.md 路径
|
|
1130
|
-
* @param extensionRoot 插件根目录
|
|
1131
|
-
* @param logger 日志记录器
|
|
1132
|
-
* @returns 文件内容和恢复状态
|
|
1133
|
-
*/
|
|
1134
|
-
export function safeReadCurrentFocus(focusPath, extensionRoot, logger) {
|
|
1135
|
-
// 文件不存在,从模板创建
|
|
1136
|
-
if (!fs.existsSync(focusPath)) {
|
|
1137
|
-
const result = recoverFromTemplate(focusPath, extensionRoot);
|
|
1138
|
-
if (result.success) {
|
|
1139
|
-
logger?.info?.(`[PD:Focus] Created CURRENT_FOCUS.md from template`);
|
|
1140
|
-
return {
|
|
1141
|
-
content: fs.readFileSync(focusPath, 'utf-8'),
|
|
1142
|
-
recovered: true,
|
|
1143
|
-
validationErrors: []
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
return {
|
|
1147
|
-
content: '',
|
|
1148
|
-
recovered: false,
|
|
1149
|
-
validationErrors: [`Failed to create from template: ${result.error}`]
|
|
1150
|
-
};
|
|
1151
|
-
}
|
|
1152
|
-
// 读取并验证
|
|
1153
|
-
const content = fs.readFileSync(focusPath, 'utf-8');
|
|
1154
|
-
const validation = validateCurrentFocus(content);
|
|
1155
|
-
// 记录警告(不触发恢复)
|
|
1156
|
-
if (validation.warnings.length > 0) {
|
|
1157
|
-
logger?.warn?.(`[PD:Focus] CURRENT_FOCUS.md warnings: ${validation.warnings.join(', ')}`);
|
|
1158
|
-
}
|
|
1159
|
-
// 仅在有严重错误时才恢复
|
|
1160
|
-
if (!validation.valid) {
|
|
1161
|
-
logger?.warn?.(`[PD:Focus] CURRENT_FOCUS.md corrupted: ${validation.errors.join(', ')}`);
|
|
1162
|
-
const result = recoverFromTemplate(focusPath, extensionRoot);
|
|
1163
|
-
if (result.success) {
|
|
1164
|
-
logger?.info?.(`[PD:Focus] Recovered CURRENT_FOCUS.md from template`);
|
|
1165
|
-
return {
|
|
1166
|
-
content: fs.readFileSync(focusPath, 'utf-8'),
|
|
1167
|
-
recovered: true,
|
|
1168
|
-
validationErrors: validation.errors
|
|
1169
|
-
};
|
|
1170
|
-
}
|
|
1171
|
-
// 恢复失败,返回原始内容(让系统继续运行)
|
|
1172
|
-
logger?.warn?.(`[PD:Focus] Failed to recover: ${result.error}`);
|
|
1173
|
-
return {
|
|
1174
|
-
content,
|
|
1175
|
-
recovered: false,
|
|
1176
|
-
validationErrors: validation.errors
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
// 正常情况:文件有效
|
|
1180
|
-
return {
|
|
1181
|
-
content,
|
|
1182
|
-
recovered: false,
|
|
1183
|
-
validationErrors: []
|
|
1184
|
-
};
|
|
1185
|
-
}
|