axstack 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 (137) hide show
  1. package/README.md +5 -4
  2. package/bin/axstack.js +4 -18
  3. package/docs/getting-started.md +6 -6
  4. package/docs/host-operations.md +9 -5
  5. package/docs/installation.md +20 -41
  6. package/docs/workflows.md +12 -9
  7. package/package.json +1 -1
  8. package/profiles/presets/claude-only.json +3 -3
  9. package/profiles/presets/codex-only.json +3 -3
  10. package/profiles/presets/mixed.json +5 -4
  11. package/skills/axstack/references/automations.md +7 -2
  12. package/skills/axstack/references/lifecycle.md +2 -1
  13. package/skills/axstack/references/review-manager-prompt.md +44 -3
  14. package/skills/axstack/references/role-roster.md +13 -6
  15. package/skills/axstack/references/routing.md +2 -1
  16. package/skills/axstack/references/run-record.md +2 -0
  17. package/skills/axstack/references/ui-verification.md +6 -10
  18. package/skills/axstack-anatomy/ANATOMY.md +172 -0
  19. package/skills/axstack-anatomy/CHANGELOG.md +70 -0
  20. package/skills/axstack-anatomy/LICENSE +21 -0
  21. package/skills/axstack-anatomy/PROVENANCE.md +21 -0
  22. package/skills/axstack-anatomy/README.md +316 -0
  23. package/skills/axstack-anatomy/SKILL.md +74 -0
  24. package/skills/axstack-anatomy/SOURCE-PROOF.json +107 -0
  25. package/skills/axstack-anatomy/examples/arcade-cabinet/arcade-cabinet.css +44 -0
  26. package/skills/axstack-anatomy/examples/arcade-cabinet/build.mjs +714 -0
  27. package/skills/axstack-anatomy/examples/arcade-cabinet/live.js +597 -0
  28. package/skills/axstack-anatomy/examples/arcade-cabinet/parts.mjs +89 -0
  29. package/skills/axstack-anatomy/examples/arcade-cabinet/screen.mjs +114 -0
  30. package/skills/axstack-anatomy/examples/desk-computer/build.mjs +435 -0
  31. package/skills/axstack-anatomy/examples/dial-indicator/build.mjs +275 -0
  32. package/skills/axstack-anatomy/examples/raptor-engine/build.mjs +938 -0
  33. package/skills/axstack-anatomy/examples/raptor-engine/live.js +907 -0
  34. package/skills/axstack-anatomy/examples/raptor-engine/raptor.css +359 -0
  35. package/skills/axstack-anatomy/examples/ripple-tank/build.mjs +558 -0
  36. package/skills/axstack-anatomy/examples/ripple-tank/live.js +579 -0
  37. package/skills/axstack-anatomy/examples/ripple-tank/model.mjs +131 -0
  38. package/skills/axstack-anatomy/examples/ripple-tank/ripple.css +103 -0
  39. package/skills/axstack-anatomy/examples/spot-plate/art.tsx +110 -0
  40. package/skills/axstack-anatomy/examples/spot-plate/drops.ts +541 -0
  41. package/skills/axstack-anatomy/examples/spot-plate/fan.ts +84 -0
  42. package/skills/axstack-anatomy/examples/spot-plate/figure.tsx +25 -0
  43. package/skills/axstack-anatomy/examples/spot-plate/geometry.ts +280 -0
  44. package/skills/axstack-anatomy/examples/spot-plate/live.tsx +339 -0
  45. package/skills/axstack-anatomy/examples/spot-plate/rig.ts +230 -0
  46. package/skills/axstack-anatomy/examples/spot-plate/spot-plate.css +92 -0
  47. package/skills/axstack-anatomy/examples/test-rig/art.tsx +196 -0
  48. package/skills/axstack-anatomy/examples/test-rig/figure.tsx +23 -0
  49. package/skills/axstack-anatomy/examples/test-rig/geometry.ts +305 -0
  50. package/skills/axstack-anatomy/examples/test-rig/live.tsx +511 -0
  51. package/skills/axstack-anatomy/examples/test-rig/pose.ts +107 -0
  52. package/skills/axstack-anatomy/examples/test-rig/rig.ts +170 -0
  53. package/skills/axstack-anatomy/examples/test-rig/stretch.css +80 -0
  54. package/skills/axstack-anatomy/examples/test-rig/view.ts +99 -0
  55. package/skills/axstack-anatomy/examples/turning-dial/build.mjs +479 -0
  56. package/skills/axstack-anatomy/examples/turning-dial/dial.css +10 -0
  57. package/skills/axstack-anatomy/examples/turning-dial/live.js +342 -0
  58. package/skills/axstack-anatomy/examples/turning-dial/stress.mjs +269 -0
  59. package/skills/axstack-anatomy/kit/audit.mjs +1913 -0
  60. package/skills/axstack-anatomy/kit/canvas-painter.mjs +722 -0
  61. package/skills/axstack-anatomy/kit/gl-shared.mjs +184 -0
  62. package/skills/axstack-anatomy/kit/gl.mjs +184 -0
  63. package/skills/axstack-anatomy/kit/iso-kit.mjs +674 -0
  64. package/skills/axstack-anatomy/kit/iso-kit.ts +842 -0
  65. package/skills/axstack-anatomy/kit/iso.css +106 -0
  66. package/skills/axstack-anatomy/kit/lathe.mjs +1184 -0
  67. package/skills/axstack-anatomy/kit/react/draw.tsx +205 -0
  68. package/skills/axstack-anatomy/kit/react/live.tsx +140 -0
  69. package/skills/axstack-anatomy/kit/tube.mjs +306 -0
  70. package/skills/axstack-anatomy/kit/turn-audit.mjs +1096 -0
  71. package/skills/axstack-anatomy/kit/turn-build.mjs +1355 -0
  72. package/skills/axstack-anatomy/kit/turn-fixed.mjs +3800 -0
  73. package/skills/axstack-anatomy/kit/turn.mjs +3610 -0
  74. package/skills/axstack-anatomy/references/3d.md +352 -0
  75. package/skills/axstack-anatomy/references/craft.md +325 -0
  76. package/skills/axstack-anatomy/references/kit.md +419 -0
  77. package/skills/axstack-anatomy/references/motion.md +165 -0
  78. package/skills/axstack-anatomy/references/react.md +42 -0
  79. package/skills/axstack-anatomy/references/verify.md +351 -0
  80. package/skills/axstack-anatomy/references/walkthrough.md +75 -0
  81. package/skills/axstack-anatomy/references/webgl.md +473 -0
  82. package/skills/axstack-anatomy/scripts/audit-affine-bounds.test.mjs +54 -0
  83. package/skills/axstack-anatomy/scripts/audit-cache.test.mjs +37 -0
  84. package/skills/axstack-anatomy/scripts/audit-clearance-envelope.test.mjs +39 -0
  85. package/skills/axstack-anatomy/scripts/audit-empty-clipping.test.mjs +38 -0
  86. package/skills/axstack-anatomy/scripts/audit-order-convergence.test.mjs +55 -0
  87. package/skills/axstack-anatomy/scripts/audit-path-origin.test.mjs +26 -0
  88. package/skills/axstack-anatomy/scripts/audit-profile.test.mjs +41 -0
  89. package/skills/axstack-anatomy/scripts/audit-projection.test.mjs +36 -0
  90. package/skills/axstack-anatomy/scripts/audit-slide-clearance.test.mjs +58 -0
  91. package/skills/axstack-anatomy/scripts/audit-svg-attributes.test.mjs +46 -0
  92. package/skills/axstack-anatomy/scripts/audit-taper.test.mjs +29 -0
  93. package/skills/axstack-anatomy/scripts/capture-selector.test.mjs +131 -0
  94. package/skills/axstack-anatomy/scripts/capture.mjs +228 -0
  95. package/skills/axstack-anatomy/scripts/capture.test.mjs +42 -0
  96. package/skills/axstack-anatomy/scripts/chrome-port.mjs +36 -0
  97. package/skills/axstack-anatomy/scripts/chrome-startup.test.mjs +38 -0
  98. package/skills/axstack-anatomy/scripts/drive-actions.test.mjs +46 -0
  99. package/skills/axstack-anatomy/scripts/drive.mjs +302 -0
  100. package/skills/axstack-anatomy/scripts/drive.test.mjs +72 -0
  101. package/skills/axstack-anatomy/scripts/enter-key.test.mjs +38 -0
  102. package/skills/axstack-anatomy/scripts/fidelity-cut.test.mjs +54 -0
  103. package/skills/axstack-anatomy/scripts/fidelity-tube-coverage.test.mjs +40 -0
  104. package/skills/axstack-anatomy/scripts/inline-kit.mjs +26 -0
  105. package/skills/axstack-anatomy/scripts/line-ends.mjs +306 -0
  106. package/skills/axstack-anatomy/scripts/lines-arcs.test.mjs +50 -0
  107. package/skills/axstack-anatomy/scripts/lines-bezier.test.mjs +28 -0
  108. package/skills/axstack-anatomy/scripts/lines-round.test.mjs +56 -0
  109. package/skills/axstack-anatomy/scripts/lines-rounded-rect.test.mjs +32 -0
  110. package/skills/axstack-anatomy/scripts/lines.mjs +31 -0
  111. package/skills/axstack-anatomy/scripts/lines.test.mjs +38 -0
  112. package/skills/axstack-anatomy/scripts/navigation.test.mjs +55 -0
  113. package/skills/axstack-anatomy/scripts/orbit-sampling.test.mjs +50 -0
  114. package/skills/axstack-anatomy/scripts/react-dots-opacity.test.mjs +51 -0
  115. package/skills/axstack-anatomy/scripts/react-readout-first-write.test.mjs +37 -0
  116. package/skills/axstack-anatomy/scripts/react-solid-seamless.test.mjs +47 -0
  117. package/skills/axstack-anatomy/scripts/react-visibility-target.test.mjs +38 -0
  118. package/skills/axstack-anatomy/scripts/turn-bench.mjs +74 -0
  119. package/skills/axstack-anatomy/scripts/turn-check.mjs +641 -0
  120. package/skills/axstack-anatomy/scripts/turn-fidelity.mjs +413 -0
  121. package/skills/axstack-anatomy/scripts/turn-group-reference.test.mjs +40 -0
  122. package/skills/axstack-anatomy/scripts/turn-split-owner.test.mjs +62 -0
  123. package/skills/axstack-anatomy/theme.js +56 -0
  124. package/skills/axstack-explain/SKILL.md +28 -15
  125. package/skills/axstack-explain/references/delivery.md +22 -0
  126. package/skills/{axstack-diagram → axstack-explain}/references/fidelity.md +1 -7
  127. package/skills/axstack-explain/references/inline-pages.md +8 -8
  128. package/skills/axstack-explain/references/visual-qa.md +3 -5
  129. package/skills/axstack-explain/references/whiteboard-defense.md +59 -0
  130. package/skills/axstack-research/SKILL.md +18 -6
  131. package/src/archify-legacy.js +70 -0
  132. package/src/capabilities.js +1 -29
  133. package/src/installer.js +11 -31
  134. package/skills/axstack-diagram/SKILL.md +0 -28
  135. package/skills/axstack-diagram/references/archify.md +0 -76
  136. package/src/archify-pin.js +0 -5
  137. package/src/archify.js +0 -151
