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
67 changes: 58 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ jobs:
apps/desktop/node_modules
apps/ade-cli/node_modules
apps/web/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json') }}
apps/ade-code/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/ade-code/package-lock.json') }}

- name: Install all dependencies (parallel)
if: steps.cache.outputs.cache-hit != 'true'
run: |
cd apps/desktop && npm ci &
cd apps/ade-cli && npm ci &
cd apps/web && npm ci &
cd apps/ade-code && npm ci &
wait

# ── Secret scanning (no deps needed) ───────────────────────────────────
Expand Down Expand Up @@ -63,7 +65,8 @@ jobs:
apps/desktop/node_modules
apps/ade-cli/node_modules
apps/web/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json') }}
apps/ade-code/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/ade-code/package-lock.json') }}
- run: cd apps/desktop && npm run typecheck

typecheck-ade-cli:
Expand All @@ -80,7 +83,8 @@ jobs:
apps/desktop/node_modules
apps/ade-cli/node_modules
apps/web/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json') }}
apps/ade-code/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/ade-code/package-lock.json') }}
- run: cd apps/ade-cli && npm run typecheck

typecheck-web:
Expand All @@ -97,9 +101,28 @@ jobs:
apps/desktop/node_modules
apps/ade-cli/node_modules
apps/web/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json') }}
apps/ade-code/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/ade-code/package-lock.json') }}
- run: cd apps/web && npm run typecheck

typecheck-ade-code:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: actions/cache/restore@v4
with:
path: |
apps/desktop/node_modules
apps/ade-cli/node_modules
apps/web/node_modules
apps/ade-code/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/ade-code/package-lock.json') }}
- run: cd apps/ade-code && npm run typecheck

lint-desktop:
needs: install
runs-on: ubuntu-latest
Expand All @@ -114,7 +137,8 @@ jobs:
apps/desktop/node_modules
apps/ade-cli/node_modules
apps/web/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json') }}
apps/ade-code/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/ade-code/package-lock.json') }}
- run: cd apps/desktop && npm run lint

test-desktop:
Expand All @@ -135,7 +159,8 @@ jobs:
apps/desktop/node_modules
apps/ade-cli/node_modules
apps/web/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json') }}
apps/ade-code/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/ade-code/package-lock.json') }}
- run: cd apps/desktop && npx vitest run --shard=${{ matrix.shard }}/8

test-ade-cli:
Expand All @@ -152,9 +177,28 @@ jobs:
apps/desktop/node_modules
apps/ade-cli/node_modules
apps/web/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json') }}
apps/ade-code/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/ade-code/package-lock.json') }}
- run: cd apps/ade-cli && npm test

test-ade-code:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: actions/cache/restore@v4
with:
path: |
apps/desktop/node_modules
apps/ade-cli/node_modules
apps/web/node_modules
apps/ade-code/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/ade-code/package-lock.json') }}
- run: cd apps/ade-code && npm test

build:
needs: install
runs-on: ubuntu-latest
Expand All @@ -169,10 +213,12 @@ jobs:
apps/desktop/node_modules
apps/ade-cli/node_modules
apps/web/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json') }}
apps/ade-code/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/ade-code/package-lock.json') }}
- run: cd apps/desktop && npm run build
- run: cd apps/ade-cli && npm run build
- run: cd apps/web && npm run build
- run: cd apps/ade-code && npm run build

validate-docs:
needs: install
Expand All @@ -188,7 +234,8 @@ jobs:
apps/desktop/node_modules
apps/ade-cli/node_modules
apps/web/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json') }}
apps/ade-code/node_modules
key: nm-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/ade-code/package-lock.json') }}
- run: node scripts/validate-docs.mjs

# ── Windows build smoke (self-contained — no shared cache) ────────────
Expand Down Expand Up @@ -235,9 +282,11 @@ jobs:
- typecheck-desktop
- typecheck-ade-cli
- typecheck-web
- typecheck-ade-code
- lint-desktop
- test-desktop
- test-ade-cli
- test-ade-code
- build
- validate-docs
- build-win
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ __pycache__/

# Build outputs
/apps/ade-cli/dist/
/apps/ade-code/dist/
/apps/desktop/release/
/apps/desktop/dist/
/apps/desktop/vendor/crsqlite/darwin-x64/
Expand Down Expand Up @@ -62,3 +63,4 @@ ios-signing/
/.playwright-mcp
/.codex-derived-data
package-lock.json
!/apps/ade-code/package-lock.json
4 changes: 4 additions & 0 deletions apps/ade-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ ade shell start --lane lane-id -- npm test
ade shell start-cli codex --lane lane-id --permission-mode edit --message "fix failing tests"
ade shell start-cli --provider claude --lane lane-id --permission-mode default
ade chat create --lane lane-id --model gpt-5.5
ade code
ade --socket /path/to/ade.sock code
ade tests run --lane lane-id --suite unit --wait
ade proof list --arg ownerKind=chat --arg ownerId=session-id
ade help ios-sim preview-render
Expand All @@ -95,6 +97,8 @@ ade cursor cloud me

Use typed commands first. They validate common arguments and provide stable JSON fields or readable text summaries. Use `ade help <command> <subcommand>` for exact flags, `ade actions list --text` to discover the full service-backed action catalog, and `ade actions run <domain.action>` only when there is no typed command for the workflow yet.

**`ade code`** starts the terminal Work chat client (`apps/ade-code`). Build it with `npm run build` inside that directory, install the `ade-code` package, or point **`ADE_CODE_EXECUTABLE`** at `dist/cli.js`. Unlike other commands that auto-pick the desktop socket from the project layout during `executePlan`, **`ade code` only forwards `--socket` when you pass global `--socket` to `ade`** (for example `ade --socket /path/to/ade.sock code`). Without that, the TUI runs in **embedded** headless mode instead of opening a socket implicitly.

