Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
7796bcc
chore: add fork label
kevinher7 Apr 19, 2026
b164559
feat: include username in generated branch names with type prefix format
kevinher7 Apr 19, 2026
f78ba69
feat: use repo PR template when generating PR content
kevinher7 Apr 19, 2026
7b5a10b
fix: allow all cases for pr template
kevinher7 Apr 20, 2026
08daec6
fix: remove pre-flight editor command availability check before launch
kevinher7 Apr 21, 2026
1553907
refactor: pre-resolve editor command map at service startup to avoid …
kevinher7 Apr 25, 2026
9e65cb8
Merge remote-tracking branch 'origin/main' into personal
kevinher7 Apr 25, 2026
82c1db2
feat(persistence): banded migration ids + set-difference runner (#2)
kevinher7 May 4, 2026
40b9fbd
feat: add tag catalog and sidebar tag filtering (#1)
kevinher7 May 4, 2026
bcbcd2d
Merge remote-tracking branch 'origin/main' into personal
kevinher7 May 4, 2026
ed9287c
fix: repair CLAUDE.md symlink (drop trailing newline in target)
kevinher7 May 4, 2026
f4a593a
feat: add project tags and tag filtering (#3)
kevinher7 May 5, 2026
ea0e712
Merge remote-tracking branch 'origin/main' into personal
kevinher7 May 10, 2026
939d092
fix: pass tags through orchestration snapshot and UI (#6)
kevinher7 May 12, 2026
b844dd4
Merge remote-tracking branch 'origin/main' into personal
kevinher7 May 18, 2026
680062d
Merge remote-tracking branch 'origin/main' into personal
kevinher7 May 29, 2026
7d88407
fix: align tag feature with upstream Effect changes after merge
kevinher7 May 29, 2026
5c6d517
feat: add flake packaging for CLI and macOS desktop app (#8)
kevinher7 Jun 6, 2026
bf02c86
Fix nix-release CI: use zip target instead of dmg
kevinher7 Jun 6, 2026
8c3afd3
Update desktop artifact hash for aarch64-darwin
kevinher7 Jun 6, 2026
ffb63ce
Point desktop artifact URL at v0.0.24-fork.1 release
kevinher7 Jun 6, 2026
8c103cd
Fix desktop artifact hash for aarch64-darwin
kevinher7 Jun 6, 2026
3865153
Fix bunDeps hash for aarch64-darwin
kevinher7 Jun 6, 2026
819ae4b
Add node-gyp to FOD nativeBuildInputs for Linux node-pty
kevinher7 Jun 6, 2026
9e8f5af
Switch CLI to prebuilt release artifacts, add Linux CI job
kevinher7 Jun 6, 2026
f11a444
Update artifact hashes for fork.2
kevinher7 Jun 6, 2026
b604470
Update all artifact hashes for v0.0.24-fork.2
kevinher7 Jun 6, 2026
ab8bc2c
ci(release): auto-compute nix artifact hashes on tag push
kevinher7 Jun 6, 2026
5295f3d
chore(release): update artifact hashes for v0.0.24-fork.3
github-actions[bot] Jun 6, 2026
4ba41b4
Merge remote-tracking branch 'origin/main' into personal
kevinher7 Jun 7, 2026
e6c827e
fix: resolve post-merge type integration after upstream merge
kevinher7 Jun 7, 2026
fb95080
ci(release): use Vite+ toolchain instead of bun
kevinher7 Jun 7, 2026
d3d4186
chore(release): update artifact hashes for v0.0.25-fork.1
github-actions[bot] Jun 7, 2026
a6eca35
ci(release): drop -- so flags reach desktop artifact script
kevinher7 Jun 7, 2026
e6fb3a8
chore(release): update artifact hashes for v0.0.25-fork.2
github-actions[bot] Jun 7, 2026
705167b
Merge remote-tracking branch 'origin/main' into personal
kevinher7 Jun 10, 2026
08bd514
chore(release): update artifact hashes for v0.0.27-fork.0
github-actions[bot] Jun 10, 2026
4cf87fb
feat(editor): support user-defined custom editors in the Open In pick…
kevinher7 Jun 12, 2026
a1f31d2
chore(release): update artifact hashes for v0.0.27-fork.1
github-actions[bot] Jun 12, 2026
8a05565
chore(nix): ship pruned prod deps in CLI release artifact (#11)
kevinher7 Jun 13, 2026
0e2e79e
fix(nix-release): activate pnpm via corepack before CLI deploy
kevinher7 Jun 13, 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
202 changes: 202 additions & 0 deletions .github/workflows/nix-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
name: Build Nix release artifacts

on:
push:
tags: ["v*"]
workflow_dispatch:
inputs:
version:
description: "Version to build (without v prefix)"
required: true
default: "0.0.24-fork.1"

permissions:
contents: write

jobs:
build-macos:
runs-on: macos-14
steps:
- uses: actions/checkout@v4

- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
with:
node-version-file: package.json
cache: true
run-install: true

- name: Resolve version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "tag=v${{ inputs.version }}" >> "$GITHUB_OUTPUT"
echo "version=${{ inputs.version }}" >> "$GITHUB_OUTPUT"
else
echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
fi

- name: Build desktop artifact
env:
CSC_IDENTITY_AUTO_DISCOVERY: "false"
run: |
vp run dist:desktop:artifact \
--platform mac \
--target zip \
--arch arm64 \
--build-version "${{ steps.version.outputs.version }}" \
--verbose

- name: Build CLI
run: vp run --filter t3 build

- name: Enable pnpm
# Vite+ embeds pnpm but does not expose a standalone `pnpm` on PATH,
# and `vp` has no `deploy` command. Activate the repo's pinned pnpm
# (the `packageManager` field) via corepack so the deploy step below
# can resolve the production dependency closure.
run: corepack enable

- name: Package CLI tarball
run: |
mkdir -p release
# Produce a pruned production tree (bundle dist/ + only runtime deps)
# instead of taring the full multi-GB node_modules. pnpm deploy resolves
# the prod dependency closure for the `t3` package into a self-contained
# dir containing dist/, node_modules/, and package.json.
rm -rf cli-deploy
pnpm --filter t3 deploy --prod --legacy cli-deploy
tar -czf release/t3code-cli-aarch64-darwin.tar.gz -C cli-deploy \
node_modules \
dist \
package.json

- name: Create release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.tag }}
files: |
release/T3-Code-*.zip
release/t3code-cli-aarch64-darwin.tar.gz

build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
with:
node-version-file: package.json
cache: true
run-install: true

- name: Resolve version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "tag=v${{ inputs.version }}" >> "$GITHUB_OUTPUT"
echo "version=${{ inputs.version }}" >> "$GITHUB_OUTPUT"
else
echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
fi

- name: Build CLI
run: vp run --filter t3 build

- name: Enable pnpm
# Vite+ embeds pnpm but does not expose a standalone `pnpm` on PATH,
# and `vp` has no `deploy` command. Activate the repo's pinned pnpm
# (the `packageManager` field) via corepack so the deploy step below
# can resolve the production dependency closure.
run: corepack enable

- name: Package CLI tarball
run: |
mkdir -p release
# Produce a pruned production tree (bundle dist/ + only runtime deps)
# instead of taring the full multi-GB node_modules. pnpm deploy resolves
# the prod dependency closure for the `t3` package into a self-contained
# dir containing dist/, node_modules/, and package.json.
rm -rf cli-deploy
pnpm --filter t3 deploy --prod --legacy cli-deploy
tar -czf release/t3code-cli-x86_64-linux.tar.gz -C cli-deploy \
node_modules \
dist \
package.json

- name: Create release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.tag }}
files: release/t3code-cli-x86_64-linux.tar.gz

update-hashes:
needs: [build-macos, build-linux]
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: personal
token: ${{ secrets.GITHUB_TOKEN }}

- uses: cachix/install-nix-action@v31

- name: Download release artifacts
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mkdir -p artifacts
gh release download "${GITHUB_REF_NAME}" \
--repo "${{ github.repository }}" \
--dir artifacts \
--pattern 't3code-cli-*.tar.gz' \
--pattern 'T3-Code-*-arm64.zip'

- name: Compute hashes and write nix-hashes.json
run: |
VERSION="${GITHUB_REF_NAME#v}"

CLI_DARWIN=$(nix hash file --sri --type sha256 artifacts/t3code-cli-aarch64-darwin.tar.gz)
CLI_LINUX=$(nix hash file --sri --type sha256 artifacts/t3code-cli-x86_64-linux.tar.gz)
DESKTOP=$(nix hash file --sri --type sha256 artifacts/T3-Code-*-arm64.zip)

echo "version: $VERSION"
echo "cli linux: $CLI_LINUX"
echo "cli darwin: $CLI_DARWIN"
echo "desktop darwin: $DESKTOP"

jq -n \
--arg version "$VERSION" \
--arg cli_linux "$CLI_LINUX" \
--arg cli_darwin "$CLI_DARWIN" \
--arg desktop "$DESKTOP" \
'{
version: $version,
cli: {
"x86_64-linux": $cli_linux,
"aarch64-darwin": $cli_darwin
},
desktop: {
"aarch64-darwin": $desktop
}
}' > nix-hashes.json

cat nix-hashes.json

- name: Commit and push
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add nix-hashes.json
if git diff --cached --quiet; then
echo "nix-hashes.json already up to date"
exit 0
fi
git commit -m "chore(release): update artifact hashes for ${GITHUB_REF_NAME}"
git push origin HEAD:personal
2 changes: 1 addition & 1 deletion CLAUDE.md
1 change: 1 addition & 0 deletions apps/mobile/src/lib/repositoryGroups.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function makeProject(
repositoryIdentity: null,
defaultModelSelection: null,
scripts: [],
tags: [],
createdAt: "2026-04-01T00:00:00.000Z",
updatedAt: "2026-04-01T00:00:00.000Z",
...input,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import {
import { deriveServerPaths, ServerConfig } from "../src/config.ts";
import { WorkspaceEntriesLive } from "../src/workspace/Layers/WorkspaceEntries.ts";
import { WorkspacePathsLive } from "../src/workspace/Layers/WorkspacePaths.ts";
import * as GitVcsDriver from "../src/vcs/GitVcsDriver.ts";
import * as VcsDriverRegistry from "../src/vcs/VcsDriverRegistry.ts";
import { VcsStatusBroadcaster } from "../src/vcs/VcsStatusBroadcaster.ts";
import { GitWorkflowService } from "../src/git/GitWorkflowService.ts";
Expand Down Expand Up @@ -319,13 +320,17 @@ export const makeOrchestrationIntegrationHarness = (
readonly newBranch: string;
}) => Effect.succeed({ branch: input.newBranch }),
});
const gitVcsDriverLayer = Layer.mock(GitVcsDriver.GitVcsDriver)({
readConfigValue: () => Effect.succeed(null),
});
const textGenerationLayer = Layer.succeed(TextGeneration, {
generateBranchName: () => Effect.succeed({ branch: "update" }),
generateThreadTitle: () => Effect.succeed({ title: "New thread" }),
} as unknown as TextGenerationShape);
const providerCommandReactorLayer = ProviderCommandReactorLive.pipe(
Layer.provideMerge(runtimeServicesLayer),
Layer.provideMerge(gitWorkflowLayer),
Layer.provideMerge(gitVcsDriverLayer),
Layer.provideMerge(textGenerationLayer),
Layer.provideMerge(serverSettingsLayer),
);
Expand Down
10 changes: 10 additions & 0 deletions apps/server/src/checkpointing/Layers/CheckpointDiffQuery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ describe("CheckpointDiffQueryLive", () => {
}),
getThreadShellById: () => Effect.succeed(Option.none()),
getThreadDetailById: () => Effect.succeed(Option.none()),
listAllTags: () => Effect.succeed([]),
getTagById: () => Effect.succeed(Option.none()),
}),
),
);
Expand Down Expand Up @@ -200,6 +202,8 @@ describe("CheckpointDiffQueryLive", () => {
getFullThreadDiffContext: () => Effect.die("unused"),
getThreadShellById: () => Effect.succeed(Option.none()),
getThreadDetailById: () => Effect.succeed(Option.none()),
listAllTags: () => Effect.succeed([]),
getTagById: () => Effect.succeed(Option.none()),
}),
),
);
Expand Down Expand Up @@ -282,6 +286,8 @@ describe("CheckpointDiffQueryLive", () => {
getFullThreadDiffContext: () => Effect.die("unused"),
getThreadShellById: () => Effect.succeed(Option.none()),
getThreadDetailById: () => Effect.succeed(Option.none()),
listAllTags: () => Effect.succeed([]),
getTagById: () => Effect.succeed(Option.none()),
}),
),
);
Expand Down Expand Up @@ -349,6 +355,8 @@ describe("CheckpointDiffQueryLive", () => {
getFullThreadDiffContext: () => Effect.die("unused"),
getThreadShellById: () => Effect.succeed(Option.none()),
getThreadDetailById: () => Effect.succeed(Option.none()),
listAllTags: () => Effect.succeed([]),
getTagById: () => Effect.succeed(Option.none()),
}),
),
);
Expand Down Expand Up @@ -401,6 +409,8 @@ describe("CheckpointDiffQueryLive", () => {
getFullThreadDiffContext: () => Effect.succeed(Option.none()),
getThreadShellById: () => Effect.succeed(Option.none()),
getThreadDetailById: () => Effect.succeed(Option.none()),
listAllTags: () => Effect.succeed([]),
getTagById: () => Effect.succeed(Option.none()),
}),
),
);
Expand Down
16 changes: 16 additions & 0 deletions apps/server/src/git/GitManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1297,13 +1297,29 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () {
});
const rangeContext = yield* gitCore.readRangeContext(cwd, baseBranch);

