Commit Graph
571 Commits
Author SHA1 Message Date
tiennm99 c965656cb5 feat(redirects): send old Stack URLs to their tsuki URLs
Generate a Cloudflare Pages _redirects file with 301s from the old
/post/YYYY/MM/DD/ post URLs and the old pagination pages. The rules are
temporary and marked for removal once the old URLs stop getting traffic.
2026-10-04 11:03:20 +07:00
tiennm99 143b6a7bf9 chore: migrate to tsuki theme
- Replace the hugo-theme-stack submodule with the tsuki theme vendored in
  themes/tsuki; drop .gitmodules
- config/_default/hugo.yml: theme: tsuki
- config/_default/permalinks.yml: keep /p/:slug/ for posts to preserve
  existing URLs (matches Stack default)
- config/_default/markup.yml: add parser.autoHeadingIDType: github-ascii
  for ASCII heading slugs
- config/_default/params.yml: add tsuki-required keys (search.enable,
  home.recentPostsCount); restructure comments.giscus to tsuki schema
  (repoId/categoryId lowercase, theme, lang, etc.). Stack-only params
  kept for now in case of rollback (silently ignored by tsuki).
- Move content/page/archives -> content/archives (tsuki uses kind=section)
- Move content/page/search -> content/search (drop json output;
  Pagefind index built in CI replaces Fuse.js search)
- Add data/profile.yaml driving the homepage hero
- Copy assets/img/avatar.png -> static/img/ for tsuki's relURL resolution

Verified: 717 pages build clean, 280 aliases preserve old URLs.
Posts at /p/<slug>/, comments + TOC + archives + search all render.
2026-10-04 10:56:03 +07:00
tiennm99 4a5a4333e0 chore(gitignore): track the vendored tsuki theme under themes/ 2026-10-04 10:54:49 +07:00
github-actions[bot] 489a2f7fe5 chore(projects): refresh projects list 2026-10-04 02:34:26 +07:00
tiennm99 695d08f39d docs(referral): add Hermes Agent Cloud referral 2026-10-03 09:30:40 +07:00
github-actions[bot] 272c38ad46 chore(projects): refresh projects list 2026-10-03 04:04:47 +07:00
tiennm99 6dcd23be8e docs(referral): refresh current deals and offers 2026-10-02 10:45:26 +07:00
tiennm99 9edfbcff82 docs(referral): remove ended OpenCode Go referral 2026-10-02 09:57:16 +07:00
tiennm99 d4e09a48e8 docs(referral): remove BigModel.cn and Claude Code guest pass entries 2026-10-02 09:00:02 +07:00
github-actions[bot] 7863d31f24 chore(projects): refresh projects list 2026-10-02 04:30:01 +07:00
github-actions[bot] 465d1bb9f1 chore(projects): refresh projects list 2026-10-01 04:04:46 +07:00
github-actions[bot] 32d89337d3 chore(projects): refresh projects list 2026-09-30 17:07:37 +07:00
github-actions[bot] b4965b05ee chore(projects): refresh projects list 2026-09-30 16:58:10 +07:00
github-actions[bot] 9b94dcb272 chore(projects): refresh projects list 2026-09-30 16:37:51 +07:00
github-actions[bot] c7d15d02cc chore(projects): refresh projects list 2026-09-30 16:22:09 +07:00
github-actions[bot] 223346a0ba chore(projects): refresh projects list 2026-09-30 04:07:31 +07:00
github-actions[bot] 6c4dbc7842 chore(projects): refresh projects list 2026-09-29 05:18:57 +07:00
tiennm99 2d50af7d07 docs(newsletter): restore original source titles on article headings 2026-09-28 09:33:15 +07:00
tiennm99 82089f32f2 docs(newsletter): mark dead links, fix headings and tags 2026-09-28 09:25:58 +07:00
github-actions[bot] 340c3067b1 chore(projects): refresh projects list 2026-09-28 02:54:30 +07:00
tiennm99 9eff4052fe docs(newsletter): rewrite newsletters with Opus 5.5 2026-09-27 21:47:31 +07:00
tiennm99 210d2cdbea feat(newsletter): protect code, verdicts and author notes in rewrites
protected-lines now keeps fenced code blocks (re-indent allowed) and
**Đánh giá:** verdict lines, and leaves summary sub-headings and short
italic key-point labels rewritable. The rewrite skill skips posts
without the AI-Assisted tag.
2026-09-27 21:47:31 +07:00
tiennm99 c0eecc1263 feat(newsletter): add mt-rewrite-newsletter skill and protected-lines guard 2026-09-27 21:47:30 +07:00
github-actions[bot] 1663964762 chore(projects): refresh projects list 2026-09-27 02:29:20 +07:00
github-actions[bot] efcd46764b chore(projects): refresh projects list 2026-09-26 03:10:28 +07:00
github-actions[bot] 581f2d8d51 chore(projects): refresh projects list 2026-09-25 03:12:20 +07:00
github-actions[bot] fc388e1f9d chore(projects): refresh projects list 2026-09-24 02:54:13 +07:00
github-actions[bot] 052cdad112 chore(projects): refresh projects list 2026-09-23 02:57:16 +07:00
github-actions[bot] 2abeabdadd chore(projects): refresh projects list 2026-09-22 03:41:18 +07:00
github-actions[bot] d33bbf8c87 chore(projects): refresh projects list 2026-09-21 02:09:31 +07:00
github-actions[bot] 8167931ffd chore(projects): refresh projects list 2026-09-20 02:02:07 +07:00
github-actions[bot] 259f1c2963 chore(projects): refresh projects list 2026-09-19 02:16:08 +07:00
tiennm99 2198711575 chore: drop Netlify
The site is built and deployed to Cloudflare Pages by hugo.yml and served by
Cloudflare; netlify.toml was referenced nowhere else and no Netlify build runs,
so the file only pinned toolchain versions for a deploy path that no longer
exists. No _redirects or _headers accompanied it.
2026-09-18 16:34:13 +07:00
tiennm99 e33d082ee2 chore: check out LF line endings everywhere
The repository normalises to LF in the index either way; the working tree was
being converted to CRLF on checkout, which is wrong for a repo worked on from
Linux. It also silently breaks the newsletter parity suite, which compares
committed golden files byte for byte against engine stdout — the reason
scripts/newsletter needed an explicit LF override until now. That override and
the *.sh one are both redundant against an LF default.
2026-09-18 16:33:41 +07:00
tiennm99 88ce69a2c5 docs(plans): record the post-push verification results
Actions deploy and the projects workflow both pass without Go. Netlify is
recorded as not applicable: the site deploys to Cloudflare Pages and netlify.toml
is referenced nowhere else, so no Netlify build exists to observe.
2026-09-18 16:28:07 +07:00
github-actions[bot] 2362422b07 chore(projects): refresh projects list 2026-09-18 16:27:11 +07:00
tiennm99 783dd37251 docs(plans): record the JavaScript migration, parity, and review
Adds the migration plan and its phase files, the parity report, and the code
review.

