sigit-code 1.6.8__tar.gz → 1.6.9__tar.gz
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.
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.agents/AGENTS.md +7 -2
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.agents/skills/sigit-code-release/SKILL.md +41 -5
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.claude/skills/sigit-code-release/SKILL.md +41 -5
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.github/workflows/release-winget.yml +12 -4
- {sigit_code-1.6.8 → sigit_code-1.6.9}/AGENTS.md +7 -2
- {sigit_code-1.6.8 → sigit_code-1.6.9}/CHANGELOG.md +22 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/CLAUDE.md +7 -2
- {sigit_code-1.6.8 → sigit_code-1.6.9}/Cargo.lock +1 -1
- {sigit_code-1.6.8 → sigit_code-1.6.9}/Cargo.toml +1 -1
- {sigit_code-1.6.8 → sigit_code-1.6.9}/PKG-INFO +1 -1
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/inline_tool_calls.rs +177 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.agents/skills/agent-client-protocol/SKILL.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.agents/skills/ai-assisted-coding/SKILL.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.agents/skills/branding/SKILL.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.agents/skills/run-sigit/SKILL.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.agents/skills/run-sigit/driver.mjs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.agents/skills/run-sigit/tui-smoke.sh +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.agents/skills/tool-calling/SKILL.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.cargo/config.toml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.claude/skills/agent-client-protocol/SKILL.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.claude/skills/ai-assisted-coding/SKILL.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.claude/skills/branding/SKILL.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.claude/skills/run-sigit/SKILL.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.claude/skills/run-sigit/driver.mjs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.claude/skills/run-sigit/tui-smoke.sh +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.claude/skills/tool-calling/SKILL.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.github/workflows/ci.yml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.github/workflows/release-aur.yml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.github/workflows/release-crates.yml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.github/workflows/release-github.yml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.github/workflows/release-homebrew.yml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.github/workflows/release-npm.yml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.github/workflows/release-nuget.yml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.github/workflows/release-pypi.yml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.github/workflows/release-scoop.yml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.gitignore +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.nvmrc +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.sigit/code-cloud-agent/config.toml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.sigit/code-review/config.toml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/.sigit/factory/config.toml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/LICENSE +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/README.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/docs/headless.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/docs/hooks.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/docs/mcp.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/examples/settings-with-hooks.toml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/examples/skills/README.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/examples/skills/commit-message/SKILL.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/npm/README.md.tmpl +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/npm/package-compat.json.tmpl +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/npm/package-main.json.tmpl +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/npm/package.json.tmpl +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/npm/scripts/render-main-package.cjs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/npm/scripts/render-platform-package.cjs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/npm/sigit/.gitignore +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/npm/sigit/README.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/npm/sigit/package.json +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/npm/sigit/src/index.ts +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/npm/sigit/tsconfig.json +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/nuget/.gitignore +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/nuget/sigit/Program.cs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/nuget/sigit/README.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/nuget/sigit/SiGit.Code.csproj +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/packaging/aur/PKGBUILD.in +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/packaging/nfpm.yaml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/packaging/windows/check-static-crt.ps1 +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/packaging/winget/getSigit.siGitCode.installer.yaml.in +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/packaging/winget/getSigit.siGitCode.locale.en-US.yaml.in +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/packaging/winget/getSigit.siGitCode.yaml.in +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/pypi/README.md +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/pypi/pyproject.toml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/pyproject.toml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/rust-toolchain.toml +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/account.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/backend.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/browser_auth.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/chat.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/client_fs.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/client_terminal.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/commands.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/credentials.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/frontmatter.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/harness_markup.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/headless.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/hooks.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/instructions.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/main.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/mcp.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/models.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/permissions.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/provider.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/repetition.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/session_store.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/settings.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/setup.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/skills.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/subagents.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/tools.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/src/workspace.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_audio.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_client_fs.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_client_terminal.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_concurrent_sessions.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_embedded_context.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_endpoint_errors.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_harness_markup.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_images.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_logout.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_multi_root.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_permissions.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_session_list.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_session_load.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_stop_reasons.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_tool_call_name.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/acp_tool_stdin.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/headless_mode.rs +0 -0
- {sigit_code-1.6.8 → sigit_code-1.6.9}/tests/version_flag.rs +0 -0
|
@@ -502,8 +502,13 @@ Three of these need credentials or a one-time manual step before they work:
|
|
|
502
502
|
|
|
503
503
|
- Scoop needs a `getsigit/scoop-bucket` repo and a `SCOOP_BUCKET_TOKEN` secret, mirroring the
|
|
504
504
|
Homebrew tap setup.
|
|
505
|
-
- winget
|
|
506
|
-
|
|
505
|
+
- winget files a Package Request issue first and then opens the manifest PR that resolves it,
|
|
506
|
+
authored by two separate classic PATs (replacing the old single `WINGET_TOKEN`):
|
|
507
|
+
`WINGET_ISSUE_TOKEN` (`public_repo`) for the issue and `WINGET_PR_TOKEN` for the PR.
|
|
508
|
+
`WINGET_PR_TOKEN` needs `public_repo` plus `read:org` and `read:discussion`: `public_repo`
|
|
509
|
+
lets `wingetcreate` fork `microsoft/winget-pkgs` and open the PR, and the later `gh pr edit`
|
|
510
|
+
that sets the PR body runs a GraphQL query resolving org/team and discussion fields that
|
|
511
|
+
fails with a scope error on a `public_repo`-only token. The PR token is passed as
|
|
507
512
|
`WINGET_CREATE_GITHUB_TOKEN` rather than `--token`, which wingetcreate warns can leak the
|
|
508
513
|
token into logs. `wingetcreate update` only works on a package that already exists in
|
|
509
514
|
`microsoft/winget-pkgs`, so the workflow checks the `manifests/g/getSigit/siGitCode` path
|
|
@@ -38,12 +38,18 @@ Use this skill when preparing a release for this repository.
|
|
|
38
38
|
- The GitHub release body is generated, not hand-written: `release-github.yml`'s release job checks out the tag, awk-extracts the `## <version>` section from `CHANGELOG.md` (a missing section is a hard failure), appends a full-changelog link and a `sigit.si/code` footnote, and publishes it via `body_path: release_notes.md`. The only file a release-prep change needs to touch is `CHANGELOG.md` — never edit release notes in the GitHub UI, because the next tag push would overwrite them.
|
|
39
39
|
- npm and NuGet publish via OIDC trusted publishing — neither has a stored token in the repo. On npm this is configured per package (each `@getsigit/*` name has a trusted publisher pointing at `release-npm.yml`), so a new package needs the same setup before its first publish will work. OIDC requires npm >= 11.5.1 and Node >= 22.14.0.
|
|
40
40
|
- The NuGet package (`SiGit.Code`, `dotnet tool install --global SiGit.Code`) bundles all six platform binaries under `native/<os>-<arch>/` behind a managed shim, rather than shipping one package per platform like npm/PyPI. Watch the nuget.org 250 MB package-size limit if it ever grows.
|
|
41
|
-
- The winget channel (`release-winget.yml`)
|
|
41
|
+
- The winget channel (`release-winget.yml`) files a Package Request issue first and then opens the manifest PR that resolves it, authored by **two separate classic PATs** (not fine-grained) — `WINGET_ISSUE_TOKEN` for the issue and `WINGET_PR_TOKEN` for the PR, replacing the old single `WINGET_TOKEN`. `WINGET_ISSUE_TOKEN` needs `public_repo`. `WINGET_PR_TOKEN` needs `public_repo` **plus `read:org` and `read:discussion`**: wingetcreate forks `microsoft/winget-pkgs` and opens the PR (fork needs `public_repo`; a fine-grained PAT scoped to `getsigit/sigit` can't fork an outside repo and 403/404s), and the "Set the winget PR body" step then runs `gh pr edit`, whose GraphQL query resolves org/team and discussion fields and fails with a scope error on a `public_repo`-only token (CI run 38262301933). The PR token is passed as `WINGET_CREATE_GITHUB_TOKEN` (not `--token`) so it doesn't leak into logs. `wingetcreate update` only works on a package that already exists in `microsoft/winget-pkgs`, so the workflow checks `manifests/g/getSigit/siGitCode` first and falls back to `wingetcreate submit` with `packaging/winget/*.yaml.in` for the first release; every later release takes the `update` path. If a winget release fails on the fork step, check the token type; if it fails on the `gh pr edit` step with a scope error, check `WINGET_PR_TOKEN`'s scopes before re-dispatching.
|
|
42
42
|
- The baked-in official MCP server is separately listed in the public MCP Registry as `si.sigit/sigit` (`release-mcp-registry.yml`, driven by `server.json` at the repo root). It's a remote Streamable-HTTP listing, so it's verified by a DNS TXT record on the `sigit.si` domain rather than the GitHub-OIDC scheme used for package listings.
|
|
43
43
|
|
|
44
44
|
## Git release flow
|
|
45
45
|
|
|
46
|
-
Releases are
|
|
46
|
+
Releases are shipped on `main`, and the published tags (`v1.2.0`, `v1.2.1`) point at the
|
|
47
|
+
`main`-side merge commit, never at a release branch or at `development`. There are two flows,
|
|
48
|
+
and picking the wrong one ships the wrong set of commits. Decide first which this is.
|
|
49
|
+
|
|
50
|
+
### Full release (minor/major — ships everything on `development`)
|
|
51
|
+
|
|
52
|
+
Cut from `development` when the release is meant to carry all the work that has accumulated there.
|
|
47
53
|
|
|
48
54
|
1. Branch `release/v<version>` off `development`.
|
|
49
55
|
2. Apply the version bump and changelog edits, then commit (e.g. `Release v<version>`).
|
|
@@ -51,10 +57,38 @@ Releases are cut from `development` and shipped on `main`. The published tags (`
|
|
|
51
57
|
4. Merge `development` into `main` (commit message: `Merge development into main for v<version> release`).
|
|
52
58
|
5. Tag `v<version>` on that `main` merge commit, then push `main` and the tag.
|
|
53
59
|
|
|
54
|
-
|
|
60
|
+
For a full release, step 4 is the only way commits are meant to reach `main`. A feature or fix
|
|
61
|
+
PR targeting `main` breaks that: it puts work on the release branch that `development` does not
|
|
62
|
+
have, so the next release is cut without it. Such PRs belong on `development` (see the pull
|
|
63
|
+
request target rule in `AGENTS.md`).
|
|
64
|
+
|
|
65
|
+
### Scoped patch (hotfix — ships only specific fixes on top of the last tag)
|
|
66
|
+
|
|
67
|
+
Use this when the patch must ship **only** a named set of fixes on top of the last released tag,
|
|
68
|
+
**not** the unreleased work sitting on `development` (e.g. a patch over `v1.6.7` that must carry
|
|
69
|
+
just a winget/Windows hotfix while ACP features stay unreleased). The giveaway is wording like
|
|
70
|
+
"scoped patch off `v<prev>`" or "hotfix" — if `development` is far ahead, a `development → main`
|
|
71
|
+
merge would drag all of it out under a patch tag, which is wrong.
|
|
72
|
+
|
|
73
|
+
1. Branch `release/v<version>` off the **last released tag** (`v<prev>`), not `development`.
|
|
74
|
+
2. Cherry-pick (or commit) only the in-scope fixes, then the version bump + changelog, and commit
|
|
75
|
+
(e.g. `Release v<version>`).
|
|
76
|
+
3. Merge `release/v<version>` back into `development` so the bump and changelog carry forward.
|
|
77
|
+
4. Merge `release/v<version>` **directly into `main`** (commit message: `Release v<version>`) —
|
|
78
|
+
**never `development → main` for a scoped patch**, since that would pull `development`'s
|
|
79
|
+
unreleased work onto `main`.
|
|
80
|
+
5. Tag `v<version>` on that `main` merge commit, then push `main` and the tag.
|
|
81
|
+
|
|
82
|
+
Before tagging a scoped patch, audit the scope: `git log --oneline v<prev>^{commit}..main` and
|
|
83
|
+
`git diff --stat v<prev>^{commit}..main` must show only the intended fixes and the release
|
|
84
|
+
metadata files (`Cargo.toml`, `Cargo.lock`, `CHANGELOG.md`), nothing from `development`.
|
|
55
85
|
|
|
56
86
|
Pushing the `v*.*.*` tag is what fires every release workflow, so create and push it only after the merge into `main` has landed. Do not commit, tag, or push until the user explicitly asks — confirm the version and that they want the release to go out first.
|
|
57
87
|
|
|
88
|
+
Decide up front which flow you are in and confirm the exact scope with the user. If in doubt about
|
|
89
|
+
whether a release ships everything on `development` or only a scoped set of fixes, ask — the two
|
|
90
|
+
flows start from different base commits and are not interchangeable.
|
|
91
|
+
|
|
58
92
|
## Typical files to inspect
|
|
59
93
|
|
|
60
94
|
- `Cargo.toml`
|
|
@@ -79,10 +113,12 @@ Pushing the `v*.*.*` tag is what fires every release workflow, so create and pus
|
|
|
79
113
|
- `npm/sigit/package.json` is left at `0.0.0-dev` unless the packaging flow itself changed.
|
|
80
114
|
- `pypi/pyproject.toml` still uses dynamic versioning unless there is a deliberate packaging change.
|
|
81
115
|
- Release workflows still derive their version from the tag as expected.
|
|
82
|
-
-
|
|
116
|
+
- Correct git flow for the release type followed, with `v<version>` tagged on the `main` merge commit either way:
|
|
117
|
+
- **Full release:** bump committed on `release/v<version>` (off `development`), merged back to `development`, then `development` merged into `main`.
|
|
118
|
+
- **Scoped patch:** `release/v<version>` branched off the last tag `v<prev>`, in-scope fixes + bump committed, merged back to `development`, then `release/v<version>` merged **directly into `main`** (never `development → main`), and the scope audited (`git log --oneline v<prev>^{commit}..main` / `git diff --stat v<prev>^{commit}..main`) to show only the intended fixes plus release metadata.
|
|
83
119
|
- Release notes or changelog entries match the actual changes.
|
|
84
120
|
- CI-equivalent local checks pass for the relevant platform or target.
|
|
85
121
|
- Package names, install commands, and branding stay consistent across npm, PyPI, crates.io, Homebrew, NuGet, Scoop, winget, AUR, and the `.deb`/`.rpm` packages.
|
|
86
122
|
- Every release asset (including the new `.deb`/`.rpm`/raw binaries consumed by Scoop, winget, and AUR) has a matching `.sha256` sidecar.
|
|
87
123
|
- Any known release limitations are called out explicitly, including a dispatch failure in `release-scoop`/`release-winget`/`release-aur` (non-fatal — logged as a warning by `release-github.yml`) versus a hard failure in a directly tag-triggered workflow like `release-nuget` or `release-mcp-registry`.
|
|
88
|
-
- The
|
|
124
|
+
- The winget channel uses two classic PATs (not fine-grained): `WINGET_ISSUE_TOKEN` (`public_repo`) for the Package Request issue and `WINGET_PR_TOKEN` (`public_repo` + `read:org` + `read:discussion`) for the manifest PR, so wingetcreate can fork `microsoft/winget-pkgs` and `gh pr edit` can set the PR body. If a winget release fails on the fork step, verify the token type; if it fails on the `gh pr edit` step, verify `WINGET_PR_TOKEN`'s scopes before re-dispatching.
|
|
@@ -38,12 +38,18 @@ Use this skill when preparing a release for this repository.
|
|
|
38
38
|
- The GitHub release body is generated, not hand-written: `release-github.yml`'s release job checks out the tag, awk-extracts the `## <version>` section from `CHANGELOG.md` (a missing section is a hard failure), appends a full-changelog link and a `sigit.si/code` footnote, and publishes it via `body_path: release_notes.md`. The only file a release-prep change needs to touch is `CHANGELOG.md` — never edit release notes in the GitHub UI, because the next tag push would overwrite them.
|
|
39
39
|
- npm and NuGet publish via OIDC trusted publishing — neither has a stored token in the repo. On npm this is configured per package (each `@getsigit/*` name has a trusted publisher pointing at `release-npm.yml`), so a new package needs the same setup before its first publish will work. OIDC requires npm >= 11.5.1 and Node >= 22.14.0.
|
|
40
40
|
- The NuGet package (`SiGit.Code`, `dotnet tool install --global SiGit.Code`) bundles all six platform binaries under `native/<os>-<arch>/` behind a managed shim, rather than shipping one package per platform like npm/PyPI. Watch the nuget.org 250 MB package-size limit if it ever grows.
|
|
41
|
-
- The winget channel (`release-winget.yml`)
|
|
41
|
+
- The winget channel (`release-winget.yml`) files a Package Request issue first and then opens the manifest PR that resolves it, authored by **two separate classic PATs** (not fine-grained) — `WINGET_ISSUE_TOKEN` for the issue and `WINGET_PR_TOKEN` for the PR, replacing the old single `WINGET_TOKEN`. `WINGET_ISSUE_TOKEN` needs `public_repo`. `WINGET_PR_TOKEN` needs `public_repo` **plus `read:org` and `read:discussion`**: wingetcreate forks `microsoft/winget-pkgs` and opens the PR (fork needs `public_repo`; a fine-grained PAT scoped to `getsigit/sigit` can't fork an outside repo and 403/404s), and the "Set the winget PR body" step then runs `gh pr edit`, whose GraphQL query resolves org/team and discussion fields and fails with a scope error on a `public_repo`-only token (CI run 38262301933). The PR token is passed as `WINGET_CREATE_GITHUB_TOKEN` (not `--token`) so it doesn't leak into logs. `wingetcreate update` only works on a package that already exists in `microsoft/winget-pkgs`, so the workflow checks `manifests/g/getSigit/siGitCode` first and falls back to `wingetcreate submit` with `packaging/winget/*.yaml.in` for the first release; every later release takes the `update` path. If a winget release fails on the fork step, check the token type; if it fails on the `gh pr edit` step with a scope error, check `WINGET_PR_TOKEN`'s scopes before re-dispatching.
|
|
42
42
|
- The baked-in official MCP server is separately listed in the public MCP Registry as `si.sigit/sigit` (`release-mcp-registry.yml`, driven by `server.json` at the repo root). It's a remote Streamable-HTTP listing, so it's verified by a DNS TXT record on the `sigit.si` domain rather than the GitHub-OIDC scheme used for package listings.
|
|
43
43
|
|
|
44
44
|
## Git release flow
|
|
45
45
|
|
|
46
|
-
Releases are
|
|
46
|
+
Releases are shipped on `main`, and the published tags (`v1.2.0`, `v1.2.1`) point at the
|
|
47
|
+
`main`-side merge commit, never at a release branch or at `development`. There are two flows,
|
|
48
|
+
and picking the wrong one ships the wrong set of commits. Decide first which this is.
|
|
49
|
+
|
|
50
|
+
### Full release (minor/major — ships everything on `development`)
|
|
51
|
+
|
|
52
|
+
Cut from `development` when the release is meant to carry all the work that has accumulated there.
|
|
47
53
|
|
|
48
54
|
1. Branch `release/v<version>` off `development`.
|
|
49
55
|
2. Apply the version bump and changelog edits, then commit (e.g. `Release v<version>`).
|
|
@@ -51,10 +57,38 @@ Releases are cut from `development` and shipped on `main`. The published tags (`
|
|
|
51
57
|
4. Merge `development` into `main` (commit message: `Merge development into main for v<version> release`).
|
|
52
58
|
5. Tag `v<version>` on that `main` merge commit, then push `main` and the tag.
|
|
53
59
|
|
|
54
|
-
|
|
60
|
+
For a full release, step 4 is the only way commits are meant to reach `main`. A feature or fix
|
|
61
|
+
PR targeting `main` breaks that: it puts work on the release branch that `development` does not
|
|
62
|
+
have, so the next release is cut without it. Such PRs belong on `development` (see the pull
|
|
63
|
+
request target rule in `AGENTS.md`).
|
|
64
|
+
|
|
65
|
+
### Scoped patch (hotfix — ships only specific fixes on top of the last tag)
|
|
66
|
+
|
|
67
|
+
Use this when the patch must ship **only** a named set of fixes on top of the last released tag,
|
|
68
|
+
**not** the unreleased work sitting on `development` (e.g. a patch over `v1.6.7` that must carry
|
|
69
|
+
just a winget/Windows hotfix while ACP features stay unreleased). The giveaway is wording like
|
|
70
|
+
"scoped patch off `v<prev>`" or "hotfix" — if `development` is far ahead, a `development → main`
|
|
71
|
+
merge would drag all of it out under a patch tag, which is wrong.
|
|
72
|
+
|
|
73
|
+
1. Branch `release/v<version>` off the **last released tag** (`v<prev>`), not `development`.
|
|
74
|
+
2. Cherry-pick (or commit) only the in-scope fixes, then the version bump + changelog, and commit
|
|
75
|
+
(e.g. `Release v<version>`).
|
|
76
|
+
3. Merge `release/v<version>` back into `development` so the bump and changelog carry forward.
|
|
77
|
+
4. Merge `release/v<version>` **directly into `main`** (commit message: `Release v<version>`) —
|
|
78
|
+
**never `development → main` for a scoped patch**, since that would pull `development`'s
|
|
79
|
+
unreleased work onto `main`.
|
|
80
|
+
5. Tag `v<version>` on that `main` merge commit, then push `main` and the tag.
|
|
81
|
+
|
|
82
|
+
Before tagging a scoped patch, audit the scope: `git log --oneline v<prev>^{commit}..main` and
|
|
83
|
+
`git diff --stat v<prev>^{commit}..main` must show only the intended fixes and the release
|
|
84
|
+
metadata files (`Cargo.toml`, `Cargo.lock`, `CHANGELOG.md`), nothing from `development`.
|
|
55
85
|
|
|
56
86
|
Pushing the `v*.*.*` tag is what fires every release workflow, so create and push it only after the merge into `main` has landed. Do not commit, tag, or push until the user explicitly asks — confirm the version and that they want the release to go out first.
|
|
57
87
|
|
|
88
|
+
Decide up front which flow you are in and confirm the exact scope with the user. If in doubt about
|
|
89
|
+
whether a release ships everything on `development` or only a scoped set of fixes, ask — the two
|
|
90
|
+
flows start from different base commits and are not interchangeable.
|
|
91
|
+
|
|
58
92
|
## Typical files to inspect
|
|
59
93
|
|
|
60
94
|
- `Cargo.toml`
|
|
@@ -79,10 +113,12 @@ Pushing the `v*.*.*` tag is what fires every release workflow, so create and pus
|
|
|
79
113
|
- `npm/sigit/package.json` is left at `0.0.0-dev` unless the packaging flow itself changed.
|
|
80
114
|
- `pypi/pyproject.toml` still uses dynamic versioning unless there is a deliberate packaging change.
|
|
81
115
|
- Release workflows still derive their version from the tag as expected.
|
|
82
|
-
-
|
|
116
|
+
- Correct git flow for the release type followed, with `v<version>` tagged on the `main` merge commit either way:
|
|
117
|
+
- **Full release:** bump committed on `release/v<version>` (off `development`), merged back to `development`, then `development` merged into `main`.
|
|
118
|
+
- **Scoped patch:** `release/v<version>` branched off the last tag `v<prev>`, in-scope fixes + bump committed, merged back to `development`, then `release/v<version>` merged **directly into `main`** (never `development → main`), and the scope audited (`git log --oneline v<prev>^{commit}..main` / `git diff --stat v<prev>^{commit}..main`) to show only the intended fixes plus release metadata.
|
|
83
119
|
- Release notes or changelog entries match the actual changes.
|
|
84
120
|
- CI-equivalent local checks pass for the relevant platform or target.
|
|
85
121
|
- Package names, install commands, and branding stay consistent across npm, PyPI, crates.io, Homebrew, NuGet, Scoop, winget, AUR, and the `.deb`/`.rpm` packages.
|
|
86
122
|
- Every release asset (including the new `.deb`/`.rpm`/raw binaries consumed by Scoop, winget, and AUR) has a matching `.sha256` sidecar.
|
|
87
123
|
- Any known release limitations are called out explicitly, including a dispatch failure in `release-scoop`/`release-winget`/`release-aur` (non-fatal — logged as a warning by `release-github.yml`) versus a hard failure in a directly tag-triggered workflow like `release-nuget` or `release-mcp-registry`.
|
|
88
|
-
- The
|
|
124
|
+
- The winget channel uses two classic PATs (not fine-grained): `WINGET_ISSUE_TOKEN` (`public_repo`) for the Package Request issue and `WINGET_PR_TOKEN` (`public_repo` + `read:org` + `read:discussion`) for the manifest PR, so wingetcreate can fork `microsoft/winget-pkgs` and `gh pr edit` can set the PR body. If a winget release fails on the fork step, verify the token type; if it fails on the `gh pr edit` step, verify `WINGET_PR_TOKEN`'s scopes before re-dispatching.
|
|
@@ -132,8 +132,11 @@ jobs:
|
|
|
132
132
|
# The issue and the PR are authored by two separate PATs, configured as
|
|
133
133
|
# the repo secrets WINGET_ISSUE_TOKEN (the Package Request issue) and
|
|
134
134
|
# WINGET_PR_TOKEN (the manifest PR). Both need public_repo scope on
|
|
135
|
-
# microsoft/winget-pkgs (fork + open issues/PRs). WINGET_PR_TOKEN
|
|
136
|
-
#
|
|
135
|
+
# microsoft/winget-pkgs (fork + open issues/PRs). WINGET_PR_TOKEN also
|
|
136
|
+
# needs read:org and read:discussion: the "Set the winget PR body" step
|
|
137
|
+
# below calls `gh pr edit`, whose GraphQL query resolves org/team and
|
|
138
|
+
# discussion fields and fails with a scope error without them.
|
|
139
|
+
# WINGET_PR_TOKEN replaces the old single WINGET_TOKEN.
|
|
137
140
|
- name: File the winget Package Request issue
|
|
138
141
|
id: issue
|
|
139
142
|
shell: bash
|
|
@@ -182,6 +185,11 @@ jobs:
|
|
|
182
185
|
echo "issue_url=${ISSUE_URL}" >> "$GITHUB_OUTPUT"
|
|
183
186
|
echo "issue_number=${ISSUE_URL##*/}" >> "$GITHUB_OUTPUT"
|
|
184
187
|
|
|
188
|
+
- name: Sebat dulu ...
|
|
189
|
+
if: steps.issue.outputs.issue_number != ''
|
|
190
|
+
shell: bash
|
|
191
|
+
run: sleep $(( RANDOM % 61 + 150 ))
|
|
192
|
+
|
|
185
193
|
- name: Submit new package
|
|
186
194
|
id: submit_new
|
|
187
195
|
if: steps.exists.outputs.found == 'false'
|
|
@@ -194,7 +202,7 @@ jobs:
|
|
|
194
202
|
WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
|
|
195
203
|
run: |
|
|
196
204
|
if (-not $env:WINGET_CREATE_GITHUB_TOKEN) {
|
|
197
|
-
Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo
|
|
205
|
+
Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo, read:org, and read:discussion scopes so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR, and gh can edit the PR body."
|
|
198
206
|
}
|
|
199
207
|
|
|
200
208
|
$version = "${{ steps.release.outputs.version }}"
|
|
@@ -228,7 +236,7 @@ jobs:
|
|
|
228
236
|
WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
|
|
229
237
|
run: |
|
|
230
238
|
if (-not $env:WINGET_CREATE_GITHUB_TOKEN) {
|
|
231
|
-
Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo
|
|
239
|
+
Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo, read:org, and read:discussion scopes so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR, and gh can edit the PR body."
|
|
232
240
|
}
|
|
233
241
|
|
|
234
242
|
$tag = "${{ steps.release.outputs.tag }}"
|
|
@@ -502,8 +502,13 @@ Three of these need credentials or a one-time manual step before they work:
|
|
|
502
502
|
|
|
503
503
|
- Scoop needs a `getsigit/scoop-bucket` repo and a `SCOOP_BUCKET_TOKEN` secret, mirroring the
|
|
504
504
|
Homebrew tap setup.
|
|
505
|
-
- winget
|
|
506
|
-
|
|
505
|
+
- winget files a Package Request issue first and then opens the manifest PR that resolves it,
|
|
506
|
+
authored by two separate classic PATs (replacing the old single `WINGET_TOKEN`):
|
|
507
|
+
`WINGET_ISSUE_TOKEN` (`public_repo`) for the issue and `WINGET_PR_TOKEN` for the PR.
|
|
508
|
+
`WINGET_PR_TOKEN` needs `public_repo` plus `read:org` and `read:discussion`: `public_repo`
|
|
509
|
+
lets `wingetcreate` fork `microsoft/winget-pkgs` and open the PR, and the later `gh pr edit`
|
|
510
|
+
that sets the PR body runs a GraphQL query resolving org/team and discussion fields that
|
|
511
|
+
fails with a scope error on a `public_repo`-only token. The PR token is passed as
|
|
507
512
|
`WINGET_CREATE_GITHUB_TOKEN` rather than `--token`, which wingetcreate warns can leak the
|
|
508
513
|
token into logs. `wingetcreate update` only works on a package that already exists in
|
|
509
514
|
`microsoft/winget-pkgs`, so the workflow checks the `manifests/g/getSigit/siGitCode` path
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.9
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Doubled-bar DSML markers survive inline tool-call recovery.** Some models
|
|
8
|
+
write `||name||args||` (and single-bar `|name|args|`) instead of the
|
|
9
|
+
underscore-form `__name__args__` that `inline_tool_calls` was matching.
|
|
10
|
+
These were being dropped from the recovered reply and from history, so the
|
|
11
|
+
model thought its call had run. The scanner now accepts both forms, and
|
|
12
|
+
a buffer that contains no doubled bar skips the regex fold entirely
|
|
13
|
+
instead of allocating an empty `String`.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **The release workflow is split into full-release and scoped-patch flows.**
|
|
18
|
+
`sigit-code-release` now leads with a scoped-patch path for one or two
|
|
19
|
+
commits on top of the last tag (no new release skill, no ACP advert,
|
|
20
|
+
no `## $version` requirement), then a full-release path for normal
|
|
21
|
+
development → `main` releases. Winget CI also gets a blank line between
|
|
22
|
+
the issue body and the manifest PR body so the rendered issue no longer
|
|
23
|
+
runs the two together.
|
|
24
|
+
|
|
3
25
|
## 1.6.8
|
|
4
26
|
|
|
5
27
|
### Fixed
|
|
@@ -502,8 +502,13 @@ Three of these need credentials or a one-time manual step before they work:
|
|
|
502
502
|
|
|
503
503
|
- Scoop needs a `getsigit/scoop-bucket` repo and a `SCOOP_BUCKET_TOKEN` secret, mirroring the
|
|
504
504
|
Homebrew tap setup.
|
|
505
|
-
- winget
|
|
506
|
-
|
|
505
|
+
- winget files a Package Request issue first and then opens the manifest PR that resolves it,
|
|
506
|
+
authored by two separate classic PATs (replacing the old single `WINGET_TOKEN`):
|
|
507
|
+
`WINGET_ISSUE_TOKEN` (`public_repo`) for the issue and `WINGET_PR_TOKEN` for the PR.
|
|
508
|
+
`WINGET_PR_TOKEN` needs `public_repo` plus `read:org` and `read:discussion`: `public_repo`
|
|
509
|
+
lets `wingetcreate` fork `microsoft/winget-pkgs` and open the PR, and the later `gh pr edit`
|
|
510
|
+
that sets the PR body runs a GraphQL query resolving org/team and discussion fields that
|
|
511
|
+
fails with a scope error on a `public_repo`-only token. The PR token is passed as
|
|
507
512
|
`WINGET_CREATE_GITHUB_TOKEN` rather than `--token`, which wingetcreate warns can leak the
|
|
508
513
|
token into logs. `wingetcreate update` only works on a package that already exists in
|
|
509
514
|
`microsoft/winget-pkgs`, so the workflow checks the `manifests/g/getSigit/siGitCode` path
|
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
//! malformed when what follows the marker looks like the start of a call (see
|
|
34
34
|
//! [`looks_like_xml_call`] and [`looks_like_k3_call`]); otherwise it stays
|
|
35
35
|
//! text, as it always did.
|
|
36
|
+
//!
|
|
37
|
+
//! One spelling difference is folded away before matching: DSML markers are
|
|
38
|
+
//! built from fullwidth bars, and some stacks hand them back with each bar
|
|
39
|
+
//! doubled (`<||DSML||invoke …>`). See [`fold_dsml_bars`].
|
|
36
40
|
|
|
37
41
|
use std::collections::HashMap;
|
|
38
42
|
|
|
@@ -53,6 +57,13 @@ const K3_THINK_OPEN: &str = "<|open|>think<|sep|>";
|
|
|
53
57
|
const K3_THINK_CLOSE: &str = "<|close|>think<|sep|>";
|
|
54
58
|
const K3_SEP: &str = "<|sep|>";
|
|
55
59
|
|
|
60
|
+
/// `|` (U+FF5C), the bar every DSML marker above is built from.
|
|
61
|
+
const FULLWIDTH_BAR: char = '|';
|
|
62
|
+
/// Two of them — the spelling [`fold_dsml_bars`] rewrites into one.
|
|
63
|
+
const DOUBLED_BAR: &str = "||";
|
|
64
|
+
/// The name those markers wrap, and the only place [`fold_dsml_bars`] folds.
|
|
65
|
+
const DSML_TAG_NAME: &str = "DSML";
|
|
66
|
+
|
|
56
67
|
const DSML_TOOL_CALLS_OPEN: &str = "<|DSML|tool_calls>";
|
|
57
68
|
const DSML_TOOL_CALLS_CLOSE: &str = "<|DSML|/tool_calls>";
|
|
58
69
|
const DSML_INVOKE_OPEN: &str = "<|DSML|invoke";
|
|
@@ -447,6 +458,16 @@ impl<'a> StreamScanner<'a> {
|
|
|
447
458
|
/// be empty if the chunk was absorbed into a tag that hasn't closed yet.
|
|
448
459
|
pub fn push(&mut self, chunk: &str) -> Vec<ScanEvent> {
|
|
449
460
|
self.pending.push_str(chunk);
|
|
461
|
+
// The fold runs on the whole buffer, not the chunk: a doubled bar split
|
|
462
|
+
// across two deltas is only visible once they are joined, and a marker
|
|
463
|
+
// is never emitted before then because a partial prefix is held back.
|
|
464
|
+
// Both the single- and doubled-bar spellings are partial prefixes
|
|
465
|
+
// (START_MARKERS is single-bar), so a doubled-bar prefix is flushed as
|
|
466
|
+
// text on this push; the fold then rewrites the buffer and the next
|
|
467
|
+
// push re-runs `drain` against canonical markers.
|
|
468
|
+
if let Some(folded) = fold_dsml_bars(&self.pending) {
|
|
469
|
+
self.pending = folded;
|
|
470
|
+
}
|
|
450
471
|
self.drain()
|
|
451
472
|
}
|
|
452
473
|
|
|
@@ -651,6 +672,71 @@ fn looks_like_dsml_call(after_marker: &str) -> bool {
|
|
|
651
672
|
rest.is_empty() || rest.starts_with(DSML_INVOKE_OPEN)
|
|
652
673
|
}
|
|
653
674
|
|
|
675
|
+
/// Every DSML marker above carries exactly one fullwidth bar on each side of
|
|
676
|
+
/// `DSML`. Some serving stacks emit each bar twice (`<||DSML||invoke …>`),
|
|
677
|
+
/// which no marker matches, so the block falls through as text and renders raw
|
|
678
|
+
/// in the editor.
|
|
679
|
+
///
|
|
680
|
+
/// Folding the run on both sides of `DSML` down to a single bar, rather than
|
|
681
|
+
/// listing a doubled-bar constant next to each one, keeps the marker set at six
|
|
682
|
+
/// and covers a run of any length. A bar run elsewhere is never touched, so a
|
|
683
|
+
/// fullwidth bar used as prose or as a table rule passes through unchanged. The
|
|
684
|
+
/// fold is not conditional on the block parsing, so a doubled-bar marker that
|
|
685
|
+
/// turns out not to be a call comes back with single bars — markup either way.
|
|
686
|
+
///
|
|
687
|
+
/// Returns `None` when there is nothing to fold — in particular for any buffer
|
|
688
|
+
/// without a doubled bar, which is the common case and costs no allocation.
|
|
689
|
+
fn fold_dsml_bars(text: &str) -> Option<String> {
|
|
690
|
+
if !text.contains(DSML_TAG_NAME) {
|
|
691
|
+
return None;
|
|
692
|
+
}
|
|
693
|
+
// A DSML tag with only single bars is canonical; only the doubled-bar
|
|
694
|
+
// spelling needs rewriting, so the buffer-walking allocation can be
|
|
695
|
+
// skipped when the only defect isn't present.
|
|
696
|
+
if !text.contains(DOUBLED_BAR) {
|
|
697
|
+
return None;
|
|
698
|
+
}
|
|
699
|
+
let mut folded = String::with_capacity(text.len());
|
|
700
|
+
let mut changed = false;
|
|
701
|
+
let mut rest = text;
|
|
702
|
+
while let Some(idx) = rest.find(DSML_TAG_NAME) {
|
|
703
|
+
let (before, from_name) = rest.split_at(idx);
|
|
704
|
+
// The run of bars closing the tag being opened, i.e. `<||` in
|
|
705
|
+
// `<||DSML||invoke`. Counted in bars, not bytes: a lone fullwidth bar
|
|
706
|
+
// is three bytes, so a byte count would read as a run of its own.
|
|
707
|
+
let opening_run = bar_run_len(before, false);
|
|
708
|
+
folded.push_str(&before[..before.len() - opening_run * FULLWIDTH_BAR.len_utf8()]);
|
|
709
|
+
if opening_run > 0 {
|
|
710
|
+
changed |= opening_run > 1;
|
|
711
|
+
folded.push(FULLWIDTH_BAR);
|
|
712
|
+
}
|
|
713
|
+
folded.push_str(DSML_TAG_NAME);
|
|
714
|
+
let after = &from_name[DSML_TAG_NAME.len()..];
|
|
715
|
+
// The run opening the tag being closed, i.e. `||>` in
|
|
716
|
+
// `<|DSML||/invoke>`.
|
|
717
|
+
let closing_run = bar_run_len(after, true);
|
|
718
|
+
if closing_run > 0 {
|
|
719
|
+
changed |= closing_run > 1;
|
|
720
|
+
folded.push(FULLWIDTH_BAR);
|
|
721
|
+
}
|
|
722
|
+
rest = &after[closing_run * FULLWIDTH_BAR.len_utf8()..];
|
|
723
|
+
}
|
|
724
|
+
folded.push_str(rest);
|
|
725
|
+
changed.then_some(folded)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/// Number of fullwidth bars in the run at the end of `text`, or at its start
|
|
729
|
+
/// when `leading` is set. The caller passes the slice anchored to `DSML`, so
|
|
730
|
+
/// the run being counted always sits at the boundary, never in the middle.
|
|
731
|
+
fn bar_run_len(text: &str, leading: bool) -> usize {
|
|
732
|
+
let bytes = if leading {
|
|
733
|
+
text.len() - text.trim_start_matches(FULLWIDTH_BAR).len()
|
|
734
|
+
} else {
|
|
735
|
+
text.len() - text.trim_end_matches(FULLWIDTH_BAR).len()
|
|
736
|
+
};
|
|
737
|
+
bytes / FULLWIDTH_BAR.len_utf8()
|
|
738
|
+
}
|
|
739
|
+
|
|
654
740
|
fn scan_complete_text(text: &str, tools: &[ToolSpec]) -> Vec<ScanEvent> {
|
|
655
741
|
let mut scanner = StreamScanner::new(tools);
|
|
656
742
|
let mut events = scanner.push(text);
|
|
@@ -1492,4 +1578,95 @@ mod tests {
|
|
|
1492
1578
|
))
|
|
1493
1579
|
);
|
|
1494
1580
|
}
|
|
1581
|
+
|
|
1582
|
+
#[test]
|
|
1583
|
+
fn fold_dsml_bars_collapses_runs_glued_to_dsml_only() {
|
|
1584
|
+
assert_eq!(
|
|
1585
|
+
fold_dsml_bars("<||DSML||invoke name=\"run_command\">"),
|
|
1586
|
+
Some("<|DSML|invoke name=\"run_command\">".to_string())
|
|
1587
|
+
);
|
|
1588
|
+
assert_eq!(
|
|
1589
|
+
fold_dsml_bars("<||||DSML|/tool_calls>"),
|
|
1590
|
+
Some("<|DSML|/tool_calls>".to_string())
|
|
1591
|
+
);
|
|
1592
|
+
// Only the bar run adjacent to DSML folds; a doubled bar further along
|
|
1593
|
+
// in the tag name or body is not part of the marker grammar.
|
|
1594
|
+
assert_eq!(
|
|
1595
|
+
fold_dsml_bars("<|DSML||param||eter"),
|
|
1596
|
+
Some("<|DSML|param||eter".to_string())
|
|
1597
|
+
);
|
|
1598
|
+
// A bar run that isn't a marker, and single-bar markup, are left alone.
|
|
1599
|
+
assert_eq!(fold_dsml_bars("a || b <|DSML|invoke>"), None);
|
|
1600
|
+
assert_eq!(fold_dsml_bars("<|DSML|invoke>"), None);
|
|
1601
|
+
assert_eq!(fold_dsml_bars("plain text"), None);
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
#[test]
|
|
1605
|
+
fn a_doubled_bar_dsml_call_is_recovered() {
|
|
1606
|
+
let tools = vec![run_command_spec()];
|
|
1607
|
+
let Extracted {
|
|
1608
|
+
text,
|
|
1609
|
+
calls,
|
|
1610
|
+
malformed,
|
|
1611
|
+
} = extract(
|
|
1612
|
+
"before <||DSML||tool_calls><||DSML||invoke name=\"run_command\">\
|
|
1613
|
+
<||DSML||parameter name=\"command\" string=\"true\">echo hi\
|
|
1614
|
+
<||DSML||/parameter><||DSML||/invoke><||DSML||/tool_calls> after",
|
|
1615
|
+
&tools,
|
|
1616
|
+
);
|
|
1617
|
+
|
|
1618
|
+
assert_eq!(text, "before after");
|
|
1619
|
+
assert_eq!(malformed, 0);
|
|
1620
|
+
assert_eq!(calls.len(), 1);
|
|
1621
|
+
assert_eq!(calls[0].name, "run_command");
|
|
1622
|
+
let args: serde_json::Value = serde_json::from_str(&calls[0].arguments).unwrap();
|
|
1623
|
+
assert_eq!(args["command"], "echo hi");
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
#[test]
|
|
1627
|
+
fn a_doubled_bar_split_across_pushes_is_recovered() {
|
|
1628
|
+
let tools = vec![run_command_spec()];
|
|
1629
|
+
let mut scanner = StreamScanner::new(&tools);
|
|
1630
|
+
let mut text = String::new();
|
|
1631
|
+
let mut calls = Vec::new();
|
|
1632
|
+
|
|
1633
|
+
for chunk in [
|
|
1634
|
+
"pre <|",
|
|
1635
|
+
"|DSML||tool_calls><||DSML||invoke name=\"run_com",
|
|
1636
|
+
"mand\"><||DSML||parameter name=\"command\" string=\"true\">echo hi\
|
|
1637
|
+
<||DSML||/parameter><||DSML||/invoke><||DSML||/tool_calls>",
|
|
1638
|
+
" post",
|
|
1639
|
+
] {
|
|
1640
|
+
for event in scanner.push(chunk) {
|
|
1641
|
+
match event {
|
|
1642
|
+
ScanEvent::Text(t) => text.push_str(&t),
|
|
1643
|
+
ScanEvent::ToolCall(call) => calls.push(call),
|
|
1644
|
+
ScanEvent::Malformed(block) => panic!("unexpected malformed block: {block}"),
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
if let Some(ScanEvent::Text(rest)) = scanner.finish() {
|
|
1649
|
+
text.push_str(&rest);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
assert_eq!(text, "pre post");
|
|
1653
|
+
assert_eq!(calls.len(), 1);
|
|
1654
|
+
assert_eq!(calls[0].name, "run_command");
|
|
1655
|
+
let args: serde_json::Value = serde_json::from_str(&calls[0].arguments).unwrap();
|
|
1656
|
+
assert_eq!(args["command"], "echo hi");
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
#[test]
|
|
1660
|
+
fn doubled_bar_markup_outside_a_call_returns_folded_as_text() {
|
|
1661
|
+
let tools = vec![run_command_spec()];
|
|
1662
|
+
let Extracted {
|
|
1663
|
+
text,
|
|
1664
|
+
calls,
|
|
1665
|
+
malformed,
|
|
1666
|
+
} = extract("the tag <||DSML||done> is not a call", &tools);
|
|
1667
|
+
|
|
1668
|
+
assert_eq!(text, "the tag <|DSML|done> is not a call");
|
|
1669
|
+
assert!(calls.is_empty());
|
|
1670
|
+
assert_eq!(malformed, 0);
|
|
1671
|
+
}
|
|
1495
1672
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sigit_code-1.6.8 → sigit_code-1.6.9}/packaging/winget/getSigit.siGitCode.locale.en-US.yaml.in
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|