sqzass.git

sqzass

~ A static site generator written in Rust
git clone https://github.com/debuman/sqzass.git
commit edd9d7488f51f1d7d95589c2f1b8356933fd7f22
parent 2b92dabed8ba5bf5501184232733e68458ef2ff9
Author: sqzer <110329244+sqzer-x@users.noreply.github.com>
Date: Sat, 1 Aug 2026 00:43:20 +0900

Publish a setup prompt for coding agents

An agent told to "add a static site generator" reaches for `cargo install
sqzass` and gets "crate does not exist" — sqzass is installed from a release
tarball or from git. That is the first of several things it cannot infer:
front matter is TOML fenced with `+++` rather than YAML, `@/` links are source
paths and not URLs, images take root-absolute paths instead, and broken
references are errors with no flag to downgrade them. It also cannot know that
runtime messages are Korean while the error identifiers are ASCII and stable,
which is the difference between parsing output and guessing at it.

`static/agent.md` is that briefing, served at /agent.md so an agent can be
pointed at one URL. It is the only copy — the docs link to it rather than
repeating it, so the two cannot drift.

On the landing it is a quiet button under the install card, not a fourth tab.
What it copies goes into a different tool than the commands above it, and
putting both in one tab strip would blur which is which. The button carries a
short standalone version of the prompt: enough to act on without a fetch, plus
the URL for the rest.

Two fixes fell out of building it. The copy handler bound to `.inst-copy`, so
reusing it meant inheriting a 2rem square icon-button box — the label spilled
out of it and off the left edge of the viewport. It now binds to
`[data-copy-text]`, which is what actually marks a copy button, and the handler
swaps a visible label when there is one instead of only the aria-label.

Verified both languages at 1100px in the settled state, and confirmed in the
rendered DOM that the marker and the new button coexist.

Diffstat:
Mdocs/content/_index.ko.md | 23+++++++++++++++++++++++
Mdocs/content/_index.md | 23+++++++++++++++++++++++
Mdocs/content/start/installation.ko.md | 17+++++++++++++++++
Mdocs/content/start/installation.md | 17+++++++++++++++++
Adocs/static/agent.md | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdocs/static/css/landing.css | 23+++++++++++++++++++++++
Mdocs/static/js/landing.js | 14+++++++++++---
Mdocs/templates/landing.html | 11+++++++++++
8 files changed, 205 insertion(+), 3 deletion(-)

diff --git a/docs/content/_index.ko.md b/docs/content/_index.ko.md
@@ -13,6 +13,29 @@ cta_start = "시작하기"
 cta_github = "GitHub에서 보기"
 install_pick = "설치 방법"
 install_note = "릴리스마다 체크섬이 붙은 타르볼도 함께 나갑니다 — x86_64·ARM64 리눅스, Apple Silicon macOS."
+install_prompt_label = "에이전트에게 줄 프롬프트 복사"
+install_prompt = """
+Set up sqzass in this project.
+
+Install it — it is not on crates.io, so `cargo install sqzass` fails:
+  curl -fsSL https://sqzass.sqzer.com/install.sh | sh
+  # from source: cargo install --git https://github.com/sqzer-x/sqzass
+
+Then scaffold and build:
+  sqzass init .
+  sqzass build -i .
+
+Things you cannot infer from the file tree: front matter is TOML fenced with
++++, not YAML; internal links are @/section/page.md, the source path rather
+than the URL, and images use root-absolute paths instead; broken references
+stop the build on purpose and there is no flag to downgrade them.
+
+Runtime messages are Korean but the error identifiers (SQZASS_E_CONFIG,
+SQZASS_E_CONTENT, ...) and doctor check names are ASCII and stable — match on
+those. --json works on every command and prints one object to stdout.
+
+The long version is at https://sqzass.sqzer.com/agent.md
+"""
 
 bento_label = "왜 sqzass인가"
 
diff --git a/docs/content/_index.md b/docs/content/_index.md
@@ -13,6 +13,29 @@ cta_start = "Get started"
 cta_github = "View on GitHub"
 install_pick = "Install with"
 install_note = "Every release also ships tarballs with checksums — Linux on x86_64 and ARM64, and Apple-silicon macOS."
+install_prompt_label = "Copy prompt for your agent"
+install_prompt = """
+Set up sqzass in this project.
+
+Install it — it is not on crates.io, so `cargo install sqzass` fails:
+  curl -fsSL https://sqzass.sqzer.com/install.sh | sh
+  # from source: cargo install --git https://github.com/sqzer-x/sqzass
+
+Then scaffold and build:
+  sqzass init .
+  sqzass build -i .
+
+Things you cannot infer from the file tree: front matter is TOML fenced with
++++, not YAML; internal links are @/section/page.md, the source path rather
+than the URL, and images use root-absolute paths instead; broken references
+stop the build on purpose and there is no flag to downgrade them.
+
+Runtime messages are Korean but the error identifiers (SQZASS_E_CONFIG,
+SQZASS_E_CONTENT, ...) and doctor check names are ASCII and stable — match on
+those. --json works on every command and prints one object to stdout.
+
+The long version is at https://sqzass.sqzer.com/agent.md
+"""
 
 bento_label = "Why sqzass"
 
