@ngockhoale/ukit 1.1.6
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/CHANGELOG.md +179 -0
- package/LICENSE +21 -0
- package/README.md +189 -0
- package/bin/ukit +30 -0
- package/manifests/platform.full.yaml +1194 -0
- package/package.json +71 -0
- package/scripts/bug/triage.mjs +37 -0
- package/scripts/index/build-index.mjs +35 -0
- package/scripts/index/query-index.mjs +92 -0
- package/scripts/index/refresh-index.mjs +85 -0
- package/scripts/release/verify-release.mjs +56 -0
- package/src/bug/triageBug.js +123 -0
- package/src/cli/adapters.js +148 -0
- package/src/cli/commands/diff.js +51 -0
- package/src/cli/commands/doctor.js +125 -0
- package/src/cli/commands/indexArgs.js +73 -0
- package/src/cli/commands/indexTools.js +509 -0
- package/src/cli/commands/install.js +293 -0
- package/src/cli/commands/memory.js +126 -0
- package/src/cli/commands/status.js +8 -0
- package/src/cli/commands/uninstall.js +51 -0
- package/src/cli/index.js +109 -0
- package/src/context/detectProjectContext.js +49 -0
- package/src/context/detectProviders.js +12 -0
- package/src/core/applyPlan.js +89 -0
- package/src/core/buildPlan.js +228 -0
- package/src/core/compact/index.js +294 -0
- package/src/core/compact/threshold.js +936 -0
- package/src/core/diffPlan.js +73 -0
- package/src/core/ensureGitignore.js +117 -0
- package/src/core/fileOps.js +188 -0
- package/src/core/memory/hygiene.js +160 -0
- package/src/core/memory/index.js +2 -0
- package/src/core/memory/retrieval.js +476 -0
- package/src/core/memory/store.js +202 -0
- package/src/core/metadata.js +132 -0
- package/src/core/migrateLegacy.js +139 -0
- package/src/core/output/index.js +1309 -0
- package/src/core/paths.js +13 -0
- package/src/core/report.js +17 -0
- package/src/core/router/advisor.js +42 -0
- package/src/core/router/index.js +2 -0
- package/src/core/router/router.js +164 -0
- package/src/core/runInstallPipeline.js +365 -0
- package/src/core/runtimeConfig.js +190 -0
- package/src/core/runtimePaths.js +24 -0
- package/src/core/status.js +186 -0
- package/src/core/token/index.js +328 -0
- package/src/core/uninstall.js +246 -0
- package/src/core/validation/confidence.js +89 -0
- package/src/core/validation/index.js +2 -0
- package/src/core/validation/validator.js +165 -0
- package/src/index/buildIndex.js +1392 -0
- package/src/index/gitHooks.js +109 -0
- package/src/index/importResolution.js +377 -0
- package/src/index/languageTools.js +127 -0
- package/src/index/paths.js +27 -0
- package/src/index/queryIndex.js +637 -0
- package/src/index/relatedTests.js +237 -0
- package/src/index/resolveContext.js +345 -0
- package/src/index/routeCatalog.js +258 -0
- package/src/index/taskRouting.js +677 -0
- package/src/index/verificationPlan.js +437 -0
- package/src/manifest/loadManifest.js +22 -0
- package/src/manifest/selectItems.js +78 -0
- package/src/manifest/validateManifest.js +115 -0
- package/src/render/buildVariables.js +39 -0
- package/src/render/renderTemplate.js +44 -0
- package/src/stack/detectStack.js +213 -0
- package/templates/.claude/agents/bug-debugger.md +57 -0
- package/templates/.claude/agents/feature-implementer.md +55 -0
- package/templates/.claude/config/providers.md +25 -0
- package/templates/.claude/hooks/auto-allow-bash.sh +155 -0
- package/templates/.claude/hooks/auto-prune-bash.sh +75 -0
- package/templates/.claude/hooks/block-dangerous.sh +54 -0
- package/templates/.claude/hooks/compress-output.sh +17 -0
- package/templates/.claude/hooks/protect-files.sh +37 -0
- package/templates/.claude/hooks/reinject-context.sh +28 -0
- package/templates/.claude/hooks/session-start.md +13 -0
- package/templates/.claude/hooks/skill-router.sh +1681 -0
- package/templates/.claude/hooks/verification-guard.sh +271 -0
- package/templates/.claude/settings.json +144 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/mce/mc.xsd +75 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/templates/.claude/skills/_shared/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/pack.py +159 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/unpack.py +29 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validate.py +69 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/base.py +951 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/docx.py +274 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/.claude/skills/_shared/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/.claude/skills/backend-api/SKILL.md +26 -0
- package/templates/.claude/skills/canvas-design/LICENSE.txt +202 -0
- package/templates/.claude/skills/canvas-design/SKILL.md +130 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +93 -0
- package/templates/.claude/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/templates/.claude/skills/code-review/SKILL.md +97 -0
- package/templates/.claude/skills/debugging-toolkit/SKILL.md +156 -0
- package/templates/.claude/skills/delivery/SKILL.md +92 -0
- package/templates/.claude/skills/discover-security/SKILL.md +86 -0
- package/templates/.claude/skills/docker-packaging/SKILL.md +60 -0
- package/templates/.claude/skills/docs-manager/SKILL.md +465 -0
- package/templates/.claude/skills/docs-manager/init-project-docs.sh +70 -0
- package/templates/.claude/skills/docs-manager/templates/README.md.template +50 -0
- package/templates/.claude/skills/docs-manager/templates/agent-roles.md.template +24 -0
- package/templates/.claude/skills/docs-manager/templates/coding-conventions.md.template +28 -0
- package/templates/.claude/skills/docs-manager/templates/memory.md.template +30 -0
- package/templates/.claude/skills/docs-manager/templates/onboarding.md.template +20 -0
- package/templates/.claude/skills/docs-manager/templates/project.md.template +26 -0
- package/templates/.claude/skills/docs-quality/SKILL.md +148 -0
- package/templates/.claude/skills/docx/LICENSE.txt +30 -0
- package/templates/.claude/skills/docx/SKILL.md +197 -0
- package/templates/.claude/skills/docx/docx-js.md +350 -0
- package/templates/.claude/skills/docx/ooxml.md +610 -0
- package/templates/.claude/skills/docx/scripts/__init__.py +1 -0
- package/templates/.claude/skills/docx/scripts/document.py +1276 -0
- package/templates/.claude/skills/docx/scripts/templates/comments.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/templates/.claude/skills/docx/scripts/templates/people.xml +3 -0
- package/templates/.claude/skills/docx/scripts/utilities.py +374 -0
- package/templates/.claude/skills/duraone/SKILL.md +204 -0
- package/templates/.claude/skills/duraone/references/backend.md +636 -0
- package/templates/.claude/skills/duraone/references/frontend.md +1506 -0
- package/templates/.claude/skills/duraone/references/sql.md +631 -0
- package/templates/.claude/skills/duraone/references/workflow.md +520 -0
- package/templates/.claude/skills/executing-plans/SKILL.md +76 -0
- package/templates/.claude/skills/file-organizer/SKILL.md +433 -0
- package/templates/.claude/skills/frontend/SKILL.md +26 -0
- package/templates/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/templates/.claude/skills/frontend-design/SKILL.md +42 -0
- package/templates/.claude/skills/frontend-vue/SKILL.md +127 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Box.vue +137 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Button.vue +93 -0
- package/templates/.claude/skills/frontend-vue/components/Control/ButtonBar.vue +29 -0
- package/templates/.claude/skills/frontend-vue/components/Control/ButtonFloat.vue +62 -0
- package/templates/.claude/skills/frontend-vue/components/Control/CheckButton.vue +75 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Checkbox.vue +58 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Datetime.vue +148 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Dropdownlist.vue +156 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Input.vue +106 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Label.vue +38 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Master/BoxColumn.vue +24 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/Confirm.vue +33 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/Info.vue +32 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/ModalInfo.vue +39 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Popup/Reject.vue +64 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Tag.vue +82 -0
- package/templates/.claude/skills/frontend-vue/components/Control/Upload.vue +61 -0
- package/templates/.claude/skills/frontend-vue/components/ControlMobile/Dropdownlist.vue +103 -0
- package/templates/.claude/skills/frontend-vue/components/ControlMobile/PagingBar.vue +108 -0
- package/templates/.claude/skills/frontend-vue/components/ControlMobile/UploadImage.vue +137 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/AG.vue +806 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/AntTable.vue +253 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/CustomDropdownEditor.vue +43 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/CustomDropdownEditorEnable.vue +55 -0
- package/templates/.claude/skills/frontend-vue/components/Grid/HtmlTable.vue +40 -0
- package/templates/.claude/skills/frontend-vue/components/PDFViewer.vue +25 -0
- package/templates/.claude/skills/frontend-vue/components/Panel/FormView.vue +309 -0
- package/templates/.claude/skills/frontend-vue/components/Partial/Footer.vue +23 -0
- package/templates/.claude/skills/frontend-vue/components/Partial/Header.vue +265 -0
- package/templates/.claude/skills/frontend-vue/components/Partial/Sidebar.vue +122 -0
- package/templates/.claude/skills/frontend-vue/components/Template.vue +16 -0
- package/templates/.claude/skills/frontend-vue/components/View/Form.vue +89 -0
- package/templates/.claude/skills/frontend-vue/composables/indexDBStore.js +140 -0
- package/templates/.claude/skills/frontend-vue/composables/masterApi.js +362 -0
- package/templates/.claude/skills/frontend-vue/composables/state.js +578 -0
- package/templates/.claude/skills/frontend-vue/composables/useRequest.js +221 -0
- package/templates/.claude/skills/frontend-vue/composables/useSession.js +179 -0
- package/templates/.claude/skills/frontend-vue/composables/useTranslation.js +54 -0
- package/templates/.claude/skills/frontend-vue/composables/useWebSocket.js +257 -0
- package/templates/.claude/skills/frontend-vue/composables/userObj.js +111 -0
- package/templates/.claude/skills/frontend-vue/composables/utils.js +322 -0
- package/templates/.claude/skills/frontend-vue/reference/composables-example.vue +320 -0
- package/templates/.claude/skills/frontend-vue/reference/form-example.vue +183 -0
- package/templates/.claude/skills/frontend-vue/reference/grid-example.vue +147 -0
- package/templates/.claude/skills/frontend-vue/reference/masterdata-example/[id].vue +106 -0
- package/templates/.claude/skills/frontend-vue/reference/masterdata-example/index.vue +58 -0
- package/templates/.claude/skills/frontend-vue/reference/popup-example.vue +159 -0
- package/templates/.claude/skills/pdf/LICENSE.txt +30 -0
- package/templates/.claude/skills/pdf/SKILL.md +294 -0
- package/templates/.claude/skills/pdf/forms.md +205 -0
- package/templates/.claude/skills/pdf/reference.md +612 -0
- package/templates/.claude/skills/pdf/scripts/check_bounding_boxes.py +70 -0
- package/templates/.claude/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/templates/.claude/skills/pdf/scripts/check_fillable_fields.py +12 -0
- package/templates/.claude/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/templates/.claude/skills/pdf/scripts/create_validation_image.py +41 -0
- package/templates/.claude/skills/pdf/scripts/extract_form_field_info.py +152 -0
- package/templates/.claude/skills/pdf/scripts/fill_fillable_fields.py +114 -0
- package/templates/.claude/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/templates/.claude/skills/pdf-processing/SKILL.md +107 -0
- package/templates/.claude/skills/pdf-processing-pro/FORMS.md +610 -0
- package/templates/.claude/skills/pdf-processing-pro/OCR.md +137 -0
- package/templates/.claude/skills/pdf-processing-pro/SKILL.md +296 -0
- package/templates/.claude/skills/pdf-processing-pro/TABLES.md +626 -0
- package/templates/.claude/skills/pdf-processing-pro/scripts/analyze_form.py +307 -0
- package/templates/.claude/skills/postgres/SKILL.md +69 -0
- package/templates/.claude/skills/postgres/reference/fn_get_examples.sql +208 -0
- package/templates/.claude/skills/postgres/reference/fn_rpt_examples.sql +239 -0
- package/templates/.claude/skills/postgres/reference/utility_functions.sql +94 -0
- package/templates/.claude/skills/pptx/LICENSE.txt +30 -0
- package/templates/.claude/skills/pptx/SKILL.md +484 -0
- package/templates/.claude/skills/pptx/html2pptx.md +625 -0
- package/templates/.claude/skills/pptx/ooxml.md +427 -0
- package/templates/.claude/skills/pptx/scripts/html2pptx.js +979 -0
- package/templates/.claude/skills/pptx/scripts/inventory.py +1020 -0
- package/templates/.claude/skills/pptx/scripts/rearrange.py +231 -0
- package/templates/.claude/skills/pptx/scripts/replace.py +385 -0
- package/templates/.claude/skills/pptx/scripts/thumbnail.py +450 -0
- package/templates/.claude/skills/repo-maintenance/SKILL.md +97 -0
- package/templates/.claude/skills/research/EXAMPLES.md +434 -0
- package/templates/.claude/skills/research/REFERENCE.md +399 -0
- package/templates/.claude/skills/research/SKILL.md +136 -0
- package/templates/.claude/skills/root-cause-tracing/SKILL.md +174 -0
- package/templates/.claude/skills/root-cause-tracing/find-polluter.sh +63 -0
- package/templates/.claude/skills/sharing-skills/SKILL.md +194 -0
- package/templates/.claude/skills/sql-optimization-patterns/SKILL.md +493 -0
- package/templates/.claude/skills/subagent-driven-development/SKILL.md +189 -0
- package/templates/.claude/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/templates/.claude/skills/systematic-debugging/SKILL.md +295 -0
- package/templates/.claude/skills/systematic-debugging/test-academic.md +14 -0
- package/templates/.claude/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/templates/.claude/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/templates/.claude/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/templates/.claude/skills/test-driven-development/SKILL.md +364 -0
- package/templates/.claude/skills/testing-anti-patterns/SKILL.md +302 -0
- package/templates/.claude/skills/testing-quality/SKILL.md +97 -0
- package/templates/.claude/skills/verification-before-completion/SKILL.md +139 -0
- package/templates/.claude/skills/webapp-testing/LICENSE.txt +202 -0
- package/templates/.claude/skills/webapp-testing/SKILL.md +96 -0
- package/templates/.claude/skills/webapp-testing/examples/console_logging.py +35 -0
- package/templates/.claude/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/templates/.claude/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/templates/.claude/skills/webapp-testing/scripts/with_server.py +106 -0
- package/templates/.claude/ukit/index/build-index.mjs +28 -0
- package/templates/.claude/ukit/index/cache-utils.mjs +140 -0
- package/templates/.claude/ukit/index/lib/index-core.mjs +2800 -0
- package/templates/.claude/ukit/index/query-index.mjs +150 -0
- package/templates/.claude/ukit/index/refresh-index.mjs +57 -0
- package/templates/.claude/ukit/index/reset-auto-permissions.mjs +76 -0
- package/templates/.claude/ukit/index/resolve-context.mjs +279 -0
- package/templates/.claude/ukit/index/route-catalog.mjs +258 -0
- package/templates/.claude/ukit/index/route-task.mjs +1994 -0
- package/templates/.claude/ukit/index/triage.mjs +133 -0
- package/templates/.claude/ukit/index/verify-context.mjs +689 -0
- package/templates/.claude/ukit/runtime/compact-threshold.mjs +1013 -0
- package/templates/.claude/ukit/runtime/output-compression.mjs +1340 -0
- package/templates/.claude/ukit/runtime/reinject-context.mjs +874 -0
- package/templates/.claude/ukit/runtime/token-utils.mjs +500 -0
- package/templates/.codex/README.md +83 -0
- package/templates/.codex/settings.json +187 -0
- package/templates/.gitignore +75 -0
- package/templates/AGENTS.md +116 -0
- package/templates/CLAUDE.md +93 -0
- package/templates/adapter-presets/antigravity/README.md +22 -0
- package/templates/adapter-presets/antigravity/rules.md +49 -0
- package/templates/adapter-presets/claude/settings.local.json +42 -0
- package/templates/adapter-presets/codex/settings.local.json +6 -0
- package/templates/adapter-presets/opencode/opencode.template.json +1 -0
- package/templates/docs/BUGFIX.md +20 -0
- package/templates/docs/BUG_INDEX.md +12 -0
- package/templates/docs/BUG_METRICS.md +7 -0
- package/templates/docs/BUG_TEMPLATE.md +13 -0
- package/templates/docs/CODE_MAP.md +35 -0
- package/templates/docs/INSTALL.md +113 -0
- package/templates/docs/MEMORY.md +49 -0
- package/templates/docs/PROJECT.md +50 -0
- package/templates/docs/UKIT_USAGE_GUIDE.md +147 -0
- package/templates/docs/WORKLOG.md +10 -0
- package/templates/ukit/README.md +14 -0
- package/templates/ukit/storage/cache/compact-history.json +3 -0
- package/templates/ukit/storage/cache/compact-pressure.json +1 -0
- package/templates/ukit/storage/cache/output-history.json +3 -0
- package/templates/ukit/storage/cache/prompt-cache.json +3 -0
- package/templates/ukit/storage/config.json +37 -0
- package/templates/ukit/storage/memory/projects/.gitkeep +2 -0
- package/templates/ukit/storage/memory/sessions/.gitkeep +0 -0
- package/templates/ukit/storage/memory/user.json +5 -0
|
@@ -0,0 +1,1340 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {
|
|
5
|
+
buildCompactMachineKey,
|
|
6
|
+
buildRuntimePaths,
|
|
7
|
+
compressLine,
|
|
8
|
+
compressMarkdownLines,
|
|
9
|
+
estimateTokenCount,
|
|
10
|
+
readJson,
|
|
11
|
+
readPromptCacheEntry,
|
|
12
|
+
recordCompaction,
|
|
13
|
+
writePromptCacheEntry,
|
|
14
|
+
} from './token-utils.mjs';
|
|
15
|
+
import { updateCompactPressureFromOutput } from './compact-threshold.mjs';
|
|
16
|
+
|
|
17
|
+
const ANSI_RE = /\u001b\[[0-9;]*m/g;
|
|
18
|
+
const DEFAULT_MAX_OUTPUT_TOKENS = 180;
|
|
19
|
+
const DEFAULT_OUTPUT_HISTORY_MAX_ENTRIES = 25;
|
|
20
|
+
const RAW_OUTPUT_SAVE_MIN_TOKENS = 600;
|
|
21
|
+
const RAW_OUTPUT_SAVE_MIN_SAVED_TOKENS = 250;
|
|
22
|
+
const GENERIC_NOISE_PATTERNS = [
|
|
23
|
+
/^[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/,
|
|
24
|
+
/^\[[=\-#>\s]{6,}\]/,
|
|
25
|
+
/^progress[:\s]/i,
|
|
26
|
+
/^download(ed|ing)?\b/i,
|
|
27
|
+
/^debug\b/i,
|
|
28
|
+
/^[.+-]{16,}$/,
|
|
29
|
+
/^[╭╰│─└┌┬┴├┤┼\s]+$/u,
|
|
30
|
+
/^\.+$/,
|
|
31
|
+
];
|
|
32
|
+
const GENERIC_IMPORTANT_PATTERNS = [
|
|
33
|
+
/\b(error|fail(?:ed)?|warning|assertionerror|exception|fatal)\b/i,
|
|
34
|
+
/(^|\s)(tests?|specs?)\//i,
|
|
35
|
+
/(^|\s)(src|app|lib|docs|ukit|\.claude|\.codex)\//i,
|
|
36
|
+
/(^|\s)at\s+.*:\d+:\d+/i,
|
|
37
|
+
/\b(Test Files|Tests|Duration|Start at|Summary|Ran all)\b/i,
|
|
38
|
+
/^stderr\s*\|/i,
|
|
39
|
+
/^stdout\s*\|/i,
|
|
40
|
+
/^\s*[✓✗×❯]/,
|
|
41
|
+
/^\s*FAIL(?:ED)?\s+/i,
|
|
42
|
+
/^\s*E\s+/,
|
|
43
|
+
];
|
|
44
|
+
const GENERIC_ANCHOR_PATTERNS = [
|
|
45
|
+
/\b(error|fail(?:ed)?|assertionerror|exception|fatal)\b/i,
|
|
46
|
+
/(^|\s)(tests?|specs?)\//i,
|
|
47
|
+
/(^|\s)(src|app|lib|docs|ukit|\.claude|\.codex)\//i,
|
|
48
|
+
/(^|\s)at\s+.*:\d+:\d+/i,
|
|
49
|
+
/\b(Test Files|Tests|Duration|Start at|Summary|Ran all)\b/i,
|
|
50
|
+
/^stderr\s*\|/i,
|
|
51
|
+
/^\s*[✗×❯]/,
|
|
52
|
+
/^\s*FAIL(?:ED)?\s+/i,
|
|
53
|
+
/^\s*AssertionError:/i,
|
|
54
|
+
];
|
|
55
|
+
const GENERIC_TAIL_PATTERNS = [
|
|
56
|
+
/^\s*(Test Files|Tests|Duration|Start at)\b/i,
|
|
57
|
+
/^\s*=+ .* in [0-9.]+s =+\s*$/i,
|
|
58
|
+
/^\s*short test summary info\b/i,
|
|
59
|
+
];
|
|
60
|
+
const OUTPUT_PROFILES = [
|
|
61
|
+
{
|
|
62
|
+
id: 'paths',
|
|
63
|
+
commandPatterns: [
|
|
64
|
+
/\brg\s+--files\b/i,
|
|
65
|
+
/\bgit\s+ls-files\b/i,
|
|
66
|
+
/(?:^|\s)(?:find|fd)\b/i,
|
|
67
|
+
/(?:^|\s)tree\b/i,
|
|
68
|
+
/\bls\b[^\n]*\s-R(?:\s|$)/i,
|
|
69
|
+
],
|
|
70
|
+
importantPatterns: [],
|
|
71
|
+
anchorPatterns: [],
|
|
72
|
+
tailPatterns: [],
|
|
73
|
+
noisePatterns: [],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'search',
|
|
77
|
+
commandPatterns: [
|
|
78
|
+
/(?:^|\s)(?:rg|ripgrep)\b/i,
|
|
79
|
+
/(?:^|\s)grep\b/i,
|
|
80
|
+
/\bgit\s+grep\b/i,
|
|
81
|
+
],
|
|
82
|
+
importantPatterns: [],
|
|
83
|
+
anchorPatterns: [],
|
|
84
|
+
tailPatterns: [],
|
|
85
|
+
noisePatterns: [],
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'package-install',
|
|
89
|
+
commandPatterns: [
|
|
90
|
+
/\b(?:npm|pnpm|yarn|bun)\b.*\b(?:install|add|ci|update)\b/i,
|
|
91
|
+
],
|
|
92
|
+
importantPatterns: [
|
|
93
|
+
/\b(?:deprecated|unmet peer|peer dependenc(?:y|ies)|ignored build scripts|approve-builds)\b/i,
|
|
94
|
+
/\b(?:vulnerab(?:ility|ilities)|audited \d+ packages|added \d+ packages|removed \d+ packages|changed \d+ packages)\b/i,
|
|
95
|
+
/\b(?:npm ERR!|ERR_PNPM|ELIFECYCLE|ERESOLVE|ENOENT|EACCES)\b/i,
|
|
96
|
+
/^Packages:\s*[+~-]?\d+/i,
|
|
97
|
+
/^Done in\b/i,
|
|
98
|
+
],
|
|
99
|
+
anchorPatterns: [
|
|
100
|
+
/\b(?:deprecated|unmet peer|peer dependenc(?:y|ies)|ignored build scripts)\b/i,
|
|
101
|
+
/\b(?:vulnerab(?:ility|ilities)|npm ERR!|ERR_PNPM|ELIFECYCLE|ERESOLVE|ENOENT|EACCES)\b/i,
|
|
102
|
+
/^Packages:\s*[+~-]?\d+/i,
|
|
103
|
+
/^Done in\b/i,
|
|
104
|
+
],
|
|
105
|
+
tailPatterns: [
|
|
106
|
+
/^Done in\b/i,
|
|
107
|
+
],
|
|
108
|
+
noisePatterns: [
|
|
109
|
+
/^Progress:\s/i,
|
|
110
|
+
/^\+\+\++$/,
|
|
111
|
+
/^\s*(?:dependencies|devDependencies|optionalDependencies):\s*$/i,
|
|
112
|
+
/^[+]\s+\S+/,
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'test-runner',
|
|
117
|
+
commandPatterns: [
|
|
118
|
+
/\b(?:vitest|jest|mocha|ava|pytest|py\.test)\b/i,
|
|
119
|
+
/\b(?:npm|pnpm|yarn|bun)\s+test\b/i,
|
|
120
|
+
],
|
|
121
|
+
importantPatterns: [
|
|
122
|
+
/^\s*FAIL(?:ED)?\b/i,
|
|
123
|
+
/^\s*PASS\b/i,
|
|
124
|
+
/^\s*AssertionError:/i,
|
|
125
|
+
/^\s*short test summary info\b/i,
|
|
126
|
+
],
|
|
127
|
+
anchorPatterns: [
|
|
128
|
+
/^\s*FAIL(?:ED)?\b/i,
|
|
129
|
+
/^\s*AssertionError:/i,
|
|
130
|
+
/^\s*short test summary info\b/i,
|
|
131
|
+
],
|
|
132
|
+
tailPatterns: [
|
|
133
|
+
/^\s*short test summary info\b/i,
|
|
134
|
+
/^\s*=+ .* in [0-9.]+s =+\s*$/i,
|
|
135
|
+
],
|
|
136
|
+
noisePatterns: [
|
|
137
|
+
/^\s*collected \d+ items?\s*$/i,
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: 'git',
|
|
142
|
+
commandPatterns: [
|
|
143
|
+
/\bgit\s+status\b/i,
|
|
144
|
+
/\bgit\s+diff\b/i,
|
|
145
|
+
],
|
|
146
|
+
importantPatterns: [
|
|
147
|
+
/^(?:M|A|D|R|C|UU|\?\?)\s+/,
|
|
148
|
+
/\bfiles? changed\b/i,
|
|
149
|
+
/\binsertions?\(\+\)/i,
|
|
150
|
+
/\bdeletions?\(-\)/i,
|
|
151
|
+
/^diff --git\b/i,
|
|
152
|
+
],
|
|
153
|
+
anchorPatterns: [
|
|
154
|
+
/^(?:M|A|D|R|C|UU|\?\?)\s+/,
|
|
155
|
+
/\bfiles? changed\b/i,
|
|
156
|
+
/\binsertions?\(\+\)/i,
|
|
157
|
+
/\bdeletions?\(-\)/i,
|
|
158
|
+
],
|
|
159
|
+
tailPatterns: [
|
|
160
|
+
/\bfiles? changed\b/i,
|
|
161
|
+
/\binsertions?\(\+\)/i,
|
|
162
|
+
/\bdeletions?\(-\)/i,
|
|
163
|
+
],
|
|
164
|
+
noisePatterns: [],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 'docker-logs',
|
|
168
|
+
commandPatterns: [
|
|
169
|
+
/\bdocker(?:\s+compose)?\s+logs\b/i,
|
|
170
|
+
],
|
|
171
|
+
importantPatterns: [
|
|
172
|
+
/\b(error|exception|warn|fatal|traceback|exited with code)\b/i,
|
|
173
|
+
/^[a-z0-9_.-]+(?:-\d+)?\s+\|/i,
|
|
174
|
+
],
|
|
175
|
+
anchorPatterns: [
|
|
176
|
+
/\b(error|exception|fatal|traceback|exited with code)\b/i,
|
|
177
|
+
/^[a-z0-9_.-]+(?:-\d+)?\s+\|.*\b(error|exception|fatal|traceback)\b/i,
|
|
178
|
+
],
|
|
179
|
+
tailPatterns: [],
|
|
180
|
+
noisePatterns: [
|
|
181
|
+
/^Attaching to\b/i,
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
async function readStdin() {
|
|
187
|
+
if (process.stdin.isTTY) return '';
|
|
188
|
+
|
|
189
|
+
const chunks = [];
|
|
190
|
+
for await (const chunk of process.stdin) {
|
|
191
|
+
chunks.push(typeof chunk === 'string' ? chunk : chunk.toString('utf8'));
|
|
192
|
+
}
|
|
193
|
+
return chunks.join('');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function stripAnsi(value) {
|
|
197
|
+
return String(value ?? '').replace(ANSI_RE, '');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function splitLines(value) {
|
|
201
|
+
return stripAnsi(value)
|
|
202
|
+
.split(/\r?\n/)
|
|
203
|
+
.map((line) => line.replace(/\s+$/g, ''));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function normalizeLineForDedupe(line) {
|
|
207
|
+
return String(line ?? '').trim().replace(/\s+/g, ' ').toLowerCase();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function matchesAnyPattern(line, patterns = []) {
|
|
211
|
+
return patterns.some((pattern) => pattern.test(line));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function detectOutputProfile(command) {
|
|
215
|
+
const commandText = String(command ?? '').trim();
|
|
216
|
+
return OUTPUT_PROFILES.find((profile) => matchesAnyPattern(commandText, profile.commandPatterns)) ?? null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function escapeRegExp(value) {
|
|
220
|
+
return String(value ?? '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function buildProjectRootVariants(projectRoot) {
|
|
224
|
+
const trimmed = String(projectRoot ?? '').trim();
|
|
225
|
+
if (!trimmed) {
|
|
226
|
+
return [];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const resolved = path.resolve(trimmed).replace(/[\\/]+$/g, '');
|
|
230
|
+
return [...new Set([
|
|
231
|
+
resolved,
|
|
232
|
+
resolved.replace(/\\/g, '/'),
|
|
233
|
+
resolved.replace(/\//g, '\\'),
|
|
234
|
+
].filter(Boolean))];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function shortenProjectRootSegments(value, projectRoot) {
|
|
238
|
+
let next = String(value ?? '');
|
|
239
|
+
if (!next.trim()) {
|
|
240
|
+
return next;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
for (const variant of buildProjectRootVariants(projectRoot)) {
|
|
244
|
+
const escaped = escapeRegExp(variant);
|
|
245
|
+
next = next
|
|
246
|
+
.replace(new RegExp(`${escaped}(?:[\\\\/])`, 'g'), '')
|
|
247
|
+
.replace(new RegExp(`${escaped}(?=$|[\\s)'":])`, 'g'), '.');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return next;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function normalizeRuntimeRelativePath(filePath) {
|
|
254
|
+
return String(filePath ?? '')
|
|
255
|
+
.trim()
|
|
256
|
+
.replace(/\\/g, '/')
|
|
257
|
+
.replace(/^\.\//, '');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function buildRawOutputRecoveryReason({ exitCode = null, tokensBefore = 0, savedTokens = 0 } = {}) {
|
|
261
|
+
if (Number.isFinite(Number(exitCode)) && Number(exitCode) !== 0) {
|
|
262
|
+
return 'failure';
|
|
263
|
+
}
|
|
264
|
+
if (Number(tokensBefore) >= RAW_OUTPUT_SAVE_MIN_TOKENS) {
|
|
265
|
+
return 'large-output';
|
|
266
|
+
}
|
|
267
|
+
if (Number(savedTokens) >= RAW_OUTPUT_SAVE_MIN_SAVED_TOKENS) {
|
|
268
|
+
return 'high-savings';
|
|
269
|
+
}
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function sanitizeFileComponent(value, { fallback = 'output', maxLength = 48 } = {}) {
|
|
274
|
+
const normalized = String(value ?? '')
|
|
275
|
+
.trim()
|
|
276
|
+
.toLowerCase()
|
|
277
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
278
|
+
.replace(/^-+|-+$/g, '')
|
|
279
|
+
.slice(0, maxLength);
|
|
280
|
+
return normalized || fallback;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function buildRecoveryFileName({
|
|
284
|
+
command = '',
|
|
285
|
+
summary = '',
|
|
286
|
+
profile = 'generic',
|
|
287
|
+
exitCode = null,
|
|
288
|
+
} = {}) {
|
|
289
|
+
const slug = sanitizeFileComponent(String(command).split(/\s+/).slice(0, 3).join('-'), {
|
|
290
|
+
fallback: sanitizeFileComponent(profile, { fallback: 'output', maxLength: 16 }),
|
|
291
|
+
maxLength: 24,
|
|
292
|
+
});
|
|
293
|
+
const fingerprint = buildCompactMachineKey('tool-output-recovery-v1', {
|
|
294
|
+
command: String(command ?? '').trim(),
|
|
295
|
+
summary: String(summary ?? '').trim(),
|
|
296
|
+
profile: String(profile ?? 'generic').trim(),
|
|
297
|
+
exitCode: Number.isFinite(Number(exitCode)) ? Number(exitCode) : null,
|
|
298
|
+
}).split(':').at(-1)?.replace(/[^a-z0-9_-]/gi, '-').slice(0, 16) || 'recovery';
|
|
299
|
+
return `${slug}-${fingerprint}.log`;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function buildRawOutputText({ command = '', stdout = '', stderr = '', exitCode = null } = {}) {
|
|
303
|
+
const sections = [
|
|
304
|
+
`Command: ${String(command ?? '').trim() || 'unknown command'}`,
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
if (Number.isFinite(Number(exitCode))) {
|
|
308
|
+
sections.push(`Exit code: ${Number(exitCode)}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
sections.push('');
|
|
312
|
+
|
|
313
|
+
if (String(stdout ?? '').length > 0) {
|
|
314
|
+
sections.push('--- stdout ---');
|
|
315
|
+
sections.push(String(stdout));
|
|
316
|
+
sections.push('');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (String(stderr ?? '').length > 0) {
|
|
320
|
+
sections.push('--- stderr ---');
|
|
321
|
+
sections.push(String(stderr));
|
|
322
|
+
sections.push('');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return `${sections.join('\n').trimEnd()}\n`;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function buildRecoveryHintSummary(summary, rawPath, { tokensBefore = 0 } = {}) {
|
|
329
|
+
const recoveryLine = `- Full output: ${rawPath}`;
|
|
330
|
+
const summaryLines = String(summary ?? '')
|
|
331
|
+
.split(/\r?\n/)
|
|
332
|
+
.map((line) => line.trim())
|
|
333
|
+
.filter(Boolean);
|
|
334
|
+
if (summaryLines.length === 0) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const directCandidate = [...summaryLines, recoveryLine].join('\n');
|
|
339
|
+
if (estimateTokenCount(directCandidate) < tokensBefore) {
|
|
340
|
+
return directCandidate;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const removalPatterns = [
|
|
344
|
+
/^-\s*stderr\s*\|/i,
|
|
345
|
+
/^-\s*FAIL(?:ED)?\b/i,
|
|
346
|
+
/^-\s*PASS\b/i,
|
|
347
|
+
/^-\s*(?:Test Files|Tests|Duration|Start at)\b/i,
|
|
348
|
+
];
|
|
349
|
+
|
|
350
|
+
for (const pattern of removalPatterns) {
|
|
351
|
+
const index = summaryLines.findIndex((line, lineIndex) => lineIndex > 0 && pattern.test(line));
|
|
352
|
+
if (index < 0) continue;
|
|
353
|
+
const candidate = [...summaryLines.slice(0, index), ...summaryLines.slice(index + 1), recoveryLine].join('\n');
|
|
354
|
+
if (estimateTokenCount(candidate) < tokensBefore) {
|
|
355
|
+
return candidate;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
for (let index = summaryLines.length - 1; index >= 1; index -= 1) {
|
|
360
|
+
const candidate = [...summaryLines.slice(0, index), ...summaryLines.slice(index + 1), recoveryLine].join('\n');
|
|
361
|
+
if (estimateTokenCount(candidate) < tokensBefore) {
|
|
362
|
+
return candidate;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async function persistRawOutput(projectRoot, {
|
|
370
|
+
command = '',
|
|
371
|
+
summary = '',
|
|
372
|
+
profile = 'generic',
|
|
373
|
+
stdout = '',
|
|
374
|
+
stderr = '',
|
|
375
|
+
exitCode = null,
|
|
376
|
+
} = {}) {
|
|
377
|
+
const runtimePaths = buildRuntimePaths(projectRoot);
|
|
378
|
+
const teeCacheDir = runtimePaths.teeCacheDir ?? path.join(runtimePaths.cacheRoot, 'tee');
|
|
379
|
+
const fileName = buildRecoveryFileName({
|
|
380
|
+
command,
|
|
381
|
+
summary,
|
|
382
|
+
profile,
|
|
383
|
+
exitCode,
|
|
384
|
+
});
|
|
385
|
+
const absolutePath = path.join(teeCacheDir, fileName);
|
|
386
|
+
const rawOutputText = buildRawOutputText({
|
|
387
|
+
command,
|
|
388
|
+
stdout,
|
|
389
|
+
stderr,
|
|
390
|
+
exitCode,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
await fs.mkdir(teeCacheDir, { recursive: true });
|
|
394
|
+
await fs.writeFile(absolutePath, rawOutputText, 'utf8');
|
|
395
|
+
|
|
396
|
+
return {
|
|
397
|
+
rawSaved: true,
|
|
398
|
+
rawPath: normalizeRuntimeRelativePath(path.relative(projectRoot, absolutePath)),
|
|
399
|
+
rawBytes: Buffer.byteLength(rawOutputText, 'utf8'),
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function isProgressNoise(line, profile = null) {
|
|
404
|
+
const normalized = String(line ?? '').trim();
|
|
405
|
+
if (!normalized) return true;
|
|
406
|
+
return matchesAnyPattern(normalized, [
|
|
407
|
+
...GENERIC_NOISE_PATTERNS,
|
|
408
|
+
...(profile?.noisePatterns ?? []),
|
|
409
|
+
]);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function isImportantLine(line, profile = null) {
|
|
413
|
+
const normalized = String(line ?? '').trim();
|
|
414
|
+
if (!normalized) return false;
|
|
415
|
+
return matchesAnyPattern(normalized, [
|
|
416
|
+
...GENERIC_IMPORTANT_PATTERNS,
|
|
417
|
+
...(profile?.importantPatterns ?? []),
|
|
418
|
+
]);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function isAnchorLine(line, profile = null) {
|
|
422
|
+
const normalized = String(line ?? '').trim();
|
|
423
|
+
if (!normalized) return false;
|
|
424
|
+
return matchesAnyPattern(normalized, [
|
|
425
|
+
...GENERIC_ANCHOR_PATTERNS,
|
|
426
|
+
...(profile?.anchorPatterns ?? []),
|
|
427
|
+
]);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function isTailSummaryLine(line, profile = null) {
|
|
431
|
+
const normalized = String(line ?? '').trim();
|
|
432
|
+
if (!normalized) return false;
|
|
433
|
+
return matchesAnyPattern(normalized, [
|
|
434
|
+
...GENERIC_TAIL_PATTERNS,
|
|
435
|
+
...(profile?.tailPatterns ?? []),
|
|
436
|
+
]);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function buildCompactedSummaryLines(lines, { maxTokens = DEFAULT_MAX_OUTPUT_TOKENS, maxLines = 10, forceFirstCount = 1 } = {}) {
|
|
440
|
+
const sourceLines = Array.isArray(lines) ? lines : [];
|
|
441
|
+
const selected = [];
|
|
442
|
+
const seen = new Set();
|
|
443
|
+
let usedTokens = 0;
|
|
444
|
+
let nonEmptyCount = 0;
|
|
445
|
+
|
|
446
|
+
for (const line of sourceLines) {
|
|
447
|
+
const compressed = compressLine(line);
|
|
448
|
+
if (!compressed) continue;
|
|
449
|
+
|
|
450
|
+
const dedupeKey = compressed.toLowerCase();
|
|
451
|
+
if (seen.has(dedupeKey)) continue;
|
|
452
|
+
|
|
453
|
+
const tokens = estimateTokenCount(compressed);
|
|
454
|
+
const forceLine = nonEmptyCount < forceFirstCount;
|
|
455
|
+
const wouldOverflow = selected.length > 0 && (usedTokens + tokens) > maxTokens;
|
|
456
|
+
const wouldExceedLines = nonEmptyCount >= maxLines;
|
|
457
|
+
|
|
458
|
+
if (!forceLine && (wouldOverflow || wouldExceedLines)) {
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
if (forceLine && wouldExceedLines) {
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
selected.push(compressed);
|
|
466
|
+
seen.add(dedupeKey);
|
|
467
|
+
usedTokens += tokens;
|
|
468
|
+
nonEmptyCount += 1;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (selected.length === 0 && sourceLines.length > 0) {
|
|
472
|
+
return compressMarkdownLines(sourceLines, { maxTokens, maxLines, preserveFirstLine: true });
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return selected;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function findMissingAnchors(summary, anchorLines) {
|
|
479
|
+
const summaryText = String(summary ?? '').toLowerCase();
|
|
480
|
+
return anchorLines.filter((line) => {
|
|
481
|
+
const compressed = compressLine(line).toLowerCase();
|
|
482
|
+
return compressed && !summaryText.includes(compressed);
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function uniqueLines(lines) {
|
|
487
|
+
const seen = new Set();
|
|
488
|
+
const unique = [];
|
|
489
|
+
for (const line of lines) {
|
|
490
|
+
const normalized = normalizeLineForDedupe(line);
|
|
491
|
+
if (!normalized || seen.has(normalized)) continue;
|
|
492
|
+
seen.add(normalized);
|
|
493
|
+
unique.push(String(line ?? '').trim());
|
|
494
|
+
}
|
|
495
|
+
return unique;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function looksLikeSearchPath(filePath) {
|
|
499
|
+
const normalized = String(filePath ?? '').trim();
|
|
500
|
+
if (!normalized) return false;
|
|
501
|
+
return /[\\/]/.test(normalized) || normalized.startsWith('.') || /\.(?:[a-z0-9]{1,10})(?:$|:)/i.test(normalized);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function parseSearchMatchLine(line) {
|
|
505
|
+
const trimmed = String(line ?? '').trim().replace(/^stderr:\s*/i, '');
|
|
506
|
+
if (!trimmed) return null;
|
|
507
|
+
|
|
508
|
+
let match = trimmed.match(/^(.+?):(\d+):(\d+):(.*)$/);
|
|
509
|
+
if (match && looksLikeSearchPath(match[1])) {
|
|
510
|
+
return {
|
|
511
|
+
rawLine: trimmed,
|
|
512
|
+
filePath: match[1],
|
|
513
|
+
anchor: `${match[1]}:${match[2]}:${match[3]}`,
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
match = trimmed.match(/^(.+?):(\d+):(.*)$/);
|
|
518
|
+
if (match && looksLikeSearchPath(match[1])) {
|
|
519
|
+
return {
|
|
520
|
+
rawLine: trimmed,
|
|
521
|
+
filePath: match[1],
|
|
522
|
+
anchor: `${match[1]}:${match[2]}`,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
match = trimmed.match(/^(.+?):(.*)$/);
|
|
527
|
+
if (match && looksLikeSearchPath(match[1])) {
|
|
528
|
+
return {
|
|
529
|
+
rawLine: trimmed,
|
|
530
|
+
filePath: match[1],
|
|
531
|
+
anchor: match[1],
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function buildSearchResultSelection(lines) {
|
|
539
|
+
const matches = [];
|
|
540
|
+
const extras = [];
|
|
541
|
+
const seenRaw = new Set();
|
|
542
|
+
for (const line of lines) {
|
|
543
|
+
const trimmed = String(line ?? '').trim();
|
|
544
|
+
if (!trimmed) continue;
|
|
545
|
+
const parsed = parseSearchMatchLine(trimmed);
|
|
546
|
+
if (parsed) {
|
|
547
|
+
const key = normalizeLineForDedupe(parsed.rawLine);
|
|
548
|
+
if (!seenRaw.has(key)) {
|
|
549
|
+
seenRaw.add(key);
|
|
550
|
+
matches.push(parsed);
|
|
551
|
+
}
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (/^Binary file .* matches$/i.test(trimmed) || /^\d+\s+matches?$/i.test(trimmed)) {
|
|
556
|
+
extras.push(trimmed);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (matches.length === 0) {
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
const detailedMatches = [];
|
|
563
|
+
const usedDetailedKeys = new Set();
|
|
564
|
+
const seenDetailedFiles = new Set();
|
|
565
|
+
for (const match of matches) {
|
|
566
|
+
if (detailedMatches.length >= 2) break;
|
|
567
|
+
if (seenDetailedFiles.has(match.filePath)) continue;
|
|
568
|
+
detailedMatches.push(match);
|
|
569
|
+
usedDetailedKeys.add(normalizeLineForDedupe(match.rawLine));
|
|
570
|
+
seenDetailedFiles.add(match.filePath);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
for (const match of matches) {
|
|
574
|
+
if (detailedMatches.length >= 2) break;
|
|
575
|
+
const key = normalizeLineForDedupe(match.rawLine);
|
|
576
|
+
if (usedDetailedKeys.has(key)) continue;
|
|
577
|
+
detailedMatches.push(match);
|
|
578
|
+
usedDetailedKeys.add(key);
|
|
579
|
+
}
|
|
580
|
+
const remainingAnchors = uniqueLines(
|
|
581
|
+
matches
|
|
582
|
+
.filter((match) => !usedDetailedKeys.has(normalizeLineForDedupe(match.rawLine)))
|
|
583
|
+
.map((match) => match.anchor),
|
|
584
|
+
);
|
|
585
|
+
const shownAnchors = remainingAnchors.slice(0, 3);
|
|
586
|
+
const remainingAnchorCount = Math.max(0, remainingAnchors.length - shownAnchors.length);
|
|
587
|
+
|
|
588
|
+
return {
|
|
589
|
+
anchorLines: uniqueLines([
|
|
590
|
+
...detailedMatches.map((match) => match.rawLine),
|
|
591
|
+
...remainingAnchors.slice(0, 6),
|
|
592
|
+
]),
|
|
593
|
+
candidateLines: uniqueLines([
|
|
594
|
+
...detailedMatches.map((match) => match.rawLine),
|
|
595
|
+
shownAnchors.length > 0
|
|
596
|
+
? `Match anchors: ${shownAnchors.join(', ')}${remainingAnchorCount > 0 ? `, +${remainingAnchorCount} more` : ''}`
|
|
597
|
+
: null,
|
|
598
|
+
...extras.slice(0, 1),
|
|
599
|
+
].filter(Boolean)),
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function parsePathListLine(line) {
|
|
604
|
+
const trimmed = String(line ?? '').trim().replace(/^stderr:\s*/i, '');
|
|
605
|
+
if (!trimmed || /[:*?"<>|]$/.test(trimmed) || /\s{2,}/.test(trimmed)) return null;
|
|
606
|
+
if (!(/[\\/]/.test(trimmed) || trimmed.startsWith('.') || /\.[a-z0-9]{1,10}$/i.test(trimmed))) return null;
|
|
607
|
+
return trimmed;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function cleanStructuredPathSegment(value) {
|
|
611
|
+
return String(value ?? '')
|
|
612
|
+
.trim()
|
|
613
|
+
.replace(/^['"]|['"]$/g, '')
|
|
614
|
+
.replace(/[\\/]+$/g, '');
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function normalizeStructuredPath(parts) {
|
|
618
|
+
return (Array.isArray(parts) ? parts : [parts])
|
|
619
|
+
.flatMap((part) => String(part ?? '').split(/[\\/]/))
|
|
620
|
+
.map((segment) => segment.trim())
|
|
621
|
+
.filter(Boolean)
|
|
622
|
+
.join('/');
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function filterLeafStructuredPaths(paths) {
|
|
626
|
+
const uniquePaths = uniqueLines(paths);
|
|
627
|
+
const parentPaths = new Set();
|
|
628
|
+
|
|
629
|
+
for (const filePath of uniquePaths) {
|
|
630
|
+
const parts = String(filePath ?? '').split('/').filter(Boolean);
|
|
631
|
+
while (parts.length > 1) {
|
|
632
|
+
parts.pop();
|
|
633
|
+
parentPaths.add(parts.join('/'));
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const leafPaths = uniquePaths.filter((filePath) => !parentPaths.has(filePath));
|
|
638
|
+
return leafPaths.length > 0 ? leafPaths : uniquePaths;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function buildTreePathList(lines) {
|
|
642
|
+
const paths = [];
|
|
643
|
+
const stack = [];
|
|
644
|
+
let rootOffset = 0;
|
|
645
|
+
|
|
646
|
+
for (const line of lines) {
|
|
647
|
+
const raw = String(line ?? '')
|
|
648
|
+
.replace(/^stderr:\s*/i, '')
|
|
649
|
+
.replace(/\s+$/g, '');
|
|
650
|
+
const trimmed = raw.trim();
|
|
651
|
+
if (!trimmed || /^\d+\s+directories?,\s+\d+\s+files?$/i.test(trimmed)) continue;
|
|
652
|
+
|
|
653
|
+
if (trimmed === '.' || trimmed === './') {
|
|
654
|
+
stack.length = 0;
|
|
655
|
+
rootOffset = 0;
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const branchMatch = raw.match(/^((?:[│ ]{4})*)(?:[├└]── )(.+)$/u);
|
|
660
|
+
if (branchMatch) {
|
|
661
|
+
const depth = Math.floor(branchMatch[1].length / 4) + rootOffset;
|
|
662
|
+
const name = cleanStructuredPathSegment(branchMatch[2]);
|
|
663
|
+
if (!name || name === '.' || name === '..') continue;
|
|
664
|
+
stack[depth] = name;
|
|
665
|
+
stack.length = depth + 1;
|
|
666
|
+
const filePath = normalizeStructuredPath(stack);
|
|
667
|
+
if (filePath) paths.push(filePath);
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (/^[│├└]/u.test(trimmed) || /:$/.test(trimmed) || /^total\s+\d+/i.test(trimmed)) {
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const name = cleanStructuredPathSegment(trimmed);
|
|
676
|
+
if (!name || name === '.' || name === '..') continue;
|
|
677
|
+
stack[0] = name;
|
|
678
|
+
stack.length = 1;
|
|
679
|
+
rootOffset = 1;
|
|
680
|
+
const filePath = normalizeStructuredPath(stack);
|
|
681
|
+
if (filePath) paths.push(filePath);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
return filterLeafStructuredPaths(paths);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function buildRecursiveListingPathList(lines) {
|
|
688
|
+
const sectionDirs = new Set();
|
|
689
|
+
const paths = [];
|
|
690
|
+
let currentDir = null;
|
|
691
|
+
|
|
692
|
+
for (const line of lines) {
|
|
693
|
+
const trimmed = String(line ?? '')
|
|
694
|
+
.trim()
|
|
695
|
+
.replace(/^stderr:\s*/i, '');
|
|
696
|
+
if (!trimmed || /^total\s+\d+/i.test(trimmed)) continue;
|
|
697
|
+
|
|
698
|
+
const sectionMatch = trimmed.match(/^(.+):$/);
|
|
699
|
+
if (sectionMatch) {
|
|
700
|
+
const sectionPath = cleanStructuredPathSegment(sectionMatch[1]);
|
|
701
|
+
if (!sectionPath) continue;
|
|
702
|
+
currentDir = sectionPath === '.'
|
|
703
|
+
? '.'
|
|
704
|
+
: normalizeStructuredPath(sectionPath);
|
|
705
|
+
if (currentDir && currentDir !== '.') {
|
|
706
|
+
sectionDirs.add(currentDir);
|
|
707
|
+
}
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (!currentDir || trimmed === '.' || trimmed === '..') continue;
|
|
712
|
+
|
|
713
|
+
const entryName = cleanStructuredPathSegment(trimmed);
|
|
714
|
+
if (!entryName) continue;
|
|
715
|
+
|
|
716
|
+
const filePath = currentDir === '.'
|
|
717
|
+
? normalizeStructuredPath([entryName])
|
|
718
|
+
: normalizeStructuredPath([currentDir, entryName]);
|
|
719
|
+
if (filePath) {
|
|
720
|
+
paths.push(filePath);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const leafPaths = uniqueLines(paths.filter((filePath) => !sectionDirs.has(filePath)));
|
|
725
|
+
return leafPaths.length > 0 ? leafPaths : uniqueLines(paths);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function extractPathCandidates(lines) {
|
|
729
|
+
const treePaths = buildTreePathList(lines);
|
|
730
|
+
const recursivePaths = buildRecursiveListingPathList(lines);
|
|
731
|
+
const directPaths = uniqueLines(lines.map((line) => parsePathListLine(line)).filter(Boolean));
|
|
732
|
+
const hasRecursiveSections = lines.some((line) => /^.+:\s*$/.test(String(line ?? '').trim().replace(/^stderr:\s*/i, '')));
|
|
733
|
+
|
|
734
|
+
if (hasRecursiveSections && recursivePaths.length > 0) {
|
|
735
|
+
return recursivePaths;
|
|
736
|
+
}
|
|
737
|
+
if (treePaths.length > 0 && treePaths.length >= recursivePaths.length && treePaths.length >= directPaths.length) {
|
|
738
|
+
return treePaths;
|
|
739
|
+
}
|
|
740
|
+
if (recursivePaths.length > 0 && recursivePaths.length >= directPaths.length) {
|
|
741
|
+
return recursivePaths;
|
|
742
|
+
}
|
|
743
|
+
return directPaths;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function getPathAreaKey(filePath) {
|
|
747
|
+
const parts = String(filePath ?? '').split(/[\\/]/).filter(Boolean);
|
|
748
|
+
if (parts[0] === '.') parts.shift();
|
|
749
|
+
if (parts.length === 0) return String(filePath ?? '');
|
|
750
|
+
if (parts[0] === 'docs') return 'docs';
|
|
751
|
+
if (parts.length >= 2 && ['src', 'tests', 'test', 'app', 'lib'].includes(parts[0])) return `${parts[0]}/${parts[1]}`;
|
|
752
|
+
return parts[0];
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
function getPathTopLevelKey(filePath) {
|
|
756
|
+
const parts = String(filePath ?? '').split(/[\\/]/).filter(Boolean);
|
|
757
|
+
if (parts[0] === '.') parts.shift();
|
|
758
|
+
return parts[0] || String(filePath ?? '');
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
function buildPathListSelection(lines) {
|
|
762
|
+
const paths = extractPathCandidates(lines);
|
|
763
|
+
if (paths.length === 0) return null;
|
|
764
|
+
if (paths.length <= 6) return { anchorLines: paths, candidateLines: paths };
|
|
765
|
+
|
|
766
|
+
const detailedPaths = [];
|
|
767
|
+
const usedAreas = new Set();
|
|
768
|
+
for (const filePath of paths) {
|
|
769
|
+
const areaKey = getPathAreaKey(filePath);
|
|
770
|
+
if (usedAreas.has(areaKey)) continue;
|
|
771
|
+
detailedPaths.push(filePath);
|
|
772
|
+
usedAreas.add(areaKey);
|
|
773
|
+
if (detailedPaths.length >= 4) break;
|
|
774
|
+
}
|
|
775
|
+
for (const filePath of paths) {
|
|
776
|
+
if (detailedPaths.length >= 4) break;
|
|
777
|
+
if (detailedPaths.includes(filePath)) continue;
|
|
778
|
+
detailedPaths.push(filePath);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const remainingPaths = paths.filter((filePath) => !detailedPaths.includes(filePath));
|
|
782
|
+
const shownAnchors = [];
|
|
783
|
+
const shownAnchorTopLevels = new Set();
|
|
784
|
+
for (const filePath of remainingPaths) {
|
|
785
|
+
const topLevelKey = getPathTopLevelKey(filePath);
|
|
786
|
+
if (shownAnchorTopLevels.has(topLevelKey)) continue;
|
|
787
|
+
shownAnchors.push(filePath);
|
|
788
|
+
shownAnchorTopLevels.add(topLevelKey);
|
|
789
|
+
if (shownAnchors.length >= 2) break;
|
|
790
|
+
}
|
|
791
|
+
for (const filePath of remainingPaths) {
|
|
792
|
+
if (shownAnchors.length >= 2) break;
|
|
793
|
+
if (shownAnchors.includes(filePath)) continue;
|
|
794
|
+
shownAnchors.push(filePath);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
return {
|
|
798
|
+
anchorLines: uniqueLines([...detailedPaths, ...shownAnchors]),
|
|
799
|
+
candidateLines: uniqueLines([
|
|
800
|
+
...detailedPaths,
|
|
801
|
+
shownAnchors.length > 0
|
|
802
|
+
? `Path anchors: ${shownAnchors.join(', ')}${remainingPaths.length > shownAnchors.length ? `, +${remainingPaths.length - shownAnchors.length} more` : ''}`
|
|
803
|
+
: null,
|
|
804
|
+
].filter(Boolean)),
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function parseGitChangeLine(line) {
|
|
809
|
+
const trimmed = String(line ?? '').trim().replace(/^stderr:\s*/i, '');
|
|
810
|
+
if (!trimmed) return null;
|
|
811
|
+
|
|
812
|
+
const statusMatch = trimmed.match(/^([ MADRCU?]{1,2})\s+(.+)$/);
|
|
813
|
+
if (statusMatch && looksLikeSearchPath(statusMatch[2].split(' -> ').at(-1))) {
|
|
814
|
+
const status = statusMatch[1].replace(/\s+/g, ' ').trim() || '?';
|
|
815
|
+
const filePath = statusMatch[2].split(' -> ').at(-1);
|
|
816
|
+
return { rawLine: trimmed, anchor: `${status} ${filePath}`, filePath, kind: 'status' };
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const statMatch = trimmed.match(/^(.+?)\s+\|\s+\d+\s+[+\-]+$/);
|
|
820
|
+
if (statMatch && looksLikeSearchPath(statMatch[1].trim())) {
|
|
821
|
+
const filePath = statMatch[1].trim();
|
|
822
|
+
return { rawLine: trimmed, anchor: filePath, filePath, kind: 'stat' };
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const filePath = parsePathListLine(trimmed);
|
|
826
|
+
return filePath ? { rawLine: filePath, anchor: filePath, filePath, kind: 'path' } : null;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function buildGitSelection(lines) {
|
|
830
|
+
const summaryLines = uniqueLines(lines.filter((line) => /\bfiles? changed\b|\binsertions?\(\+\)|\bdeletions?\(-\)/i.test(String(line ?? '').trim())));
|
|
831
|
+
const entries = [];
|
|
832
|
+
const seen = new Set();
|
|
833
|
+
for (const line of lines) {
|
|
834
|
+
const entry = parseGitChangeLine(line);
|
|
835
|
+
if (!entry) continue;
|
|
836
|
+
const key = normalizeLineForDedupe(entry.rawLine);
|
|
837
|
+
if (seen.has(key)) continue;
|
|
838
|
+
seen.add(key);
|
|
839
|
+
entries.push(entry);
|
|
840
|
+
}
|
|
841
|
+
if (entries.length === 0 || (entries.length <= 4 && summaryLines.length <= 1)) return null;
|
|
842
|
+
|
|
843
|
+
const detailed = [];
|
|
844
|
+
const addDetailed = (predicate) => {
|
|
845
|
+
const entry = entries.find((item) => !detailed.includes(item) && predicate(item));
|
|
846
|
+
if (entry) detailed.push(entry);
|
|
847
|
+
};
|
|
848
|
+
addDetailed((item) => /^(?:src|app|lib)\//.test(item.filePath));
|
|
849
|
+
if (summaryLines.length > 0) addDetailed((item) => /^(?:src|app|lib)\//.test(item.filePath));
|
|
850
|
+
addDetailed((item) => /^(?:tests?|specs?)\//.test(item.filePath));
|
|
851
|
+
if (summaryLines.length === 0) addDetailed((item) => /^docs\//.test(item.filePath));
|
|
852
|
+
while (detailed.length < 3) {
|
|
853
|
+
const entry = entries.find((item) => !detailed.includes(item));
|
|
854
|
+
if (!entry) break;
|
|
855
|
+
detailed.push(entry);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
const remaining = entries.filter((item) => !detailed.includes(item));
|
|
859
|
+
const shownAnchor = remaining.find((item) => /^docs\//.test(item.filePath)) || remaining[0] || null;
|
|
860
|
+
return {
|
|
861
|
+
anchorLines: uniqueLines([...detailed.map((item) => item.rawLine), ...(shownAnchor ? [shownAnchor.anchor] : []), ...summaryLines]),
|
|
862
|
+
candidateLines: uniqueLines([
|
|
863
|
+
...detailed.map((item) => item.rawLine),
|
|
864
|
+
shownAnchor ? `Changed paths: ${shownAnchor.anchor}${remaining.length > 1 ? `, +${remaining.length - 1} more` : ''}` : null,
|
|
865
|
+
...summaryLines,
|
|
866
|
+
].filter(Boolean)),
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
function selectCandidateLines(lines, profile = null) {
|
|
871
|
+
if (profile?.id === 'git') {
|
|
872
|
+
const gitSelection = buildGitSelection(lines);
|
|
873
|
+
if (gitSelection) return gitSelection;
|
|
874
|
+
}
|
|
875
|
+
if (profile?.id === 'paths') {
|
|
876
|
+
const pathSelection = buildPathListSelection(lines);
|
|
877
|
+
if (pathSelection) return pathSelection;
|
|
878
|
+
}
|
|
879
|
+
if (profile?.id === 'search') {
|
|
880
|
+
const searchSelection = buildSearchResultSelection(lines);
|
|
881
|
+
if (searchSelection) {
|
|
882
|
+
return searchSelection;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
const important = [];
|
|
887
|
+
const anchors = [];
|
|
888
|
+
const fallback = [];
|
|
889
|
+
const tail = [];
|
|
890
|
+
const seen = new Set();
|
|
891
|
+
const hasFailureSignal = lines.some((line) => /\b(error|fail(?:ed)?|assertionerror|exception|fatal)\b/i.test(String(line ?? '')));
|
|
892
|
+
|
|
893
|
+
for (const line of lines) {
|
|
894
|
+
const trimmed = String(line ?? '').trim();
|
|
895
|
+
const normalized = normalizeLineForDedupe(trimmed);
|
|
896
|
+
if (!normalized || seen.has(normalized)) continue;
|
|
897
|
+
|
|
898
|
+
const progressNoise = isProgressNoise(trimmed, profile);
|
|
899
|
+
const importantLine = isImportantLine(trimmed, profile);
|
|
900
|
+
const anchorLine = isAnchorLine(trimmed, profile);
|
|
901
|
+
const shouldDropOnFailure = hasFailureSignal && (/^\s*RUN\b/i.test(trimmed) || /^\s*✓\s+/i.test(trimmed) || /^stdout\s*\|/i.test(trimmed));
|
|
902
|
+
|
|
903
|
+
if (anchorLine && !shouldDropOnFailure) {
|
|
904
|
+
anchors.push(trimmed);
|
|
905
|
+
important.push(trimmed);
|
|
906
|
+
seen.add(normalized);
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
if (importantLine && !shouldDropOnFailure) {
|
|
911
|
+
important.push(trimmed);
|
|
912
|
+
seen.add(normalized);
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
if (!progressNoise && !shouldDropOnFailure && fallback.length < 3) {
|
|
917
|
+
fallback.push(trimmed);
|
|
918
|
+
seen.add(normalized);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
for (const line of [...lines].reverse()) {
|
|
923
|
+
const trimmed = String(line ?? '').trim();
|
|
924
|
+
const normalized = normalizeLineForDedupe(trimmed);
|
|
925
|
+
if (!normalized || seen.has(normalized) || isProgressNoise(trimmed, profile)) continue;
|
|
926
|
+
|
|
927
|
+
if (isTailSummaryLine(trimmed, profile)) {
|
|
928
|
+
tail.unshift(trimmed);
|
|
929
|
+
seen.add(normalized);
|
|
930
|
+
}
|
|
931
|
+
if (tail.length >= 4) break;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
return {
|
|
935
|
+
anchorLines: uniqueLines([...anchors, ...tail]).slice(0, 6),
|
|
936
|
+
candidateLines: uniqueLines([...important, ...tail, ...fallback]),
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function compressToolOutput({
|
|
941
|
+
command = '',
|
|
942
|
+
stdout = '',
|
|
943
|
+
stderr = '',
|
|
944
|
+
exitCode = null,
|
|
945
|
+
maxTokens = DEFAULT_MAX_OUTPUT_TOKENS,
|
|
946
|
+
projectRoot = null,
|
|
947
|
+
} = {}) {
|
|
948
|
+
const commandText = String(command ?? '').trim();
|
|
949
|
+
const displayCommand = shortenProjectRootSegments(commandText, projectRoot).trim() || commandText;
|
|
950
|
+
const profile = detectOutputProfile(displayCommand);
|
|
951
|
+
const stdoutLines = splitLines(stdout).map((line) => shortenProjectRootSegments(line, projectRoot));
|
|
952
|
+
const stderrLines = splitLines(stderr).map((line) => shortenProjectRootSegments(line, projectRoot));
|
|
953
|
+
const allLines = [
|
|
954
|
+
...stdoutLines,
|
|
955
|
+
...stderrLines.map((line) => (line.trim() ? `stderr: ${line}` : line)),
|
|
956
|
+
];
|
|
957
|
+
const rawText = [commandText, stdout, stderr].filter(Boolean).join('\n');
|
|
958
|
+
const { candidateLines, anchorLines } = selectCandidateLines(allLines, profile);
|
|
959
|
+
const headerLine = `- Recent command: ${displayCommand || 'unknown command'}${exitCode === null || exitCode === undefined ? '' : ` (exit ${exitCode})`}`;
|
|
960
|
+
const compactedLines = buildCompactedSummaryLines([headerLine, ...candidateLines.map((line) => `- ${line}`)], {
|
|
961
|
+
maxTokens,
|
|
962
|
+
maxLines: 10,
|
|
963
|
+
});
|
|
964
|
+
let summary = compactedLines.join('\n').trim();
|
|
965
|
+
const missingAnchors = findMissingAnchors(summary, anchorLines);
|
|
966
|
+
|
|
967
|
+
if (missingAnchors.length > 0) {
|
|
968
|
+
const anchorFirstSummary = buildCompactedSummaryLines([
|
|
969
|
+
headerLine,
|
|
970
|
+
...anchorLines.map((line) => `- ${line}`),
|
|
971
|
+
...candidateLines.map((line) => `- ${line}`),
|
|
972
|
+
], {
|
|
973
|
+
maxTokens,
|
|
974
|
+
maxLines: 10,
|
|
975
|
+
forceFirstCount: Math.min(1 + anchorLines.length, 5),
|
|
976
|
+
}).join('\n').trim();
|
|
977
|
+
|
|
978
|
+
if (findMissingAnchors(anchorFirstSummary, anchorLines).length === 0) {
|
|
979
|
+
summary = anchorFirstSummary;
|
|
980
|
+
} else {
|
|
981
|
+
const compactHeaderLine = `- Cmd: ${displayCommand || 'unknown'}${exitCode === null || exitCode === undefined ? '' : ` (exit ${exitCode})`}`;
|
|
982
|
+
summary = buildCompactedSummaryLines([
|
|
983
|
+
compactHeaderLine,
|
|
984
|
+
...anchorLines.map((line) => `- ${line}`),
|
|
985
|
+
], {
|
|
986
|
+
maxTokens,
|
|
987
|
+
maxLines: 10,
|
|
988
|
+
forceFirstCount: Math.min(1 + anchorLines.length, 5),
|
|
989
|
+
}).join('\n').trim();
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
const tokensBefore = estimateTokenCount(rawText);
|
|
994
|
+
const tokensAfter = estimateTokenCount(summary);
|
|
995
|
+
|
|
996
|
+
return {
|
|
997
|
+
summary,
|
|
998
|
+
tokensBefore,
|
|
999
|
+
tokensAfter,
|
|
1000
|
+
savedTokens: Math.max(0, tokensBefore - tokensAfter),
|
|
1001
|
+
command: displayCommand,
|
|
1002
|
+
exitCode,
|
|
1003
|
+
profile: profile?.id ?? 'generic',
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function extractCommand(payload) {
|
|
1008
|
+
return String(payload?.tool_input?.command || payload?.command || '').trim();
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
function extractExitCode(payload) {
|
|
1012
|
+
const candidates = [
|
|
1013
|
+
payload?.tool_output?.exitCode,
|
|
1014
|
+
payload?.tool_output?.exit_code,
|
|
1015
|
+
payload?.tool_result?.exitCode,
|
|
1016
|
+
payload?.tool_result?.exit_code,
|
|
1017
|
+
payload?.exitCode,
|
|
1018
|
+
payload?.exit_code,
|
|
1019
|
+
];
|
|
1020
|
+
for (const candidate of candidates) {
|
|
1021
|
+
const value = Number(candidate);
|
|
1022
|
+
if (Number.isFinite(value)) return value;
|
|
1023
|
+
}
|
|
1024
|
+
return null;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
function extractText(payload, preferredKeys = []) {
|
|
1028
|
+
const queue = [payload];
|
|
1029
|
+
const seen = new Set();
|
|
1030
|
+
|
|
1031
|
+
while (queue.length > 0) {
|
|
1032
|
+
const current = queue.shift();
|
|
1033
|
+
if (!current || typeof current !== 'object' || seen.has(current)) continue;
|
|
1034
|
+
seen.add(current);
|
|
1035
|
+
|
|
1036
|
+
for (const key of preferredKeys) {
|
|
1037
|
+
if (typeof current[key] === 'string' && current[key].trim()) {
|
|
1038
|
+
return current[key];
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
for (const value of Object.values(current)) {
|
|
1043
|
+
if (typeof value === 'string' && value.trim() && preferredKeys.length === 0) {
|
|
1044
|
+
return value;
|
|
1045
|
+
}
|
|
1046
|
+
if (value && typeof value === 'object') {
|
|
1047
|
+
queue.push(value);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
return '';
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
function normalizeOutputHistoryEntry(entry) {
|
|
1056
|
+
if (!entry || typeof entry !== 'object') return null;
|
|
1057
|
+
const command = typeof entry.command === 'string' ? entry.command.trim() : '';
|
|
1058
|
+
const summary = typeof entry.summary === 'string' ? entry.summary.trim() : '';
|
|
1059
|
+
if (!command || !summary) return null;
|
|
1060
|
+
const rawPath = typeof entry.rawPath === 'string' ? normalizeRuntimeRelativePath(entry.rawPath) : '';
|
|
1061
|
+
const rawBytes = Number(entry.rawBytes);
|
|
1062
|
+
const recoveryReason = typeof entry.recoveryReason === 'string' ? entry.recoveryReason.trim() : '';
|
|
1063
|
+
const truncated = typeof entry.truncated === 'boolean' ? entry.truncated : false;
|
|
1064
|
+
|
|
1065
|
+
return {
|
|
1066
|
+
...entry,
|
|
1067
|
+
command,
|
|
1068
|
+
summary,
|
|
1069
|
+
timestamp: Number.isFinite(Number(entry.timestamp)) ? Number(entry.timestamp) : Date.now(),
|
|
1070
|
+
tokensBefore: Number.isFinite(Number(entry.tokensBefore)) ? Number(entry.tokensBefore) : 0,
|
|
1071
|
+
tokensAfter: Number.isFinite(Number(entry.tokensAfter)) ? Number(entry.tokensAfter) : 0,
|
|
1072
|
+
savedTokens: Number.isFinite(Number(entry.savedTokens)) ? Number(entry.savedTokens) : 0,
|
|
1073
|
+
rawSaved: Boolean(entry.rawSaved),
|
|
1074
|
+
rawPath,
|
|
1075
|
+
rawBytes: Number.isFinite(rawBytes) ? rawBytes : 0,
|
|
1076
|
+
recoveryReason: recoveryReason || null,
|
|
1077
|
+
truncated,
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function normalizeOutputSummaryForDedupe(summary) {
|
|
1082
|
+
return String(summary ?? '')
|
|
1083
|
+
.split(/\r?\n/)
|
|
1084
|
+
.map((line) => String(line ?? '').trim())
|
|
1085
|
+
.filter((line) => line && !/^- Full output:\s+/i.test(line))
|
|
1086
|
+
.join('\n');
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
function buildOutputHistoryDedupeKey(entry) {
|
|
1090
|
+
return [
|
|
1091
|
+
String(entry?.command ?? '').trim(),
|
|
1092
|
+
normalizeOutputSummaryForDedupe(entry?.summary),
|
|
1093
|
+
].join('\n').trim().toLowerCase();
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
function normalizeOutputHistoryDocument(raw) {
|
|
1097
|
+
const candidates = Array.isArray(raw?.entries)
|
|
1098
|
+
? raw.entries
|
|
1099
|
+
: Array.isArray(raw)
|
|
1100
|
+
? raw
|
|
1101
|
+
: [];
|
|
1102
|
+
|
|
1103
|
+
return {
|
|
1104
|
+
entries: candidates
|
|
1105
|
+
.map((entry) => normalizeOutputHistoryEntry(entry))
|
|
1106
|
+
.filter(Boolean)
|
|
1107
|
+
.sort((left, right) => (right.timestamp ?? 0) - (left.timestamp ?? 0))
|
|
1108
|
+
.slice(0, DEFAULT_OUTPUT_HISTORY_MAX_ENTRIES),
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
async function writeJson(filePath, value) {
|
|
1113
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
1114
|
+
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
async function appendOutputHistory(projectRoot, entry) {
|
|
1118
|
+
const runtimePaths = buildRuntimePaths(projectRoot);
|
|
1119
|
+
const current = normalizeOutputHistoryDocument(await readJson(runtimePaths.outputHistoryPath, { entries: [] }));
|
|
1120
|
+
const normalizedEntry = normalizeOutputHistoryEntry(entry);
|
|
1121
|
+
if (!normalizedEntry) return current;
|
|
1122
|
+
const normalizedKey = buildOutputHistoryDedupeKey(normalizedEntry);
|
|
1123
|
+
const dedupedExistingEntries = current.entries.filter((candidate) => !(
|
|
1124
|
+
buildOutputHistoryDedupeKey(candidate) === normalizedKey
|
|
1125
|
+
));
|
|
1126
|
+
|
|
1127
|
+
const nextDocument = normalizeOutputHistoryDocument({
|
|
1128
|
+
entries: [normalizedEntry, ...dedupedExistingEntries],
|
|
1129
|
+
});
|
|
1130
|
+
await writeJson(runtimePaths.outputHistoryPath, nextDocument);
|
|
1131
|
+
return nextDocument;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
function shouldCompress(config) {
|
|
1135
|
+
return Boolean(config?.tokenPipeline?.outputCompression);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
function mergeDeep(base, override) {
|
|
1139
|
+
if (!override || typeof override !== 'object' || Array.isArray(override)) {
|
|
1140
|
+
return override === undefined ? base : override;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
const merged = { ...base };
|
|
1144
|
+
for (const [key, value] of Object.entries(override)) {
|
|
1145
|
+
if (value && typeof value === 'object' && !Array.isArray(value) && merged[key] && typeof merged[key] === 'object' && !Array.isArray(merged[key])) {
|
|
1146
|
+
merged[key] = mergeDeep(merged[key], value);
|
|
1147
|
+
} else {
|
|
1148
|
+
merged[key] = value;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
return merged;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
async function loadRuntimeConfig(projectRoot) {
|
|
1155
|
+
const runtimePaths = buildRuntimePaths(projectRoot);
|
|
1156
|
+
return mergeDeep({
|
|
1157
|
+
tokenPipeline: {
|
|
1158
|
+
outputCompression: true,
|
|
1159
|
+
promptCache: true,
|
|
1160
|
+
},
|
|
1161
|
+
}, await readJson(runtimePaths.configPath, {}));
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
async function main() {
|
|
1165
|
+
const rawInput = await readStdin();
|
|
1166
|
+
const payload = (() => {
|
|
1167
|
+
try {
|
|
1168
|
+
return JSON.parse(rawInput || '{}');
|
|
1169
|
+
} catch {
|
|
1170
|
+
return {};
|
|
1171
|
+
}
|
|
1172
|
+
})();
|
|
1173
|
+
|
|
1174
|
+
const projectRoot = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
1175
|
+
const config = await loadRuntimeConfig(projectRoot);
|
|
1176
|
+
if (!shouldCompress(config)) {
|
|
1177
|
+
process.exit(0);
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
const command = extractCommand(payload);
|
|
1182
|
+
if (!command) {
|
|
1183
|
+
process.exit(0);
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
const stdout = extractText(payload?.tool_output ?? payload?.tool_result ?? payload, ['stdout', 'output', 'result']);
|
|
1188
|
+
const stderr = extractText(payload?.tool_output ?? payload?.tool_result ?? payload, ['stderr', 'error']);
|
|
1189
|
+
const exitCode = extractExitCode(payload);
|
|
1190
|
+
if (!stdout.trim() && !stderr.trim()) {
|
|
1191
|
+
process.exit(0);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
const requestKey = buildCompactMachineKey('tool-output-v1', {
|
|
1196
|
+
command,
|
|
1197
|
+
stdoutHash: buildCompactMachineKey('stdout', stripAnsi(stdout)),
|
|
1198
|
+
stderrHash: buildCompactMachineKey('stderr', stripAnsi(stderr)),
|
|
1199
|
+
exitCode,
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
if (config?.tokenPipeline?.promptCache) {
|
|
1203
|
+
const cached = await readPromptCacheEntry(projectRoot, requestKey, { touch: true });
|
|
1204
|
+
if (cached?.content) {
|
|
1205
|
+
const cachedCommand = String(cached.metadata?.command ?? command ?? '').trim();
|
|
1206
|
+
const cachedDisplayCommand = shortenProjectRootSegments(cachedCommand, projectRoot).trim() || cachedCommand;
|
|
1207
|
+
const cachedProfile = typeof cached.metadata?.profile === 'string' && cached.metadata.profile.trim()
|
|
1208
|
+
? cached.metadata.profile.trim()
|
|
1209
|
+
: (detectOutputProfile(cachedDisplayCommand)?.id ?? 'generic');
|
|
1210
|
+
await appendOutputHistory(projectRoot, {
|
|
1211
|
+
timestamp: Date.now(),
|
|
1212
|
+
command: cachedCommand,
|
|
1213
|
+
profile: cachedProfile,
|
|
1214
|
+
summary: String(cached.content),
|
|
1215
|
+
tokensBefore: cached.tokensBefore ?? 0,
|
|
1216
|
+
tokensAfter: cached.tokensAfter ?? 0,
|
|
1217
|
+
savedTokens: cached.savedTokens ?? 0,
|
|
1218
|
+
exitCode,
|
|
1219
|
+
rawSaved: Boolean(cached.metadata?.rawSaved),
|
|
1220
|
+
rawPath: cached.metadata?.rawPath ?? '',
|
|
1221
|
+
rawBytes: cached.metadata?.rawBytes ?? 0,
|
|
1222
|
+
recoveryReason: cached.metadata?.recoveryReason ?? null,
|
|
1223
|
+
truncated: Boolean(cached.metadata?.truncated),
|
|
1224
|
+
});
|
|
1225
|
+
await updateCompactPressureFromOutput(projectRoot, {
|
|
1226
|
+
timestamp: Date.now(),
|
|
1227
|
+
command: cachedCommand,
|
|
1228
|
+
profile: cachedProfile,
|
|
1229
|
+
summary: String(cached.content),
|
|
1230
|
+
tokensBefore: cached.tokensBefore ?? 0,
|
|
1231
|
+
tokensAfter: cached.tokensAfter ?? 0,
|
|
1232
|
+
savedTokens: cached.savedTokens ?? 0,
|
|
1233
|
+
exitCode,
|
|
1234
|
+
}, config);
|
|
1235
|
+
process.stdout.write(String(cached.content));
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
const result = compressToolOutput({
|
|
1241
|
+
command,
|
|
1242
|
+
stdout,
|
|
1243
|
+
stderr,
|
|
1244
|
+
exitCode,
|
|
1245
|
+
projectRoot,
|
|
1246
|
+
});
|
|
1247
|
+
const recoveryReason = buildRawOutputRecoveryReason({
|
|
1248
|
+
exitCode,
|
|
1249
|
+
tokensBefore: result.tokensBefore,
|
|
1250
|
+
savedTokens: result.savedTokens,
|
|
1251
|
+
});
|
|
1252
|
+
let rawOutputMetadata = {
|
|
1253
|
+
rawSaved: false,
|
|
1254
|
+
rawPath: '',
|
|
1255
|
+
rawBytes: 0,
|
|
1256
|
+
recoveryReason: null,
|
|
1257
|
+
truncated: false,
|
|
1258
|
+
};
|
|
1259
|
+
|
|
1260
|
+
if (recoveryReason) {
|
|
1261
|
+
rawOutputMetadata = {
|
|
1262
|
+
...rawOutputMetadata,
|
|
1263
|
+
...(await persistRawOutput(projectRoot, {
|
|
1264
|
+
command,
|
|
1265
|
+
summary: result.summary,
|
|
1266
|
+
profile: result.profile,
|
|
1267
|
+
stdout,
|
|
1268
|
+
stderr,
|
|
1269
|
+
exitCode,
|
|
1270
|
+
})),
|
|
1271
|
+
recoveryReason,
|
|
1272
|
+
};
|
|
1273
|
+
const summaryWithRecoveryHint = buildRecoveryHintSummary(result.summary, rawOutputMetadata.rawPath, {
|
|
1274
|
+
tokensBefore: result.tokensBefore,
|
|
1275
|
+
});
|
|
1276
|
+
if (summaryWithRecoveryHint) {
|
|
1277
|
+
result.summary = summaryWithRecoveryHint;
|
|
1278
|
+
result.tokensAfter = estimateTokenCount(result.summary);
|
|
1279
|
+
result.savedTokens = Math.max(0, result.tokensBefore - result.tokensAfter);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
await appendOutputHistory(projectRoot, {
|
|
1283
|
+
timestamp: Date.now(),
|
|
1284
|
+
command: result.command,
|
|
1285
|
+
profile: result.profile,
|
|
1286
|
+
summary: result.summary,
|
|
1287
|
+
tokensBefore: result.tokensBefore,
|
|
1288
|
+
tokensAfter: result.tokensAfter,
|
|
1289
|
+
savedTokens: result.savedTokens,
|
|
1290
|
+
exitCode,
|
|
1291
|
+
...rawOutputMetadata,
|
|
1292
|
+
});
|
|
1293
|
+
await recordCompaction(projectRoot, {
|
|
1294
|
+
timestamp: Date.now(),
|
|
1295
|
+
source: 'tool-output',
|
|
1296
|
+
tokensBefore: result.tokensBefore,
|
|
1297
|
+
tokensAfter: result.tokensAfter,
|
|
1298
|
+
savedTokens: result.savedTokens,
|
|
1299
|
+
metadata: { command: result.command, exitCode },
|
|
1300
|
+
});
|
|
1301
|
+
await updateCompactPressureFromOutput(projectRoot, {
|
|
1302
|
+
timestamp: Date.now(),
|
|
1303
|
+
command: result.command,
|
|
1304
|
+
profile: result.profile,
|
|
1305
|
+
summary: result.summary,
|
|
1306
|
+
tokensBefore: result.tokensBefore,
|
|
1307
|
+
tokensAfter: result.tokensAfter,
|
|
1308
|
+
savedTokens: result.savedTokens,
|
|
1309
|
+
exitCode,
|
|
1310
|
+
}, config);
|
|
1311
|
+
|
|
1312
|
+
if (config?.tokenPipeline?.promptCache) {
|
|
1313
|
+
await writePromptCacheEntry(projectRoot, {
|
|
1314
|
+
requestKey,
|
|
1315
|
+
kind: 'tool-output',
|
|
1316
|
+
updatedAt: Date.now(),
|
|
1317
|
+
tokensBefore: result.tokensBefore,
|
|
1318
|
+
tokensAfter: result.tokensAfter,
|
|
1319
|
+
savedTokens: result.savedTokens,
|
|
1320
|
+
content: result.summary,
|
|
1321
|
+
metadata: {
|
|
1322
|
+
command: result.command,
|
|
1323
|
+
exitCode,
|
|
1324
|
+
profile: result.profile,
|
|
1325
|
+
rawSaved: rawOutputMetadata.rawSaved,
|
|
1326
|
+
rawPath: rawOutputMetadata.rawPath,
|
|
1327
|
+
rawBytes: rawOutputMetadata.rawBytes,
|
|
1328
|
+
recoveryReason: rawOutputMetadata.recoveryReason,
|
|
1329
|
+
truncated: rawOutputMetadata.truncated,
|
|
1330
|
+
},
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
process.stdout.write(result.summary);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
main().catch((error) => {
|
|
1338
|
+
console.error('[UKit] Failed to compress tool output:', error?.message || String(error));
|
|
1339
|
+
process.exitCode = 1;
|
|
1340
|
+
});
|