Annotates the two records this work supersedes rather than rewriting them: the
2026-09-18 language-choice research, whose first recommendation was to keep the
engine in Go and was overridden the same day, and the 2026-08-18 plan that
produced that engine. Both bodies are left as written, since erasing them would
destroy the reasoning a future reader needs.

Six success criteria stay unticked with the reason inline: they need a push, a
GitHub token, or a Codex run to verify.
2026-09-18 16:24:56 +07:00
tiennm99 52bb21cce7 ci: drop the Go toolchain from Actions and Netlify
Nothing in the repository needs Go any more. Kept as its own commit so a bisect
can attribute a Hugo build break to this change rather than to the engine
deletion.

Node setup and the conditional npm ci stay: the lockfile exists, so the
dependencies the engine now needs are installed if a workflow ever invokes it.
2026-09-18 16:24:49 +07:00
tiennm99 e21c12eefe chore(newsletter): delete the Go engine
Parity is proven and every call site now invokes the JavaScript engine, so the
Go implementation has no callers. It remains recoverable from history.

The newsletter engine was Go's only consumer here: the Hugo theme is vendored
under themes/ and the site configuration declares no module block, so nothing
else needs a toolchain. Dropping go.mod also disposes of a latent defect, since
it declared a newer Go version than either build environment pinned and only
worked because the toolchain was fetched on demand.

The Go patterns in .gitignore go with it.
2026-09-18 16:24:42 +07:00
tiennm99 baab3d443a refactor(skills): invoke the Node engine and centralise the command reference
Switches every call site to `node scripts/newsletter` and, in the same pass,
fixes three structural problems in the skill layer.

The seven subcommands were spelled out in five files; they are now enumerated
once in docs/newsletter/engine-commands.md, and every other mention is a link
except where a skill inlines the one or two commands it actually invokes. The
shared post mechanics move out of mt-add-url's directory into docs/newsletter/,
so no skill owns another skill's documentation and both runtimes reach it by a
repository-relative path. The Codex adapters become symlinks to their canonical
counterparts, which removes the parallel frontmatter that could drift.

Two skills are renamed for a uniform mt-<verb>-<object> scheme: mt-add-post
becomes mt-add-article, since it adds an article to a post, and mt-webfetch
becomes mt-fetch-url. The other four already conformed. Note that $mt-add-post
and mt-webfetch no longer resolve; no alias directories are left behind, because
two names for one skill is the duplication this change removes.

Trigger wording in the frontmatter descriptions is left alone apart from the
renamed tokens and the corrected fetch-tier summary: those strings are how the
runtimes decide whether to invoke a skill.