diff --git a/docs/content/start/installation.ko.md b/docs/content/start/installation.ko.md
@@ -41,6 +41,23 @@ yay -S sqzass          # paru도, 클론해서 makepkg -si도 됩니다
 `sqzass-bin`은 만들지 않았습니다. Arch에서 Rust 도구를 소스에서 빌드하는 건
 정상이고, 패키지가 둘이면 어느 쪽이 최신인지 묻는 일만 생깁니다.
 
+## 코딩 에이전트로
+
+이걸 에이전트에 붙여 넣으면 추측 없이 끝냅니다.
+
+```
+Set up sqzass in this project.
+Read https://sqzass.sqzer.com/agent.md first — it covers the install (sqzass is
+not on crates.io), the TOML front matter, how @/ links resolve, and the error
+identifiers to match on.
+```
+
+[`/agent.md`](/agent.md)이 긴 쪽입니다. 같은 설치 명령과, 파일 트리만 봐서는
+알 수 없는 규칙들과, exit code 표가 들어 있습니다. 이 파일이 있는 이유는
+"정적 사이트 생성기를 붙여 줘"라는 말을 들은 에이전트가 그냥 두면
+`cargo install sqzass`부터 시도하기 때문입니다 — 그건 실패합니다. sqzass는
+git이나 릴리스 타르볼에서 설치합니다.
+
 ## 소스에서 빌드
 
 ```bash
diff --git a/docs/content/start/installation.md b/docs/content/start/installation.md
@@ -41,6 +41,23 @@ yay -S sqzass          # or paru, or makepkg -si on a clone
 There is no `sqzass-bin`. Building a Rust tool from source is normal on Arch,
 and two packages would only raise the question of which one is current.
 
+## With a coding agent
+
+Paste this at an agent and it has enough to finish without guessing:
+
+```
+Set up sqzass in this project.
+Read https://sqzass.sqzer.com/agent.md first — it covers the install (sqzass is
+not on crates.io), the TOML front matter, how @/ links resolve, and the error
+identifiers to match on.
+```
+
+[`/agent.md`](/agent.md) is the long version: the same install commands, the
+rules that are not guessable from a file tree, and the exit-code table. It exists
+because an agent asked to "add a static site generator" will otherwise reach for
+`cargo install sqzass`, which fails — sqzass is installed from git or a release
+tarball.
+
 ## From source
 
 ```bash
