@1agh/maude 0.36.1 → 0.38.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.
Files changed (71) hide show
  1. package/README.md +2 -0
  2. package/apps/studio/acp/bootstrap-brief.ts +93 -0
  3. package/apps/studio/acp/bridge.ts +114 -1
  4. package/apps/studio/acp/index.ts +184 -21
  5. package/apps/studio/acp/plugin-bootstrap.ts +115 -0
  6. package/apps/studio/acp/transcript.ts +36 -3
  7. package/apps/studio/activity.ts +45 -0
  8. package/apps/studio/api.ts +265 -47
  9. package/apps/studio/bin/_ensure-browser.mjs +305 -0
  10. package/apps/studio/bin/ensure-browser.sh +26 -0
  11. package/apps/studio/bin/screenshot.sh +33 -8
  12. package/apps/studio/bin/smoke.sh +46 -0
  13. package/apps/studio/canvas-edit.ts +422 -6
  14. package/apps/studio/canvas-lib.tsx +48 -0
  15. package/apps/studio/canvas-shell.tsx +684 -12
  16. package/apps/studio/client/app.jsx +686 -35
  17. package/apps/studio/client/github.js +6 -0
  18. package/apps/studio/client/panels/ChatPanel.jsx +593 -31
  19. package/apps/studio/client/panels/CreateProject.jsx +57 -23
  20. package/apps/studio/client/panels/GitPanel.jsx +1 -1
  21. package/apps/studio/client/panels/IdentityBar.jsx +43 -2
  22. package/apps/studio/client/panels/OnboardingWizard.jsx +24 -21
  23. package/apps/studio/client/panels/RepoBranchSwitcher.jsx +4 -4
  24. package/apps/studio/client/panels/acp-runtime.js +227 -70
  25. package/apps/studio/client/panels/chat-context.js +124 -0
  26. package/apps/studio/client/panels/slash-commands.js +147 -0
  27. package/apps/studio/client/styles/3-shell-maude.css +15 -0
  28. package/apps/studio/client/styles/6-acp-chat.css +244 -0
  29. package/apps/studio/client/tour/collab-tour.js +3 -3
  30. package/apps/studio/commands/reorder-command.ts +77 -0
  31. package/apps/studio/config.schema.json +6 -0
  32. package/apps/studio/dist/client.bundle.js +17 -13
  33. package/apps/studio/dist/styles.css +1 -1
  34. package/apps/studio/github/endpoints.ts +42 -0
  35. package/apps/studio/hmr-broadcast.ts +27 -19
  36. package/apps/studio/http.ts +183 -26
  37. package/apps/studio/inspect.ts +108 -1
  38. package/apps/studio/paths.ts +32 -0
  39. package/apps/studio/readiness.ts +79 -30
  40. package/apps/studio/scaffold-design.ts +95 -2
  41. package/apps/studio/server.ts +11 -2
  42. package/apps/studio/test/acp-activity.test.ts +154 -0
  43. package/apps/studio/test/acp-ai-activity.test.ts +182 -0
  44. package/apps/studio/test/acp-bootstrap-brief.test.ts +167 -0
  45. package/apps/studio/test/acp-commands.test.ts +108 -0
  46. package/apps/studio/test/acp-origin-gate.test.ts +64 -1
  47. package/apps/studio/test/acp-plugin-bootstrap.test.ts +89 -0
  48. package/apps/studio/test/acp-session-plugins.test.ts +132 -0
  49. package/apps/studio/test/acp-transcript.test.ts +53 -0
  50. package/apps/studio/test/active-state.test.ts +41 -0
  51. package/apps/studio/test/canvas-freshness-deps.test.ts +64 -0
  52. package/apps/studio/test/canvas-origin-gate.test.ts +10 -0
  53. package/apps/studio/test/canvas-reorder.test.ts +211 -0
  54. package/apps/studio/test/chat-context.test.ts +129 -0
  55. package/apps/studio/test/csrf-write-guard.test.ts +24 -0
  56. package/apps/studio/test/edit-suppress.test.ts +170 -0
  57. package/apps/studio/test/ensure-browser.test.ts +92 -0
  58. package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +44 -0
  59. package/apps/studio/test/hmr-broadcast.test.ts +44 -0
  60. package/apps/studio/test/inspect-selections.test.ts +219 -0
  61. package/apps/studio/test/paths.test.ts +57 -0
  62. package/apps/studio/test/readiness.test.ts +83 -13
  63. package/apps/studio/test/reorder-api.test.ts +210 -0
  64. package/apps/studio/test/slash-commands.test.ts +117 -0
  65. package/apps/studio/test/sync-agent.test.ts +9 -2
  66. package/apps/studio/undo-stack.ts +6 -0
  67. package/apps/studio/whats-new.json +63 -0
  68. package/apps/studio/ws.ts +37 -0
  69. package/cli/commands/design.mjs +1 -0
  70. package/package.json +11 -9
  71. package/plugins/design/dependencies.json +3 -3
