@agent-native/core 0.69.0 → 0.70.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -15
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +109 -0
- package/corpus/core/docs/design/durable-agent-runs.md +217 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action.ts +89 -0
- package/corpus/core/src/agent/model-config.ts +0 -2
- package/corpus/core/src/agent/run-loop-with-resume.ts +48 -0
- package/corpus/core/src/changelog/parse.ts +245 -0
- package/corpus/core/src/cli/changelog.ts +199 -0
- package/corpus/core/src/cli/index.ts +19 -0
- package/corpus/core/src/cli/recap.ts +24 -1
- package/corpus/core/src/cli/skills.ts +74 -47
- package/corpus/core/src/client/AgentPanel.tsx +5 -3
- package/corpus/core/src/client/AssistantChat.tsx +8 -0
- package/corpus/core/src/client/CommandMenu.tsx +183 -75
- package/corpus/core/src/client/agent-chat-adapter.ts +44 -7
- package/corpus/core/src/client/analytics.ts +181 -0
- package/corpus/core/src/client/changelog/Changelog.tsx +321 -0
- package/corpus/core/src/client/client-surface.ts +41 -0
- package/corpus/core/src/client/composer/TiptapComposer.tsx +123 -12
- package/corpus/core/src/client/feedback-context.ts +4 -0
- package/corpus/core/src/client/index.ts +12 -0
- package/corpus/core/src/client/mcp-apps/McpAppRenderer.tsx +54 -0
- package/corpus/core/src/client/settings/useBuilderStatus.ts +82 -20
- package/corpus/core/src/client/sse-event-processor.ts +7 -1
- package/corpus/core/src/mcp/build-server.ts +57 -1
- package/corpus/core/src/mcp/embed-app.ts +197 -7
- package/corpus/core/src/mcp/oauth-route.ts +109 -13
- package/corpus/core/src/server/attribution.ts +211 -0
- package/corpus/core/src/server/auth.ts +6 -0
- package/corpus/core/src/server/better-auth-instance.ts +49 -5
- package/corpus/core/src/server/builder-browser.ts +27 -1
- package/corpus/core/src/server/core-routes-plugin.ts +136 -0
- package/corpus/core/src/server/google-oauth.ts +19 -6
- package/corpus/core/src/server/onboarding-html.ts +35 -0
- package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
- package/corpus/core/src/styles/agent-conversation.css +21 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +22 -0
- package/corpus/templates/analytics/AGENTS.md +8 -1
- package/corpus/templates/analytics/CHANGELOG.md +10 -0
- package/corpus/templates/analytics/actions/install-dashboard-template.ts +99 -4
- package/corpus/templates/analytics/actions/open-traffic-dashboard.ts +1 -1
- package/corpus/templates/analytics/actions/update-dashboard.ts +39 -8
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +30 -3
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +180 -144
- package/corpus/templates/analytics/app/components/layout/Header.tsx +2 -3
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +7 -5
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +221 -215
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +6 -6
- package/corpus/templates/analytics/app/lib/demo-dashboard-path.ts +1 -1
- package/corpus/templates/analytics/app/lib/last-opened.ts +2 -1
- package/corpus/templates/analytics/app/pages/Settings.tsx +4 -0
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +2 -2
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -2
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +101 -99
- package/corpus/templates/analytics/app/pages/overview/OverviewPage.tsx +1 -1
- package/corpus/templates/analytics/app/routes/adhoc.$id.tsx +18 -5
- package/corpus/templates/analytics/app/routes/catalog.tsx +1 -1
- package/corpus/templates/analytics/app/routes/dashboards.$id.tsx +9 -0
- package/corpus/templates/analytics/app/routes/traffic.tsx +1 -1
- package/corpus/templates/analytics/changelog/2026-06-23-long-dashboard-and-analysis-names-in-the-sidebar-now-truncat.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-23-open-any-dashboard-chart-full-screen-in-a-modal-from-the-pan.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-23-the-dashboards-and-analyses-sidebar-settings-buttons-now-sta.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +145 -0
- package/corpus/templates/analytics/server/db/index.ts +1 -1
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +9 -2
- package/corpus/templates/analytics/server/lib/demo-dashboards.ts +1 -1
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +2 -2
- package/corpus/templates/analytics/server/plugins/core-routes.ts +6 -6
- package/corpus/templates/assets/CHANGELOG.md +10 -0
- package/corpus/templates/assets/actions/generate-image.ts +24 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +2 -2
- package/corpus/templates/assets/app/global.css +17 -1
- package/corpus/templates/assets/app/root.tsx +7 -1
- package/corpus/templates/assets/app/routes/_index.tsx +94 -14
- package/corpus/templates/assets/app/routes/settings.tsx +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-chat-dates-in-the-sidebar-no-longer-wrap-onto-two-lines.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-clicking-image-video-or-refine-on-the-start-screen-now-drops.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-23-pick-your-image-generation-model-right-from-the-chat-model-m.md +6 -0
- package/corpus/templates/brain/CHANGELOG.md +10 -0
- package/corpus/templates/brain/app/root.tsx +7 -1
- package/corpus/templates/brain/app/routes/settings.tsx +8 -1
- package/corpus/templates/calendar/CHANGELOG.md +10 -0
- package/corpus/templates/calendar/app/pages/Settings.tsx +4 -0
- package/corpus/templates/calendar/app/root.tsx +7 -1
- package/corpus/templates/chat/CHANGELOG.md +10 -0
- package/corpus/templates/chat/app/root.tsx +7 -1
- package/corpus/templates/clips/CHANGELOG.md +10 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -20
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +33 -7
- package/corpus/templates/clips/app/components/player/sign-in-prompt-dialog.tsx +8 -0
- package/corpus/templates/clips/app/components/player/video-player.tsx +150 -20
- package/corpus/templates/clips/app/components/recorder/countdown-overlay.tsx +72 -48
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +7 -11
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +31 -0
- package/corpus/templates/clips/app/components/sharing/slack-share-hint.tsx +71 -0
- package/corpus/templates/clips/app/lib/countdown-audio-cue.ts +35 -17
- package/corpus/templates/clips/app/root.tsx +7 -1
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +15 -1
- package/corpus/templates/clips/app/routes/record.tsx +78 -5
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +86 -5
- package/corpus/templates/clips/changelog/2026-06-23-clips-shared-in-slack-no-longer-show-a-could-not-start-playb.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-made-the-play-button-on-shared-and-embedded-clips-scale-with.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-public-clip-share-links-now-show-whether-they-ll-play-inline.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-removed-select-button-from-library-hover-a-clip-to-select-it.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-screen-camera-recordings-now-keep-the-presenter-s-face-in-th.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-shared-video-links-now-play-for-anyone-without-signing-in.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-skip-or-cancel-the-recording-countdown-with-buttons-beside-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-desktop-camera-bubble-now-looks-sharp-the-instant-it-ope.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-desktop-recording-widget-now-grows-downward-only-when-yo.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-the-recording-start-sound-is-now-a-softer-more-modern-chime.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +17 -8
- package/corpus/templates/clips/chrome-extension/src/background.ts +769 -180
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +177 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +466 -329
- package/corpus/templates/clips/chrome-extension/src/overlay.css +377 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.html +12 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +330 -0
- package/corpus/templates/clips/chrome-extension/src/permission.html +227 -0
- package/corpus/templates/clips/chrome-extension/src/permission.ts +105 -0
- package/corpus/templates/clips/chrome-extension/src/popup.html +3 -0
- package/corpus/templates/clips/chrome-extension/src/popup.ts +50 -2
- package/corpus/templates/clips/chrome-extension/src/styles.css +50 -0
- package/corpus/templates/clips/chrome-extension/vite.config.ts +3 -0
- package/corpus/templates/clips/desktop/src/lib/audio-cue.ts +41 -24
- package/corpus/templates/clips/desktop/src/lib/bubble-webrtc.ts +119 -2
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +20 -0
- package/corpus/templates/clips/desktop/src/main.tsx +3 -0
- package/corpus/templates/clips/desktop/src/overlays/countdown.tsx +21 -2
- package/corpus/templates/clips/desktop/src/overlays/region-record-border.tsx +76 -0
- package/corpus/templates/clips/desktop/src/styles.css +119 -2
- package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +2 -1
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +140 -1
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
- package/corpus/templates/clips/desktop/src-tauri/src/util.rs +4 -1
- package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +85 -6
- package/corpus/templates/clips/shared/share-attribution.ts +79 -0
- package/corpus/templates/content/AGENTS.md +20 -3
- package/corpus/templates/content/CHANGELOG.md +10 -0
- package/corpus/templates/content/actions/_database-utils.ts +15 -0
- package/corpus/templates/content/actions/_property-utils.ts +371 -4
- package/corpus/templates/content/actions/configure-document-property.ts +35 -1
- package/corpus/templates/content/actions/create-content-database.ts +5 -1
- package/corpus/templates/content/actions/delete-document-property.ts +52 -2
- package/corpus/templates/content/actions/duplicate-document-property.ts +34 -17
- package/corpus/templates/content/actions/reorder-document-property.ts +79 -0
- package/corpus/templates/content/actions/set-document-property.ts +39 -0
- package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +806 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +226 -68
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +59 -30
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +25 -1
- package/corpus/templates/content/app/components/editor/blockFieldSaveController.ts +180 -0
- package/corpus/templates/content/app/components/editor/blockFieldSaveRegistry.ts +179 -0
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +244 -0
- package/corpus/templates/content/app/components/editor/previewDocumentSaveRegistry.ts +132 -0
- package/corpus/templates/content/app/global.css +9 -0
- package/corpus/templates/content/app/hooks/use-document-properties.ts +21 -0
- package/corpus/templates/content/app/root.tsx +7 -1
- package/corpus/templates/content/server/db/schema.ts +30 -0
- package/corpus/templates/content/server/plugins/db.ts +87 -0
- package/corpus/templates/content/shared/api.ts +7 -0
- package/corpus/templates/content/shared/properties.ts +109 -0
- package/corpus/templates/design/CHANGELOG.md +10 -0
- package/corpus/templates/design/actions/generate-design.ts +4 -0
- package/corpus/templates/design/app/root.tsx +7 -1
- package/corpus/templates/design/app/routes/settings.tsx +10 -2
- package/corpus/templates/dispatch/CHANGELOG.md +10 -0
- package/corpus/templates/dispatch/app/root.tsx +7 -1
- package/corpus/templates/forms/.agents/skills/form-responses/SKILL.md +10 -0
- package/corpus/templates/forms/CHANGELOG.md +10 -0
- package/corpus/templates/forms/actions/export-responses.ts +6 -0
- package/corpus/templates/forms/actions/list-responses.ts +2 -0
- package/corpus/templates/forms/actions/response-insights.ts +2 -0
- package/corpus/templates/forms/app/pages/ResponsesPage.tsx +138 -1
- package/corpus/templates/forms/app/root.tsx +7 -1
- package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-the-page-each-submission-came-from-.md +6 -0
- package/corpus/templates/forms/changelog/2026-06-23-response-tables-now-show-whether-feedback-came-from-the-web-.md +6 -0
- package/corpus/templates/forms/server/db/schema.ts +7 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +19 -2
- package/corpus/templates/forms/server/lib/integrations.ts +76 -2
- package/corpus/templates/forms/server/plugins/db.ts +20 -0
- package/corpus/templates/forms/shared/types.ts +12 -0
- package/corpus/templates/macros/CHANGELOG.md +10 -0
- package/corpus/templates/macros/app/root.tsx +7 -1
- package/corpus/templates/mail/CHANGELOG.md +10 -0
- package/corpus/templates/mail/app/components/layout/CommandPalette.tsx +3 -0
- package/corpus/templates/mail/app/pages/SettingsPage.tsx +27 -0
- package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +31 -17
- package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +7 -4
- package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +34 -24
- package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +7 -4
- package/corpus/templates/plan/AGENTS.md +4 -0
- package/corpus/templates/plan/CHANGELOG.md +10 -0
- package/corpus/templates/plan/actions/view-screen.ts +5 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +98 -1
- package/corpus/templates/plan/app/components/plan/wireframe/html-artboard.css +3 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +107 -7
- package/corpus/templates/plan/app/root.tsx +7 -1
- package/corpus/templates/plan/changelog/2026-06-23-large-diagrams-in-a-plan-now-scroll-within-their-block-inste.md +6 -0
- package/corpus/templates/plan/shared/share-attribution.ts +72 -0
- package/corpus/templates/slides/CHANGELOG.md +10 -0
- package/corpus/templates/slides/app/root.tsx +7 -1
- package/corpus/templates/videos/CHANGELOG.md +4 -151
- package/corpus/templates/videos/app/root.tsx +7 -1
- package/dist/action.js +87 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/model-config.d.ts +2 -2
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +0 -2
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +13 -0
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +44 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/changelog/parse.d.ts +70 -0
- package/dist/changelog/parse.d.ts.map +1 -0
- package/dist/changelog/parse.js +200 -0
- package/dist/changelog/parse.js.map +1 -0
- package/dist/cli/changelog.d.ts +4 -0
- package/dist/cli/changelog.d.ts.map +1 -0
- package/dist/cli/changelog.js +166 -0
- package/dist/cli/changelog.js.map +1 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +22 -1
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.d.ts +3 -3
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +74 -47
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +5 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +6 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +2 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/CommandMenu.d.ts +17 -1
- package/dist/client/CommandMenu.d.ts.map +1 -1
- package/dist/client/CommandMenu.js +49 -13
- package/dist/client/CommandMenu.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +43 -6
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/analytics.d.ts +18 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +177 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/changelog/Changelog.d.ts +47 -0
- package/dist/client/changelog/Changelog.d.ts.map +1 -0
- package/dist/client/changelog/Changelog.js +138 -0
- package/dist/client/changelog/Changelog.js.map +1 -0
- package/dist/client/client-surface.d.ts +17 -0
- package/dist/client/client-surface.d.ts.map +1 -0
- package/dist/client/client-surface.js +24 -0
- package/dist/client/client-surface.js.map +1 -0
- package/dist/client/composer/TiptapComposer.d.ts +26 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +27 -5
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/feedback-context.d.ts +3 -0
- package/dist/client/feedback-context.d.ts.map +1 -1
- package/dist/client/feedback-context.js +2 -0
- package/dist/client/feedback-context.js.map +1 -1
- package/dist/client/index.d.ts +3 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.d.ts +1 -0
- package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.js +42 -1
- package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +2 -0
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +58 -10
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +7 -1
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/mcp/build-server.d.ts +21 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +43 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +197 -7
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/mcp/oauth-route.d.ts.map +1 -1
- package/dist/mcp/oauth-route.js +95 -12
- package/dist/mcp/oauth-route.js.map +1 -1
- package/dist/server/attribution.d.ts +83 -0
- package/dist/server/attribution.d.ts.map +1 -0
- package/dist/server/attribution.js +178 -0
- package/dist/server/attribution.js.map +1 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +7 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts +9 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +32 -2
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/builder-browser.d.ts +4 -0
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +20 -1
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +25 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +101 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +16 -2
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +35 -0
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/shared/mcp-embed-headers.js +1 -1
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/styles/agent-conversation.css +21 -0
- package/dist/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
- package/dist/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
- package/docs/design/durable-agent-runs.md +217 -0
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
- package/src/templates/workspace-core/.agents/skills/reliable-mutations/SKILL.md +72 -0
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
- package/corpus/templates/analytics/app/pages/About.tsx +0 -108
- package/corpus/templates/analytics/app/routes/about.tsx +0 -9
- /package/corpus/templates/analytics/app/routes/{dashboards.tsx → dashboards._index.tsx} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-loop-with-resume.js","sourceRoot":"","sources":["../../src/agent/run-loop-with-resume.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Wraps `runAgentLoop` with two layered recovery mechanisms so a single hosted\n * invocation can survive interruptions without showing the user a dead chat:\n *\n * 1. **Soft timeout** — an inner timer that aborts the LLM call before the\n * hosting function's hard limit (Lambda 75s, Vercel 60s, etc.) so we have a\n * chance to gracefully wind down and append a continuation nudge. Without\n * this the function gets killed mid-stream and the user sees a frozen\n * spinner.\n *\n * 2. **Resumable-error continuation** — when the LLM call errors with a\n * transport- or gateway-level interruption (Builder gateway 45s timeout,\n * socket hang up, ECONNRESET, upstream 5xx that survived engine retries),\n * we save the conversation prefix, append a \"continue from where you left\n * off\" message, and run another LLM call. Anthropic's prompt cache makes\n * the resume call dramatically faster than the cold first attempt, and the\n * agent gets explicit context that it was cut off so it doesn't re-do\n * completed work.\n *\n * Both paths route through `appendAgentLoopContinuation` so the agent sees a\n * uniform \"continue\" instruction regardless of which recovery fired.\n */\n\nimport {\n runAgentLoop,\n appendAgentLoopContinuation,\n isResumableEngineError,\n continuationReasonForResumableError,\n} from \"./production-agent.js\";\nimport { resolveRunSoftTimeoutMs } from \"./run-manager.js\";\nimport { getCurrentTurnEventsForThread } from \"./run-store.js\";\nimport {\n classifyToolCallJournal,\n buildResumeJournalNote,\n} from \"./tool-call-journal.js\";\nimport type { EngineMessage } from \"./engine/types.js\";\n\n/**\n * Derive the per-turn tool-call journal from the durable run-event ledger and,\n * when there is anything to report, append a STRUCTURED note to the message\n * prefix so the resumed model:\n * - does NOT re-execute tool calls that already completed (avoiding duplicate\n * side effects like re-sending an email or re-creating a ticket), and\n * - is explicitly told about any tool call that started but whose outcome was\n * never recorded (\"interrupted, unknown outcome\") so it can decide.\n *\n * This is additive to the existing \"continue from where you left off\" nudge —\n * it is appended right after it. When the journal is empty (no completed or\n * interrupted tool calls — e.g. a turn with no tool activity, or a clean\n * continuation), nothing extra is appended and resume behavior is byte-for-byte\n * what it was before. Best-effort: any ledger read/parse failure is swallowed so\n * a journal hiccup can never block a recovery that would otherwise succeed.\n *\n * This prompt-level journal is paired with tool-layer enforcement in\n * production-agent.ts/runToolCall, which refuses to re-execute a journaled-\n * complete write tool (returning the journaled result instead). See\n * `tool-call-journal.ts` (`findCompletedJournalEntry`) for the keying used.\n */\nasync function appendToolCallJournalNote(\n messages: EngineMessage[],\n threadId: string | undefined,\n): Promise<void> {\n if (!threadId) return;\n try {\n const events = await getCurrentTurnEventsForThread(threadId);\n if (events.length === 0) return;\n const journal = classifyToolCallJournal(events);\n const note = buildResumeJournalNote(journal);\n if (!note) return;\n messages.push({\n role: \"user\",\n content: [{ type: \"text\", text: note }],\n });\n } catch {\n // The journal is a hardening layer, never a gate. A failed ledger read or\n // parse must not break the resume that the continuation nudge already set\n // up — the model still continues, just without the structured journal.\n }\n}\n\n/**\n * Cap on continuation iterations inside a single\n * `runAgentLoopDirectWithSoftTimeout` invocation. The host's hard function\n * timeout usually bounds this naturally — but a defensive cap prevents an\n * instant-error spiral from looping forever inside hosting environments with a\n * generous budget.\n *\n * 6 leaves room for: 1 normal completion + a few resume rounds for design\n * generation (prompt + 3 variants ≈ 4 LLM calls), with a small safety margin.\n */\nexport const MAX_RUN_LOOP_CONTINUATIONS = 6;\n\n/**\n * Internal entry point used by the agent-chat plugin's run handler. Wraps\n * `runAgentLoop` with soft-timeout + resumable-error continuation recovery.\n *\n * The `softTimeoutMs` argument falls back to `resolveRunSoftTimeoutMs(...)` so\n * different hosting environments (Lambda, Vercel, Cloudflare, local dev) get\n * an appropriate inner budget. Setting it to <= 0 disables both layers — the\n * call goes straight to `runAgentLoop` with no wrapping.\n */\nexport async function runAgentLoopDirectWithSoftTimeout(\n opts: Parameters<typeof runAgentLoop>[0],\n softTimeoutMs?: number,\n): Promise<Awaited<ReturnType<typeof runAgentLoop>>> {\n const timeoutMs = resolveRunSoftTimeoutMs(softTimeoutMs);\n if (timeoutMs <= 0) return runAgentLoop(opts);\n\n const upstreamSignal = opts.signal;\n const usage: Awaited<ReturnType<typeof runAgentLoop>> = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n model: opts.model,\n };\n\n const addUsage = (next: Awaited<ReturnType<typeof runAgentLoop>>) => {\n usage.inputTokens += next.inputTokens;\n usage.outputTokens += next.outputTokens;\n usage.cacheReadTokens += next.cacheReadTokens;\n usage.cacheWriteTokens += next.cacheWriteTokens;\n usage.model = next.model;\n };\n\n let attempts = 0;\n while (!upstreamSignal.aborted && attempts < MAX_RUN_LOOP_CONTINUATIONS) {\n attempts++;\n const controller = new AbortController();\n const abortFromUpstream = () => controller.abort();\n if (upstreamSignal.aborted) {\n controller.abort();\n } else {\n upstreamSignal.addEventListener(\"abort\", abortFromUpstream, {\n once: true,\n });\n }\n\n let softTimedOut = false;\n const timer = setTimeout(() => {\n if (controller.signal.aborted) return;\n softTimedOut = true;\n controller.abort();\n }, timeoutMs);\n\n try {\n const nextUsage = await runAgentLoop({\n ...opts,\n signal: controller.signal,\n });\n addUsage(nextUsage);\n if (softTimedOut && !upstreamSignal.aborted) {\n appendAgentLoopContinuation(opts.messages, \"run_timeout\");\n await appendToolCallJournalNote(opts.messages, opts.threadId);\n continue;\n }\n return usage;\n } catch (err) {\n if (softTimedOut && !upstreamSignal.aborted) {\n // Clear partial text the client received before the abort so the\n // resumed model doesn't re-emit it and produce duplicated output.\n opts.send({ type: \"clear\" });\n appendAgentLoopContinuation(opts.messages, \"run_timeout\");\n await appendToolCallJournalNote(opts.messages, opts.threadId);\n continue;\n }\n // Resumable transport / gateway interruptions: the LLM call was cut off\n // mid-stream (gateway 45s timeout, socket hang up, function-level\n // timeout that didn't trip our soft timer first). Treat it the same way\n // as a soft timeout — append a \"continue from where you left off\" nudge\n // and let the loop run another LLM call. The conversation prefix up to\n // the cut-off is preserved in opts.messages, and Anthropic's prompt\n // cache makes the resume call much faster.\n //\n // Emit 'clear' so any partial streamed text is discarded on the client\n // before the model resumes. Without this the model restarts its sentence\n // from scratch and the fold produces duplicated text in one message\n // (the partial text was already sent to the client but never entered\n // the in-memory messages array, so the next attempt re-emits it).\n if (!upstreamSignal.aborted && isResumableEngineError(err)) {\n opts.send({ type: \"clear\" });\n appendAgentLoopContinuation(\n opts.messages,\n continuationReasonForResumableError(err),\n );\n await appendToolCallJournalNote(opts.messages, opts.threadId);\n continue;\n }\n throw err;\n } finally {\n clearTimeout(timer);\n upstreamSignal.removeEventListener(\"abort\", abortFromUpstream);\n }\n }\n\n return usage;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"run-loop-with-resume.js","sourceRoot":"","sources":["../../src/agent/run-loop-with-resume.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Wraps `runAgentLoop` with two layered recovery mechanisms so a single hosted\n * invocation can survive interruptions without showing the user a dead chat:\n *\n * 1. **Soft timeout** — an inner timer that aborts the LLM call before the\n * hosting function's hard limit (Lambda 75s, Vercel 60s, etc.) so we have a\n * chance to gracefully wind down and append a continuation nudge. Without\n * this the function gets killed mid-stream and the user sees a frozen\n * spinner.\n *\n * 2. **Resumable-error continuation** — when the LLM call errors with a\n * transport- or gateway-level interruption (Builder gateway 45s timeout,\n * socket hang up, ECONNRESET, upstream 5xx that survived engine retries),\n * we save the conversation prefix, append a \"continue from where you left\n * off\" message, and run another LLM call. Anthropic's prompt cache makes\n * the resume call dramatically faster than the cold first attempt, and the\n * agent gets explicit context that it was cut off so it doesn't re-do\n * completed work.\n *\n * Both paths route through `appendAgentLoopContinuation` so the agent sees a\n * uniform \"continue\" instruction regardless of which recovery fired.\n */\n\nimport {\n runAgentLoop,\n appendAgentLoopContinuation,\n isResumableEngineError,\n continuationReasonForResumableError,\n} from \"./production-agent.js\";\nimport { resolveRunSoftTimeoutMs } from \"./run-manager.js\";\nimport { getCurrentTurnEventsForThread } from \"./run-store.js\";\nimport {\n classifyToolCallJournal,\n buildResumeJournalNote,\n} from \"./tool-call-journal.js\";\nimport type { EngineMessage } from \"./engine/types.js\";\n\n/**\n * Derive the per-turn tool-call journal from the durable run-event ledger and,\n * when there is anything to report, append a STRUCTURED note to the message\n * prefix so the resumed model:\n * - does NOT re-execute tool calls that already completed (avoiding duplicate\n * side effects like re-sending an email or re-creating a ticket), and\n * - is explicitly told about any tool call that started but whose outcome was\n * never recorded (\"interrupted, unknown outcome\") so it can decide.\n *\n * This is additive to the existing \"continue from where you left off\" nudge —\n * it is appended right after it. When the journal is empty (no completed or\n * interrupted tool calls — e.g. a turn with no tool activity, or a clean\n * continuation), nothing extra is appended and resume behavior is byte-for-byte\n * what it was before. Best-effort: any ledger read/parse failure is swallowed so\n * a journal hiccup can never block a recovery that would otherwise succeed.\n *\n * This prompt-level journal is paired with tool-layer enforcement in\n * production-agent.ts/runToolCall, which refuses to re-execute a journaled-\n * complete write tool (returning the journaled result instead). See\n * `tool-call-journal.ts` (`findCompletedJournalEntry`) for the keying used.\n */\nasync function appendToolCallJournalNote(\n messages: EngineMessage[],\n threadId: string | undefined,\n): Promise<void> {\n if (!threadId) return;\n try {\n const events = await getCurrentTurnEventsForThread(threadId);\n if (events.length === 0) return;\n const journal = classifyToolCallJournal(events);\n const note = buildResumeJournalNote(journal);\n if (!note) return;\n messages.push({\n role: \"user\",\n content: [{ type: \"text\", text: note }],\n });\n } catch {\n // The journal is a hardening layer, never a gate. A failed ledger read or\n // parse must not break the resume that the continuation nudge already set\n // up — the model still continues, just without the structured journal.\n }\n}\n\n/**\n * Cap on continuation iterations inside a single\n * `runAgentLoopDirectWithSoftTimeout` invocation. The host's hard function\n * timeout usually bounds this naturally — but a defensive cap prevents an\n * instant-error spiral from looping forever inside hosting environments with a\n * generous budget.\n *\n * 6 leaves room for: 1 normal completion + a few resume rounds for design\n * generation (prompt + 3 variants ≈ 4 LLM calls), with a small safety margin.\n */\nexport const MAX_RUN_LOOP_CONTINUATIONS = 6;\n\n/** Machine-readable code carried on the give-up terminal `error` event so the\n * client renders a loud \"stopped before finishing\" terminal instead of an\n * ambiguous silent stall. Deliberately NOT in the client's auto-recoverable\n * allow-list (`isAutoRecoverableError`) so it terminates the chain rather than\n * looping another POST that would hit the same wall. */\nexport const RUN_BUDGET_EXHAUSTED_ERROR_CODE = \"run_budget_exhausted\";\n\n/** User-facing terminal message when a hosted run is cut off mid-step and\n * exhausts its in-invocation continuation budget without finishing. Generic and\n * framework-level (not app-specific). Mirrors the `reliable-mutations` skill's\n * \"fail loud, retry as a single bulk action\" guidance so the user understands\n * the turn stopped before finishing and was not partially saved by the run\n * itself. */\nexport const RUN_BUDGET_EXHAUSTED_MESSAGE =\n \"I ran out of time before finishing this step (hosted runs have a ~40s budget). \" +\n \"I stopped rather than leave things half-done — nothing was partially saved by me here. \" +\n \"Please retry, ideally as a single bulk action.\";\n\n/**\n * Internal entry point used by the agent-chat plugin's run handler. Wraps\n * `runAgentLoop` with soft-timeout + resumable-error continuation recovery.\n *\n * The `softTimeoutMs` argument falls back to `resolveRunSoftTimeoutMs(...)` so\n * different hosting environments (Lambda, Vercel, Cloudflare, local dev) get\n * an appropriate inner budget. Setting it to <= 0 disables both layers — the\n * call goes straight to `runAgentLoop` with no wrapping.\n */\nexport async function runAgentLoopDirectWithSoftTimeout(\n opts: Parameters<typeof runAgentLoop>[0],\n softTimeoutMs?: number,\n): Promise<Awaited<ReturnType<typeof runAgentLoop>>> {\n const timeoutMs = resolveRunSoftTimeoutMs(softTimeoutMs);\n if (timeoutMs <= 0) return runAgentLoop(opts);\n\n const upstreamSignal = opts.signal;\n const usage: Awaited<ReturnType<typeof runAgentLoop>> = {\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheWriteTokens: 0,\n model: opts.model,\n };\n\n const addUsage = (next: Awaited<ReturnType<typeof runAgentLoop>>) => {\n usage.inputTokens += next.inputTokens;\n usage.outputTokens += next.outputTokens;\n usage.cacheReadTokens += next.cacheReadTokens;\n usage.cacheWriteTokens += next.cacheWriteTokens;\n usage.model = next.model;\n };\n\n let attempts = 0;\n // Tracks whether the most recent attempt ended by scheduling another\n // continuation (soft-timeout or resumable error → `continue`) rather than\n // returning a finished turn. When the loop then exits because the budget is\n // exhausted (NOT because the user aborted and NOT because the turn finished),\n // this is the silent give-up case: emit a loud terminal so the user sees an\n // unambiguous \"stopped before finishing\" instead of a bare done/\"…\".\n let lastAttemptWasUnfinishedContinuation = false;\n while (!upstreamSignal.aborted && attempts < MAX_RUN_LOOP_CONTINUATIONS) {\n attempts++;\n lastAttemptWasUnfinishedContinuation = false;\n const controller = new AbortController();\n const abortFromUpstream = () => controller.abort();\n if (upstreamSignal.aborted) {\n controller.abort();\n } else {\n upstreamSignal.addEventListener(\"abort\", abortFromUpstream, {\n once: true,\n });\n }\n\n let softTimedOut = false;\n const timer = setTimeout(() => {\n if (controller.signal.aborted) return;\n softTimedOut = true;\n controller.abort();\n }, timeoutMs);\n\n try {\n const nextUsage = await runAgentLoop({\n ...opts,\n signal: controller.signal,\n });\n addUsage(nextUsage);\n if (softTimedOut && !upstreamSignal.aborted) {\n lastAttemptWasUnfinishedContinuation = true;\n appendAgentLoopContinuation(opts.messages, \"run_timeout\");\n await appendToolCallJournalNote(opts.messages, opts.threadId);\n continue;\n }\n return usage;\n } catch (err) {\n if (softTimedOut && !upstreamSignal.aborted) {\n // Clear partial text the client received before the abort so the\n // resumed model doesn't re-emit it and produce duplicated output.\n lastAttemptWasUnfinishedContinuation = true;\n opts.send({ type: \"clear\" });\n appendAgentLoopContinuation(opts.messages, \"run_timeout\");\n await appendToolCallJournalNote(opts.messages, opts.threadId);\n continue;\n }\n // Resumable transport / gateway interruptions: the LLM call was cut off\n // mid-stream (gateway 45s timeout, socket hang up, function-level\n // timeout that didn't trip our soft timer first). Treat it the same way\n // as a soft timeout — append a \"continue from where you left off\" nudge\n // and let the loop run another LLM call. The conversation prefix up to\n // the cut-off is preserved in opts.messages, and Anthropic's prompt\n // cache makes the resume call much faster.\n //\n // Emit 'clear' so any partial streamed text is discarded on the client\n // before the model resumes. Without this the model restarts its sentence\n // from scratch and the fold produces duplicated text in one message\n // (the partial text was already sent to the client but never entered\n // the in-memory messages array, so the next attempt re-emits it).\n if (!upstreamSignal.aborted && isResumableEngineError(err)) {\n lastAttemptWasUnfinishedContinuation = true;\n opts.send({ type: \"clear\" });\n appendAgentLoopContinuation(\n opts.messages,\n continuationReasonForResumableError(err),\n );\n await appendToolCallJournalNote(opts.messages, opts.threadId);\n continue;\n }\n throw err;\n } finally {\n clearTimeout(timer);\n upstreamSignal.removeEventListener(\"abort\", abortFromUpstream);\n }\n }\n\n // The loop exited without a clean return. If the user aborted, that's a Stop —\n // stay silent. Otherwise we only get here by exhausting\n // MAX_RUN_LOOP_CONTINUATIONS while the last attempt was still trying to\n // continue (soft-timeout / resumable error). That is the genuinely-silent\n // give-up the run-manager would otherwise report as a clean `done`: emit a\n // loud, non-auto-continuing terminal so the user knows the turn stopped\n // before finishing and nothing was partially saved by the run itself.\n if (!upstreamSignal.aborted && lastAttemptWasUnfinishedContinuation) {\n // Discard any partial text already streamed for the unfinished attempt so\n // the terminal message stands alone instead of trailing a half sentence.\n opts.send({ type: \"clear\" });\n opts.send({\n type: \"error\",\n error: RUN_BUDGET_EXHAUSTED_MESSAGE,\n errorCode: RUN_BUDGET_EXHAUSTED_ERROR_CODE,\n recoverable: true,\n });\n }\n\n return usage;\n}\n"]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, dependency-free changelog parsing + serialization.
|
|
3
|
+
*
|
|
4
|
+
* Used by BOTH:
|
|
5
|
+
* - the browser bundle (rendering an app's CHANGELOG.md in the command
|
|
6
|
+
* menu / settings "What's new" surface), and
|
|
7
|
+
* - the `agent-native changelog` CLI (rolling pending entry files up into
|
|
8
|
+
* CHANGELOG.md).
|
|
9
|
+
*
|
|
10
|
+
* Keep this file isomorphic: no Node, no browser, no third-party deps. The
|
|
11
|
+
* markdown shape is the conventional "Keep a Changelog" layout — a top-level
|
|
12
|
+
* `# Changelog` heading followed by one `## <release>` section per release.
|
|
13
|
+
*/
|
|
14
|
+
/** A single released section of a CHANGELOG.md file. */
|
|
15
|
+
export interface ChangelogEntry {
|
|
16
|
+
/** Stable id derived from the heading — used for "unseen" tracking. */
|
|
17
|
+
id: string;
|
|
18
|
+
/** Raw heading text, e.g. `2026-06-23` or `v1.2.0 — 2026-06-23`. */
|
|
19
|
+
title: string;
|
|
20
|
+
/** ISO date (YYYY-MM-DD) extracted from the heading, if present. */
|
|
21
|
+
date?: string;
|
|
22
|
+
/** Version label extracted from the heading, if present. */
|
|
23
|
+
version?: string;
|
|
24
|
+
/** Markdown body beneath the heading (until the next `## ` section). */
|
|
25
|
+
body: string;
|
|
26
|
+
}
|
|
27
|
+
/** A not-yet-released entry authored as a `changelog/<file>.md` file. */
|
|
28
|
+
export interface PendingChangelogEntry {
|
|
29
|
+
/** Category — `added`, `improved`, `fixed`, `changed`, etc. */
|
|
30
|
+
type: ChangelogChangeType;
|
|
31
|
+
/** ISO date the entry was authored (YYYY-MM-DD). */
|
|
32
|
+
date?: string;
|
|
33
|
+
/** User-facing description (markdown, single bullet). */
|
|
34
|
+
text: string;
|
|
35
|
+
}
|
|
36
|
+
export type ChangelogChangeType = "added" | "improved" | "fixed" | "changed" | "removed" | "security";
|
|
37
|
+
/**
|
|
38
|
+
* Order changes are grouped under a release heading. Anything not listed here
|
|
39
|
+
* falls back to the "changed" group, then renders in insertion order.
|
|
40
|
+
*/
|
|
41
|
+
export declare const CHANGELOG_GROUP_ORDER: ChangelogChangeType[];
|
|
42
|
+
declare const GROUP_LABELS: Record<ChangelogChangeType, string>;
|
|
43
|
+
/** Lowercase, hyphenate, and strip to a URL/id-safe slug. */
|
|
44
|
+
export declare function changelogSlug(value: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Parse a CHANGELOG.md document into structured release entries.
|
|
47
|
+
*
|
|
48
|
+
* Tolerant by design: an empty or malformed file yields an empty list rather
|
|
49
|
+
* than throwing, so a missing/partial changelog never breaks the UI.
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseChangelog(markdown: string): ChangelogEntry[];
|
|
52
|
+
/**
|
|
53
|
+
* Parse a pending `changelog/<file>.md` entry: optional `---` frontmatter
|
|
54
|
+
* (`type:` / `date:`) followed by the markdown description body.
|
|
55
|
+
*/
|
|
56
|
+
export declare function parsePendingEntry(content: string): PendingChangelogEntry;
|
|
57
|
+
/**
|
|
58
|
+
* Render a set of pending entries as a single dated release section (the body
|
|
59
|
+
* that goes beneath a `## <date>` heading). Groups bullets by type.
|
|
60
|
+
*/
|
|
61
|
+
export declare function renderReleaseBody(entries: PendingChangelogEntry[]): string;
|
|
62
|
+
declare const CHANGELOG_HEADER: string;
|
|
63
|
+
/**
|
|
64
|
+
* Roll a batch of pending entries into an existing CHANGELOG.md document,
|
|
65
|
+
* prepending a new `## <date>` section above the most recent release. Returns
|
|
66
|
+
* the full updated document. Pure — the CLI handles file IO and deletion.
|
|
67
|
+
*/
|
|
68
|
+
export declare function rollupChangelog(existing: string, pending: PendingChangelogEntry[], releaseDate: string): string;
|
|
69
|
+
export { CHANGELOG_HEADER, GROUP_LABELS };
|
|
70
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/changelog/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,yEAAyE;AACzE,MAAM,WAAW,qBAAqB;IACpC,+DAA+D;IAC/D,IAAI,EAAE,mBAAmB,CAAC;IAC1B,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,mBAAmB,GAC3B,OAAO,GACP,UAAU,GACV,OAAO,GACP,SAAS,GACT,SAAS,GACT,UAAU,CAAC;AAEf;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,mBAAmB,EAOtD,CAAC;AAEF,QAAA,MAAM,YAAY,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAOrD,CAAC;AAIF,6DAA6D;AAC7D,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMnD;AAiBD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,EAAE,CA+CjE;AAOD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAuBxE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,qBAAqB,EAAE,GAAG,MAAM,CAwB1E;AAED,QAAA,MAAM,gBAAgB,QAEgD,CAAC;AAEvE;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,qBAAqB,EAAE,EAChC,WAAW,EAAE,MAAM,GAClB,MAAM,CAgBR;AAED,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, dependency-free changelog parsing + serialization.
|
|
3
|
+
*
|
|
4
|
+
* Used by BOTH:
|
|
5
|
+
* - the browser bundle (rendering an app's CHANGELOG.md in the command
|
|
6
|
+
* menu / settings "What's new" surface), and
|
|
7
|
+
* - the `agent-native changelog` CLI (rolling pending entry files up into
|
|
8
|
+
* CHANGELOG.md).
|
|
9
|
+
*
|
|
10
|
+
* Keep this file isomorphic: no Node, no browser, no third-party deps. The
|
|
11
|
+
* markdown shape is the conventional "Keep a Changelog" layout — a top-level
|
|
12
|
+
* `# Changelog` heading followed by one `## <release>` section per release.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Order changes are grouped under a release heading. Anything not listed here
|
|
16
|
+
* falls back to the "changed" group, then renders in insertion order.
|
|
17
|
+
*/
|
|
18
|
+
export const CHANGELOG_GROUP_ORDER = [
|
|
19
|
+
"added",
|
|
20
|
+
"improved",
|
|
21
|
+
"fixed",
|
|
22
|
+
"changed",
|
|
23
|
+
"removed",
|
|
24
|
+
"security",
|
|
25
|
+
];
|
|
26
|
+
const GROUP_LABELS = {
|
|
27
|
+
added: "Added",
|
|
28
|
+
improved: "Improved",
|
|
29
|
+
fixed: "Fixed",
|
|
30
|
+
changed: "Changed",
|
|
31
|
+
removed: "Removed",
|
|
32
|
+
security: "Security",
|
|
33
|
+
};
|
|
34
|
+
const ISO_DATE = /(\d{4}-\d{2}-\d{2})/;
|
|
35
|
+
/** Lowercase, hyphenate, and strip to a URL/id-safe slug. */
|
|
36
|
+
export function changelogSlug(value) {
|
|
37
|
+
return value
|
|
38
|
+
.toLowerCase()
|
|
39
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
40
|
+
.replace(/^-+|-+$/g, "")
|
|
41
|
+
.slice(0, 60);
|
|
42
|
+
}
|
|
43
|
+
function normalizeType(raw) {
|
|
44
|
+
const value = (raw ?? "").trim().toLowerCase();
|
|
45
|
+
if (CHANGELOG_GROUP_ORDER.includes(value)) {
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
// Friendly aliases.
|
|
49
|
+
if (value === "feature" || value === "new" || value === "add")
|
|
50
|
+
return "added";
|
|
51
|
+
if (value === "improvement" || value === "enhancement" || value === "perf") {
|
|
52
|
+
return "improved";
|
|
53
|
+
}
|
|
54
|
+
if (value === "fix" || value === "bugfix" || value === "bug")
|
|
55
|
+
return "fixed";
|
|
56
|
+
if (value === "remove" || value === "deprecated")
|
|
57
|
+
return "removed";
|
|
58
|
+
return "changed";
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Parse a CHANGELOG.md document into structured release entries.
|
|
62
|
+
*
|
|
63
|
+
* Tolerant by design: an empty or malformed file yields an empty list rather
|
|
64
|
+
* than throwing, so a missing/partial changelog never breaks the UI.
|
|
65
|
+
*/
|
|
66
|
+
export function parseChangelog(markdown) {
|
|
67
|
+
if (!markdown || typeof markdown !== "string")
|
|
68
|
+
return [];
|
|
69
|
+
const lines = markdown.split(/\r?\n/);
|
|
70
|
+
const entries = [];
|
|
71
|
+
const seenIds = new Set();
|
|
72
|
+
let currentTitle = null;
|
|
73
|
+
let bodyLines = [];
|
|
74
|
+
const flush = () => {
|
|
75
|
+
if (currentTitle === null)
|
|
76
|
+
return;
|
|
77
|
+
const title = currentTitle.trim();
|
|
78
|
+
const date = title.match(ISO_DATE)?.[1];
|
|
79
|
+
const version = title.match(/v?\d+\.\d+(?:\.\d+)?/)?.[0];
|
|
80
|
+
// Build a stable, unique id from the heading.
|
|
81
|
+
let base = changelogSlug(title) || "entry";
|
|
82
|
+
let id = base;
|
|
83
|
+
let n = 2;
|
|
84
|
+
while (seenIds.has(id))
|
|
85
|
+
id = `${base}-${n++}`;
|
|
86
|
+
seenIds.add(id);
|
|
87
|
+
entries.push({
|
|
88
|
+
id,
|
|
89
|
+
title,
|
|
90
|
+
date,
|
|
91
|
+
version: version && version !== date ? version : undefined,
|
|
92
|
+
body: bodyLines.join("\n").trim(),
|
|
93
|
+
});
|
|
94
|
+
currentTitle = null;
|
|
95
|
+
bodyLines = [];
|
|
96
|
+
};
|
|
97
|
+
for (const line of lines) {
|
|
98
|
+
// `## ` (but not `### `) starts a new release section.
|
|
99
|
+
const match = /^##\s+(?!#)(.+?)\s*$/.exec(line);
|
|
100
|
+
if (match) {
|
|
101
|
+
flush();
|
|
102
|
+
currentTitle = stripBrackets(match[1]);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// Skip the top-level `# Changelog` title and anything before the first `##`.
|
|
106
|
+
if (currentTitle === null)
|
|
107
|
+
continue;
|
|
108
|
+
bodyLines.push(line);
|
|
109
|
+
}
|
|
110
|
+
flush();
|
|
111
|
+
return entries;
|
|
112
|
+
}
|
|
113
|
+
/** `## [1.2.0]` → `1.2.0`; leaves un-bracketed headings untouched. */
|
|
114
|
+
function stripBrackets(title) {
|
|
115
|
+
return title.replace(/^\[(.+?)\]\s*/, "$1 ").trim();
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Parse a pending `changelog/<file>.md` entry: optional `---` frontmatter
|
|
119
|
+
* (`type:` / `date:`) followed by the markdown description body.
|
|
120
|
+
*/
|
|
121
|
+
export function parsePendingEntry(content) {
|
|
122
|
+
let type;
|
|
123
|
+
let date;
|
|
124
|
+
let body = content;
|
|
125
|
+
const fm = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(content);
|
|
126
|
+
if (fm) {
|
|
127
|
+
for (const raw of fm[1].split(/\r?\n/)) {
|
|
128
|
+
const kv = /^([a-zA-Z]+)\s*:\s*(.+?)\s*$/.exec(raw);
|
|
129
|
+
if (!kv)
|
|
130
|
+
continue;
|
|
131
|
+
const key = kv[1].toLowerCase();
|
|
132
|
+
const value = kv[2].replace(/^["']|["']$/g, "").trim();
|
|
133
|
+
if (key === "type")
|
|
134
|
+
type = value;
|
|
135
|
+
else if (key === "date")
|
|
136
|
+
date = value.match(ISO_DATE)?.[1] ?? value;
|
|
137
|
+
}
|
|
138
|
+
body = content.slice(fm[0].length);
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
type: normalizeType(type),
|
|
142
|
+
date,
|
|
143
|
+
text: body.trim(),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Render a set of pending entries as a single dated release section (the body
|
|
148
|
+
* that goes beneath a `## <date>` heading). Groups bullets by type.
|
|
149
|
+
*/
|
|
150
|
+
export function renderReleaseBody(entries) {
|
|
151
|
+
const groups = new Map();
|
|
152
|
+
for (const entry of entries) {
|
|
153
|
+
const text = entry.text.trim();
|
|
154
|
+
if (!text)
|
|
155
|
+
continue;
|
|
156
|
+
const bullet = text.includes("\n")
|
|
157
|
+
? // Preserve multi-line bodies, indenting continuation lines.
|
|
158
|
+
text
|
|
159
|
+
.split(/\r?\n/)
|
|
160
|
+
.map((l, i) => (i === 0 ? `- ${l}` : ` ${l}`))
|
|
161
|
+
.join("\n")
|
|
162
|
+
: `- ${text}`;
|
|
163
|
+
const list = groups.get(entry.type) ?? [];
|
|
164
|
+
list.push(bullet);
|
|
165
|
+
groups.set(entry.type, list);
|
|
166
|
+
}
|
|
167
|
+
const sections = [];
|
|
168
|
+
for (const type of CHANGELOG_GROUP_ORDER) {
|
|
169
|
+
const list = groups.get(type);
|
|
170
|
+
if (!list?.length)
|
|
171
|
+
continue;
|
|
172
|
+
sections.push(`### ${GROUP_LABELS[type]}\n\n${list.join("\n")}`);
|
|
173
|
+
}
|
|
174
|
+
return sections.join("\n\n");
|
|
175
|
+
}
|
|
176
|
+
const CHANGELOG_HEADER = "# Changelog\n\n" +
|
|
177
|
+
"All notable user-facing changes to this app are documented here.\n";
|
|
178
|
+
/**
|
|
179
|
+
* Roll a batch of pending entries into an existing CHANGELOG.md document,
|
|
180
|
+
* prepending a new `## <date>` section above the most recent release. Returns
|
|
181
|
+
* the full updated document. Pure — the CLI handles file IO and deletion.
|
|
182
|
+
*/
|
|
183
|
+
export function rollupChangelog(existing, pending, releaseDate) {
|
|
184
|
+
const body = renderReleaseBody(pending);
|
|
185
|
+
if (!body)
|
|
186
|
+
return existing || `${CHANGELOG_HEADER}`;
|
|
187
|
+
const section = `## ${releaseDate}\n\n${body}\n`;
|
|
188
|
+
const doc = (existing || CHANGELOG_HEADER).replace(/\s+$/, "");
|
|
189
|
+
// Insert the new section immediately before the first existing `## ` release
|
|
190
|
+
// so the header/intro stays on top and releases stay newest-first.
|
|
191
|
+
const firstRelease = doc.search(/^##\s+(?!#)/m);
|
|
192
|
+
if (firstRelease === -1) {
|
|
193
|
+
return `${doc}\n\n${section}\n`;
|
|
194
|
+
}
|
|
195
|
+
const head = doc.slice(0, firstRelease).replace(/\s+$/, "");
|
|
196
|
+
const rest = doc.slice(firstRelease);
|
|
197
|
+
return `${head}\n\n${section}\n${rest}\n`;
|
|
198
|
+
}
|
|
199
|
+
export { CHANGELOG_HEADER, GROUP_LABELS };
|
|
200
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/changelog/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAkCH;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAA0B;IAC1D,OAAO;IACP,UAAU;IACV,OAAO;IACP,SAAS;IACT,SAAS;IACT,UAAU;CACX,CAAC;AAEF,MAAM,YAAY,GAAwC;IACxD,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,UAAU;CACrB,CAAC;AAEF,MAAM,QAAQ,GAAG,qBAAqB,CAAC;AAEvC,6DAA6D;AAC7D,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,KAAK;SACT,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,aAAa,CAAC,GAAuB;IAC5C,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/C,IAAK,qBAAkC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO,KAA4B,CAAC;IACtC,CAAC;IACD,oBAAoB;IACpB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,OAAO,CAAC;IAC9E,IAAI,KAAK,KAAK,aAAa,IAAI,KAAK,KAAK,aAAa,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QAC3E,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,OAAO,CAAC;IAC7E,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,YAAY;QAAE,OAAO,SAAS,CAAC;IACnE,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IAEzD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,OAAO,GAAqB,EAAE,CAAC;IACrC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,IAAI,SAAS,GAAa,EAAE,CAAC;IAE7B,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,IAAI,YAAY,KAAK,IAAI;YAAE,OAAO;QAClC,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACzD,8CAA8C;QAC9C,IAAI,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC;QAC3C,IAAI,EAAE,GAAG,IAAI,CAAC;QACd,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,OAAO,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,EAAE,GAAG,GAAG,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC;YACX,EAAE;YACF,KAAK;YACL,IAAI;YACJ,OAAO,EAAE,OAAO,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;YAC1D,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE;SAClC,CAAC,CAAC;QACH,YAAY,GAAG,IAAI,CAAC;QACpB,SAAS,GAAG,EAAE,CAAC;IACjB,CAAC,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,uDAAuD;QACvD,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,EAAE,CAAC;YACR,YAAY,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACvC,SAAS;QACX,CAAC;QACD,6EAA6E;QAC7E,IAAI,YAAY,KAAK,IAAI;YAAE,SAAS;QACpC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IACD,KAAK,EAAE,CAAC;IAER,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,sEAAsE;AACtE,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,KAAK,CAAC,OAAO,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,IAAI,IAAwB,CAAC;IAC7B,IAAI,IAAwB,CAAC;IAC7B,IAAI,IAAI,GAAG,OAAO,CAAC;IAEnB,MAAM,EAAE,GAAG,mCAAmC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7D,IAAI,EAAE,EAAE,CAAC;QACP,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,MAAM,EAAE,GAAG,8BAA8B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACpD,IAAI,CAAC,EAAE;gBAAE,SAAS;YAClB,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YACvD,IAAI,GAAG,KAAK,MAAM;gBAAE,IAAI,GAAG,KAAK,CAAC;iBAC5B,IAAI,GAAG,KAAK,MAAM;gBAAE,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;QACtE,CAAC;QACD,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,OAAO;QACL,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC;QACzB,IAAI;QACJ,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;KAClB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAgC;IAChE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAiC,CAAC;IACxD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAChC,CAAC,CAAC,4DAA4D;gBAC5D,IAAI;qBACD,KAAK,CAAC,OAAO,CAAC;qBACd,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;qBAC9C,IAAI,CAAC,IAAI,CAAC;YACf,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClB,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,qBAAqB,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,EAAE,MAAM;YAAE,SAAS;QAC5B,QAAQ,CAAC,IAAI,CAAC,OAAO,YAAY,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,gBAAgB,GACpB,iBAAiB;IACjB,oEAAoE,CAAC;AAEvE;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAgB,EAChB,OAAgC,EAChC,WAAmB;IAEnB,MAAM,IAAI,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,QAAQ,IAAI,GAAG,gBAAgB,EAAE,CAAC;IAEpD,MAAM,OAAO,GAAG,MAAM,WAAW,OAAO,IAAI,IAAI,CAAC;IAEjD,MAAM,GAAG,GAAG,CAAC,QAAQ,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/D,6EAA6E;IAC7E,mEAAmE;IACnE,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAChD,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;QACxB,OAAO,GAAG,GAAG,OAAO,OAAO,IAAI,CAAC;IAClC,CAAC;IACD,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACrC,OAAO,GAAG,IAAI,OAAO,OAAO,KAAK,IAAI,IAAI,CAAC;AAC5C,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,CAAC","sourcesContent":["/**\n * Shared, dependency-free changelog parsing + serialization.\n *\n * Used by BOTH:\n * - the browser bundle (rendering an app's CHANGELOG.md in the command\n * menu / settings \"What's new\" surface), and\n * - the `agent-native changelog` CLI (rolling pending entry files up into\n * CHANGELOG.md).\n *\n * Keep this file isomorphic: no Node, no browser, no third-party deps. The\n * markdown shape is the conventional \"Keep a Changelog\" layout — a top-level\n * `# Changelog` heading followed by one `## <release>` section per release.\n */\n\n/** A single released section of a CHANGELOG.md file. */\nexport interface ChangelogEntry {\n /** Stable id derived from the heading — used for \"unseen\" tracking. */\n id: string;\n /** Raw heading text, e.g. `2026-06-23` or `v1.2.0 — 2026-06-23`. */\n title: string;\n /** ISO date (YYYY-MM-DD) extracted from the heading, if present. */\n date?: string;\n /** Version label extracted from the heading, if present. */\n version?: string;\n /** Markdown body beneath the heading (until the next `## ` section). */\n body: string;\n}\n\n/** A not-yet-released entry authored as a `changelog/<file>.md` file. */\nexport interface PendingChangelogEntry {\n /** Category — `added`, `improved`, `fixed`, `changed`, etc. */\n type: ChangelogChangeType;\n /** ISO date the entry was authored (YYYY-MM-DD). */\n date?: string;\n /** User-facing description (markdown, single bullet). */\n text: string;\n}\n\nexport type ChangelogChangeType =\n | \"added\"\n | \"improved\"\n | \"fixed\"\n | \"changed\"\n | \"removed\"\n | \"security\";\n\n/**\n * Order changes are grouped under a release heading. Anything not listed here\n * falls back to the \"changed\" group, then renders in insertion order.\n */\nexport const CHANGELOG_GROUP_ORDER: ChangelogChangeType[] = [\n \"added\",\n \"improved\",\n \"fixed\",\n \"changed\",\n \"removed\",\n \"security\",\n];\n\nconst GROUP_LABELS: Record<ChangelogChangeType, string> = {\n added: \"Added\",\n improved: \"Improved\",\n fixed: \"Fixed\",\n changed: \"Changed\",\n removed: \"Removed\",\n security: \"Security\",\n};\n\nconst ISO_DATE = /(\\d{4}-\\d{2}-\\d{2})/;\n\n/** Lowercase, hyphenate, and strip to a URL/id-safe slug. */\nexport function changelogSlug(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60);\n}\n\nfunction normalizeType(raw: string | undefined): ChangelogChangeType {\n const value = (raw ?? \"\").trim().toLowerCase();\n if ((CHANGELOG_GROUP_ORDER as string[]).includes(value)) {\n return value as ChangelogChangeType;\n }\n // Friendly aliases.\n if (value === \"feature\" || value === \"new\" || value === \"add\") return \"added\";\n if (value === \"improvement\" || value === \"enhancement\" || value === \"perf\") {\n return \"improved\";\n }\n if (value === \"fix\" || value === \"bugfix\" || value === \"bug\") return \"fixed\";\n if (value === \"remove\" || value === \"deprecated\") return \"removed\";\n return \"changed\";\n}\n\n/**\n * Parse a CHANGELOG.md document into structured release entries.\n *\n * Tolerant by design: an empty or malformed file yields an empty list rather\n * than throwing, so a missing/partial changelog never breaks the UI.\n */\nexport function parseChangelog(markdown: string): ChangelogEntry[] {\n if (!markdown || typeof markdown !== \"string\") return [];\n\n const lines = markdown.split(/\\r?\\n/);\n const entries: ChangelogEntry[] = [];\n const seenIds = new Set<string>();\n\n let currentTitle: string | null = null;\n let bodyLines: string[] = [];\n\n const flush = () => {\n if (currentTitle === null) return;\n const title = currentTitle.trim();\n const date = title.match(ISO_DATE)?.[1];\n const version = title.match(/v?\\d+\\.\\d+(?:\\.\\d+)?/)?.[0];\n // Build a stable, unique id from the heading.\n let base = changelogSlug(title) || \"entry\";\n let id = base;\n let n = 2;\n while (seenIds.has(id)) id = `${base}-${n++}`;\n seenIds.add(id);\n entries.push({\n id,\n title,\n date,\n version: version && version !== date ? version : undefined,\n body: bodyLines.join(\"\\n\").trim(),\n });\n currentTitle = null;\n bodyLines = [];\n };\n\n for (const line of lines) {\n // `## ` (but not `### `) starts a new release section.\n const match = /^##\\s+(?!#)(.+?)\\s*$/.exec(line);\n if (match) {\n flush();\n currentTitle = stripBrackets(match[1]);\n continue;\n }\n // Skip the top-level `# Changelog` title and anything before the first `##`.\n if (currentTitle === null) continue;\n bodyLines.push(line);\n }\n flush();\n\n return entries;\n}\n\n/** `## [1.2.0]` → `1.2.0`; leaves un-bracketed headings untouched. */\nfunction stripBrackets(title: string): string {\n return title.replace(/^\\[(.+?)\\]\\s*/, \"$1 \").trim();\n}\n\n/**\n * Parse a pending `changelog/<file>.md` entry: optional `---` frontmatter\n * (`type:` / `date:`) followed by the markdown description body.\n */\nexport function parsePendingEntry(content: string): PendingChangelogEntry {\n let type: string | undefined;\n let date: string | undefined;\n let body = content;\n\n const fm = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/.exec(content);\n if (fm) {\n for (const raw of fm[1].split(/\\r?\\n/)) {\n const kv = /^([a-zA-Z]+)\\s*:\\s*(.+?)\\s*$/.exec(raw);\n if (!kv) continue;\n const key = kv[1].toLowerCase();\n const value = kv[2].replace(/^[\"']|[\"']$/g, \"\").trim();\n if (key === \"type\") type = value;\n else if (key === \"date\") date = value.match(ISO_DATE)?.[1] ?? value;\n }\n body = content.slice(fm[0].length);\n }\n\n return {\n type: normalizeType(type),\n date,\n text: body.trim(),\n };\n}\n\n/**\n * Render a set of pending entries as a single dated release section (the body\n * that goes beneath a `## <date>` heading). Groups bullets by type.\n */\nexport function renderReleaseBody(entries: PendingChangelogEntry[]): string {\n const groups = new Map<ChangelogChangeType, string[]>();\n for (const entry of entries) {\n const text = entry.text.trim();\n if (!text) continue;\n const bullet = text.includes(\"\\n\")\n ? // Preserve multi-line bodies, indenting continuation lines.\n text\n .split(/\\r?\\n/)\n .map((l, i) => (i === 0 ? `- ${l}` : ` ${l}`))\n .join(\"\\n\")\n : `- ${text}`;\n const list = groups.get(entry.type) ?? [];\n list.push(bullet);\n groups.set(entry.type, list);\n }\n\n const sections: string[] = [];\n for (const type of CHANGELOG_GROUP_ORDER) {\n const list = groups.get(type);\n if (!list?.length) continue;\n sections.push(`### ${GROUP_LABELS[type]}\\n\\n${list.join(\"\\n\")}`);\n }\n return sections.join(\"\\n\\n\");\n}\n\nconst CHANGELOG_HEADER =\n \"# Changelog\\n\\n\" +\n \"All notable user-facing changes to this app are documented here.\\n\";\n\n/**\n * Roll a batch of pending entries into an existing CHANGELOG.md document,\n * prepending a new `## <date>` section above the most recent release. Returns\n * the full updated document. Pure — the CLI handles file IO and deletion.\n */\nexport function rollupChangelog(\n existing: string,\n pending: PendingChangelogEntry[],\n releaseDate: string,\n): string {\n const body = renderReleaseBody(pending);\n if (!body) return existing || `${CHANGELOG_HEADER}`;\n\n const section = `## ${releaseDate}\\n\\n${body}\\n`;\n\n const doc = (existing || CHANGELOG_HEADER).replace(/\\s+$/, \"\");\n // Insert the new section immediately before the first existing `## ` release\n // so the header/intro stays on top and releases stay newest-first.\n const firstRelease = doc.search(/^##\\s+(?!#)/m);\n if (firstRelease === -1) {\n return `${doc}\\n\\n${section}\\n`;\n }\n const head = doc.slice(0, firstRelease).replace(/\\s+$/, \"\");\n const rest = doc.slice(firstRelease);\n return `${head}\\n\\n${section}\\n${rest}\\n`;\n}\n\nexport { CHANGELOG_HEADER, GROUP_LABELS };\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/cli/changelog.ts"],"names":[],"mappings":"AAiBA,OAAO,EAKL,gBAAgB,EAEjB,MAAM,uBAAuB,CAAC;AAsJ/B,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBlE;AAGD,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agent-native changelog` — author and roll up an app's user-facing changelog.
|
|
3
|
+
*
|
|
4
|
+
* The model mirrors changesets: instead of editing the shared CHANGELOG.md
|
|
5
|
+
* directly (which conflicts when many agents work in parallel), each change
|
|
6
|
+
* drops a small pending entry file under `changelog/`. A later `release` rolls
|
|
7
|
+
* every pending file up into a single dated section of CHANGELOG.md.
|
|
8
|
+
*
|
|
9
|
+
* agent-native changelog add "Recordings can be trimmed before sharing" --type added
|
|
10
|
+
* agent-native changelog release # roll pending → CHANGELOG.md (today)
|
|
11
|
+
* agent-native changelog list # show pending + released
|
|
12
|
+
*
|
|
13
|
+
* Runs in the current app directory (process.cwd()).
|
|
14
|
+
*/
|
|
15
|
+
import fs from "fs";
|
|
16
|
+
import path from "path";
|
|
17
|
+
import { parsePendingEntry, parseChangelog, rollupChangelog, changelogSlug, CHANGELOG_HEADER, } from "../changelog/parse.js";
|
|
18
|
+
const CHANGELOG_FILE = "CHANGELOG.md";
|
|
19
|
+
const PENDING_DIR = "changelog";
|
|
20
|
+
function todayIso() {
|
|
21
|
+
// Local date in YYYY-MM-DD. The CLI is the one place a wall-clock read is
|
|
22
|
+
// appropriate (unlike workflow scripts), so `new Date()` is fine here.
|
|
23
|
+
const d = new Date();
|
|
24
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
25
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
|
26
|
+
}
|
|
27
|
+
/** Minimal flag parser: supports `--key value` and `--key=value`. */
|
|
28
|
+
function parseFlags(args) {
|
|
29
|
+
const flags = {};
|
|
30
|
+
const rest = [];
|
|
31
|
+
for (let i = 0; i < args.length; i++) {
|
|
32
|
+
const arg = args[i];
|
|
33
|
+
if (arg.startsWith("--")) {
|
|
34
|
+
const eq = arg.indexOf("=");
|
|
35
|
+
if (eq !== -1) {
|
|
36
|
+
flags[arg.slice(2, eq)] = arg.slice(eq + 1);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const next = args[i + 1];
|
|
40
|
+
if (next && !next.startsWith("--")) {
|
|
41
|
+
flags[arg.slice(2)] = next;
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
flags[arg.slice(2)] = "true";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
rest.push(arg);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { flags, rest };
|
|
54
|
+
}
|
|
55
|
+
function printUsage() {
|
|
56
|
+
console.log([
|
|
57
|
+
"Usage:",
|
|
58
|
+
' agent-native changelog add "<summary>" [--type added|improved|fixed|changed|removed|security] [--date YYYY-MM-DD]',
|
|
59
|
+
" agent-native changelog release [--date YYYY-MM-DD]",
|
|
60
|
+
" agent-native changelog list",
|
|
61
|
+
"",
|
|
62
|
+
"Entries are user-facing notes. `add` writes a pending file under",
|
|
63
|
+
` ${PENDING_DIR}/; \`release\` rolls all pending files into ${CHANGELOG_FILE}.`,
|
|
64
|
+
].join("\n"));
|
|
65
|
+
}
|
|
66
|
+
function cmdAdd(args) {
|
|
67
|
+
const { flags, rest } = parseFlags(args);
|
|
68
|
+
const summary = rest.join(" ").trim();
|
|
69
|
+
if (!summary) {
|
|
70
|
+
console.error('Provide a summary, e.g. changelog add "Faster search"');
|
|
71
|
+
return 1;
|
|
72
|
+
}
|
|
73
|
+
const type = (flags.type ?? "changed");
|
|
74
|
+
const date = flags.date?.match(/\d{4}-\d{2}-\d{2}/)?.[0] ?? todayIso();
|
|
75
|
+
const dir = path.resolve(PENDING_DIR);
|
|
76
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
77
|
+
const slug = changelogSlug(summary) || "entry";
|
|
78
|
+
let file = path.join(dir, `${date}-${slug}.md`);
|
|
79
|
+
let n = 2;
|
|
80
|
+
while (fs.existsSync(file)) {
|
|
81
|
+
file = path.join(dir, `${date}-${slug}-${n++}.md`);
|
|
82
|
+
}
|
|
83
|
+
// Blank line after the closing frontmatter delimiter so the generated file
|
|
84
|
+
// is already Prettier-clean (Markdown requires a blank line before body text)
|
|
85
|
+
// and never trips the repo fmt check.
|
|
86
|
+
const content = `---\ntype: ${type}\ndate: ${date}\n---\n\n${summary}\n`;
|
|
87
|
+
fs.writeFileSync(file, content, "utf-8");
|
|
88
|
+
console.log(`Added changelog entry: ${path.relative(process.cwd(), file)}`);
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
function readPending() {
|
|
92
|
+
const dir = path.resolve(PENDING_DIR);
|
|
93
|
+
if (!fs.existsSync(dir))
|
|
94
|
+
return [];
|
|
95
|
+
return fs
|
|
96
|
+
.readdirSync(dir)
|
|
97
|
+
.filter((f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md")
|
|
98
|
+
.sort()
|
|
99
|
+
.map((f) => ({
|
|
100
|
+
file: path.join(dir, f),
|
|
101
|
+
content: fs.readFileSync(path.join(dir, f), "utf-8"),
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
function cmdRelease(args) {
|
|
105
|
+
const { flags } = parseFlags(args);
|
|
106
|
+
const date = flags.date?.match(/\d{4}-\d{2}-\d{2}/)?.[0] ?? todayIso();
|
|
107
|
+
const pendingFiles = readPending();
|
|
108
|
+
if (pendingFiles.length === 0) {
|
|
109
|
+
console.log("No pending changelog entries to release.");
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
const pending = pendingFiles.map((p) => parsePendingEntry(p.content));
|
|
113
|
+
const changelogPath = path.resolve(CHANGELOG_FILE);
|
|
114
|
+
const existing = fs.existsSync(changelogPath)
|
|
115
|
+
? fs.readFileSync(changelogPath, "utf-8")
|
|
116
|
+
: "";
|
|
117
|
+
const next = rollupChangelog(existing, pending, date);
|
|
118
|
+
fs.writeFileSync(changelogPath, next, "utf-8");
|
|
119
|
+
// Remove the pending entries now that they're released.
|
|
120
|
+
for (const p of pendingFiles)
|
|
121
|
+
fs.rmSync(p.file);
|
|
122
|
+
console.log(`Released ${pendingFiles.length} entr${pendingFiles.length === 1 ? "y" : "ies"} into ${CHANGELOG_FILE} (## ${date}).`);
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
function cmdList() {
|
|
126
|
+
const pending = readPending();
|
|
127
|
+
const changelogPath = path.resolve(CHANGELOG_FILE);
|
|
128
|
+
const released = fs.existsSync(changelogPath)
|
|
129
|
+
? parseChangelog(fs.readFileSync(changelogPath, "utf-8"))
|
|
130
|
+
: [];
|
|
131
|
+
console.log(`Pending (${pending.length}):`);
|
|
132
|
+
for (const p of pending) {
|
|
133
|
+
const entry = parsePendingEntry(p.content);
|
|
134
|
+
console.log(` - [${entry.type}] ${entry.text.split("\n")[0]} (${path.basename(p.file)})`);
|
|
135
|
+
}
|
|
136
|
+
console.log("");
|
|
137
|
+
console.log(`Released (${released.length}):`);
|
|
138
|
+
for (const r of released.slice(0, 10))
|
|
139
|
+
console.log(` - ${r.title}`);
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
export async function runChangelog(args) {
|
|
143
|
+
const sub = args[0];
|
|
144
|
+
const rest = args.slice(1);
|
|
145
|
+
switch (sub) {
|
|
146
|
+
case "add":
|
|
147
|
+
return cmdAdd(rest);
|
|
148
|
+
case "release":
|
|
149
|
+
return cmdRelease(rest);
|
|
150
|
+
case "list":
|
|
151
|
+
return cmdList();
|
|
152
|
+
case undefined:
|
|
153
|
+
case "help":
|
|
154
|
+
case "--help":
|
|
155
|
+
case "-h":
|
|
156
|
+
printUsage();
|
|
157
|
+
return 0;
|
|
158
|
+
default:
|
|
159
|
+
console.error(`Unknown changelog subcommand: ${sub}\n`);
|
|
160
|
+
printUsage();
|
|
161
|
+
return 1;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Re-export header for callers that want to seed an empty CHANGELOG.md.
|
|
165
|
+
export { CHANGELOG_HEADER };
|
|
166
|
+
//# sourceMappingURL=changelog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/cli/changelog.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `agent-native changelog` — author and roll up an app's user-facing changelog.\n *\n * The model mirrors changesets: instead of editing the shared CHANGELOG.md\n * directly (which conflicts when many agents work in parallel), each change\n * drops a small pending entry file under `changelog/`. A later `release` rolls\n * every pending file up into a single dated section of CHANGELOG.md.\n *\n * agent-native changelog add \"Recordings can be trimmed before sharing\" --type added\n * agent-native changelog release # roll pending → CHANGELOG.md (today)\n * agent-native changelog list # show pending + released\n *\n * Runs in the current app directory (process.cwd()).\n */\nimport fs from \"fs\";\nimport path from \"path\";\n\nimport {\n parsePendingEntry,\n parseChangelog,\n rollupChangelog,\n changelogSlug,\n CHANGELOG_HEADER,\n type ChangelogChangeType,\n} from \"../changelog/parse.js\";\n\nconst CHANGELOG_FILE = \"CHANGELOG.md\";\nconst PENDING_DIR = \"changelog\";\n\nfunction todayIso(): string {\n // Local date in YYYY-MM-DD. The CLI is the one place a wall-clock read is\n // appropriate (unlike workflow scripts), so `new Date()` is fine here.\n const d = new Date();\n const pad = (n: number) => String(n).padStart(2, \"0\");\n return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;\n}\n\n/** Minimal flag parser: supports `--key value` and `--key=value`. */\nfunction parseFlags(args: string[]): {\n flags: Record<string, string>;\n rest: string[];\n} {\n const flags: Record<string, string> = {};\n const rest: string[] = [];\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg.startsWith(\"--\")) {\n const eq = arg.indexOf(\"=\");\n if (eq !== -1) {\n flags[arg.slice(2, eq)] = arg.slice(eq + 1);\n } else {\n const next = args[i + 1];\n if (next && !next.startsWith(\"--\")) {\n flags[arg.slice(2)] = next;\n i++;\n } else {\n flags[arg.slice(2)] = \"true\";\n }\n }\n } else {\n rest.push(arg);\n }\n }\n return { flags, rest };\n}\n\nfunction printUsage(): void {\n console.log(\n [\n \"Usage:\",\n ' agent-native changelog add \"<summary>\" [--type added|improved|fixed|changed|removed|security] [--date YYYY-MM-DD]',\n \" agent-native changelog release [--date YYYY-MM-DD]\",\n \" agent-native changelog list\",\n \"\",\n \"Entries are user-facing notes. `add` writes a pending file under\",\n ` ${PENDING_DIR}/; \\`release\\` rolls all pending files into ${CHANGELOG_FILE}.`,\n ].join(\"\\n\"),\n );\n}\n\nfunction cmdAdd(args: string[]): number {\n const { flags, rest } = parseFlags(args);\n const summary = rest.join(\" \").trim();\n if (!summary) {\n console.error('Provide a summary, e.g. changelog add \"Faster search\"');\n return 1;\n }\n const type = (flags.type ?? \"changed\") as ChangelogChangeType;\n const date = flags.date?.match(/\\d{4}-\\d{2}-\\d{2}/)?.[0] ?? todayIso();\n\n const dir = path.resolve(PENDING_DIR);\n fs.mkdirSync(dir, { recursive: true });\n\n const slug = changelogSlug(summary) || \"entry\";\n let file = path.join(dir, `${date}-${slug}.md`);\n let n = 2;\n while (fs.existsSync(file)) {\n file = path.join(dir, `${date}-${slug}-${n++}.md`);\n }\n\n // Blank line after the closing frontmatter delimiter so the generated file\n // is already Prettier-clean (Markdown requires a blank line before body text)\n // and never trips the repo fmt check.\n const content = `---\\ntype: ${type}\\ndate: ${date}\\n---\\n\\n${summary}\\n`;\n fs.writeFileSync(file, content, \"utf-8\");\n console.log(`Added changelog entry: ${path.relative(process.cwd(), file)}`);\n return 0;\n}\n\nfunction readPending(): { file: string; content: string }[] {\n const dir = path.resolve(PENDING_DIR);\n if (!fs.existsSync(dir)) return [];\n return fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(\".md\") && f.toLowerCase() !== \"readme.md\")\n .sort()\n .map((f) => ({\n file: path.join(dir, f),\n content: fs.readFileSync(path.join(dir, f), \"utf-8\"),\n }));\n}\n\nfunction cmdRelease(args: string[]): number {\n const { flags } = parseFlags(args);\n const date = flags.date?.match(/\\d{4}-\\d{2}-\\d{2}/)?.[0] ?? todayIso();\n\n const pendingFiles = readPending();\n if (pendingFiles.length === 0) {\n console.log(\"No pending changelog entries to release.\");\n return 0;\n }\n\n const pending = pendingFiles.map((p) => parsePendingEntry(p.content));\n const changelogPath = path.resolve(CHANGELOG_FILE);\n const existing = fs.existsSync(changelogPath)\n ? fs.readFileSync(changelogPath, \"utf-8\")\n : \"\";\n\n const next = rollupChangelog(existing, pending, date);\n fs.writeFileSync(changelogPath, next, \"utf-8\");\n\n // Remove the pending entries now that they're released.\n for (const p of pendingFiles) fs.rmSync(p.file);\n\n console.log(\n `Released ${pendingFiles.length} entr${\n pendingFiles.length === 1 ? \"y\" : \"ies\"\n } into ${CHANGELOG_FILE} (## ${date}).`,\n );\n return 0;\n}\n\nfunction cmdList(): number {\n const pending = readPending();\n const changelogPath = path.resolve(CHANGELOG_FILE);\n const released = fs.existsSync(changelogPath)\n ? parseChangelog(fs.readFileSync(changelogPath, \"utf-8\"))\n : [];\n\n console.log(`Pending (${pending.length}):`);\n for (const p of pending) {\n const entry = parsePendingEntry(p.content);\n console.log(\n ` - [${entry.type}] ${entry.text.split(\"\\n\")[0]} (${path.basename(\n p.file,\n )})`,\n );\n }\n console.log(\"\");\n console.log(`Released (${released.length}):`);\n for (const r of released.slice(0, 10)) console.log(` - ${r.title}`);\n return 0;\n}\n\nexport async function runChangelog(args: string[]): Promise<number> {\n const sub = args[0];\n const rest = args.slice(1);\n switch (sub) {\n case \"add\":\n return cmdAdd(rest);\n case \"release\":\n return cmdRelease(rest);\n case \"list\":\n return cmdList();\n case undefined:\n case \"help\":\n case \"--help\":\n case \"-h\":\n printUsage();\n return 0;\n default:\n console.error(`Unknown changelog subcommand: ${sub}\\n`);\n printUsage();\n return 1;\n }\n}\n\n// Re-export header for callers that want to seed an empty CHANGELOG.md.\nexport { CHANGELOG_HEADER };\n"]}
|
package/dist/cli/index.js
CHANGED
|
@@ -730,6 +730,20 @@ switch (command) {
|
|
|
730
730
|
});
|
|
731
731
|
break;
|
|
732
732
|
}
|
|
733
|
+
case "changelog": {
|
|
734
|
+
// Author and roll up the app's user-facing changelog (changeset-style
|
|
735
|
+
// pending entry files → a dated CHANGELOG.md section).
|
|
736
|
+
import("./changelog.js")
|
|
737
|
+
.then(async (m) => {
|
|
738
|
+
const code = await m.runChangelog(args);
|
|
739
|
+
process.exit(code);
|
|
740
|
+
})
|
|
741
|
+
.catch((err) => {
|
|
742
|
+
console.error(err?.message ?? err);
|
|
743
|
+
process.exit(1);
|
|
744
|
+
});
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
733
747
|
case "--version":
|
|
734
748
|
case "-v": {
|
|
735
749
|
console.log(_version);
|
|
@@ -807,6 +821,10 @@ Usage:
|
|
|
807
821
|
Pass a URL instead of a name for a generic
|
|
808
822
|
research-and-integrate blueprint. --list to
|
|
809
823
|
browse available blueprints.
|
|
824
|
+
agent-native changelog <cmd> Author the app's user-facing changelog.
|
|
825
|
+
cmds: add "<summary>" [--type added|fixed|...] |
|
|
826
|
+
release | list. Pending entries live in
|
|
827
|
+
changelog/; 'release' rolls them into CHANGELOG.md.
|
|
810
828
|
agent-native audit-agent-web Audit a public URL for agent-readable surfaces
|
|
811
829
|
agent-native eval [pattern] Run the app's evals (**/*.eval.ts, evals/*.ts)
|
|
812
830
|
and exit non-zero if any scores below its
|