const githubDir = path.join(cwd, ".github");
const prTemplate = yield* Effect.gen(function* () {
const entries = yield* fileSystem
.readDirectory(githubDir, { recursive: false })
.pipe(Effect.option);
const match = Option.getOrElse(entries, () => [] as Array<string>).find(
(name) => name.toLowerCase() === "pull_request_template.md",
);
if (!match) return undefined;
return yield* fileSystem
.readFileString(path.join(githubDir, match))
.pipe(Effect.option)
.pipe(Effect.map(Option.getOrUndefined));
});

const generated = yield* textGeneration.generatePrContent({
cwd,
baseBranch,
headBranch: headContext.headBranch,
commitSummary: limitContext(rangeContext.commitSummary, 20_000),
diffSummary: limitContext(rangeContext.diffSummary, 20_000),
diffPatch: limitContext(rangeContext.diffPatch, 60_000),
prTemplate,
modelSelection,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ describe("OrchestrationEngine", () => {
const projectionSnapshot = {
snapshotSequence: 7,
updatedAt: "2026-03-03T00:00:04.000Z",
tags: [],
projects: [
{
id: asProjectId("project-bootstrap"),
Expand All @@ -124,6 +125,7 @@ describe("OrchestrationEngine", () => {
model: "gpt-5-codex",
},
scripts: [],
tags: [],
createdAt: "2026-03-03T00:00:00.000Z",
updatedAt: "2026-03-03T00:00:01.000Z",
deletedAt: null,
Expand Down Expand Up @@ -181,13 +183,15 @@ describe("OrchestrationEngine", () => {
snapshotSequence: projectionSnapshot.snapshotSequence,
projects: [],
threads: [],
tags: [],
updatedAt: projectionSnapshot.updatedAt,
}),
getArchivedShellSnapshot: () =>
Effect.succeed({
snapshotSequence: projectionSnapshot.snapshotSequence,
projects: [],
threads: [],
tags: [],
updatedAt: projectionSnapshot.updatedAt,
}),
getSnapshotSequence: () =>
Expand All @@ -200,6 +204,8 @@ describe("OrchestrationEngine", () => {
getFullThreadDiffContext: () => Effect.succeed(Option.none()),
getThreadShellById: () => Effect.succeed(Option.none()),
getThreadDetailById: () => Effect.succeed(Option.none()),
listAllTags: () => Effect.succeed([]),
getTagById: () => Effect.succeed(Option.none()),
}),
),
Layer.provide(
Expand Down
12 changes: 10 additions & 2 deletions apps/server/src/orchestration/Layers/OrchestrationEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {
OrchestrationEvent,
OrchestrationReadModel,
ProjectId,
TagId,
ThreadId,
} from "@t3tools/contracts";
import { OrchestrationCommand } from "@t3tools/contracts";
Expand Down Expand Up @@ -57,8 +58,8 @@ interface CommandEnvelope {
}

function commandToAggregateRef(command: OrchestrationCommand): {
readonly aggregateKind: "project" | "thread";
readonly aggregateId: ProjectId | ThreadId;
readonly aggregateKind: "project" | "thread" | "tag";
readonly aggregateId: ProjectId | ThreadId | TagId;
} {
switch (command.type) {
case "project.create":
Expand All @@ -68,6 +69,13 @@ function commandToAggregateRef(command: OrchestrationCommand): {
aggregateKind: "project",
aggregateId: command.projectId,
};
case "tag.create":
case "tag.rename":
case "tag.delete":
return {
aggregateKind: "tag",
aggregateId: command.tagId,
};
default:
return {
aggregateKind: "thread",
Expand Down
Loading
Loading