@oh-my-pi/pi-coding-agent 8.1.0 → 8.2.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/CHANGELOG.md +21 -1
- package/docs/session.md +111 -46
- package/examples/custom-tools/hello/index.ts +1 -1
- package/examples/custom-tools/todo/index.ts +3 -4
- package/examples/extensions/api-demo.ts +0 -1
- package/examples/extensions/chalk-logger.ts +2 -3
- package/examples/extensions/hello.ts +0 -1
- package/examples/extensions/pirate.ts +0 -1
- package/examples/extensions/plan-mode.ts +15 -16
- package/examples/extensions/todo.ts +3 -4
- package/examples/extensions/tools.ts +1 -2
- package/examples/extensions/with-deps/index.ts +0 -1
- package/examples/hooks/auto-commit-on-exit.ts +1 -2
- package/examples/hooks/confirm-destructive.ts +0 -1
- package/examples/hooks/custom-compaction.ts +1 -2
- package/examples/hooks/dirty-repo-guard.ts +0 -1
- package/examples/hooks/file-trigger.ts +3 -4
- package/examples/hooks/git-checkpoint.ts +0 -1
- package/examples/hooks/handoff.ts +3 -4
- package/examples/hooks/permission-gate.ts +1 -2
- package/examples/hooks/protected-paths.ts +1 -2
- package/examples/hooks/qna.ts +2 -3
- package/examples/hooks/snake.ts +4 -5
- package/examples/hooks/status-line.ts +0 -1
- package/examples/sdk/01-minimal.ts +2 -3
- package/examples/sdk/02-custom-model.ts +2 -3
- package/examples/sdk/03-custom-prompt.ts +3 -4
- package/examples/sdk/04-skills.ts +2 -3
- package/examples/sdk/06-extensions.ts +1 -2
- package/examples/sdk/06-hooks.ts +6 -7
- package/examples/sdk/07-context-files.ts +0 -1
- package/examples/sdk/08-prompt-templates.ts +0 -1
- package/examples/sdk/08-slash-commands.ts +0 -1
- package/examples/sdk/09-api-keys-and-oauth.ts +0 -1
- package/examples/sdk/10-settings.ts +0 -1
- package/examples/sdk/11-sessions.ts +0 -1
- package/package.json +51 -23
- package/scripts/format-prompts.ts +0 -1
- package/src/capability/context-file.ts +2 -3
- package/src/capability/extension-module.ts +2 -3
- package/src/capability/extension.ts +2 -3
- package/src/capability/fs.ts +20 -21
- package/src/capability/hook.ts +2 -3
- package/src/capability/index.ts +15 -16
- package/src/capability/instruction.ts +2 -3
- package/src/capability/mcp.ts +2 -3
- package/src/capability/prompt.ts +2 -3
- package/src/capability/rule.ts +2 -3
- package/src/capability/settings.ts +1 -2
- package/src/capability/skill.ts +2 -3
- package/src/capability/slash-command.ts +2 -3
- package/src/capability/ssh.ts +2 -3
- package/src/capability/system-prompt.ts +2 -3
- package/src/capability/tool.ts +2 -3
- package/src/cli/args.ts +5 -6
- package/src/cli/config-cli.ts +6 -7
- package/src/cli/file-processor.ts +19 -17
- package/src/cli/jupyter-cli.ts +105 -0
- package/src/cli/list-models.ts +10 -11
- package/src/cli/plugin-cli.ts +20 -21
- package/src/cli/session-picker.ts +2 -3
- package/src/cli/setup-cli.ts +2 -3
- package/src/cli/stats-cli.ts +2 -3
- package/src/cli/update-cli.ts +25 -22
- package/src/commit/agentic/agent.ts +21 -23
- package/src/commit/agentic/fallback.ts +9 -9
- package/src/commit/agentic/index.ts +30 -38
- package/src/commit/agentic/state.ts +1 -6
- package/src/commit/agentic/tools/analyze-file.ts +15 -15
- package/src/commit/agentic/tools/git-file-diff.ts +3 -3
- package/src/commit/agentic/tools/git-hunk.ts +7 -7
- package/src/commit/agentic/tools/git-overview.ts +5 -5
- package/src/commit/agentic/tools/index.ts +14 -14
- package/src/commit/agentic/tools/propose-changelog.ts +6 -6
- package/src/commit/agentic/tools/propose-commit.ts +8 -8
- package/src/commit/agentic/tools/recent-commits.ts +2 -2
- package/src/commit/agentic/tools/split-commit.ts +19 -23
- package/src/commit/agentic/topo-sort.ts +1 -1
- package/src/commit/agentic/trivial.ts +3 -3
- package/src/commit/agentic/validation.ts +12 -12
- package/src/commit/analysis/conventional.ts +7 -11
- package/src/commit/analysis/index.ts +4 -4
- package/src/commit/analysis/scope.ts +4 -4
- package/src/commit/analysis/summary.ts +7 -9
- package/src/commit/analysis/validation.ts +1 -1
- package/src/commit/changelog/detect.ts +6 -6
- package/src/commit/changelog/generate.ts +7 -9
- package/src/commit/changelog/index.ts +13 -13
- package/src/commit/changelog/parse.ts +2 -2
- package/src/commit/cli.ts +1 -1
- package/src/commit/git/diff.ts +3 -3
- package/src/commit/git/index.ts +19 -24
- package/src/commit/index.ts +1 -1
- package/src/commit/map-reduce/index.ts +9 -9
- package/src/commit/map-reduce/map-phase.ts +19 -34
- package/src/commit/map-reduce/reduce-phase.ts +9 -11
- package/src/commit/message.ts +2 -2
- package/src/commit/model-selection.ts +3 -7
- package/src/commit/pipeline.ts +20 -22
- package/src/commit/utils/exclusions.ts +3 -3
- package/src/config/file-lock.ts +17 -7
- package/src/config/keybindings.ts +6 -8
- package/src/config/model-registry.ts +55 -37
- package/src/config/model-resolver.ts +18 -19
- package/src/config/prompt-templates.ts +11 -11
- package/src/config/settings-manager.ts +50 -34
- package/src/config.ts +60 -62
- package/src/cursor.ts +11 -9
- package/src/discovery/agents-md.ts +11 -12
- package/src/discovery/builtin.ts +68 -73
- package/src/discovery/claude.ts +41 -42
- package/src/discovery/cline.ts +11 -12
- package/src/discovery/codex.ts +52 -53
- package/src/discovery/cursor.ts +9 -10
- package/src/discovery/gemini.ts +17 -22
- package/src/discovery/github.ts +13 -14
- package/src/discovery/helpers.ts +35 -34
- package/src/discovery/index.ts +16 -18
- package/src/discovery/mcp-json.ts +8 -9
- package/src/discovery/ssh.ts +8 -9
- package/src/discovery/vscode.ts +4 -5
- package/src/discovery/windsurf.ts +6 -7
- package/src/exa/company.ts +1 -2
- package/src/exa/index.ts +2 -3
- package/src/exa/linkedin.ts +1 -2
- package/src/exa/mcp-client.ts +14 -16
- package/src/exa/render.ts +10 -11
- package/src/exa/researcher.ts +1 -2
- package/src/exa/search.ts +1 -2
- package/src/exa/types.ts +0 -1
- package/src/exa/websets.ts +1 -2
- package/src/exec/bash-executor.ts +3 -4
- package/src/exec/exec.ts +0 -1
- package/src/export/custom-share.ts +5 -6
- package/src/export/html/index.ts +24 -21
- package/src/export/ttsr.ts +2 -3
- package/src/extensibility/custom-commands/bundled/review/index.ts +7 -8
- package/src/extensibility/custom-commands/loader.ts +17 -14
- package/src/extensibility/custom-commands/types.ts +1 -2
- package/src/extensibility/custom-tools/loader.ts +10 -11
- package/src/extensibility/custom-tools/types.ts +6 -7
- package/src/extensibility/custom-tools/wrapper.ts +2 -3
- package/src/extensibility/extensions/loader.ts +75 -53
- package/src/extensibility/extensions/runner.ts +11 -12
- package/src/extensibility/extensions/types.ts +19 -26
- package/src/extensibility/extensions/wrapper.ts +3 -4
- package/src/extensibility/hooks/index.ts +1 -1
- package/src/extensibility/hooks/loader.ts +8 -9
- package/src/extensibility/hooks/runner.ts +7 -8
- package/src/extensibility/hooks/tool-wrapper.ts +0 -1
- package/src/extensibility/hooks/types.ts +10 -17
- package/src/extensibility/plugins/doctor.ts +3 -3
- package/src/extensibility/plugins/installer.ts +27 -27
- package/src/extensibility/plugins/loader.ts +59 -56
- package/src/extensibility/plugins/manager.ts +211 -171
- package/src/extensibility/plugins/parser.ts +1 -1
- package/src/extensibility/plugins/paths.ts +8 -8
- package/src/extensibility/skills.ts +63 -60
- package/src/extensibility/slash-commands.ts +10 -10
- package/src/index.ts +46 -46
- package/src/internal-urls/agent-protocol.ts +21 -11
- package/src/internal-urls/artifact-protocol.ts +17 -13
- package/src/internal-urls/router.ts +1 -2
- package/src/internal-urls/rule-protocol.ts +3 -4
- package/src/internal-urls/skill-protocol.ts +3 -4
- package/src/ipy/executor.ts +14 -10
- package/src/ipy/gateway-coordinator.ts +79 -90
- package/src/ipy/kernel.ts +32 -30
- package/src/ipy/modules.ts +13 -13
- package/src/lsp/client.ts +21 -10
- package/src/lsp/clients/biome-client.ts +1 -2
- package/src/lsp/clients/index.ts +3 -3
- package/src/lsp/clients/lsp-linter-client.ts +4 -5
- package/src/lsp/config.ts +15 -15
- package/src/lsp/edits.ts +4 -5
- package/src/lsp/index.ts +43 -44
- package/src/lsp/lspmux.ts +8 -8
- package/src/lsp/render.ts +10 -16
- package/src/lsp/utils.ts +3 -3
- package/src/main.ts +55 -34
- package/src/mcp/client.ts +2 -3
- package/src/mcp/config.ts +5 -6
- package/src/mcp/json-rpc.ts +0 -1
- package/src/mcp/loader.ts +3 -4
- package/src/mcp/manager.ts +17 -18
- package/src/mcp/tool-bridge.ts +4 -9
- package/src/mcp/tool-cache.ts +2 -3
- package/src/mcp/transports/http.ts +2 -4
- package/src/mcp/transports/stdio.ts +1 -2
- package/src/migrations.ts +60 -49
- package/src/modes/components/armin.ts +4 -5
- package/src/modes/components/assistant-message.ts +6 -6
- package/src/modes/components/bash-execution.ts +7 -8
- package/src/modes/components/bordered-loader.ts +3 -3
- package/src/modes/components/branch-summary-message.ts +3 -3
- package/src/modes/components/compaction-summary-message.ts +3 -3
- package/src/modes/components/countdown-timer.ts +0 -1
- package/src/modes/components/custom-message.ts +5 -5
- package/src/modes/components/diff.ts +1 -1
- package/src/modes/components/dynamic-border.ts +2 -2
- package/src/modes/components/extensions/extension-dashboard.ts +6 -7
- package/src/modes/components/extensions/extension-list.ts +2 -3
- package/src/modes/components/extensions/inspector-panel.ts +3 -4
- package/src/modes/components/extensions/state-manager.ts +25 -26
- package/src/modes/components/extensions/types.ts +1 -2
- package/src/modes/components/footer.ts +47 -43
- package/src/modes/components/history-search.ts +2 -2
- package/src/modes/components/hook-editor.ts +3 -4
- package/src/modes/components/hook-input.ts +2 -3
- package/src/modes/components/hook-message.ts +5 -5
- package/src/modes/components/hook-selector.ts +2 -3
- package/src/modes/components/keybinding-hints.ts +2 -3
- package/src/modes/components/login-dialog.ts +2 -2
- package/src/modes/components/model-selector.ts +12 -12
- package/src/modes/components/oauth-selector.ts +2 -2
- package/src/modes/components/plugin-settings.ts +20 -20
- package/src/modes/components/python-execution.ts +7 -8
- package/src/modes/components/queue-mode-selector.ts +3 -3
- package/src/modes/components/read-tool-group.ts +2 -2
- package/src/modes/components/session-selector.ts +4 -4
- package/src/modes/components/settings-defs.ts +77 -69
- package/src/modes/components/settings-selector.ts +16 -16
- package/src/modes/components/show-images-selector.ts +2 -2
- package/src/modes/components/status-line/segments.ts +4 -4
- package/src/modes/components/status-line/separators.ts +1 -1
- package/src/modes/components/status-line/types.ts +2 -2
- package/src/modes/components/status-line-segment-editor.ts +7 -8
- package/src/modes/components/status-line.ts +12 -12
- package/src/modes/components/theme-selector.ts +8 -7
- package/src/modes/components/thinking-selector.ts +4 -4
- package/src/modes/components/todo-display.ts +2 -2
- package/src/modes/components/todo-reminder.ts +4 -4
- package/src/modes/components/tool-execution.ts +11 -16
- package/src/modes/components/tree-selector.ts +11 -11
- package/src/modes/components/ttsr-notification.ts +5 -5
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +1 -1
- package/src/modes/components/visual-truncate.ts +0 -1
- package/src/modes/components/welcome.ts +4 -4
- package/src/modes/controllers/command-controller.ts +46 -47
- package/src/modes/controllers/event-controller.ts +16 -20
- package/src/modes/controllers/extension-ui-controller.ts +40 -46
- package/src/modes/controllers/input-controller.ts +17 -18
- package/src/modes/controllers/selector-controller.ts +103 -91
- package/src/modes/index.ts +3 -3
- package/src/modes/interactive-mode.ts +27 -29
- package/src/modes/print-mode.ts +12 -13
- package/src/modes/rpc/rpc-client.ts +7 -8
- package/src/modes/rpc/rpc-mode.ts +24 -25
- package/src/modes/rpc/rpc-types.ts +3 -4
- package/src/modes/theme/mermaid-cache.ts +2 -2
- package/src/modes/theme/theme.ts +128 -53
- package/src/modes/types.ts +10 -10
- package/src/modes/utils/ui-helpers.ts +17 -17
- package/src/patch/applicator.ts +18 -19
- package/src/patch/diff.ts +1 -2
- package/src/patch/fuzzy.ts +1 -2
- package/src/patch/index.ts +10 -11
- package/src/patch/normalize.ts +4 -4
- package/src/patch/normative.ts +1 -2
- package/src/patch/parser.ts +8 -9
- package/src/patch/shared.ts +12 -13
- package/src/sdk.ts +60 -63
- package/src/session/agent-session.ts +83 -84
- package/src/session/agent-storage.ts +11 -11
- package/src/session/artifacts.ts +8 -9
- package/src/session/auth-storage.ts +25 -29
- package/src/session/compaction/branch-summarization.ts +7 -10
- package/src/session/compaction/compaction.ts +8 -19
- package/src/session/compaction/utils.ts +6 -9
- package/src/session/history-storage.ts +10 -10
- package/src/session/messages.ts +4 -5
- package/src/session/session-manager.ts +76 -65
- package/src/session/session-storage.ts +57 -69
- package/src/session/storage-migration.ts +2 -3
- package/src/session/streaming-output.ts +2 -2
- package/src/ssh/connection-manager.ts +43 -50
- package/src/ssh/ssh-executor.ts +2 -2
- package/src/ssh/sshfs-mount.ts +11 -18
- package/src/system-prompt.ts +27 -34
- package/src/task/agents.ts +45 -30
- package/src/task/commands.ts +6 -7
- package/src/task/discovery.ts +39 -76
- package/src/task/executor.ts +14 -15
- package/src/task/index.ts +33 -36
- package/src/task/output-manager.ts +3 -4
- package/src/task/parallel.ts +0 -1
- package/src/task/render.ts +19 -20
- package/src/task/subprocess-tool-registry.ts +1 -2
- package/src/task/worker-protocol.ts +3 -3
- package/src/task/worker.ts +32 -38
- package/src/task/worktree.ts +19 -19
- package/src/tools/ask.ts +8 -9
- package/src/tools/bash-interceptor.ts +1 -5
- package/src/tools/bash.ts +19 -18
- package/src/tools/calculator.ts +12 -12
- package/src/tools/complete.ts +3 -4
- package/src/tools/context.ts +2 -2
- package/src/tools/fetch.ts +23 -26
- package/src/tools/find.ts +15 -16
- package/src/tools/gemini-image.ts +14 -14
- package/src/tools/grep.ts +27 -27
- package/src/tools/index.ts +78 -56
- package/src/tools/list-limit.ts +1 -1
- package/src/tools/ls.ts +7 -7
- package/src/tools/notebook.ts +5 -5
- package/src/tools/output-meta.ts +3 -4
- package/src/tools/output-utils.ts +1 -1
- package/src/tools/path-utils.ts +5 -5
- package/src/tools/python.ts +36 -37
- package/src/tools/read.ts +23 -23
- package/src/tools/render-utils.ts +8 -9
- package/src/tools/renderers.ts +6 -7
- package/src/tools/review.ts +8 -11
- package/src/tools/ssh.ts +31 -30
- package/src/tools/todo-write.ts +13 -13
- package/src/tools/tool-errors.ts +3 -3
- package/src/tools/tool-result.ts +3 -8
- package/src/tools/write.ts +11 -16
- package/src/tui/code-cell.ts +3 -9
- package/src/tui/file-list.ts +3 -4
- package/src/tui/output-block.ts +1 -2
- package/src/tui/status-line.ts +2 -3
- package/src/tui/tree-list.ts +2 -3
- package/src/tui/types.ts +1 -2
- package/src/tui/utils.ts +2 -3
- package/src/utils/changelog.ts +9 -10
- package/src/utils/clipboard.ts +11 -11
- package/src/utils/file-mentions.ts +4 -10
- package/src/utils/frontmatter.ts +6 -3
- package/src/utils/fuzzy.ts +2 -2
- package/src/utils/image-convert.ts +1 -1
- package/src/utils/image-resize.ts +1 -1
- package/src/utils/mime.ts +2 -2
- package/src/utils/shell-snapshot.ts +11 -13
- package/src/utils/shell.ts +4 -5
- package/src/utils/title-generator.ts +8 -9
- package/src/utils/tools-manager.ts +23 -23
- package/src/vendor/photon/index.js +1099 -1059
- package/src/vendor/photon/photon_rs_bg.wasm +0 -0
- package/src/web/scrapers/artifacthub.ts +1 -1
- package/src/web/scrapers/arxiv.ts +2 -2
- package/src/web/scrapers/bluesky.ts +2 -2
- package/src/web/scrapers/cheatsh.ts +1 -1
- package/src/web/scrapers/chocolatey.ts +2 -2
- package/src/web/scrapers/choosealicense.ts +5 -5
- package/src/web/scrapers/cisa-kev.ts +1 -1
- package/src/web/scrapers/crossref.ts +2 -2
- package/src/web/scrapers/devto.ts +3 -3
- package/src/web/scrapers/discogs.ts +3 -4
- package/src/web/scrapers/discourse.ts +1 -1
- package/src/web/scrapers/dockerhub.ts +1 -1
- package/src/web/scrapers/fdroid.ts +2 -2
- package/src/web/scrapers/firefox-addons.ts +3 -3
- package/src/web/scrapers/flathub.ts +1 -1
- package/src/web/scrapers/github.ts +3 -3
- package/src/web/scrapers/gitlab.ts +4 -4
- package/src/web/scrapers/hackernews.ts +2 -2
- package/src/web/scrapers/huggingface.ts +1 -1
- package/src/web/scrapers/iacr.ts +2 -2
- package/src/web/scrapers/index.ts +0 -1
- package/src/web/scrapers/jetbrains-marketplace.ts +1 -1
- package/src/web/scrapers/lemmy.ts +2 -2
- package/src/web/scrapers/maven.ts +2 -2
- package/src/web/scrapers/mdn.ts +2 -4
- package/src/web/scrapers/metacpan.ts +2 -2
- package/src/web/scrapers/musicbrainz.ts +1 -2
- package/src/web/scrapers/npm.ts +1 -1
- package/src/web/scrapers/nuget.ts +2 -2
- package/src/web/scrapers/nvd.ts +3 -3
- package/src/web/scrapers/ollama.ts +7 -9
- package/src/web/scrapers/opencorporates.ts +2 -2
- package/src/web/scrapers/openlibrary.ts +6 -6
- package/src/web/scrapers/orcid.ts +0 -1
- package/src/web/scrapers/osv.ts +2 -2
- package/src/web/scrapers/packagist.ts +1 -1
- package/src/web/scrapers/pubmed.ts +1 -2
- package/src/web/scrapers/rawg.ts +2 -2
- package/src/web/scrapers/readthedocs.ts +1 -2
- package/src/web/scrapers/repology.ts +2 -2
- package/src/web/scrapers/rfc.ts +1 -1
- package/src/web/scrapers/searchcode.ts +2 -2
- package/src/web/scrapers/semantic-scholar.ts +1 -1
- package/src/web/scrapers/snapcraft.ts +2 -2
- package/src/web/scrapers/sourcegraph.ts +1 -1
- package/src/web/scrapers/spdx.ts +3 -3
- package/src/web/scrapers/spotify.ts +0 -1
- package/src/web/scrapers/twitter.ts +1 -1
- package/src/web/scrapers/types.ts +1 -2
- package/src/web/scrapers/utils.ts +5 -5
- package/src/web/scrapers/wikidata.ts +3 -3
- package/src/web/scrapers/youtube.ts +9 -14
- package/src/web/search/auth.ts +4 -9
- package/src/web/search/index.ts +11 -21
- package/src/web/search/providers/anthropic.ts +3 -9
- package/src/web/search/providers/exa.ts +6 -10
- package/src/web/search/providers/perplexity.ts +5 -5
- package/src/web/search/render.ts +16 -18
- package/scripts/generate-wasm-b64.ts +0 -24
- package/src/commit/map-reduce/.map-phase.ts.kate-swp +0 -0
- package/src/task/.executor.ts.kate-swp +0 -0
- package/src/vendor/photon/photon_rs_bg.wasm.b64.js +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as path from "node:path";
|
|
2
2
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
3
|
-
import { detectChangelogBoundaries } from "@oh-my-pi/pi-coding-agent/commit/changelog/detect";
|
|
4
|
-
import { generateChangelogEntries } from "@oh-my-pi/pi-coding-agent/commit/changelog/generate";
|
|
5
|
-
import { parseUnreleasedSection } from "@oh-my-pi/pi-coding-agent/commit/changelog/parse";
|
|
6
|
-
import type { ControlledGit } from "@oh-my-pi/pi-coding-agent/commit/git";
|
|
7
3
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import type { ControlledGit } from "../../commit/git";
|
|
5
|
+
import { detectChangelogBoundaries } from "./detect";
|
|
6
|
+
import { generateChangelogEntries } from "./generate";
|
|
7
|
+
import { parseUnreleasedSection } from "./parse";
|
|
8
8
|
|
|
9
9
|
const CHANGELOG_SECTIONS = ["Breaking Changes", "Added", "Changed", "Deprecated", "Removed", "Fixed", "Security"];
|
|
10
10
|
|
|
@@ -67,7 +67,7 @@ export async function runChangelogFlow({
|
|
|
67
67
|
continue;
|
|
68
68
|
}
|
|
69
69
|
const existingEntries = formatExistingEntries(unreleased.entries);
|
|
70
|
-
const isPackageChangelog = resolve(boundary.changelogPath) !== resolve(cwd, "CHANGELOG.md");
|
|
70
|
+
const isPackageChangelog = path.resolve(boundary.changelogPath) !== path.resolve(cwd, "CHANGELOG.md");
|
|
71
71
|
const generated = await generateChangelogEntries({
|
|
72
72
|
model,
|
|
73
73
|
apiKey,
|
|
@@ -82,7 +82,7 @@ export async function runChangelogFlow({
|
|
|
82
82
|
const updatedContent = applyChangelogEntries(changelogContent, unreleased, generated.entries);
|
|
83
83
|
if (!dryRun) {
|
|
84
84
|
await Bun.write(boundary.changelogPath, updatedContent);
|
|
85
|
-
await git.stageFiles([relative(cwd, boundary.changelogPath)]);
|
|
85
|
+
await git.stageFiles([path.relative(cwd, boundary.changelogPath)]);
|
|
86
86
|
}
|
|
87
87
|
updated.push(boundary.changelogPath);
|
|
88
88
|
}
|
|
@@ -127,7 +127,7 @@ export async function applyChangelogProposals({
|
|
|
127
127
|
const updatedContent = applyChangelogEntries(changelogContent, unreleased, normalized, normalizedDeletions);
|
|
128
128
|
if (!dryRun) {
|
|
129
129
|
await Bun.write(proposal.path, updatedContent);
|
|
130
|
-
await git.stageFiles([relative(cwd, proposal.path)]);
|
|
130
|
+
await git.stageFiles([path.relative(cwd, proposal.path)]);
|
|
131
131
|
}
|
|
132
132
|
updated.push(proposal.path);
|
|
133
133
|
}
|
|
@@ -178,8 +178,8 @@ function applyDeletions(
|
|
|
178
178
|
): Record<string, string[]> {
|
|
179
179
|
const result: Record<string, string[]> = {};
|
|
180
180
|
for (const [section, items] of Object.entries(existing)) {
|
|
181
|
-
const toDelete = new Set((deletions[section] ?? []).map(
|
|
182
|
-
const filtered = items.filter(
|
|
181
|
+
const toDelete = new Set((deletions[section] ?? []).map(d => d.toLowerCase()));
|
|
182
|
+
const filtered = items.filter(item => !toDelete.has(item.toLowerCase()));
|
|
183
183
|
if (filtered.length > 0) {
|
|
184
184
|
result[section] = filtered;
|
|
185
185
|
}
|
|
@@ -194,7 +194,7 @@ function mergeEntries(
|
|
|
194
194
|
const merged: Record<string, string[]> = { ...existing };
|
|
195
195
|
for (const [section, items] of Object.entries(incoming)) {
|
|
196
196
|
const current = merged[section] ?? [];
|
|
197
|
-
const lower = new Set(current.map(
|
|
197
|
+
const lower = new Set(current.map(item => item.toLowerCase()));
|
|
198
198
|
for (const item of items) {
|
|
199
199
|
if (!lower.has(item.toLowerCase())) {
|
|
200
200
|
current.push(item);
|
|
@@ -225,9 +225,9 @@ function renderUnreleasedSections(entries: Record<string, string[]>): string[] {
|
|
|
225
225
|
function normalizeEntries(entries: Record<string, string[]>): Record<string, string[]> {
|
|
226
226
|
const result: Record<string, string[]> = {};
|
|
227
227
|
for (const [section, items] of Object.entries(entries)) {
|
|
228
|
-
const trimmed = items.map(
|
|
228
|
+
const trimmed = items.map(item => item.trim().replace(/\.$/, "")).filter(item => item.length > 0);
|
|
229
229
|
if (trimmed.length === 0) continue;
|
|
230
|
-
result[section] = Array.from(new Set(trimmed.map(
|
|
230
|
+
result[section] = Array.from(new Set(trimmed.map(item => item.trim())));
|
|
231
231
|
}
|
|
232
232
|
return result;
|
|
233
233
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { UnreleasedSection } from "
|
|
1
|
+
import type { UnreleasedSection } from "../../commit/types";
|
|
2
2
|
|
|
3
3
|
const UNRELEASED_PATTERN = /^##\s+\[?Unreleased\]?/i;
|
|
4
4
|
const SECTION_PATTERN = /^###\s+(.*)$/;
|
|
5
5
|
|
|
6
6
|
export function parseUnreleasedSection(content: string): UnreleasedSection {
|
|
7
7
|
const lines = content.split("\n");
|
|
8
|
-
const startIndex = lines.findIndex(
|
|
8
|
+
const startIndex = lines.findIndex(line => UNRELEASED_PATTERN.test(line.trim()));
|
|
9
9
|
if (startIndex === -1) {
|
|
10
10
|
throw new Error("No [Unreleased] section found in changelog");
|
|
11
11
|
}
|
package/src/commit/cli.ts
CHANGED
package/src/commit/git/diff.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DiffHunk, FileDiff, FileHunks, NumstatEntry } from "
|
|
1
|
+
import type { DiffHunk, FileDiff, FileHunks, NumstatEntry } from "../../commit/types";
|
|
2
2
|
|
|
3
3
|
export function parseNumstat(output: string): NumstatEntry[] {
|
|
4
4
|
const entries: NumstatEntry[] = [];
|
|
@@ -31,7 +31,7 @@ export function parseFileDiffs(diff: string): FileDiff[] {
|
|
|
31
31
|
if (!match) continue;
|
|
32
32
|
const filename = match[2];
|
|
33
33
|
const content = part;
|
|
34
|
-
const isBinary = lines.some(
|
|
34
|
+
const isBinary = lines.some(line => line.startsWith("Binary files "));
|
|
35
35
|
let additions = 0;
|
|
36
36
|
let deletions = 0;
|
|
37
37
|
for (const line of lines) {
|
|
@@ -52,7 +52,7 @@ export function parseFileDiffs(diff: string): FileDiff[] {
|
|
|
52
52
|
|
|
53
53
|
export function parseDiffHunks(diff: string): FileHunks[] {
|
|
54
54
|
const files = parseFileDiffs(diff);
|
|
55
|
-
return files.map(
|
|
55
|
+
return files.map(file => parseFileHunks(file));
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export function parseFileHunks(fileDiff: FileDiff): FileHunks {
|
package/src/commit/git/index.ts
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
parseDiffHunks,
|
|
7
|
-
parseFileDiffs,
|
|
8
|
-
parseFileHunks,
|
|
9
|
-
parseNumstat,
|
|
10
|
-
} from "@oh-my-pi/pi-coding-agent/commit/git/diff";
|
|
11
|
-
import { GitError } from "@oh-my-pi/pi-coding-agent/commit/git/errors";
|
|
12
|
-
import { commit, push, resetStaging, runGitCommand, stageFiles } from "@oh-my-pi/pi-coding-agent/commit/git/operations";
|
|
13
|
-
import type { FileDiff, FileHunks, NumstatEntry } from "@oh-my-pi/pi-coding-agent/commit/types";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
import * as path from "node:path";
|
|
14
5
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
6
|
+
import type { FileDiff, FileHunks, NumstatEntry } from "../../commit/types";
|
|
7
|
+
import { parseDiffHunks, parseFileDiffs, parseFileHunks, parseNumstat } from "./diff";
|
|
8
|
+
import { GitError } from "./errors";
|
|
9
|
+
import { commit, push, resetStaging, runGitCommand, stageFiles } from "./operations";
|
|
15
10
|
|
|
16
11
|
export type HunkSelection = {
|
|
17
12
|
path: string;
|
|
@@ -41,7 +36,7 @@ export class ControlledGit {
|
|
|
41
36
|
this.ensureSuccess(result, "git diff --name-only");
|
|
42
37
|
return result.stdout
|
|
43
38
|
.split("\n")
|
|
44
|
-
.map(
|
|
39
|
+
.map(line => line.trim())
|
|
45
40
|
.filter(Boolean);
|
|
46
41
|
}
|
|
47
42
|
|
|
@@ -71,7 +66,7 @@ export class ControlledGit {
|
|
|
71
66
|
this.ensureSuccess(result, "git log");
|
|
72
67
|
return result.stdout
|
|
73
68
|
.split("\n")
|
|
74
|
-
.map(
|
|
69
|
+
.map(line => line.trim())
|
|
75
70
|
.filter(Boolean);
|
|
76
71
|
}
|
|
77
72
|
|
|
@@ -80,7 +75,7 @@ export class ControlledGit {
|
|
|
80
75
|
this.ensureSuccess(result, "git diff --cached --name-only");
|
|
81
76
|
return result.stdout
|
|
82
77
|
.split("\n")
|
|
83
|
-
.map(
|
|
78
|
+
.map(line => line.trim())
|
|
84
79
|
.filter(Boolean);
|
|
85
80
|
}
|
|
86
81
|
|
|
@@ -89,7 +84,7 @@ export class ControlledGit {
|
|
|
89
84
|
this.ensureSuccess(result, "git ls-files --others --exclude-standard");
|
|
90
85
|
return result.stdout
|
|
91
86
|
.split("\n")
|
|
92
|
-
.map(
|
|
87
|
+
.map(line => line.trim())
|
|
93
88
|
.filter(Boolean);
|
|
94
89
|
}
|
|
95
90
|
|
|
@@ -107,7 +102,7 @@ export class ControlledGit {
|
|
|
107
102
|
if (selections.length === 0) return;
|
|
108
103
|
const diff = await this.getDiff(false);
|
|
109
104
|
const fileDiffs = parseFileDiffs(diff);
|
|
110
|
-
const fileDiffMap = new Map(fileDiffs.map(
|
|
105
|
+
const fileDiffMap = new Map(fileDiffs.map(entry => [entry.filename, entry]));
|
|
111
106
|
const patchParts: string[] = [];
|
|
112
107
|
for (const selection of selections) {
|
|
113
108
|
const fileDiff = fileDiffMap.get(selection.path);
|
|
@@ -133,19 +128,19 @@ export class ControlledGit {
|
|
|
133
128
|
throw new GitError("git apply --cached", `No hunks selected for ${selection.path}`);
|
|
134
129
|
}
|
|
135
130
|
const header = extractFileHeader(fileDiff.content);
|
|
136
|
-
const filePatch = [header, ...selectedHunks.map(
|
|
131
|
+
const filePatch = [header, ...selectedHunks.map(hunk => hunk.content)].join("\n");
|
|
137
132
|
patchParts.push(filePatch);
|
|
138
133
|
}
|
|
139
134
|
|
|
140
135
|
const patch = joinPatch(patchParts);
|
|
141
136
|
if (!patch.trim()) return;
|
|
142
|
-
const tempPath = join(tmpdir(), `omp-hunks-${randomUUID()}.patch`);
|
|
137
|
+
const tempPath = path.join(os.tmpdir(), `omp-hunks-${randomUUID()}.patch`);
|
|
143
138
|
try {
|
|
144
139
|
await Bun.write(tempPath, patch);
|
|
145
140
|
const result = await runGitCommand(this.cwd, ["apply", "--cached", "--binary", tempPath]);
|
|
146
141
|
this.ensureSuccess(result, "git apply --cached");
|
|
147
142
|
} finally {
|
|
148
|
-
await rm(tempPath, { force: true });
|
|
143
|
+
await fs.rm(tempPath, { force: true });
|
|
149
144
|
}
|
|
150
145
|
}
|
|
151
146
|
|
|
@@ -197,7 +192,7 @@ function extractFileHeader(diff: string): string {
|
|
|
197
192
|
|
|
198
193
|
function joinPatch(parts: string[]): string {
|
|
199
194
|
return parts
|
|
200
|
-
.map(
|
|
195
|
+
.map(part => (part.endsWith("\n") ? part : `${part}\n`))
|
|
201
196
|
.join("\n")
|
|
202
197
|
.trimEnd()
|
|
203
198
|
.concat("\n");
|
|
@@ -205,13 +200,13 @@ function joinPatch(parts: string[]): string {
|
|
|
205
200
|
|
|
206
201
|
function selectHunks(file: FileHunks, selector: HunkSelection["hunks"]): FileHunks["hunks"] {
|
|
207
202
|
if (selector.type === "indices") {
|
|
208
|
-
const wanted = new Set(selector.indices.map(
|
|
209
|
-
return file.hunks.filter(
|
|
203
|
+
const wanted = new Set(selector.indices.map(value => Math.max(1, Math.floor(value))));
|
|
204
|
+
return file.hunks.filter(hunk => wanted.has(hunk.index + 1));
|
|
210
205
|
}
|
|
211
206
|
if (selector.type === "lines") {
|
|
212
207
|
const start = Math.floor(selector.start);
|
|
213
208
|
const end = Math.floor(selector.end);
|
|
214
|
-
return file.hunks.filter(
|
|
209
|
+
return file.hunks.filter(hunk => hunk.newStart <= end && hunk.newStart + hunk.newLines - 1 >= start);
|
|
215
210
|
}
|
|
216
211
|
return file.hunks;
|
|
217
212
|
}
|
package/src/commit/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
2
|
-
import { parseFileDiffs } from "
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
2
|
+
import { parseFileDiffs } from "../../commit/git/diff";
|
|
3
|
+
import type { ConventionalAnalysis } from "../../commit/types";
|
|
4
|
+
import { isExcludedFile } from "../../commit/utils/exclusions";
|
|
5
|
+
import { runMapPhase } from "./map-phase";
|
|
6
|
+
import { runReducePhase } from "./reduce-phase";
|
|
7
|
+
import { estimateTokens } from "./utils";
|
|
8
8
|
|
|
9
9
|
const MIN_FILES_FOR_MAP_REDUCE = 4;
|
|
10
10
|
const MAX_FILE_TOKENS = 50_000;
|
|
@@ -34,10 +34,10 @@ export function shouldUseMapReduce(diff: string, settings?: MapReduceSettings):
|
|
|
34
34
|
if (settings?.enabled === false) return false;
|
|
35
35
|
const minFiles = settings?.minFiles ?? MIN_FILES_FOR_MAP_REDUCE;
|
|
36
36
|
const maxFileTokens = settings?.maxFileTokens ?? MAX_FILE_TOKENS;
|
|
37
|
-
const files = parseFileDiffs(diff).filter(
|
|
37
|
+
const files = parseFileDiffs(diff).filter(file => !isExcludedFile(file.filename));
|
|
38
38
|
const fileCount = files.length;
|
|
39
39
|
if (fileCount >= minFiles) return true;
|
|
40
|
-
return files.some(
|
|
40
|
+
return files.some(file => estimateTokens(file.content) > maxFileTokens);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -45,7 +45,7 @@ export function shouldUseMapReduce(diff: string, settings?: MapReduceSettings):
|
|
|
45
45
|
*/
|
|
46
46
|
|
|
47
47
|
export async function runMapReduceAnalysis(input: MapReduceInput): Promise<ConventionalAnalysis> {
|
|
48
|
-
const fileDiffs = parseFileDiffs(input.diff).filter(
|
|
48
|
+
const fileDiffs = parseFileDiffs(input.diff).filter(file => !isExcludedFile(file.filename));
|
|
49
49
|
const observations = await runMapPhase({
|
|
50
50
|
model: input.smolModel,
|
|
51
51
|
apiKey: input.smolApiKey,
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
1
|
+
import type { Api, AssistantMessage, Message, Model } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import { completeSimple } from "@oh-my-pi/pi-ai";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
};
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
};
|
|
10
|
-
import type { FileDiff, FileObservation } from "@oh-my-pi/pi-coding-agent/commit/types";
|
|
11
|
-
import { isExcludedFile } from "@oh-my-pi/pi-coding-agent/commit/utils/exclusions";
|
|
12
|
-
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
3
|
+
import fileObserverSystemPrompt from "../../commit/prompts/file-observer-system.md" with { type: "text" };
|
|
4
|
+
import fileObserverUserPrompt from "../../commit/prompts/file-observer-user.md" with { type: "text" };
|
|
5
|
+
import type { FileDiff, FileObservation } from "../../commit/types";
|
|
6
|
+
import { isExcludedFile } from "../../commit/utils/exclusions";
|
|
7
|
+
import { renderPromptTemplate } from "../../config/prompt-templates";
|
|
8
|
+
import { truncateToTokenLimit } from "./utils";
|
|
13
9
|
|
|
14
10
|
const MAX_FILE_TOKENS = 50_000;
|
|
15
11
|
const MAX_CONTEXT_FILES = 20;
|
|
@@ -32,14 +28,14 @@ export interface MapPhaseInput {
|
|
|
32
28
|
}
|
|
33
29
|
|
|
34
30
|
export async function runMapPhase({ model, apiKey, files, config }: MapPhaseInput): Promise<FileObservation[]> {
|
|
35
|
-
const filtered = files.filter(
|
|
31
|
+
const filtered = files.filter(file => !isExcludedFile(file.filename));
|
|
36
32
|
const systemPrompt = renderPromptTemplate(fileObserverSystemPrompt);
|
|
37
33
|
const maxFileTokens = config?.maxFileTokens ?? MAX_FILE_TOKENS;
|
|
38
34
|
const maxConcurrency = config?.maxConcurrency ?? MAX_CONCURRENCY;
|
|
39
35
|
const timeoutMs = config?.timeoutMs ?? MAP_PHASE_TIMEOUT_MS;
|
|
40
36
|
const maxRetries = config?.maxRetries ?? MAX_RETRIES;
|
|
41
37
|
const retryBackoffMs = config?.retryBackoffMs ?? RETRY_BACKOFF_MS;
|
|
42
|
-
return runWithConcurrency(filtered, maxConcurrency, async
|
|
38
|
+
return runWithConcurrency(filtered, maxConcurrency, async file => {
|
|
43
39
|
if (file.isBinary) {
|
|
44
40
|
return {
|
|
45
41
|
file: file.filename,
|
|
@@ -56,24 +52,13 @@ export async function runMapPhase({ model, apiKey, files, config }: MapPhaseInpu
|
|
|
56
52
|
diff: truncated,
|
|
57
53
|
context_header: contextHeader,
|
|
58
54
|
});
|
|
55
|
+
const request = {
|
|
56
|
+
systemPrompt,
|
|
57
|
+
messages: [{ role: "user", content: prompt, timestamp: Date.now() }] as Message[],
|
|
58
|
+
};
|
|
59
59
|
|
|
60
60
|
const response = await withRetry(
|
|
61
|
-
|
|
62
|
-
const controller = new AbortController();
|
|
63
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
64
|
-
try {
|
|
65
|
-
return await completeSimple(
|
|
66
|
-
model,
|
|
67
|
-
{
|
|
68
|
-
systemPrompt: systemPrompt,
|
|
69
|
-
messages: [{ role: "user", content: prompt, timestamp: Date.now() }],
|
|
70
|
-
},
|
|
71
|
-
{ apiKey, maxTokens: 400, signal: controller.signal },
|
|
72
|
-
);
|
|
73
|
-
} finally {
|
|
74
|
-
clearTimeout(timeout);
|
|
75
|
-
}
|
|
76
|
-
},
|
|
61
|
+
() => completeSimple(model, request, { apiKey, maxTokens: 400, signal: AbortSignal.timeout(timeoutMs) }),
|
|
77
62
|
maxRetries,
|
|
78
63
|
retryBackoffMs,
|
|
79
64
|
);
|
|
@@ -90,8 +75,8 @@ export async function runMapPhase({ model, apiKey, files, config }: MapPhaseInpu
|
|
|
90
75
|
|
|
91
76
|
function parseObservations(message: AssistantMessage): string[] {
|
|
92
77
|
const text = message.content
|
|
93
|
-
.filter(
|
|
94
|
-
.map(
|
|
78
|
+
.filter(content => content.type === "text")
|
|
79
|
+
.map(content => content.text)
|
|
95
80
|
.join("")
|
|
96
81
|
.trim();
|
|
97
82
|
|
|
@@ -99,9 +84,9 @@ function parseObservations(message: AssistantMessage): string[] {
|
|
|
99
84
|
|
|
100
85
|
const lines = text
|
|
101
86
|
.split("\n")
|
|
102
|
-
.map(
|
|
87
|
+
.map(line => line.trim())
|
|
103
88
|
.filter(Boolean)
|
|
104
|
-
.map(
|
|
89
|
+
.map(line => line.replace(/^[-*]\s+/, ""))
|
|
105
90
|
.filter(Boolean);
|
|
106
91
|
|
|
107
92
|
return lines.slice(0, 5);
|
|
@@ -112,7 +97,7 @@ function generateContextHeader(files: FileDiff[], currentFile: string): string {
|
|
|
112
97
|
return `(Large commit with ${files.length} total files)`;
|
|
113
98
|
}
|
|
114
99
|
|
|
115
|
-
const otherFiles = files.filter(
|
|
100
|
+
const otherFiles = files.filter(file => file.filename !== currentFile);
|
|
116
101
|
if (otherFiles.length === 0) return "";
|
|
117
102
|
|
|
118
103
|
const sorted = [...otherFiles].sort((a, b) => b.additions + b.deletions - (a.additions + a.deletions));
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Api, AssistantMessage, Model, ToolCall } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
|
|
3
|
-
import reduceSystemPrompt from "@oh-my-pi/pi-coding-agent/commit/prompts/reduce-system.md" with { type: "text" };
|
|
4
|
-
import reduceUserPrompt from "@oh-my-pi/pi-coding-agent/commit/prompts/reduce-user.md" with { type: "text" };
|
|
5
|
-
import type { ChangelogCategory, ConventionalAnalysis, FileObservation } from "@oh-my-pi/pi-coding-agent/commit/types";
|
|
6
|
-
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
7
3
|
import { Type } from "@sinclair/typebox";
|
|
4
|
+
import reduceSystemPrompt from "../../commit/prompts/reduce-system.md" with { type: "text" };
|
|
5
|
+
import reduceUserPrompt from "../../commit/prompts/reduce-user.md" with { type: "text" };
|
|
6
|
+
import type { ChangelogCategory, ConventionalAnalysis, FileObservation } from "../../commit/types";
|
|
7
|
+
import { renderPromptTemplate } from "../../config/prompt-templates";
|
|
8
8
|
|
|
9
9
|
const ReduceTool = {
|
|
10
10
|
name: "create_conventional_analysis",
|
|
@@ -64,7 +64,7 @@ export async function runReducePhase({
|
|
|
64
64
|
}: ReducePhaseInput): Promise<ConventionalAnalysis> {
|
|
65
65
|
const prompt = renderPromptTemplate(reduceUserPrompt, {
|
|
66
66
|
types_description: typesDescription,
|
|
67
|
-
observations: observations.flatMap(
|
|
67
|
+
observations: observations.flatMap(obs => obs.observations.map(line => `- ${obs.file}: ${line}`)).join("\n"),
|
|
68
68
|
stat,
|
|
69
69
|
scope_candidates: scopeCandidates,
|
|
70
70
|
});
|
|
@@ -123,7 +123,7 @@ function normalizeAnalysis(parsed: {
|
|
|
123
123
|
return {
|
|
124
124
|
type: parsed.type,
|
|
125
125
|
scope: parsed.scope?.trim() || null,
|
|
126
|
-
details: parsed.details.map(
|
|
126
|
+
details: parsed.details.map(detail => ({
|
|
127
127
|
text: detail.text.trim(),
|
|
128
128
|
changelogCategory: detail.user_visible ? detail.changelog_category : undefined,
|
|
129
129
|
userVisible: detail.user_visible ?? false,
|
|
@@ -133,15 +133,13 @@ function normalizeAnalysis(parsed: {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
function extractToolCall(message: AssistantMessage, name: string): ToolCall | undefined {
|
|
136
|
-
return message.content.find(
|
|
137
|
-
| ToolCall
|
|
138
|
-
| undefined;
|
|
136
|
+
return message.content.find(content => content.type === "toolCall" && content.name === name) as ToolCall | undefined;
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
function extractTextContent(message: AssistantMessage): string {
|
|
142
140
|
return message.content
|
|
143
|
-
.filter(
|
|
144
|
-
.map(
|
|
141
|
+
.filter(content => content.type === "text")
|
|
142
|
+
.map(content => content.text)
|
|
145
143
|
.join("")
|
|
146
144
|
.trim();
|
|
147
145
|
}
|
package/src/commit/message.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { ConventionalAnalysis } from "
|
|
1
|
+
import type { ConventionalAnalysis } from "./types";
|
|
2
2
|
|
|
3
3
|
export function formatCommitMessage(analysis: ConventionalAnalysis, summary: string): string {
|
|
4
4
|
const scopePart = analysis.scope ? `(${analysis.scope})` : "";
|
|
5
5
|
const header = `${analysis.type}${scopePart}: ${summary}`;
|
|
6
|
-
const bodyLines = analysis.details.map(
|
|
6
|
+
const bodyLines = analysis.details.map(detail => `- ${detail.text.trim()}`);
|
|
7
7
|
if (bodyLines.length === 0) {
|
|
8
8
|
return header;
|
|
9
9
|
}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
parseModelString,
|
|
5
|
-
SMOL_MODEL_PRIORITY,
|
|
6
|
-
} from "@oh-my-pi/pi-coding-agent/config/model-resolver";
|
|
7
|
-
import type { SettingsManager } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
|
|
2
|
+
import { parseModelPattern, parseModelString, SMOL_MODEL_PRIORITY } from "../config/model-resolver";
|
|
3
|
+
import type { SettingsManager } from "../config/settings-manager";
|
|
8
4
|
|
|
9
5
|
export async function resolvePrimaryModel(
|
|
10
6
|
override: string | undefined,
|
|
@@ -69,7 +65,7 @@ function resolveModelFromSettings(settingsManager: SettingsManager, available: M
|
|
|
69
65
|
function resolveModelFromString(value: string, available: Model<Api>[]): Model<Api> | undefined {
|
|
70
66
|
const parsed = parseModelString(value);
|
|
71
67
|
if (parsed) {
|
|
72
|
-
return available.find(
|
|
68
|
+
return available.find(model => model.provider === parsed.provider && model.id === parsed.id);
|
|
73
69
|
}
|
|
74
70
|
return parseModelPattern(value, available).model;
|
|
75
71
|
}
|
package/src/commit/pipeline.ts
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as path from "node:path";
|
|
2
2
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
3
|
-
import {
|
|
3
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
5
|
+
import { SettingsManager } from "../config/settings-manager";
|
|
6
|
+
import { discoverAuthStorage, discoverModels } from "../sdk";
|
|
7
|
+
import { loadProjectContextFiles } from "../system-prompt";
|
|
8
|
+
import { runAgenticCommit } from "./agentic";
|
|
4
9
|
import {
|
|
5
10
|
extractScopeCandidates,
|
|
6
11
|
generateConventionalAnalysis,
|
|
7
12
|
generateSummary,
|
|
8
13
|
validateAnalysis,
|
|
9
14
|
validateSummary,
|
|
10
|
-
} from "
|
|
11
|
-
import { runChangelogFlow } from "
|
|
12
|
-
import { ControlledGit } from "
|
|
13
|
-
import { runMapReduceAnalysis, shouldUseMapReduce } from "
|
|
14
|
-
import { formatCommitMessage } from "
|
|
15
|
-
import { resolvePrimaryModel, resolveSmolModel } from "
|
|
16
|
-
import summaryRetryPrompt from "
|
|
17
|
-
import typesDescriptionPrompt from "
|
|
18
|
-
|
|
19
|
-
};
|
|
20
|
-
import type { CommitCommandArgs, ConventionalAnalysis } from "@oh-my-pi/pi-coding-agent/commit/types";
|
|
21
|
-
import { renderPromptTemplate } from "@oh-my-pi/pi-coding-agent/config/prompt-templates";
|
|
22
|
-
import { SettingsManager } from "@oh-my-pi/pi-coding-agent/config/settings-manager";
|
|
23
|
-
import { discoverAuthStorage, discoverModels } from "@oh-my-pi/pi-coding-agent/sdk";
|
|
24
|
-
import { loadProjectContextFiles } from "@oh-my-pi/pi-coding-agent/system-prompt";
|
|
25
|
-
import { logger } from "@oh-my-pi/pi-utils";
|
|
15
|
+
} from "./analysis";
|
|
16
|
+
import { runChangelogFlow } from "./changelog";
|
|
17
|
+
import { ControlledGit } from "./git";
|
|
18
|
+
import { runMapReduceAnalysis, shouldUseMapReduce } from "./map-reduce";
|
|
19
|
+
import { formatCommitMessage } from "./message";
|
|
20
|
+
import { resolvePrimaryModel, resolveSmolModel } from "./model-selection";
|
|
21
|
+
import summaryRetryPrompt from "./prompts/summary-retry.md" with { type: "text" };
|
|
22
|
+
import typesDescriptionPrompt from "./prompts/types-description.md" with { type: "text" };
|
|
23
|
+
import type { CommitCommandArgs, ConventionalAnalysis } from "./types";
|
|
26
24
|
|
|
27
25
|
const SUMMARY_MAX_CHARS = 72;
|
|
28
26
|
const RECENT_COMMITS_COUNT = 8;
|
|
@@ -43,7 +41,7 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
43
41
|
const settingsManager = await SettingsManager.create(cwd);
|
|
44
42
|
const commitSettings = settingsManager.getCommitSettings();
|
|
45
43
|
const authStorage = await discoverAuthStorage();
|
|
46
|
-
const modelRegistry =
|
|
44
|
+
const modelRegistry = discoverModels(authStorage);
|
|
47
45
|
|
|
48
46
|
const { model: primaryModel, apiKey: primaryApiKey } = await resolvePrimaryModel(
|
|
49
47
|
args.model,
|
|
@@ -87,8 +85,8 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
87
85
|
const scopeCandidates = extractScopeCandidates(numstat).scopeCandidates;
|
|
88
86
|
const recentCommits = await git.getRecentCommits(RECENT_COMMITS_COUNT);
|
|
89
87
|
const contextFiles = await loadProjectContextFiles({ cwd });
|
|
90
|
-
const formattedContextFiles = contextFiles.map(
|
|
91
|
-
path: relative(cwd, file.path),
|
|
88
|
+
const formattedContextFiles = contextFiles.map(file => ({
|
|
89
|
+
path: path.relative(cwd, file.path),
|
|
92
90
|
content: file.content,
|
|
93
91
|
}));
|
|
94
92
|
|
|
@@ -209,7 +207,7 @@ async function generateSummaryWithRetry(input: {
|
|
|
209
207
|
apiKey: input.apiKey,
|
|
210
208
|
commitType: input.analysis.type,
|
|
211
209
|
scope: input.analysis.scope,
|
|
212
|
-
details: input.analysis.details.map(
|
|
210
|
+
details: input.analysis.details.map(detail => detail.text),
|
|
213
211
|
stat: input.stat,
|
|
214
212
|
maxChars: SUMMARY_MAX_CHARS,
|
|
215
213
|
userContext: context,
|
|
@@ -31,12 +31,12 @@ const EXCLUDED_SUFFIXES = [".lock.yml", ".lock.yaml", "-lock.yml", "-lock.yaml"]
|
|
|
31
31
|
|
|
32
32
|
export function isExcludedFile(path: string): boolean {
|
|
33
33
|
const lower = path.toLowerCase();
|
|
34
|
-
if (EXCLUDED_FILES.some(
|
|
34
|
+
if (EXCLUDED_FILES.some(name => lower.endsWith(name.toLowerCase()))) {
|
|
35
35
|
return true;
|
|
36
36
|
}
|
|
37
|
-
return EXCLUDED_SUFFIXES.some(
|
|
37
|
+
return EXCLUDED_SUFFIXES.some(suffix => lower.endsWith(suffix));
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export function filterExcludedFiles<T extends { filename: string }>(files: T[]): T[] {
|
|
41
|
-
return files.filter(
|
|
41
|
+
return files.filter(file => !isExcludedFile(file.filename));
|
|
42
42
|
}
|
package/src/config/file-lock.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
3
3
|
|
|
4
4
|
export interface FileLockOptions {
|
|
5
5
|
staleMs?: number;
|
|
@@ -29,7 +29,7 @@ async function writeLockInfo(lockPath: string): Promise<void> {
|
|
|
29
29
|
|
|
30
30
|
async function readLockInfo(lockPath: string): Promise<LockInfo | null> {
|
|
31
31
|
try {
|
|
32
|
-
const content = await readFile(`${lockPath}/info`, "utf-8");
|
|
32
|
+
const content = await fs.readFile(`${lockPath}/info`, "utf-8");
|
|
33
33
|
return JSON.parse(content) as LockInfo;
|
|
34
34
|
} catch {
|
|
35
35
|
return null;
|
|
@@ -58,7 +58,7 @@ async function isLockStale(lockPath: string, staleMs: number): Promise<boolean>
|
|
|
58
58
|
|
|
59
59
|
async function tryAcquireLock(lockPath: string): Promise<boolean> {
|
|
60
60
|
try {
|
|
61
|
-
await mkdir(lockPath);
|
|
61
|
+
await fs.mkdir(lockPath);
|
|
62
62
|
await writeLockInfo(lockPath);
|
|
63
63
|
return true;
|
|
64
64
|
} catch (error) {
|
|
@@ -71,12 +71,22 @@ async function tryAcquireLock(lockPath: string): Promise<boolean> {
|
|
|
71
71
|
|
|
72
72
|
async function releaseLock(lockPath: string): Promise<void> {
|
|
73
73
|
try {
|
|
74
|
-
await rm(lockPath, { recursive: true });
|
|
74
|
+
await fs.rm(lockPath, { recursive: true });
|
|
75
75
|
} catch {
|
|
76
76
|
// Ignore errors on release
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
async function lockExists(lockPath: string): Promise<boolean> {
|
|
81
|
+
try {
|
|
82
|
+
await fs.stat(lockPath);
|
|
83
|
+
return true;
|
|
84
|
+
} catch (err) {
|
|
85
|
+
if (isEnoent(err)) return false;
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
80
90
|
async function acquireLock(filePath: string, options: FileLockOptions = {}): Promise<() => Promise<void>> {
|
|
81
91
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
82
92
|
const lockPath = getLockPath(filePath);
|
|
@@ -86,12 +96,12 @@ async function acquireLock(filePath: string, options: FileLockOptions = {}): Pro
|
|
|
86
96
|
return () => releaseLock(lockPath);
|
|
87
97
|
}
|
|
88
98
|
|
|
89
|
-
if (
|
|
99
|
+
if ((await lockExists(lockPath)) && (await isLockStale(lockPath, opts.staleMs))) {
|
|
90
100
|
await releaseLock(lockPath);
|
|
91
101
|
continue;
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
await new Promise(
|
|
104
|
+
await new Promise(resolve => setTimeout(resolve, opts.retryDelayMs));
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
throw new Error(`Failed to acquire lock for ${filePath} after ${opts.retries} attempts`);
|