@dxos/plugin-github 0.7.4 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +102 -5
- package/PLUGIN.mdl +784 -0
- package/dist/lib/GitHubCapabilities.mjs +10 -0
- package/dist/lib/GitHubCapabilities.mjs.map +1 -0
- package/dist/lib/GitHubEvents.mjs +15 -0
- package/dist/lib/GitHubEvents.mjs.map +1 -0
- package/dist/lib/GitHubOperation.mjs +384 -0
- package/dist/lib/GitHubOperation.mjs.map +1 -0
- package/dist/lib/GitHubPlugin.mjs +16 -0
- package/dist/lib/GitHubPlugin.mjs.map +1 -0
- package/dist/lib/Walkthrough.mjs +59 -0
- package/dist/lib/Walkthrough.mjs.map +1 -0
- package/dist/lib/capabilities.browser.mjs +54 -0
- package/dist/lib/capabilities.browser.mjs.map +1 -0
- package/dist/lib/capabilities.mjs +54 -0
- package/dist/lib/capabilities.mjs.map +1 -0
- package/dist/lib/capabilities.node.mjs +30 -0
- package/dist/lib/capabilities.node.mjs.map +1 -0
- package/dist/lib/capabilities.tauri.mjs +54 -0
- package/dist/lib/capabilities.tauri.mjs.map +1 -0
- package/dist/lib/capabilities.workerd.mjs +30 -0
- package/dist/lib/capabilities.workerd.mjs.map +1 -0
- package/dist/lib/chunk-ImportPullRequestDialog.mjs +116 -0
- package/dist/lib/chunk-ImportPullRequestDialog.mjs.map +1 -0
- package/dist/lib/chunk-PLUGIN.mjs +6 -0
- package/dist/lib/chunk-PLUGIN.mjs.map +1 -0
- package/dist/lib/chunk-PullRequestArticle.mjs +1506 -0
- package/dist/lib/chunk-PullRequestArticle.mjs.map +1 -0
- package/dist/lib/chunk-PullRequestArticle2.mjs +2 -0
- package/dist/lib/chunk-PullRequestCardMenu.mjs +123 -0
- package/dist/lib/chunk-PullRequestCardMenu.mjs.map +1 -0
- package/dist/lib/chunk-PullRequestsArticle.mjs +594 -0
- package/dist/lib/chunk-PullRequestsArticle.mjs.map +1 -0
- package/dist/lib/chunk-WalkthroughArticle.mjs +26 -0
- package/dist/lib/chunk-WalkthroughArticle.mjs.map +1 -0
- package/dist/lib/chunk-add-pull-request-comment.mjs +19 -0
- package/dist/lib/chunk-add-pull-request-comment.mjs.map +1 -0
- package/dist/lib/chunk-add-pull-request-review-comment.mjs +25 -0
- package/dist/lib/chunk-add-pull-request-review-comment.mjs.map +1 -0
- package/dist/lib/chunk-app-graph-builder.mjs +39 -0
- package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
- package/dist/lib/chunk-connector.mjs +70 -0
- package/dist/lib/chunk-connector.mjs.map +1 -0
- package/dist/lib/chunk-constants.mjs +11 -0
- package/dist/lib/chunk-constants.mjs.map +1 -0
- package/dist/lib/chunk-errors.mjs +29 -0
- package/dist/lib/chunk-errors.mjs.map +1 -0
- package/dist/lib/chunk-generate-walkthrough.mjs +86 -0
- package/dist/lib/chunk-generate-walkthrough.mjs.map +1 -0
- package/dist/lib/chunk-generate.mjs +719 -0
- package/dist/lib/chunk-generate.mjs.map +1 -0
- package/dist/lib/chunk-get-pull-request-diff.mjs +25 -0
- package/dist/lib/chunk-get-pull-request-diff.mjs.map +1 -0
- package/dist/lib/chunk-get-pull-request-status.mjs +29 -0
- package/dist/lib/chunk-get-pull-request-status.mjs.map +1 -0
- package/dist/lib/chunk-get-repositories.mjs +32 -0
- package/dist/lib/chunk-get-repositories.mjs.map +1 -0
- package/dist/lib/chunk-github-api.mjs +375 -0
- package/dist/lib/chunk-github-api.mjs.map +1 -0
- package/dist/lib/chunk-github-link.mjs +59 -0
- package/dist/lib/chunk-github-link.mjs.map +1 -0
- package/dist/lib/chunk-import-pull-request-from-snapshot.mjs +25 -0
- package/dist/lib/chunk-import-pull-request-from-snapshot.mjs.map +1 -0
- package/dist/lib/chunk-import-pull-request.mjs +171 -0
- package/dist/lib/chunk-import-pull-request.mjs.map +1 -0
- package/dist/lib/chunk-link-resolver.mjs +109 -0
- package/dist/lib/chunk-link-resolver.mjs.map +1 -0
- package/dist/lib/chunk-markdown-extension.mjs +92 -0
- package/dist/lib/chunk-markdown-extension.mjs.map +1 -0
- package/dist/lib/chunk-materialize-target.mjs +36 -0
- package/dist/lib/chunk-materialize-target.mjs.map +1 -0
- package/dist/lib/chunk-operation-handler.mjs +12 -0
- package/dist/lib/chunk-operation-handler.mjs.map +1 -0
- package/dist/lib/chunk-page-action.mjs +22 -0
- package/dist/lib/chunk-page-action.mjs.map +1 -0
- package/dist/lib/chunk-pull-request.mjs +58 -0
- package/dist/lib/chunk-pull-request.mjs.map +1 -0
- package/dist/lib/chunk-pull-request2.mjs +110 -0
- package/dist/lib/chunk-pull-request2.mjs.map +1 -0
- package/dist/lib/chunk-react-surface.mjs +233 -0
- package/dist/lib/chunk-react-surface.mjs.map +1 -0
- package/dist/lib/chunk-refresh-pull-requests.mjs +49 -0
- package/dist/lib/chunk-refresh-pull-requests.mjs.map +1 -0
- package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
- package/dist/lib/chunk-schema.mjs +15 -0
- package/dist/lib/chunk-schema.mjs.map +1 -0
- package/dist/lib/chunk-skill-definition.mjs +10 -0
- package/dist/lib/chunk-skill-definition.mjs.map +1 -0
- package/dist/lib/chunk-submit-pull-request-approval.mjs +56 -0
- package/dist/lib/chunk-submit-pull-request-approval.mjs.map +1 -0
- package/dist/lib/chunk-sync-pull-request.mjs +26 -0
- package/dist/lib/chunk-sync-pull-request.mjs.map +1 -0
- package/dist/lib/chunk-sync.mjs +481 -0
- package/dist/lib/chunk-sync.mjs.map +1 -0
- package/dist/lib/chunk-useOpenObject.mjs +43 -0
- package/dist/lib/chunk-useOpenObject.mjs.map +1 -0
- package/dist/lib/extensions.mjs +132 -0
- package/dist/lib/extensions.mjs.map +1 -0
- package/dist/lib/index.mjs +4 -0
- package/dist/lib/meta.mjs +47 -0
- package/dist/lib/meta.mjs.map +1 -0
- package/dist/lib/ns/Extensions.mjs +18 -0
- package/dist/lib/ns/Extensions.mjs.map +1 -0
- package/dist/lib/operations.mjs +23 -0
- package/dist/lib/operations.mjs.map +1 -0
- package/dist/lib/plugin.mjs +9 -0
- package/dist/lib/plugin.mjs.map +1 -0
- package/dist/lib/skills.mjs +44 -0
- package/dist/lib/skills.mjs.map +1 -0
- package/dist/lib/testing.mjs +189 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/lib/translations.mjs +136 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +5 -0
- package/dist/types/dx.config.d.ts +28 -0
- package/dist/types/dx.config.d.ts.map +1 -0
- package/dist/types/src/Extensions.d.ts +2 -0
- package/dist/types/src/Extensions.d.ts.map +1 -0
- package/dist/types/src/GitHubPlugin.d.ts +37 -0
- package/dist/types/src/GitHubPlugin.d.ts.map +1 -0
- package/dist/types/src/GitHubPlugin.workerd.test.d.ts +2 -0
- package/dist/types/src/GitHubPlugin.workerd.test.d.ts.map +1 -0
- package/dist/types/src/approval.d.ts +21 -0
- package/dist/types/src/approval.d.ts.map +1 -0
- package/dist/types/src/approval.test.d.ts +2 -0
- package/dist/types/src/approval.test.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +9 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/capabilities/connector.d.ts +17 -0
- package/dist/types/src/capabilities/connector.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/browser.d.ts +13 -0
- package/dist/types/src/capabilities/gen/browser.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/node.d.ts +13 -0
- package/dist/types/src/capabilities/gen/node.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/tauri.d.ts +13 -0
- package/dist/types/src/capabilities/gen/tauri.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/workerd.d.ts +13 -0
- package/dist/types/src/capabilities/gen/workerd.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +13 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/link-resolver.d.ts +10 -0
- package/dist/types/src/capabilities/link-resolver.d.ts.map +1 -0
- package/dist/types/src/capabilities/markdown-extension.d.ts +14 -0
- package/dist/types/src/capabilities/markdown-extension.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-handler.d.ts +5 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/page-action.d.ts +5 -0
- package/dist/types/src/capabilities/page-action.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/schema.d.ts +12 -0
- package/dist/types/src/capabilities/schema.d.ts.map +1 -0
- package/dist/types/src/capabilities/skill-definition.d.ts +5 -0
- package/dist/types/src/capabilities/skill-definition.d.ts.map +1 -0
- package/dist/types/src/cards/GitHubCard.d.ts +11 -0
- package/dist/types/src/cards/GitHubCard.d.ts.map +1 -0
- package/dist/types/src/cards/GitHubCard.stories.d.ts +45 -0
- package/dist/types/src/cards/GitHubCard.stories.d.ts.map +1 -0
- package/dist/types/src/cards/PullRequestCard.d.ts +9 -0
- package/dist/types/src/cards/PullRequestCard.d.ts.map +1 -0
- package/dist/types/src/cards/index.d.ts +3 -0
- package/dist/types/src/cards/index.d.ts.map +1 -0
- package/dist/types/src/components/CommentComposer/CommentComposer.d.ts +30 -0
- package/dist/types/src/components/CommentComposer/CommentComposer.d.ts.map +1 -0
- package/dist/types/src/components/CommentComposer/index.d.ts +2 -0
- package/dist/types/src/components/CommentComposer/index.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestFiles/DiffStat.d.ts +8 -0
- package/dist/types/src/components/PullRequestFiles/DiffStat.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestFiles/FileTree.d.ts +17 -0
- package/dist/types/src/components/PullRequestFiles/FileTree.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestFiles/PullRequestFiles.d.ts +23 -0
- package/dist/types/src/components/PullRequestFiles/PullRequestFiles.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestFiles/files.d.ts +39 -0
- package/dist/types/src/components/PullRequestFiles/files.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestFiles/files.test.d.ts +2 -0
- package/dist/types/src/components/PullRequestFiles/files.test.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestFiles/index.d.ts +5 -0
- package/dist/types/src/components/PullRequestFiles/index.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestOverview/ArtifactPill.d.ts +21 -0
- package/dist/types/src/components/PullRequestOverview/ArtifactPill.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestOverview/CheckRunList.d.ts +18 -0
- package/dist/types/src/components/PullRequestOverview/CheckRunList.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestOverview/CheckRunList.test.d.ts +2 -0
- package/dist/types/src/components/PullRequestOverview/CheckRunList.test.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestOverview/PullRequestDetails.d.ts +10 -0
- package/dist/types/src/components/PullRequestOverview/PullRequestDetails.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestOverview/PullRequestOverview.d.ts +16 -0
- package/dist/types/src/components/PullRequestOverview/PullRequestOverview.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestOverview/RelatedCards.d.ts +28 -0
- package/dist/types/src/components/PullRequestOverview/RelatedCards.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestOverview/index.d.ts +6 -0
- package/dist/types/src/components/PullRequestOverview/index.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestStatus/PullRequestStatus.d.ts +20 -0
- package/dist/types/src/components/PullRequestStatus/PullRequestStatus.d.ts.map +1 -0
- package/dist/types/src/components/PullRequestStatus/index.d.ts +2 -0
- package/dist/types/src/components/PullRequestStatus/index.d.ts.map +1 -0
- package/dist/types/src/components/WalkthroughView/WalkthroughView.d.ts +18 -0
- package/dist/types/src/components/WalkthroughView/WalkthroughView.d.ts.map +1 -0
- package/dist/types/src/components/WalkthroughView/extensions.d.ts +16 -0
- package/dist/types/src/components/WalkthroughView/extensions.d.ts.map +1 -0
- package/dist/types/src/components/WalkthroughView/index.d.ts +3 -0
- package/dist/types/src/components/WalkthroughView/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +5 -8
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/constants.d.ts +7 -0
- package/dist/types/src/constants.d.ts.map +1 -0
- package/dist/types/src/containers/ImportPullRequestDialog/ImportPullRequestDialog.d.ts +6 -0
- package/dist/types/src/containers/ImportPullRequestDialog/ImportPullRequestDialog.d.ts.map +1 -0
- package/dist/types/src/containers/ImportPullRequestDialog/index.d.ts +3 -0
- package/dist/types/src/containers/ImportPullRequestDialog/index.d.ts.map +1 -0
- package/dist/types/src/containers/PullRequestArticle/PullRequestArticle.d.ts +15 -0
- package/dist/types/src/containers/PullRequestArticle/PullRequestArticle.d.ts.map +1 -0
- package/dist/types/src/containers/PullRequestArticle/failure.d.ts +10 -0
- package/dist/types/src/containers/PullRequestArticle/failure.d.ts.map +1 -0
- package/dist/types/src/containers/PullRequestArticle/failure.test.d.ts +2 -0
- package/dist/types/src/containers/PullRequestArticle/failure.test.d.ts.map +1 -0
- package/dist/types/src/containers/PullRequestArticle/index.d.ts +2 -0
- package/dist/types/src/containers/PullRequestArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/PullRequestCardMenu/PullRequestCardMenu.d.ts +14 -0
- package/dist/types/src/containers/PullRequestCardMenu/PullRequestCardMenu.d.ts.map +1 -0
- package/dist/types/src/containers/PullRequestCardMenu/index.d.ts +2 -0
- package/dist/types/src/containers/PullRequestCardMenu/index.d.ts.map +1 -0
- package/dist/types/src/containers/PullRequestsArticle/PullRequestsArticle.d.ts +13 -0
- package/dist/types/src/containers/PullRequestsArticle/PullRequestsArticle.d.ts.map +1 -0
- package/dist/types/src/containers/PullRequestsArticle/arrange.d.ts +44 -0
- package/dist/types/src/containers/PullRequestsArticle/arrange.d.ts.map +1 -0
- package/dist/types/src/containers/PullRequestsArticle/arrange.test.d.ts +2 -0
- package/dist/types/src/containers/PullRequestsArticle/arrange.test.d.ts.map +1 -0
- package/dist/types/src/containers/PullRequestsArticle/format.d.ts +3 -0
- package/dist/types/src/containers/PullRequestsArticle/format.d.ts.map +1 -0
- package/dist/types/src/containers/PullRequestsArticle/index.d.ts +2 -0
- package/dist/types/src/containers/PullRequestsArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/WalkthroughArticle/WalkthroughArticle.d.ts +13 -0
- package/dist/types/src/containers/WalkthroughArticle/WalkthroughArticle.d.ts.map +1 -0
- package/dist/types/src/containers/WalkthroughArticle/index.d.ts +2 -0
- package/dist/types/src/containers/WalkthroughArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +7 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/errors.d.ts +157 -0
- package/dist/types/src/errors.d.ts.map +1 -0
- package/dist/types/src/extensions/index.d.ts +3 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -0
- package/dist/types/src/extensions/links.d.ts +21 -0
- package/dist/types/src/extensions/links.d.ts.map +1 -0
- package/dist/types/src/extensions/links.test.d.ts +2 -0
- package/dist/types/src/extensions/links.test.d.ts.map +1 -0
- package/dist/types/src/extensions/references.d.ts +23 -0
- package/dist/types/src/extensions/references.d.ts.map +1 -0
- package/dist/types/src/extensions/references.test.d.ts +2 -0
- package/dist/types/src/extensions/references.test.d.ts.map +1 -0
- package/dist/types/src/github-link.d.ts +29 -0
- package/dist/types/src/github-link.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +5 -2
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useOpenObject.d.ts +9 -0
- package/dist/types/src/hooks/useOpenObject.d.ts.map +1 -0
- package/dist/types/src/hooks/usePullRequestChecks.d.ts +10 -0
- package/dist/types/src/hooks/usePullRequestChecks.d.ts.map +1 -0
- package/dist/types/src/hooks/usePullRequestDiff.d.ts +12 -0
- package/dist/types/src/hooks/usePullRequestDiff.d.ts.map +1 -0
- package/dist/types/src/hooks/usePullRequestFiles.d.ts +22 -0
- package/dist/types/src/hooks/usePullRequestFiles.d.ts.map +1 -0
- package/dist/types/src/hooks/useSyncPullRequest.d.ts +10 -0
- package/dist/types/src/hooks/useSyncPullRequest.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +34 -11
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/operations/add-pull-request-comment.d.ts +5 -0
- package/dist/types/src/operations/add-pull-request-comment.d.ts.map +1 -0
- package/dist/types/src/operations/add-pull-request-review-comment.d.ts +5 -0
- package/dist/types/src/operations/add-pull-request-review-comment.d.ts.map +1 -0
- package/dist/types/src/operations/generate-walkthrough.d.ts +11 -0
- package/dist/types/src/operations/generate-walkthrough.d.ts.map +1 -0
- package/dist/types/src/operations/get-pull-request-diff.d.ts +5 -0
- package/dist/types/src/operations/get-pull-request-diff.d.ts.map +1 -0
- package/dist/types/src/operations/get-pull-request-status.d.ts +5 -0
- package/dist/types/src/operations/get-pull-request-status.d.ts.map +1 -0
- package/dist/types/src/operations/get-repositories.d.ts +13 -0
- package/dist/types/src/operations/get-repositories.d.ts.map +1 -0
- package/dist/types/src/operations/import-pull-request-from-snapshot.d.ts +12 -0
- package/dist/types/src/operations/import-pull-request-from-snapshot.d.ts.map +1 -0
- package/dist/types/src/operations/import-pull-request.d.ts +63 -0
- package/dist/types/src/operations/import-pull-request.d.ts.map +1 -0
- package/dist/types/src/operations/import-pull-request.test.d.ts +2 -0
- package/dist/types/src/operations/import-pull-request.test.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/operations/materialize-target.d.ts +12 -0
- package/dist/types/src/operations/materialize-target.d.ts.map +1 -0
- package/dist/types/src/operations/pull-request.d.ts +48 -0
- package/dist/types/src/operations/pull-request.d.ts.map +1 -0
- package/dist/types/src/operations/pull-request.test.d.ts +2 -0
- package/dist/types/src/operations/pull-request.test.d.ts.map +1 -0
- package/dist/types/src/operations/refresh-pull-requests.d.ts +5 -0
- package/dist/types/src/operations/refresh-pull-requests.d.ts.map +1 -0
- package/dist/types/src/operations/refresh.d.ts +37 -0
- package/dist/types/src/operations/refresh.d.ts.map +1 -0
- package/dist/types/src/operations/refresh.test.d.ts +2 -0
- package/dist/types/src/operations/refresh.test.d.ts.map +1 -0
- package/dist/types/src/operations/submit-pull-request-approval.d.ts +5 -0
- package/dist/types/src/operations/submit-pull-request-approval.d.ts.map +1 -0
- package/dist/types/src/operations/sync-pull-request.d.ts +5 -0
- package/dist/types/src/operations/sync-pull-request.d.ts.map +1 -0
- package/dist/types/src/operations/sync.d.ts +123 -0
- package/dist/types/src/operations/sync.d.ts.map +1 -0
- package/dist/types/src/operations/sync.test.d.ts +2 -0
- package/dist/types/src/operations/sync.test.d.ts.map +1 -0
- package/dist/types/src/plugin.d.ts +4 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/plugin.test.d.ts +2 -0
- package/dist/types/src/plugin.test.d.ts.map +1 -0
- package/dist/types/src/pull-request-body.d.ts +37 -0
- package/dist/types/src/pull-request-body.d.ts.map +1 -0
- package/dist/types/src/pull-request-body.test.d.ts +2 -0
- package/dist/types/src/pull-request-body.test.d.ts.map +1 -0
- package/dist/types/src/pull-request.d.ts +28 -0
- package/dist/types/src/pull-request.d.ts.map +1 -0
- package/dist/types/src/pull-request.test.d.ts +2 -0
- package/dist/types/src/pull-request.test.d.ts.map +1 -0
- package/dist/types/src/services/github-api.d.ts +332 -0
- package/dist/types/src/services/github-api.d.ts.map +1 -0
- package/dist/types/src/services/index.d.ts +2 -0
- package/dist/types/src/services/index.d.ts.map +1 -0
- package/dist/types/src/skills/GitHubSkill.d.ts +12 -0
- package/dist/types/src/skills/GitHubSkill.d.ts.map +1 -0
- package/dist/types/src/skills/index.d.ts +2 -0
- package/dist/types/src/skills/index.d.ts.map +1 -0
- package/dist/types/src/stories/Generate.stories.d.ts +27 -0
- package/dist/types/src/stories/Generate.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Links.stories.d.ts +23 -0
- package/dist/types/src/stories/Links.stories.d.ts.map +1 -0
- package/dist/types/src/stories/PullRequestArticle.stories.d.ts +359 -0
- package/dist/types/src/stories/PullRequestArticle.stories.d.ts.map +1 -0
- package/dist/types/src/stories/PullRequestFiles.stories.d.ts +365 -0
- package/dist/types/src/stories/PullRequestFiles.stories.d.ts.map +1 -0
- package/dist/types/src/stories/PullRequestOverview.stories.d.ts +367 -0
- package/dist/types/src/stories/PullRequestOverview.stories.d.ts.map +1 -0
- package/dist/types/src/stories/PullRequestsArticle.stories.d.ts +357 -0
- package/dist/types/src/stories/PullRequestsArticle.stories.d.ts.map +1 -0
- package/dist/types/src/stories/Walkthrough.stories.d.ts +380 -0
- package/dist/types/src/stories/Walkthrough.stories.d.ts.map +1 -0
- package/dist/types/src/stories/github.d.ts +23 -0
- package/dist/types/src/stories/github.d.ts.map +1 -0
- package/dist/types/src/stories/github.test.d.ts +2 -0
- package/dist/types/src/stories/github.test.d.ts.map +1 -0
- package/dist/types/src/stories/walkthrough-fixture.d.ts +8 -0
- package/dist/types/src/stories/walkthrough-fixture.d.ts.map +1 -0
- package/dist/types/src/testing/fixtures.d.ts +14 -0
- package/dist/types/src/testing/fixtures.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +4 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/plugin.d.ts +4 -0
- package/dist/types/src/testing/plugin.d.ts.map +1 -0
- package/dist/types/src/testing/pull-request-13348.d.ts +6 -0
- package/dist/types/src/testing/pull-request-13348.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +339 -53
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/GitHubCapabilities.d.ts +13 -0
- package/dist/types/src/types/GitHubCapabilities.d.ts.map +1 -0
- package/dist/types/src/types/GitHubEvents.d.ts +8 -0
- package/dist/types/src/types/GitHubEvents.d.ts.map +1 -0
- package/dist/types/src/types/GitHubOperation.d.ts +281 -0
- package/dist/types/src/types/GitHubOperation.d.ts.map +1 -0
- package/dist/types/src/types/PullRequestsView.d.ts +35 -0
- package/dist/types/src/types/PullRequestsView.d.ts.map +1 -0
- package/dist/types/src/types/Walkthrough.d.ts +31 -0
- package/dist/types/src/types/Walkthrough.d.ts.map +1 -0
- package/dist/types/src/types/Walkthrough.test.d.ts +2 -0
- package/dist/types/src/types/Walkthrough.test.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +5 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/src/walkthrough/fill.d.ts +28 -0
- package/dist/types/src/walkthrough/fill.d.ts.map +1 -0
- package/dist/types/src/walkthrough/fill.test.d.ts +2 -0
- package/dist/types/src/walkthrough/fill.test.d.ts.map +1 -0
- package/dist/types/src/walkthrough/generate.d.ts +67 -0
- package/dist/types/src/walkthrough/generate.d.ts.map +1 -0
- package/dist/types/src/walkthrough/generate.test.d.ts +2 -0
- package/dist/types/src/walkthrough/generate.test.d.ts.map +1 -0
- package/dist/types/src/walkthrough/generated.d.ts +20 -0
- package/dist/types/src/walkthrough/generated.d.ts.map +1 -0
- package/dist/types/src/walkthrough/generated.test.d.ts +2 -0
- package/dist/types/src/walkthrough/generated.test.d.ts.map +1 -0
- package/dist/types/src/walkthrough/headings.d.ts +8 -0
- package/dist/types/src/walkthrough/headings.d.ts.map +1 -0
- package/dist/types/src/walkthrough/headings.test.d.ts +2 -0
- package/dist/types/src/walkthrough/headings.test.d.ts.map +1 -0
- package/dist/types/src/walkthrough/index.d.ts +9 -0
- package/dist/types/src/walkthrough/index.d.ts.map +1 -0
- package/dist/types/src/walkthrough/patch.d.ts +48 -0
- package/dist/types/src/walkthrough/patch.d.ts.map +1 -0
- package/dist/types/src/walkthrough/plan.d.ts +55 -0
- package/dist/types/src/walkthrough/plan.d.ts.map +1 -0
- package/dist/types/src/walkthrough/plan.test.d.ts +2 -0
- package/dist/types/src/walkthrough/plan.test.d.ts.map +1 -0
- package/dist/types/src/walkthrough/prompt.d.ts +33 -0
- package/dist/types/src/walkthrough/prompt.d.ts.map +1 -0
- package/dist/types/src/walkthrough/prompt.test.d.ts +2 -0
- package/dist/types/src/walkthrough/prompt.test.d.ts.map +1 -0
- package/dist/types/src/walkthrough/score.d.ts +43 -0
- package/dist/types/src/walkthrough/score.d.ts.map +1 -0
- package/dist/types/src/walkthrough/score.test.d.ts +2 -0
- package/dist/types/src/walkthrough/score.test.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/dx.config.ts +39 -0
- package/package.json +167 -48
- package/src/Extensions.ts +7 -0
- package/src/GitHubPlugin.ts +15 -0
- package/src/GitHubPlugin.workerd.test.ts +23 -0
- package/src/approval.test.ts +40 -0
- package/src/approval.ts +39 -0
- package/src/capabilities/app-graph-builder.ts +47 -0
- package/src/capabilities/connector.ts +95 -0
- package/src/capabilities/gen/browser.ts +82 -0
- package/src/capabilities/gen/node.ts +43 -0
- package/src/capabilities/gen/tauri.ts +82 -0
- package/src/capabilities/gen/workerd.ts +43 -0
- package/src/capabilities/index.ts +71 -0
- package/src/capabilities/link-resolver.ts +137 -0
- package/src/capabilities/markdown-extension.ts +146 -0
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/page-action.ts +32 -0
- package/src/capabilities/react-surface.ts +98 -0
- package/src/capabilities/schema.ts +16 -0
- package/src/capabilities/skill-definition.ts +14 -0
- package/src/cards/GitHubCard.stories.tsx +165 -0
- package/src/cards/GitHubCard.tsx +93 -0
- package/src/cards/PullRequestCard.tsx +23 -0
- package/src/cards/index.ts +6 -0
- package/src/components/CommentComposer/CommentComposer.tsx +109 -0
- package/src/components/CommentComposer/index.ts +5 -0
- package/src/components/PullRequestFiles/DiffStat.tsx +15 -0
- package/src/components/PullRequestFiles/FileTree.tsx +102 -0
- package/src/components/PullRequestFiles/PullRequestFiles.tsx +87 -0
- package/src/components/PullRequestFiles/files.test.ts +59 -0
- package/src/components/PullRequestFiles/files.ts +115 -0
- package/src/components/PullRequestFiles/index.ts +8 -0
- package/src/components/PullRequestOverview/ArtifactPill.tsx +82 -0
- package/src/components/PullRequestOverview/CheckRunList.test.ts +29 -0
- package/src/components/PullRequestOverview/CheckRunList.tsx +141 -0
- package/src/components/PullRequestOverview/PullRequestDetails.tsx +15 -0
- package/src/components/PullRequestOverview/PullRequestOverview.tsx +110 -0
- package/src/components/PullRequestOverview/RelatedCards.tsx +147 -0
- package/src/components/PullRequestOverview/index.ts +9 -0
- package/src/components/PullRequestStatus/PullRequestStatus.tsx +84 -0
- package/src/components/PullRequestStatus/index.ts +5 -0
- package/src/components/WalkthroughView/WalkthroughView.tsx +62 -0
- package/src/components/WalkthroughView/extensions.ts +53 -0
- package/src/components/WalkthroughView/index.ts +6 -0
- package/src/components/index.ts +6 -9
- package/src/constants.ts +12 -0
- package/src/containers/ImportPullRequestDialog/ImportPullRequestDialog.tsx +160 -0
- package/src/containers/ImportPullRequestDialog/index.ts +7 -0
- package/src/containers/PullRequestArticle/PullRequestArticle.tsx +573 -0
- package/src/containers/PullRequestArticle/failure.test.ts +42 -0
- package/src/containers/PullRequestArticle/failure.ts +16 -0
- package/src/containers/PullRequestArticle/index.ts +5 -0
- package/src/containers/PullRequestCardMenu/PullRequestCardMenu.tsx +134 -0
- package/src/containers/PullRequestCardMenu/index.ts +5 -0
- package/src/containers/PullRequestsArticle/PullRequestsArticle.tsx +361 -0
- package/src/containers/PullRequestsArticle/arrange.test.ts +122 -0
- package/src/containers/PullRequestsArticle/arrange.ts +245 -0
- package/src/containers/PullRequestsArticle/format.ts +31 -0
- package/src/containers/PullRequestsArticle/index.ts +5 -0
- package/src/containers/WalkthroughArticle/WalkthroughArticle.tsx +31 -0
- package/src/containers/WalkthroughArticle/index.ts +5 -0
- package/src/containers/index.ts +11 -0
- package/src/errors.ts +61 -0
- package/src/extensions/index.ts +6 -0
- package/src/extensions/links.test.ts +133 -0
- package/src/extensions/links.ts +91 -0
- package/src/extensions/references.test.ts +83 -0
- package/src/extensions/references.ts +100 -0
- package/src/github-link.ts +70 -0
- package/src/hooks/index.ts +6 -3
- package/src/hooks/useOpenObject.ts +48 -0
- package/src/hooks/usePullRequestChecks.ts +81 -0
- package/src/hooks/usePullRequestDiff.ts +57 -0
- package/src/hooks/usePullRequestFiles.ts +98 -0
- package/src/hooks/useSyncPullRequest.ts +51 -0
- package/src/index.ts +4 -6
- package/src/meta.ts +8 -13
- package/src/operations/add-pull-request-comment.ts +31 -0
- package/src/operations/add-pull-request-review-comment.ts +32 -0
- package/src/operations/generate-walkthrough.ts +124 -0
- package/src/operations/get-pull-request-diff.ts +37 -0
- package/src/operations/get-pull-request-status.ts +44 -0
- package/src/operations/get-repositories.ts +53 -0
- package/src/operations/import-pull-request-from-snapshot.ts +48 -0
- package/src/operations/import-pull-request.test.ts +151 -0
- package/src/operations/import-pull-request.ts +149 -0
- package/src/operations/index.ts +30 -0
- package/src/operations/materialize-target.ts +60 -0
- package/src/operations/pull-request.test.ts +120 -0
- package/src/operations/pull-request.ts +141 -0
- package/src/operations/refresh-pull-requests.ts +69 -0
- package/src/operations/refresh.test.ts +112 -0
- package/src/operations/refresh.ts +111 -0
- package/src/operations/submit-pull-request-approval.ts +41 -0
- package/src/operations/sync-pull-request.ts +34 -0
- package/src/operations/sync.test.ts +453 -0
- package/src/operations/sync.ts +807 -0
- package/src/plugin.test.ts +40 -0
- package/src/plugin.ts +37 -0
- package/src/pull-request-body.test.ts +61 -0
- package/src/pull-request-body.ts +144 -0
- package/src/pull-request.test.ts +67 -0
- package/src/pull-request.ts +80 -0
- package/src/services/github-api.ts +735 -0
- package/src/services/index.ts +5 -0
- package/src/skills/GitHubSkill.ts +46 -0
- package/src/skills/index.ts +5 -0
- package/src/stories/Generate.stories.tsx +152 -0
- package/src/stories/Links.stories.tsx +153 -0
- package/src/stories/PullRequestArticle.stories.tsx +107 -0
- package/src/stories/PullRequestFiles.stories.tsx +69 -0
- package/src/stories/PullRequestOverview.stories.tsx +114 -0
- package/src/stories/PullRequestsArticle.stories.tsx +170 -0
- package/src/stories/Walkthrough.stories.tsx +110 -0
- package/src/stories/github.test.ts +65 -0
- package/src/stories/github.ts +70 -0
- package/src/stories/walkthrough-fixture.ts +115 -0
- package/src/testing/fixtures.ts +99 -0
- package/src/testing/index.ts +7 -0
- package/src/testing/plugin.ts +25 -0
- package/src/testing/pull-request-13348.ts +104 -0
- package/src/testing/pull-request-13363.patch +2981 -0
- package/src/translations.ts +135 -63
- package/src/types/GitHubCapabilities.ts +27 -0
- package/src/types/GitHubEvents.ts +16 -0
- package/src/types/GitHubOperation.ts +397 -0
- package/src/types/PullRequestsView.ts +45 -0
- package/src/types/Walkthrough.test.ts +75 -0
- package/src/types/Walkthrough.ts +66 -0
- package/src/types/index.ts +8 -0
- package/src/vite-env.d.ts +15 -0
- package/src/walkthrough/fill.test.ts +337 -0
- package/src/walkthrough/fill.ts +246 -0
- package/src/walkthrough/generate.test.ts +219 -0
- package/src/walkthrough/generate.ts +248 -0
- package/src/walkthrough/generated.test.ts +70 -0
- package/src/walkthrough/generated.ts +70 -0
- package/src/walkthrough/headings.test.ts +40 -0
- package/src/walkthrough/headings.ts +39 -0
- package/src/walkthrough/index.ts +12 -0
- package/src/walkthrough/patch.ts +154 -0
- package/src/walkthrough/plan.test.ts +178 -0
- package/src/walkthrough/plan.ts +251 -0
- package/src/walkthrough/prompt.test.ts +100 -0
- package/src/walkthrough/prompt.ts +147 -0
- package/src/walkthrough/score.test.ts +272 -0
- package/src/walkthrough/score.ts +430 -0
- package/README.md +0 -15
- package/dist/lib/browser/chunk-S4MPSBA5.mjs +0 -21
- package/dist/lib/browser/chunk-S4MPSBA5.mjs.map +0 -7
- package/dist/lib/browser/index.css +0 -7
- package/dist/lib/browser/index.css.map +0 -7
- package/dist/lib/browser/index.mjs +0 -1535
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/meta.mjs +0 -11
- package/dist/lib/browser/meta.mjs.map +0 -7
- package/dist/types/src/GithubPlugin.d.ts +0 -4
- package/dist/types/src/GithubPlugin.d.ts.map +0 -1
- package/dist/types/src/components/EmbeddedMain/EmbeddedMain.d.ts +0 -4
- package/dist/types/src/components/EmbeddedMain/EmbeddedMain.d.ts.map +0 -1
- package/dist/types/src/components/EmbeddedMain/GfmPreview.d.ts +0 -9
- package/dist/types/src/components/EmbeddedMain/GfmPreview.d.ts.map +0 -1
- package/dist/types/src/components/EmbeddedMain/index.d.ts +0 -2
- package/dist/types/src/components/EmbeddedMain/index.d.ts.map +0 -1
- package/dist/types/src/components/ExportDialog.d.ts +0 -8
- package/dist/types/src/components/ExportDialog.d.ts.map +0 -1
- package/dist/types/src/components/GithubApiProviders/GitHubSettings.d.ts +0 -3
- package/dist/types/src/components/GithubApiProviders/GitHubSettings.d.ts.map +0 -1
- package/dist/types/src/components/GithubApiProviders/OctokitProvider.d.ts +0 -15
- package/dist/types/src/components/GithubApiProviders/OctokitProvider.d.ts.map +0 -1
- package/dist/types/src/components/GithubApiProviders/index.d.ts +0 -3
- package/dist/types/src/components/GithubApiProviders/index.d.ts.map +0 -1
- package/dist/types/src/components/GithubEchoResolverProviders/DocumentTreeItem.d.ts +0 -6
- package/dist/types/src/components/GithubEchoResolverProviders/DocumentTreeItem.d.ts.map +0 -1
- package/dist/types/src/components/GithubEchoResolverProviders/ResolverContext.d.ts +0 -7
- package/dist/types/src/components/GithubEchoResolverProviders/ResolverContext.d.ts.map +0 -1
- package/dist/types/src/components/GithubEchoResolverProviders/ResolverDialog.d.ts +0 -6
- package/dist/types/src/components/GithubEchoResolverProviders/ResolverDialog.d.ts.map +0 -1
- package/dist/types/src/components/GithubEchoResolverProviders/ResolverProps.d.ts +0 -16
- package/dist/types/src/components/GithubEchoResolverProviders/ResolverProps.d.ts.map +0 -1
- package/dist/types/src/components/GithubEchoResolverProviders/ResolverTree.d.ts +0 -3
- package/dist/types/src/components/GithubEchoResolverProviders/ResolverTree.d.ts.map +0 -1
- package/dist/types/src/components/GithubEchoResolverProviders/SpacePickerTreeItem.d.ts +0 -11
- package/dist/types/src/components/GithubEchoResolverProviders/SpacePickerTreeItem.d.ts.map +0 -1
- package/dist/types/src/components/GithubEchoResolverProviders/index.d.ts +0 -3
- package/dist/types/src/components/GithubEchoResolverProviders/index.d.ts.map +0 -1
- package/dist/types/src/components/GithubEchoResolverProviders/spaceResolvers.d.ts +0 -6
- package/dist/types/src/components/GithubEchoResolverProviders/spaceResolvers.d.ts.map +0 -1
- package/dist/types/src/components/ImportDialog.d.ts +0 -7
- package/dist/types/src/components/ImportDialog.d.ts.map +0 -1
- package/dist/types/src/components/Issue.d.ts +0 -5
- package/dist/types/src/components/Issue.d.ts.map +0 -1
- package/dist/types/src/components/Loading/Loading.d.ts +0 -15
- package/dist/types/src/components/Loading/Loading.d.ts.map +0 -1
- package/dist/types/src/components/Loading/Loading.stories.d.ts +0 -40
- package/dist/types/src/components/Loading/Loading.stories.d.ts.map +0 -1
- package/dist/types/src/components/Loading/index.d.ts +0 -2
- package/dist/types/src/components/Loading/index.d.ts.map +0 -1
- package/dist/types/src/components/MarkdownActions.d.ts +0 -7
- package/dist/types/src/components/MarkdownActions.d.ts.map +0 -1
- package/dist/types/src/components/UrlDialog.d.ts +0 -6
- package/dist/types/src/components/UrlDialog.d.ts.map +0 -1
- package/dist/types/src/hooks/useDocGhId.d.ts +0 -6
- package/dist/types/src/hooks/useDocGhId.d.ts.map +0 -1
- package/dist/types/src/hooks/useGhIdFromUrl.d.ts +0 -2
- package/dist/types/src/hooks/useGhIdFromUrl.d.ts.map +0 -1
- package/dist/types/src/types.d.ts +0 -30
- package/dist/types/src/types.d.ts.map +0 -1
- package/src/GithubPlugin.tsx +0 -127
- package/src/components/EmbeddedMain/EmbeddedMain.tsx +0 -294
- package/src/components/EmbeddedMain/GfmPreview.tsx +0 -110
- package/src/components/EmbeddedMain/embedded.css +0 -3
- package/src/components/EmbeddedMain/index.ts +0 -5
- package/src/components/ExportDialog.tsx +0 -157
- package/src/components/GithubApiProviders/GitHubSettings.tsx +0 -72
- package/src/components/GithubApiProviders/OctokitProvider.tsx +0 -60
- package/src/components/GithubApiProviders/index.ts +0 -6
- package/src/components/GithubEchoResolverProviders/DocumentTreeItem.tsx +0 -26
- package/src/components/GithubEchoResolverProviders/ResolverContext.tsx +0 -137
- package/src/components/GithubEchoResolverProviders/ResolverDialog.tsx +0 -34
- package/src/components/GithubEchoResolverProviders/ResolverProps.ts +0 -23
- package/src/components/GithubEchoResolverProviders/ResolverTree.tsx +0 -86
- package/src/components/GithubEchoResolverProviders/SpacePickerTreeItem.tsx +0 -105
- package/src/components/GithubEchoResolverProviders/index.ts +0 -6
- package/src/components/GithubEchoResolverProviders/spaceResolvers.ts +0 -86
- package/src/components/ImportDialog.tsx +0 -72
- package/src/components/Issue.tsx +0 -62
- package/src/components/Loading/Loading.stories.tsx +0 -32
- package/src/components/Loading/Loading.tsx +0 -70
- package/src/components/Loading/index.ts +0 -5
- package/src/components/MarkdownActions.tsx +0 -136
- package/src/components/UrlDialog.tsx +0 -52
- package/src/hooks/useDocGhId.ts +0 -38
- package/src/hooks/useGhIdFromUrl.ts +0 -26
- package/src/types.ts +0 -51
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { parsePatch } from './patch.ts';
|
|
8
|
+
import { assembleWalkthrough, buildChapterPrompt, buildPlannerPrompt, chapterDiff, parsePlan } from './plan.ts';
|
|
9
|
+
|
|
10
|
+
const PATCH = [file('src/core.ts', 3), file('src/caller.ts', 2), file('pnpm-lock.yaml', 400)].join('\n');
|
|
11
|
+
const FACTS = { owner: 'dxos', repo: 'dxos', number: 13288, title: 'A large change' };
|
|
12
|
+
const PATHS = ['src/core.ts', 'src/caller.ts'];
|
|
13
|
+
|
|
14
|
+
describe('buildPlannerPrompt', () => {
|
|
15
|
+
test('lists files and sizes, never their contents', () => {
|
|
16
|
+
const prompt = buildPlannerPrompt(FACTS, PATCH);
|
|
17
|
+
|
|
18
|
+
expect(prompt).to.contain('src/core.ts (+3/-0, 1 hunks)');
|
|
19
|
+
expect(prompt).to.not.contain('+line 0;');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('leaves generated files out of the plan entirely', () => {
|
|
23
|
+
expect(buildPlannerPrompt(FACTS, PATCH)).to.not.contain('pnpm-lock.yaml');
|
|
24
|
+
expect(buildPlannerPrompt(FACTS, PATCH)).to.contain('Files changed (2)');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('parsePlan', () => {
|
|
29
|
+
const RESPONSE = JSON.stringify({
|
|
30
|
+
title: 'Evaluate queries in SQLite',
|
|
31
|
+
overview: 'A second executor, off by default.',
|
|
32
|
+
chapters: [
|
|
33
|
+
{ title: 'The compiler', summary: 'Why a CTE per step.', files: ['src/core.ts'] },
|
|
34
|
+
{ title: 'The call sites', files: ['src/caller.ts'] },
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('reads a plan, fenced or bare', () => {
|
|
39
|
+
const plan = parsePlan(RESPONSE, PATHS);
|
|
40
|
+
expect(plan?.title).to.eq('Evaluate queries in SQLite');
|
|
41
|
+
expect(plan?.chapters.map((chapter) => chapter.title)).to.deep.eq(['The compiler', 'The call sites']);
|
|
42
|
+
expect(parsePlan('Here you go:\n```json\n' + RESPONSE + '\n```\n', PATHS)?.chapters).to.have.length(2);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('drops a file the patch does not contain', () => {
|
|
46
|
+
const invented = JSON.stringify({
|
|
47
|
+
title: 'A change',
|
|
48
|
+
chapters: [{ title: 'One', files: ['src/core.ts', 'src/invented.ts'] }],
|
|
49
|
+
});
|
|
50
|
+
expect(parsePlan(invented, PATHS)?.chapters[0].files).to.deep.eq(['src/core.ts']);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('gives a file to the first chapter that claims it', () => {
|
|
54
|
+
const duplicated = JSON.stringify({
|
|
55
|
+
title: 'A change',
|
|
56
|
+
chapters: [
|
|
57
|
+
{ title: 'One', files: ['src/core.ts'] },
|
|
58
|
+
{ title: 'Two', files: ['src/core.ts', 'src/caller.ts'] },
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
const plan = parsePlan(duplicated, PATHS);
|
|
62
|
+
expect(plan?.chapters[0].files).to.deep.eq(['src/core.ts']);
|
|
63
|
+
expect(plan?.chapters[1].files).to.deep.eq(['src/caller.ts']);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('sweeps up files the planner forgot, so the reader still meets them', () => {
|
|
67
|
+
const partial = JSON.stringify({ title: 'A change', chapters: [{ title: 'One', files: ['src/core.ts'] }] });
|
|
68
|
+
const plan = parsePlan(partial, PATHS);
|
|
69
|
+
|
|
70
|
+
expect(plan?.chapters).to.have.length(2);
|
|
71
|
+
expect(plan?.chapters[1]).to.deep.contain({ title: 'The rest of the change' });
|
|
72
|
+
expect(plan?.chapters[1].files).to.deep.eq(['src/caller.ts']);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('drops a summary that is not a string, rather than carrying it into the next prompt', () => {
|
|
76
|
+
const malformed = JSON.stringify({
|
|
77
|
+
title: 'A change',
|
|
78
|
+
overview: { not: 'a string' },
|
|
79
|
+
chapters: [{ title: 'One', summary: { also: 'not a string' }, files: ['src/core.ts', 42] }],
|
|
80
|
+
});
|
|
81
|
+
const plan = parsePlan(malformed, PATHS);
|
|
82
|
+
|
|
83
|
+
expect(plan?.overview).to.eq('');
|
|
84
|
+
expect(plan?.chapters[0].summary).to.be.undefined;
|
|
85
|
+
expect(plan?.chapters[0].files).to.deep.eq(['src/core.ts']);
|
|
86
|
+
expect(buildChapterPrompt(plan!, plan!.chapters[0], 'diff')).to.not.contain('[object Object]');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('reads a fenced plan whose JSON contains a fence-like sequence', () => {
|
|
90
|
+
const response = [
|
|
91
|
+
'Here is the plan:',
|
|
92
|
+
'```json',
|
|
93
|
+
JSON.stringify({
|
|
94
|
+
title: 'A change',
|
|
95
|
+
overview: 'Chapters are written as ```text blocks elsewhere.',
|
|
96
|
+
chapters: [{ title: 'One', files: ['src/core.ts'] }],
|
|
97
|
+
}),
|
|
98
|
+
'```',
|
|
99
|
+
].join('\n');
|
|
100
|
+
|
|
101
|
+
expect(parsePlan(response, PATHS)?.chapters[0].title).to.eq('One');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('refuses an unreadable answer rather than inventing a plan', () => {
|
|
105
|
+
expect(parsePlan('I cannot help with that.', PATHS)).to.be.undefined;
|
|
106
|
+
expect(parsePlan('{"title": "A change"}', PATHS)).to.be.undefined;
|
|
107
|
+
expect(parsePlan(JSON.stringify({ title: 'A', chapters: [{ title: 'B', files: ['gone.ts'] }] }), PATHS)).to.be
|
|
108
|
+
.undefined;
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe('chapterDiff', () => {
|
|
113
|
+
test('carries only the chapter’s own files', () => {
|
|
114
|
+
const files = parsePatch(PATCH);
|
|
115
|
+
const diff = chapterDiff(files, { title: 'The compiler', files: ['src/core.ts'] });
|
|
116
|
+
|
|
117
|
+
expect(diff).to.contain('src/core.ts');
|
|
118
|
+
expect(diff).to.not.contain('src/caller.ts');
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('buildChapterPrompt', () => {
|
|
123
|
+
test('names the heading to write and the other chapters as context', () => {
|
|
124
|
+
const plan = parsePlan(
|
|
125
|
+
JSON.stringify({
|
|
126
|
+
title: 'A change',
|
|
127
|
+
overview: 'Why.',
|
|
128
|
+
chapters: [
|
|
129
|
+
{ title: 'One', summary: 'Explain the core.', files: ['src/core.ts'] },
|
|
130
|
+
{ title: 'Two', files: ['src/caller.ts'] },
|
|
131
|
+
],
|
|
132
|
+
}),
|
|
133
|
+
PATHS,
|
|
134
|
+
)!;
|
|
135
|
+
const prompt = buildChapterPrompt(plan, plan.chapters[0], 'diff goes here');
|
|
136
|
+
|
|
137
|
+
expect(prompt).to.contain('Your chapter: ## One');
|
|
138
|
+
expect(prompt).to.contain('What it must explain: Explain the core.');
|
|
139
|
+
expect(prompt).to.contain('Other chapters, for context only: Two');
|
|
140
|
+
expect(prompt).to.contain('diff goes here');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('assembleWalkthrough', () => {
|
|
145
|
+
test('puts one H1 over the chapters the writers produced', () => {
|
|
146
|
+
const body = assembleWalkthrough({ title: 'A change', overview: 'Why it exists.', chapters: [] }, [
|
|
147
|
+
'## One\n\nFirst.\n',
|
|
148
|
+
'## Two\n\nSecond.\n',
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
expect(body.match(/^# /gm)).to.have.length(1);
|
|
152
|
+
expect(body).to.contain('# A change');
|
|
153
|
+
expect(body).to.contain('Why it exists.');
|
|
154
|
+
expect(body.indexOf('## One')).to.be.lessThan(body.indexOf('## Two'));
|
|
155
|
+
expect(body).to.not.contain('\n\n\n');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('puts a blank line between one chapter and the next heading', () => {
|
|
159
|
+
const body = assembleWalkthrough({ title: 'A change', overview: 'Why it exists.', chapters: [] }, [
|
|
160
|
+
'## One\n\nFirst.',
|
|
161
|
+
'## Two\n\nSecond.',
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
expect(body).to.contain('First.\n\n## Two');
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// A declaration rather than an arrow: the fixture above calls it at module evaluation.
|
|
169
|
+
function file(path: string, lines: number): string {
|
|
170
|
+
return [
|
|
171
|
+
`diff --git a/${path} b/${path}`,
|
|
172
|
+
`--- a/${path}`,
|
|
173
|
+
`+++ b/${path}`,
|
|
174
|
+
`@@ -1,1 +1,${lines + 1} @@`,
|
|
175
|
+
' keep();',
|
|
176
|
+
...Array.from({ length: lines }, (_, index) => `+line ${index};`),
|
|
177
|
+
].join('\n');
|
|
178
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { isGeneratedFile } from './generated.ts';
|
|
6
|
+
import { spaceHeadings } from './headings.ts';
|
|
7
|
+
import { type PatchFile, parsePatch } from './patch.ts';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Two-stage generation for a pull request whose diff does not fit one prompt.
|
|
11
|
+
*
|
|
12
|
+
* One-shot generation over a trimmed diff fails in a specific way on a large change: the model
|
|
13
|
+
* writes the first two sections in full, then runs out of room and leaves the rest to the appended
|
|
14
|
+
* `## Also changed` section — so the bigger the pull request, the less of it the walkthrough
|
|
15
|
+
* actually covers. Here a planner sees only the file list and decides the chapters, then each
|
|
16
|
+
* chapter is narrated against its own files, and the chapters are concatenated.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** A chapter the planner assigned files to. */
|
|
20
|
+
export type Chapter = {
|
|
21
|
+
title: string;
|
|
22
|
+
/** Paths from the file list, exactly as given. */
|
|
23
|
+
files: string[];
|
|
24
|
+
/** Why this chapter exists, handed to the chapter writer as its brief. */
|
|
25
|
+
summary?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type Plan = {
|
|
29
|
+
/** The H1 for the whole document. */
|
|
30
|
+
title: string;
|
|
31
|
+
/** Two or three sentences opening the document, before the first chapter. */
|
|
32
|
+
overview: string;
|
|
33
|
+
chapters: Chapter[];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Diffs longer than this get the two-stage treatment.
|
|
38
|
+
*
|
|
39
|
+
* Twice the one-shot budget: below that, trimming drops a file or two and the single prompt still
|
|
40
|
+
* sees the shape of the change, and a second model call would buy nothing.
|
|
41
|
+
*/
|
|
42
|
+
export const DEFAULT_CHAPTER_THRESHOLD_CHARS = 32_000;
|
|
43
|
+
|
|
44
|
+
export const PLANNER_SYSTEM_PROMPT = [
|
|
45
|
+
'You plan the chapters of a walkthrough: the document that takes a reviewer through a pull',
|
|
46
|
+
'request in the order it is best understood.',
|
|
47
|
+
'',
|
|
48
|
+
'You see the files and their sizes, never their contents. Decide the ORDER a reviewer should meet',
|
|
49
|
+
'this change in, and which files belong together.',
|
|
50
|
+
'',
|
|
51
|
+
'Rules:',
|
|
52
|
+
'- The core of the change comes first. Mechanical consequences (call sites, tests, docs) come',
|
|
53
|
+
' after the thing that forced them.',
|
|
54
|
+
'- A chapter is a reason, not a directory. Files that change for the same reason belong together',
|
|
55
|
+
' however far apart they sit in the tree.',
|
|
56
|
+
'- Every file in the list belongs to exactly one chapter. Do not invent files.',
|
|
57
|
+
'- Between three and seven chapters. Fewer on a change with one idea, more on a change that',
|
|
58
|
+
' genuinely does several things.',
|
|
59
|
+
'',
|
|
60
|
+
'Answer with JSON and nothing else:',
|
|
61
|
+
'{"title": "…", "overview": "…", "chapters": [{"title": "…", "summary": "…", "files": ["…"]}]}',
|
|
62
|
+
'',
|
|
63
|
+
'`title` is the H1 for the document: what the change does, in one line. `overview` is two or',
|
|
64
|
+
'three sentences on what it does and why, for the reader who has not seen the diff. Each',
|
|
65
|
+
"chapter's `summary` is one sentence telling that chapter's writer what to explain.",
|
|
66
|
+
].join('\n');
|
|
67
|
+
|
|
68
|
+
/** The file list the planner sees: path, size of change, and nothing else. */
|
|
69
|
+
export const buildPlannerPrompt = (
|
|
70
|
+
facts: { owner: string; repo: string; number: number; title: string; description?: string },
|
|
71
|
+
diff: string,
|
|
72
|
+
): string => {
|
|
73
|
+
const files = parsePatch(diff).filter((file) => !isGeneratedFile(file));
|
|
74
|
+
const lines = [`Repository: ${facts.owner}/${facts.repo}`, `Pull request: #${facts.number}`, `Title: ${facts.title}`];
|
|
75
|
+
if (facts.description?.trim()) {
|
|
76
|
+
lines.push('', 'Description as written by the author:', facts.description.trim());
|
|
77
|
+
}
|
|
78
|
+
lines.push('', `Files changed (${files.length}):`, '');
|
|
79
|
+
for (const file of files) {
|
|
80
|
+
lines.push(`${file.path} (+${file.added}/-${file.removed}, ${file.hunks.length} hunks)`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return lines.join('\n');
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Reads the planner's answer.
|
|
88
|
+
*
|
|
89
|
+
* Tolerant of a fenced block or surrounding prose, because a refusal to parse costs the whole run
|
|
90
|
+
* and the recovery — falling back to one-shot — is worse than reading a fence.
|
|
91
|
+
*/
|
|
92
|
+
export const parsePlan = (response: string, paths: readonly string[]): Plan | undefined => {
|
|
93
|
+
// A closer is its own line of at least as many backticks: a ```text` sequence inside a JSON
|
|
94
|
+
// string would otherwise end the block early and leave `JSON.parse` a truncated payload.
|
|
95
|
+
const fenced = response.match(/^[ \t]*(`{3,})[^\n]*\n([\s\S]*?)^[ \t]*\1`*[ \t]*$/m);
|
|
96
|
+
const text = (fenced?.[2] ?? response).trim();
|
|
97
|
+
const start = text.indexOf('{');
|
|
98
|
+
const end = text.lastIndexOf('}');
|
|
99
|
+
if (start < 0 || end <= start) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let parsed: unknown;
|
|
104
|
+
try {
|
|
105
|
+
parsed = JSON.parse(text.slice(start, end + 1));
|
|
106
|
+
} catch {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const decoded = decodePlan(parsed);
|
|
111
|
+
if (!decoded) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const known = new Set(paths);
|
|
116
|
+
const claimed = new Set<string>();
|
|
117
|
+
const chapters: Chapter[] = [];
|
|
118
|
+
for (const chapter of decoded.chapters) {
|
|
119
|
+
// A path the patch does not contain, or one a previous chapter already took, would make a
|
|
120
|
+
// chapter prompt carrying no diff — an invented file is the planner's likeliest mistake.
|
|
121
|
+
const files = chapter.files.filter((path) => known.has(path) && !claimed.has(path));
|
|
122
|
+
for (const path of files) {
|
|
123
|
+
claimed.add(path);
|
|
124
|
+
}
|
|
125
|
+
if (files.length > 0) {
|
|
126
|
+
chapters.push({ title: chapter.title, files, summary: chapter.summary });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (chapters.length === 0) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Files the planner forgot still have to reach the reader, and the last chapter is where the
|
|
134
|
+
// mechanical remainder belongs.
|
|
135
|
+
const unclaimed = paths.filter((path) => !claimed.has(path));
|
|
136
|
+
if (unclaimed.length > 0) {
|
|
137
|
+
chapters.push({ title: 'The rest of the change', files: [...unclaimed] });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return { title: decoded.title, overview: decoded.overview, chapters };
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/** Fields of the shape the planner was asked for; anything else is dropped rather than carried. */
|
|
144
|
+
const isString = (value: unknown): value is string => typeof value === 'string';
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Reads the model's JSON into the typed shape, field by field.
|
|
148
|
+
*
|
|
149
|
+
* Every value is checked, `summary` included: a non-string there would reach `buildChapterPrompt`
|
|
150
|
+
* and be interpolated into the next prompt as `[object Object]`.
|
|
151
|
+
*/
|
|
152
|
+
const decodePlan = (value: unknown): (Omit<Plan, 'chapters'> & { chapters: Chapter[] }) | undefined => {
|
|
153
|
+
if (typeof value !== 'object' || value === null) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
const record = value as Record<string, unknown>;
|
|
157
|
+
if (!isString(record.title) || !Array.isArray(record.chapters)) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const chapters = record.chapters.flatMap((entry): Chapter[] => {
|
|
162
|
+
if (typeof entry !== 'object' || entry === null) {
|
|
163
|
+
return [];
|
|
164
|
+
}
|
|
165
|
+
const chapter = entry as Record<string, unknown>;
|
|
166
|
+
if (!isString(chapter.title) || !Array.isArray(chapter.files)) {
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
return [
|
|
170
|
+
{
|
|
171
|
+
title: chapter.title,
|
|
172
|
+
files: chapter.files.filter(isString),
|
|
173
|
+
...(isString(chapter.summary) ? { summary: chapter.summary } : {}),
|
|
174
|
+
},
|
|
175
|
+
];
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return { title: record.title, overview: isString(record.overview) ? record.overview : '', chapters };
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/** The diff of one chapter's files, in patch order. */
|
|
182
|
+
export const chapterDiff = (files: readonly PatchFile[], chapter: Chapter): string => {
|
|
183
|
+
const wanted = new Set(chapter.files);
|
|
184
|
+
return files
|
|
185
|
+
.filter((file) => wanted.has(file.path))
|
|
186
|
+
.map((file) => [...file.preamble, ...file.hunks.flatMap((hunk) => [hunk.header, ...hunk.lines])].join('\n'))
|
|
187
|
+
.join('\n');
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export const CHAPTER_SYSTEM_PROMPT = [
|
|
191
|
+
'You write ONE chapter of a walkthrough of a pull request: the section a reviewer reads to',
|
|
192
|
+
'understand this part of the change.',
|
|
193
|
+
'',
|
|
194
|
+
'You are given the chapter you are writing, the document it belongs to, and the diff of this',
|
|
195
|
+
"chapter's files only. Other chapters cover the rest; do not describe them.",
|
|
196
|
+
'',
|
|
197
|
+
'Output:',
|
|
198
|
+
'- Open with the H2 heading you are given, exactly as given. No H1, no other H2.',
|
|
199
|
+
'- Under it, a short paragraph saying what this part does and what constraint it satisfies, then',
|
|
200
|
+
' the code it refers to.',
|
|
201
|
+
'- Reference a change with an EMPTY fenced block, exactly like this, and nothing inside it:',
|
|
202
|
+
'',
|
|
203
|
+
' ```diff file=path/to/file.ts lines=66-99',
|
|
204
|
+
' ```',
|
|
205
|
+
'',
|
|
206
|
+
'- `file` is the path exactly as the diff gives it. `lines` is the range in the file AFTER the',
|
|
207
|
+
' change, from the `@@` header. NEVER put diff content inside the fence: it is filled in for you',
|
|
208
|
+
' from the real patch, and anything you write there is discarded.',
|
|
209
|
+
'- A mechanical change repeated across files is one point: fence the instance that shows the',
|
|
210
|
+
' shape and say how many others take it.',
|
|
211
|
+
'- EVERY file listed in your chapter appears in at least one fence. A file whose hunks do not',
|
|
212
|
+
' each earn a paragraph still gets one fence, grouped with the others that change for its reason.',
|
|
213
|
+
'',
|
|
214
|
+
'Write for a reviewer reading once. The sentence rule is the one that matters most:',
|
|
215
|
+
'- ONE IDEA PER SENTENCE, under thirty words. No semicolons joining two clauses, no dashes',
|
|
216
|
+
' carrying a second thought, no sentence that needs a comma-separated list of parentheticals.',
|
|
217
|
+
' Where you would write a long sentence, write two short ones.',
|
|
218
|
+
'- Name the mechanism, not the feeling. Prose explains WHY the change is the way it is: the',
|
|
219
|
+
' constraint, the failure it avoids, the alternative rejected. Do not narrate what the diff shows.',
|
|
220
|
+
'- Active voice with the actor named. Plain words. Sentence case headings. No emoji.',
|
|
221
|
+
'- Output the markdown for this chapter and nothing else. No preamble, no closing summary.',
|
|
222
|
+
].join('\n');
|
|
223
|
+
|
|
224
|
+
export const buildChapterPrompt = (plan: Plan, chapter: Chapter, diff: string): string =>
|
|
225
|
+
[
|
|
226
|
+
`Document: ${plan.title}`,
|
|
227
|
+
plan.overview ? `Overview: ${plan.overview}` : '',
|
|
228
|
+
'',
|
|
229
|
+
`Your chapter: ## ${chapter.title}`,
|
|
230
|
+
chapter.summary ? `What it must explain: ${chapter.summary}` : '',
|
|
231
|
+
'',
|
|
232
|
+
`Other chapters, for context only: ${plan.chapters
|
|
233
|
+
.filter((other) => other.title !== chapter.title)
|
|
234
|
+
.map((other) => other.title)
|
|
235
|
+
.join('; ')}`,
|
|
236
|
+
'',
|
|
237
|
+
'Diff:',
|
|
238
|
+
'',
|
|
239
|
+
diff,
|
|
240
|
+
]
|
|
241
|
+
.filter((line) => line !== '')
|
|
242
|
+
.join('\n');
|
|
243
|
+
|
|
244
|
+
/** Stitches the planner's opening and the chapter bodies into one document. */
|
|
245
|
+
export const assembleWalkthrough = (plan: Plan, chapters: string[]): string =>
|
|
246
|
+
spaceHeadings(
|
|
247
|
+
[`# ${plan.title}`, '', plan.overview, '', ...chapters.map((chapter) => chapter.trim())]
|
|
248
|
+
.filter((part, index) => part !== '' || index < 3)
|
|
249
|
+
.join('\n')
|
|
250
|
+
.replace(/\n{3,}/g, '\n\n'),
|
|
251
|
+
).trimEnd() + '\n';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { SYSTEM_PROMPT, buildPrompt, promptOmissions } from './prompt.ts';
|
|
8
|
+
|
|
9
|
+
const INPUT = {
|
|
10
|
+
owner: 'dxos',
|
|
11
|
+
repo: 'dxos',
|
|
12
|
+
number: 13082,
|
|
13
|
+
title: 'A change',
|
|
14
|
+
description: 'Why it exists.',
|
|
15
|
+
baseBranch: 'main',
|
|
16
|
+
headBranch: 'feature',
|
|
17
|
+
diff: [file('src/small.ts', 2), file('src/large.ts', 400)].join('\n'),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
describe('buildPrompt', () => {
|
|
21
|
+
test('states the change and appends the diff', () => {
|
|
22
|
+
const prompt = buildPrompt(INPUT);
|
|
23
|
+
|
|
24
|
+
expect(prompt).to.contain('Repository: dxos/dxos');
|
|
25
|
+
expect(prompt).to.contain('Pull request: #13082');
|
|
26
|
+
expect(prompt).to.contain('Title: A change');
|
|
27
|
+
expect(prompt).to.contain('Branch: feature into main');
|
|
28
|
+
expect(prompt).to.contain('Why it exists.');
|
|
29
|
+
expect(prompt).to.contain('diff --git a/src/small.ts');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('omits the branch line when the pull request does not say', () => {
|
|
33
|
+
const { baseBranch, headBranch, ...rest } = INPUT;
|
|
34
|
+
expect(buildPrompt(rest)).to.not.contain('Branch:');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('drops whole files to fit the budget, largest first', () => {
|
|
38
|
+
const prompt = buildPrompt(INPUT, { maxDiffChars: 200 });
|
|
39
|
+
|
|
40
|
+
expect(prompt).to.contain('src/small.ts');
|
|
41
|
+
expect(prompt).to.not.contain('+line 399;');
|
|
42
|
+
expect(prompt).to.contain('Omitted from the diff below for length');
|
|
43
|
+
expect(prompt).to.contain('src/large.ts');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('drops a generated file even when everything fits', () => {
|
|
47
|
+
const withLock = {
|
|
48
|
+
...INPUT,
|
|
49
|
+
diff: [INPUT.diff, file('pnpm-lock.yaml', 3), file('assets/logo.png', 1)].join('\n'),
|
|
50
|
+
};
|
|
51
|
+
const prompt = buildPrompt(withLock);
|
|
52
|
+
|
|
53
|
+
// A lockfile is the largest hunk in most pull requests and the one nobody reads, so it never
|
|
54
|
+
// competes with code for the budget.
|
|
55
|
+
expect(prompt).to.not.contain('diff --git a/pnpm-lock.yaml');
|
|
56
|
+
expect(prompt).to.contain('src/small.ts');
|
|
57
|
+
expect(prompt).to.contain('not yours to describe: pnpm-lock.yaml, assets/logo.png');
|
|
58
|
+
expect(promptOmissions(withLock)).to.deep.eq(['pnpm-lock.yaml', 'assets/logo.png']);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('reports what the budget dropped', () => {
|
|
62
|
+
expect(promptOmissions(INPUT, { maxDiffChars: 200 })).to.deep.eq(['src/large.ts']);
|
|
63
|
+
expect(promptOmissions(INPUT)).to.deep.eq([]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('leaves a diff within budget untouched', () => {
|
|
67
|
+
const prompt = buildPrompt(INPUT);
|
|
68
|
+
expect(prompt).to.contain('+line 399;');
|
|
69
|
+
expect(prompt).to.not.contain('Omitted from the diff');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('SYSTEM_PROMPT', () => {
|
|
74
|
+
test('tells the model the fence is empty and filled for it', () => {
|
|
75
|
+
// The whole design rests on this instruction; a model that transcribes the diff produces an
|
|
76
|
+
// artefact that misquotes the change.
|
|
77
|
+
expect(SYSTEM_PROMPT).to.contain('```diff file=path/to/file.ts lines=66-99');
|
|
78
|
+
expect(SYSTEM_PROMPT).to.contain('NEVER put diff content');
|
|
79
|
+
expect(SYSTEM_PROMPT).to.contain('file AFTER the change');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('asks for prose a reviewer can read once', () => {
|
|
83
|
+
// Review time is what the walkthrough exists to save; see `.agents/skills/readable-prose`.
|
|
84
|
+
expect(SYSTEM_PROMPT).to.contain('One idea per sentence');
|
|
85
|
+
expect(SYSTEM_PROMPT).to.contain('Name the mechanism, not the feeling');
|
|
86
|
+
expect(SYSTEM_PROMPT).to.contain('Sentence case headings');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// A declaration rather than an arrow: the fixture above calls it at module evaluation.
|
|
91
|
+
function file(path: string, lines: number): string {
|
|
92
|
+
return [
|
|
93
|
+
`diff --git a/${path} b/${path}`,
|
|
94
|
+
`--- a/${path}`,
|
|
95
|
+
`+++ b/${path}`,
|
|
96
|
+
`@@ -1,1 +1,${lines + 1} @@`,
|
|
97
|
+
' keep();',
|
|
98
|
+
...Array.from({ length: lines }, (_, index) => `+line ${index};`),
|
|
99
|
+
].join('\n');
|
|
100
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { isGeneratedFile } from './generated.ts';
|
|
6
|
+
import { parsePatch } from './patch.ts';
|
|
7
|
+
|
|
8
|
+
/** What the model is told about the change. */
|
|
9
|
+
export type WalkthroughInput = {
|
|
10
|
+
owner: string;
|
|
11
|
+
repo: string;
|
|
12
|
+
number: number;
|
|
13
|
+
title: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
baseBranch?: string;
|
|
16
|
+
headBranch?: string;
|
|
17
|
+
/** The whole unified diff, as git wrote it. */
|
|
18
|
+
diff: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type PromptOptions = {
|
|
22
|
+
/**
|
|
23
|
+
* Character budget for the diff. Files are dropped smallest-budget-first when it is exceeded;
|
|
24
|
+
* anything dropped still reaches the reader, because the postprocess appends every hunk the prose
|
|
25
|
+
* does not claim.
|
|
26
|
+
*/
|
|
27
|
+
maxDiffChars?: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Measured against Sonnet, whose 16k output budget covers reasoning AND the answer. A whole
|
|
32
|
+
* 75k-character diff is ~33k input tokens, and the model then spends the entire output budget
|
|
33
|
+
* reasoning: the response comes back `finishReason: 'length'` carrying a reasoning block and NO
|
|
34
|
+
* text at all. At ~19k characters the same request answers in full with room to spare.
|
|
35
|
+
*
|
|
36
|
+
* Trimming costs nothing a reader sees, because every hunk the prose does not claim is appended
|
|
37
|
+
* verbatim by {@link fillWalkthrough}.
|
|
38
|
+
*/
|
|
39
|
+
const DEFAULT_MAX_DIFF_CHARS = 16_000;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* What the model is for: the ORDER and the REASONS, never the diff content.
|
|
43
|
+
*
|
|
44
|
+
* Every chunk is emitted as an empty fence carrying a path and a line range, which a postprocess
|
|
45
|
+
* fills from the patch itself. A model asked to transcribe a diff gets lines subtly wrong, and a
|
|
46
|
+
* review artefact that misquotes the change is worse than no artefact.
|
|
47
|
+
*/
|
|
48
|
+
export const SYSTEM_PROMPT = [
|
|
49
|
+
'You write walkthroughs of pull requests: one markdown document that takes a reviewer through a',
|
|
50
|
+
'change in the order it is best understood, rather than in the order the files happen to sort.',
|
|
51
|
+
'',
|
|
52
|
+
'Structure:',
|
|
53
|
+
'- Open with an H1 naming the change, then two or three sentences on what it does and why.',
|
|
54
|
+
'- Group the change into sections with H2 headings, ordered so each one builds on the last.',
|
|
55
|
+
' Put the core of the change first and the mechanical consequences after it.',
|
|
56
|
+
'- Under a heading, write a short paragraph saying what this part does and what constraint it',
|
|
57
|
+
' satisfies, then show the code it refers to.',
|
|
58
|
+
'',
|
|
59
|
+
'Showing code — this is the part to get right:',
|
|
60
|
+
'- Reference a change with an EMPTY fenced block, exactly like this, and nothing inside it:',
|
|
61
|
+
'',
|
|
62
|
+
' ```diff file=path/to/file.ts lines=66-99',
|
|
63
|
+
' ```',
|
|
64
|
+
'',
|
|
65
|
+
'- `file` is the path exactly as the diff gives it, after the change. `lines` is the range in the',
|
|
66
|
+
' file AFTER the change, taken from the `@@` header. Omit `lines` to show the whole file.',
|
|
67
|
+
'- NEVER put diff content, code, `@@` headers or `+`/`-` lines inside the fence. It is filled in',
|
|
68
|
+
' for you from the real patch. Anything you write there is discarded.',
|
|
69
|
+
'- One fence per point being made. Several fences under one heading is normal.',
|
|
70
|
+
'',
|
|
71
|
+
'Style:',
|
|
72
|
+
'- Prose explains WHY the change is the way it is: the constraint, the failure it avoids, the',
|
|
73
|
+
' alternative that was rejected. Do not narrate what the diff already shows.',
|
|
74
|
+
'- Do not describe every file. Cover what a reviewer must understand; the rest is appended for',
|
|
75
|
+
' you under a separate heading.',
|
|
76
|
+
'- No preamble, no "this PR", no closing summary. Output the markdown document and nothing else.',
|
|
77
|
+
'',
|
|
78
|
+
'Write for a reviewer reading once:',
|
|
79
|
+
'- One idea per sentence. Past thirty words, split it. The reader must never backtrack to parse.',
|
|
80
|
+
'- Name the mechanism, not the feeling: "the second attempt runs on a warmed socket", never',
|
|
81
|
+
' "this makes the call more robust". A sentence that would fit another project unchanged says',
|
|
82
|
+
' nothing about this one. Cut it.',
|
|
83
|
+
'- Active voice with the actor named: "the compiler validates queries", not "queries are validated".',
|
|
84
|
+
'- Plain words. Never: additionally, crucial, delve, enhance, leverage, pivotal, robust, seamless,',
|
|
85
|
+
' showcase, testament, underscore, utilize. Say "is" rather than "serves as" or "stands as".',
|
|
86
|
+
'- Sentence case headings. No emoji. No bold label that restates the line it introduces.',
|
|
87
|
+
'- End a sentence or use a comma rather than an em dash.',
|
|
88
|
+
].join('\n');
|
|
89
|
+
|
|
90
|
+
/** Trims the patch to the budget, keeping as many files as fit and naming what was left out. */
|
|
91
|
+
const budgetDiff = (diff: string, maxChars: number): { diff: string; omitted: string[] } => {
|
|
92
|
+
const parsed = parsePatch(diff);
|
|
93
|
+
const generated = parsed.filter((file) => isGeneratedFile(file)).map((file) => file.path);
|
|
94
|
+
const files = parsed
|
|
95
|
+
// Generated files go first, whatever the budget: a lockfile is the largest thing in most pull
|
|
96
|
+
// requests and the one the reader skips, so it would otherwise push out code that matters.
|
|
97
|
+
.filter((file) => !isGeneratedFile(file))
|
|
98
|
+
.map((file) => ({
|
|
99
|
+
path: file.path,
|
|
100
|
+
text: [...file.preamble, ...file.hunks.flatMap((hunk) => [hunk.header, ...hunk.lines])].join('\n'),
|
|
101
|
+
}));
|
|
102
|
+
const withoutGenerated = files.map((file) => file.text).join('\n');
|
|
103
|
+
if (withoutGenerated.length <= maxChars) {
|
|
104
|
+
return { diff: generated.length > 0 ? withoutGenerated : diff, omitted: generated };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const order = [...files].sort((left, right) => left.text.length - right.text.length);
|
|
108
|
+
const kept = new Set<string>();
|
|
109
|
+
let used = 0;
|
|
110
|
+
for (const file of order) {
|
|
111
|
+
if (used + file.text.length > maxChars) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
used += file.text.length + 1;
|
|
115
|
+
kept.add(file.path);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
diff: files
|
|
120
|
+
.filter((file) => kept.has(file.path))
|
|
121
|
+
.map((file) => file.text)
|
|
122
|
+
.join('\n'),
|
|
123
|
+
omitted: [...generated, ...files.filter((file) => !kept.has(file.path)).map((file) => file.path)],
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/** The user-side content: what the change is, followed by the diff the fences will be filled from. */
|
|
128
|
+
export const buildPrompt = (input: WalkthroughInput, options: PromptOptions = {}): string => {
|
|
129
|
+
const { diff, omitted } = budgetDiff(input.diff, options.maxDiffChars ?? DEFAULT_MAX_DIFF_CHARS);
|
|
130
|
+
const lines = [`Repository: ${input.owner}/${input.repo}`, `Pull request: #${input.number}`, `Title: ${input.title}`];
|
|
131
|
+
if (input.headBranch && input.baseBranch) {
|
|
132
|
+
lines.push(`Branch: ${input.headBranch} into ${input.baseBranch}`);
|
|
133
|
+
}
|
|
134
|
+
if (input.description?.trim()) {
|
|
135
|
+
lines.push('', 'Description as written by the author:', input.description.trim());
|
|
136
|
+
}
|
|
137
|
+
if (omitted.length > 0) {
|
|
138
|
+
lines.push('', `Omitted from the diff below for length, and not yours to describe: ${omitted.join(', ')}.`);
|
|
139
|
+
}
|
|
140
|
+
lines.push('', 'Diff:', '', diff);
|
|
141
|
+
|
|
142
|
+
return lines.join('\n');
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/** Chunks the model cannot have claimed, because the diff never showed them. */
|
|
146
|
+
export const promptOmissions = (input: WalkthroughInput, options: PromptOptions = {}): string[] =>
|
|
147
|
+
budgetDiff(input.diff, options.maxDiffChars ?? DEFAULT_MAX_DIFF_CHARS).omitted;
|