@@ -0,0 +1,22 @@
1
+ # Delivery and holds
2
+
3
+ Use this single fallback rule for Explain and direct Anatomy delivery.
4
+ A whiteboard-defense explanation covers a whole system, PR, run or change
5
+ under [Whiteboard defense](whiteboard-defense.md).
6
+ If the reviewer is unavailable, hold the whiteboard-defense explanation and name the unavailable reviewer.
7
+ Hold a whiteboard-defense explanation still failing claim review after two repair rounds with
8
+ the same author, naming unresolved findings. Never deliver it unreviewed or as reviewed.
9
+ Visual-only findings follow the visual rule, and claim findings hold the whiteboard-defense explanation.
10
+
11
+ For a missing preview or render tool, unavailable verifier or check, or page
12
+ absent after ambiguous `html_render`, hold the page and answer in plain prose
13
+ naming the held page and the reason.
14
+ Whiteboard-defense plain prose still requires `axstack-explainer-review`.
15
+ If a required author or reviewer seat is unavailable, hold the visual and
16
+ answer in plain prose naming the held visual.
17
+ Replace a visual still failing checks after two repair rounds with the same
18
+ author by plain prose with the reason.
19
+ The driver never writes a substitute visual.
20
+ Each repair round returns findings to the same author and rechecks the exact final bytes.
21
+ An anatomy page above the size cap follows [Anatomy](../../axstack-anatomy/SKILL.md)
22
+ for oversized delivery, with the same author and reviewer bindings.
@@ -6,14 +6,8 @@ For uncommitted sources, use the content hash and mark their pins unknown.
6
6
  Keep the source map outside the reader view.