Setup documentation now states the npm ci prerequisite. The engine has
dependencies, so unlike its predecessor it does not run on a bare clone.
2026-09-18 16:24:34 +07:00
tiennm99 682d5239b8 test(newsletter): add a golden-output parity suite
Captures the Go engine's stdout for 26 deterministic cases, commits those
outputs as fixtures, and asserts the JavaScript engine reproduces them through
the real CLI surface, exit codes included. This is both the migration's proof of
correctness and the regression net the engine never had.

Every fixture was generated from the Go engine rather than written by hand, and
reviewed before commit so no existing bug became a permanent contract. Each
behaviour that a JavaScript-idiomatic rewrite would silently break has a case
that goes red when the naive implementation is reintroduced; verified by
mutating the engine and re-running.

Content-scanning commands are pointed at a fixture tree through the subprocess
working directory. Both engines already resolve content/post from the process
CWD, so neither needed a test-only configuration knob, and the suite is proven
isolated from the real content by renaming it away and re-running.

Cases that depend on live upstreams are opt-in behind NEWSLETTER_NET=1: a
publication's feed window moves, and a suite that goes red on someone else's
outage teaches people to ignore red. The deterministic tier reaches the network
only through .invalid hosts, which never resolve, so it runs offline in about
two seconds.

The golden files are compared byte for byte, so scripts/newsletter is pinned to
LF; the repository otherwise checks out CRLF, which would break every comparison
on a fresh clone.
2026-09-18 16:24:16 +07:00
tiennm99 64c7b76b4e feat(newsletter): port the engine to JavaScript
Translates the seven-subcommand engine to Node ESM, one module per former Go
file, invoked as `node scripts/newsletter <command>` from the repo root. The Go
implementation stays in place for now so parity can be measured against it.

Hand-rolled HTML and XML regexes give way to cheerio, which removes the manual
string surgery in caption extraction and covers RSS and sitemap XML through
xmlMode without a second parser. fetch-via-defuddle gains a local extraction
stage ahead of the defuddle.md proxy; the proxy stays, because fetching from a
third IP is the whole point when this machine's IP is the blocked one. Both
stages now emit the same YAML frontmatter plus body, and an extraction that
looks like a bot challenge counts as a local failure so the proxy still runs.

Behaviour is preserved where it is load-bearing rather than where it is merely
idiomatic: query strings are rebuilt by string surgery so surviving parameters
keep their original order and encoding, duplicate detection stays an index loop
with byte-offset boundary checks so a prefix of a stored URL is not a false
match, empty optional fields are omitted rather than emitted as "", a deep-crawl
miss reports cutoff as null rather than dropping the key, bullet filtering counts
code points, tag counts keep their six-column alignment, and a malformed percent
sequence falls back to the raw substring.

The printer lives in its own module so a command module never imports the
dispatcher: index.js runs main() at module scope, so that cycle would execute
the CLI as a side effect of any import. Missing dependencies report one
actionable line instead of a module-resolution stack trace, and a reader that
closes early exits quietly instead of raising EPIPE.
2026-09-18 16:23:58 +07:00
tiennm99 fdbbc5ba42 refactor(scripts): unify module extensions on .js
The root package.json already declares "type": "module", so a .js file here is
ESM and the .mjs suffix only restates what the manifest states. Renames the three
GitHub Actions scripts and follows every reference, including the six JSDoc
import("...") type annotations that would otherwise degrade to any without
throwing.
2026-09-18 16:23:36 +07:00
tiennm99 d41176f149 feat(projects): drop in-development namespaces from the demo list 2026-09-18 13:21:41 +07:00
tiennm99 26e2ff3721 refactor(projects): rename the demo list identifiers to match the page 2026-09-18 13:18:37 +07:00
tiennm99 2b6ba57f47 docs(projects): reword section headings and intros 2026-09-18 13:13:13 +07:00
tiennm99 3c1dc203b9 docs(projects): rewrite projects page intros in a lighter voice 2026-09-18 13:04:36 +07:00
github-actions[bot] 0ca22b7025 chore(projects): refresh projects list 2026-09-18 12:58:43 +07:00
tiennm99 a0de52c877 refactor(projects): build the projects list on the octokit SDK
Replace hand-rolled REST paging, GraphQL cursor loops and header plumbing with
octokit, which also brings request retry and rate-limit backoff. Introduces the
repo's first package.json, lockfile and ESLint flat config; the Hugo site build
itself still needs no Node step.

Output verified against the previous implementation: identical counts and rows
apart from live star drift on four upstream repos.
2026-09-18 12:28:11 +07:00
tiennm99 f0b6b4904d docs(plans): record script language evaluation for repo scripts 2026-09-18 12:24:44 +07:00