Skip to content

Commit cc3cc85

Browse files
authored
Merge pull request #102 from peopleworks/desktop-installer
0.8.1 — an installer a teacher can run, and a page that stops saying there is none
2 parents 39c0f3c + 8883afe commit cc3cc85

12 files changed

Lines changed: 159 additions & 44 deletions

File tree

.claude-plugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
33
"name": "signs-of-ai",
44
"description": "Measure a text for the signs of AI writing and read back the evidence — offline, EN/ES, with the tool's own false-positive rate attached.",
5-
"version": "0.8.0",
5+
"version": "0.8.1",
66
"author": {
77
"name": "Pedro Hernández — PeopleWorks",
88
"url": "https://github.com/peopleworks"

.github/workflows/desktop-release.yml

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -91,27 +91,45 @@ jobs:
9191
-o "staging/${{ steps.version.outputs.name }}" `
9292
--nologo
9393
94-
- name: Zip and checksum
94+
# The installer is built from the same staging folder the zip is, so the two can never carry
95+
# different files. Inno Setup is not guaranteed on the runner image, so it is installed when it
96+
# is missing rather than assumed. The version is passed in, never written into the script.
97+
- name: Build the installer
98+
shell: pwsh
99+
run: |
100+
$iscc = Join-Path ${env:ProgramFiles(x86)} 'Inno Setup 6\ISCC.exe'
101+
if (-not (Test-Path $iscc)) {
102+
choco install innosetup --no-progress -y
103+
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
104+
}
105+
$staging = (Resolve-Path "staging/${{ steps.version.outputs.name }}").Path
106+
& $iscc "/DAppVersion=${{ steps.version.outputs.version }}" "/DStaging=$staging" "/O$PWD" `
107+
src/SignsOfAI.Desktop/installer/SignsOfAI.iss
108+
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
109+
110+
- name: Package and checksum
95111
id: package
96112
shell: pwsh
97113
run: |
98114
$name = "${{ steps.version.outputs.name }}"
99115
Compress-Archive -Path "staging/$name" -DestinationPath "$name.zip" -CompressionLevel Optimal
100-
$hash = (Get-FileHash "$name.zip" -Algorithm SHA256).Hash.ToLower()
101-
# The sidecar travels with the release; the summary is for pasting into the notes, which
102-
# have carried the checksum inline since desktop-v0.1.0.
103-
"sha256 $hash" | Set-Content "$name.zip.sha256" -NoNewline
104-
"sha256=$hash" >> $env:GITHUB_OUTPUT
105116
106-
$mb = [math]::Round((Get-Item "$name.zip").Length / 1MB)
107-
@(
108-
"### $name.zip"
109-
""
110-
"- **$mb MB**"
111-
"- ``sha256 $hash``"
112-
""
113-
"Paste the checksum line into the release notes."
114-
) | Add-Content $env:GITHUB_STEP_SUMMARY
117+
# The sidecars travel with the release; the summary is for pasting into the notes, which
118+
# have carried the checksum inline since desktop-v0.1.0. Both artifacts get one: the
119+
# installer is the one most people will run, so it is the one most worth checking.
120+
foreach ($file in @("$name-Setup.exe", "$name.zip")) {
121+
$hash = (Get-FileHash $file -Algorithm SHA256).Hash.ToLower()
122+
"sha256 $hash" | Set-Content "$file.sha256" -NoNewline
123+
$mb = [math]::Round((Get-Item $file).Length / 1MB)
124+
@(
125+
"### $file"
126+
""
127+
"- **$mb MB**"
128+
"- ``sha256 $hash``"
129+
""
130+
) | Add-Content $env:GITHUB_STEP_SUMMARY
131+
}
132+
"Paste both checksum lines into the release notes." | Add-Content $env:GITHUB_STEP_SUMMARY
115133
116134
- name: Attach to the release
117135
if: github.event_name == 'release'
@@ -120,13 +138,16 @@ jobs:
120138
GH_TOKEN: ${{ github.token }}
121139
run: |
122140
$name = "${{ steps.version.outputs.name }}"
123-
gh release upload $env:GITHUB_REF_NAME "$name.zip" "$name.zip.sha256" --clobber
141+
gh release upload $env:GITHUB_REF_NAME `
142+
"$name-Setup.exe" "$name-Setup.exe.sha256" "$name.zip" "$name.zip.sha256" --clobber
124143
125144
# Dispatch runs produce something to download and check by hand before tagging for real.
126145
- name: Keep the build as a run artifact
127146
if: github.event_name == 'workflow_dispatch'
128147
uses: actions/upload-artifact@v4
129148
with:
130149
name: ${{ steps.version.outputs.name }}
131-
path: ${{ steps.version.outputs.name }}.zip
150+
path: |
151+
${{ steps.version.outputs.name }}-Setup.exe
152+
${{ steps.version.outputs.name }}.zip
132153
retention-days: 7

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
Claude Code, Codex, Gemini CLI and Cursor, in one line: `npx skills add peopleworks/SignsofAI -g`.
2121
It edits by the same rules this engine scores by, and it never invents a number.
2222

23-
**[Download the Windows app →](https://github.com/peopleworks/SignsofAI/releases?q=desktop&expanded=true)** — the same tool in a window. Nothing to install alongside it: the .NET runtime is bundled.
23+
**[Download the Windows app →](https://peopleworks.github.io/SignsofAI/download)** — the same tool in a window. An installer that needs no administrator rights, with the .NET runtime bundled; a .zip too, if you would rather not.
2424

2525
![Signs of AI Writing analyzing text live: the score climbs as AI tells accumulate, then every tell is highlighted with a fix](Docs/screenshots/analyze-live.gif)
2626

src/SignsOfAI.Cli/SignsOfAI.Cli.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<PackAsTool>true</PackAsTool>
1414
<ToolCommandName>signsofai</ToolCommandName>
1515
<PackageId>SignsOfAI.Cli</PackageId>
16-
<Version>0.8.0</Version>
16+
<Version>0.8.1</Version>
1717
<Authors>Pedro Hernández (PeopleWorks)</Authors>
1818
<Company>PeopleWorks</Company>
1919
<Description>CLI to detect the signs of AI writing (English &amp; Spanish) and recommend fixes — lint your prose in CI.</Description>

src/SignsOfAI.Core/SignsOfAI.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
<!-- Publishable as a library so others can embed the engine. -->
1212
<PackageId>SignsOfAI.Core</PackageId>
13-
<Version>0.8.0</Version>
13+
<Version>0.8.1</Version>
1414
<Authors>Pedro Hernández (PeopleWorks)</Authors>
1515
<Company>PeopleWorks</Company>
1616
<Description>The engine behind Signs of AI Writing: detects the stylometric tells of AI-generated text (English &amp; Spanish) — overused vocabulary, rhetorical crutches, syntactic tells and burstiness — and returns an actionable fix for each finding. No external dependencies.</Description>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
; The Windows installer for Signs of AI Writing.
2+
;
3+
; It exists because the audience this app is for — a teacher who wants to open a folder of PDFs —
4+
; was being asked to right-click a zip, tick Unblock, run Get-FileHash in PowerShell, extract it,
5+
; and then keep the folder together, because the app's web assets must sit next to the executable.
6+
; An installer does the last three for them and cannot get the folder wrong.
7+
;
8+
; It does NOT make the app trusted. It is unsigned, exactly like the executable inside it, so
9+
; SmartScreen still warns on first run. Only code signing removes that, and the download page says so.
10+
;
11+
; Built by .github/workflows/desktop-release.yml, which passes the two values that change:
12+
;
13+
; ISCC /DAppVersion=0.8.1 /DStaging=staging\SignsOfAI-Desktop-0.8.1-win-x64 SignsOfAI.iss
14+
;
15+
; No version is written in this file. It is already typed in seven places that ReleaseVersionTests
16+
; holds together, and an eighth copy here is how one of them would end up disagreeing.
17+
18+
#ifndef AppVersion
19+
#error Pass the version on the command line: /DAppVersion=0.8.1
20+
#endif
21+
#ifndef Staging
22+
#error Pass the publish folder on the command line: /DStaging=path\to\publish
23+
#endif
24+
25+
[Setup]
26+
; Never change this. It is how Windows recognises a newer installer as the same app to upgrade,
27+
; rather than installing a second copy beside the first.
28+
AppId={{FDD6B2FD-9E64-4C17-BA52-C0BFB387BA85}
29+
AppName=Signs of AI Writing
30+
AppVersion={#AppVersion}
31+
AppVerName=Signs of AI Writing {#AppVersion}
32+
AppPublisher=PeopleWorks
33+
AppPublisherURL=https://github.com/peopleworks/SignsofAI
34+
AppSupportURL=https://github.com/peopleworks/SignsofAI/issues
35+
AppUpdatesURL=https://peopleworks.github.io/SignsofAI/download
36+
37+
; For the current user only, into their own profile. School laptops are often managed, and a teacher
38+
; who is not an administrator must still be able to install it without asking IT.
39+
PrivilegesRequired=lowest
40+
DefaultDirName={localappdata}\Programs\SignsOfAI
41+
DisableProgramGroupPage=yes
42+
DisableDirPage=auto
43+
44+
; Windows 10 1809, the oldest the download page promises; 64-bit only, like the build.
45+
MinVersion=10.0.17763
46+
ArchitecturesAllowed=x64compatible
47+
ArchitecturesInstallIn64BitMode=x64compatible
48+
49+
OutputBaseFilename=SignsOfAI-Desktop-{#AppVersion}-win-x64-Setup
50+
SetupIconFile=..\appicon.ico
51+
UninstallDisplayIcon={app}\SignsOfAI.Desktop.exe
52+
UninstallDisplayName=Signs of AI Writing
53+
WizardStyle=modern
54+
Compression=lzma2/max
55+
SolidCompression=yes
56+
57+
[Languages]
58+
Name: "english"; MessagesFile: "compiler:Default.isl"
59+
Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl"
60+
61+
[Tasks]
62+
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
63+
64+
[Files]
65+
; The whole publish tree, subfolders included — wwwroot above all. That is the folder the zip told
66+
; people to keep together, and here it cannot be split.
67+
Source: "{#Staging}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
68+
69+
[Icons]
70+
Name: "{autoprograms}\Signs of AI Writing"; Filename: "{app}\SignsOfAI.Desktop.exe"
71+
Name: "{autodesktop}\Signs of AI Writing"; Filename: "{app}\SignsOfAI.Desktop.exe"; Tasks: desktopicon
72+
73+
[Run]
74+
Filename: "{app}\SignsOfAI.Desktop.exe"; Description: "{cm:LaunchProgram,Signs of AI Writing}"; Flags: nowait postinstall skipifsilent

src/SignsOfAI.Mcp/.mcp/server.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
22
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
33
"name": "io.github.peopleworks/signs-of-ai",
4-
"version": "0.8.0",
4+
"version": "0.8.1",
55
"title": "Signs of AI Writing",
66
"description": "AI-writing detection (EN/ES) that shows the evidence: named tells, hidden characters, citations.",
77
"packages": [
88
{
99
"registryType": "nuget",
1010
"registryBaseUrl": "https://api.nuget.org/v3/index.json",
1111
"identifier": "SignsOfAI.Mcp",
12-
"version": "0.8.0",
12+
"version": "0.8.1",
1313
"transport": {
1414
"type": "stdio"
1515
},

src/SignsOfAI.Mcp/SignsOfAI.Mcp.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<PackAsTool>true</PackAsTool>
1313
<ToolCommandName>signsofai-mcp</ToolCommandName>
1414
<PackageId>SignsOfAI.Mcp</PackageId>
15-
<Version>0.8.0</Version>
15+
<Version>0.8.1</Version>
1616
<Authors>Pedro Hernández (PeopleWorks)</Authors>
1717
<Company>PeopleWorks</Company>
1818
<Description>Model Context Protocol (MCP) server for Signs of AI Writing. Exposes the engine as tools — analyze text for AI tells, check originality between documents, search the sign catalog, extract distinctive phrases, and (optionally, via the hosted API) measure perplexity and find paraphrases — to Claude Desktop and any MCP client.</Description>

src/SignsOfAI.UI/Pages/Download.razor

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,16 @@
5555
else
5656
{
5757
<section class="card dl-get">
58-
<a class="dl-cta" href="@DesktopRelease.ZipUrl">
58+
@* The installer first. The zip stays one click away for anyone who would rather not run an
59+
installer, but it is no longer what a teacher is handed by default. *@
60+
<a class="dl-cta" href="@DesktopRelease.SetupUrl">
5961
<Icon Name="download" />
6062
<span class="dl-cta-main">@L.F("dl.get", DesktopRelease.Version)</span>
6163
<span class="dl-cta-sub">@L["dl.for"]</span>
6264
</a>
6365
<p class="hint sub">
66+
<a href="@DesktopRelease.ZipUrl">@L["dl.zip.link"]</a>
67+
·
6468
<a href="@DesktopRelease.ReleaseUrl" target="_blank" rel="noopener">@L["dl.notes"]</a>
6569
·
6670
<a href="@DesktopRelease.AllReleasesUrl" target="_blank" rel="noopener">@L["dl.all"]</a>
@@ -100,9 +104,12 @@ else
100104
<p>@L.M("dl.warn.body")</p>
101105
<ol class="dl-steps">
102106
<li>@L["dl.warn.step1"]</li>
103-
<li>@L["dl.warn.step2"]</li>
104-
<li>@L.M("dl.warn.step3")</li>
107+
<li>@L.M("dl.warn.step2")</li>
108+
<li>@L["dl.warn.step3"]</li>
105109
</ol>
110+
@* Checking the file is still worth doing and still explained — it just no longer stands between a
111+
teacher and the app. Before the installer it was step two of three. *@
112+
<p class="hint">@L.M("dl.verify")</p>
106113
<p class="hint">@L.M("dl.zip.body")</p>
107114
</section>
108115

src/SignsOfAI.UI/Services/DesktopRelease.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,20 @@ namespace SignsOfAI.UI.Services;
2020
public static class DesktopRelease
2121
{
2222
/// <summary>The published version, without the <c>desktop-v</c> prefix its tag carries.</summary>
23-
public const string Version = "0.8.0";
23+
public const string Version = "0.8.1";
2424

2525
/// <summary>
26-
/// The .zip itself, so the button downloads rather than starting a scavenger hunt through a
27-
/// release page. Interpolated from <see cref="Version"/> so the two cannot disagree.
26+
/// The installer, which is what the page offers first. It installs for the current user only, so
27+
/// a teacher on a managed laptop needs no administrator — and it cannot split the folder the app
28+
/// needs kept together, which the zip left to the person extracting it. Still unsigned, so it
29+
/// warns exactly as the executable does. Interpolated from <see cref="Version"/>, like the zip.
30+
/// </summary>
31+
public const string SetupUrl =
32+
$"https://github.com/peopleworks/SignsofAI/releases/download/desktop-v{Version}/SignsOfAI-Desktop-{Version}-win-x64-Setup.exe";
33+
34+
/// <summary>
35+
/// The .zip itself, for anyone who would rather not run an installer: extract anywhere, delete the
36+
/// folder to remove it. Interpolated from <see cref="Version"/> so the two cannot disagree.
2837
/// </summary>
2938
public const string ZipUrl =
3039
$"https://github.com/peopleworks/SignsofAI/releases/download/desktop-v{Version}/SignsOfAI-Desktop-{Version}-win-x64.zip";

0 commit comments

Comments
 (0)