7
7
  Display the short SHA.
8
8
 
9
- Give planned and unknown nodes a visible marker in the node label and their card, if any.
10
- Use the literal marker `planned` or `unknown` for all five types.
9
+ Give planned and unknown nodes a visible marker in the node label.
11
10
  Give planned and unknown edges a visible `planned` or `unknown` marker in the edge label.
12
- For dashed styling, use only properties the pinned schema accepts and verify with `validate`.
13
- This is an extra cue, such as a dashed relationship where supported.
14
- The pinned node collections reject `variant`.
15
- Add a planned or unknown legend entry only for sequence and dataflow.
16
- Architecture, workflow, and lifecycle reject a `dashed` legend entry.
17
11
  In Mermaid, use the same visible markers and a dashed cue where its syntax permits.
18
12
 
19
13
  Treat about 9 core nodes as a signal to split.
@@ -9,10 +9,11 @@ Use T3 theme variables and follow the live theme.
9
9
  Only an explicit user theme request overrides the page theme.
10
10
  Use `--background`, `--foreground`, `--muted-foreground`, `--ring`,
11
11
  `--font-sans`, and `--font-mono` for their named purposes.
12
- The dark default stays archify-only.
13
12
 
14
13
  Use fluid width, with zero outer horizontal padding.
15
14
  Never use an outer frame, banner, or viewport heights.
15
+ Anatomy may retain its figure card as an inner panel with at least 16px padding
16
+ and `var(--radius)`; its page wrapper still has zero gutters and no background.
16
17
  Let content set the page height. Use fixed chart heights.
17
18
  Set frame `height` to the larger contentHeight of the two previews, capped at 2000.
18
19
 
@@ -44,7 +45,9 @@ Pages never use storage, cookies, modals, or popups.
44
45
 
45
46
  Keep page bytes, SHA-256, and attachmentId in run evidence.
46
47
  Scan the exact bytes of every page for remote `src`, `srcset`, and `href` on loaded resources.
47
- Scan for `@import`, `url()`, `fetch`, XHR, WebSocket, EventSource, and meta refresh.
48
+ Scan for `@import`, CSS `url(` tokens at a word boundary with non-`#` targets, `fetch`, XHR, WebSocket, EventSource, and meta refresh.
49
+ A word boundary before `url(` excludes identifiers such as `knurl(`; local `url(#id)` remains allowed.
50
+ Trim CSS URL target whitespace and quotes before checking whether it starts with `#`.
48
51
  Reject scan matches except local `url(#id)` references such as SVG markers and gradients.
49
52
 
50
53
  Before publishing, run `html_preview` at 728px dark and 360px light.
@@ -63,13 +66,10 @@ Follow [UI verification](../../axstack/references/ui-verification.md) for that p
63
66
  ## Publish or fall back
64
67
 
65
68
  Publish one page per answer.
66
- Publish with `html_render` from exactly the checked bytes.
69
+ Only the driver publishes with `html_render` from exactly the checked bytes.
67
70
  Fix a failed check and rerun on the new bytes.
68
71
  A byte change invalidates affected checks and review.
69
- If either tool cannot be found after one bounded discovery attempt, fall back
70
- to chat with Mermaid and state the reason.
71
- If a check, verifier, or required reviewer is unavailable or still fails, fall
72
- back to chat with Mermaid and state the reason.
72
+ Follow [Delivery](delivery.md) for missing tools, seats, failed checks and repair holds.
73
73
  After an ambiguous `html_render` result, never retry.
74
74
  After an ambiguous `html_render` result, read `t3_thread_read`, keep the page
75
- if present, and otherwise fall back to chat.
75
+ if present, and otherwise hold the page and follow Delivery.
@@ -8,18 +8,16 @@ rendering matters.
8
8
  Browser and visual checks must run in the delegated `axstack-ui-verifier` in its own detached checkout; outputs go to its private evidence folder, never the driver worktree.
