dsh-plugin-git-commit-push 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/setup.ps1 ADDED
@@ -0,0 +1,226 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Install a local CHECKOUT of dsh-plugin-git-commit-push into a DSH profile.
4
+
5
+ .DESCRIPTION
6
+ Wires the plugin into the profile's bundle stack in two deliberate steps:
7
+
8
+ 1. adds the plugin to the profile's package.json as a `link:` dependency;
9
+ 2. appends the package to `dsh.profile.bundles`.
10
+
11
+ Installing from npm does not need this script: use Settings > Plugins (type
12
+ `dsh-plugin-git-commit-push`) or
13
+ `dsh plugin --profile <profile> add dsh-plugin-git-commit-push`.
14
+ This script is for a checkout you are editing, which should be linked rather
15
+ than copied into node_modules.
16
+
17
+ The mount row is deliberately NOT written into the profile's patch file: it
18
+ comes from the package's own bundle patch (`cordis.patch.yml`, declared as
19
+ `dsh.bundle.patch` in this package's package.json), which the launcher applies
20
+ for every profile that selects the bundle. That declaration is also what makes
21
+ the package manageable: the Plugins page can enable, disable and uninstall a
22
+ bundle, and refuses every action on a package without one with the diagnostic
23
+ "这个包没有声明组合包,不能作为插件管理" (`not-bundle`).
24
+
25
+ A profile set up by an earlier revision of this script still carries a
26
+ hand-written mount row in its cordis.patch.yml under the marker block below.
27
+ Loader `insert` is append-only — two inserts of the same id mount the plugin
28
+ twice — so this script strips that legacy block whenever it finds one.
29
+
30
+ Steps 1 and 2 change the bundle set, which a running profile does not pick up:
31
+ DSH must be restarted afterwards. Every write is idempotent — run this as many
32
+ times as you like — and a backup of each file is written before the first
33
+ modification.
34
+
35
+ Nothing here touches your git configuration, your repositories, or any DSH
36
+ setting other than the edits above.
37
+
38
+ .PARAMETER Profile
39
+ Profile name under $env:USERPROFILE\.dsh\profiles. Defaults to `desktop`.
40
+
41
+ .PARAMETER Uninstall
42
+ Reverses the two edits, strips a legacy mount row and removes the linked package.
43
+
44
+ .EXAMPLE
45
+ powershell -ExecutionPolicy Bypass -File .\setup.ps1
46
+ powershell -ExecutionPolicy Bypass -File .\setup.ps1 -Profile web
47
+ powershell -ExecutionPolicy Bypass -File .\setup.ps1 -Uninstall
48
+ #>
49
+ [CmdletBinding()]
50
+ param(
51
+ [string]$Profile = 'desktop',
52
+ [switch]$Uninstall
53
+ )
54
+
55
+ $ErrorActionPreference = 'Stop'
56
+
57
+ $pluginDir = $PSScriptRoot
58
+ $pluginName = 'dsh-plugin-git-commit-push'
59
+ $dshHome = Join-Path $env:USERPROFILE '.dsh'
60
+ $profileDir = Join-Path $dshHome "profiles\$Profile"
61
+ $nodeExe = Join-Path $dshHome 'dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe'
62
+ $pnpmCli = Join-Path $dshHome 'dsh-runtimes\dsh-primary-runtime\dependencies\pnpm\bin\pnpm.mjs'
63
+
64
+ function Write-Step([string]$Text) { Write-Host " $Text" }
65
+ function Write-Ok([string]$Text) { Write-Host " [ok] $Text" -ForegroundColor Green }
66
+ function Write-Info([string]$Text) { Write-Host " [info] $Text" -ForegroundColor Cyan }
67
+ function Write-Warn2([string]$Text) { Write-Host " [warn] $Text" -ForegroundColor Yellow }
68
+
69
+ Write-Host ''
70
+ Write-Host "dsh-plugin-git-commit-push $(if ($Uninstall) { 'uninstall' } else { 'install' })" -ForegroundColor White
71
+ Write-Host ''
72
+
73
+ # --- 1. Preconditions ------------------------------------------------------
74
+ if (-not (Test-Path -LiteralPath $profileDir)) {
75
+ throw "Profile directory not found: $profileDir"
76
+ }
77
+ if (-not (Test-Path -LiteralPath (Join-Path $profileDir 'package.json'))) {
78
+ throw "Not a DSH profile (no package.json): $profileDir"
79
+ }
80
+
81
+ $packageJsonPath = Join-Path $profileDir 'package.json'
82
+ $patchPath = Join-Path $profileDir 'cordis.patch.yml'
83
+ if (-not (Test-Path -LiteralPath $patchPath)) {
84
+ throw "Not a DSH profile (no cordis.patch.yml): $profileDir"
85
+ }
86
+
87
+ $gitCmd = Get-Command git -ErrorAction SilentlyContinue
88
+ if ($null -eq $gitCmd) {
89
+ Write-Warn2 'git was not found on PATH. The plugin needs it at runtime; install Git before using it.'
90
+ } else {
91
+ Write-Ok "git found: $($gitCmd.Source)"
92
+ }
93
+
94
+ if (-not (Test-Path -LiteralPath $nodeExe)) {
95
+ throw "Bundled node not found: $nodeExe`nSet it manually, or install the package with your own pnpm instead."
96
+ }
97
+ if (-not (Test-Path -LiteralPath $pnpmCli)) {
98
+ throw "Bundled pnpm not found: $pnpmCli"
99
+ }
100
+ Write-Ok 'bundled node + pnpm located'
101
+
102
+ # --- 2. Back up once -------------------------------------------------------
103
+ # NOTE: the concatenation must be `$file + '.suffix'`, NOT "$file.suffix".
104
+ # Inside an expandable string PowerShell reads `.git-commit-plugin` as a
105
+ # PROPERTY access on $file, which yields $null — the backup would silently land
106
+ # in the current directory under a mangled name and the second file would never
107
+ # be backed up at all.
108
+ #
109
+ # The patch file is backed up further down, and only when this run actually has
110
+ # to change it (the legacy mount-row migration). A fresh install leaves it
111
+ # untouched.
112
+ $manifestBackup = $packageJsonPath + '.git-commit-plugin.bak'
113
+ if (-not (Test-Path -LiteralPath $manifestBackup)) {
114
+ Copy-Item -LiteralPath $packageJsonPath -Destination $manifestBackup
115
+ Write-Ok "backed up package.json -> $(Split-Path -Leaf $manifestBackup)"
116
+ }
117
+
118
+ # --- 3. Edit the profile manifest and patch layer --------------------------
119
+ # The manifest edit is delegated to lib/profile-edit.mjs, the SAME code setup.sh
120
+ # runs on macOS/Linux. Editing a user's profile package.json is the one step that
121
+ # must not go wrong, and two hand-written implementations of it would drift: this
122
+ # way one reviewed implementation (idempotent, keeps unknown fields, no BOM,
123
+ # self-verifying) serves both platforms.
124
+ $editArgs = @(
125
+ "$pluginDir\lib\profile-edit.mjs"
126
+ '--profile-dir', $profileDir
127
+ '--package', $pluginName
128
+ )
129
+ if ($Uninstall) { $editArgs += '--remove' } else { $editArgs += @('--link', $pluginDir) }
130
+
131
+ & $nodeExe @editArgs
132
+ if ($LASTEXITCODE -ne 0) { throw "profile manifest edit failed with exit code $LASTEXITCODE" }
133
+ Write-Ok 'package.json updated'
134
+
135
+ # The plugin is mounted by the bundle patch this package ships
136
+ # (cordis.patch.yml, declared as dsh.bundle.patch). Nothing is written into the
137
+ # profile's own cordis.patch.yml on a fresh install.
138
+ #
139
+ # What this step does is the MIGRATION: an earlier revision of this script wrote
140
+ # a mount row into the profile patch instead, and Loader `insert` is
141
+ # append-only — leaving that row in place next to the bundle's own row would
142
+ # mount the plugin twice. So the marker block is stripped whenever it is found;
143
+ # the edit is idempotent (it is stripped and never rewritten) and the profile
144
+ # patch is backed up first, because this is the only run that may change it.
145
+ $beginMarker = '# >>> dsh-plugin-git-commit-push'
146
+ $endMarker = '# <<< dsh-plugin-git-commit-push'
147
+ $patchText = Get-Content -LiteralPath $patchPath -Raw
148
+ $existingBlock = [regex]::Match($patchText, "(?s)\s*$([regex]::Escape($beginMarker)).*?$([regex]::Escape($endMarker))")
149
+ if ($existingBlock.Success) {
150
+ $patchBackup = $patchPath + '.git-commit-plugin.bak'
151
+ if (-not (Test-Path -LiteralPath $patchBackup)) {
152
+ Copy-Item -LiteralPath $patchPath -Destination $patchBackup
153
+ Write-Ok "backed up cordis.patch.yml -> $(Split-Path -Leaf $patchBackup)"
154
+ }
155
+ $patchText = $patchText.Remove($existingBlock.Index, $existingBlock.Length)
156
+ $patchText = $patchText -replace "(\r?\n){3,}", "`n`n"
157
+ [System.IO.File]::WriteAllText($patchPath, "$($patchText.TrimEnd())`n", (New-Object System.Text.UTF8Encoding($false)))
158
+ Write-Ok 'cordis.patch.yml: legacy mount row removed (the bundle patch mounts the plugin now)'
159
+ } else {
160
+ Write-Ok 'cordis.patch.yml: no legacy mount row to remove'
161
+ }
162
+
163
+ # --- 4. Link the package ---------------------------------------------------
164
+ Write-Step 'running pnpm install in the profile (this may take a moment)...'
165
+ Push-Location $profileDir
166
+ # pnpm writes progress and warnings to stderr routinely. With
167
+ # $ErrorActionPreference = 'Stop' and a `2>&1` redirect, Windows PowerShell turns
168
+ # those into terminating errors, so the script would die before it could read
169
+ # $LASTEXITCODE. Relax the preference for the duration of the native call.
170
+ $previousPreference = $ErrorActionPreference
171
+ $ErrorActionPreference = 'Continue'
172
+ try {
173
+ & $nodeExe $pnpmCli install
174
+ $pnpmExit = $LASTEXITCODE
175
+ } finally {
176
+ $ErrorActionPreference = $previousPreference
177
+ Pop-Location
178
+ }
179
+ if ($pnpmExit -ne 0) { throw "pnpm install failed with exit code $pnpmExit" }
180
+ Write-Ok 'package linked into the profile'
181
+
182
+ # --- 5. Verify -------------------------------------------------------------
183
+ # On uninstall the linked directory is expected to be gone (pnpm pruned it in
184
+ # step 4); reporting that as a warning would read like a failure.
185
+ $linkedModule = Join-Path $profileDir "node_modules\$pluginName"
186
+ if ($Uninstall) {
187
+ if (Test-Path -LiteralPath $linkedModule) {
188
+ Write-Warn2 "node_modules\$pluginName is still present; run pnpm install in the profile to prune it"
189
+ } else {
190
+ Write-Ok "module removed from node_modules\$pluginName"
191
+ }
192
+ } elseif (Test-Path -LiteralPath $linkedModule) {
193
+ Write-Ok "module present at node_modules\$pluginName"
194
+ } else {
195
+ Write-Warn2 "node_modules\$pluginName is missing; the profile will not find the plugin"
196
+ }
197
+
198
+ if (-not $Uninstall) {
199
+ Write-Host ''
200
+ Write-Host 'Done. Restart DeepSeek Harness to load the bundle.' -ForegroundColor White
201
+ Write-Host ''
202
+ Write-Host ' Close DeepSeek Harness first if it is running: a bundle addition is' -ForegroundColor Gray
203
+ Write-Host ' read at profile start, not live. Then start it again.' -ForegroundColor Gray
204
+ Write-Host ''
205
+ Write-Host ' After the restart you get:' -ForegroundColor Gray
206
+ Write-Host ' - a tool named git_commit_push (prepare / apply / auto)' -ForegroundColor Gray
207
+ Write-Host ' - a slash command /git-commit-push (runs without the model, 0 tokens)' -ForegroundColor Gray
208
+ Write-Host ''
209
+ Write-Host ' The plugin is a declared bundle, so Settings > Plugins can now' -ForegroundColor Gray
210
+ Write-Host ' enable, disable and uninstall it without this script.' -ForegroundColor Gray
211
+ Write-Host ''
212
+ Write-Host ' Settings live in:' -ForegroundColor Gray
213
+ Write-Host " $(Join-Path $pluginDir 'git-commit-push.config.json')" -ForegroundColor Gray
214
+ Write-Host ''
215
+ Write-Host ' If anything goes wrong, restore the backed-up profile files:' -ForegroundColor Gray
216
+ Write-Host " $(Join-Path $profileDir 'package.json.git-commit-plugin.bak')" -ForegroundColor Gray
217
+ Write-Host ' cordis.patch.yml.git-commit-plugin.bak (only if the migration touched it)' -ForegroundColor Gray
218
+ Write-Host ''
219
+ } else {
220
+ Write-Host ''
221
+ Write-Host 'Uninstalled. Restart DeepSeek Harness for the bundle set to change.' -ForegroundColor White
222
+ Write-Host 'Backups of your original profile files are next to them (*.git-commit-plugin.bak).' -ForegroundColor Gray
223
+ Write-Host ''
224
+ }
225
+
226
+ exit 0
package/setup.sh ADDED
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Install a local CHECKOUT of dsh-plugin-git-commit-push into a DSH profile on
4
+ # macOS or Linux. The Windows equivalent is setup.ps1; both do the same two
5
+ # things and both delegate the profile-manifest edit to lib/profile-edit.mjs, so
6
+ # the risky step is the same reviewed code on every platform.
7
+ #
8
+ # 1. links the plugin into the profile's package.json as a `link:` dependency;
9
+ # 2. appends the package to `dsh.profile.bundles`.
10
+ #
11
+ # Installing from npm does not need this script: use Settings > Plugins, or
12
+ # dsh plugin --profile <profile> add dsh-plugin-git-commit-push
13
+ # This script is for a checkout you are editing, which should be linked rather
14
+ # than copied into node_modules.
15
+ #
16
+ # The mount row is deliberately NOT written here. It comes from the package's own
17
+ # bundle patch (cordis.patch.yml, declared as `dsh.bundle.patch`), which the
18
+ # launcher applies for every profile that selects the bundle. That declaration is
19
+ # also what makes the package manageable: without it the Plugins page answers
20
+ # every request with "这个包没有声明组合包,不能作为插件管理" (`not-bundle`).
21
+ #
22
+ # What this script does add is the migration: an earlier revision wrote the mount
23
+ # row into the profile's cordis.patch.yml instead, and Loader `insert` is
24
+ # append-only — two inserts of one id mount the plugin twice — so that legacy
25
+ # block is stripped whenever it is found.
26
+ #
27
+ # Restart DSH afterwards. `patchReload: live` re-reads the mount row but does
28
+ # NOT re-import an ESM module, so code changes need a restart.
29
+ #
30
+ # Usage:
31
+ # ./setup.sh [profile] install (default profile: desktop)
32
+ # ./setup.sh [profile] --uninstall
33
+ #
34
+ # Environment:
35
+ # DSH_HOME DSH home directory (default: ~/.dsh)
36
+
37
+ set -eu
38
+
39
+ plugin_dir=$(cd -- "$(dirname -- "$0")" && pwd)
40
+ plugin_name='dsh-plugin-git-commit-push'
41
+ dsh_home=${DSH_HOME:-"$HOME/.dsh"}
42
+
43
+ profile='desktop'
44
+ uninstall=0
45
+ for arg in "$@"; do
46
+ case "$arg" in
47
+ --uninstall|-u) uninstall=1 ;;
48
+ --help|-h)
49
+ sed -n '2,35p' "$0" | sed 's/^# \{0,1\}//'
50
+ exit 0
51
+ ;;
52
+ -*) printf 'unknown option: %s\n' "$arg" >&2; exit 2 ;;
53
+ *) profile=$arg ;;
54
+ esac
55
+ done
56
+
57
+ profile_dir="$dsh_home/profiles/$profile"
58
+
59
+ ok() { printf ' [ok] %s\n' "$1"; }
60
+ info() { printf ' [info] %s\n' "$1"; }
61
+ warn() { printf ' [warn] %s\n' "$1"; }
62
+ die() { printf ' [fail] %s\n' "$1" >&2; exit 1; }
63
+
64
+ printf '\n%s %s\n\n' "$plugin_name" "$([ "$uninstall" = 1 ] && echo uninstall || echo install)"
65
+
66
+ # ---------------------------------------------------------------- preconditions
67
+ [ -d "$profile_dir" ] || die "profile directory not found: $profile_dir"
68
+ manifest="$profile_dir/package.json"
69
+ patch_file="$profile_dir/cordis.patch.yml"
70
+ [ -f "$manifest" ] || die "not a DSH profile (no package.json): $profile_dir"
71
+ [ -f "$patch_file" ] || die "not a DSH profile (no cordis.patch.yml): $profile_dir"
72
+
73
+ if command -v git >/dev/null 2>&1; then
74
+ ok "git found: $(command -v git)"
75
+ else
76
+ warn 'git was not found on PATH. The plugin needs it at runtime; install Git before using it.'
77
+ fi
78
+
79
+ # Prefer a node already on PATH; otherwise use the runtime DSH ships with.
80
+ node_bin=''
81
+ if command -v node >/dev/null 2>&1; then
82
+ node_bin=$(command -v node)
83
+ else
84
+ for candidate in \
85
+ "$dsh_home/dsh-runtimes/dsh-primary-runtime/dependencies/node/bin/node" \
86
+ "/Applications/DeepSeek Harness.app/Contents/Resources/app.asar.unpacked/dsh/dsh-runtimes/dsh-primary-runtime/dependencies/node/bin/node"
87
+ do
88
+ if [ -x "$candidate" ]; then node_bin=$candidate; break; fi
89
+ done
90
+ fi
91
+ [ -n "$node_bin" ] || die 'no node executable found. Install Node 20+, or set DSH_HOME to your DSH home.'
92
+ ok "node: $node_bin"
93
+
94
+ pnpm_cli="$dsh_home/dsh-runtimes/dsh-primary-runtime/dependencies/pnpm/bin/pnpm.mjs"
95
+
96
+ # ------------------------------------------------------------------- backups
97
+ stamp=$(date +%Y%m%d-%H%M%S)
98
+ for file in "$manifest" "$patch_file"; do
99
+ backup="$file.git-commit-plugin.bak"
100
+ if [ ! -f "$backup" ]; then
101
+ # `cp -p` preserves the mode; a plain cp is fine but this keeps parity with
102
+ # the Windows installer's intent (never overwrite an existing backup).
103
+ cp -p "$file" "$backup"
104
+ ok "backed up $(basename "$file") -> $(basename "$backup")"
105
+ fi
106
+ cp -p "$file" "$file.pre-install-$stamp"
107
+ done
108
+ info "pre-install snapshots written as *.pre-install-$stamp"
109
+
110
+ # ------------------------------------------------------- 1+2. profile manifest
111
+ if [ "$uninstall" = 1 ]; then
112
+ "$node_bin" "$plugin_dir/lib/profile-edit.mjs" \
113
+ --profile-dir "$profile_dir" --package "$plugin_name" --remove || die 'manifest edit failed'
114
+ else
115
+ "$node_bin" "$plugin_dir/lib/profile-edit.mjs" \
116
+ --profile-dir "$profile_dir" --package "$plugin_name" --link "$plugin_dir" || die 'manifest edit failed'
117
+ fi
118
+ ok 'package.json updated'
119
+
120
+ # ------------------------------------------------------- 3. legacy mount row
121
+ # The mount row comes from this package's bundle patch (cordis.patch.yml), so
122
+ # nothing is added to the profile patch here. What is removed is the row an
123
+ # earlier revision of this script wrote: Loader `insert` is append-only, so
124
+ # leaving it next to the bundle's own row would mount the plugin twice.
125
+ begin_marker='# >>> dsh-plugin-git-commit-push'
126
+ end_marker='# <<< dsh-plugin-git-commit-push'
127
+
128
+ # Strip any previous block (POSIX awk, no GNU-only flags, no in-place sed: the
129
+ # `-i` flag differs between BSD/macOS and GNU, which is exactly the kind of
130
+ # portability trap this avoids). The file is only replaced when the block was
131
+ # actually there, so a fresh install leaves cordis.patch.yml byte-identical.
132
+ awk -v begin="$begin_marker" -v end="$end_marker" '
133
+ $0 == begin { skipping = 1 }
134
+ skipping != 1 { print }
135
+ $0 == end { skipping = 0; next }
136
+ ' "$patch_file" > "$patch_file.tmp"
137
+
138
+ if cmp -s "$patch_file" "$patch_file.tmp"; then
139
+ rm -f "$patch_file.tmp"
140
+ ok 'cordis.patch.yml: no legacy mount row to remove'
141
+ else
142
+ backup="$patch_file.git-commit-plugin.bak"
143
+ if [ ! -f "$backup" ]; then
144
+ cp -p "$patch_file" "$backup"
145
+ ok "backed up $(basename "$patch_file") -> $(basename "$backup")"
146
+ fi
147
+ mv "$patch_file.tmp" "$patch_file"
148
+ ok 'cordis.patch.yml: legacy mount row removed (the bundle patch mounts the plugin now)'
149
+ fi
150
+
151
+ # ---------------------------------------------------------------- 4. link it
152
+ if [ "$uninstall" = 1 ]; then
153
+ info 'skipping pnpm install (removal)'
154
+ else
155
+ if [ -f "$pnpm_cli" ]; then
156
+ info 'running pnpm install in the profile (this may take a moment)...'
157
+ ( cd "$profile_dir" && "$node_bin" "$pnpm_cli" install ) || die 'pnpm install failed'
158
+ ok 'package linked into the profile'
159
+ else
160
+ warn "bundled pnpm not found at $pnpm_cli"
161
+ warn "run this yourself to link the package: cd '$profile_dir' && pnpm install"
162
+ fi
163
+
164
+ if [ -e "$profile_dir/node_modules/$plugin_name" ]; then
165
+ ok "module present at node_modules/$plugin_name"
166
+ else
167
+ warn "node_modules/$plugin_name is missing; the profile will not find the plugin"
168
+ warn 'run `pnpm install` inside the profile directory and try again'
169
+ fi
170
+ fi
171
+
172
+ # --------------------------------------------------------------------- report
173
+ printf '\n'
174
+ if [ "$uninstall" = 1 ]; then
175
+ printf 'Uninstalled. Restart DeepSeek Harness for the bundle set to change.\n'
176
+ printf 'Backups: %s\n\n' "$manifest.git-commit-plugin.bak"
177
+ else
178
+ printf 'Done. Restart DeepSeek Harness to load the plugin.\n\n'
179
+ printf ' A bundle change is read at profile start, not live, and a host-code\n'
180
+ printf ' change needs a fresh ESM import. Quit DSH first, then start it again.\n\n'
181
+ printf ' After the restart you get:\n'
182
+ printf ' - tool git_commit_push (prepare / apply / auto) [costs model tokens]\n'
183
+ printf ' - command /git-commit-push (no model involved) [0 model tokens]\n\n'
184
+ printf ' The plugin is a declared bundle, so Settings > Plugins can now enable,\n'
185
+ printf ' disable and uninstall it without this script.\n\n'
186
+ printf ' Settings: %s\n' "$plugin_dir/git-commit-push.config.json"
187
+ printf ' Backups : %s\n\n' "$manifest.git-commit-plugin.bak"
188
+ fi
189
+
190
+ exit 0