让免费 AI 帮我抓住偷钱的订阅 | I Made Free AI Hunt My Sneaky Subscriptions

用 Codex 的免费额度,配合两套开源工程技能,做一个纯 Python 标准库的本地订阅账单管家,重点是让它先把"月末扣款日"这个坑问出来、再用测试锁死。/ Using Codex's free tier plus two open-source engineering skill sets, build a local subscription-bill tracker in pure standard-library Python — the point being to let it interrogate the month-end billing-date trap out of you first, then lock it down with tests.

完整实操案例

为什么选这个:纯标准库、零 pip 安装、M2 16G 毫无压力、模型全在云端跑不占本地内存,而"1 月 31 日订阅下次几号扣"是个真实且非平凡的正确性陷阱。/ Why this one: pure stdlib, zero pip installs, effortless on an M2 with 16GB, all inference runs in the cloud so local memory is untouched — and "January 31st subscription, what's the next billing date" is a genuine, non-trivial correctness trap.


1. 装 Codex CLI/Install Codex CLI

macOS 用官方一键脚本或 Homebrew,装完 codex login 用免费 ChatGPT 账户登录。/On macOS use the official one-liner or Homebrew, then run codex login with a free ChatGPT account.

不需要信用卡,Free 档就能跑通整个例子。/No credit card needed; the Free tier carries this whole example.


2. 建一个干净仓库/Create a clean repo

mkdir sub-tracker && cd sub-tracker && git init,然后 python3 --version 确认是 3.9 以上。/Run mkdir sub-tracker, cd into it, git init, then check python3 --version is 3.9 or above.

macOS 自带的 Python 3 就够,zoneinfo 和 unittest 都在标准库里。/The Python 3 that ships with macOS is enough; zoneinfo and unittest are both stdlib.


3. 装技能库,只装一套/Install the skills, pick one path only

npx skills@latest add mattpocock/skills,在勾选界面务必勾上 setup-matt-pocock-skills。/Run npx skills@latest add mattpocock/skills, and be sure to tick setup-matt-pocock-skills in the picker.

用 Claude Code 的人改用 claude plugins install mattpocock-skills,但两条路只能选一条,否则每个技能都会有两份。/Claude Code users run claude plugins install mattpocock-skills instead — pick one path only, or you get every skill twice.


4. 跑一次初始化/Run the one-time setup

在代理里执行 /setup-matt-pocock-skills,issue tracker 选"本地文件"。/In the agent run /setup-matt-pocock-skills and choose local files as the issue tracker.

选本地文件是为了完全免费、也不用授权任何仓库权限。/Local files keeps it entirely free and avoids granting any repo permissions.


5. 装 Superpowers,并指定谁当家/Install Superpowers, and name the controller

按仓库 docs/README.codex.md 装 Codex 版;Claude Code 用 /plugin install superpowers@claude-plugins-official。/Install the Codex build per the repo's docs/README.codex.md; Claude Code users run /plugin install superpowers@claude-plugins-official.

在 AGENTS.md 里写明"Superpowers 负责流程,mattpocock 技能按需调用",避免两套指令打架。/Write in AGENTS.md that Superpowers owns the process and mattpocock skills are called on demand, so the two don't fight.


6. 先被追问,别急着写码/Get grilled before any code

运行 /grill-with-docs,只给一句需求:"我想知道我的订阅什么时候扣钱"。/Run /grill-with-docs and give it just one line: I want to know when my subscriptions charge me.

它会逼出你没想过的分支——年付还是月付、试用期算不算、时区按哪个、以及那个致命问题:1 月 31 日订的,2 月几号扣?/It will force out the branches you never considered: annual or monthly, does the trial count, whose timezone, and the killer — subscribed January 31st, which day does February bill?


7. 把术语钉进 CONTEXT.md/Nail the vocabulary into CONTEXT.md

让它把"锚定日""计费周期""宽限期"写成项目共享语言。/Have it write anchor day, billing cycle, and grace period into the project's shared language.

以后你说"锚定日漂移"它就懂,不用每次解释三句话。/From then on you say anchor-day drift and it knows, instead of you explaining it three times over.


8. 出 spec 和 tickets/Produce the spec and tickets