9
9
 
10
10
  1. Identify the final artifact bytes and theme. The explicit user theme wins;
11
- otherwise use the dark default.
12
- For archify output, bind the `axstack-ui-verifier` pass to the finalize receipt's `artifact.sha256`.
13
- For archify output, bind `axstack-explainer-review` to the same finalize receipt's `artifact.sha256`.
11
+ otherwise use `prefers-color-scheme`.
14
12
  2. Delegate the rendered pass through [UI verification](../../axstack/references/ui-verification.md).
15
13
  Record actual desktop and mobile observations, or name the missing layout
16
14
  check.
17
15
  3. Have the verifier exercise relevant interactions, keyboard and screen-reader
18
16
  accessibility, and reduced-motion behavior. Report each unavailable check
19
17
  honestly.
20
- 4. The explainer reviewer checks text and source fidelity. Keep source
18
+ 4. The explainer reviewer checks text and source fidelity, including every node
19
+ and edge against source. Keep source
21
20
  correctness, tests, rendered behavior, independent review, and publication
22
21
  evidence separate.
23
- For archify output, require `axstack-explainer-review` of every node and edge against source.
24
22
  5. Bind review to the exact artifact identity. Any byte change invalidates the
25
23
  affected approval and requires fresh QA and review.
@@ -0,0 +1,59 @@
1
+ # Whiteboard defense
2
+
3
+ Apply whiteboard defense to a whole system, PR, run or change, whether shipped,
4
+ in review or planned. Narrow questions stay proportional.
5
+ Triggers include "help me defend X", "whiteboard defense for PR N", and
6
+ "prep me for questions on X".
7
+ Extend §2.1's base for every explanation with the five elements below.
8
+ Use the §1.6 decision trail as evidence for decisions.
9
+ Spec-readback identity wins when defending a spec.
10
+ Show-your-work and blast-radius requests keep their rules unless the user also
11
+ asks to understand or defend the whole.
12
+
13
+ 1. Cover how it works at a high level and where data flows.
14
+ 2. Cover key decisions as "why X instead of Y".
15
+ 3. Cover the main data structures or state and why they were chosen.
16
+ 4. Cover what happens if a user, dependency, agent or attacker misbehaves or
17
+ acts maliciously.
18
+ 5. Cover where it fails and how that failure shows.
19
+
20
+ Traced means source, tests, observation or recorded design (spec, run-record
21
+ Decisions, ADR, PR), each with its §1.3 label.
22
+ Label design-only claims planned/proposed.
23
+ For shipped or in-review work, list design-only (4) or (5) as an open gap:
24
+ "implementation not traced".
25
+ List a missing alternative, reason, data-structure rationale, or untraced
26
+ misbehaviour or failure as open gaps. Never invent alternatives, reasons,
27
+ threats or failures.
28
+ Name each trust boundary on the flow traced in (1), with an evidence pointer
29
+ and the actors crossing it; an unclear boundary is an open gap.
30
+ Answer each actor the user names or list it open.
31
+ State in one line that other actors are out of scope.
32
+ Omissions of (2) and (3) require "does not apply", a one-line reason and
33
+ an evidence pointer. Never omit (1), (4) or (5); answer them or mark them open.
34
+ Never drop an element silently.
35
+ (5) does not trigger the blast-radius proof; that runs only on "what could this break".
36
+ Use function names and line-level detail only as evidence pointers.
37
+
38
+ Close with likely whiteboard questions beyond (1)-(5), each with an
39
+ evidence-backed labelled answer or marked open.
40
+ Give each open gap one line naming the missing evidence and saying to resolve
41
+ it before defending the work.
42
+ Every whiteboard-defense explanation is consequential for review, in chat too.
43
+ Follow [Delivery](delivery.md) for reviewer-outage and two-round holds.
44
+
45
+ If five elements and closing questions exceed 700 words, keep the top level,
46
+ every element and the open gaps as at least one line each.
47
+ Offer per-component explanations for closing questions that cannot fit and
48
+ count how many moved. Never drop questions silently.
49
+ If mandatory lines exceed 700 words, give the top-level explanation first and
50
+ split the rest into per-component explanations; never exceed the cap.
51
+ The delivered view keeps (1), one line each for (4) and (5), and the open-gap
52
+ count with the components holding them.
53
+ State how many elements and open gaps moved.
54
+
55
+ When the user or stated records (run record, spec, PR body or README) identify
56
+ a PoC, demo or experiment, use an optional checklist and proportional answer
57
+ under the ordinary review rule. Never infer that status.
58
+ An intent to ship overrides the PoC exemption.
59
+ An explicit checklist request requires full whiteboard defense.
@@ -28,10 +28,20 @@ is part of research.
28
28
  gathering evidence only after the target and stopping boundary are explicit.
29
29
 
30
30
  2. **Fan out research:** A single factual lookup stays in the current chat.
31
- Every other research run dispatches every configured research branch through
32
- T3 `delegate_task`: requirements, code, and web (Sonnet high in mixed/claude-only;
33
- Codex in codex-only), web-google (Gemini/Antigravity, with Google Search
34
- built in), and X (Grok).
31
+ For other runs, dispatch through T3 `delegate_task` only branches whose
32
+ source class (requirements, code, execution, web, X) an open answer-changing claim needs.
33
+ Dispatch requirements only when scope is unclear.
34
+ Dispatch execution when a claim depends on runtime behavior.
35
+ Dispatch X only when a claim depends on X posts.
36
+ Record each skipped branch as `not needed (<reason>)`.
37
+ For each needed source class, retain two isolated branches where configured.
38
+ In mixed, each needed source class retains branches from different providers.
39
+ The code pair is `axstack-research-code` +
40
+ `axstack-research-code-sol`; the web pair is `axstack-research-web` +
41
+ `axstack-research-web-google`; the execution pair is `axstack-explore-execution` +
42
+ `axstack-explore-execution-sol`. Requirements, code, and web use Sonnet high
43
+ in mixed/claude-only and Codex in codex-only; web-google uses
44
+ Gemini/Antigravity with Google Search built in, and X uses Grok.
35
45
  Give each branch one owner, allow no cross-reading, and require a cited note
