diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index be95476..3b365ca 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -134,9 +134,22 @@ jobs:
# Kept at the same indentation as the block above: inside `run: |`, YAML strips
# only the common indentation, so a further-indented heredoc body would arrive
# in the notes with leading spaces and render as a code block.
- FLAGS=""
+ # Both directions are stated outright rather than one of them relying on a
+ # default. `gh release create` defaults to "not a pre-release", so an empty
+ # FLAGS was correct on the create path — but the same variable is reused on the
+ # path below where the release already exists, and there an empty FLAGS meant a
+ # release someone had hand-drafted as a pre-release stayed one permanently.
+ # 1.0.0 is precisely the release where that would be wrong, and the sort of
+ # wrong nobody thinks to check for.
+ if [ "$PRERELEASE" = "true" ]; then
+ CREATE_FLAGS="--prerelease"
+ EDIT_FLAGS="--prerelease=true"
+ else
+ CREATE_FLAGS="--latest"
+ EDIT_FLAGS="--prerelease=false --latest"
+ fi
+
if [ "$PRERELEASE" = "true" ]; then
- FLAGS="--prerelease"
cat >> notes.md <<'EOF'
---
@@ -161,12 +174,12 @@ jobs:
if gh release view "${GITHUB_REF_NAME}" >/dev/null 2>&1; then
echo "::notice::Release ${GITHUB_REF_NAME} already exists — attaching the .vsix to it."
gh release upload "${GITHUB_REF_NAME}" "$VSIX" --clobber
- if [ -n "$FLAGS" ]; then gh release edit "${GITHUB_REF_NAME}" $FLAGS; fi
+ gh release edit "${GITHUB_REF_NAME}" $EDIT_FLAGS
echo "::notice::Existing release notes were left unchanged. SHA-256 ${SHA256}"
else
gh release create "${GITHUB_REF_NAME}" "$VSIX" \
--title "HirayaCoder ${GITHUB_REF_NAME}" \
--notes-file notes.md \
--generate-notes \
- $FLAGS
+ $CREATE_FLAGS
fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1c17d5b..99682f9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,92 @@ All notable changes to HirayaCoder are documented here.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [0.9.0] — unreleased
+## [1.0.0] — 2026-08-20
+
+The version number is the news, and it is a claim about stability rather than about
+features: 1.0.0 adds no tool, no permission, no network call, and no dependency. What it
+adds is the things a release has to have before strangers install it — a guide inside the
+panel, a test that every control in that panel is wired to something, and a security pass
+that measured what previous passes had only read.
+
+### Added — a setup guide in the panel
+
+A **Guide** button in the chat header opens a card covering the four setup steps and,
+more usefully, what to expect once they are done: that a task takes one to five minutes
+on a laptop, that a refused write is usually the checks working, and that a small model
+is good at one file at a time and bad at a whole application.
+
+The panel previously assumed its reader had read the README. For the release where
+strangers arrive that is the wrong assumption: the most likely reader is someone whose
+first run went nowhere because Ollama is not running, or who is four minutes into a task
+and does not know whether that is normal. The card renders locally rather than asking the
+host for anything — unlike every other control here, it has nothing to ask for — and sits
+in the transcript rather than over it, so it can be read beside the run that prompted it.
+
+### Fixed — 85 seconds of frozen editor, from a paste
+
+`stepBrief.PATH_TOKEN` scans an item's text for filenames, and scanning for a token that
+is not there costs one attempt per start position, so an unbroken run of word characters
+was O(n²). On a single run of `a` — which is what a pasted data URI, minified line or
+hash looks like to that expression — 3,200 characters took 23 ms, 51,200 took 6.1 s, and
+204,800 took **85.2 seconds**, with the extension host frozen throughout.
+
+`core/commonSense` holds the same expression and has bounded it since it was measured
+there. `stepBrief` had inherited the comment explaining that a single match is linear —
+which is true, and answered a different question than the one that mattered. Bounding
+each segment to 120 characters returns the sweep to linear: 244 ms at 204,800 characters.
+Output is unchanged on every path in this repository, and the timing is now pinned by a
+test. Tracked as SAST-014.
+
+### Fixed — a 1.0.0 could have been published as a pre-release
+
+The release workflow already derived release-vs-pre-release from the version, so 1.0.0
+was always going to come out unflagged on the normal path. The hole was the other path:
+where a release for the tag already exists, the flag was only ever *added*, never
+cleared, so a hand-drafted pre-release would have stayed one. Both directions are now
+stated explicitly, the edit is unconditional, and full releases are marked `--latest` —
+which is what `/releases/latest` resolves to, and therefore what install instructions
+point at. Pinned by a test, since this is checkable exactly once per tag.
+
+### Added — a test that every button does something
+
+`test/unit/webviewWiring.test.js` reads `index.html`, `main.js` and `chatTab.js` as text
+and checks the seams: every interactive element is resolved, every button has a way to be
+activated (its own listener, a delegated container, or the form it submits), and the
+message protocol closes in both directions — 13 webview-to-host types and 16 coming back.
+
+A dead button was the one UI defect nothing here could see. The component tests build
+nodes and assert their shape; the integration tests drive the host. Neither notices a
+control nobody listened to, and neither looks wrong on screen until someone clicks.
+
+### Security
+
+Full report in `security/sast-report-2026-08-20-1.0.0.md`. Zero production dependencies,
+zero advisories, no Critical or High findings.
+
+The pass measured all 22 expressions ESLint flags as `detect-unsafe-regex` in `app/`,
+timing each against an input built to be its worst case rather than reasoning about the
+shape. Twenty of them are linear; one was already found and bounded; one was SAST-014
+above. ESLint still reports the same 22 warnings after the fix, which is the clearest
+case this codebase has for why they are reviewed rather than counted.
+
+### Changed — the marketing set
+
+Version badges to v1.0.0 across the six README images, plus four new 1:1 launch ads with
+captions in `docs/images/ADS-1.0.0.md`. "Pre-release" is gone from the images: it
+described distribution rather than stability, and a 1.0.0 calling itself a pre-release
+reads as a mistake. The install route deliberately still points at GitHub Releases —
+the tag is cut before the Marketplace listing exists, and `doc/PUBLISHING.md` Step 9b
+says what to swap on the day it is live.
+
+### Documentation
+
+`doc/PUBLISHING.md` is split into a one-time first publish and the repeating update, with
+a full account of what a version bump touches beyond `package.json` — ten image sources,
+the README's hero alt text, the changelog, and a new SAST report, none of which fail
+loudly when they go stale.
+
+## [0.9.0] — 2026-08-20
0.8.0 gave the agent a record of what it had already done. Running the same React +
Vite + Tailwind brief again, graded this time **in a browser**, showed that the record
diff --git a/README.md b/README.md
index 6649a92..be1d893 100644
--- a/README.md
+++ b/README.md
@@ -5,16 +5,17 @@
-
+
*A local Filipino-inspired AI coder that brings imagination and speed to your VS Code workflow.*
-> **Pre-release.** HirayaCoder is not on the VS Code Marketplace yet. Releases are
-> published as a `.vsix` on the
+> **1.0.0 is here.** Everything described below is implemented, tested on Windows, macOS
+> and Linux in CI, and stable enough to put a 1.0 on. The one thing still missing is the
+> one-click install: the Marketplace listing is not up yet, so releases are published as
+> a `.vsix` on the
> [Releases page](https://github.com/jaymar921/HirayaCoder/releases) and installed by
-> hand — [Step 4](#step-4--install-hirayacoder) has the one command it takes. Everything
-> described below works today; what is missing is the one-click install.
+> hand — [Step 4](#step-4--install-hirayacoder) has the one command it takes.
**HirayaCoder is a free AI coding assistant that runs entirely on your own computer.**
You type what you want in plain English, and it writes and edits the files for you — no
@@ -132,6 +133,11 @@ Good first things to type:
- `create a simple to-do list app in one HTML file`
- `explain what this project does` *(in a folder that already has code)*
+**If anything above is unclear once you are in there, press *Guide* in the chat header.**
+It opens the same four setup steps and — more usefully — what to expect: how long a task
+takes, why a refusal is usually the checks working, and what a small model is and is not
+good at.
+
There is a longer, friendlier walkthrough in
[TUTORIAL.md](https://github.com/jaymar921/HirayaCoder/blob/main/doc/TUTORIAL.md).
diff --git a/app/agent/stepBrief.js b/app/agent/stepBrief.js
index 8b8b6de..a93030c 100644
--- a/app/agent/stepBrief.js
+++ b/app/agent/stepBrief.js
@@ -74,10 +74,24 @@ const MAX_CONSTRAINT_CHARS = 500;
* A path, or a path-like token, inside an item's text.
*
* The repeated group is separated by a mandatory `/`, which the character class
- * excludes, so no two iterations can claim the same characters and the match is linear
- * despite the nested quantifier the linter flags.
+ * excludes, so no two iterations can claim the same characters and *one* match is
+ * linear despite the nested quantifier the linter flags.
+ *
+ * The cost that is not linear is scanning with `/g` for matches that are not there.
+ * Every start position inside an unbroken run of word characters gets its own scan,
+ * which makes the sweep O(n²) in the length of that run — the same shape
+ * `core/commonSense` documents and bounds, in the module that copied its comment and
+ * not its bound. Measured on a single run of `a`, which is what a pasted data URI,
+ * minified line or hash looks like to this expression: 23 ms at 3,200 characters,
+ * 6.1 s at 51,200, and **85 s at 204,800**. That is a hard freeze of the extension
+ * host, reached by a paste rather than by an attack.
+ *
+ * Bounding the segment is what fixes it: work per start position is capped, so the
+ * sweep is linear again — 244 ms at 204,800 characters. 120 is far past any real path
+ * segment, and the filter below still requires an extension or a `/`, so nothing this
+ * repo has ever matched is affected. Pinned by a test rather than left to the comment.
*/
-const PATH_TOKEN = /[\w@.-]+(?:\/[\w@.-]+)+|[\w@-]+\.[a-z0-9]{1,6}\b/gi;
+const PATH_TOKEN = /[\w@.-]{1,120}(?:\/[\w@.-]{1,120})+|[\w@-]{1,120}\.[a-z0-9]{1,6}\b/gi;
/**
* The files an item names outright.
diff --git a/app/webview/components/guideCard.js b/app/webview/components/guideCard.js
new file mode 100644
index 0000000..eaf6737
--- /dev/null
+++ b/app/webview/components/guideCard.js
@@ -0,0 +1,181 @@
+/**
+ * The setup guide — what to install, and what the thing actually does once installed.
+ *
+ * ## Why this is static text in the webview
+ *
+ * Every other control here posts to the host, because the host is the only side that
+ * can do anything. This one has nothing to ask for: the guide is the same sentences on
+ * every machine, in every workspace, whether or not Ollama is running. Routing it
+ * through a message would buy a protocol and a round-trip for a string constant.
+ *
+ * ## Why it is a card in the transcript rather than a modal
+ *
+ * The most likely reader is someone whose first run did not go the way they expected —
+ * a model that has not been pulled, a task that is taking four minutes, a write that
+ * was refused. They need the guide *beside* the thing that confused them, and they need
+ * to keep scrolling back to it. A modal takes the transcript away to show it.
+ *
+ * The content is deliberately blunt about the trade. A first-time user who is told to
+ * expect ChatGPT and gets a 1B model will conclude the extension is broken; one who is
+ * told a task takes one to five minutes on a laptop will wait for it.
+ *
+ * @module webview/components/guideCard
+ */
+
+/**
+ * @typedef {object} GuideStep
+ * @property {string} title
+ * @property {string} detail
+ * @property {string} [command] A line to paste into a terminal, if the step has one.
+ */
+
+/** @type {GuideStep[]} */
+const SETUP = [
+ {
+ title: 'Install Ollama and leave it running',
+ detail:
+ 'Ollama is the free program that runs the AI on your own machine. Download it from ollama.com. It has no window — it sits in your system tray or menu bar, and that is normal.',
+ },
+ {
+ title: 'Download one model',
+ detail:
+ 'Paste this into a terminal. It downloads a few gigabytes once, then never again. On 8 GB of RAM use llama3.2:1b instead.',
+ command: 'ollama pull gemma4:e2b',
+ },
+ {
+ title: 'Open a folder',
+ detail:
+ 'File → Open Folder. This is required, not a suggestion: HirayaCoder confines every file operation to the folder you opened, so with no folder open there is nowhere it is allowed to work.',
+ },
+ {
+ title: 'Pick your model above and start typing',
+ detail:
+ 'The dropdown in this header lists what Ollama has installed. Ask for one thing at a time — "add a delete button to index.html" goes much better than "build me a social network".',
+ },
+];
+
+/** @type {Array<{title: string, detail: string}>} */
+const EXPECT = [
+ {
+ title: 'It is slower than you are used to',
+ detail:
+ 'A task takes 1–5 minutes on a laptop with no graphics card, 20–60 seconds with one. The step panel shows you each action as it happens so you can tell "thinking" from "stuck" — and stop it when it is the second one.',
+ },
+ {
+ title: 'Nothing is saved until you approve it',
+ detail:
+ 'Every write shows you a diff first. Turn on Auto Edit from the Permissions button once you trust it; deleting a file asks even then.',
+ },
+ {
+ title: 'A refusal is usually the checks working',
+ detail:
+ 'Writes that would truncate a file, drop an export, or leave a stub inside a function are blocked before they reach the disk. Ask again — it usually gets it right the second time.',
+ },
+ {
+ title: 'Small models are capable, not clever',
+ detail:
+ 'One file, one feature, one fix at a time is where a local model is genuinely good. Handed a whole application it will write plausible files that do not run together. That is a real limit, not a setting you have missed.',
+ },
+ {
+ title: 'Three modes, and Agent is the right default',
+ detail:
+ 'Agent reads and writes. Plan looks without touching anything and hands back a checklist you can edit and then run. Ask answers a question with no tools at all. You do not need to switch to Ask to ask something — Agent notices a question and just answers it.',
+ },
+];
+
+/**
+ * One titled paragraph, optionally with a copyable-looking command under it.
+ *
+ * @param {string} tag The element for the title — `li` items carry their own marker.
+ * @param {{title: string, detail: string, command?: string}} entry
+ * @returns {HTMLElement}
+ */
+function renderEntry(tag, entry) {
+ const item = document.createElement(tag);
+ item.className = 'guide-item';
+
+ const title = document.createElement('span');
+ title.className = 'guide-item-title';
+ title.textContent = entry.title;
+ item.appendChild(title);
+
+ const detail = document.createElement('span');
+ detail.className = 'guide-item-detail';
+ detail.textContent = entry.detail;
+ item.appendChild(detail);
+
+ if (entry.command) {
+ const command = document.createElement('code');
+ command.className = 'guide-command';
+ command.textContent = entry.command;
+ item.appendChild(command);
+ }
+
+ return item;
+}
+
+/**
+ * @param {string} heading
+ * @param {string} listTag `ol` for the ordered setup steps, `ul` for the rest.
+ * @param {Array<{title: string, detail: string, command?: string}>} entries
+ * @returns {DocumentFragment}
+ */
+function renderSection(heading, listTag, entries) {
+ const fragment = document.createDocumentFragment();
+
+ const title = document.createElement('h3');
+ title.className = 'guide-heading';
+ title.textContent = heading;
+ fragment.appendChild(title);
+
+ const list = document.createElement(listTag);
+ list.className = 'guide-list';
+ for (const entry of entries) list.appendChild(renderEntry('li', entry));
+ fragment.appendChild(list);
+
+ return fragment;
+}
+
+/**
+ * Build the guide card.
+ *
+ * @param {() => void} onDismiss Called when the reader closes it.
+ * @returns {HTMLElement}
+ */
+export function renderGuide(onDismiss) {
+ const wrapper = document.createElement('section');
+ wrapper.className = 'guide';
+ wrapper.setAttribute('aria-label', 'Setup guide');
+
+ const bar = document.createElement('div');
+ bar.className = 'guide-bar';
+
+ const title = document.createElement('h2');
+ title.className = 'guide-title';
+ title.textContent = 'Setting up, and what to expect';
+ bar.appendChild(title);
+
+ const close = document.createElement('button');
+ close.className = 'chip-remove';
+ close.type = 'button';
+ close.textContent = '×';
+ close.setAttribute('aria-label', 'Close the guide');
+ close.addEventListener('click', () => onDismiss());
+ bar.appendChild(close);
+
+ wrapper.appendChild(bar);
+
+ const blurb = document.createElement('p');
+ blurb.className = 'guide-blurb';
+ blurb.textContent =
+ 'Everything runs on your machine. No account, no internet after setup, and nothing you type or open leaves this computer.';
+ wrapper.appendChild(blurb);
+
+ wrapper.appendChild(renderSection('Setup — four steps', 'ol', SETUP));
+ wrapper.appendChild(renderSection('What to expect', 'ul', EXPECT));
+
+ return wrapper;
+}
+
+/** Exported for the tests, which assert the guide covers each of these. */
+export const sections = { SETUP, EXPECT };
diff --git a/app/webview/index.html b/app/webview/index.html
index 678d645..d507a23 100644
--- a/app/webview/index.html
+++ b/app/webview/index.html
@@ -68,6 +68,21 @@
+
+
+
diff --git a/app/webview/main.js b/app/webview/main.js
index 0881b7a..9bcaeae 100644
--- a/app/webview/main.js
+++ b/app/webview/main.js
@@ -11,6 +11,7 @@ import { createMessage, appendImages, TraceView, renderTodos, renderChanges } fr
import { ThinkingIndicator } from './components/thinkingIndicator.js';
import { renderPlanChecklist } from './components/planChecklist.js';
import { renderClarification } from './components/clarificationCard.js';
+import { renderGuide } from './components/guideCard.js';
import { render } from './components/markdown.js';
const vscode = acquireVsCodeApi();
@@ -30,6 +31,7 @@ const el = {
stepSessions: document.getElementById('step-sessions'),
addFile: document.getElementById('add-file'),
addImage: document.getElementById('add-image'),
+ guide: document.getElementById('guide'),
status: document.getElementById('status'),
sessionBadge: document.getElementById('session-badge'),
};
@@ -82,6 +84,37 @@ function clearWelcome() {
if (welcome) welcome.remove();
}
+/* ------------------------------------------------------------------- guide */
+
+/*
+ The guide sits at the end of the transcript and scrolls with it, so it can be read
+ alongside whatever prompted the reader to open it. It is removed rather than hidden:
+ a stale copy halfway up a long conversation reads as part of the run.
+*/
+function toggleGuide() {
+ const open = document.getElementById('guide-card');
+ if (open) {
+ closeGuide();
+ return;
+ }
+
+ clearWelcome();
+ const card = renderGuide(closeGuide);
+ card.id = 'guide-card';
+ el.messages.appendChild(card);
+ el.guide.setAttribute('aria-pressed', 'true');
+ scrollToEnd();
+}
+
+function closeGuide() {
+ const card = document.getElementById('guide-card');
+ if (card) card.remove();
+ el.guide.setAttribute('aria-pressed', 'false');
+ // The welcome screen is the empty state, so it comes back only if closing the guide
+ // has actually left the transcript empty — not on top of a conversation.
+ if (el.messages.children.length === 0) showWelcome();
+}
+
/* ------------------------------------------------------------------- chips */
/** @type {Array<{kind: 'file' | 'image', name: string, path: string, dataUri?: string}>} */
@@ -431,6 +464,7 @@ el.stepSessions.addEventListener('click', () => {
vscode.postMessage({ type: 'step-sessions', enabled: state.stepSessions });
});
+el.guide.addEventListener('click', toggleGuide);
el.permissions.addEventListener('click', () => vscode.postMessage({ type: 'permissions' }));
el.addFile.addEventListener('click', () => vscode.postMessage({ type: 'attach-file' }));
el.addImage.addEventListener('click', () => vscode.postMessage({ type: 'attach-image' }));
diff --git a/app/webview/style.css b/app/webview/style.css
index 32de296..045de17 100644
--- a/app/webview/style.css
+++ b/app/webview/style.css
@@ -771,3 +771,86 @@ select.control {
font-size: var(--fs-sm);
color: var(--muted);
}
+
+/*
+ The setup guide.
+
+ Bordered and inset like `.clarify`, and for the same reason: it is a block of reading
+ rather than a thing to act on, and the sunrise is spoken for. The one visual weight it
+ does carry is on the step titles, because someone scanning for "which step am I on"
+ should not have to read the paragraphs to find out.
+*/
+.guide {
+ margin: var(--sp-3) 0;
+ padding: var(--sp-3) var(--sp-4);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ background: var(--surface);
+}
+
+.guide-bar {
+ display: flex;
+ align-items: flex-start;
+ gap: var(--sp-2);
+}
+
+.guide-title {
+ flex: 1;
+ margin: 0;
+ font-size: 1.05em;
+}
+
+.guide-blurb {
+ margin: var(--sp-2) 0 0;
+ font-size: var(--fs-sm);
+ color: var(--muted);
+}
+
+.guide-heading {
+ margin: var(--sp-4) 0 0;
+ font-size: var(--fs-sm);
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--muted);
+}
+
+.guide-list {
+ margin: var(--sp-2) 0 0;
+ padding-left: var(--sp-4);
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+}
+
+.guide-item {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.guide-item-title {
+ font-weight: 600;
+}
+
+.guide-item-detail {
+ font-size: var(--fs-sm);
+ color: var(--muted);
+}
+
+/*
+ A command is the one thing here the reader has to reproduce exactly, so it gets the
+ editor's monospace font and a box — and its own scrollbar, because the panel is often
+ docked narrow and a wrapped command line is a mistyped command line.
+*/
+.guide-command {
+ align-self: flex-start;
+ max-width: 100%;
+ margin-top: var(--sp-1);
+ padding: var(--sp-1) var(--sp-2);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ background: var(--vscode-textCodeBlock-background, rgba(128, 128, 128, 0.12));
+ font-size: var(--fs-sm);
+ white-space: pre;
+ overflow-x: auto;
+}
diff --git a/doc/PUBLISHING.md b/doc/PUBLISHING.md
index 3dc9560..61321c6 100644
--- a/doc/PUBLISHING.md
+++ b/doc/PUBLISHING.md
@@ -1,8 +1,40 @@
# Publishing HirayaCoder to the VS Code Marketplace
-*A clear, step-by-step guide for `jaymar921` to follow once HirayaCoder is fully built, tested, and ready to ship. No prior Marketplace-publishing experience assumed.*
+*A clear, step-by-step guide for `jaymar921`. No prior Marketplace-publishing experience assumed.*
-Follow these steps **in order**. Steps 1–3 are one-time setup you only do once per publisher account. Steps 4 onward repeat for every new version you release.
+## Which half of this do I need?
+
+There are two jobs in this document and they are not the same size.
+
+| | What it covers | Read |
+|---|---|---|
+| **Publishing 1.0.0 for the first time** | Creating the publisher, getting a token, and getting the listing to exist at all. Done once, ever. | Steps 1–3, then 4–11 |
+| **Shipping an update** | Bump, package, test, publish, tag. Twenty minutes once you have done it once. | [Part B](#part-b--shipping-an-update) — Steps 4–11 only |
+
+Steps 1–3 are **one-time setup per publisher account**. If `vsce login jaymar921`
+already works on this machine, skip straight to Part B.
+
+> **First time?** The order that matters most: **do not tag before the version is right.**
+> Step 5 bumps `package.json`, and the tag push in Step 10 is compared against it. A tag
+> that disagrees fails the release job rather than shipping something mislabelled — which
+> is the design working, but it is easier to get right the first time.
+
+---
+
+## What "publishing" actually means here
+
+Three things happen at a release and they are independent. Confusing them is the main
+way this goes wrong.
+
+| | Who does it | When | Reversible? |
+|---|---|---|---|
+| **GitHub Release** — the `.vsix` attached to a tag | **CI, automatically** on any `v*.*.*` tag | Step 10 | Yes, delete the release |
+| **Marketplace listing** — the one-click install | **You, by hand** (`vsce publish`) | Step 8 | **No** — see Step 11 |
+| **Version number** in `package.json` | You | Step 5 | Yes, until it is published |
+
+The GitHub Release is automated because it is safe to repeat. Marketplace publishing is
+deliberately manual and **no Marketplace token is stored in this repository**: pushing a
+tag should not be able to ship to every installed user.
---
@@ -13,8 +45,9 @@ Don't proceed to packaging until every box here is checked — publishing a brok
- [ ] All features in `/setup/PROMPT.md` that are in scope for this version are implemented and working.
- [ ] `npm run test:all` passes locally (lint + unit + integration), and **CI is green on
all three platforms** — the Actions run for the commit you are about to tag.
-- [ ] SAST suite has been run and a filled-out report exists in `/security/` (see
- `sast-report-2026-08-12.md`) with no unresolved Critical/High findings.
+- [ ] SAST suite has been run and a filled-out report exists in `/security/` — the most
+ recent is `sast-report-2026-08-20-1.0.0.md` — with no unresolved Critical/High
+ findings. Copy `sast-report-template.md` rather than editing the previous report.
- [ ] Smoke-tested manually on Windows, macOS, and Linux. CI covers the automated suites
on all three; this box is about a human using the packaged `.vsix` — see Step 7b
for the short list of things that actually differ per platform.
@@ -23,9 +56,25 @@ Don't proceed to packaging until every box here is checked — publishing a brok
- [ ] `CHANGELOG.md` has an entry for this version.
- [ ] `LICENSE` file exists and its content matches what `README.md` links to.
- [ ] `docs/assets/icon-128.png` exists and looks correct at both small and large sizes.
+- [ ] **The version badge in the marketing images matches this release.** They are
+ rendered from `docs/images/src/*.html` and every one carries the version — see that
+ folder's `README.md` for what to bump and, just as importantly, what not to.
+- [ ] **Nothing in the repo claims a Marketplace listing that does not exist yet.** The
+ hero image's CTA, `capabilities.html`'s footer, `ad-4-launch.html`'s footer, and
+ the README's banner all describe how to install. Through 0.7.0 one of them said
+ *Search "HirayaCoder" in the Extensions view*, months before that could work.
+- [ ] **The release will not be published as a pre-release.** CI derives this from the
+ version: `0.x` and any `-rc`/`-beta` suffix are flagged, everything else is not.
+ `test/unit/releaseWorkflow.test.js` asserts it for the current `package.json`, so a
+ green suite is the check — there is no checkbox to get wrong.
---
+# Part A — First Publish (one-time setup)
+
+*Steps 1–3. Done once per publisher account, ever. If `vsce login jaymar921` already
+works on this machine, skip to [Part B](#part-b--shipping-an-update).*
+
## Step 1 — Create a Publisher (one-time)
The Marketplace groups extensions under a **publisher ID**. You'll use `jaymar921`.
@@ -77,26 +126,50 @@ Paste the PAT when prompted. You only need to do this once per machine (or again
---
+# Part B — Shipping an Update
+
+*Everything from here repeats for every release. Steps 1–3 above are done.*
+
## Step 4 — Make Sure `package.json` Is Marketplace-Ready
-Before every release, confirm these fields are correct in `package.json`:
+Before every release, confirm these fields are correct in `package.json`. This is the
+current manifest, copied verbatim — if yours differs, yours is the one that ships:
```json
{
"name": "hirayacoder",
"displayName": "HirayaCoder",
- "description": "A local Filipino-inspired AI programmer that generates, refactors, and understands code directly inside VS Code — fully offline, powered by Ollama.",
+ "description": "A fully offline, privacy-first AI coding agent powered by your local Ollama instance. Agentic on every model — even 1B.",
"version": "1.0.0",
"publisher": "jaymar921",
- "author": "jaymar921",
- "license": "SEE LICENSE IN LICENSE",
- "engines": { "vscode": "^1.85.0" },
- "categories": ["Machine Learning", "Programming Languages", "Other"],
- "keywords": ["ai", "ollama", "offline", "agent", "coding assistant", "local llm"],
+ "author": {
+ "name": "jaymar921",
+ "url": "https://github.com/jaymar921"
+ },
+ "license": "MIT",
"icon": "docs/assets/icon-128.png",
+ "engines": {
+ "vscode": "^1.85.0",
+ "node": ">=18"
+ },
+ "categories": [
+ "AI",
+ "Programming Languages",
+ "Machine Learning",
+ "Other"
+ ],
+ "keywords": [
+ "ollama",
+ "offline",
+ "local llm",
+ "ai agent",
+ "privacy",
+ "copilot alternative",
+ "code assistant"
+ ],
"repository": {
"type": "git",
- "url": "https://github.com/jaymar921/HirayaCoder"
+ "url": "https://github.com/jaymar921/HirayaCoder.git"
}
}
```
@@ -119,7 +192,44 @@ Pick the right bump based on what changed since the last release:
npm version patch # or: minor / major
```
-This updates `package.json`'s `version` field and creates a git commit + tag automatically. Update `CHANGELOG.md` with a short entry for this version before or right after this step.
+This updates `package.json`'s `version` field and creates a git commit + tag automatically.
+
+> **Careful with `npm version` here.** It tags immediately, and the release workflow
+> compares that tag against `package.json`. That is fine when the bump is the last thing
+> you do — but if you still have documentation or images to update, use
+> `npm version --no-git-tag-version` and tag by hand in Step 10, once everything
+> that mentions the version agrees.
+
+### The rest of the bump — everything else that names the version
+
+`package.json` is one of several places the version appears, and the others do not fail
+loudly when they go stale; they just quietly ship a picture saying `v0.9.0` on a 1.2.0
+release. Work down this list:
+
+| What | Where | Notes |
+|---|---|---|
+| Version | `package.json` | The one CI enforces against the tag |
+| Changelog entry | `CHANGELOG.md` | Dated, with a real summary — see below |
+| Version badge, ×6 | `docs/images/src/*.html` | Then **re-render** — `docs/images/src/README.md` has the command |
+| Version badge, ×4 | `docs/images/src/ad-*.html` | The social ads. Same re-render, different window size |
+| Hero alt text | `README.md` | Names the version in the `alt` attribute, where nobody looks |
+| SAST report | `security/sast-report--.md` | New file from the template, not an edit of the last one |
+| CI test counts | this file, Step 10 | Only when they have moved enough to mislead |
+
+Two rules that have already caught mistakes here:
+
+- **Not every version string is the current version.** `knows-what-it-has.html` contains
+ a badge labelling the release a *measurement* was taken on. Bumping that makes the
+ image claim a number it never measured. When in doubt, read the surrounding text.
+- **The "New in …" tag moves to the card the release actually changed, or comes off.**
+ Only ever one card carries it. For 1.0.0 it came off, because 1.0.0 rewrote none of
+ them.
+
+### Writing the changelog entry
+
+Keep-a-Changelog format, and **date it** — several older entries in this file are marked
+`unreleased` despite having been tagged and shipped, which makes the history harder to
+read than it needs to be. The heading should be `## [1.2.0] — 2026-08-20`.
---
@@ -216,6 +326,27 @@ Publishing typically shows up on the Marketplace within a few minutes.
2. Check: the icon renders correctly, the README renders correctly (headings, images, badges), the description and categories look right, and the version number matches what you just published.
3. Install it fresh from within VS Code (`Extensions` → search "HirayaCoder") on a clean profile if possible, to see exactly what a new user sees.
+### Step 9b — Flip the install route (the very first publish only)
+
+**Do this only once the listing above is actually live and installable.** Until 1.0.0 the
+repo deliberately told everyone to download a `.vsix` from GitHub Releases, because that
+was the only thing that worked. Once "search the Extensions view" is true, four places
+should say so:
+
+| File | What to change |
+|---|---|
+| `README.md` | The banner near the top, and Step 4 of *Getting started* |
+| `docs/images/src/hero-offline-agent.html` | The CTA button and the note beside it |
+| `docs/images/src/capabilities.html` | The footer |
+| `docs/images/src/ad-4-launch.html` | The footer, and the matching caption in `docs/images/ADS-1.0.0.md` |
+
+Re-render the three images afterwards. Keep GitHub Releases mentioned as well rather than
+replacing it — VSCodium users and anyone on a locked-down machine still need the `.vsix`.
+
+**Do not do this ahead of time.** `capabilities.html` shipped a footer reading
+*Search "HirayaCoder" in the Extensions view* through 0.7.0, when there was nothing to
+find, and that is a worse first impression than a `.vsix` download.
+
---
## Step 10 — Tag the Release in Git & Publish on GitHub
@@ -228,9 +359,9 @@ git push origin main --tags
That one push triggers, in order:
-1. **Verify** — lint, 573 unit tests, and the 12 integration tests against a real VS
- Code, on **Ubuntu, macOS, and Windows** in parallel, plus a production dependency
- audit. Packaging does not start unless all three platforms pass.
+1. **Verify** — lint, the full unit suite (1,545 tests at 1.0.0), and the 16 integration
+ tests against a real VS Code, on **Ubuntu, macOS, and Windows** in parallel, plus a
+ production dependency audit. Packaging does not start unless all three platforms pass.
2. **Guard** — the tag is compared against `package.json`. A `v0.2.0` tag on a manifest
still saying `0.1.0` fails here, rather than producing a `.vsix` whose filename
disagrees with the release it hangs from.
@@ -238,6 +369,16 @@ That one push triggers, in order:
4. **Publish** — a GitHub Release for the tag, with the `.vsix` attached, its SHA-256
recorded, install instructions, and auto-generated commit notes.
+**Release or pre-release is decided by the version, not by a checkbox.** A `0.x` version
+or one with a `-rc`/`-beta` suffix is published as a GitHub pre-release and gets an extra
+"offered to try, not to depend on" note appended. Anything else — 1.0.0 onward — is
+published as a full release and marked `--latest`, which is what
+`/releases/latest` resolves to and therefore what install instructions point at.
+
+Nothing to set: `test/unit/releaseWorkflow.test.js` asserts the classification for
+whatever is currently in `package.json`, so a green suite already tells you which one
+this tag will produce.
+
It uses the runner's built-in `GITHUB_TOKEN`. **No Personal Access Token is stored in
this repository**, and the workflow is read-only except for the single job that creates
the release. Marketplace publishing (Step 8) stays deliberately manual — pushing a tag
@@ -267,17 +408,24 @@ This gives users (and you) a durable, versioned home for every `.vsix` you've ev
## Quick Reference (once you've done Steps 1–3 once)
```bash
-npm version patch # bump version + tag
-npm test # confirm green
-vsce package # produces hirayacoder-.vsix
-mkdir -p builds/v
-mv hirayacoder-.vsix builds/v/
+npm version patch --no-git-tag-version # bump package.json only
+# ...then update CHANGELOG.md, the 10 image sources, and the README alt text (Step 5)
+npm run test:all # lint + unit + integration, all green
+npm run package # builds/v/hirayacoder-.vsix
+
code --install-extension builds/v/hirayacoder-.vsix # smoke test
-vsce publish --packagePath builds/v/hirayacoder-.vsix
-git push origin main --tags
-# then create a GitHub Release for the pushed tag and attach the .vsix
+code --uninstall-extension jaymar921.hirayacoder # then remove it
+
+vsce publish --packagePath builds/v/hirayacoder-.vsix # the manual bit
+
+git commit -S -am "chore: v"
+git tag -s v -m "HirayaCoder v"
+git push origin main --tags # CI builds and creates the GitHub Release
```
+The tag push is the last step, and it is the one that is hard to undo — everything above
+it is reversible.
+
---
## Optional: Also Publishing to Open VSX
diff --git a/docs/images$name.png b/docs/images$name.png
deleted file mode 100644
index 80c2a52..0000000
Binary files a/docs/images$name.png and /dev/null differ
diff --git a/docs/images/ADS-1.0.0.md b/docs/images/ADS-1.0.0.md
new file mode 100644
index 0000000..fec71ae
--- /dev/null
+++ b/docs/images/ADS-1.0.0.md
@@ -0,0 +1,126 @@
+# HirayaCoder 1.0.0 — launch ad set
+
+Four square (1:1) ads for the 1.0.0 launch, with the caption each one ships with, plus a
+single caption for posting all four together as a carousel.
+
+Rendered at **2160×2160** from the sources in `src/` (1080×1080 CSS pixels at
+`--force-device-scale-factor=2`). 1:1 is the safe ratio everywhere — it is the native
+LinkedIn/Instagram feed shape, and X and Facebook centre-crop it without losing content.
+
+| # | Image | Source | The one thing it argues |
+|---|---|---|---|
+| 1 | `ad-1-offline.png` | `src/ad-1-offline.html` | Your code never leaves your machine |
+| 2 | `ad-2-agentic.png` | `src/ad-2-agentic.html` | Fully agentic even on a 1B model |
+| 3 | `ad-3-approval.png` | `src/ad-3-approval.html` | Nothing is written without your approval |
+| 4 | `ad-4-launch.png` | `src/ad-4-launch.html` | 1.0 is out, and here is how to start |
+
+Order matters if you post them as a carousel: 1 is the hook, 2 is the proof it actually
+works, 3 is the objection ("will an AI wreck my project?"), 4 is the ask.
+
+Regenerating them is the same command as the README images — see
+[`src/README.md`](src/README.md).
+
+---
+
+## Ad 1 — `ad-1-offline.png`
+
+> Your AI coding assistant is reading your code on someone else's servers right now.
+>
+> HirayaCoder does not. It runs entirely on your own machine through Ollama — no
+> account, no subscription, no internet connection. Turn your Wi-Fi off and it keeps
+> working. That is not a privacy policy, it is the architecture: a non-loopback address
+> is refused in the code itself, before any connection is opened.
+>
+> Free, MIT-licensed, and zero production dependencies. Nothing third-party ships inside
+> it.
+>
+> HirayaCoder 1.0 for VS Code is out now.
+
+## Ad 2 — `ad-2-agentic.png`
+
+> Most "local AI" extensions give a small model autocomplete and call it a day.
+>
+> HirayaCoder gives a 1B model the whole job: it reads your files, writes them, deletes
+> them, and runs your build — across multiple files, on its own. Two loop strategies sit
+> behind one driver, so native tool-calling runs on capable models and a constrained
+> one-action-per-turn loop runs on the small ones. The mechanism changes with the model.
+> The reach never does.
+>
+> Which matters because the model that fits on your laptop is the small one.
+>
+> 16 GB and no graphics card is the machine it was built for.
+
+## Ad 3 — `ad-3-approval.png`
+
+> "I am not letting an AI loose on my project."
+>
+> Correct — and neither are we. Every write shows you the diff first and waits. Nothing
+> reaches disk until you approve it. Deleting a file asks even when you have turned
+> automatic edits on, because a wrong write is visible in the diff and a wrong delete of
+> an uncommitted file is gone.
+>
+> Underneath that: the agent cannot leave the folder you opened, commands come from a
+> fixed allow-list with no shell, and the write guards refuse a truncated file, a
+> dropped export, or a function replaced by a stub.
+>
+> Those guards exist because four different models produced six damaged files in one
+> seventeen-run sweep. Every one of them parsed cleanly.
+
+## Ad 4 — `ad-4-launch.png`
+
+> HirayaCoder 1.0 is out.
+>
+> A free, fully offline AI coding agent for VS Code, built for 16 GB laptops with no
+> graphics card rather than for top-spec dev machines.
+>
+> Three steps to running:
+> 1. Install Ollama and leave it running
+> 2. `ollama pull gemma4:e2b`
+> 3. Install HirayaCoder, open a folder, press Ctrl+Shift+H
+>
+> Three modes when you get there: Agent writes, Plan looks without touching anything and
+> hands back a checklist you can edit and run, Ask just answers.
+>
+> *Hiraya* (Filipino) — imagination, aspiration, the spark of an idea before it becomes
+> real.
+
+**Once the Marketplace listing is live**, swap the install line in the ad-4 caption and
+in the carousel caption for *"Search HirayaCoder in the VS Code Extensions view"*, and
+re-render `ad-4-launch.png`, whose footer carries the same route. Until then every caption
+here points at GitHub, which works today — the 0.7.0 images shipped a "search the
+Extensions view" line months before there was anything to find, and that is the mistake
+this note exists to prevent.
+
+---
+
+## The one caption for all four
+
+Use this when posting the set together — a carousel, an album, or a single launch post
+with all four attached.
+
+> **HirayaCoder 1.0 is out — a free AI coding agent that never sends your code anywhere.**
+>
+> Four things worth knowing about it, one per image:
+>
+> **1. It is genuinely offline.** It talks to Ollama on 127.0.0.1 and nothing else. A
+> non-loopback address is refused in the code, so "your code stays local" is a property
+> of the architecture rather than a promise in a policy. No account, no telemetry, no
+> production dependencies.
+>
+> **2. It is fully agentic on small models.** Not autocomplete — it reads, writes,
+> deletes and runs your build across multiple files on its own, down to 1B parameters.
+> Capable models get native tool-calling; small ones get a constrained one-action-per-turn
+> loop. Same reach, different mechanism.
+>
+> **3. You approve everything.** Every write shows a diff and waits. Deletes ask even
+> with auto-edit on. The agent cannot leave your folder, and commands run from a fixed
+> allow-list with no shell.
+>
+> **4. It was built for the machine you actually have.** 16 GB, no graphics card, a task
+> in one to five minutes. Honest about the trade too: small models handle one file, one
+> feature, one fix at a time well, and a whole application badly.
+>
+> Free and MIT-licensed. Install Ollama, `ollama pull gemma4:e2b`, then grab the
+> extension from github.com/jaymar921/HirayaCoder.
+>
+> #OfflineAI #LocalLLM #VSCode #PrivacyFirst #OpenSource
diff --git a/docs/images/ad-1-offline.png b/docs/images/ad-1-offline.png
new file mode 100644
index 0000000..f3efd77
Binary files /dev/null and b/docs/images/ad-1-offline.png differ
diff --git a/docs/images/ad-2-agentic.png b/docs/images/ad-2-agentic.png
new file mode 100644
index 0000000..bde2f13
Binary files /dev/null and b/docs/images/ad-2-agentic.png differ
diff --git a/docs/images/ad-3-approval.png b/docs/images/ad-3-approval.png
new file mode 100644
index 0000000..09b7082
Binary files /dev/null and b/docs/images/ad-3-approval.png differ
diff --git a/docs/images/ad-4-launch.png b/docs/images/ad-4-launch.png
new file mode 100644
index 0000000..ede5cf5
Binary files /dev/null and b/docs/images/ad-4-launch.png differ
diff --git a/docs/images/asked-the-wrong-way.png b/docs/images/asked-the-wrong-way.png
index dde84bc..a16d669 100644
Binary files a/docs/images/asked-the-wrong-way.png and b/docs/images/asked-the-wrong-way.png differ
diff --git a/docs/images/capabilities.png b/docs/images/capabilities.png
index fe6d081..2ece7fa 100644
Binary files a/docs/images/capabilities.png and b/docs/images/capabilities.png differ
diff --git a/docs/images/hero-offline-agent.png b/docs/images/hero-offline-agent.png
index 6523ecc..d9345c0 100644
Binary files a/docs/images/hero-offline-agent.png and b/docs/images/hero-offline-agent.png differ
diff --git a/docs/images/knows-what-it-has.png b/docs/images/knows-what-it-has.png
index a70e071..6a35f32 100644
Binary files a/docs/images/knows-what-it-has.png and b/docs/images/knows-what-it-has.png differ
diff --git a/docs/images/live-session.png b/docs/images/live-session.png
index 1a95dc6..abd4826 100644
Binary files a/docs/images/live-session.png and b/docs/images/live-session.png differ
diff --git a/docs/images/src/README.md b/docs/images/src/README.md
index e1d917e..9b0c9ff 100644
--- a/docs/images/src/README.md
+++ b/docs/images/src/README.md
@@ -12,6 +12,14 @@ one-line change instead of a redesign.
| `knows-what-it-has.html` | `../knows-what-it-has.png` | README, "Small models that finish" |
| `asked-the-wrong-way.html` | `../asked-the-wrong-way.png` | README, "Asked the wrong way" |
| `your-structure-is-the-plan.html` | `../your-structure-is-the-plan.png` | README, "Your structure is the plan" |
+| `ad-1-offline.html` | `../ad-1-offline.png` | Social ad — nothing leaves your machine |
+| `ad-2-agentic.html` | `../ad-2-agentic.png` | Social ad — agentic down to 1B |
+| `ad-3-approval.html` | `../ad-3-approval.png` | Social ad — you approve every write |
+| `ad-4-launch.html` | `../ad-4-launch.png` | Social ad — the 1.0 launch card |
+
+The six README images are **1280×720**; the four `ad-*` files are **1080×1080**, because
+a social post gets cropped to a square on most of the places it lands. Their captions
+live in [`../ADS-1.0.0.md`](../ADS-1.0.0.md).
All of them are self-contained: no fonts, scripts, or images are fetched, and the app icon is
inlined as SVG. Everything renders from system fonts, so they look the same on any
@@ -30,6 +38,8 @@ chrome --headless=new --disable-gpu --hide-scrollbars \
docs/images/src/hero-offline-agent.html
```
+The `ad-*` files take `--window-size=1080,1080` instead, which gives 2160×2160.
+
On Windows, `msedge.exe` takes the same flags and needs `--user-data-dir` pointed at a
scratch folder — without it the launch is handed to the browser the user already has open
and no screenshot is written.
@@ -44,8 +54,11 @@ writes nothing, so check the file timestamps rather than the exit code:
## When these need updating
-- **The version badge**, on every release. It appears once per file, as `v0.9.0`.
- All six sources were bumped together for 0.9.0.
+- **The version badge**, on every release. It appears once per file, as `v1.0.0`.
+ All six README sources were bumped together for 1.0.0, and the four ads carry it too.
+ **Not** the `badge a` inside `knows-what-it-has.html`: that one labels the release the
+ measurement was taken on, and bumping it would make the picture claim a number it never
+ measured.
- **The "New in …" line**, whenever a release changes what a card is claiming. It moves
to whichever card the release actually changed rather than staying put — it sat on
*Knows your machine* for 0.6.1, moved to *Big requests become a checklist* for 0.7.0,
@@ -54,12 +67,26 @@ writes nothing, so check the file timestamps rather than the exit code:
is the card 0.9.0 rewrote — the checklist now comes from your own headings rather than
from a planning call. Only ever one card carries it; two "New in" tags read as a
changelog rather than as a highlight.
+
+ **For 1.0.0 it was removed rather than moved.** 1.0.0 rewrote none of these six cards —
+ it is the release that stabilises them — so every card was equally "new", which is
+ another way of saying none of them was. Leaving the 0.9.0 tag in place would have let
+ this release take credit for the previous one's work.
- **The mock chat transcript** in the hero, if the panel's real layout changes enough that
the picture stops being an honest one. It is a mock, not a screenshot — but it should
never show something the extension does not do.
-- **The install route, on the day it reaches the Marketplace.** Both images currently say
- this is a pre-release fetched from GitHub Releases, because it is:
- `hero-offline-agent.html` in the CTA button and its note, `capabilities.html` in the
- footer. Until that day the Marketplace must not be mentioned as a way to get it —
- `capabilities.html` shipped a footer reading *Search "HirayaCoder" in the Extensions
- view* through 0.7.0, which was an instruction that could not work.
+- **The install route, on the day it reaches the Marketplace.** Still unchanged at 1.0.0,
+ and deliberately: the version number and the Marketplace listing are separate events,
+ and the tag is cut before the listing exists. What 1.0.0 did change is the *wording* —
+ "Pre-release" was about distribution rather than stability, and a 1.0.0 that calls
+ itself a pre-release is confusing — so the footers now read **Free & MIT** and the
+ hero's note reads **Free and MIT-licensed**. Neither asserts a Marketplace listing.
+
+ The route itself — GitHub Releases — is true today and stays true afterwards, so it is
+ the safe thing to ship. On the day the listing goes live, swap the CTA in
+ `hero-offline-agent.html`, the footer in `capabilities.html`, and the footer in
+ `ad-4-launch.html`, then re-render. Until that day the Marketplace must not be
+ mentioned as a way to get it — `capabilities.html` shipped a footer reading
+ *Search "HirayaCoder" in the Extensions view* through 0.7.0, which was an instruction
+ that could not work. `ad-4-launch.html` was drafted with the same mistake in it and
+ corrected before it was ever rendered.
diff --git a/docs/images/src/ad-1-offline.html b/docs/images/src/ad-1-offline.html
new file mode 100644
index 0000000..4ad135b
--- /dev/null
+++ b/docs/images/src/ad-1-offline.html
@@ -0,0 +1,131 @@
+
+
+
+
+HirayaCoder ad 1 — offline
+
+
+
+
+
+
+
+
+
+ HirayaCoder
+ v1.0.0
+
+
+
Your code never leaves your laptop.
+
+
+ An AI coding agent that runs entirely on your own machine through Ollama.
+ No account. No subscription. No internet.
+
+
+
+
+ ✓
+
+
Works with your Wi-Fi off
+
Try it — that is the proof, not a promise.
+
+
+
+ ✕
+
+
Cannot reach the internet, by construction
+
A non-loopback address is refused in code, before any connection opens.
+
+
+
+ ✕
+
+
No telemetry, and no third-party code
+
Zero production dependencies. Nobody is counting your keystrokes.
+
+
+
+
+
+
+
+
diff --git a/docs/images/src/ad-2-agentic.html b/docs/images/src/ad-2-agentic.html
new file mode 100644
index 0000000..e1f5998
--- /dev/null
+++ b/docs/images/src/ad-2-agentic.html
@@ -0,0 +1,139 @@
+
+
+
+
+HirayaCoder ad 2 — agentic on every model
+
+
+
+
+
+
+
+
+
+ HirayaCoder
+ v1.0.0
+
+
+
Agentic on every model — down to 1B.
+
+
+ It reads, writes, deletes and runs scripts across your project on its own. The
+ mechanism changes with the model. The reach never does.
+
+
+
+
+ Lite tier
+ one action per turn
+ llama3.2:1b
+
+
+ 1Read
+ README.md
+ to learn the project structure ✓
+
+
+ 2Wrote
+ src/hooks/useTodos.js
+ the hook the request named ✓
+
+
+ 3Wrote
+ src/components/TodoItem.jsx
+ one row, edit and delete ✓
+
+
+ 4Running
+ npm run build
+ waiting for your approval
+
+
+
+
+
+
+
diff --git a/docs/images/src/ad-3-approval.html b/docs/images/src/ad-3-approval.html
new file mode 100644
index 0000000..2a6caa3
--- /dev/null
+++ b/docs/images/src/ad-3-approval.html
@@ -0,0 +1,161 @@
+
+
+
+
+HirayaCoder ad 3 — you approve every change
+
+
+
+
+
+
+
+
+
+ HirayaCoder
+ v1.0.0
+
+
+
Nothing is saved without your say-so.
+
+
+ Every write shows you the diff first. You approve it, or it does not happen.
+
+
+
+
+ Apply this change to
+ src/components/TodoItem.jsx
+ +5-1
+