/to-spec 把对话变成规格,/to-tickets 拆成带阻塞关系的小票。/Run /to-spec to turn the conversation into a spec, then /to-tickets to split it into tickets with blocking edges.

你会看到"锚定日计算"自动被拆成独立一票——这就是流程在帮你。/You'll see anchor-day calculation break out as its own ticket — that's the process working for you.


9. 用 worktree 隔离/Isolate with a worktree

git worktree add ../sub-tracker-anchor -b feat/anchor,让代理只在这个副本里干活。/Run git worktree add ../sub-tracker-anchor -b feat/anchor and let the agent work only in that copy.

主分支永远干净,搞砸了删掉目录就行。/Your main branch stays pristine; if it goes wrong, just delete the directory.


10. 先写会红的测试/Write the test that goes red first

/tdd,要求第一个测试就是"1 月 31 日订阅,第 1 期应是 2 月 28 日,第 2 期必须回到 3 月 31 日"。/Run /tdd and demand the first test be: subscribed January 31st, cycle one lands February 28th, and cycle two must return to March 31st.

"必须回到 31 号"这句是整个例子的灵魂,90% 的实现都会在这里错。/That "must return to the 31st" is the soul of this example; nine out of ten implementations get it wrong.


11. 让它实现到测试变绿/Let it turn the test green

盯住一点:必须从原始起始日按期数直接算,不能逐月累加。/Watch for one thing: it must compute the nth cycle from the original start date, never accumulate month by month.

逐月累加会让 31 号在二月被永久截断成 28 号,这就是真实世界的账单 bug。/Accumulating month by month permanently clips the 31st down to the 28th, and that is the real-world billing bug.


12. 故意制造一个 bug 再诊断/Plant a bug, then diagnose it

手动把 min 改成 max 提交,然后运行 /diagnosing-bugs。/Manually change min to max, commit, then run /diagnosing-bugs.

它会先建一个能稳定复现的红灯,再最小化、假设、加探针、修复、补回归测试。/It builds a reliably red repro first, then minimises, hypothesises, instruments, fixes, and adds a regression test.


13. 双轴代码审查/Two-axis code review

/code-review 会分两路子代理:一路查代码规范和坏味道,一路查是否忠实实现了第 8 步的 spec。/Run /code-review and it splits into two sub-agents: one checks standards and smells, the other checks faithfulness to the spec from step 8.

两路分开跑,所以不会互相污染结论。/They run separately, so neither contaminates the other's verdict.


14. 规划装不进一个上下文的部分/Plan what won't fit in one context

/wayfinder,目标写"多币种 + 导出 iCal 提醒 + 汇率历史"。/Run /wayfinder with the goal: multi-currency plus iCal reminder export plus exchange-rate history.

它会拆成一串决策票存进本地文件,进度不再靠聊天记忆撑着。/It breaks that into decision tickets in local files, so progress no longer rides on chat memory.


15. 交接给明天的自己/Hand off to tomorrow's you

/handoff 生成交接文档,然后 codex 退出,额度归零也不怕。/Run /handoff to produce a handoff doc, then quit codex — hitting your limit no longer costs you anything.

明天开新会话读那份文档就能续上,这是免费额度用户最重要的一招。/Tomorrow a fresh session reads that doc and picks up where you left off; for free-tier users this is the single most important move.


16. 每天早上一行命令/One command each morning

python3 -m subtracker upcoming --days 14,把它加进 shell alias。/Run python3 -m subtracker upcoming --days 14 and drop it into a shell alias.

七个房客全部现形,一分钱模型额度都不用花。/All seven tenants show their faces, and it costs zero model credits.


核心 Python 代码(纯标准库,零安装)


subtracker/anchor.py — 整个例子的技术核心:


"""

Billing-date arithmetic. Pure stdlib: no third-party packages, no registry needed.

"""

from __future__ import annotations


import calendar

from dataclasses import dataclass

from datetime import date



def _clamp_to_month(year: int, month: int, anchor_day: int) -> date:

    """Place anchor_day in (year, month), clamping to that month's last day."""

    last = calendar.monthrange(year, month)[1]

    return date(year, month, min(anchor_day, last))