diff --git a/docs/static/agent.md b/docs/static/agent.md
@@ -0,0 +1,80 @@
+# Set up sqzass in this project
+
+sqzass is a static site generator: markdown in, a directory of HTML out. One
+binary, no runtime — no Node, no Python, no system libraries.
+
+## 1. Install
+
+It is **not on crates.io**. `cargo install sqzass` will fail.
+
+```bash
+curl -fsSL https://sqzass.sqzer.com/install.sh | sh
+# with Rust: cargo install --git https://github.com/sqzer-x/sqzass
+# on Arch:   yay -S sqzass
+```
+
+## 2. Scaffold and build
+
+`init` writes three files and refuses to run where a `sqzass.toml` already
+exists, so it cannot half-overwrite a site that is already there.
+
+```bash
+sqzass init .        # sqzass.toml, content/_index.md, templates/page.html
+sqzass build -i .    # writes public/
+sqzass serve -i .    # live reload on http://127.0.0.1:3000
+```
+
+Two config keys are required: `title` and `base_url`. Everything else has a
+default.
+
+## 3. Rules you cannot infer from the file tree
+
+- Front matter is **TOML fenced with `+++`**, not YAML with `---`. `title` is
+  the only required field.
+- Internal links are `@/section/page.md` — the *source path* under `content/`,
+  not the URL. The build rewrites it and resolves it to the reader's language.
+- Images are different: use a root-absolute path, `![](/images/x.png)`. `@/`
+  resolves against pages only, so `@/images/x.png` stops the build.
+- Translations sit beside each other: `page.md` and `page.ko.md`.
+- `page.content` is already HTML in templates, so it needs `| safe`. Templates
+  autoescape by default and reading an undefined value is an error.
+
+## 4. Errors are deliberate
+
+An unresolved link, a template that does not exist, two pages claiming one URL,
+a misspelled key in `sqzass.toml` or in front matter — each one stops the build
+and names the file and line. There is no flag to downgrade any of them to a
+warning. Fix the cause.
+
+## 5. Reading the output as a program
+
+Runtime messages are in Korean. The identifiers are not: error codes and
+`doctor` check names are ASCII and stable, so match on those rather than on
+message text.
+
+| Exit | Identifier | |
+|---|---|---|
+| `0` | | Success. |
+| `1` | `SQZASS_E` | Unclassified failure. |
+| `2` | | Bad command line (clap's code, not ours). |
+| `3` | `SQZASS_E_CONFIG` | `sqzass.toml`. |
+| `4` | `SQZASS_E_CONTENT` | Anything under `content/`. |
+| `5` | `SQZASS_E_TEMPLATE` | `templates/`, `i18n/`, or a missing asset. |
+| `6` | `SQZASS_E_IO` | Reading or writing failed. |
+| `7` | | `doctor` found something at or above `--fail-on`. |
+
+`--json` works on every command and prints one object to stdout — on failure
+too, so a script reads one pipe.
+
+```console
+$ sqzass build -i . --json
+{"ok":true,"output":"public","pages":1}
+
+$ sqzass doctor -i . --json
+{"findings":[{"check":"base-url","file":"sqzass.toml","message":"…","severity":"warn"}],"gated":1,"ok":false}
+```
+
+## Full documentation
+
+<https://sqzass.sqzer.com> — and <https://sqzass.sqzer.com/llms.txt> is a flat
+list of every page with its description.
diff --git a/docs/static/css/landing.css b/docs/static/css/landing.css
@@ -425,6 +425,29 @@ main { display: block; }
 .install:has(#inst-3:checked) .pick-panel:nth-child(3) { visibility: visible; opacity: 1; }
 
 .install-note { margin: 0.7rem 0 0; font-size: 0.82rem; color: var(--muted); }
+/* 설치 카드보다 조용해야 한다. 대부분의 방문자는 셸에 붙여 넣지 에이전트에
+   붙여 넣지 않으므로, 있는 걸 알아볼 수는 있되 먼저 눈에 들어오면 안 된다. */
+.install-agent { margin: 0.55rem 0 0; }
+.agent-copy {
+  display: inline-flex;
+  align-items: center;
+  gap: 0.34rem;
+  white-space: nowrap;
+  padding: 0.34rem 0.66rem;
+  border: 1px solid var(--line);
+  border-radius: 999px;
+  background: transparent;
+  color: var(--muted);
+  font: inherit;
+  font-size: 0.78rem;
+  line-height: 1.2;
+  cursor: pointer;
+  transition: color 0.15s, border-color 0.15s;
+}
+.agent-copy:hover { color: var(--ink); border-color: var(--ink-3); }
+.agent-copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
+.agent-copy.is-done { color: var(--accent-ink); border-color: rgba(255, 140, 90, 0.55); }
+.agent-copy svg { flex: none; }
 
 .pick-panel > code {
   font-family: var(--mono);
diff --git a/docs/static/js/landing.js b/docs/static/js/landing.js
@@ -16,16 +16,24 @@
   }
 
   /* 설치 한 줄 복사 */
-  document.querySelectorAll(".inst-copy").forEach(function (btn) {
+  /* 복사 버튼은 클래스가 아니라 data-copy-text로 찾는다. 클래스로 찾으면 새
+     버튼마다 아이콘 전용 스타일(.inst-copy는 2rem 정사각형)을 같이 받게 된다. */
+  document.querySelectorAll("[data-copy-text]").forEach(function (btn) {
     var label = btn.getAttribute("aria-label");
+    /* 글자를 달고 있는 버튼은 그 글자가 바뀌어야 눌린 걸 안다. 아이콘만 있는
+       버튼은 aria-label만 바꾼다 — 볼 게 없으니 들릴 것이라도 바뀌어야 한다. */
+    var text = btn.querySelector(".copy-label");
+    var was = text ? text.textContent : null;
     btn.addEventListener("click", function () {
       if (!navigator.clipboard) return;
       navigator.clipboard.writeText(btn.getAttribute("data-copy-text") || "").then(function () {
         btn.classList.add("is-done");
-        btn.setAttribute("aria-label", btn.getAttribute("data-copied"));
+        if (label) btn.setAttribute("aria-label", btn.getAttribute("data-copied"));
+        if (text) text.textContent = btn.getAttribute("data-copied");
         setTimeout(function () {
           btn.classList.remove("is-done");
-          btn.setAttribute("aria-label", label);
+          if (label) btn.setAttribute("aria-label", label);
+          if (text) text.textContent = was;
         }, 1600);
       });
     });
diff --git a/docs/templates/landing.html b/docs/templates/landing.html
@@ -111,7 +111,18 @@
         {%- endfor %}
         </div>
       </fieldset>
+      {#- 에이전트용 프롬프트. 설치 명령과 나란히 놓지 않고 카드 밖에 따로 둔다 —
+          붙여 넣을 곳이 셸이 아니라 다른 도구라, 같은 탭에 섞으면 어느 쪽이
+          어디로 가는 것인지 흐려진다. #}
       <p class="install-note" data-reveal>{{ page.extra.install_note }}</p>
+      <p class="install-agent" data-reveal>
+        <button class="agent-copy" type="button"
+                data-copy-text="{{ page.extra.install_prompt }}"
+                data-copied="{{ t("copied") }}">
+          <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg>
+          <span class="copy-label">{{ page.extra.install_prompt_label }}</span>
+        </button>
+      </p>
       <div class="lede" data-reveal>
         {{ page.content | safe }}
       </div>