@@ -289,6 +289,12 @@
289
289
  font-weight: 600;
290
290
  color: var(--presence-agent);
291
291
  }
292
+ /* Live elapsed counter — dimmer than the label; tabular so it doesn't jitter. */
293
+ .chat-activity-elapsed {
294
+ font-variant-numeric: tabular-nums;
295
+ color: var(--fg-2);
296
+ opacity: 0.8;
297
+ }
292
298
 
293
299
  /* Feed (scrolls live) */
294
300
  .chat-feed {
@@ -324,6 +330,41 @@
324
330
  line-height: var(--lh-sm);
325
331
  max-width: 240px;
326
332
  }
333
+ /* One-time "Create new design system" CTA — the prominent accent button in the
334
+ empty state (prefills /design:setup-ds). Sits above the muted suggestion list. */
335
+ .chat-empty-cta {
336
+ display: inline-flex;
337
+ align-items: center;
338
+ justify-content: center;
339
+ gap: var(--space-2);
340
+ width: 100%;
341
+ margin-top: var(--space-2);
342
+ font-family: var(--font-body);
343
+ font-size: var(--type-sm);
344
+ font-weight: 600;
345
+ color: var(--accent-fg);
346
+ background: var(--accent);
347
+ border: 1px solid var(--accent);
348
+ border-radius: var(--radius-sm);
349
+ padding: var(--space-3) var(--space-4);
350
+ cursor: pointer;
351
+ transition:
352
+ background var(--dur-soft) var(--ease-out),
353
+ border-color var(--dur-soft) var(--ease-out);
354
+ }
355
+ .chat-empty-cta:hover {
356
+ background: var(--accent-hover);
357
+ border-color: var(--accent-hover);
358
+ }
359
+ .chat-empty-cta:focus-visible {
360
+ outline: none;
361
+ border-color: var(--accent);
362
+ box-shadow: 0 0 0 3px var(--accent-tint);
363
+ }
364
+ .chat-empty-cta-ic {
365
+ display: inline-flex;
366
+ color: currentColor;
367
+ }
327
368
  .chat-sugs {
328
369
  display: flex;
329
370
  flex-direction: column;
@@ -408,6 +449,14 @@
408
449
  line-height: var(--lh-base);
409
450
  }
410
451
 
452
+ /* Continuation bubble — background work (subagent results / consolidation)
453
+ that streamed in after the turn's premature settle (RCA F2). A dashed rule
454
+ marks it as a continuation of the message above, not a fresh reply. */
455
+ .chat-msg--continued {
456
+ border-top: 1px dashed var(--border-subtle);
457
+ padding-top: var(--space-3);
458
+ }
459
+
411
460
  /* Rendered markdown inside the assistant bubble (chat-markdown.jsx) */
412
461
  .chat-msg--assistant .chat-bubble > * + * {
413
462
  margin-top: var(--space-2);
@@ -589,6 +638,37 @@
589
638
  color: var(--fg-1);
590
639
  font-weight: 600;
591
640
  }
641
+ /* Context attachment chip (feature-acp-context-hardening) — the visible
642
+ reveal of the frozen <maude-context> block; label + a remove ×. */
643
+ .chat-ctx-label {
644
+ display: inline-block;
645
+ max-width: 100%;
646
+ overflow: hidden;
647
+ text-overflow: ellipsis;
648
+ white-space: nowrap;
649
+ padding: 2px var(--space-2);
650
+ border: 1px solid var(--border-subtle);
651
+ border-radius: var(--radius-sm);
652
+ background: var(--bg-2);
653
+ color: var(--fg-1);
654
+ }
655
+ .chat-ctx--stale .chat-ctx-label {
656
+ border-color: var(--warn, hsl(38 92% 50%));
657
+ color: var(--warn, hsl(38 92% 50%));
658
+ }
659
+ .chat-ctx-x {
660
+ flex: none;
661
+ padding: 0 var(--space-1);
662
+ border: 0;
663
+ background: none;
664
+ color: var(--fg-2);
665
+ font-size: var(--type-sm);
666
+ line-height: 1;
667
+ cursor: pointer;
668
+ }
669
+ .chat-ctx-x:hover {
670
+ color: var(--fg-0);
671
+ }
592
672
 