36
46
  with a URL and access date per claim; re-open sources and never trust a search
37
47
  summary. The driver reconciles agreements/disagreements per claim.
@@ -61,8 +71,10 @@ is part of research.
61
71
  never averaging findings. Record an intentionally absent pair and proceed
62
72
  with the base seat alone. The `-sol` pair is optional: fence a launch failure,
63
73
  trust/login prompt, or prompt block; record `absent (<reason>)`, and continue
64
- with the base seat. In mixed fan-out,
65
- retain a Codex and a Claude seat or hold the affected fan-out.
74
+ with the base seat. A sized mixed research fan-out requires dispatched
75
+ branches spanning at least two different providers; otherwise hold that fan-out.
76
+ Every other mixed fan-out, including arena and peer review, retains at least one
77
+ Codex and one Claude seat; otherwise hold that fan-out.
66
78
 
67
79
  3. **Gather primary source evidence.** Inspect the actual documentation, code,
68
80
  or tool output for every answer-changing claim. Apply the source standards
@@ -0,0 +1,70 @@
1
+ import { lstat, readFile, rm } from 'node:fs/promises';
2
+
3
+ export const ARCHIFY_RECORD = 'axstack-diagram/archify.json';
4
+
5
+ function archifyGit(path, ...args) {
6
+ try {
7
+ const result = Bun.spawnSync(['git', ...(path ? ['-C', path] : []), ...args], {
8
+ stdout: 'pipe', stderr: 'pipe', timeout: 60000,
9
+ env: { ...Bun.env, GIT_TERMINAL_PROMPT: '0' },
10
+ });
11
+ if (result.exitCode !== 0) throw new Error(result.stderr.toString().trim() || `git exit ${result.exitCode}`);
12
+ return result.stdout.toString().trim();
13
+ } catch (err) {
14
+ throw new Error(`git: ${err.message}`);
15
+ }
16
+ }
17
+
18
+ async function ownersSnapshot(path) {
19
+ const st = await lstat(path).catch((err) => {
20
+ if (err.code === 'ENOENT') return null;
21
+ throw err;
22
+ });
23
+ if (st && (!st.isFile() || st.isSymbolicLink())) throw new Error(`unsafe archify owners file: ${path}`);
24
+ const bytes = st ? await readFile(path) : null;
25
+ // JSON.parse coerces its argument to a string; preserve the existing Buffer coercion.
26
+ const owners = bytes ? /** @type {(text: {toString(): string}) => unknown} */ (JSON.parse)(bytes) : [];
27
+ if (!Array.isArray(owners) || owners.some((owner) => typeof owner !== 'string' || !owner.startsWith('/'))) {
28
+ throw new Error(`invalid archify owners file: ${path}`);
29
+ }
30
+ return { bytes, owners, mode: st ? st.mode & 0o777 : null };
31
+ }
32
+
33
+ // Drop ownership transactionally; defer destructive cleanup until the skills
34
+ // manifest commits. A failed manifest write can then restore the owner list.
35
+ export async function planArchifyRelease({ record, skillsRoot, writeAtomic, log }) {
36
+ const ownersFile = `${record.path}.owners.json`;
37
+ const before = await ownersSnapshot(ownersFile);
38
+ const remaining = before.owners.filter((owner) => owner !== skillsRoot);
39
+ if (!before.owners.includes(skillsRoot)) {
40
+ return { rollback: async () => {}, finish: async () => log(`archify: kept ${record.path} (owner is absent)`) };
41
+ }
42
+ try {
43
+ await writeAtomic(ownersFile, JSON.stringify(remaining) + '\n', { mode: 0o600 });
44
+ } catch (err) {
45
+ const current = await readFile(ownersFile);
46
+ if (!current.equals(before.bytes)) {
47
+ try { await writeAtomic(ownersFile, before.bytes, { mode: before.mode }); }
48
+ catch (restoreErr) { err.message += ` (archify owner restore failed: ${restoreErr.message})`; }
49
+ }
50
+ throw err;
51
+ }
52
+ return {
53
+ rollback: () => writeAtomic(ownersFile, before.bytes, { mode: before.mode }),
54
+ finish: async () => {
55
+ try {
56
+ if (remaining.length) throw new Error('other skills roots still own this copy');
57
+ const st = await lstat(record.path);
58
+ if (!st.isDirectory() || st.isSymbolicLink()) throw new Error('copy is not a real directory');
59
+ if (archifyGit(record.path, 'rev-parse', 'HEAD') !== record.sha) throw new Error('HEAD differs from the recorded SHA');
60
+ if (archifyGit(record.path, 'status', '--porcelain', '--ignored')) throw new Error('copy has tracked, untracked, or ignored changes');
61
+ await rm(record.path, { recursive: true });
62
+ await rm(ownersFile);
63
+ log(`archify: removed ${record.path}`);
64
+ } catch (err) {
65
+ log(`archify: kept ${record.path} (${err.message})`);
66
+ }
67
+ },
68
+ };
69
+ }
70
+
@@ -1,8 +1,4 @@
1
1
  // Host capability checks. Injected execution keeps tests off live runtimes.