The `prs path-to-merge` and `prs pipeline save` commands persist a partial `PipelineSettings` patch via `issue_inventory.savePipelineSettings` before launching the resolver. The Path to Merge orchestrator reads these from saved settings, so the same flags work either way:

| Flag | PipelineSettings field | Values |
Expand Down
69 changes: 69 additions & 0 deletions apps/ade-cli/src/adeRpcServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,75 @@ function createFakePathExecutable(dir: string, name: string): string {
}

describe("adeRpcServer", () => {
it("routes app/navigate through the runtime navigation service", async () => {
const { runtime } = createRuntime();
const navigate = vi.fn(async () => ({ ok: true, mode: "desktop", windowId: 7 }));
runtime.appNavigationService = { navigate };
const handler = createAdeRpcRequestHandler({ runtime, serverVersion: "test" });
await initialize(handler, { role: "cto" });

const result = await handler({
jsonrpc: "2.0",
id: 2,
method: "app/navigate",
params: {
source: "ade-code",
target: { kind: "lane", sessionId: "chat-1", laneId: "lane-1" },
},
});

expect(result).toEqual({ ok: true, mode: "desktop", windowId: 7 });
expect(navigate).toHaveBeenCalledWith({
source: "ade-code",
target: { kind: "lane", sessionId: "chat-1", laneId: "lane-1" },
});
});

it("reports app/navigate unavailable in headless runtime", async () => {
const { runtime } = createRuntime();
const handler = createAdeRpcRequestHandler({ runtime, serverVersion: "test" });
await initialize(handler, { role: "cto" });

const result = await handler({
jsonrpc: "2.0",
id: 2,
method: "app/navigate",
params: {
source: "ade-code",
target: { kind: "work" },
},
});

expect(result).toEqual({
ok: false,
mode: "unavailable",
message: "Desktop navigation is unavailable in this runtime.",
});
});

it("rejects malformed app/navigate targets before calling the runtime service", async () => {
const { runtime } = createRuntime();
const navigate = vi.fn(async () => ({ ok: true, mode: "desktop", windowId: 7 }));
runtime.appNavigationService = { navigate };
const handler = createAdeRpcRequestHandler({ runtime, serverVersion: "test" });
await initialize(handler, { role: "cto" });

await expect(handler({
jsonrpc: "2.0",
id: 2,
method: "app/navigate",
params: {
source: "ade-code",
target: { kind: "lane" },
},
})).rejects.toMatchObject({
code: JsonRpcErrorCode.invalidParams,
message: "app/navigate target 'lane' requires laneId.",
});

expect(navigate).not.toHaveBeenCalled();
});

it("treats requested privileged roles as external without trusted env identity", async () => {
const { runtime } = createRuntime();
const handler = createAdeRpcRequestHandler({ runtime, serverVersion: "test" });
Expand Down
43 changes: 43 additions & 0 deletions apps/ade-cli/src/adeRpcServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
type DockLayout,
type GraphPersistedState,
type MergeMethod,
type AppNavigationRequest,
} from "../../desktop/src/shared/types";
import type { PrActionRun, PrCheck, PrComment, PrReviewThread } from "../../desktop/src/shared/types/prs";
import { resolveAdeLayout } from "../../desktop/src/shared/adeLayout";
Expand Down Expand Up @@ -7344,6 +7345,48 @@ export function createAdeRpcRequestHandler(args: {
return await readResource(runtime, uri);
}

if (method === "app/navigate") {
const target = safeObject(params.target);
const kind = asOptionalTrimmedString(target.kind);
if (!kind) {
throw new JsonRpcError(JsonRpcErrorCode.invalidParams, "app/navigate requires target.kind.");
}
if (kind !== "work" && kind !== "chat" && kind !== "lane" && kind !== "pr" && kind !== "route") {
throw new JsonRpcError(JsonRpcErrorCode.invalidParams, `Unsupported app navigation target kind: ${kind}.`);
}
if (kind === "lane" && !asOptionalTrimmedString(target.laneId)) {
throw new JsonRpcError(JsonRpcErrorCode.invalidParams, "app/navigate target 'lane' requires laneId.");
}
if (kind === "route" && !asOptionalTrimmedString(target.route)) {
throw new JsonRpcError(JsonRpcErrorCode.invalidParams, "app/navigate target 'route' requires route.");
}
const normalizedTarget: Record<string, unknown> = { kind };
const sessionId = asOptionalTrimmedString(target.sessionId);
const laneId = asOptionalTrimmedString(target.laneId);
if ((kind === "work" || kind === "chat" || kind === "lane") && sessionId) normalizedTarget.sessionId = sessionId;
if ((kind === "work" || kind === "chat" || kind === "lane" || kind === "pr") && laneId) normalizedTarget.laneId = laneId;
if (kind === "pr") {
const prId = asOptionalTrimmedString(target.prId);
if (prId) normalizedTarget.prId = prId;
if (typeof target.prNumber === "number") normalizedTarget.prNumber = target.prNumber;
}
if (kind === "route") {
normalizedTarget.route = asOptionalTrimmedString(target.route);
}
const request = {
target: normalizedTarget,
source: asOptionalTrimmedString(params.source) ?? "ade-rpc",
} as AppNavigationRequest;
if (!runtime.appNavigationService) {
return {
ok: false,
mode: "unavailable",
message: "Desktop navigation is unavailable in this runtime.",
};
}
return await runtime.appNavigationService.navigate(request);
}

if (method === "shutdown") {
return {};
}
Expand Down
Loading