Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 65 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -325,13 +325,65 @@ jobs:
if-no-files-found: error
retention-days: 30

# ===========================================================================
# Build CLI/MCP companion package
# ===========================================================================
build-companion:
name: Build CLI Companion
runs-on: ubuntu-latest
needs: validate-tag
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Build CLI and MCP entry points
run: |
npm run build:cli
npm run build:mcp

- name: Pack and verify companion
run: |
set -euo pipefail
VERSION=${{ needs.validate-tag.outputs.version }}
PACKAGE="markuprx-${VERSION}.tgz"

npm pack
test -f "$PACKAGE"
tar -tzf "$PACKAGE" | grep -Fx 'package/dist/cli/index.mjs'
tar -tzf "$PACKAGE" | grep -Fx 'package/dist/mcp/index.mjs'

PACKED_VERSION=$(tar -xOf "$PACKAGE" package/package.json \
| node -e "let value=''; process.stdin.on('data', chunk => value += chunk); process.stdin.on('end', () => process.stdout.write(JSON.parse(value).version));")
test "$PACKED_VERSION" = "$VERSION"

PREFIX="$RUNNER_TEMP/companion-prefix"
npm install --global --prefix "$PREFIX" "./$PACKAGE"
test "$("$PREFIX/bin/markuprx" --version)" = "$VERSION"

- name: Upload companion artifact
uses: actions/upload-artifact@v6
with:
name: release-companion
path: markuprx-${{ needs.validate-tag.outputs.version }}.tgz
if-no-files-found: error
retention-days: 30

# ===========================================================================
# Create GitHub Release
# ===========================================================================
create-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: [validate-tag, build-macos, build-windows]
needs: [validate-tag, build-macos, build-windows, build-companion]
permissions:
contents: write
steps:
Expand Down Expand Up @@ -377,14 +429,15 @@ jobs:
echo "| macOS (Apple Silicon) | \`markuprplus-${VERSION}-arm64.dmg\` |" >> $NOTES
echo "| macOS (Intel) | \`markuprplus-${VERSION}-x64.dmg\` |" >> $NOTES
echo "| Windows | \`markuprplus-Setup-${VERSION}.exe\` |" >> $NOTES
echo "| CLI / MCP companion | \`markuprx-${VERSION}.tgz\` |" >> $NOTES
echo "" >> $NOTES
echo "The macOS builds are signed with a Developer ID Application certificate," >> $NOTES
echo "notarized by Apple, and stapled, so they open without a Gatekeeper warning." >> $NOTES
echo "" >> $NOTES
echo "## Checksums" >> $NOTES
echo "" >> $NOTES
echo "\`\`\`" >> $NOTES
find artifacts -type f \( -name "*.dmg" -o -name "*.exe" -o -name "*.zip" \) -exec sha256sum {} \; >> $NOTES
find artifacts -type f \( -name "*.dmg" -o -name "*.exe" -o -name "*.zip" -o -name "*.tgz" \) -exec sha256sum {} \; >> $NOTES
echo "\`\`\`" >> $NOTES

