cuelo 0.10.52 → 0.10.53
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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +35 -35
- package/.next/build-manifest.json +4 -4
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +3 -3
- package/.next/react-loadable-manifest.json +549 -561
- package/.next/required-server-files.js +25 -25
- package/.next/required-server-files.json +25 -25
- package/.next/server/app/_global-error/page.js +3 -3
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js +2 -2
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/api/agent/[id]/account/route.js +1 -1
- package/.next/server/app/api/agent/[id]/account/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/[id]/bash-output/route.js +1 -1
- package/.next/server/app/api/agent/[id]/bash-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/[id]/events/route.js +2 -2
- package/.next/server/app/api/agent/[id]/events/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/[id]/route.js +1 -1
- package/.next/server/app/api/agent/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/new/route.js +1 -1
- package/.next/server/app/api/agent/new/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/running/route.js +1 -1
- package/.next/server/app/api/agent/running/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/attachment-settings/route.js +1 -1
- package/.next/server/app/api/attachment-settings/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/attachments/[id]/transcription/route.js +2 -2
- package/.next/server/app/api/attachments/[id]/transcription/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/attachments/draft/route.js +1 -1
- package/.next/server/app/api/attachments/draft/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/attachments/route.js +2 -2
- package/.next/server/app/api/attachments/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/all-providers/route.js +1 -1
- package/.next/server/app/api/auth/all-providers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/api-key/[provider]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/login/[provider]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/logout/[provider]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/providers/route.js +1 -1
- package/.next/server/app/api/auth/providers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/character-mode/route.js +1 -2
- package/.next/server/app/api/character-mode/route.js.nft.json +1 -1
- package/.next/server/app/api/character-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/cwd/browse/route.js +1 -1
- package/.next/server/app/api/cwd/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/cwd/validate/route.js +1 -1
- package/.next/server/app/api/cwd/validate/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/default-cwd/route.js +1 -1
- package/.next/server/app/api/default-cwd/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/display-settings/route.js +1 -1
- package/.next/server/app/api/display-settings/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/document-text/route.js +2 -2
- package/.next/server/app/api/document-text/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/experiments/route.js +1 -1
- package/.next/server/app/api/experiments/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/file-index/route.js +1 -1
- package/.next/server/app/api/file-index/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/files/[...path]/route.js +1 -1
- package/.next/server/app/api/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/git/diff/route.js +1 -1
- package/.next/server/app/api/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/git/status/route.js +2 -2
- package/.next/server/app/api/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/github/projects/route.js +1 -1
- package/.next/server/app/api/github/projects/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/gpt6/handles/route.js +1 -1
- package/.next/server/app/api/gpt6/handles/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/gpt6/reply/route.js +1 -1
- package/.next/server/app/api/gpt6/reply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/gpt6/token/route.js +1 -1
- package/.next/server/app/api/gpt6/token/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/gpt6/web6-session/route.js +1 -1
- package/.next/server/app/api/gpt6/web6-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/home/route.js +1 -1
- package/.next/server/app/api/home/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/live/call/[callId]/route.js +2 -2
- package/.next/server/app/api/live/call/[callId]/route.js.nft.json +1 -1
- package/.next/server/app/api/live/call/[callId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/live/events/route.js +2 -2
- package/.next/server/app/api/live/events/route.js.nft.json +1 -1
- package/.next/server/app/api/live/events/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/live/interpret/route.js +2 -2
- package/.next/server/app/api/live/interpret/route.js.nft.json +1 -1
- package/.next/server/app/api/live/interpret/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/live/offer/route.js +1 -1
- package/.next/server/app/api/live/offer/route.js.nft.json +1 -1
- package/.next/server/app/api/live/offer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/live/voices/route.js +1 -1
- package/.next/server/app/api/live/voices/route.js.nft.json +1 -1
- package/.next/server/app/api/live/voices/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/mcp/route.js +2 -2
- package/.next/server/app/api/mcp/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/model-roles/route.js +1 -1
- package/.next/server/app/api/model-roles/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models/route.js +1 -1
- package/.next/server/app/api/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/catalog/route.js +1 -1
- package/.next/server/app/api/models-config/catalog/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/discover/route.js +1 -1
- package/.next/server/app/api/models-config/discover/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/route.js +3 -3
- package/.next/server/app/api/models-config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/test/route.js +1 -1
- package/.next/server/app/api/models-config/test/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/office-characters/[...path]/route.js +1 -2
- package/.next/server/app/api/office-characters/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/office-characters/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/plugins/route.js +1 -1
- package/.next/server/app/api/plugins/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/project-docs/route.js +1 -1
- package/.next/server/app/api/project-docs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/project-trust/route.js +1 -1
- package/.next/server/app/api/project-trust/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/projects/route.js +2 -2
- package/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/runs/[runId]/prelude/route.js +1 -1
- package/.next/server/app/api/runs/[runId]/prelude/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/runs/[runId]/route.js +1 -1
- package/.next/server/app/api/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/runs/[runId]/turns/route.js +1 -1
- package/.next/server/app/api/runs/[runId]/turns/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/runs/route.js +1 -1
- package/.next/server/app/api/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/auto-name/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/auto-name/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/context/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/context/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/export/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/state/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/state/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/route.js +1 -1
- package/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/search/route.js +1 -1
- package/.next/server/app/api/sessions/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/route.js +1 -1
- package/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidecars/[service]/[...path]/route.js +1 -1
- package/.next/server/app/api/sidecars/[service]/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/check/route.js +1 -1
- package/.next/server/app/api/skills/check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/install/route.js +2 -2
- package/.next/server/app/api/skills/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/search/route.js +1 -1
- package/.next/server/app/api/skills/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/update/route.js +1 -1
- package/.next/server/app/api/skills/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/subagents/eta/route.js +1 -1
- package/.next/server/app/api/subagents/eta/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/theme/route.js +1 -1
- package/.next/server/app/api/theme/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tool-image/route.js +1 -1
- package/.next/server/app/api/tool-image/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/update-maintenance/route.js +2 -2
- package/.next/server/app/api/update-maintenance/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/updates/route.js +1 -1
- package/.next/server/app/api/updates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/web-access/recovery/route.js +1 -1
- package/.next/server/app/api/web-access/recovery/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/web-access/route.js +1 -1
- package/.next/server/app/api/web-access/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/web6/consults/route.js +1 -1
- package/.next/server/app/api/web6/consults/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/favicon.ico/route.js +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +4 -4
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
- package/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/manifest.webmanifest/route.js +2 -2
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +5 -5
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/recover/page.js +2 -2
- package/.next/server/app/recover/page_client-reference-manifest.js +1 -1
- package/.next/server/app/recover.html +1 -1
- package/.next/server/app/recover.rsc +3 -3
- package/.next/server/app/recover.segments/_full.segment.rsc +3 -3
- package/.next/server/app/recover.segments/_head.segment.rsc +1 -1
- package/.next/server/app/recover.segments/_index.segment.rsc +2 -2
- package/.next/server/app/recover.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/recover.segments/recover/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/recover.segments/recover.segment.rsc +1 -1
- package/.next/server/app-paths-manifest.json +35 -35
- package/.next/server/chunks/1008.js +2 -2
- package/.next/server/chunks/1966.js +2 -0
- package/.next/server/chunks/319.js +3 -3
- package/.next/server/chunks/6429.js +2 -2
- package/.next/server/chunks/6775.js +7 -0
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/middleware.js +9 -9
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/app/_not-found/page-041b64675adcdf82.js +1 -0
- package/.next/static/chunks/app/layout-3b1f8b74c7fc5e77.js +1 -0
- package/.next/static/chunks/app/{page-3640086c732eb87d.js → page-efae4fdcf84b7a14.js} +4 -4
- package/.next/static/chunks/app/recover/{page-c0c40b3c215078a4.js → page-2bcd6a351f3eda3d.js} +1 -1
- package/.next/static/chunks/{main-app-fbc4aa3c15d48613.js → main-app-ca46ab75db2b4e4b.js} +1 -1
- package/.next/static/chunks/main-dd4c9daba44bef88.js +5 -0
- package/.next/static/chunks/next/dist/client/components/builtin/global-error-dac2a58a3b9eb28e.js +1 -0
- package/.next/static/chunks/webpack-546c8221d1906945.js +1 -0
- package/.next/trace +107 -107
- package/.next/trace-build +1 -1
- package/.next/types/app/api/agent/[id]/account/route.ts +1 -1
- package/.next/types/app/api/agent/[id]/bash-output/route.ts +1 -1
- package/.next/types/app/api/agent/[id]/events/route.ts +1 -1
- package/.next/types/app/api/agent/[id]/route.ts +1 -1
- package/.next/types/app/api/agent/new/route.ts +1 -1
- package/.next/types/app/api/agent/running/route.ts +1 -1
- package/.next/types/app/api/attachment-settings/route.ts +1 -1
- package/.next/types/app/api/attachments/[id]/transcription/route.ts +1 -1
- package/.next/types/app/api/attachments/draft/route.ts +1 -1
- package/.next/types/app/api/attachments/route.ts +1 -1
- package/.next/types/app/api/auth/all-providers/route.ts +1 -1
- package/.next/types/app/api/auth/api-key/[provider]/route.ts +1 -1
- package/.next/types/app/api/auth/login/[provider]/route.ts +1 -1
- package/.next/types/app/api/auth/logout/[provider]/route.ts +1 -1
- package/.next/types/app/api/auth/providers/route.ts +1 -1
- package/.next/types/app/api/character-mode/route.ts +1 -1
- package/.next/types/app/api/cwd/browse/route.ts +1 -1
- package/.next/types/app/api/cwd/validate/route.ts +1 -1
- package/.next/types/app/api/default-cwd/route.ts +1 -1
- package/.next/types/app/api/display-settings/route.ts +1 -1
- package/.next/types/app/api/document-text/route.ts +1 -1
- package/.next/types/app/api/experiments/route.ts +1 -1
- package/.next/types/app/api/file-index/route.ts +1 -1
- package/.next/types/app/api/files/[...path]/route.ts +1 -1
- package/.next/types/app/api/git/diff/route.ts +1 -1
- package/.next/types/app/api/git/status/route.ts +1 -1
- package/.next/types/app/api/github/projects/route.ts +1 -1
- package/.next/types/app/api/gpt6/handles/route.ts +1 -1
- package/.next/types/app/api/gpt6/reply/route.ts +1 -1
- package/.next/types/app/api/gpt6/token/route.ts +1 -1
- package/.next/types/app/api/gpt6/web6-session/route.ts +1 -1
- package/.next/types/app/api/home/route.ts +1 -1
- package/.next/types/app/api/live/call/[callId]/route.ts +1 -1
- package/.next/types/app/api/live/events/route.ts +1 -1
- package/.next/types/app/api/live/interpret/route.ts +1 -1
- package/.next/types/app/api/live/offer/route.ts +1 -1
- package/.next/types/app/api/live/voices/route.ts +1 -1
- package/.next/types/app/api/mcp/route.ts +1 -1
- package/.next/types/app/api/model-roles/route.ts +1 -1
- package/.next/types/app/api/models/route.ts +1 -1
- package/.next/types/app/api/models-config/catalog/route.ts +1 -1
- package/.next/types/app/api/models-config/discover/route.ts +1 -1
- package/.next/types/app/api/models-config/route.ts +1 -1
- package/.next/types/app/api/models-config/test/route.ts +1 -1
- package/.next/types/app/api/office-characters/[...path]/route.ts +1 -1
- package/.next/types/app/api/plugins/route.ts +1 -1
- package/.next/types/app/api/project-docs/route.ts +1 -1
- package/.next/types/app/api/project-trust/route.ts +1 -1
- package/.next/types/app/api/projects/route.ts +1 -1
- package/.next/types/app/api/runs/[runId]/prelude/route.ts +1 -1
- package/.next/types/app/api/runs/[runId]/route.ts +1 -1
- package/.next/types/app/api/runs/[runId]/turns/route.ts +1 -1
- package/.next/types/app/api/runs/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/auto-name/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/context/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/entries/[entryId]/thinking/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/export/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/state/route.ts +1 -1
- package/.next/types/app/api/sessions/route.ts +1 -1
- package/.next/types/app/api/sessions/search/route.ts +1 -1
- package/.next/types/app/api/settings/route.ts +1 -1
- package/.next/types/app/api/sidecars/[service]/[...path]/route.ts +1 -1
- package/.next/types/app/api/skills/check/route.ts +1 -1
- package/.next/types/app/api/skills/install/route.ts +1 -1
- package/.next/types/app/api/skills/route.ts +1 -1
- package/.next/types/app/api/skills/search/route.ts +1 -1
- package/.next/types/app/api/skills/update/route.ts +1 -1
- package/.next/types/app/api/subagents/eta/route.ts +1 -1
- package/.next/types/app/api/theme/route.ts +1 -1
- package/.next/types/app/api/tool-image/route.ts +1 -1
- package/.next/types/app/api/update-maintenance/route.ts +1 -1
- package/.next/types/app/api/updates/route.ts +1 -1
- package/.next/types/app/api/web-access/recovery/route.ts +1 -1
- package/.next/types/app/api/web-access/route.ts +1 -1
- package/.next/types/app/api/web6/consults/route.ts +1 -1
- package/.next/types/app/api/worktrees/route.ts +1 -1
- package/.next/types/app/layout.ts +1 -1
- package/.next/types/app/page.ts +1 -1
- package/.next/types/app/recover/page.ts +1 -1
- package/LICENSE +22 -22
- package/README.md +273 -271
- package/Tools/CUELO_Setup/files/btw-server.js +346 -346
- package/Tools/CUELO_Setup/files/native-runtime-patch.js +238 -238
- package/Tools/CUELO_Setup/files/subagent-server.js +587 -587
- package/Tools/CUELO_Setup/files/usage-server.js +1057 -1057
- package/Tools/OMP_Global_Config/agent/AGENTS.md +144 -144
- package/Tools/OMP_Global_Config/agent/RULES.md +140 -140
- package/Tools/OMP_Global_Config/agent/TITLE_SYSTEM.md +17 -17
- package/Tools/OMP_Global_Config/agent/WATCHDOG.md +55 -55
- package/Tools/OMP_Global_Config/agent/agents/maker.md +413 -413
- package/Tools/OMP_Global_Config/agent/extensions/character-voice.ts +1035 -1035
- package/Tools/OMP_Global_Config/agent/extensions/command-guard/index.ts +1110 -1110
- package/Tools/OMP_Global_Config/agent/extensions/command-guard/interop-hint.ts +133 -133
- package/Tools/OMP_Global_Config/agent/extensions/command-guard/matcher.ts +2258 -2258
- package/Tools/OMP_Global_Config/agent/extensions/command-guard/powershell-syntax.ts +210 -210
- package/Tools/OMP_Global_Config/agent/extensions/command-guard/task-guard.ts +1137 -1121
- package/Tools/OMP_Global_Config/agent/extensions/cross-review.ts +132 -132
- package/Tools/OMP_Global_Config/agent/extensions/docmap-route.ts +133 -133
- package/Tools/OMP_Global_Config/agent/extensions/draft/markdown.ts +226 -226
- package/Tools/OMP_Global_Config/agent/extensions/draft/ooxml.ts +222 -222
- package/Tools/OMP_Global_Config/agent/extensions/draft/pdf.ts +63 -63
- package/Tools/OMP_Global_Config/agent/extensions/draft/zip.ts +73 -73
- package/Tools/OMP_Global_Config/agent/extensions/draft.ts +204 -204
- package/Tools/OMP_Global_Config/agent/extensions/edit-lessons.ts +159 -159
- package/Tools/OMP_Global_Config/agent/extensions/external-advice-check.ts +36 -36
- package/Tools/OMP_Global_Config/agent/extensions/gitnexus-autosync.ts +406 -406
- package/Tools/OMP_Global_Config/agent/extensions/image-question-router.ts +55 -55
- package/Tools/OMP_Global_Config/agent/extensions/jev-runtime.ts +2700 -2700
- package/Tools/OMP_Global_Config/agent/extensions/korean-reply-guard.ts +190 -190
- package/Tools/OMP_Global_Config/agent/extensions/lib/cross-review.ts +1208 -1208
- package/Tools/OMP_Global_Config/agent/extensions/lib/docmap-search.ts +612 -612
- package/Tools/OMP_Global_Config/agent/extensions/lib/job-observation.ts +182 -182
- package/Tools/OMP_Global_Config/agent/extensions/lib/maker-routing.ts +1332 -1332
- package/Tools/OMP_Global_Config/agent/extensions/lib/mcp-selection.ts +361 -361
- package/Tools/OMP_Global_Config/agent/extensions/lib/memory-excerpt.ts +5 -5
- package/Tools/OMP_Global_Config/agent/extensions/lib/native-judge.ts +113 -113
- package/Tools/OMP_Global_Config/agent/extensions/lib/prepared-task.ts +211 -211
- package/Tools/OMP_Global_Config/agent/extensions/lib/routing-ledger.ts +538 -538
- package/Tools/OMP_Global_Config/agent/extensions/lib/session-identity.ts +34 -34
- package/Tools/OMP_Global_Config/agent/extensions/lib/task-progress.ts +492 -492
- package/Tools/OMP_Global_Config/agent/extensions/mcp-selection.ts +301 -301
- package/Tools/OMP_Global_Config/agent/extensions/prompt-compact.ts +67 -67
- package/Tools/OMP_Global_Config/agent/extensions/report-chart-guard.ts +102 -102
- package/Tools/OMP_Global_Config/agent/extensions/skim.ts +332 -332
- package/Tools/OMP_Global_Config/agent/extensions/steering-reply-gate.ts +207 -207
- package/Tools/OMP_Global_Config/agent/extensions/todo-nudge.ts +80 -80
- package/Tools/OMP_Global_Config/agent/extensions/turn-end-guard.ts +421 -421
- package/Tools/OMP_Global_Config/agent/prompt-copies/AGENTS.en.md +147 -147
- package/Tools/OMP_Global_Config/agent/prompt-copies/RULES.en.md +49 -49
- package/Tools/OMP_Global_Config/agent/rules/docs-handoff.md +39 -39
- package/Tools/OMP_Global_Config/agent/rules/frontend.md +74 -74
- package/Tools/OMP_Global_Config/agent/rules/harness-policy.json +1224 -1224
- package/Tools/OMP_Global_Config/agent/rules/legacy-encoding.md +21 -21
- package/Tools/OMP_Global_Config/agent/rules/meta-harness.md +45 -45
- package/Tools/OMP_Global_Config/agent/rules/omp-harness.md +151 -151
- package/Tools/OMP_Global_Config/agent/rules/subagent.md +750 -750
- package/Tools/OMP_Global_Config/agent/rules/task-guard.md +62 -62
- package/Tools/OMP_Global_Config/agent/rules/verdict.md +210 -210
- package/Tools/OMP_Global_Config/agent/rules/web6-consult.md +83 -83
- package/Tools/OMP_Global_Config/agent/sop/_common.md +136 -136
- package/Tools/OMP_Global_Config/agent/sop/_writer.md +259 -259
- package/Tools/OMP_Global_Config/agent/sop/maker.md +9 -9
- package/Tools/OMP_Global_Config/agent/tools/git-finalizer/finalizer.ps1 +409 -409
- package/Tools/OMP_Global_Config/agent/tools/git-finalizer/index.ts +303 -303
- package/Tools/OMP_Global_Config/agent/tools/git-finalizer/sync-checkout.ts +184 -184
- package/Tools/OMP_Global_Config/agent/tools/git-finalizer/wsl-git.ts +96 -96
- package/Tools/OMP_Global_Config/agent/tools/windows-build/index.ts +387 -387
- package/Tools/OMP_Global_Config/agent/tools/windows-control/control.ps1 +200 -200
- package/Tools/OMP_Global_Config/agent/tools/windows-control/index.ts +255 -255
- package/Tools/OMP_Global_Config/notices/orchestrate.md +26 -26
- package/Tools/OMP_Global_Config/notices/ultrathink.md +9 -9
- package/Tools/OMP_Global_Config/notices/workflow.md +16 -16
- package/Tools/OMP_Global_Config/patches/apply-core-patch.mjs +18339 -18304
- package/Tools/OMP_Global_Config/patches/apply-notices.mjs +135 -135
- package/bin/cuelo-options.js +38 -38
- package/bin/cuelo.js +273 -273
- package/bin/docker-entrypoint.mjs +19 -19
- package/bin/password-prompt.js +100 -100
- package/bin/prepare-runtime.js +112 -112
- package/bin/runtime.js +134 -134
- package/bin/web-auth-store.d.ts +104 -104
- package/bin/web-auth-store.js +593 -593
- package/install.mjs +427 -427
- package/next.config.ts +104 -104
- package/package.json +141 -141
- package/public/audio/completion/voices.json +1393 -1393
- package/public/fonts/cuelo-display/NOTICE.txt +15 -15
- package/public/fonts/fonts.css +288 -288
- package/public/fonts/jetbrains-mono/OFL.txt +93 -93
- package/public/fonts/wanted-sans/OFL.txt +93 -93
- package/public/icons/catppuccin/LICENSE +22 -22
- package/public/icons/catppuccin/README.md +8 -8
- package/public/icons/catppuccin/latte/_file.svg +3 -3
- package/public/icons/catppuccin/latte/_folder.svg +3 -3
- package/public/icons/catppuccin/latte/_folder_open.svg +3 -3
- package/public/icons/catppuccin/latte/bash.svg +7 -7
- package/public/icons/catppuccin/latte/bun-lock.svg +8 -8
- package/public/icons/catppuccin/latte/config.svg +3 -3
- package/public/icons/catppuccin/latte/css.svg +6 -6
- package/public/icons/catppuccin/latte/database.svg +3 -3
- package/public/icons/catppuccin/latte/docker.svg +3 -3
- package/public/icons/catppuccin/latte/env.svg +3 -3
- package/public/icons/catppuccin/latte/eslint.svg +6 -6
- package/public/icons/catppuccin/latte/git.svg +6 -6
- package/public/icons/catppuccin/latte/go.svg +3 -3
- package/public/icons/catppuccin/latte/graphql.svg +3 -3
- package/public/icons/catppuccin/latte/html.svg +6 -6
- package/public/icons/catppuccin/latte/javascript-react.svg +8 -8
- package/public/icons/catppuccin/latte/javascript.svg +6 -6
- package/public/icons/catppuccin/latte/json.svg +3 -3
- package/public/icons/catppuccin/latte/lock.svg +3 -3
- package/public/icons/catppuccin/latte/markdown.svg +3 -3
- package/public/icons/catppuccin/latte/ms-word.svg +6 -6
- package/public/icons/catppuccin/latte/next.svg +6 -6
- package/public/icons/catppuccin/latte/npm-lock.svg +7 -7
- package/public/icons/catppuccin/latte/pdf.svg +3 -3
- package/public/icons/catppuccin/latte/python.svg +8 -8
- package/public/icons/catppuccin/latte/rust.svg +6 -6
- package/public/icons/catppuccin/latte/sass.svg +3 -3
- package/public/icons/catppuccin/latte/terraform.svg +3 -3
- package/public/icons/catppuccin/latte/toml.svg +3 -3
- package/public/icons/catppuccin/latte/typescript-react.svg +8 -8
- package/public/icons/catppuccin/latte/typescript.svg +6 -6
- package/public/icons/catppuccin/latte/yaml.svg +3 -3
- package/public/icons/catppuccin/mocha/_file.svg +3 -3
- package/public/icons/catppuccin/mocha/_folder.svg +3 -3
- package/public/icons/catppuccin/mocha/_folder_open.svg +3 -3
- package/public/icons/catppuccin/mocha/bash.svg +7 -7
- package/public/icons/catppuccin/mocha/bun-lock.svg +8 -8
- package/public/icons/catppuccin/mocha/config.svg +3 -3
- package/public/icons/catppuccin/mocha/css.svg +6 -6
- package/public/icons/catppuccin/mocha/database.svg +3 -3
- package/public/icons/catppuccin/mocha/docker.svg +3 -3
- package/public/icons/catppuccin/mocha/env.svg +3 -3
- package/public/icons/catppuccin/mocha/eslint.svg +6 -6
- package/public/icons/catppuccin/mocha/git.svg +6 -6
- package/public/icons/catppuccin/mocha/go.svg +3 -3
- package/public/icons/catppuccin/mocha/graphql.svg +3 -3
- package/public/icons/catppuccin/mocha/html.svg +6 -6
- package/public/icons/catppuccin/mocha/javascript-react.svg +8 -8
- package/public/icons/catppuccin/mocha/javascript.svg +6 -6
- package/public/icons/catppuccin/mocha/json.svg +3 -3
- package/public/icons/catppuccin/mocha/lock.svg +3 -3
- package/public/icons/catppuccin/mocha/markdown.svg +3 -3
- package/public/icons/catppuccin/mocha/ms-word.svg +6 -6
- package/public/icons/catppuccin/mocha/next.svg +6 -6
- package/public/icons/catppuccin/mocha/npm-lock.svg +7 -7
- package/public/icons/catppuccin/mocha/pdf.svg +3 -3
- package/public/icons/catppuccin/mocha/python.svg +8 -8
- package/public/icons/catppuccin/mocha/rust.svg +6 -6
- package/public/icons/catppuccin/mocha/sass.svg +3 -3
- package/public/icons/catppuccin/mocha/terraform.svg +3 -3
- package/public/icons/catppuccin/mocha/toml.svg +3 -3
- package/public/icons/catppuccin/mocha/typescript-react.svg +8 -8
- package/public/icons/catppuccin/mocha/typescript.svg +6 -6
- package/public/icons/catppuccin/mocha/yaml.svg +3 -3
- package/public/icons/cuelo-icon.svg +8 -8
- package/public/offline.html +145 -145
- package/public/sw.js +135 -135
- package/.next/server/chunks/205.js +0 -7
- package/.next/server/chunks/7738.js +0 -1
- package/.next/static/chunks/1966.b8f83f7ba376c962.js +0 -1
- package/.next/static/chunks/3899.3f761f7e3a944e8c.js +0 -1
- package/.next/static/chunks/app/_not-found/page-9e5295df095e2e41.js +0 -1
- package/.next/static/chunks/app/layout-8b29ba206a96135e.js +0 -1
- package/.next/static/chunks/main-1ddb405481ce21f6.js +0 -5
- package/.next/static/chunks/next/dist/client/components/builtin/global-error-50782c8c9c06c8b5.js +0 -1
- package/.next/static/chunks/webpack-7b514b9855bd748c.js +0 -1
- /package/.next/static/{dQA--5NX6NZt_A6pLZUde → -mDQEyWQ8VQsAfLpiACIP}/_buildManifest.js +0 -0
- /package/.next/static/{dQA--5NX6NZt_A6pLZUde → -mDQEyWQ8VQsAfLpiACIP}/_ssgManifest.js +0 -0
|
@@ -1,303 +1,303 @@
|
|
|
1
|
-
import type { CustomToolFactory } from "@oh-my-pi/pi-coding-agent";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
|
-
import { access, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
-
import { tmpdir } from "node:os";
|
|
5
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
|
-
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { currentBlobs, ledgerPath, resolveRepoTargets, runGate, spawnExec } from "../../extensions/lib/cross-review";
|
|
8
|
-
import { hasPosixGitdirWorktree, isWindowsDrivePath, selectGitExecutor, usesWindowsDriveRepo } from "./wsl-git";
|
|
9
|
-
|
|
10
|
-
export { hasPosixGitdirWorktree };
|
|
11
|
-
|
|
12
|
-
interface FinalizerResult {
|
|
13
|
-
ok: boolean;
|
|
14
|
-
stage: string;
|
|
15
|
-
error?: string;
|
|
16
|
-
commitSha?: string;
|
|
17
|
-
remote?: string;
|
|
18
|
-
upstreamRef?: string;
|
|
19
|
-
resumedPush?: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const scriptPath = fileURLToPath(new URL("./finalizer.ps1", import.meta.url));
|
|
23
|
-
const helperPath = "Tools/CUELO_Setup/files/source-build-helper.js";
|
|
24
|
-
const manifestPath = "Tools/CUELO_Setup/files/source-integrity.json";
|
|
25
|
-
const excludedSourcePaths: Record<string, true> = {
|
|
26
|
-
"HANDOFF.md": true, "PC-SETUP.md": true, ".publicignore": true, ".publicdeny": true,
|
|
27
|
-
".github/workflows/publish-public.yml": true,
|
|
28
|
-
};
|
|
29
|
-
const generatedSourceSegments: Record<string, true> = { ".git": true, ".next": true, ".omp": true, node_modules: true };
|
|
30
|
-
// helper.buildFileMap()은 전체 트리를 검사하므로 무관한 writer의 변경까지 읽는다.
|
|
31
|
-
// 여기서는 helper의 source 경계와 LF 해시 규칙을 선택된 파일에만 적용한다.
|
|
32
|
-
const manifestInstructions = `From cwd Tools/CUELO_Setup, run:
|
|
33
|
-
node files/source-build-helper.js create-manifest ../.. files/runtime-integrity.json --output files/source-integrity.json
|
|
34
|
-
node files/source-build-helper.js verify-source ../.. files/source-integrity.json files/runtime-integrity.json
|
|
35
|
-
Then include Tools/CUELO_Setup/files/source-integrity.json in the same commit.`;
|
|
36
|
-
const memorySyncScript = "Tools/OMP_Global_Config/memory-sync/sync.ts";
|
|
37
|
-
const memoryTransport = "Tools/OMP_Global_Config/memory-sync/memories.jsonl";
|
|
38
|
-
const pathKey = (value: string) => process.platform === "win32" ? value.toLowerCase() : value;
|
|
39
|
-
|
|
40
|
-
type Exec = (command: string, args: string[], options: { cwd: string }) => Promise<{ code: number; stdout: string; stderr?: string }>;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 기억 PC 간 동기화의 export를 커밋 경로에 붙인다. 원래 hook은 손으로 돌리는 export.ps1 끝뿐이라
|
|
44
|
-
* 평소 커밋·배포로는 한 번도 내보내지지 않았다(2026-10-02). 같은 저장소 커밋이면 먼저 export하고,
|
|
45
|
-
* 바뀐 transport 파일을 커밋 목록에 더한다. 실패는 커밋을 막지 않고 결과 문구로 알린다.
|
|
46
|
-
*/
|
|
47
|
-
async function exportMemories(cwd: string, files: string[], exec: Exec): Promise<{ file?: string; note?: string }> {
|
|
48
|
-
const top = await exec("git", ["-C", cwd, "rev-parse", "--show-toplevel"], { cwd });
|
|
49
|
-
if (top.code !== 0) return {};
|
|
50
|
-
const root = resolve(top.stdout.trim());
|
|
51
|
-
try {
|
|
52
|
-
await access(join(root, ...memorySyncScript.split("/")));
|
|
53
|
-
} catch {
|
|
54
|
-
return {};
|
|
55
|
-
}
|
|
56
|
-
const inRoot = (path: string) => {
|
|
57
|
-
const rel = relative(root, resolve(cwd, path));
|
|
58
|
-
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
59
|
-
};
|
|
60
|
-
if (!files.every(inRoot)) return {};
|
|
61
|
-
const transport = relative(cwd, join(root, ...memoryTransport.split("/"))).split(sep).join("/");
|
|
62
|
-
if (transport.startsWith("../")) return { note: "기억 export 생략: 현재 cwd 밖의 파일이라 이 커밋에 넣을 수 없다." };
|
|
63
|
-
if (files.some((file) => pathKey(file.replace(/\\/g, "/")) === pathKey(transport))) return {};
|
|
64
|
-
const exported = await exec("bun", [join(root, ...memorySyncScript.split("/")), "export", root], { cwd: root });
|
|
65
|
-
if (exported.code !== 0) {
|
|
66
|
-
const reason = (exported.stderr || exported.stdout).trim().split(/\r?\n/).at(-1) || `exit ${exported.code}`;
|
|
67
|
-
return { note: `기억 export 실패(커밋은 진행): ${reason}` };
|
|
68
|
-
}
|
|
69
|
-
const status = await exec("git", ["-C", root, "status", "--porcelain", "--", memoryTransport], { cwd: root });
|
|
70
|
-
return status.code === 0 && status.stdout.trim() ? { file: transport } : {};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// 문서·그림은 HANDOFF 의무의 대상인 소스·설정 변경이 아니다. 목록 밖의 자산(예: 오디오)은 소스로 보고 알림이 날 수 있다(advisory).
|
|
74
|
-
const nonSourceFile = /\.(md|markdown|rst|adoc|txt|rtf|pdf|docx?|xlsx?|pptx?|odt|ods|odp|hwpx?|png|jpe?g|gif|svg|webp|ico|bmp|tiff?|avif|heic)$/i;
|
|
75
|
-
|
|
76
|
-
async function exists(path: string): Promise<boolean> {
|
|
77
|
-
try {
|
|
78
|
-
await access(path);
|
|
79
|
-
return true;
|
|
80
|
-
} catch {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* 프로젝트가 여럿인 저장소에서, HANDOFF가 없던 프로젝트의 첫 코드 커밋이 기록 없이 올라간 일이 두 번 있었다(2026-10-13 확인, rule://docs-handoff).
|
|
87
|
-
* 바꾼 코드 파일에서 저장소 루트(`.git`이 있는 폴더)까지 `HANDOFF.md`가 하나도 없을 때만 결과 문구로 알린다. 커밋은 막지 않는다.
|
|
88
|
-
*/
|
|
89
|
-
async function missingHandoffNote(cwd: string, files: string[]): Promise<string | undefined> {
|
|
90
|
-
// 폴더 → 그 폴더의 저장소 루트(`.git`이 있는 폴더)와, 그 폴더에서 루트까지 HANDOFF.md가 있는지. 저장소 밖이면 undefined.
|
|
91
|
-
const memo = new Map<string, Promise<{ root: string; handoff: boolean } | undefined>>();
|
|
92
|
-
const scan = (dir: string): Promise<{ root: string; handoff: boolean } | undefined> => {
|
|
93
|
-
let found = memo.get(dir);
|
|
94
|
-
if (!found) {
|
|
95
|
-
found = (async () => {
|
|
96
|
-
const handoff = await exists(join(dir, "HANDOFF.md"));
|
|
97
|
-
if (await exists(join(dir, ".git"))) return { root: dir, handoff };
|
|
98
|
-
const parent = dirname(dir);
|
|
99
|
-
if (parent === dir) return undefined;
|
|
100
|
-
const above = await scan(parent);
|
|
101
|
-
return above && { root: above.root, handoff: handoff || above.handoff };
|
|
102
|
-
})();
|
|
103
|
-
memo.set(dir, found);
|
|
104
|
-
}
|
|
105
|
-
return found;
|
|
106
|
-
};
|
|
107
|
-
const folders = new Set<string>();
|
|
108
|
-
for (const file of files) {
|
|
109
|
-
const path = resolve(cwd, file);
|
|
110
|
-
if (nonSourceFile.test(path)) continue;
|
|
111
|
-
const repo = await scan(dirname(path));
|
|
112
|
-
// `doc/`는 cwd나 요청 문자열이 아니라 대상 저장소 루트 기준 경로로 가린다.
|
|
113
|
-
if (!repo || repo.handoff || relative(repo.root, path).split(sep).includes("doc")) continue;
|
|
114
|
-
folders.add(relative(cwd, dirname(path)).split(sep).join("/") || ".");
|
|
115
|
-
}
|
|
116
|
-
if (folders.size === 0) return undefined;
|
|
117
|
-
const shown = [...folders].slice(0, 3).map((folder) => `\`${folder}\``).join(", ");
|
|
118
|
-
const more = folders.size > 3 ? ` 외 ${folders.size - 3}곳` : "";
|
|
119
|
-
return `HANDOFF 확인: ${shown}${more}에서 저장소 루트까지 HANDOFF.md가 없다. 소스·설정을 바꾼 첫 커밋이면 rule://docs-handoff대로 작업한 프로젝트 폴더에 HANDOFF.md·doc/README.md·doc/history 기록을 만들어 이어서 커밋한다(이번 커밋은 그대로 진행됐다).`;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function isSourcePath(path: string, packagedTools: string[]): boolean {
|
|
123
|
-
if (path.split("/").some((segment) => generatedSourceSegments[segment] || segment.endsWith(".tsbuildinfo"))) return false;
|
|
124
|
-
if (path === "doc" || path.startsWith("doc/") || excludedSourcePaths[path]) return false;
|
|
125
|
-
if (path.startsWith("Tools/")) {
|
|
126
|
-
return packagedTools.some((entry) => path === entry || path.startsWith(`${entry}/`));
|
|
127
|
-
}
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async function checkSourceManifest(
|
|
132
|
-
cwd: string,
|
|
133
|
-
files: string[],
|
|
134
|
-
exec: (command: string, args: string[], options: { cwd: string }) => Promise<{ code: number; stdout: string }>,
|
|
135
|
-
): Promise<void> {
|
|
136
|
-
const resolved = await resolveRepoTargets(cwd, files, exec);
|
|
137
|
-
if ("failure" in resolved) return; // 잘못된 경로·저장소 밖·여러 저장소의 최종 판정은 finalizer.ps1에 둔다.
|
|
138
|
-
const { root, targets } = resolved;
|
|
139
|
-
try {
|
|
140
|
-
await stat(join(root, ...helperPath.split("/")));
|
|
141
|
-
} catch (error) {
|
|
142
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") return;
|
|
143
|
-
throw error;
|
|
144
|
-
}
|
|
145
|
-
if (targets.some(({ path }) => pathKey(path) === pathKey(manifestPath))) return;
|
|
146
|
-
|
|
147
|
-
let packagedTools: string[] = [];
|
|
148
|
-
try {
|
|
149
|
-
const pkg = JSON.parse(await readFile(join(root, "package.json"), "utf8")) as { files?: unknown };
|
|
150
|
-
if (Array.isArray(pkg.files)) {
|
|
151
|
-
packagedTools = pkg.files
|
|
152
|
-
.filter((entry): entry is string => typeof entry === "string" && entry.startsWith("Tools/"))
|
|
153
|
-
.map((entry) => entry.replace(/\/+$/, ""));
|
|
154
|
-
}
|
|
155
|
-
} catch (error) {
|
|
156
|
-
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
157
|
-
}
|
|
158
|
-
const sourcePaths = targets.map(({ path }) => path).filter((path) => isSourcePath(path, packagedTools));
|
|
159
|
-
if (!sourcePaths.length) return;
|
|
160
|
-
|
|
161
|
-
let manifest: { files?: Record<string, string> } = {};
|
|
162
|
-
try {
|
|
163
|
-
manifest = JSON.parse(await readFile(join(root, ...manifestPath.split("/")), "utf8"));
|
|
164
|
-
} catch (error) {
|
|
165
|
-
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
166
|
-
}
|
|
167
|
-
for (const path of sourcePaths) {
|
|
168
|
-
let bytes: Buffer | undefined;
|
|
169
|
-
try {
|
|
170
|
-
bytes = await readFile(join(root, ...path.split("/")));
|
|
171
|
-
} catch (error) {
|
|
172
|
-
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
173
|
-
}
|
|
174
|
-
const content = bytes && !bytes.includes(0) && bytes.includes("\r\n")
|
|
175
|
-
? Buffer.from(bytes.toString("latin1").replace(/\r\n/g, "\n"), "latin1")
|
|
176
|
-
: bytes;
|
|
177
|
-
const hash = content && createHash("sha256").update(content).digest("hex");
|
|
178
|
-
if (!hash || manifest.files?.[path] !== hash) {
|
|
179
|
-
throw new Error(`git_finalize: source manifest is stale for ${path}. ${manifestInstructions}`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function parseResult(stdout: string): FinalizerResult | undefined {
|
|
185
|
-
const lines = stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
186
|
-
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
187
|
-
try {
|
|
188
|
-
const value = JSON.parse(lines[index]) as FinalizerResult;
|
|
189
|
-
if (value && typeof value.ok === "boolean" && typeof value.stage === "string") return value;
|
|
190
|
-
} catch {
|
|
191
|
-
// PowerShell or Git may emit a non-JSON diagnostic before the final result.
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return undefined;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* WSL에서 Windows 드라이브(`/mnt/<드라이브>/`) 위 저장소는 Linux git이 9p로 파일마다 stat해 매우 느리다.
|
|
199
|
-
* 2026-10-08 V: 아카이브(추적 파일 21만여 개)에서 Linux `git status`는 300초를 넘겼고 finalizer 제한 시간(180초)에
|
|
200
|
-
* 걸려 `exit -1`로 끊겼다. 같은 저장소의 Windows `git.exe status`는 2.5초다. 그런 저장소는 Windows PowerShell과
|
|
201
|
-
* Windows git으로 같은 finalizer를 돌리고, 넘기는 경로는 `wslpath -w`로 바꾼다. 해당하지 않으면 undefined다.
|
|
202
|
-
* 판정(`usesWindowsDriveRepo`: `/mnt/<드라이브>/` + POSIX gitdir worktree 제외)은 command-guard·앱의 git 실행기와 같다.
|
|
203
|
-
* 단 cwd만 보지 않고 대상 파일 위치도 본다. cwd가 드라이브 위여도 대상이 ext4(`~/src/...`) 같은 드라이브 밖이면 Windows git은
|
|
204
|
-
* 그 저장소를 열지 못한다. 2026-10-13 cwd `/mnt/f/CUELO`에서 `~/src/CUELO-private` 파일을 넘기자 `fatal: not a git repository`로 끝났다.
|
|
205
|
-
*/
|
|
206
|
-
export async function windowsDrivePaths(cwd: string, files: string[], paths: string[], exec: Exec): Promise<string[] | undefined> {
|
|
207
|
-
if (!files.every((file) => isWindowsDrivePath(resolve(cwd, file)))) return undefined;
|
|
208
|
-
if (!(await usesWindowsDriveRepo(cwd, files))) return undefined;
|
|
209
|
-
const converted: string[] = [];
|
|
210
|
-
for (const path of paths) {
|
|
211
|
-
const result = await exec("wslpath", ["-w", path], { cwd });
|
|
212
|
-
if (result.code !== 0 || !result.stdout.trim()) throw new Error(`git_finalize: wslpath -w ${path} 변환 실패: ${(result.stderr || result.stdout).trim()}`);
|
|
213
|
-
converted.push(result.stdout.trim());
|
|
214
|
-
}
|
|
215
|
-
return converted;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const factory: CustomToolFactory = (pi) => ({
|
|
219
|
-
name: "git_finalize",
|
|
220
|
-
label: "Git Finalize",
|
|
221
|
-
loadMode: "essential",
|
|
222
|
-
description:
|
|
223
|
-
"Atomically finalize relative exact files in one repository. Resolve every target from its nearest existing parent. Treat matching git common-dir values as the same repository: restrict those targets to paths inside cwd or an exact direct child file of the cwd repository root, while allowing one distinct repository; reject mixed repositories before staging. Serialize by repository/upstream, verify exact paths and ancestry, commit, then push the created SHA. If an earlier run committed but its push failed, call again with the same files: unchanged files found in unpushed upstream..HEAD commits are accepted and HEAD is pushed without a new commit. Cross-family review gate (stage `cross-review`): when there is something new to commit, a code file (docs, tests and generated files excluded) needs a passed cross_review resolve for its exact current content if crossReviewHighRisk is true, a high-risk marker hits its changed lines, or the deterministic 10% sample selects the commit; otherwise the call is refused with the files, the reason and the next step. crossReviewWaiver is allowed only on the user's explicit instruction and is recorded. Main only.",
|
|
224
|
-
parameters: pi.zod.object({
|
|
225
|
-
files: pi.zod.array(pi.zod.string()),
|
|
226
|
-
message: pi.zod.string(),
|
|
227
|
-
crossReviewHighRisk: pi.zod.boolean().optional(),
|
|
228
|
-
crossReviewWaiver: pi.zod.string().optional(),
|
|
229
|
-
}),
|
|
230
|
-
|
|
231
|
-
async execute(_toolCallId, params, onUpdate, _ctx, signal) {
|
|
232
|
-
const requested = params.files.map((value) => value.trim()).filter(Boolean);
|
|
233
|
-
const message = params.message.trim();
|
|
234
|
-
if (requested.length === 0) throw new Error("git_finalize requires at least one exact file path.");
|
|
235
|
-
if (!message) throw new Error("git_finalize requires a non-empty commit message.");
|
|
236
|
-
|
|
237
|
-
const exec: Exec = (command, args, options) => pi.exec(command, args, { ...options, signal });
|
|
238
|
-
await checkSourceManifest(pi.cwd, requested, exec);
|
|
239
|
-
// 계열 교차검증 게이트: 새로 커밋할 코드가 trigger·표본에 걸리면 통과한 검토 기록이 있어야 한다.
|
|
240
|
-
// 기억 transport(memory.file)는 생성물이라 요청 파일에만 적용한다.
|
|
241
|
-
let gateBlobs: Record<string, string> | undefined;
|
|
242
|
-
try {
|
|
243
|
-
gateBlobs = (await runGate({
|
|
244
|
-
cwd: pi.cwd, files: requested, highRisk: params.crossReviewHighRisk, waiver: params.crossReviewWaiver,
|
|
245
|
-
ledgerPath: ledgerPath(), exec: spawnExec(signal), gitFor: async (root) => (await selectGitExecutor(root)).command,
|
|
246
|
-
})).gateBlobs;
|
|
247
|
-
} catch (error) {
|
|
248
|
-
// 게이트가 판정하지 못한 오류(git 실패 등)도 열어 두지 않는다: 확인하지 못한 채 커밋하지 않는다.
|
|
249
|
-
throw new Error(`git_finalize failed at cross-review: ${error instanceof Error ? error.message : String(error)}`);
|
|
250
|
-
}
|
|
251
|
-
const memory = await exportMemories(pi.cwd, requested, exec);
|
|
252
|
-
const files = memory.file ? [...requested, memory.file] : requested;
|
|
253
|
-
const handoffNote = await missingHandoffNote(pi.cwd, requested);
|
|
254
|
-
if (gateBlobs && memory.file) {
|
|
255
|
-
// 기억 transport는 게이트 뒤에 방금 만든 생성물(검토 대상 아님)이라 finalizer가 같은 기준으로 비교하도록 지금 blob을 넣는다.
|
|
256
|
-
const transport = await resolveRepoTargets(pi.cwd, [memory.file], exec);
|
|
257
|
-
if (!("failure" in transport)) {
|
|
258
|
-
const blobs = await currentBlobs(spawnExec(signal), (await selectGitExecutor(transport.root)).command, transport.root, transport.targets.map((target) => target.path));
|
|
259
|
-
for (const [path, blob] of blobs) gateBlobs[path] = blob;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
onUpdate?.({
|
|
264
|
-
content: [{ type: "text", text: "Repository finalizer lock을 획득하고 변경 경계를 확인하는 중입니다." }],
|
|
265
|
-
details: { phase: "preflight", files },
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
const tempDir = await mkdtemp(join(tmpdir(), "omp-git-finalize-"));
|
|
269
|
-
const requestPath = join(tempDir, "request.json");
|
|
270
|
-
try {
|
|
271
|
-
const windows = await windowsDrivePaths(pi.cwd, files, [pi.cwd, scriptPath, requestPath], exec);
|
|
272
|
-
// gateBlobs: 게이트가 본 코드 파일의 blob. finalizer.ps1이 잠금 안에서 stage된 내용과 대조해, 게이트 뒤에 파일이 바뀌었으면 커밋하지 않는다.
|
|
273
|
-
await writeFile(requestPath, JSON.stringify({ cwd: windows?.[0] ?? pi.cwd, files, message, ...(gateBlobs && Object.keys(gateBlobs).length > 0 ? { gateBlobs } : {}) }), "utf8");
|
|
274
|
-
const result = await pi.exec(
|
|
275
|
-
process.platform === "win32" || windows ? "powershell.exe" : "pwsh",
|
|
276
|
-
["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", windows?.[1] ?? scriptPath, "-Request", windows?.[2] ?? requestPath],
|
|
277
|
-
{ cwd: pi.cwd, signal, timeout: 180_000 },
|
|
278
|
-
);
|
|
279
|
-
const parsed = parseResult(result.stdout);
|
|
280
|
-
if (result.code !== 0 || !parsed?.ok) {
|
|
281
|
-
const detail = parsed?.error || result.stderr.trim() || result.stdout.trim() || `exit ${result.code}`;
|
|
282
|
-
throw new Error(`git_finalize failed${parsed?.stage ? ` at ${parsed.stage}` : ""}: ${detail}`);
|
|
283
|
-
}
|
|
284
|
-
return {
|
|
285
|
-
content: [
|
|
286
|
-
{
|
|
287
|
-
type: "text",
|
|
288
|
-
text: `Committed and pushed ${parsed.commitSha} to ${parsed.remote} ${parsed.upstreamRef}.`
|
|
289
|
-
+ (parsed.resumedPush ? " 이미 커밋돼 있던 미전송 커밋을 함께 다시 보냈다." : "")
|
|
290
|
-
+ (memory.file ? ` 기억 transport(${memory.file})를 함께 커밋했다.` : "")
|
|
291
|
-
+ (memory.note ? ` ${memory.note}` : "")
|
|
292
|
-
+ (handoffNote ? ` ${handoffNote}` : ""),
|
|
293
|
-
},
|
|
294
|
-
],
|
|
295
|
-
details: parsed,
|
|
296
|
-
};
|
|
297
|
-
} finally {
|
|
298
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
299
|
-
}
|
|
300
|
-
},
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
export default factory;
|
|
1
|
+
import type { CustomToolFactory } from "@oh-my-pi/pi-coding-agent";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { access, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { currentBlobs, ledgerPath, resolveRepoTargets, runGate, spawnExec } from "../../extensions/lib/cross-review";
|
|
8
|
+
import { hasPosixGitdirWorktree, isWindowsDrivePath, selectGitExecutor, usesWindowsDriveRepo } from "./wsl-git";
|
|
9
|
+
|
|
10
|
+
export { hasPosixGitdirWorktree };
|
|
11
|
+
|
|
12
|
+
interface FinalizerResult {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
stage: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
commitSha?: string;
|
|
17
|
+
remote?: string;
|
|
18
|
+
upstreamRef?: string;
|
|
19
|
+
resumedPush?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const scriptPath = fileURLToPath(new URL("./finalizer.ps1", import.meta.url));
|
|
23
|
+
const helperPath = "Tools/CUELO_Setup/files/source-build-helper.js";
|
|
24
|
+
const manifestPath = "Tools/CUELO_Setup/files/source-integrity.json";
|
|
25
|
+
const excludedSourcePaths: Record<string, true> = {
|
|
26
|
+
"HANDOFF.md": true, "PC-SETUP.md": true, ".publicignore": true, ".publicdeny": true,
|
|
27
|
+
".github/workflows/publish-public.yml": true,
|
|
28
|
+
};
|
|
29
|
+
const generatedSourceSegments: Record<string, true> = { ".git": true, ".next": true, ".omp": true, node_modules: true };
|
|
30
|
+
// helper.buildFileMap()은 전체 트리를 검사하므로 무관한 writer의 변경까지 읽는다.
|
|
31
|
+
// 여기서는 helper의 source 경계와 LF 해시 규칙을 선택된 파일에만 적용한다.
|
|
32
|
+
const manifestInstructions = `From cwd Tools/CUELO_Setup, run:
|
|
33
|
+
node files/source-build-helper.js create-manifest ../.. files/runtime-integrity.json --output files/source-integrity.json
|
|
34
|
+
node files/source-build-helper.js verify-source ../.. files/source-integrity.json files/runtime-integrity.json
|
|
35
|
+
Then include Tools/CUELO_Setup/files/source-integrity.json in the same commit.`;
|
|
36
|
+
const memorySyncScript = "Tools/OMP_Global_Config/memory-sync/sync.ts";
|
|
37
|
+
const memoryTransport = "Tools/OMP_Global_Config/memory-sync/memories.jsonl";
|
|
38
|
+
const pathKey = (value: string) => process.platform === "win32" ? value.toLowerCase() : value;
|
|
39
|
+
|
|
40
|
+
type Exec = (command: string, args: string[], options: { cwd: string }) => Promise<{ code: number; stdout: string; stderr?: string }>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 기억 PC 간 동기화의 export를 커밋 경로에 붙인다. 원래 hook은 손으로 돌리는 export.ps1 끝뿐이라
|
|
44
|
+
* 평소 커밋·배포로는 한 번도 내보내지지 않았다(2026-10-02). 같은 저장소 커밋이면 먼저 export하고,
|
|
45
|
+
* 바뀐 transport 파일을 커밋 목록에 더한다. 실패는 커밋을 막지 않고 결과 문구로 알린다.
|
|
46
|
+
*/
|
|
47
|
+
async function exportMemories(cwd: string, files: string[], exec: Exec): Promise<{ file?: string; note?: string }> {
|
|
48
|
+
const top = await exec("git", ["-C", cwd, "rev-parse", "--show-toplevel"], { cwd });
|
|
49
|
+
if (top.code !== 0) return {};
|
|
50
|
+
const root = resolve(top.stdout.trim());
|
|
51
|
+
try {
|
|
52
|
+
await access(join(root, ...memorySyncScript.split("/")));
|
|
53
|
+
} catch {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
const inRoot = (path: string) => {
|
|
57
|
+
const rel = relative(root, resolve(cwd, path));
|
|
58
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
59
|
+
};
|
|
60
|
+
if (!files.every(inRoot)) return {};
|
|
61
|
+
const transport = relative(cwd, join(root, ...memoryTransport.split("/"))).split(sep).join("/");
|
|
62
|
+
if (transport.startsWith("../")) return { note: "기억 export 생략: 현재 cwd 밖의 파일이라 이 커밋에 넣을 수 없다." };
|
|
63
|
+
if (files.some((file) => pathKey(file.replace(/\\/g, "/")) === pathKey(transport))) return {};
|
|
64
|
+
const exported = await exec("bun", [join(root, ...memorySyncScript.split("/")), "export", root], { cwd: root });
|
|
65
|
+
if (exported.code !== 0) {
|
|
66
|
+
const reason = (exported.stderr || exported.stdout).trim().split(/\r?\n/).at(-1) || `exit ${exported.code}`;
|
|
67
|
+
return { note: `기억 export 실패(커밋은 진행): ${reason}` };
|
|
68
|
+
}
|
|
69
|
+
const status = await exec("git", ["-C", root, "status", "--porcelain", "--", memoryTransport], { cwd: root });
|
|
70
|
+
return status.code === 0 && status.stdout.trim() ? { file: transport } : {};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 문서·그림은 HANDOFF 의무의 대상인 소스·설정 변경이 아니다. 목록 밖의 자산(예: 오디오)은 소스로 보고 알림이 날 수 있다(advisory).
|
|
74
|
+
const nonSourceFile = /\.(md|markdown|rst|adoc|txt|rtf|pdf|docx?|xlsx?|pptx?|odt|ods|odp|hwpx?|png|jpe?g|gif|svg|webp|ico|bmp|tiff?|avif|heic)$/i;
|
|
75
|
+
|
|
76
|
+
async function exists(path: string): Promise<boolean> {
|
|
77
|
+
try {
|
|
78
|
+
await access(path);
|
|
79
|
+
return true;
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 프로젝트가 여럿인 저장소에서, HANDOFF가 없던 프로젝트의 첫 코드 커밋이 기록 없이 올라간 일이 두 번 있었다(2026-10-13 확인, rule://docs-handoff).
|
|
87
|
+
* 바꾼 코드 파일에서 저장소 루트(`.git`이 있는 폴더)까지 `HANDOFF.md`가 하나도 없을 때만 결과 문구로 알린다. 커밋은 막지 않는다.
|
|
88
|
+
*/
|
|
89
|
+
async function missingHandoffNote(cwd: string, files: string[]): Promise<string | undefined> {
|
|
90
|
+
// 폴더 → 그 폴더의 저장소 루트(`.git`이 있는 폴더)와, 그 폴더에서 루트까지 HANDOFF.md가 있는지. 저장소 밖이면 undefined.
|
|
91
|
+
const memo = new Map<string, Promise<{ root: string; handoff: boolean } | undefined>>();
|
|
92
|
+
const scan = (dir: string): Promise<{ root: string; handoff: boolean } | undefined> => {
|
|
93
|
+
let found = memo.get(dir);
|
|
94
|
+
if (!found) {
|
|
95
|
+
found = (async () => {
|
|
96
|
+
const handoff = await exists(join(dir, "HANDOFF.md"));
|
|
97
|
+
if (await exists(join(dir, ".git"))) return { root: dir, handoff };
|
|
98
|
+
const parent = dirname(dir);
|
|
99
|
+
if (parent === dir) return undefined;
|
|
100
|
+
const above = await scan(parent);
|
|
101
|
+
return above && { root: above.root, handoff: handoff || above.handoff };
|
|
102
|
+
})();
|
|
103
|
+
memo.set(dir, found);
|
|
104
|
+
}
|
|
105
|
+
return found;
|
|
106
|
+
};
|
|
107
|
+
const folders = new Set<string>();
|
|
108
|
+
for (const file of files) {
|
|
109
|
+
const path = resolve(cwd, file);
|
|
110
|
+
if (nonSourceFile.test(path)) continue;
|
|
111
|
+
const repo = await scan(dirname(path));
|
|
112
|
+
// `doc/`는 cwd나 요청 문자열이 아니라 대상 저장소 루트 기준 경로로 가린다.
|
|
113
|
+
if (!repo || repo.handoff || relative(repo.root, path).split(sep).includes("doc")) continue;
|
|
114
|
+
folders.add(relative(cwd, dirname(path)).split(sep).join("/") || ".");
|
|
115
|
+
}
|
|
116
|
+
if (folders.size === 0) return undefined;
|
|
117
|
+
const shown = [...folders].slice(0, 3).map((folder) => `\`${folder}\``).join(", ");
|
|
118
|
+
const more = folders.size > 3 ? ` 외 ${folders.size - 3}곳` : "";
|
|
119
|
+
return `HANDOFF 확인: ${shown}${more}에서 저장소 루트까지 HANDOFF.md가 없다. 소스·설정을 바꾼 첫 커밋이면 rule://docs-handoff대로 작업한 프로젝트 폴더에 HANDOFF.md·doc/README.md·doc/history 기록을 만들어 이어서 커밋한다(이번 커밋은 그대로 진행됐다).`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function isSourcePath(path: string, packagedTools: string[]): boolean {
|
|
123
|
+
if (path.split("/").some((segment) => generatedSourceSegments[segment] || segment.endsWith(".tsbuildinfo"))) return false;
|
|
124
|
+
if (path === "doc" || path.startsWith("doc/") || excludedSourcePaths[path]) return false;
|
|
125
|
+
if (path.startsWith("Tools/")) {
|
|
126
|
+
return packagedTools.some((entry) => path === entry || path.startsWith(`${entry}/`));
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function checkSourceManifest(
|
|
132
|
+
cwd: string,
|
|
133
|
+
files: string[],
|
|
134
|
+
exec: (command: string, args: string[], options: { cwd: string }) => Promise<{ code: number; stdout: string }>,
|
|
135
|
+
): Promise<void> {
|
|
136
|
+
const resolved = await resolveRepoTargets(cwd, files, exec);
|
|
137
|
+
if ("failure" in resolved) return; // 잘못된 경로·저장소 밖·여러 저장소의 최종 판정은 finalizer.ps1에 둔다.
|
|
138
|
+
const { root, targets } = resolved;
|
|
139
|
+
try {
|
|
140
|
+
await stat(join(root, ...helperPath.split("/")));
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return;
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
if (targets.some(({ path }) => pathKey(path) === pathKey(manifestPath))) return;
|
|
146
|
+
|
|
147
|
+
let packagedTools: string[] = [];
|
|
148
|
+
try {
|
|
149
|
+
const pkg = JSON.parse(await readFile(join(root, "package.json"), "utf8")) as { files?: unknown };
|
|
150
|
+
if (Array.isArray(pkg.files)) {
|
|
151
|
+
packagedTools = pkg.files
|
|
152
|
+
.filter((entry): entry is string => typeof entry === "string" && entry.startsWith("Tools/"))
|
|
153
|
+
.map((entry) => entry.replace(/\/+$/, ""));
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
157
|
+
}
|
|
158
|
+
const sourcePaths = targets.map(({ path }) => path).filter((path) => isSourcePath(path, packagedTools));
|
|
159
|
+
if (!sourcePaths.length) return;
|
|
160
|
+
|
|
161
|
+
let manifest: { files?: Record<string, string> } = {};
|
|
162
|
+
try {
|
|
163
|
+
manifest = JSON.parse(await readFile(join(root, ...manifestPath.split("/")), "utf8"));
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
166
|
+
}
|
|
167
|
+
for (const path of sourcePaths) {
|
|
168
|
+
let bytes: Buffer | undefined;
|
|
169
|
+
try {
|
|
170
|
+
bytes = await readFile(join(root, ...path.split("/")));
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
173
|
+
}
|
|
174
|
+
const content = bytes && !bytes.includes(0) && bytes.includes("\r\n")
|
|
175
|
+
? Buffer.from(bytes.toString("latin1").replace(/\r\n/g, "\n"), "latin1")
|
|
176
|
+
: bytes;
|
|
177
|
+
const hash = content && createHash("sha256").update(content).digest("hex");
|
|
178
|
+
if (!hash || manifest.files?.[path] !== hash) {
|
|
179
|
+
throw new Error(`git_finalize: source manifest is stale for ${path}. ${manifestInstructions}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function parseResult(stdout: string): FinalizerResult | undefined {
|
|
185
|
+
const lines = stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
186
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
187
|
+
try {
|
|
188
|
+
const value = JSON.parse(lines[index]) as FinalizerResult;
|
|
189
|
+
if (value && typeof value.ok === "boolean" && typeof value.stage === "string") return value;
|
|
190
|
+
} catch {
|
|
191
|
+
// PowerShell or Git may emit a non-JSON diagnostic before the final result.
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* WSL에서 Windows 드라이브(`/mnt/<드라이브>/`) 위 저장소는 Linux git이 9p로 파일마다 stat해 매우 느리다.
|
|
199
|
+
* 2026-10-08 V: 아카이브(추적 파일 21만여 개)에서 Linux `git status`는 300초를 넘겼고 finalizer 제한 시간(180초)에
|
|
200
|
+
* 걸려 `exit -1`로 끊겼다. 같은 저장소의 Windows `git.exe status`는 2.5초다. 그런 저장소는 Windows PowerShell과
|
|
201
|
+
* Windows git으로 같은 finalizer를 돌리고, 넘기는 경로는 `wslpath -w`로 바꾼다. 해당하지 않으면 undefined다.
|
|
202
|
+
* 판정(`usesWindowsDriveRepo`: `/mnt/<드라이브>/` + POSIX gitdir worktree 제외)은 command-guard·앱의 git 실행기와 같다.
|
|
203
|
+
* 단 cwd만 보지 않고 대상 파일 위치도 본다. cwd가 드라이브 위여도 대상이 ext4(`~/src/...`) 같은 드라이브 밖이면 Windows git은
|
|
204
|
+
* 그 저장소를 열지 못한다. 2026-10-13 cwd `/mnt/f/CUELO`에서 `~/src/CUELO-private` 파일을 넘기자 `fatal: not a git repository`로 끝났다.
|
|
205
|
+
*/
|
|
206
|
+
export async function windowsDrivePaths(cwd: string, files: string[], paths: string[], exec: Exec): Promise<string[] | undefined> {
|
|
207
|
+
if (!files.every((file) => isWindowsDrivePath(resolve(cwd, file)))) return undefined;
|
|
208
|
+
if (!(await usesWindowsDriveRepo(cwd, files))) return undefined;
|
|
209
|
+
const converted: string[] = [];
|
|
210
|
+
for (const path of paths) {
|
|
211
|
+
const result = await exec("wslpath", ["-w", path], { cwd });
|
|
212
|
+
if (result.code !== 0 || !result.stdout.trim()) throw new Error(`git_finalize: wslpath -w ${path} 변환 실패: ${(result.stderr || result.stdout).trim()}`);
|
|
213
|
+
converted.push(result.stdout.trim());
|
|
214
|
+
}
|
|
215
|
+
return converted;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const factory: CustomToolFactory = (pi) => ({
|
|
219
|
+
name: "git_finalize",
|
|
220
|
+
label: "Git Finalize",
|
|
221
|
+
loadMode: "essential",
|
|
222
|
+
description:
|
|
223
|
+
"Atomically finalize relative exact files in one repository. Resolve every target from its nearest existing parent. Treat matching git common-dir values as the same repository: restrict those targets to paths inside cwd or an exact direct child file of the cwd repository root, while allowing one distinct repository; reject mixed repositories before staging. Serialize by repository/upstream, verify exact paths and ancestry, commit, then push the created SHA. If an earlier run committed but its push failed, call again with the same files: unchanged files found in unpushed upstream..HEAD commits are accepted and HEAD is pushed without a new commit. Cross-family review gate (stage `cross-review`): when there is something new to commit, a code file (docs, tests and generated files excluded) needs a passed cross_review resolve for its exact current content if crossReviewHighRisk is true, a high-risk marker hits its changed lines, or the deterministic 10% sample selects the commit; otherwise the call is refused with the files, the reason and the next step. crossReviewWaiver is allowed only on the user's explicit instruction and is recorded. Main only.",
|
|
224
|
+
parameters: pi.zod.object({
|
|
225
|
+
files: pi.zod.array(pi.zod.string()),
|
|
226
|
+
message: pi.zod.string(),
|
|
227
|
+
crossReviewHighRisk: pi.zod.boolean().optional(),
|
|
228
|
+
crossReviewWaiver: pi.zod.string().optional(),
|
|
229
|
+
}),
|
|
230
|
+
|
|
231
|
+
async execute(_toolCallId, params, onUpdate, _ctx, signal) {
|
|
232
|
+
const requested = params.files.map((value) => value.trim()).filter(Boolean);
|
|
233
|
+
const message = params.message.trim();
|
|
234
|
+
if (requested.length === 0) throw new Error("git_finalize requires at least one exact file path.");
|
|
235
|
+
if (!message) throw new Error("git_finalize requires a non-empty commit message.");
|
|
236
|
+
|
|
237
|
+
const exec: Exec = (command, args, options) => pi.exec(command, args, { ...options, signal });
|
|
238
|
+
await checkSourceManifest(pi.cwd, requested, exec);
|
|
239
|
+
// 계열 교차검증 게이트: 새로 커밋할 코드가 trigger·표본에 걸리면 통과한 검토 기록이 있어야 한다.
|
|
240
|
+
// 기억 transport(memory.file)는 생성물이라 요청 파일에만 적용한다.
|
|
241
|
+
let gateBlobs: Record<string, string> | undefined;
|
|
242
|
+
try {
|
|
243
|
+
gateBlobs = (await runGate({
|
|
244
|
+
cwd: pi.cwd, files: requested, highRisk: params.crossReviewHighRisk, waiver: params.crossReviewWaiver,
|
|
245
|
+
ledgerPath: ledgerPath(), exec: spawnExec(signal), gitFor: async (root) => (await selectGitExecutor(root)).command,
|
|
246
|
+
})).gateBlobs;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
// 게이트가 판정하지 못한 오류(git 실패 등)도 열어 두지 않는다: 확인하지 못한 채 커밋하지 않는다.
|
|
249
|
+
throw new Error(`git_finalize failed at cross-review: ${error instanceof Error ? error.message : String(error)}`);
|
|
250
|
+
}
|
|
251
|
+
const memory = await exportMemories(pi.cwd, requested, exec);
|
|
252
|
+
const files = memory.file ? [...requested, memory.file] : requested;
|
|
253
|
+
const handoffNote = await missingHandoffNote(pi.cwd, requested);
|
|
254
|
+
if (gateBlobs && memory.file) {
|
|
255
|
+
// 기억 transport는 게이트 뒤에 방금 만든 생성물(검토 대상 아님)이라 finalizer가 같은 기준으로 비교하도록 지금 blob을 넣는다.
|
|
256
|
+
const transport = await resolveRepoTargets(pi.cwd, [memory.file], exec);
|
|
257
|
+
if (!("failure" in transport)) {
|
|
258
|
+
const blobs = await currentBlobs(spawnExec(signal), (await selectGitExecutor(transport.root)).command, transport.root, transport.targets.map((target) => target.path));
|
|
259
|
+
for (const [path, blob] of blobs) gateBlobs[path] = blob;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
onUpdate?.({
|
|
264
|
+
content: [{ type: "text", text: "Repository finalizer lock을 획득하고 변경 경계를 확인하는 중입니다." }],
|
|
265
|
+
details: { phase: "preflight", files },
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const tempDir = await mkdtemp(join(tmpdir(), "omp-git-finalize-"));
|
|
269
|
+
const requestPath = join(tempDir, "request.json");
|
|
270
|
+
try {
|
|
271
|
+
const windows = await windowsDrivePaths(pi.cwd, files, [pi.cwd, scriptPath, requestPath], exec);
|
|
272
|
+
// gateBlobs: 게이트가 본 코드 파일의 blob. finalizer.ps1이 잠금 안에서 stage된 내용과 대조해, 게이트 뒤에 파일이 바뀌었으면 커밋하지 않는다.
|
|
273
|
+
await writeFile(requestPath, JSON.stringify({ cwd: windows?.[0] ?? pi.cwd, files, message, ...(gateBlobs && Object.keys(gateBlobs).length > 0 ? { gateBlobs } : {}) }), "utf8");
|
|
274
|
+
const result = await pi.exec(
|
|
275
|
+
process.platform === "win32" || windows ? "powershell.exe" : "pwsh",
|
|
276
|
+
["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", windows?.[1] ?? scriptPath, "-Request", windows?.[2] ?? requestPath],
|
|
277
|
+
{ cwd: pi.cwd, signal, timeout: 180_000 },
|
|
278
|
+
);
|
|
279
|
+
const parsed = parseResult(result.stdout);
|
|
280
|
+
if (result.code !== 0 || !parsed?.ok) {
|
|
281
|
+
const detail = parsed?.error || result.stderr.trim() || result.stdout.trim() || `exit ${result.code}`;
|
|
282
|
+
throw new Error(`git_finalize failed${parsed?.stage ? ` at ${parsed.stage}` : ""}: ${detail}`);
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
content: [
|
|
286
|
+
{
|
|
287
|
+
type: "text",
|
|
288
|
+
text: `Committed and pushed ${parsed.commitSha} to ${parsed.remote} ${parsed.upstreamRef}.`
|
|
289
|
+
+ (parsed.resumedPush ? " 이미 커밋돼 있던 미전송 커밋을 함께 다시 보냈다." : "")
|
|
290
|
+
+ (memory.file ? ` 기억 transport(${memory.file})를 함께 커밋했다.` : "")
|
|
291
|
+
+ (memory.note ? ` ${memory.note}` : "")
|
|
292
|
+
+ (handoffNote ? ` ${handoffNote}` : ""),
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
details: parsed,
|
|
296
|
+
};
|
|
297
|
+
} finally {
|
|
298
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
export default factory;
|