Creating a New PSI Tool

Decision guide for building and distributing new tools at PSI. Covers where to host it, how to deploy it, and which distribution channel to use.


Where Does My Tool Go?

What are you building?
        │
        ├── Desktop app used by everyone (shop floor, office)
        │   └── ProApps pipeline → DFS share → Pro Update
        │       See: [[onboard-new-app]]
        │
        ├── Web application (browser-based)
        │   └── Azure App Service or Static Web App
        │       See: [[deploy-to-azure]]
        │
        ├── CLI / developer tool (command-line)
        │   └── PSI WinGet Source → winget install PSI.MyTool
        │       See below ↓
        │
        ├── API / backend service
        │   └── Azure App Service (container or zip deploy)
        │       See: [[deploy-to-azure]]
        │
        └── Windows Service (runs in background)
            └── ProApps pipeline (service variant)
                See: [[onboard-new-app#if-your-app-is-a-windows-service|If Your App Is a Windows Service]]

Quick rule: If a developer types it in a terminal, it goes to WinGet. If an employee clicks an icon, it goes to Pro Update. If it runs in a browser, it goes to Azure.


Creating a CLI Tool for PSI WinGet

Step 1: Create the GHE Repo

Create a new repo at progressivesurface.ghe.com/ProgressiveSurface/your-tool-name.

Step 2: Write Your Tool

Python tools — single script with a version:

#!/usr/bin/env python3
"""My PSI Tool — does the thing."""
 
__version__ = "1.0.0"
 
# Your code here...

.NET tools — set version in .csproj:

<PropertyGroup>
  <Version>1.0.0</Version>
</PropertyGroup>

Step 3: Choose Your Package ID

Follow the naming convention: PSI.<ToolName>

ExamplePackage ID
Claude Session ManagerPSI.CSM
Developer SetupPSI.DevSetup
Export CLIPSI.ExportCLI
PLC RunnerPSI.PLCRunner

Step 4: Add the Publish Workflow

Copy the appropriate template from the psi-winget-source repo to .github/workflows/release.yml in your repo.

Templates available:

  • release-python.yml — Python tools (PyInstaller)
  • release-dotnet.yml — .NET tools (self-contained publish)

Only 5 values to change at the top of the file:

env:
  PACKAGE_ID: "PSI.MyTool"              # WinGet package identifier
  PACKAGE_NAME: "My Tool"               # Display name
  DESCRIPTION: "What this tool does"    # Short description
  SCRIPT_FILE: "mytool.py"             # Main script (Python) or PROJECT_PATH (dotnet)
  EXE_NAME: "mytool"                   # Command name users will type

Step 5: Push and Release

git add .
git commit -m "Initial tool implementation"
git push origin main

The workflow:

  1. Reads __version__ (Python) or <Version> (.NET) from your code
  2. Checks if that version is already published — skips if so
  3. Builds the executable
  4. Uploads to Azure Blob Storage
  5. Registers in the PSI WinGet Source API
  6. Users can now winget install PSI.MyTool --source PSI

To release a new version: bump the version in your code and push.

__version__ = "1.1.0"  # Was 1.0.0

That’s it. No manual steps.


How It Works Under the Hood

Developer pushes to main (with version bump)
    │
    └──► GitHub Actions (ps-gr-runner)
         │
         ├── Read version from code
         ├── Check if already published (skip if so)
         ├── Build executable (PyInstaller or dotnet publish)
         ├── Compute SHA256
         ├── Upload zip to Azure Blob Storage (psiwingetpkgs)
         └── Register version via PSI WinGet Source API
              │
              └──► packages.progressivesurface.com/api/admin/packages/{id}/versions
                   │
                   └──► Azure SQL (persistent package registry)

Users:
    winget upgrade --source PSI  →  sees new version  →  installs

What the templates handle automatically:

  • Creating the package in WinGet if it doesn’t exist yet (first publish)
  • Skipping if the version is already published (idempotent)
  • Building, packaging, hashing, uploading, and registering
  • Managed identity auth to Azure (no secrets to configure)

Distribution Channel Comparison

PSI WinGetPro Update (DFS)Azure App Service
ForCLI/dev toolsDesktop apps (all employees)Web apps
Installwinget install PSI.XPro Update clientBrowser
Updatewinget upgrade or app self-checkAuto-poll every 5minAuto-deploy on push
BuildPyInstaller / dotnet publishMSBuildnpm build / dotnet publish
StorageAzure Blob + WinGet APIDFS network shareApp Service
AuthIntune pushes sourceAD username in PSApplications.txtEntra ID / MSAL
AudienceDevelopers (5-10)All employees (200+)Internal users

Checklist

  • Repo created on GHE
  • Tool has __version__ (Python) or <Version> (.NET) in the code
  • Workflow file copied from template and 5 env vars updated
  • First push to main — workflow runs and publishes v1.0.0
  • Verify: winget search --source PSI PSI.YourTool
  • Verify: winget install PSI.YourTool --source PSI
  • Update this wiki if adding a new tool to the Available Packages list

Available Packages

Package IDNameRepoDescription
PSI.CSMClaude Session Managerclaude-session-managerTUI for browsing/resuming Claude Code sessions (csm); also ships the Claude Work Board (csm board) — a live local web app showing every project (sessions ↔ git ↔ GHE Issues/PRs ↔ Azure DevOps). One-click ”▶ start Claude” on any issue launches an interactive claude --remote-control session and opens claude.ai/code/session_<id> in a new tab so you can drive it from any device. Alt-click for unattended claude -p

Update this table when adding new packages.



Last updated: April 2026