593
673
  /* Composer box — textarea on top, a bottom toolbar (model · effort · send),
594
674
  Zed-style. One border + focus ring around the whole box. */
@@ -679,6 +759,42 @@
679
759
  opacity: 0.45;
680
760
  cursor: default;
681
761
  }
762
+ /* Attachment reveal strip — shows what each paste chip expands to BEFORE send,
763
+ so a collapsed [file-1]/[link-1]/[image-N] can never carry a value the user
764
+ can't see into the auto-approving agent (DDR-140 / security-review mitigation). */
765
+ .chat-attach {
766
+ display: flex;
767
+ flex-direction: column;
768
+ gap: 2px;
769
+ margin-top: var(--space-2);
770
+ }
771
+ .chat-attach-row {
772
+ display: flex;
773
+ align-items: center;
774
+ gap: var(--space-2);
775
+ min-width: 0;
776
+ font-size: var(--type-xs);
777
+ }
778
+ .chat-attach-tok {
779
+ flex-shrink: 0;
780
+ font-family: var(--font-mono);
781
+ color: var(--fg-1);
782
+ background: var(--bg-4, var(--bg-3));
783
+ border-radius: var(--radius-xs);
784
+ padding: 0 4px;
785
+ }
786
+ .chat-attach-arrow {
787
+ flex-shrink: 0;
788
+ color: var(--fg-3);
789
+ }
790
+ .chat-attach-val {
791
+ min-width: 0;
792
+ overflow: hidden;
793
+ text-overflow: ellipsis;
794
+ white-space: nowrap;
795
+ color: var(--fg-2);
796
+ font-family: var(--font-mono);
797
+ }
682
798
  .chat-foot {
683
799
  display: flex;
684
800
  align-items: center;
@@ -691,6 +807,134 @@
691
807
  flex: 1;
692
808
  }
693
809
 