2
- import { lstat, readFile } from 'node:fs/promises';
3
- import { join } from './posixpath.js';
4
- import { ARCHIFY_RECORD, archifyGit } from './archify.js';
5
- import { ARCHIFY_PIN } from './archify-pin.js';
6
2
  export const BUN_FLOOR = '1.3.14';
7
3
  export const T3_FLOOR = '0.0.46-nightly.20261003.2610';
8
4
 
@@ -109,28 +105,4 @@ export async function checkCapabilities(exec) {
109
105
  return { checks, gaps, limitations: [...PROBE_LIMITATIONS] };
110
106
  }
111
107
 
112
- export async function checkArchify(skillsDir) {
113
- const recordFile = join(skillsDir, ARCHIFY_RECORD);
114
- const override = Bun.env.ARCHIFY_CHROME;
115
- const chrome = override ? Bun.which(override) :
116
- ['google-chrome', 'google-chrome-stable', 'chromium', 'chromium-browser', 'chrome']
117
- .map((name) => Bun.which(name)).find(Boolean);
118
- let record = null;
119
- let actual = null;
120
- let reason = null;
121
- try {
122
- if ((await lstat(recordFile)).isSymbolicLink()) throw new Error('record is a symlink');
123
- record = JSON.parse(await readFile(recordFile, 'utf8'));
124
- if (!record || typeof record.path !== 'string' || !record.path.startsWith('/') ||
125
- !/^[a-f0-9]{40}$/.test(record.sha)) throw new Error('invalid record');
126
- const copy = await lstat(record.path);
127
- if (!copy.isDirectory() || copy.isSymbolicLink()) throw new Error('copy is not a real directory');
128
- actual = archifyGit(record.path, 'rev-parse', 'HEAD');
129
- if (actual !== record.sha || record.sha !== ARCHIFY_PIN.sha) {
130
- throw new Error(`SHA mismatch: recorded ${record.sha}, checked-out ${actual}, expected ${ARCHIFY_PIN.sha}`);
131
- }
132
- } catch (err) {
133
- reason = err.code === 'ENOENT' ? 'missing record or copy' : err.message;
134
- }
135
- return { recordFile, record, actual, chrome: chrome ?? null, reason };
136
- }
108
+
package/src/installer.js CHANGED
@@ -38,7 +38,7 @@ import {
38
38
  writeManifest,
39
39
  } from './manifest.js';
40
40
  import { planInstallClaudeSettings, planUninstallClaudeSettings } from './claude-settings.js';
