@1agh/maude 0.34.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/studio/client/app.jsx +90 -3
- package/apps/studio/client/panels/GitPanel.jsx +6 -0
- package/apps/studio/client/panels/RepoBranchSwitcher.jsx +199 -68
- package/apps/studio/client/styles/3-shell-maude.css +22 -0
- package/apps/studio/dist/client.bundle.js +21 -53108
- package/apps/studio/dist/comment-mount.js +1 -2048
- package/apps/studio/dist/styles.css +1 -12220
- package/apps/studio/git/endpoints.ts +17 -0
- package/apps/studio/git/service.ts +483 -40
- package/apps/studio/github/endpoints.ts +28 -4
- package/apps/studio/github/identity-cache.ts +138 -0
- package/apps/studio/github/service.ts +40 -23
- package/apps/studio/http.ts +12 -0
- package/apps/studio/test/canvas-origin-gate.test.ts +2 -0
- package/apps/studio/test/git-branches.test.ts +141 -3
- package/apps/studio/test/github-api.test.ts +58 -1
- package/apps/studio/test/identity-cache.test.ts +83 -0
- package/apps/studio/test/remote-ahead-behind-cache.test.ts +89 -0
- package/apps/studio/whats-new.json +18 -0
- package/package.json +15 -10
- package/plugins/flow/.claude-plugin/config.schema.json +4 -4
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// DDR-132 — remote ahead/behind probe: in-memory TTL cache + in-flight dedupe.
|
|
2
|
+
//
|
|
3
|
+
// Verifies the cache WRAPPER (TTL-serve / invalidate / in-flight coalesce) fully
|
|
4
|
+
// offline, with no network and no git spawn. Trick: the probe's DDR-131 transport
|
|
5
|
+
// gate classifies a bare local-path remote as `unsafe`, so `remoteAheadBehind`
|
|
6
|
+
// returns a FRESH `{ahead:0,behind:0}` object on every *uncached* evaluation
|
|
7
|
+
// without touching the network. Object IDENTITY (`Object.is`) therefore tells a
|
|
8
|
+
// cache-serve (same reference) apart from a re-run (new reference) — a stronger
|
|
9
|
+
// signal than value equality, and one the security hardening can't invalidate.
|
|
10
|
+
//
|
|
11
|
+
// (The real fetch/count path needs a reachable github.com remote and is covered
|
|
12
|
+
// by the manual native dogfood — same offline ceiling DDR-131 records for
|
|
13
|
+
// `gitFetchRemote`. This file owns the wrapper semantics only.)
|
|
14
|
+
//
|
|
15
|
+
// This file deliberately does NOT set MAUDE_USE_SYSTEM_GIT (it would leak into the
|
|
16
|
+
// shared bun-test process and flip other files' iso-engine expectations).
|
|
17
|
+
|
|
18
|
+
import { afterEach, beforeEach, expect, test } from 'bun:test';
|
|
19
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
20
|
+
import { tmpdir } from 'node:os';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { invalidateRemoteProbe, remoteAheadBehind } from '../git/service.ts';
|
|
23
|
+
|
|
24
|
+
function git(cwd: string, args: string[]): void {
|
|
25
|
+
const p = Bun.spawnSync(['git', ...args], {
|
|
26
|
+
cwd,
|
|
27
|
+
env: { ...process.env, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null' },
|
|
28
|
+
});
|
|
29
|
+
if (p.exitCode !== 0) throw new Error(`git ${args.join(' ')}: ${p.stderr.toString()}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let work: string;
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
work = mkdtempSync(join(tmpdir(), 'maude-ab-work-'));
|
|
36
|
+
git(work, ['init', '-q', '-b', 'main']);
|
|
37
|
+
git(work, ['config', 'user.email', 't@t.dev']);
|
|
38
|
+
git(work, ['config', 'user.name', 'Tester']);
|
|
39
|
+
writeFileSync(join(work, 'a.txt'), 'hello\n');
|
|
40
|
+
git(work, ['add', '.']);
|
|
41
|
+
git(work, ['commit', '-q', '-m', 'init']);
|
|
42
|
+
// A bare local path → DDR-131 transport gate classifies it `unsafe` → the probe
|
|
43
|
+
// returns {0,0} with no spawn. (Any value is fine; we assert on identity.)
|
|
44
|
+
git(work, ['remote', 'add', 'origin', '/var/empty/maude-test-remote']);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
afterEach(() => {
|
|
48
|
+
invalidateRemoteProbe(work);
|
|
49
|
+
rmSync(work, { recursive: true, force: true });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('a second call within the TTL serves the cached object (no re-run)', async () => {
|
|
53
|
+
const a = await remoteAheadBehind(work, undefined);
|
|
54
|
+
expect(a).toEqual({ ahead: 0, behind: 0 });
|
|
55
|
+
const b = await remoteAheadBehind(work, undefined);
|
|
56
|
+
expect(Object.is(a, b)).toBe(true); // same reference ⇒ served from cache
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('invalidateRemoteProbe forces a fresh evaluation (new object)', async () => {
|
|
60
|
+
const a = await remoteAheadBehind(work, undefined);
|
|
61
|
+
invalidateRemoteProbe(work);
|
|
62
|
+
const c = await remoteAheadBehind(work, undefined);
|
|
63
|
+
expect(c).toEqual({ ahead: 0, behind: 0 });
|
|
64
|
+
expect(Object.is(a, c)).toBe(false); // new reference ⇒ re-ran after invalidation
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('SECURITY (DDR-133 F1): the unattended probe never fetches a non-github HTTP remote', async () => {
|
|
68
|
+
// Repoint origin to a non-github HTTPS host (a poisoned `.git/config` riding a clone).
|
|
69
|
+
// With system git auto-preferred (DDR-133), the host-allowlist must gate this probe
|
|
70
|
+
// BEFORE the engine branch — otherwise an http transport reached `git fetch` against
|
|
71
|
+
// ANY host on the unattended 45s poll (SSRF / presence beacon). The guard returns
|
|
72
|
+
// {0,0} with NO spawn; a regression would attempt the fetch and reject/hang here.
|
|
73
|
+
git(work, ['remote', 'set-url', 'origin', 'https://evil.example.invalid/repo.git']);
|
|
74
|
+
invalidateRemoteProbe(work);
|
|
75
|
+
const r = await remoteAheadBehind(work, 'tok_must_never_be_sent');
|
|
76
|
+
expect(r).toEqual({ ahead: 0, behind: 0 });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('concurrent callers coalesce onto one shared in-flight result', async () => {
|
|
80
|
+
const [a, b, c] = await Promise.all([
|
|
81
|
+
remoteAheadBehind(work, undefined),
|
|
82
|
+
remoteAheadBehind(work, undefined),
|
|
83
|
+
remoteAheadBehind(work, undefined),
|
|
84
|
+
]);
|
|
85
|
+
// One shared in-flight promise (or the just-written cache) ⇒ identical reference,
|
|
86
|
+
// never three independent evaluations.
|
|
87
|
+
expect(Object.is(a, b)).toBe(true);
|
|
88
|
+
expect(Object.is(b, c)).toBe(true);
|
|
89
|
+
});
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./whats-new.schema.json",
|
|
3
3
|
"entries": [
|
|
4
|
+
{
|
|
5
|
+
"id": "git-native-switcher",
|
|
6
|
+
"version": "0.36.0",
|
|
7
|
+
"date": "2026-06-30",
|
|
8
|
+
"kind": "feature",
|
|
9
|
+
"title": "The version switcher now speaks Git",
|
|
10
|
+
"summary": "The bottom-left switcher uses plain Git names now — your branches by name, main as the default branch everyone shares, and a one-click “Merge this branch → main.” It opens instantly even on repos with hundreds of branches (it uses your installed Git when you have it), and “Pull a local copy” lists every repo you can reach on GitHub — including ones shared through your team or organization.",
|
|
11
|
+
"surface": "design-ui"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "remote-drafts-in-switcher",
|
|
15
|
+
"version": "0.36.0",
|
|
16
|
+
"date": "2026-06-30",
|
|
17
|
+
"kind": "feature",
|
|
18
|
+
"title": "Open any draft, including your team's",
|
|
19
|
+
"summary": "The version switcher now shows drafts that live on your team's shared remote — not only the ones already on this computer. They're marked “from your team,” and picking one downloads it for you. Hit Refresh to pull in a draft someone just made, search to find one in a long list, and your most recently worked-on drafts sort to the top. Works whether your project connects over SSH or HTTPS.",
|
|
20
|
+
"surface": "design-ui"
|
|
21
|
+
},
|
|
4
22
|
{
|
|
5
23
|
"id": "desktop-ai-chat-fix",
|
|
6
24
|
"version": "0.32.1",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1agh/maude",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"description": "Marketplace of Claude Code plugins by Michal Dovrtěl: `design` (canvas-first design iteration) + `flow` (generic agentic workflow loop with .ai second brain). Ships the `maude` CLI (with `mdcc` legacy alias) to scaffold workspace, run the design dev server, and manage configs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -22,7 +22,10 @@
|
|
|
22
22
|
"dev:site": "pnpm --filter @maude/site dev --port 4398",
|
|
23
23
|
"dev:desktop": "PATH=\"$HOME/.cargo/bin:$PATH\" pnpm --filter @maude/desktop tauri dev",
|
|
24
24
|
"build:desktop": "PATH=\"$HOME/.cargo/bin:$PATH\" pnpm --filter @maude/desktop tauri build",
|
|
25
|
-
"build": "pnpm
|
|
25
|
+
"test:e2e:desktop:build": "PATH=\"$HOME/.cargo/bin:$PATH\" pnpm --filter @maude/desktop tauri build --debug --config src-tauri/tauri.e2e.conf.json",
|
|
26
|
+
"test:e2e:desktop": "pnpm --filter @maude/desktop-e2e e2e",
|
|
27
|
+
"test:e2e:desktop:git": "pnpm --filter @maude/desktop-e2e e2e:git",
|
|
28
|
+
"build": "pnpm -r --if-present --filter '!@maude/desktop' '!@maude/desktop-e2e' run build",
|
|
26
29
|
"build:binary": "bun run apps/studio/build.ts --release",
|
|
27
30
|
"test": "node --test --test-reporter=spec cli/**/*.test.mjs",
|
|
28
31
|
"test:dev-server": "cd apps/studio && bun test",
|
|
@@ -40,16 +43,18 @@
|
|
|
40
43
|
"video:render": "cd scripts/video/final && pnpm run render",
|
|
41
44
|
"video:studio": "cd scripts/video/final && pnpm run studio",
|
|
42
45
|
"postinstall": "node cli/install.cjs",
|
|
43
|
-
"prepublishOnly": "bash scripts/check-version-parity.sh && bash apps/studio/bin/check-runtime-bundles.sh"
|
|
46
|
+
"prepublishOnly": "bash scripts/check-version-parity.sh && bash apps/studio/bin/check-runtime-bundles.sh",
|
|
47
|
+
"test:e2e:desktop:lifecycle": "pnpm --filter @maude/desktop-e2e e2e:lifecycle",
|
|
48
|
+
"test:e2e:desktop:switchrepos": "pnpm --filter @maude/desktop-e2e e2e:switchrepos"
|
|
44
49
|
},
|
|
45
50
|
"optionalDependencies": {
|
|
46
|
-
"@1agh/maude-darwin-arm64": "0.
|
|
47
|
-
"@1agh/maude-darwin-x64": "0.
|
|
48
|
-
"@1agh/maude-linux-arm64": "0.
|
|
49
|
-
"@1agh/maude-linux-arm64-musl": "0.
|
|
50
|
-
"@1agh/maude-linux-x64": "0.
|
|
51
|
-
"@1agh/maude-linux-x64-musl": "0.
|
|
52
|
-
"@1agh/maude-win32-x64": "0.
|
|
51
|
+
"@1agh/maude-darwin-arm64": "0.36.0",
|
|
52
|
+
"@1agh/maude-darwin-x64": "0.36.0",
|
|
53
|
+
"@1agh/maude-linux-arm64": "0.36.0",
|
|
54
|
+
"@1agh/maude-linux-arm64-musl": "0.36.0",
|
|
55
|
+
"@1agh/maude-linux-x64": "0.36.0",
|
|
56
|
+
"@1agh/maude-linux-x64-musl": "0.36.0",
|
|
57
|
+
"@1agh/maude-win32-x64": "0.36.0"
|
|
53
58
|
},
|
|
54
59
|
"files": [
|
|
55
60
|
"cli",
|
|
@@ -236,13 +236,13 @@
|
|
|
236
236
|
"orchestration": {
|
|
237
237
|
"type": "object",
|
|
238
238
|
"additionalProperties": false,
|
|
239
|
-
"description": "Opt-
|
|
239
|
+
"description": "Opt-OUT, capability-gated bookend debate (diverge→adversarial→research). The debate is ON by default: an absent `orchestration` block is treated as `mode:auto`, so the layer engages everywhere — a live native agent-team (`relay`) when the CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS flag is enabled, else a cheap read-only `reduce` panel. Add this block ONLY to dial it down or off. No hard dependency on experimental Claude Code features (degrades to `reduce` without the flag). See DDR-130.",
|
|
240
240
|
"properties": {
|
|
241
241
|
"mode": {
|
|
242
242
|
"type": "string",
|
|
243
243
|
"enum": ["auto", "reduce", "off"],
|
|
244
244
|
"default": "auto",
|
|
245
|
-
"description": "`auto`
|
|
245
|
+
"description": "Opt-out default `auto` (absent block == `auto`). `auto` = live native agent-team (relay) when the CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS capability is detected, else the read-only reduce-pass; gated further by the stakes-gate + short-circuit. `reduce` = always parallel panel + reduce-pass, never a live team. `off` = disable the debate entirely; today's raw single-pass / sum-of-verdicts, byte-for-byte unchanged."
|
|
246
246
|
},
|
|
247
247
|
"bookends": {
|
|
248
248
|
"type": "object",
|
|
@@ -292,9 +292,9 @@
|
|
|
292
292
|
"designTeam": {
|
|
293
293
|
"type": "object",
|
|
294
294
|
"additionalProperties": false,
|
|
295
|
-
"description": "`/design:critic` live-team (relay) tier.
|
|
295
|
+
"description": "`/design:critic` live-team (relay) tier — opt-out, ON by default. The conflicting critics convene as a native team and revise stances when the experimental flag is on AND the panel produced ≥ `minConflicts` cross-discipline conflicting blockers; otherwise the read-only reduce-pass stands. Set `enabled:false` to keep `/design:critic` on the reduce-pass only.",
|
|
296
296
|
"properties": {
|
|
297
|
-
"enabled": { "type": "boolean", "default":
|
|
297
|
+
"enabled": { "type": "boolean", "default": true },
|
|
298
298
|
"minConflicts": { "type": "integer", "minimum": 2, "default": 2 }
|
|
299
299
|
}
|
|
300
300
|
}
|