810
+ /* ── Slash-command autocomplete + inline highlight ────────────────────────
811
+ The composer becomes command-aware: typing "/" opens `.chat-cmd-menu`
812
+ (populated from the static list ∪ the live ACP catalogue), and a recognized
813
+ command renders as a `.chat-cmd-pill` INSIDE the input via a mirror overlay. */
814
+
815
+ /* Relative anchor so the popover floats above the composer box. */
816
+ .chat-cmd-anchor {
817
+ position: relative;
818
+ }
819
+
820
+ /* Mirror-overlay: a text layer behind a transparent-text textarea. The mirror
821
+ is the visible text; the textarea owns the caret + input. Their box models
822
+ MUST stay identical (padding / font / line-height / wrap) or the pill drifts. */
823
+ .chat-input-wrap {
824
+ position: relative;
825
+ display: block;
826
+ }
827
+ .chat-input-mirror {
828
+ position: absolute;
829
+ inset: 0;
830
+ z-index: 0;
831
+ padding: var(--space-3) var(--space-4);
832
+ font-family: var(--font-body);
833
+ font-size: var(--type-sm);
834
+ line-height: var(--lh-base);
835
+ letter-spacing: 0;
836
+ color: var(--fg-0);
837
+ white-space: pre-wrap;
838
+ overflow-wrap: break-word;
839
+ word-break: break-word;
840
+ overflow: hidden;
841
+ pointer-events: none;
842
+ }
843
+ .chat-input--overlay {
844
+ position: relative;
845
+ z-index: 1;
846
+ line-height: var(--lh-base);
847
+ letter-spacing: 0;
848
+ color: transparent;
849
+ caret-color: var(--fg-0);
850
+ background: transparent;
851
+ }
852
+ /* The recognized-command pill. NO padding/margin/letter-spacing — those would
853
+ change glyph advance and desync the mirror from the textarea caret. The 2px
854
+ box-shadow fakes padding without touching layout. */
855
+ .chat-cmd-pill {
856
+ background: var(--accent-tint);
857
+ color: var(--accent);
858
+ border-radius: var(--radius-xs);
859
+ box-shadow: 0 0 0 2px var(--accent-tint);
860
+ }
861
+
862
+ /* Pasted-path / URL chip ([image-1] / [file-1] / [link-1]). Same zero-advance
863
+ discipline as .chat-cmd-pill (no padding/margin/letter-spacing → the mirror
864
+ stays glyph-aligned with the textarea caret). Neutral tint so it reads as an
865
+ attachment badge, distinct from the accent command pill. Reused verbatim in
866
+ the sent-message bubble, where the box-shadow padding is harmless. */
867
+ .chat-paste-chip {
868
+ background: var(--bg-4, var(--bg-3));
869
+ color: var(--fg-1);
870
+ border-radius: var(--radius-xs);
871
+ box-shadow: 0 0 0 2px var(--bg-4, var(--bg-3));
872
+ }
873
+ .chat-msg--user .chat-paste-chip {
874
+ color: var(--accent);
875
+ }
876
+
877
+ /* Autocomplete popover — opens upward (composer sits at the panel bottom). */
878
+ .chat-cmd-menu {
879
+ position: absolute;
880
+ left: 0;
881
+ right: 0;
882
+ bottom: calc(100% + var(--space-2));
883
+ z-index: 20;
884
+ max-height: 280px;
885
+ overflow-y: auto;
886
+ display: flex;
887
+ flex-direction: column;
888
+ gap: 1px;
889
+ padding: var(--space-1);
890
+ background: var(--bg-2);
891
+ border: 1px solid var(--border-default);
892
+ border-radius: var(--radius-sm);
893
+ box-shadow: var(--shadow-lg);
894
+ }
895
+ .chat-cmd-item {
896
+ display: flex;
897
+ align-items: baseline;
898
+ gap: var(--space-2);
899
+ width: 100%;
900
+ text-align: left;
901
+ appearance: none;
902
+ border: 0;
903
+ background: transparent;
904
+ border-radius: var(--radius-xs);
905
+ padding: var(--space-2) var(--space-3);
906
+ cursor: pointer;
907
+ color: var(--fg-1);
908
+ }
909
+ .chat-cmd-item:hover,
910
+ .chat-cmd-item.is-active {
911
+ background: var(--bg-3);
912
+ }
913
+ .chat-cmd-name {
914
+ flex-shrink: 0;
915
+ font-family: var(--font-mono);
916
+ font-size: var(--type-xs);
917
+ color: var(--accent);
918
+ }
919
+ .chat-cmd-name--flow {
920
+ color: var(--presence-agent);
921
+ }
922
+ .chat-cmd-desc {
923
+ flex: 1;
924
+ min-width: 0;
925
+ font-size: var(--type-xs);
926
+ color: var(--fg-2);
927
+ overflow: hidden;
928
+ text-overflow: ellipsis;
929
+ white-space: nowrap;
930
+ }
931
+ .chat-cmd-arg {
932
+ flex-shrink: 0;
933
+ font-family: var(--font-mono);
934
+ font-size: var(--type-xs);
935
+ color: var(--fg-3);
936
+ }
937
+
694
938
  /* Streaming → Stop bar */
