sigit-code 1.6.6__tar.gz → 1.6.8__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.
Files changed (118) hide show
  1. {sigit_code-1.6.6 → sigit_code-1.6.8}/.agents/AGENTS.md +5 -1
  2. sigit_code-1.6.8/.github/workflows/release-winget.yml +320 -0
  3. {sigit_code-1.6.6 → sigit_code-1.6.8}/AGENTS.md +5 -1
  4. {sigit_code-1.6.6 → sigit_code-1.6.8}/CHANGELOG.md +36 -1
  5. {sigit_code-1.6.6 → sigit_code-1.6.8}/CLAUDE.md +5 -1
  6. {sigit_code-1.6.6 → sigit_code-1.6.8}/Cargo.lock +1 -1
  7. {sigit_code-1.6.6 → sigit_code-1.6.8}/Cargo.toml +1 -1
  8. {sigit_code-1.6.6 → sigit_code-1.6.8}/PKG-INFO +1 -1
  9. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/main.rs +158 -16
  10. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_audio.rs +45 -0
  11. sigit_code-1.6.6/.github/workflows/release-winget.yml +0 -178
  12. {sigit_code-1.6.6 → sigit_code-1.6.8}/.agents/skills/agent-client-protocol/SKILL.md +0 -0
  13. {sigit_code-1.6.6 → sigit_code-1.6.8}/.agents/skills/ai-assisted-coding/SKILL.md +0 -0
  14. {sigit_code-1.6.6 → sigit_code-1.6.8}/.agents/skills/branding/SKILL.md +0 -0
  15. {sigit_code-1.6.6 → sigit_code-1.6.8}/.agents/skills/run-sigit/SKILL.md +0 -0
  16. {sigit_code-1.6.6 → sigit_code-1.6.8}/.agents/skills/run-sigit/driver.mjs +0 -0
  17. {sigit_code-1.6.6 → sigit_code-1.6.8}/.agents/skills/run-sigit/tui-smoke.sh +0 -0
  18. {sigit_code-1.6.6 → sigit_code-1.6.8}/.agents/skills/sigit-code-release/SKILL.md +0 -0
  19. {sigit_code-1.6.6 → sigit_code-1.6.8}/.agents/skills/tool-calling/SKILL.md +0 -0
  20. {sigit_code-1.6.6 → sigit_code-1.6.8}/.cargo/config.toml +0 -0
  21. {sigit_code-1.6.6 → sigit_code-1.6.8}/.claude/skills/agent-client-protocol/SKILL.md +0 -0
  22. {sigit_code-1.6.6 → sigit_code-1.6.8}/.claude/skills/ai-assisted-coding/SKILL.md +0 -0
  23. {sigit_code-1.6.6 → sigit_code-1.6.8}/.claude/skills/branding/SKILL.md +0 -0
  24. {sigit_code-1.6.6 → sigit_code-1.6.8}/.claude/skills/run-sigit/SKILL.md +0 -0
  25. {sigit_code-1.6.6 → sigit_code-1.6.8}/.claude/skills/run-sigit/driver.mjs +0 -0
  26. {sigit_code-1.6.6 → sigit_code-1.6.8}/.claude/skills/run-sigit/tui-smoke.sh +0 -0
  27. {sigit_code-1.6.6 → sigit_code-1.6.8}/.claude/skills/sigit-code-release/SKILL.md +0 -0
  28. {sigit_code-1.6.6 → sigit_code-1.6.8}/.claude/skills/tool-calling/SKILL.md +0 -0
  29. {sigit_code-1.6.6 → sigit_code-1.6.8}/.github/workflows/ci.yml +0 -0
  30. {sigit_code-1.6.6 → sigit_code-1.6.8}/.github/workflows/release-aur.yml +0 -0
  31. {sigit_code-1.6.6 → sigit_code-1.6.8}/.github/workflows/release-crates.yml +0 -0
  32. {sigit_code-1.6.6 → sigit_code-1.6.8}/.github/workflows/release-github.yml +0 -0
  33. {sigit_code-1.6.6 → sigit_code-1.6.8}/.github/workflows/release-homebrew.yml +0 -0
  34. {sigit_code-1.6.6 → sigit_code-1.6.8}/.github/workflows/release-npm.yml +0 -0
  35. {sigit_code-1.6.6 → sigit_code-1.6.8}/.github/workflows/release-nuget.yml +0 -0
  36. {sigit_code-1.6.6 → sigit_code-1.6.8}/.github/workflows/release-pypi.yml +0 -0
  37. {sigit_code-1.6.6 → sigit_code-1.6.8}/.github/workflows/release-scoop.yml +0 -0
  38. {sigit_code-1.6.6 → sigit_code-1.6.8}/.gitignore +0 -0
  39. {sigit_code-1.6.6 → sigit_code-1.6.8}/.nvmrc +0 -0
  40. {sigit_code-1.6.6 → sigit_code-1.6.8}/.sigit/code-cloud-agent/config.toml +0 -0
  41. {sigit_code-1.6.6 → sigit_code-1.6.8}/.sigit/code-review/config.toml +0 -0
  42. {sigit_code-1.6.6 → sigit_code-1.6.8}/.sigit/factory/config.toml +0 -0
  43. {sigit_code-1.6.6 → sigit_code-1.6.8}/LICENSE +0 -0
  44. {sigit_code-1.6.6 → sigit_code-1.6.8}/README.md +0 -0
  45. {sigit_code-1.6.6 → sigit_code-1.6.8}/docs/headless.md +0 -0
  46. {sigit_code-1.6.6 → sigit_code-1.6.8}/docs/hooks.md +0 -0
  47. {sigit_code-1.6.6 → sigit_code-1.6.8}/docs/mcp.md +0 -0
  48. {sigit_code-1.6.6 → sigit_code-1.6.8}/examples/settings-with-hooks.toml +0 -0
  49. {sigit_code-1.6.6 → sigit_code-1.6.8}/examples/skills/README.md +0 -0
  50. {sigit_code-1.6.6 → sigit_code-1.6.8}/examples/skills/commit-message/SKILL.md +0 -0
  51. {sigit_code-1.6.6 → sigit_code-1.6.8}/npm/README.md.tmpl +0 -0
  52. {sigit_code-1.6.6 → sigit_code-1.6.8}/npm/package-compat.json.tmpl +0 -0
  53. {sigit_code-1.6.6 → sigit_code-1.6.8}/npm/package-main.json.tmpl +0 -0
  54. {sigit_code-1.6.6 → sigit_code-1.6.8}/npm/package.json.tmpl +0 -0
  55. {sigit_code-1.6.6 → sigit_code-1.6.8}/npm/scripts/render-main-package.cjs +0 -0
  56. {sigit_code-1.6.6 → sigit_code-1.6.8}/npm/scripts/render-platform-package.cjs +0 -0
  57. {sigit_code-1.6.6 → sigit_code-1.6.8}/npm/sigit/.gitignore +0 -0
  58. {sigit_code-1.6.6 → sigit_code-1.6.8}/npm/sigit/README.md +0 -0
  59. {sigit_code-1.6.6 → sigit_code-1.6.8}/npm/sigit/package.json +0 -0
  60. {sigit_code-1.6.6 → sigit_code-1.6.8}/npm/sigit/src/index.ts +0 -0
  61. {sigit_code-1.6.6 → sigit_code-1.6.8}/npm/sigit/tsconfig.json +0 -0
  62. {sigit_code-1.6.6 → sigit_code-1.6.8}/nuget/.gitignore +0 -0
  63. {sigit_code-1.6.6 → sigit_code-1.6.8}/nuget/sigit/Program.cs +0 -0
  64. {sigit_code-1.6.6 → sigit_code-1.6.8}/nuget/sigit/README.md +0 -0
  65. {sigit_code-1.6.6 → sigit_code-1.6.8}/nuget/sigit/SiGit.Code.csproj +0 -0
  66. {sigit_code-1.6.6 → sigit_code-1.6.8}/packaging/aur/PKGBUILD.in +0 -0
  67. {sigit_code-1.6.6 → sigit_code-1.6.8}/packaging/nfpm.yaml +0 -0
  68. {sigit_code-1.6.6 → sigit_code-1.6.8}/packaging/windows/check-static-crt.ps1 +0 -0
  69. {sigit_code-1.6.6 → sigit_code-1.6.8}/packaging/winget/getSigit.siGitCode.installer.yaml.in +0 -0
  70. {sigit_code-1.6.6 → sigit_code-1.6.8}/packaging/winget/getSigit.siGitCode.locale.en-US.yaml.in +0 -0
  71. {sigit_code-1.6.6 → sigit_code-1.6.8}/packaging/winget/getSigit.siGitCode.yaml.in +0 -0
  72. {sigit_code-1.6.6 → sigit_code-1.6.8}/pypi/README.md +0 -0
  73. {sigit_code-1.6.6 → sigit_code-1.6.8}/pypi/pyproject.toml +0 -0
  74. {sigit_code-1.6.6 → sigit_code-1.6.8}/pyproject.toml +0 -0
  75. {sigit_code-1.6.6 → sigit_code-1.6.8}/rust-toolchain.toml +0 -0
  76. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/account.rs +0 -0
  77. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/backend.rs +0 -0
  78. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/browser_auth.rs +0 -0
  79. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/chat.rs +0 -0
  80. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/client_fs.rs +0 -0
  81. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/client_terminal.rs +0 -0
  82. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/commands.rs +0 -0
  83. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/credentials.rs +0 -0
  84. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/frontmatter.rs +0 -0
  85. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/harness_markup.rs +0 -0
  86. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/headless.rs +0 -0
  87. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/hooks.rs +0 -0
  88. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/inline_tool_calls.rs +0 -0
  89. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/instructions.rs +0 -0
  90. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/mcp.rs +0 -0
  91. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/models.rs +0 -0
  92. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/permissions.rs +0 -0
  93. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/provider.rs +0 -0
  94. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/repetition.rs +0 -0
  95. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/session_store.rs +0 -0
  96. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/settings.rs +0 -0
  97. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/setup.rs +0 -0
  98. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/skills.rs +0 -0
  99. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/subagents.rs +0 -0
  100. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/tools.rs +0 -0
  101. {sigit_code-1.6.6 → sigit_code-1.6.8}/src/workspace.rs +0 -0
  102. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_client_fs.rs +0 -0
  103. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_client_terminal.rs +0 -0
  104. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_concurrent_sessions.rs +0 -0
  105. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_embedded_context.rs +0 -0
  106. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_endpoint_errors.rs +0 -0
  107. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_harness_markup.rs +0 -0
  108. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_images.rs +0 -0
  109. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_logout.rs +0 -0
  110. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_multi_root.rs +0 -0
  111. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_permissions.rs +0 -0
  112. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_session_list.rs +0 -0
  113. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_session_load.rs +0 -0
  114. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_stop_reasons.rs +0 -0
  115. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_tool_call_name.rs +0 -0
  116. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/acp_tool_stdin.rs +0 -0
  117. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/headless_mode.rs +0 -0
  118. {sigit_code-1.6.6 → sigit_code-1.6.8}/tests/version_flag.rs +0 -0