41
- import { ARCHIFY_RECORD, planArchifyRelease, provisionArchify } from './archify.js';
41
+ import { ARCHIFY_RECORD, planArchifyRelease } from './archify-legacy.js';
42
42
  import {
43
43
  applyInstructionPlan,
44
44
  findLegacyRoutingLines,
@@ -485,14 +485,13 @@ async function assertFileSnapshot(dest, expected) {
485
485
  */
486
486
 
487
487
  /**
488
- * @param {InstallerOptions & {bundleDir?: string, preset?: string, toolsDir?: string,
488
+ * @param {InstallerOptions & {bundleDir?: string, preset?: string,
489
489
  * inheritedInstructions?: {path: string, hash: string}}} [options]
490
490
  */
491
491
  export async function installBundle({
492
492
  bundleDir,
493
493
  skillsDir,
494
494
  preset,
495
- toolsDir = null,
496
495
  instructionsPath = null,
497
496
  inheritedInstructions = null,
498
497
  force = false,
@@ -511,14 +510,6 @@ export async function installBundle({
511
510
  ? await canonicalInstructionFile(instructionsPath)
512
511
  : null;
513
512
  assertOutsideHome(skillsRoot, { yes, kind: 'skills directory' });
514
- const toolsPath = toolsDir ?? (homeDir() ? join(homeDir(), '.axstack', 'tools') : null);
515
- if (!toolsPath) throw new Error('HOME is unavailable; pass --tools-dir explicitly');
516
- await assertNoSymlinksBelow('/', resolve(toolsPath));
517
- const toolsRoot = await canonicalTargetDir(toolsPath);
518
- if (withinRoot(toolsRoot, skillsRoot) || withinRoot(skillsRoot, toolsRoot)) {
519
- throw new Error('tools directory overlaps the skills root; refusing');
520
- }
521
- assertOutsideHome(toolsRoot, { yes, kind: 'tools directory' });
522
513
  if (instructionsFile) assertOutsideHome(instructionsFile, { yes, kind: 'instruction file' });
523
514
  const claudePlan = await planInstallClaudeSettings({ claude, skillsRoot });
524
515
  if (claudePlan.report.path) {
@@ -586,11 +577,6 @@ export async function installBundle({
586
577
  // manifest entry resolves its destination, current bytes, and pristine
587
578
  // flag through the same guard before Phase 2 mutates anything.
588
579
  const desired = [];
589
- const archifyTarget = await readOwnedTarget(skillsRoot, ARCHIFY_RECORD);
590
- if (archifyTarget.current !== null &&
591
- hashContent(archifyTarget.current) !== ownedFiles[ARCHIFY_RECORD]) {
592
- throw new Error('archify record is edited or unowned; refusing to replace it');
593
- }
594
580
  for (const { rel, abs, content: inlineContent } of bundle.files) {
595
581
  const { dest, current, mode } = await readOwnedTarget(skillsRoot, rel);
596
582
  const content = inlineContent ?? await readFile(abs);
@@ -608,7 +594,6 @@ export async function installBundle({
608
594
  }
609
595
 
610
596
  const bundleRels = new Set(bundle.files.map((file) => file.rel));
611
- bundleRels.add(ARCHIFY_RECORD);
612
597
  const stalePlan = [];
613
598
  for (const rel of Object.keys(ownedFiles)) {
614
599
  if (bundleRels.has(rel)) continue;
@@ -638,7 +623,6 @@ export async function installBundle({
638
623
  const installedHashes = {};
639
624
  const claudeWritten = [];
640
625
  let instructionWritten = false;
641
- let archify = null;
642
626
  let archifyRelease = null;
643
627
  const manifestFile = join(skillsRoot, '.axstack-manifest.json');
644
628
  const manifestBefore = await readFile(manifestFile).catch((err) => {
@@ -649,12 +633,6 @@ export async function installBundle({
649
633
  let manifestAttempted = false;
650
634
  log('plan complete');
651
635
  try {
652
- archify = await provisionArchify({ toolsRoot, skillsRoot, writeAtomic, log });
653
- const archifyRecord = archify.record ?? prevManifest.archify ?? null;
654
- if (archifyRecord) {
655
- desired.push({ rel: ARCHIFY_RECORD, ...archifyTarget,
656
- content: Buffer.from(JSON.stringify(archifyRecord, null, 2) + '\n') });
657
- }
658
636
  if (instructionsFile) {
659
637
  await assertFileSnapshot(instructionsFile, existingInstructionsRaw);
660
638
  }
@@ -771,7 +749,11 @@ export async function installBundle({
771
749
  )
772
750
  : null;
773
751
 
774
- if (prevManifest.archify && archifyRecord?.path !== prevManifest.archify.path) {
752
+ const legacyRecord = await readOwnedTarget(skillsRoot, ARCHIFY_RECORD);
753
+ if (legacyRecord.current !== null) {
754
+ log(`archify: kept ${prevManifest.archify?.path ?? 'unbound copy'} (preserved record ${legacyRecord.dest})`);
755
+ }
756
+ if (prevManifest.archify && legacyRecord.current === null) {
775
757
  archifyRelease = await planArchifyRelease({
776
758
  record: prevManifest.archify, skillsRoot, writeAtomic, log,
777
759
  });
@@ -785,7 +767,6 @@ export async function installBundle({
785
767
  path: claudePlan.settingsPath ?? boundClaudeSettingsPath,
786
768
  },
787
769
  instructions: nextInstructions,
788
- archify: archifyRecord,
789
770
  });
790
771
  if (archifyRelease) await archifyRelease.finish();
791
772
  if (legacyProfileNote || legacyInstructionNote) {
@@ -837,10 +818,6 @@ export async function installBundle({
837
818
  try { await archifyRelease.rollback(); }
838
819
  catch (restoreErr) { rollbackErrors.push(`old archify owner: ${restoreErr.message}`); }
839
820
  }
840
- if (archify) {
841
- try { await archify.rollback(); }
842
- catch (restoreErr) { rollbackErrors.push(`archify: ${restoreErr.message}`); }
843
- }
844
821
  for (const write of claudeWritten.reverse()) {
845
822
  try {
846
823
  if (write.before === null) await rm(write.path);
@@ -1052,6 +1029,9 @@ export async function uninstallBundle({
1052
1029
  summary.claudeSettings = claudePlan.report;
1053
1030
 
1054
1031
  const remainingArchify = ARCHIFY_RECORD in remainingFiles ? manifest.archify : null;
1032
+ if (remainingArchify) {
1033
+ log(`archify: kept ${remainingArchify.path} (preserved record ${join(skillsRoot, ARCHIFY_RECORD)})`);
1034
+ }
1055
1035
  if (manifest.archify && !remainingArchify) {
1056
1036
  archifyRelease = await planArchifyRelease({ record: manifest.archify, skillsRoot, writeAtomic, log });
1057
1037
  }
@@ -1070,10 +1050,10 @@ export async function uninstallBundle({
1070
1050
  await writeManifest(skillsRoot, {
1071
1051
  version: MANIFEST_VERSION,
1072
1052
  files: remainingFiles,
1053
+ ...(remainingArchify ? { archify: remainingArchify } : {}),
1073
1054
  profiles: legacyProfiles,
1074
1055
  claudeSettings: { path: remainingClaudeSettingsPath },
1075
1056
  instructions: remainingInstructions,
1076
- archify: remainingArchify,
1077
1057
  });
1078
1058
  }
1079
1059
  if (archifyRelease) await archifyRelease.finish();
@@ -1,28 +0,0 @@
1
- ---
2
- name: axstack-diagram
3
- description: When an explanation needs a diagram, use axstack-diagram for inline SVG or CSS in pages, Mermaid in chat, GitHub, and docs, or archify only on an explicit viewer request.
4
- ---
5
-
6
- # Diagram
7
-
8
- Usage: `/axstack-diagram <question + destination>` or load this skill from explain.
9
- Load [Standing contracts](../axstack/references/contracts.md) before acting.
10
- Follow [Fidelity](references/fidelity.md) for every format.
11
-
12
- Use archify HTML only on an explicit viewer request.
13
- The configured `axstack-explainer` authors the viewer; follow [Archify](references/archify.md).
14
- Use inline SVG or CSS inside inline pages.
15
- Never use Mermaid in inline pages.
16
- Use Mermaid for chat, GitHub, and docs.
17
- Include `accTitle` and `accDescr` in each Mermaid view.
18
- Never use theme init in Mermaid.
19
- Split large Mermaid diagrams into views.
20
- T3 chat renders the Mermaid fence once the reply settles.
21
-
22
- Explain owns evidence, claim labels, the word cap, dispatch, and delivery.
23
- Diagram owns format and visual rules.
24
- Never call explain.
25
- For direct use, keep fidelity and rendered QA.
26
- For direct viewer requests, follow [Explain's declared launch path](../axstack-explain/SKILL.md#2-choose-proportional-output).
27
-
28
- Ideas: [archify](https://github.com/tt-a1i/archify), [diagram-design](https://github.com/cathrynlavery/diagram-design), [visual-explainer](https://github.com/nicobailon/visual-explainer), [Matt Pocock](https://github.com/mattpocock/skills), and [poteto](https://github.com/poteto/how); guidance uses Axstack's own words.
@@ -1,76 +0,0 @@
1
- # Archify
2
-
3
- Archify turns a typed JSON intermediate representation (IR) into an HTML viewer.
4
- Follow [Fidelity](fidelity.md) before authoring.
5
-
6
- ## Bind the tool
7
-
8
- Read `archify.json` beside the skill entrypoint for its `{path, sha}` record.
9
- Verify that `git -C <path> rev-parse HEAD` equals the recorded SHA.
10
- If the record or copy is missing, HEAD mismatches the recorded SHA, or Chrome is missing, hold and ask the user.
11
- Honor `ARCHIFY_CHROME` when set.
12
- Otherwise find Chrome or Chromium on `PATH`.
13
- Set `ARCHIFY_UPDATE_CHECK_DISABLED=1` on every archify call.
14
- Never substitute Mermaid or unverified HTML when the viewer is required.
15
-
16
- ## Author the IR
17
-
18
- If the pinned archify procedure conflicts with this file, this file takes precedence.
19
- Read `<path>/archify/SKILL.md` and follow its named references for IR authoring and repair.
20
- Run the referenced archify commands with Bun.
21
-
22
- Set `meta.title` and a portable relative HTML filename in `meta.output`.
23
- Use `meta.quality_profile: "showcase"`.
24
- Set `meta.animation: "none"`.
25
- Keep trace motion, exports, share cards, and brand marks off unless the user asks.
26
- Deliver the viewer with `?theme=dark` unless the user names a theme.
27
- Theme is viewer state rather than an IR field; without the query, localStorage and `prefers-color-scheme` determine it.
28
- Preserve explain's claim labels in `meta.subtitle` or the companion explanation.
29
- Set `meta.repository` to the inspected repository URL and full `revision`.
30
- Keep per-node source pins in each node's `sources` entries for inspected paths and line ranges.
31
- Keep the private source map for relationships that have no source field.
32
- Keep proposed sources distinct from factual pins.
33
- Use [Fidelity](fidelity.md) for visible planned and unknown markers.
34
- For a separate check, run `bun <path>/archify/bin/archify.mjs validate <type> <ir.json> --json --repo-root <repo>`.
35
- When sources are declared, require `--repo-root <repo>` on both `validate` and `finalize`.
36
- For sequence and dataflow, a schema-valid legend cue is `meta.legend.entries.dashed: {"label":"Planned or unknown"}`.
37
-
38
- Use one top-level `cards[]` entry with `{dot, title, items[]}` per key node as its node detail card.
39
- Each node detail card has at most 40 words and cites its source.
40
- Count the title plus all items toward each card's 40-word cap.
41
- Cite each card's source inline as `file:lines` in its `items[]`.
42
- Node detail cards are exempt from explain's 700-word cap.
43
- Count node labels, headings, captions, and all non-card text.
44
-
45
- ## Build and hold
46
-
47
- Write the IR, HTML, and receipt only inside the private evidence folder.
48
- For direct invocation, create a private run evidence folder first.
49
- Override archify's default `.archify/` folder with an evidence output path and `--out-dir <evidence dir>`.
50
- Run `bun <path>/archify/bin/archify.mjs finalize <type> <ir.json> <out.html> --quality showcase --json --out-dir <evidence dir> --repo-root <repo>`.
51
- This is the author's headless build gate.
52
- Read the saved receipt named by the JSON result's `evidence.receipt`.
53
- Accept only receipt `status: "pass"` as success.
54
- Never trust exit codes as proof of a pass.
55
- When a receipt is missing or remains non-pass after two repair rounds, hold delivery and ask the user.
56
- If Chrome is missing, report "viewer not verified: Chrome missing".
57
- Chrome absence yields `status: "skipped"` and exit 2.
58
- Follow archify's receipt fixes and references for layout and composition repairs.
59
- After every repair edit, rerun the complete `finalize` command.
60
- Count one repair round as one complete `finalize` rerun after an edit.
61
- Validate runs never count as repair rounds.
62
- Do not require validate before finalize.
63
- Run at most two repair rounds, then hold with remaining defects for the user's decision.
64
- Never use archify's extra evidence-based retry.
65
-
66
- ## Rendered and source checks
67
-
68
- Follow [UI verification](../../axstack/references/ui-verification.md) for dispatch.
69
- Keep `finalize` as the only author browser check.
70
- Delegate `visual-check`, browser opening, preview, and first-screen inspection to `axstack-ui-verifier`.
71
- Require `axstack-ui-verifier` checks of the receipt's `artifact.sha256` bytes on desktop, 390px mobile, keyboard, reduced motion, and the text alternative.
72
- Include theme, search or focus, and directional reach interactions.
73
- Require `axstack-ui-verifier` checks with the theme set explicitly.
74
- Require `explainer-review` of every node and edge against source for archify output.
75
- Every byte change requires fresh checks.
76
- Keep these independent verdicts with the build receipt before delivery.
@@ -1,5 +0,0 @@
1
- // Pin changes require a reviewed chore PR and an upstream smoke run.
2
- export const ARCHIFY_PIN = Object.freeze({
3
- repo: 'https://github.com/tt-a1i/archify',
4
- sha: '594f6087358610bd16e64e5602976020871b6bff',
5
- });