Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e1713bc
LinkBar v0.4: the review gate in your menu bar
gowtham0992 Jul 12, 2026
345b1f0
LinkBar v0.5: the design pass
gowtham0992 Jul 12, 2026
c4e59da
bundle.sh --install replaces /Applications/LinkBar.app and relaunches
gowtham0992 Jul 12, 2026
8d6ec00
LinkBar: settings as an in-popover pane — sheets misbehave in MenuBar…
gowtham0992 Jul 12, 2026
d876eb9
Capture provenance: reviewable, correctly-attributed session context
gowtham0992 Jul 12, 2026
3ec7c6c
LinkBar: capture rows expand to 'How Link read this session' decision…
gowtham0992 Jul 12, 2026
1805737
Merge develop: core hardening (injection, slug bounds, capture race)
gowtham0992 Jul 12, 2026
f517b7a
Merge develop: final benchmark numbers in README + homepage
gowtham0992 Jul 12, 2026
4521c95
LinkBar v0.6: living overview, activity pulse, and review-pass fixes
gowtham0992 Jul 12, 2026
079ccc9
LinkBar 1.0: launch the new app at 1.0, not 0.6
gowtham0992 Jul 12, 2026
6c28338
Rank proposals by durability; single-hero Quick Start; quieter onboard
gowtham0992 Jul 12, 2026
fb7c79c
Merge develop: proposal ranking + onboarding polish
gowtham0992 Jul 12, 2026
3bbea76
Merge develop: CI fixes (type ratchet, Windows, MCP smoke)
gowtham0992 Jul 12, 2026
6cf3898
Merge develop: MCP smoke + windows cold-walk CI fixes
gowtham0992 Jul 13, 2026
273d6c2
Merge develop: macOS smoke isolation
gowtham0992 Jul 13, 2026
61f15f8
Merge develop: macOS smoke slim-first fix
gowtham0992 Jul 13, 2026
c97d5b6
Merge develop: macOS cold-onboard smoke fix
gowtham0992 Jul 13, 2026
347e5bb
LinkBar: tabbed UI with a Status dashboard for every Link surface
gowtham0992 Jul 13, 2026
e8a2a92
LinkBar: Status fixes verify their outcome instead of claiming success
gowtham0992 Jul 13, 2026
a9276f4
Merge branch 'develop' into feature/menubar
gowtham0992 Jul 17, 2026
788a7ec
Homepage: demo terminals become alternating side-by-side rows
gowtham0992 Jul 17, 2026
b100df2
PyPI readme: surface the benchmark results instead of one buried link
gowtham0992 Jul 17, 2026
047f46a
README: pepy downloads badge
gowtham0992 Jul 18, 2026
62fad28
LinkBar: Memory Palette — global-hotkey recall/remember from any app
gowtham0992 Jul 18, 2026
57ecb51
LinkBar: live agent pulse — see memory being made in real time
gowtham0992 Jul 18, 2026
b20ba8e
LinkBar: capture notifications — built, gated on real authorization
gowtham0992 Jul 18, 2026
2ad9dd5
LinkBar: Memory browser — every memory file, searchable and filterable
gowtham0992 Jul 18, 2026
b2bc870
Homepage: version banner, per-agent onboard picker, origin story
gowtham0992 Jul 18, 2026
dd90496
Restore LinkBar .build gitignore at app and root level
gowtham0992 Jul 28, 2026
47d8d47
Fix semantic status count: index embeds all memories, so denominator …
gowtham0992 Jul 28, 2026
6a1f851
2.0 story: LinkBar in CHANGELOG + README with screenshots
gowtham0992 Jul 28, 2026
1b44432
LinkBar release mechanics: bundle.sh --release-zip, cask template, 2.…
gowtham0992 Jul 28, 2026
ad1fe48
prepare_release also bumps the homepage release banner
gowtham0992 Jul 28, 2026
0d67d69
Fix: homepage banner update must not require docs/index.html
gowtham0992 Jul 28, 2026
b63dc9c
getting-started: add LinkBar as step 10, renumber semantic/FAQ
gowtham0992 Jul 28, 2026
1bb11ef
Release checklist: rewrite formula caveats to surface LinkBar and dro…
gowtham0992 Jul 28, 2026
c6db2ba
Release 2.0.0: version stamps, changelog, homepage banner
gowtham0992 Jul 28, 2026
da58c42
Lower type ratchet baseline 387 -> 385
gowtham0992 Jul 28, 2026
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,6 @@ Link Console Handoff.html

# vhs demo render workspace
docs/media/.aha-demo/

# LinkBar build artifacts (never commit)
apps/LinkBar/.build/
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,33 @@ Release sections use `MAJOR.MINOR.PATCH` versions that match `link-mcp` on PyPI

## [Unreleased]