@@ -310,7 +310,11 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
310
310
  conversation); lifecycle and config handlers hold it throughout, and `handle_prompt` holds it
311
311
  through a `WorkspaceHold` that it releases while waiting on an HTTP endpoint or a permission
312
312
  answer and retakes (reinstalling its session via `resume_workspace`) before any tool runs. An
313
- on-device turn never releases it. Take the session lock first, never the other way round. Tool
313
+ on-device turn never releases it. Take the session lock first, never the other way round. A turn registers its `PromptCancellation` at
314
+ the top of `handle_prompt`, before it waits on either lock, and a `PromptRegistration`
315
+ guard clears it on every return, so a `session/cancel` that arrives while the turn
316
+ is still queued, switching threads, loading a model or running a slash command ends
317
+ it as `cancelled`. The checks sit after each of those waits. Tool
314
318
  execution, subagents included, still runs one session at a time.
315
319
  - **`src/instructions.rs`** — project instruction files, the always-on counterpart to skills.
316
320
  Reads `AGENTS.md` (the cross-tool [agents.md](https://agents.md) standard) and `CLAUDE.md`,
@@ -0,0 +1,320 @@
1
+ name: winget Release
2
+
3
+ # Dispatched by release-github.yml once the release and its checksums exist.
4
+ #
5
+ # winget manifests live in microsoft/winget-pkgs, and `wingetcreate update`
6
+ # only works on a package that is already there. So this workflow branches:
7
+ #
8
+ # - package already in winget-pkgs -> `wingetcreate update`, which rewrites
9
+ # the URLs and re-hashes the installers for us.
10
+ # - package not there yet -> render the manifests in packaging/winget/ and
11
+ # `wingetcreate submit` them as a new package.
12
+ #
13
+ # The second path only runs once, but keeping it here means a first release
14
+ # does not need someone to sit at a Windows box running `wingetcreate new`
15
+ # interactively.
16
+ on:
17
+ workflow_dispatch:
18
+ inputs:
19
+ tag:
20
+ description: "Release tag (e.g. v1.5.2)"
21
+ required: true
22
+
23
+ concurrency:
24
+ # A re-pushed or re-dispatched tag fires this a second time. Queue the
25
+ # duplicate behind the original instead of cancelling it: cancelling a run
26
+ # mid-publish can leave a channel half-uploaded, whereas a queued duplicate
27
+ # just hits the "already published" check and exits clean.
28
+ group: ${{ github.workflow }}-${{ github.event.inputs.tag || github.ref_name }}
29
+ cancel-in-progress: false
30
+
31
+ permissions:
32
+ contents: read
33
+
34
+ env:
35
+ REPO: getsigit/sigit
36
+ PACKAGE_IDENTIFIER: getSigit.siGitCode
37
+
38
+ jobs:
39
+ submit-winget-manifest:
40
+ name: Submit winget manifest
41
+ # wingetcreate is a Windows-only tool.
42
+ runs-on: windows-latest
43
+
44
+ steps:
45
+ - name: Check out repository
46
+ uses: actions/checkout@v6
47
+
48
+ - name: Resolve tag, version and release date
49
+ id: release
50
+ shell: bash
51
+ env:
52
+ GH_TOKEN: ${{ github.token }}
53
+ run: |
54
+ TAG="${{ github.event.inputs.tag }}"
55
+ VERSION="${TAG#v}"
56
+ # wingetcreate wants ReleaseDate as YYYY-MM-DD.
57
+ DATE=$(gh release view "$TAG" --repo "$REPO" --json publishedAt --jq '.publishedAt[0:10]')
58
+
59
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
60
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
61
+ echo "date=${DATE}" >> "$GITHUB_OUTPUT"
62
+
63
+ - name: Check whether the package is already in winget-pkgs
64
+ id: exists
65
+ shell: bash
66
+ env:
67
+ GH_TOKEN: ${{ github.token }}
68
+ run: |
69
+ # manifests are filed under the lowercased first letter of the
70
+ # publisher, e.g. manifests/g/getSigit/siGitCode.
71
+ PUBLISHER="${PACKAGE_IDENTIFIER%%.*}"
72
+ NAME="${PACKAGE_IDENTIFIER#*.}"
73
+ FIRST=$(echo "${PUBLISHER:0:1}" | tr '[:upper:]' '[:lower:]')
74
+ PATH_IN_REPO="manifests/${FIRST}/${PUBLISHER}/${NAME}"
75
+
76
+ if gh api "repos/microsoft/winget-pkgs/contents/${PATH_IN_REPO}" >/dev/null 2>&1; then
77
+ echo "Found ${PACKAGE_IDENTIFIER} at ${PATH_IN_REPO}; submitting an update."
78
+ echo "found=true" >> "$GITHUB_OUTPUT"
79
+ else
80
+ echo "No ${PACKAGE_IDENTIFIER} at ${PATH_IN_REPO}; submitting it as a new package."
81
+ echo "found=false" >> "$GITHUB_OUTPUT"
82
+ fi
83
+
84
+ - name: Render manifests for a first submission
85
+ if: steps.exists.outputs.found == 'false'
86
+ shell: bash
87
+ env:
88
+ GH_TOKEN: ${{ github.token }}
89
+ run: |
90
+ TAG="${{ steps.release.outputs.tag }}"
91
+ VERSION="${{ steps.release.outputs.version }}"
92
+
93
+ mkdir -p artifacts manifests
94
+
95
+ gh release download "$TAG" \
96
+ --repo "$REPO" \
97
+ --pattern "sigit-win-*.exe.sha256" \
98
+ --dir artifacts/
99
+
100
+ # The schema wants 64 hex characters; the community validation
101
+ # pipeline writes them uppercase, so match that.
102
+ SHA_AMD64=$(tr -d '[:space:]' < artifacts/sigit-win-amd64.exe.sha256 | tr '[:lower:]' '[:upper:]')
103
+ SHA_ARM64=$(tr -d '[:space:]' < artifacts/sigit-win-arm64.exe.sha256 | tr '[:lower:]' '[:upper:]')
104
+
105
+ RELEASE_DATE="${{ steps.release.outputs.date }}"
106
+
107
+ for template in packaging/winget/*.yaml.in; do
108
+ out="manifests/$(basename "$template" .in)"
109
+ sed \
110
+ -e "s|@VERSION@|${VERSION}|g" \
111
+ -e "s|@TAG@|${TAG}|g" \
112
+ -e "s|@REPO@|${REPO}|g" \
113
+ -e "s|@SHA_AMD64@|${SHA_AMD64}|g" \
114
+ -e "s|@SHA_ARM64@|${SHA_ARM64}|g" \
115
+ -e "s|@RELEASE_DATE@|${RELEASE_DATE}|g" \
116
+ "$template" > "$out"
117
+
118
+ echo "--- ${out} ---"
119
+ cat "$out"
120
+ done
121
+
122
+ - name: Download wingetcreate
123
+ shell: pwsh
124
+ run: Invoke-WebRequest -Uri "https://aka.ms/wingetcreate/latest" -OutFile wingetcreate.exe
125
+
126
+ # winget-pkgs convention: file the "please add/update this package"
127
+ # Package Request issue *first*, then open the manifest PR that fulfils
128
+ # it with "Resolves #N". Doing it the other way round (PR first, issue
129
+ # after) reads as "here's a request, and here's the PR that already did
130
+ # it", posted seconds apart.
131
+ #
132
+ # The issue and the PR are authored by two separate PATs, configured as
133
+ # the repo secrets WINGET_ISSUE_TOKEN (the Package Request issue) and
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
+ # replaces the old single WINGET_TOKEN.
137
+ - name: File the winget Package Request issue
138
+ id: issue
139
+ shell: bash
140
+ env:
141
+ # Authored by the issue account for this repo.
142
+ GH_TOKEN: ${{ secrets.WINGET_ISSUE_TOKEN }}
143
+ run: |
144
+ set -euo pipefail
145
+
146
+ if [ -z "${GH_TOKEN:-}" ]; then
147
+ echo "WINGET_ISSUE_TOKEN is not set. It needs a PAT (public_repo) for the account that should author the Package Request issue." >&2
148
+ exit 1
149
+ fi
150
+
151
+ VERSION="${{ steps.release.outputs.version }}"
152
+ TAG="${{ steps.release.outputs.tag }}"
153
+
154
+ # The release assets, same URLs the update step feeds wingetcreate.
155
+ BASE="https://github.com/${REPO}/releases/download/${TAG}"
156
+ INSTALLER_AMD64="${BASE}/sigit-win-amd64.exe"
157
+ INSTALLER_ARM64="${BASE}/sigit-win-arm64.exe"
158
+
159
+ # winget-pkgs' Package Request is an issue *form*
160
+ # (.github/ISSUE_TEMPLATE/package_request.yml): the title must be
161
+ # "[Package Request]: ..." and the body has to carry the labelled
162
+ # fields below (Installer Url especially — without it a maintainer
163
+ # just replies "please provide the installer link"). A free-text
164
+ # body lands as Needs-Triage and can't be acted on.
165
+ ISSUE_BODY=$(cat <<EOF
166
+ Download Page Url: https://github.com/${REPO}/releases/tag/${TAG}
167
+ Publisher: getSigit
168
+ Package Name: siGit Code
169
+ Description: siGit Code, an AI coding agent that runs on your machine.
170
+ Package Version: ${VERSION}
171
+ Installer Url: ${INSTALLER_AMD64}
172
+ Installer Url: ${INSTALLER_ARM64}
173
+ EOF
174
+ )
175
+
176
+ ISSUE_URL=$(gh issue create \
177
+ --repo microsoft/winget-pkgs \
178
+ --title "[Package Request]: ${PACKAGE_IDENTIFIER} ${VERSION}" \
179
+ --body "$ISSUE_BODY")
180
+
181
+ echo "Opened issue ${ISSUE_URL}"
182
+ echo "issue_url=${ISSUE_URL}" >> "$GITHUB_OUTPUT"
183
+ echo "issue_number=${ISSUE_URL##*/}" >> "$GITHUB_OUTPUT"
184
+
185
+ - name: Submit new package
186
+ id: submit_new
187
+ if: steps.exists.outputs.found == 'false'
188
+ shell: pwsh
189
+ env:
190
+ # wingetcreate reads the token from this variable. Passing it as
191
+ # --token instead makes the tool warn that the token can end up in
192
+ # logs, which on a shared runner is not a warning worth ignoring.
193
+ # This is the PR-author account for this repo (see mapping above).
194
+ WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
195
+ run: |
196
+ if (-not $env:WINGET_CREATE_GITHUB_TOKEN) {
197
+ Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo scope so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR."
198
+ }
199
+
200
+ $version = "${{ steps.release.outputs.version }}"
201
+
202
+ # Tee stdout so we can scrape the PR URL afterwards. wingetcreate
203
+ # prints "Pull request can be found here: <url>" on success, and
204
+ # that line is the only handle we get on the PR it just opened.
205
+ # --no-open because the runner has no browser to open the PR in.
206
+ .\wingetcreate.exe submit manifests `
207
+ --prtitle "New package: $env:PACKAGE_IDENTIFIER version $version" `
208
+ --no-open 2>&1 | Tee-Object -Variable output
209
+
210
+ if ($LASTEXITCODE -ne 0) {
211
+ Write-Error "wingetcreate submit failed with exit code $LASTEXITCODE"
212
+ }
213
+
214
+ $match = [regex]::Match(($output -join "`n"), 'https://github\.com/microsoft/winget-pkgs/pull/(\d+)')
215
+ if ($match.Success) {
216
+ "pr_url=$($match.Value)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
217
+ "pr_number=$($match.Groups[1].Value)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
218
+ } else {
219
+ Write-Warning "Could not find the PR URL in wingetcreate output; skipping issue linking."
220
+ }
221
+
222
+ - name: Submit manifest update
223
+ id: submit_update
224
+ if: steps.exists.outputs.found == 'true'
225
+ shell: pwsh
226
+ env:
227
+ # PR-author account for this repo (see mapping above).
228
+ WINGET_CREATE_GITHUB_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
229
+ run: |
230
+ if (-not $env:WINGET_CREATE_GITHUB_TOKEN) {
231
+ Write-Error "WINGET_PR_TOKEN is not set. It needs a PAT with public_repo scope so wingetcreate can fork microsoft/winget-pkgs and open the manifest PR."
232
+ }
233
+
234
+ $tag = "${{ steps.release.outputs.tag }}"
235
+ $version = "${{ steps.release.outputs.version }}"
236
+ $base = "https://github.com/${env:REPO}/releases/download/$tag"
237
+
238
+ # The trailing |x64 and |arm64 tell wingetcreate which installer
239
+ # entry each URL replaces. Without them it guesses from the file
240
+ # name, and "sigit-win-amd64.exe" is not a spelling it recognises.
241
+ .\wingetcreate.exe update $env:PACKAGE_IDENTIFIER `
242
+ --version $version `
243
+ --urls "$base/sigit-win-amd64.exe|x64" "$base/sigit-win-arm64.exe|arm64" `
244
+ --release-notes-url "https://github.com/${env:REPO}/releases/tag/$tag" `
245
+ --release-date "${{ steps.release.outputs.date }}" `
246
+ --prtitle "Update $env:PACKAGE_IDENTIFIER to version $version" `
247
+ --submit `
248
+ --no-open 2>&1 | Tee-Object -Variable output
249
+
250
+ if ($LASTEXITCODE -ne 0) {
251
+ Write-Error "wingetcreate failed with exit code $LASTEXITCODE"
252
+ }
253
+
254
+ $match = [regex]::Match(($output -join "`n"), 'https://github\.com/microsoft/winget-pkgs/pull/(\d+)')
255
+ if ($match.Success) {
256
+ "pr_url=$($match.Value)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
257
+ "pr_number=$($match.Groups[1].Value)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
258
+ } else {
259
+ Write-Warning "Could not find the PR URL in wingetcreate output; skipping issue linking."
260
+ }
261
+
262
+ # wingetcreate has no --prbody flag (only --prtitle), so it always opens
263
+ # the PR with the winget-pkgs template: an empty Description and an
264
+ # unticked "Linked to an issue" box next to a "Resolves #[Issue Number]"
265
+ # placeholder. The issue already exists (filed first, above), so this
266
+ # step just rewrites the body to point the PR at it with "Resolves #N".
267
+ #
268
+ # This must run as the PR author (WINGET_PR_TOKEN) — the account that
269
+ # opened the PR via wingetcreate — since only the author (or a repo
270
+ # maintainer) can edit it.
271
+ - name: Set the winget PR body and link the issue
272
+ id: link
273
+ if: >-
274
+ (steps.submit_new.outputs.pr_number != '') ||
275
+ (steps.submit_update.outputs.pr_number != '')
276
+ shell: bash
277
+ env:
278
+ GH_TOKEN: ${{ secrets.WINGET_PR_TOKEN }}
279
+ run: |
280
+ set -euo pipefail
281
+
282
+ PR_URL="${{ steps.submit_new.outputs.pr_url || steps.submit_update.outputs.pr_url }}"
283
+ ISSUE_NUMBER="${{ steps.issue.outputs.issue_number }}"
284
+ VERSION="${{ steps.release.outputs.version }}"
285
+ TAG="${{ steps.release.outputs.tag }}"
286
+
287
+ if [ -n "$ISSUE_NUMBER" ]; then
288
+ CHECKBOX="- [x] Linked to an issue (if applicable)"
289
+ RESOLVES="- Resolves #${ISSUE_NUMBER}"
290
+ else
291
+ CHECKBOX="- [ ] Linked to an issue (if applicable)"
292
+ RESOLVES=""
293
+ fi
294
+
295
+ if [ "${{ steps.exists.outputs.found }}" = "false" ]; then
296
+ DESC="Adds ${PACKAGE_IDENTIFIER} version ${VERSION} from ${TAG}."
297
+ else
298
+ DESC="Updates ${PACKAGE_IDENTIFIER} to version ${VERSION} from ${TAG}."
299
+ fi
300
+
301
+ # Mirrors the current .github/PULL_REQUEST_TEMPLATE.md; keep the
302
+ # checklist items in sync with it (e.g. the 1.12 schema line).
303
+ BODY=$(cat <<EOF
304
+ ## 📖 Description
305
+ ${DESC}
306
+ ## ✅ Checklist
307
+ - [x] Signed the [Contributor License Agreement](https://cla.opensource.microsoft.com)
308
+ ${CHECKBOX}
309
+ ${RESOLVES}
310
+ ## 📦 Manifest Checklist
311
+ - [x] Checked that there aren't other open [pull requests](https://github.com/microsoft/winget-pkgs/pulls) for the same manifest update/change
312
+ - [x] This PR only modifies one (1) manifest
313
+ - [x] Validated manifest locally with \`winget validate --manifest <directory>\`
314
+ - [x] Tested manifest locally with \`winget install --manifest <directory>\`
315
+ - [x] Manifest conforms to the [1.12 schema](https://github.com/microsoft/winget-pkgs/tree/master/doc/manifest/schema/1.12.0)
316
+ EOF
317
+ )
318
+
319
+ gh pr edit "$PR_URL" --body "$BODY"
320
+ echo "Linked ${PR_URL} to issue #${ISSUE_NUMBER:-<none>}"
@@ -310,7 +310,11 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
310
310
  conversation); lifecycle and config handlers hold it throughout, and `handle_prompt` holds it
311
311
  through a `WorkspaceHold` that it releases while waiting on an HTTP endpoint or a permission
312
312
  answer and retakes (reinstalling its session via `resume_workspace`) before any tool runs. An
313
- on-device turn never releases it. Take the session lock first, never the other way round. Tool
313
+ on-device turn never releases it. Take the session lock first, never the other way round. A turn registers its `PromptCancellation` at
314
+ the top of `handle_prompt`, before it waits on either lock, and a `PromptRegistration`
315
+ guard clears it on every return, so a `session/cancel` that arrives while the turn
316
+ is still queued, switching threads, loading a model or running a slash command ends
317
+ it as `cancelled`. The checks sit after each of those waits. Tool
314
318
  execution, subagents included, still runs one session at a time.
315
319
  - **`src/instructions.rs`** — project instruction files, the always-on counterpart to skills.
316
320
  Reads `AGENTS.md` (the cross-tool [agents.md](https://agents.md) standard) and `CLAUDE.md`,
@@ -1,6 +1,41 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 1.6.8
4
+
5
+ ### Fixed
6
+
7
+ - **A bare launch of `sigit.exe` on Windows exits 0 instead of bailing.** A
8
+ no-argument launch with a TTY stdin falls through to the non-Unix branch,
9
+ which has no TUI to start (the interactive client needs Unix-only fd
10
+ redirection). It now prints a short usage message pointing at ACP and
11
+ headless modes and exits 0, so winget's installer validator no longer reports
12
+ a `Validation-Executable-Error`.
13
+
14
+ ### Changed
15
+
16
+ - **winget submissions link back to their Package Request issue.** The release
17
+ workflow scrapes the winget-pkgs PR URL from wingetcreate, opens a Package
18
+ Request issue on first submission, and rewrites the PR body to fill the
19
+ Description, tick the issue checkbox, and add `Resolves #NNN` — replacing the
20
+ manual per-PR edit that used to follow each release.
21
+ - **The winget release workflow files its Package Request issue first and uses
22
+ two scoped tokens.** The Package Request issue is opened before the
23
+ winget-pkgs PR so the PR can reference it immediately, and issue creation and
24
+ PR submission now use separate tokens (`WINGET_ISSUE_TOKEN` and
25
+ `WINGET_PR_TOKEN`) rather than one shared token.
26
+
27
+ ## 1.6.7
28
+
29
+ ### Fixed
30
+
31
+ - **A cancel that arrives before the turn starts is no longer lost.** A prompt
32
+ now registers for `session/cancel` as soon as it begins, not once it reaches
33
+ inference. A cancel that lands while the turn is queued on the workspace,
34
+ switching threads, loading a model, or running a slash command ends it with
35
+ `cancelled` instead of `end_turn`.
36
+ - **A prompt that is only audio is answered.** A clip with no text or image used
37
+ to end the turn at once with nothing sent to the model, even though the agent
38
+ advertises audio.
4
39
 
5
40
  ## 1.6.6
6
41
 
@@ -310,7 +310,11 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
310
310
  conversation); lifecycle and config handlers hold it throughout, and `handle_prompt` holds it
311
311
  through a `WorkspaceHold` that it releases while waiting on an HTTP endpoint or a permission
312
312
  answer and retakes (reinstalling its session via `resume_workspace`) before any tool runs. An
313
- on-device turn never releases it. Take the session lock first, never the other way round. Tool
313
+ on-device turn never releases it. Take the session lock first, never the other way round. A turn registers its `PromptCancellation` at
314
+ the top of `handle_prompt`, before it waits on either lock, and a `PromptRegistration`
315
+ guard clears it on every return, so a `session/cancel` that arrives while the turn
316
+ is still queued, switching threads, loading a model or running a slash command ends
317
+ it as `cancelled`. The checks sit after each of those waits. Tool
314
318
  execution, subagents included, still runs one session at a time.
315
319
  - **`src/instructions.rs`** — project instruction files, the always-on counterpart to skills.
316
320
  Reads `AGENTS.md` (the cross-tool [agents.md](https://agents.md) standard) and `CLAUDE.md`,
@@ -5901,7 +5901,7 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
5901
5901
 
5902
5902
  [[package]]
5903
5903
  name = "sigit"
5904
- version = "1.6.6"
5904
+ version = "1.6.8"
5905
5905
  dependencies = [
5906
5906
  "agent-client-protocol",
5907
5907
  "anyhow",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sigit"
3
- version = "1.6.6"
3
+ version = "1.6.8"
4
4
  edition = "2024"
5
5
  description = "siGit Code — ACP-compatible AI coding agent. Sí, git."
6
6
  documentation = "https://github.com/getsigit/sigit"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sigit-code
3
- Version: 1.6.6
3
+ Version: 1.6.8
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -465,6 +465,21 @@ impl PromptCancellation {
465
465
  }
466
466
  }
467
467
 
468
+ /// Removes a turn's cancellation from the agent's registry when the turn ends,
469
+ /// whichever way it returns.
470
+ struct PromptRegistration<'a> {
471
+ agent: &'a SiGitAgent,
472
+ session_id: SessionId,
473
+ cancellation: Arc<PromptCancellation>,
474
+ }
475
+
476
+ impl Drop for PromptRegistration<'_> {
477
+ fn drop(&mut self) {
478
+ self.agent
479
+ .finish_prompt(&self.session_id, &self.cancellation);
480
+ }
481
+ }
482
+
468
483
  enum DrainTurnError {
469
484
  Backend(backend::BackendError),
470
485
  Cancelled,
@@ -3063,10 +3078,31 @@ impl SiGitAgent {
3063
3078
  return Ok(PromptResponse::new(StopReason::Cancelled));
3064
3079
  }
3065
3080
 
3081
+ // Registered before anything that can wait, so a `session/cancel` that
3082
+ // lands while this turn is still queued on the workspace, switching
3083
+ // sessions or loading a model is seen. The session lock is held, so the
3084
+ // slot can only be this turn's. The guard clears it on every return.
3085
+ let cancellation = Arc::new(PromptCancellation::default());
3086
+ self.prompt_cancellations
3087
+ .lock()
3088
+ .unwrap()
3089
+ .insert(session_id.to_string(), Arc::clone(&cancellation));
3090
+ let _registration = PromptRegistration {
3091
+ agent: self,
3092
+ session_id: session_id.clone(),
3093
+ cancellation: Arc::clone(&cancellation),
3094
+ };
3095
+
3066
3096
  // Everything below (slash commands included) acts on the live session,
3067
3097
  // so it has to be this one. Resource links are read relative to its cwd.
3068
3098
  let mut workspace = WorkspaceHold::acquire(&self.workspace_lock).await;
3099
+ if cancellation.cancelled.load(Ordering::Acquire) {
3100
+ return Ok(PromptResponse::new(StopReason::Cancelled));
3101
+ }
3069
3102
  self.activate_session(&session_id).await?;
3103
+ if cancellation.cancelled.load(Ordering::Acquire) {
3104
+ return Ok(PromptResponse::new(StopReason::Cancelled));
3105
+ }
3070
3106
 
3071
3107
  // log every block so we can debug @ references and file context
3072
3108
  for (i, block) in args.prompt.iter().enumerate() {
@@ -3204,7 +3240,7 @@ impl SiGitAgent {
3204
3240
 
3205
3241
  let user_text = parts.join("\n");
3206
3242
 
3207
- if user_text.trim().is_empty() && images.is_empty() {
3243
+ if user_text.trim().is_empty() && images.is_empty() && audio.is_empty() {
3208
3244
  return Ok(PromptResponse::new(StopReason::EndTurn));
3209
3245
  }
3210
3246
 
@@ -3230,17 +3266,18 @@ impl SiGitAgent {
3230
3266
  match commands::resolve_command(&command) {
3231
3267
  Some(custom) => commands::render(&custom.body, argument.as_deref()),
3232
3268
  None => {
3233
- return exec_slash_acp(
3234
- self,
3235
- cx,
3236
- session_id,
3237
- SlashCommand::Unknown(command, argument),
3238
- )
3239
- .await;
3269
+ return self
3270
+ .run_slash(
3271
+ cx,
3272
+ session_id,
3273
+ SlashCommand::Unknown(command, argument),
3274
+ &cancellation,
3275
+ )
3276
+ .await;
3240
3277
  }
3241
3278
  }
3242
3279
  }
3243
- Some(command) => return exec_slash_acp(self, cx, session_id, command).await,
3280
+ Some(command) => return self.run_slash(cx, session_id, command, &cancellation).await,
3244
3281
  None => user_text,
3245
3282
  };
3246
3283
 
@@ -3267,7 +3304,12 @@ impl SiGitAgent {
3267
3304
  {
3268
3305
  if self.auto_load_local_model {
3269
3306
  self.start_startup_model_load_if_needed();
3270
- self.await_model_ready(cx, &session_id).await?;
3307
+ tokio::select! {
3308
+ ready = self.await_model_ready(cx, &session_id) => ready?,
3309
+ () = cancellation.cancelled() => {
3310
+ return Ok(PromptResponse::new(StopReason::Cancelled));
3311
+ }
3312
+ }
3271
3313
  } else {
3272
3314
  self.send_assistant_message(
3273
3315
  cx,
@@ -3280,11 +3322,9 @@ impl SiGitAgent {
3280
3322
  }
3281
3323
  }
3282
3324
 
3283
- let cancellation = Arc::new(PromptCancellation::default());
3284
- self.prompt_cancellations
3285
- .lock()
3286
- .unwrap()
3287
- .insert(session_id.to_string(), Arc::clone(&cancellation));
3325
+ if cancellation.cancelled.load(Ordering::Acquire) {
3326
+ return Ok(PromptResponse::new(StopReason::Cancelled));
3327
+ }
3288
3328
 
3289
3329
  // ── tool-calling loop ────────────────────────────────────────────
3290
3330
  // send message → execute any tool calls → feed results back
@@ -3987,6 +4027,24 @@ impl SiGitAgent {
3987
4027
  }
3988
4028
  }
3989
4029
 
4030
+ /// Run a built-in slash command as a turn. A `session/cancel` ends it with
4031
+ /// `Cancelled`; only `/compact` and `/load` take long enough to notice.
4032
+ async fn run_slash(
4033
+ &self,
4034
+ cx: &ConnectionTo<Client>,
4035
+ session_id: SessionId,
4036
+ command: SlashCommand,
4037
+ cancellation: &PromptCancellation,
4038
+ ) -> agent_client_protocol::Result<PromptResponse> {
4039
+ if cancellation.cancelled.load(Ordering::Acquire) {
4040
+ return Ok(PromptResponse::new(StopReason::Cancelled));
4041
+ }
4042
+ tokio::select! {
4043
+ response = exec_slash_acp(self, cx, session_id, command) => response,
4044
+ () = cancellation.cancelled() => Ok(PromptResponse::new(StopReason::Cancelled)),
4045
+ }
4046
+ }
4047
+
3990
4048
  fn finish_prompt(&self, session_id: &SessionId, cancellation: &Arc<PromptCancellation>) {
3991
4049
  let key = session_id.to_string();
3992
4050
  let mut active = self.prompt_cancellations.lock().unwrap();
@@ -6187,7 +6245,26 @@ async fn main() -> anyhow::Result<()> {
6187
6245
  }
6188
6246
  #[cfg(not(unix))]
6189
6247
  {
6190
- anyhow::bail!("interactive mode requires Unix (macOS / Linux)");
6248
+ // No TUI on non-Unix: the interactive full-screen client relies on
6249
+ // fd redirection that only exists on Unix. A bare launch in a
6250
+ // terminal (the common case, and exactly what the winget installer
6251
+ // validator does — run `sigit.exe` with no arguments) therefore has
6252
+ // nothing to do, so print how to use the binary and exit cleanly.
6253
+ // Bailing here instead would return a non-zero exit code, which the
6254
+ // validator reads as a failure (Validation-Executable-Error).
6255
+ println!(
6256
+ "siGit Code v{}\n\n\
6257
+ Interactive terminal mode is Unix-only (macOS / Linux). On this \
6258
+ platform sigit runs as an ACP agent for editor integration, or \
6259
+ non-interactively from a script:\n\n\
6260
+ \x20 sigit run \"<prompt>\" run one prompt and exit\n\
6261
+ \x20 sigit --version print the version\n\
6262
+ \x20 sigit login sign in to siGit Code Cloud\n\n\
6263
+ Point your editor's ACP client at this binary to use it \
6264
+ interactively.",
6265
+ env!("CARGO_PKG_VERSION")
6266
+ );
6267
+ Ok(())
6191
6268
  }
6192
6269
  } else {
6193
6270
  // ACP mode: keep stdout untouched for protocol JSON only.
@@ -6406,6 +6483,71 @@ mod tests {
6406
6483
  assert!(agent.active_session.lock().unwrap().is_none());
6407
6484
  }
6408
6485
 
6486
+ #[tokio::test]
6487
+ async fn a_turn_leaves_no_cancellation_behind_however_it_returns() {
6488
+ let agent = SiGitAgent::new(
6489
+ Arc::new(ChatEngine::new()),
6490
+ default_local_model_config(),
6491
+ Arc::new(AtomicBool::new(true)),
6492
+ Arc::new(AtomicBool::new(false)),
6493
+ Arc::new(std::sync::Mutex::new(None)),
6494
+ false,
6495
+ false,
6496
+ );
6497
+ let session_id = SessionId::new("early");
6498
+ let turn = Arc::new(PromptCancellation::default());
6499
+ agent
6500
+ .prompt_cancellations
6501
+ .lock()
6502
+ .unwrap()
6503
+ .insert("early".into(), Arc::clone(&turn));
6504
+ let registration = PromptRegistration {
6505
+ agent: &agent,
6506
+ session_id: session_id.clone(),
6507
+ cancellation: Arc::clone(&turn),
6508
+ };
6509
+
6510
+ // A cancel that arrives before the turn reaches inference is recorded
6511
+ // and stays visible to every check the turn makes afterwards.
6512
+ agent
6513
+ .handle_cancel(CancelNotification::new(session_id.clone()))
6514
+ .await
6515
+ .unwrap();
6516
+ assert!(turn.cancelled.load(Ordering::Acquire));
6517
+ tokio::time::timeout(std::time::Duration::from_millis(50), turn.cancelled())
6518
+ .await
6519
+ .expect("a pre-cancelled turn must resolve at once");
6520
+
6521
+ drop(registration);
6522
+ assert!(agent.prompt_cancellations.lock().unwrap().is_empty());
6523
+ }
6524
+
6525
+ #[tokio::test]
6526
+ async fn a_finished_turn_does_not_remove_its_successors_cancellation() {
6527
+ let agent = SiGitAgent::new(
6528
+ Arc::new(ChatEngine::new()),
6529
+ default_local_model_config(),
6530
+ Arc::new(AtomicBool::new(true)),
6531
+ Arc::new(AtomicBool::new(false)),
6532
+ Arc::new(std::sync::Mutex::new(None)),
6533
+ false,
6534
+ false,
6535
+ );
6536
+ let old = Arc::new(PromptCancellation::default());
6537
+ let new = Arc::new(PromptCancellation::default());
6538
+ agent
6539
+ .prompt_cancellations
6540
+ .lock()
6541
+ .unwrap()
6542
+ .insert("s".into(), Arc::clone(&new));
6543
+ drop(PromptRegistration {
6544
+ agent: &agent,
6545
+ session_id: SessionId::new("s"),
6546
+ cancellation: old,
6547
+ });
6548
+ assert!(agent.prompt_cancellations.lock().unwrap().contains_key("s"));
6549
+ }
6550
+
6409
6551
  #[tokio::test]
6410
6552
  async fn cancel_signals_only_the_matching_active_prompt() {
6411
6553
  let agent = SiGitAgent::new(