cat $NOTES
Expand All @@ -407,10 +460,19 @@ jobs:
artifacts/release-windows/*.zip
artifacts/release-windows/*.blockmap
artifacts/release-windows/*.yml
artifacts/release-companion/*.tgz
fail_on_unmatched_files: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Verify companion release asset
run: |
VERSION=${{ needs.validate-tag.outputs.version }}
gh release view "$GITHUB_REF_NAME" --json assets --jq '.assets[].name' \
| grep -Fx "markuprx-${VERSION}.tgz"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Release Summary
run: |
echo "## Release Summary" >> $GITHUB_STEP_SUMMARY
Expand All @@ -420,6 +482,6 @@ jobs:
echo "**macOS:** signed, notarized, and stapled" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Artifacts" >> $GITHUB_STEP_SUMMARY
find artifacts -type f \( -name "*.dmg" -o -name "*.exe" -o -name "*.zip" \) | while read f; do
find artifacts -type f \( -name "*.dmg" -o -name "*.exe" -o -name "*.zip" -o -name "*.tgz" \) | while read f; do
echo "- $(basename $f)" >> $GITHUB_STEP_SUMMARY
done
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Notarization no longer skips silently: missing credentials, a non-Developer ID certificate, or a failed submission now fail the release build.
- Release notes now list the real `markuprplus-*` artifact names and no longer overwrite the curated changelog.

## 3.1.1 - 2026-08-30

### Fixed

- AI-analyzed reports now place each marked screenshot directly inside its matching feedback item, beside the finding's narration and metadata.
- Marked evidence that cannot be matched confidently is preserved in an Unmatched Marked Evidence section instead of being discarded.

### Changed

- Bumped the Mac App Store bundle to version 3.1.1, build 4.

## 3.1.0 - 2026-08-30

### Added
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<p align="center">
<a href="https://github.com/hashfunction/MarkuprPlus/actions/workflows/ci.yml?query=branch%3Amain"><img src="https://github.com/hashfunction/MarkuprPlus/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI status"></a>
<a href="https://github.com/hashfunction/MarkuprPlus/actions/workflows/deploy-landing.yml?query=branch%3Amain"><img src="https://github.com/hashfunction/MarkuprPlus/actions/workflows/deploy-landing.yml/badge.svg?branch=main" alt="Deployment status"></a>
<img src="https://img.shields.io/badge/version-3.1.0-f59e0b?style=flat-square" alt="Version 3.1.0">
<img src="https://img.shields.io/badge/version-3.1.1-f59e0b?style=flat-square" alt="Version 3.1.1">
<img src="https://img.shields.io/badge/macOS%20%7C%20Windows-desktop-lightgrey?style=flat-square" alt="Platforms">
<img src="https://img.shields.io/badge/transcription-local%20Whisper-4ade80?style=flat-square" alt="Local Whisper transcription">
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="License"></a>
Expand Down Expand Up @@ -134,7 +134,7 @@ The sandboxed Mac App Store app can use AI command-line tools already installed
Install and pair it from a Terminal:

```bash
npm install -g https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.0/markuprx-3.1.0.tgz
npm install -g https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.1/markuprx-3.1.1.tgz
markuprx bridge install # installs and starts a per-user LaunchAgent
markuprx bridge token # paste this value in Settings → Advanced
markuprx bridge status
Expand Down
4 changes: 2 additions & 2 deletions app-store/metadata/en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ Circle the bug. Give your AI the whole story. MarkuprPlus turns each mark and sp
## Keywords
bug report,screen recorder,AI feedback,annotation,transcription,Markdown,QA,code review,screenshot

## What's New in Version 3.1.0
CLI report integrations are back in the Mac App Store app through an optional local companion. Connect Codex CLI, Claude Code CLI, OpenCode, Cursor Agent, Qwen Code, Goose, Amp, Kiro, or Aider from Advanced Settings. This update also adds secure Keychain pairing, bridge status diagnostics, and stricter report-transport limits. Local Rules remains available with no setup or network connection.
## What's New in Version 3.1.1
Marked screenshots now appear directly inside the matching AI-generated issue, keeping each finding's visual evidence beside its title, narration, and timestamp. Evidence that cannot be matched confidently is preserved in an Unmatched Marked Evidence section. This update also includes the CLI integrations and secure optional local companion introduced in version 3.1.0.

## Description
Circle the bug. Give your AI the whole story.
Expand Down
10 changes: 7 additions & 3 deletions app-store/review-notes.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# App Review Notes — MarkuprPlus 3.1.0
# App Review Notes — MarkuprPlus 3.1.1

MarkuprPlus is a menu bar app for creating structured visual-feedback reports for AI coding agents. It does not require an account and has no developer-operated telemetry.

Expand All @@ -13,11 +13,15 @@ MarkuprPlus is a menu bar app for creating structured visual-feedback reports fo
7. Press Command-Shift-F again to stop. MarkuprPlus writes a local report and opens the review surface.
8. Edit or reorder findings, preview the Markdown, and export or copy the report path.

## Version 3.1.1 fix

Marked screenshots are now placed directly inside their matching AI-generated feedback items. When a screenshot cannot be matched confidently, the evidence remains available in an Unmatched Marked Evidence section instead of being discarded.

## Optional CLI Bridge

Version 3.1.0 restores optional compatibility with Codex CLI, Claude Code CLI, OpenCode, Cursor Agent, Qwen Code, Goose, Amp, Kiro, and Aider. This feature is not required to complete the review walkthrough above.
Version 3.1.1 includes optional compatibility with Codex CLI, Claude Code CLI, OpenCode, Cursor Agent, Qwen Code, Goose, Amp, Kiro, and Aider. This feature is not required to complete the review walkthrough above.

To test it, install the versioned package from the public project release in Terminal with `npm install -g https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.0/markuprx-3.1.0.tgz`, run `markuprx bridge install`, then run `markuprx bridge token`. In MarkuprPlus, open Settings → Advanced, paste that token into CLI Integrations, and choose any compatible CLI already installed and signed in for the reviewer. `markuprx bridge status` reports whether the per-user companion is running.
To test it, install the versioned package from the public project release in Terminal with `npm install -g https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.1/markuprx-3.1.1.tgz`, run `markuprx bridge install`, then run `markuprx bridge token`. In MarkuprPlus, open Settings → Advanced, paste that token into CLI Integrations, and choose any compatible CLI already installed and signed in for the reviewer. `markuprx bridge status` reports whether the per-user companion is running.

The companion binds only to IPv4 loopback at `127.0.0.1:49647`, requires bearer-token authentication, and accepts only a fixed structured report protocol. The Mac App Store app does not execute external command-line tools inside its sandbox, does not accept or send arbitrary shell commands, and does not install the companion. The separately installed companion invokes only the provider explicitly selected by the user.

Expand Down
2 changes: 1 addition & 1 deletion electron-builder.mas.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ appId: com.eddiesanjuan.markuprx
productName: MarkuprPlus
executableName: MarkuprPlus
copyright: Copyright 2026 Trieflow LLC
buildVersion: "3"
buildVersion: "4"

directories:
output: release-mas
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "markuprx",
"productName": "MarkuprPlus",
"version": "3.1.0",
"version": "3.1.1",
"description": "Record your screen, narrate feedback, get structured Markdown with screenshots. Desktop app, CLI, and MCP server for AI coding agents like Claude Code, Cursor, and Windsurf.",
"type": "module",
"main": "dist/main/index.mjs",
Expand Down
2 changes: 1 addition & 1 deletion scripts/verify-brand.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function findBrandViolations(files, readFile, packageJson) {
const expectedPackageFields = {
name: 'markuprx',
productName: 'MarkuprPlus',
version: '3.1.0',
version: '3.1.1',
homepage: 'https://markuprplus.com',
repository: {
type: 'git',
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/settings/CliBridgeSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useTheme } from '../../hooks/useTheme';
import { styles } from './settingsStyles';

export const CLI_BRIDGE_SETUP_COMMANDS = [
'npm install -g https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.0/markuprx-3.1.0.tgz',
'npm install -g https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.1/markuprx-3.1.1.tgz',
'markuprx bridge install',
'markuprx bridge token',
] as const;
Expand Down
4 changes: 3 additions & 1 deletion tests/unit/appStoreMetadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ describe('App Store public metadata', () => {
expect(fields.description).toMatch(/Codex CLI/);
expect(fields.description).toMatch(/optional local companion/i);
expect(fields.description).not.toMatch(/App Store app (?:directly )?(?:runs|executes|launches).*CLI/is);
expect(section(metadata, "What's New in Version 3.1.0").length).toBeLessThanOrEqual(4_000);
const releaseNotes = section(metadata, "What's New in Version 3.1.1");
expect(releaseNotes.length).toBeGreaterThan(0);
expect(releaseNotes.length).toBeLessThanOrEqual(4_000);
});
});
2 changes: 1 addition & 1 deletion tests/unit/brandAudit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type BrandVerifier = {
const compatiblePackageJson = {
name: 'markuprx',
productName: 'MarkuprPlus',
version: '3.1.0',
version: '3.1.1',
homepage: 'https://markuprplus.com',
repository: {
type: 'git',
Expand Down
32 changes: 22 additions & 10 deletions tests/unit/cliBridgeDocumentation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,42 @@ const lifecycleCommands = [
];

describe('CLI Bridge release documentation', () => {
it('sets version 3.1.0 and Mac App Store build 3', async () => {
it('sets version 3.1.1 and Mac App Store build 4', async () => {
const [packageJson, lockJson, masConfig] = await Promise.all([
readFile('package.json', 'utf8').then(JSON.parse),
readFile('package-lock.json', 'utf8').then(JSON.parse),
readFile('electron-builder.mas.yml', 'utf8'),
]);

expect(packageJson.version).toBe('3.1.0');
expect(lockJson.version).toBe('3.1.0');
expect(lockJson.packages[''].version).toBe('3.1.0');
expect(masConfig).toMatch(/^buildVersion: "3"$/m);
expect(packageJson.version).toBe('3.1.1');
expect(lockJson.version).toBe('3.1.1');
expect(lockJson.packages[''].version).toBe('3.1.1');
expect(masConfig).toMatch(/^buildVersion: "4"$/m);
});

it('documents the complete optional companion lifecycle and fixed loopback endpoint', async () => {
const readme = await readFile('README.md', 'utf8');
expect(readme).toMatch(/optional.*companion/is);
expect(readme).toContain(
'npm install -g https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.0/markuprx-3.1.0.tgz',
'npm install -g https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.1/markuprx-3.1.1.tgz',
);
for (const command of lifecycleCommands) expect(readme).toContain(command);
expect(readme).toContain('127.0.0.1:49647');
expect(readme).toMatch(/App Store app does not (?:run|execute|launch) shell commands/i);
});

it('publishes the versioned companion package as a required release asset', async () => {
const workflow = await readFile('.github/workflows/release.yml', 'utf8');

expect(workflow).toMatch(/^ build-companion:$/m);
expect(workflow).toContain('npm run build:cli');
expect(workflow).toContain('npm run build:mcp');
expect(workflow).toContain('markuprx-${VERSION}.tgz');
expect(workflow).toContain('name: release-companion');
expect(workflow).toContain('artifacts/release-companion/*.tgz');
expect(workflow).toContain('Verify companion release asset');
});

it('discloses bridge data flow in Store copy, privacy copy, and review notes', async () => {
const [metadata, reviewNotes, privacyAnswers, privacyPage, changelog] = await Promise.all([
readFile('app-store/metadata/en-US.md', 'utf8'),
Expand All @@ -48,14 +60,14 @@ describe('CLI Bridge release documentation', () => {
expect(content).toMatch(/companion/i);
expect(content).toMatch(/(?:localhost|loopback|127\.0\.0\.1)/i);
}
expect(metadata).toContain("## What's New in Version 3.1.0");
expect(reviewNotes).toContain('# App Review Notes — MarkuprPlus 3.1.0');
expect(metadata).toContain("## What's New in Version 3.1.1");
expect(reviewNotes).toContain('# App Review Notes — MarkuprPlus 3.1.1');
expect(reviewNotes).toContain(
'https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.0/markuprx-3.1.0.tgz',
'https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.1/markuprx-3.1.1.tgz',
);
expect(reviewNotes.indexOf('Local Rules')).toBeLessThan(reviewNotes.indexOf('Optional CLI Bridge'));
expect(reviewNotes).toMatch(/does not execute external.*inside.*sandbox/is);
expect(privacyPage).toMatch(/transcript.*selected screenshots.*companion/is);
expect(changelog).toMatch(/^## 3\.1\.0 - 2026-08-30$/m);
expect(changelog).toMatch(/^## 3\.1\.1 - 2026-08-30$/m);
});
});
2 changes: 1 addition & 1 deletion tests/unit/cliBridgeSetup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
describe('CLI bridge setup presentation', () => {
it('provides deterministic install, start, and token commands', () => {
expect(CLI_BRIDGE_SETUP_COMMANDS).toEqual([
'npm install -g https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.0/markuprx-3.1.0.tgz',
'npm install -g https://github.com/hashfunction/MarkuprPlus/releases/download/v3.1.1/markuprx-3.1.1.tgz',
'markuprx bridge install',
'markuprx bridge token',
]);
Expand Down
12 changes: 6 additions & 6 deletions tests/unit/publicPackageVerification.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,12 +430,12 @@ describe('public package verification', () => {
});

it.each([
'markuprplus-3.1.0-arm64.dmg',
'MarkuprPlus-3.1.0-arm64-mac.zip',
'markuprplus-Setup-3.1.0.exe',
'MarkuprPlus 3.1.0.exe',
'markuprplus-3.1.0-x86_64.AppImage',
'markuprplus-3.1.0-amd64.deb',
'markuprplus-3.1.1-arm64.dmg',
'MarkuprPlus-3.1.1-arm64-mac.zip',
'markuprplus-Setup-3.1.1.exe',
'MarkuprPlus 3.1.1.exe',
'markuprplus-3.1.1-x86_64.AppImage',
'markuprplus-3.1.1-amd64.deb',
])('accepts the canonical filename contract before runtime verification: %s', (name) => {
const root = fixture();
writeFileSync(join(root, name), 'fixture');
Expand Down
Loading