695
939
  .chat-stopbar {
696
940
  display: flex;
@@ -14,7 +14,7 @@
14
14
  // the one honest hard thing, centered.
15
15
  //
16
16
  // Vocabulary is the canonical set ONLY (teaching-model table) — no raw git terms:
17
- // Save changes locally · Publish for everyone · Pull changes · Draft · Shared version.
17
+ // Save version · Publish · Get latest · branch · Shared version (matches the shipped buttons).
18
18
 
19
19
  import CollabModelInfographic from '../panels/CollabModelInfographic.jsx';
20
20
 
@@ -43,8 +43,8 @@ export const COLLAB_TOUR = [
43
43
  {
44
44
  target: "[data-tour='pull']",
45
45
  changes: true,
46
- title: 'Pull changes',
47
- body: 'When teammates publish, Pull brings their work onto your computer so you’re both looking at the same thing.',
46
+ title: 'Get latest',
47
+ body: 'When teammates publish, Get latest brings their work onto your computer so you’re both looking at the same thing.',
48
48
  placement: 'left',
49
49
  },
50
50
  {
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @file commands/reorder-command.ts — undo entry for element reorders
3
+ * @scope apps/studio/commands/reorder-command.ts
4
+ * @purpose Reversible record of a Phase 12.1 node-move (canvas drag /
5
+ * Layers-panel drag / Alt+arrow keyboard move). Before this command
6
+ * existed a reorder was covered by NEITHER the Cmd+Z stack NOR any
7
+ * in-app affordance — only /design:rollback could recover it.
8
+ *
9
+ * Why not an inverse move descriptor. A reorder churns every positional
10
+ * data-cd-id at or after the touched span, so a stored "move A back before B"
11
+ * goes stale the moment it's recorded. Instead the SERVER logs the whole-file
12
+ * {before, after} under a monotonic `seq` (api.ts reorderLog) and this record
13
+ * stores only { canvas, seq }: undo() asks the server to swap back to `before`,
14
+ * redo() to `after`. The server refuses (409) when the file changed since —
15
+ * honest failure instead of corrupting an interleaved edit.
16
+ *
17
+ * Origin split (DDR-054). This command runs INSIDE the untrusted canvas iframe,
18
+ * but `/_api/reorder-revert` is main-origin-only — so do()/undo() route through
19
+ * the injected `reorderRevertFn` sink, which posts `dgn:'reorder-revert'` to the
20
+ * parent shell; the shell performs the privileged write. Serializable payload
21
+ * (DDR-050 rev 2) so the stack survives the HMR reload the write triggers.
22
+ */
23
+
24
+ import type { CommandRecord, EditCommand } from '../undo-stack.ts';
25
+ import { registerCommand } from '../undo-stack.ts';
26
+
27
+ export interface ReorderPayload {
28
+ /** Canvas file path (same string the reorder POST used). */
29
+ canvas: string;
30
+ /** Server-assigned revert-log sequence number (api.ts reorderLog). */
31
+ seq: number;
32
+ }
33
+
34
+ /** Posts the revert request to the shell (production) or a spy (tests). */
35
+ export type ReorderRevertFn = (revert: {
36
+ canvas: string;
37
+ seq: number;
38
+ dir: 'undo' | 'redo';
39
+ }) => void | Promise<void>;
40
+
41
+ export const REORDER_KIND = 'reorder';
42
+
43
+ export function createReorderCommand(init: {
44
+ payload: ReorderPayload;
45
+ revertFn: ReorderRevertFn;
46
+ label?: string;
47
+ }): EditCommand {
48
+ const { payload, revertFn } = init;
49
+ return {
50
+ kind: REORDER_KIND,
51
+ label: init.label ?? 'move element',
52
+ async do() {
53
+ await revertFn({ canvas: payload.canvas, seq: payload.seq, dir: 'redo' });
54
+ },
55
+ async undo() {
56
+ await revertFn({ canvas: payload.canvas, seq: payload.seq, dir: 'undo' });
57
+ },
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Build a persistable record. The reorder already landed (the shell POSTed
63
+ * /_api/reorder before telling the canvas), so push via `record()` — appends
64
+ * WITHOUT re-running do().
65
+ */
66
+ export function buildReorderRecord(
67
+ payload: ReorderPayload,
68
+ label?: string
69
+ ): CommandRecord<ReorderPayload> {
70
+ return { kind: REORDER_KIND, label: label ?? 'move element', payload };
71
+ }
72
+
73
+ registerCommand<ReorderPayload>(REORDER_KIND, (record, sinks) => {
74
+ const revertFn = sinks.reorderRevertFn as ReorderRevertFn | undefined;
75
+ if (!revertFn) return null;
76
+ return createReorderCommand({ payload: record.payload, revertFn, label: record.label });
77
+ });
@@ -134,6 +134,12 @@
134
134
  "description": "How chromatically/decoratively expressive the DS is. Inferred by ux-research-agent from brand character during /design:setup-ds (NOT a picker), then anchors the structural knobs (accentStrategy, shadow/decor) and sets the default opt_out_scope for canvases under this DS (restrained|confident → palette, expressive → aesthetic, maximalist → full). Default 'restrained' is the legacy/no-bootstrap fallback only — a bootstrapped DS always writes the inferred value. Per DDR-073.",
135
135
  "default": "restrained"
136
136
  },
137
+ "dsFidelity": {
138
+ "type": "string",
139
+ "enum": ["advisory", "strict"],
140
+ "description": "How hard DS reuse is enforced during canvas generation/edit. 'advisory' (default) = reuse findings (invented brand mark/icons, reinvented components, parallel product shell) surface as warnings. 'strict' = when the DS ships a canonical specimen (logo, iconography, component, platform showcase), reinventing it is a blocker the auto-fix loop must clear. Orthogonal to opt_out_scope on the SAME axis of 'how much the DS binds': a canvas's explicit opt_out_scope=full always wins over strict (explicit free-use beats project policy). A11y is independent of this knob. Per DDR-141.",
141
+ "default": "advisory"
142
+ },
137
143
  "designSystems": {
138
144
  "type": "array",
139
145
  "description": "Design systems available in this project. Single-DS projects have one entry; multi-DS projects (marketing vs. admin vs. mobile) list each here. Each canvas's .meta.json declares which DS it uses via the 'designSystem' field. The System view's DS picker iterates this array.",