def nth_monthly_renewal(start: date, n: int) -> date:

    """The nth renewal after `start`, computed from the ORIGINAL anchor day.


    This is the whole point. Never accumulate month by month: doing so

    permanently clips a 31st down to 28th once it passes February.


    >>> nth_monthly_renewal(date(2026, 1, 31), 1)

    datetime.date(2026, 2, 28)

    >>> nth_monthly_renewal(date(2026, 1, 31), 2)   # must RETURN to the 31st

    datetime.date(2026, 3, 31)

    """

    if n < 1:

        raise ValueError("n must be >= 1")

    total = start.month - 1 + n

    return _clamp_to_month(start.year + total // 12, total % 12 + 1, start.day)



@dataclass(frozen=True)

class Subscription:

    name: str

    amount_cents: int

    start: date

    cadence: str = "monthly"  # "monthly" | "yearly"


    def next_renewal(self, today: date) -> date:

        if self.cadence == "yearly":

            return _clamp_to_month(

                today.year + (1 if (today.month, today.day) > (self.start.month, self.start.day) else 0),

                self.start.month,

                self.start.day,

            )

        n = 1

        while nth_monthly_renewal(self.start, n) <= today:

            n += 1

        return nth_monthly_renewal(self.start, n)


tests/test_anchor.py — 第 10 步那个会变红的测试:


import unittest

from datetime import date


from subtracker.anchor import Subscription, nth_monthly_renewal



class TestAnchorDrift(unittest.TestCase):

    def test_jan31_clamps_in_february(self):

        self.assertEqual(nth_monthly_renewal(date(2026, 1, 31), 1), date(2026, 2, 28))


    def test_jan31_returns_to_31_in_march(self):

        """The test that catches the real-world billing bug."""

        self.assertEqual(nth_monthly_renewal(date(2026, 1, 31), 2), date(2026, 3, 31))


    def test_no_cumulative_drift_over_a_year(self):

        self.assertEqual(nth_monthly_renewal(date(2026, 1, 31), 12), date(2027, 1, 31))


    def test_leap_year_february_29(self):

        self.assertEqual(nth_monthly_renewal(date(2028, 1, 31), 1), date(2028, 2, 29))


    def test_next_renewal_skips_past_dates(self):

        sub = Subscription("Streaming", 1599, date(2026, 1, 31))

        self.assertEqual(sub.next_renewal(date(2026, 3, 1)), date(2026, 3, 31))



if __name__ == "__main__":

    unittest.main()


跑测试:python3 -m unittest discover -s tests -v


Related Links(相关链接):

https://github.com/romgX/openrelay

https://openai.com/codex

https://github.com/openai/codex

https://developers.openai.com/codex/cli

https://openai.com/chatgpt/pricing/

https://github.com/mattpocock/skills

https://skills.sh/mattpocock/skills

https://www.aihero.dev/skills

https://github.com/obra/superpowers

https://code.claude.com/docs/en/plugins

https://docs.python.org/3/library/calendar.html

https://docs.python.org/3/library/datetime.html

https://docs.python.org/3/library/unittest.html

https://docs.python.org/3/library/zoneinfo.html

https://git-scm.com/docs/git-worktree


评论

爽文共赏/Popular Posts

5分钟拿下原生IP + 免费节点 手残变大佬 | From Noob to Pro in 5 Minutes: 10 Real IPs + Daily Fresh Free Nodes

免费节点无限续?OneBox+subs-check,付费党沉默了 | Free Nodes Never Die? OneBox + subs-check, Paid Users Left Speechless

一口气搭好!Cloudflare融合Railway与Galaxy打造纯净原生免费VPS | Build a Pure Free VPS with Cloudflare + Railway + Galaxy in One Go

3 Steps to AI Video Fame✨WebSocket+Copilot Auto-Update, Newbies Win Too | 3 步 AI 视频封神✨WebSocket+Copilot 自动更,小白也能卷赢

Stop Note-Taking Pain—AI One-Click, Efficiency Skyrockets | 别再苦抄笔记!用AI一键效率爆表💥

Game-Changing! Get AI Parenting+Math Enlightenment for Free | 0成本get AI育儿+数学启蒙双buff,躺赢养娃