## [2.0.0] - 2026-07-27

Link gets a face. The memory layer is unchanged in shape — plain Markdown,
review-gated writes, no LLM in the write path — but it is no longer only a
CLI and an MCP server: **LinkBar** puts the review gate in your macOS menu
bar, and memory becomes something that meets you rather than somewhere you
go. That shift is why this is a major version.

**No breaking changes.** Every CLI command, MCP tool, hook, and memory file
from 1.7 works exactly as before; upgrading is `brew upgrade link`.

### Added

- **LinkBar 1.0** — Link's memory, ambient in your macOS menu bar (`apps/LinkBar`, Swift/SwiftUI, the `lnk --json` CLI is its entire backend). The review gate stops being a place you go and starts being something that meets you:
- **Memory Palette**: a global hotkey (⌥⌘M) opens a floating panel over any app — type to recall (copy straight into what you're writing), prefix with `+` to remember, review-gated as always.
- **Live agent pulse**: LinkBar detects agent sessions writing transcripts right now and shows a breathing "N agents active · project" row; the menu-bar icon carries a quiet green dot while memory is being made.
- **Capture notifications**: a new session capture posts a native banner — "Will save: <proposal>" — with an Accept action right on it. Confirmed working on an unsigned/ad-hoc build (no Apple Developer signature required).
- **Memory browser**: a Memory tab listing every memory file — search, type filters, archived toggle, supersede lineage, archive/restore — read straight from `wiki/memories/*.md`.
- **Status dashboard**: health dots for CLI, workspace, MCP, hooks, recall tier, and viewer, each with a one-click fix that verifies its outcome before claiming success.
- **Inbox**: review/accept memories and captures with "Will save" previews, decision trails ("How Link read this session"), and per-proposal Accept menus.
- Distributed as an unsigned cask with a postflight quarantine-strip — `brew install --cask gowtham0992/link/linkbar` — zero Gatekeeper friction, zero signing fees.
- Homepage refresh: version banner, per-agent onboard picker (real `lnk onboard --agent <yours> --write` commands), origin-story section, and the three demo terminals as alternating side-by-side rows.

### Fixed

- `lnk semantic` status reported "Indexed memories: 16 of 6" — the index embeds every memory (archived included; they stay inert in default recall), so the denominator is now the total memory count, not active-only.

## [1.7.0] - 2026-07-17

### Added
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<a href="https://github.com/gowtham0992/link/actions/workflows/ci.yml"><img src="https://github.com/gowtham0992/link/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<a href="https://registry.modelcontextprotocol.io/?q=io.github.gowtham0992%2Flink"><img src="https://img.shields.io/badge/MCP_Registry-io.github.gowtham0992%2Flink-blue" alt="MCP Registry"></a>
<a href="https://pypi.org/project/link-mcp/"><img src="https://img.shields.io/pypi/v/link-mcp" alt="PyPI"></a>
<a href="https://pepy.tech/projects/link-mcp"><img src="https://static.pepy.tech/personalized-badge/link-mcp?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads" alt="PyPI Downloads"></a>
</p>

<p align="center">
Expand Down Expand Up @@ -210,6 +211,27 @@ The generated demo is the public proof wiki. Generated content inside `wiki/`,
`raw/`, and `link-demo/` is ignored by git so personal memory is not published
by accident.

## LinkBar — the menu bar app (macOS)

Link's memory, ambient. LinkBar puts the review gate in your menu bar: a
global palette (⌥⌘M) to recall or remember from any app, native
notifications with one-tap Accept when a session capture lands, a live
pulse while agents are writing, and a browser over every memory file —
all running on the same reviewed `lnk` commands as the CLI.

<p align="center">
<img src="docs/assets/linkbar-inbox.png" alt="LinkBar inbox: live agent pulse, session captures with Will-save previews and decision trails" width="380">
<img src="docs/assets/linkbar-status.png" alt="LinkBar status: health dots for CLI, workspace, MCP, hooks, recall tier, and viewer with one-click fixes" width="380">
</p>

```bash
brew install --cask gowtham0992/link/linkbar
```

Unsigned on purpose (no Apple fee inflating anything): the cask strips
the quarantine flag on install, so it opens like any app. Building from
source instead: `cd apps/LinkBar && bash Scripts/bundle.sh --install`.

## Killer Demo: One Memory, Two Agents

This is the moment Link is built for:
Expand Down
1 change: 1 addition & 0 deletions apps/LinkBar/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.build/
14 changes: 14 additions & 0 deletions apps/LinkBar/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// swift-tools-version: 5.10
import PackageDescription

let package = Package(
name: "LinkBar",
platforms: [.macOS(.v14)],
targets: [
.executableTarget(
name: "LinkBar",
path: "Sources/LinkBar",
resources: [.process("Resources")]
)
]
)
25 changes: 25 additions & 0 deletions apps/LinkBar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# LinkBar

The review gate in your menu bar. Link's promise is that nothing becomes
durable memory without your approval; LinkBar makes approving ambient
instead of a chore.

- Badge: pending-review count · Popover: the review inbox with one-click
approve (mark reviewed) and archive
- Quick recall ("What do I know about…") with honest abstention — when the
memory has nothing reliable, it says so
- Backend: the `lnk` CLI's `--json` output. No server, no sockets, no new
API surface. Workspace: `LINK_WORKSPACE` or `~/link`.

## Build & run

```
cd apps/LinkBar
swift build # debug binary
./Scripts/bundle.sh # release .app bundle at .build/LinkBar.app
open .build/LinkBar.app
```

Requires macOS 14+, Swift 5.10+, and Link installed (`brew install
gowtham0992/link/link`). Status: early preview on the feature/menubar
branch — not yet part of a release.
51 changes: 51 additions & 0 deletions apps/LinkBar/Scripts/bundle.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/sh
# Wrap the SPM executable in a minimal .app bundle (menu-bar agent app).
set -e
cd "$(dirname "$0")/.."
swift build -c release
APP=.build/LinkBar.app
rm -rf "$APP"
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
cp .build/release/LinkBar "$APP/Contents/MacOS/LinkBar"
cp Sources/LinkBar/Resources/AppIcon.icns "$APP/Contents/Resources/AppIcon.icns"
cp -R .build/release/LinkBar_LinkBar.bundle "$APP/Contents/Resources/" 2>/dev/null || true
cat > "$APP/Contents/Info.plist" <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key><string>io.github.gowtham0992.linkbar</string>
<key>CFBundleName</key><string>LinkBar</string>
<key>CFBundleDisplayName</key><string>LinkBar</string>
<key>CFBundleExecutable</key><string>LinkBar</string>
<key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleShortVersionString</key><string>1.0.0</string>
<key>LSMinimumSystemVersion</key><string>14.0</string>
<key>LSUIElement</key><true/>
<key>CFBundleIconFile</key><string>AppIcon</string>
</dict>
</plist>
PLIST
codesign --force --sign - "$APP" 2>/dev/null || true
echo "bundled: $APP"

# --release-zip: produce the distributable zip the cask points at.
if [ "$1" = "--release-zip" ]; then
VERSION=$(grep -o 'static let version = "[^"]*"' Sources/LinkBar/DesignSystem.swift | grep -o '"[^"]*"' | tr -d '"')
ZIP=".build/LinkBar-${VERSION}.0.zip"
rm -f "$ZIP"
ditto -c -k --keepParent "$APP" "$ZIP"
shasum -a 256 "$ZIP"
echo "release zip: $ZIP (attach to the GitHub release; put the sha256 in the cask)"
exit 0
fi

# --install: replace /Applications/LinkBar.app with this build and relaunch.
if [ "$1" = "--install" ]; then
pkill -x LinkBar 2>/dev/null || true
sleep 1
rm -rf /Applications/LinkBar.app
cp -R "$APP" /Applications/LinkBar.app
open /Applications/LinkBar.app
echo "installed: /Applications/LinkBar.app (running)"
fi
193 changes: 193 additions & 0 deletions apps/LinkBar/Sources/LinkBar/DesignSystem.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import SwiftUI

/// LinkBar's design tokens: native macOS materials carry the structure,
/// Link's rust carries the opinion, one serif wordmark is the signature.
enum LinkBrand {
/// One source of truth for the app version shown in footer + settings.
static let version = "1.0"

/// Rust — Link's accent. Lifted and desaturated slightly in dark mode
/// so it reads as warm, not muddy, on dark materials.
static let rust = Color(nsColor: NSColor(name: nil) { appearance in
appearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
? NSColor(red: 0.85, green: 0.47, blue: 0.34, alpha: 1) // #d97856
: NSColor(red: 0.66, green: 0.29, blue: 0.18, alpha: 1) // #a8492f
})

/// Spacing scale (4/8 rhythm): tight within groups, generous between.
static let inGroup: CGFloat = 6
static let betweenSections: CGFloat = 16
static let pad: CGFloat = 14
}

/// Uppercase tracked section label with an optional trailing count.
struct SectionHeader: View {
let title: String
var count: Int?
var highlight = false

var body: some View {
HStack(spacing: 6) {
Text(title.uppercased())
.font(.system(size: 10.5, weight: .semibold))
.tracking(0.7)
.foregroundStyle(.secondary)
Spacer()
if let count, count > 0 {
Text("\(count)")
.font(.system(size: 10.5, weight: .semibold))
.foregroundStyle(highlight ? Color.white : Color.secondary)
.padding(.horizontal, 6)
.padding(.vertical, 1.5)
.background(highlight ? AnyShapeStyle(LinkBrand.rust) : AnyShapeStyle(.quaternary))
.clipShape(Capsule())
}
}
}
}

/// Row container with a soft hover highlight — the popover feels alive.
struct HoverRow<Content: View>: View {
@State private var hovering = false
@ViewBuilder let content: Content

var body: some View {
content
.padding(.horizontal, 8)
.padding(.vertical, 6)
.background(
RoundedRectangle(cornerRadius: 7)
.fill(hovering ? AnyShapeStyle(.quaternary.opacity(0.6)) : AnyShapeStyle(.clear))
)
.onHover { hovering = $0 }
.animation(.easeOut(duration: 0.15), value: hovering)
}
}

/// The Link wordmark: the one serif moment in the app.
struct Wordmark: View {
var body: some View {
HStack(spacing: 7) {
Circle()
.fill(LinkBrand.rust)
.frame(width: 7, height: 7)
Text("Link")
.font(.system(size: 15, weight: .semibold, design: .serif))
}
}
}


/// Fourteen days of memory activity as tiny bars — the "pulse" of the
/// workspace. Rust bars on quiet quaternary track; today is the last bar.
struct Sparkline: View {
let values: [Int]

private var peak: Int { max(values.max() ?? 1, 1) }

var body: some View {
HStack(alignment: .bottom, spacing: 3) {
ForEach(Array(values.enumerated()), id: \.offset) { _, value in
RoundedRectangle(cornerRadius: 1.5)
.fill(value > 0 ? AnyShapeStyle(LinkBrand.rust.opacity(0.55 + 0.45 * Double(value) / Double(peak)))
: AnyShapeStyle(.quaternary.opacity(0.6)))
.frame(width: 9, height: value > 0 ? max(6, 26 * CGFloat(value) / CGFloat(peak)) : 3)
}
}
.frame(height: 26, alignment: .bottom)
.accessibilityLabel("Memory activity, last \(values.count) days")
}
}

/// Small stat: big number, quiet label underneath.
struct StatChip: View {
let value: String
let label: String
var tint: Color = .primary

var body: some View {
VStack(spacing: 1) {
Text(value)
.font(.system(size: 16, weight: .semibold, design: .rounded))
.foregroundStyle(tint)
Text(label.uppercased())
.font(.system(size: 8.5, weight: .medium))
.tracking(0.5)
.foregroundStyle(.tertiary)
}
.frame(maxWidth: .infinity)
}
}

/// A colored health dot for a Link surface (green / amber / red / neutral).
struct HealthDot: View {
let level: SurfaceHealth.Level
var body: some View {
let c = level.color
Circle()
.fill(Color(red: c.r, green: c.g, blue: c.b))
.frame(width: 8, height: 8)
.overlay(
Circle().stroke(Color(red: c.r, green: c.g, blue: c.b).opacity(0.35), lineWidth: 3)
.opacity(level == .error || level == .warn ? 1 : 0)
)
}
}

/// One row in the Status dashboard: dot · icon · name/detail · optional Fix.
struct StatusRow: View {
let surface: SurfaceHealth
@State private var hovering = false

var body: some View {
HStack(spacing: 10) {
HealthDot(level: surface.level)
Image(systemName: surface.icon)
.font(.system(size: 12))
.foregroundStyle(.secondary)
.frame(width: 16)
VStack(alignment: .leading, spacing: 1) {
Text(surface.name)
.font(.system(size: 12.5, weight: .medium))
Text(surface.detail)
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(1)
.truncationMode(.tail)
}
Spacer(minLength: 6)
if let fix = surface.fix {
Button(fix.label) { fix.action() }
.buttonStyle(.bordered)
.controlSize(.small)
.tint(LinkBrand.rust)
}
}
.padding(.horizontal, 8).padding(.vertical, 6)
.background(
RoundedRectangle(cornerRadius: 7)
.fill(hovering ? AnyShapeStyle(.quaternary.opacity(0.5)) : AnyShapeStyle(.clear))
)
.onHover { hovering = $0 }
.animation(.easeOut(duration: 0.15), value: hovering)
}
}

/// A softly breathing green dot — "agents are working right now".
struct PulseDot: View {
@State private var on = false
var body: some View {
Circle()
.fill(Color.green)
.frame(width: 7, height: 7)
.overlay(
Circle()
.stroke(Color.green.opacity(0.5), lineWidth: 1.5)
.scaleEffect(on ? 2.1 : 1.0)
.opacity(on ? 0 : 0.8)
)
.onAppear {
withAnimation(.easeOut(duration: 1.6).repeatForever(autoreverses: false)) { on = true }
}
}
}
Loading
Loading