尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗A static PWA to track tasks, habits, goals (by horizon: daily to yearly, or with a deadline), sources (books, videos, courses, articles, podcasts) and an encrypted journal across domains (areas such as work, sport or studies), plus a command line (compass) that works on the same data and prints a compact "today" view for scripts and agents. Anyone can use it with their own private data.
compass-data (the app suggests that name).compass-data) and the branch (main) are filled in. The data files (goals.json, tasks.json…) live at the
repo's root; if goals.json doesn't exist, the app creates an empty one.api.github.com (strict CSP, no third-party scripts), plus
Open Library for books: openlibrary.org when you search for one, and its cover images (served from
covers.openlibrary.org and archive.org). Open Library sees those searches and which covers are shown.Each kind of data has its own JSON file in the data repo (goals.json, tasks.json, habits.json, sources.json, journal.json, trades.json, finance.json, domains.json), saved with
2-space indentation so diffs stay readable. Commits that change data start with the file's prefix (goals:,
tasks:, domains:).
goals.json){
"version": 1,
"timeZone": "Area/City",
"categories": ["…"],
"goals": [{ "id": "…", "kind": "goal", "title": "…", "category": "…",
"horizon": "day|week|month|quarter|year|deadline", "target": 3, "unit": "…",
"deadline": null, "period": "2026-W40",
"progress": 2, "createdAt": "…", "repo": null, "archived": false,
"subgoals": [{ "id": "…", "title": "…", "done": false }] }]
}
Period keys (YYYY-MM-DD, YYYY-Www ISO week, YYYY-MM, YYYY-Qn, YYYY) are computed in the file's timeZone.
period), and when the week ends it moves to Past as completed or missed. Goals do not repeat.progress); without a target, any progress means done.period; it moves to Past the day after deadline.domains.json){
"version": 1,
"domains": [
{ "id": "studies", "name": "Studies", "paths": ["~/projects/studies"], "color": "#3366aa" },
{ "id": "studies/course-a", "name": "Course A", "parent": "studies", "paths": ["~/projects/studies/course-a"] },
{ "id": "sport", "name": "Sport" }
]
}
id is hierarchical: studies/course-a is a subdomain of studies (parent is derived from the id when it is
missing). paths (folders where that domain's work happens, absolute or starting with ~) and color are optional.category is the id or the name of a domain
(Sport and sport both match the sport domain). Categories that are not registered keep working as
plain labels, and compass domains lists them so they can be registered. Nothing needs migrating.~/projects/studies/course-a/week-3
→ studies/course-a; whole folder names only). compass today scoped to a domain shows it in full and only the
agenda of its sub-domains with folders of their own, not its parents (see below).tasks.json){
"version": 1,
"timeZone": "Area/City",
"tasks": [{ "id": "t_…", "title": "…", "domain": "studies/course-a", "due": "2026-10-30", "notes": null,
"subtasks": [{ "id": "s_…", "title": "…", "done": false }],
"repeat": { "type": "weekly", "days": [1, 4] }, "focus": false,
"ongoing": "notes/draft.md", "status": "open", "createdAt": "…", "doneAt": null }]
}
domain is required; due (YYYY-MM-DD, a civil day in the file's timeZone) is optional. There are no
priorities or time estimates: focus (⭐) marks what matters now, at most 3 open tasks at a time.status: open, done or dropped; doneAt is when it was closed.repeat: {"type": "days", "every": N}, {"type": "weekly", "days": [1…7]} (ISO weekdays, 1 = Monday) or
{"type": "monthly", "day": 1…31} (past the end of a short month it falls on its last day). Closing a repeating
task creates the next occurrence as a new task (the closed one keeps its id in next). The next date counts
from the due date, or from today when the task was late, so a late completion does not pile up overdue copies.ongoing: path of a text file with the state of work in progress; "today" lists these tasks even without a date.sessions (omitted when empty): the chat sessions working on the task,
[{ "id": "…", "title": "…", "parent": null, "subtask": "s_…", "addedAt": "…" }]. id is whatever identifies the
session in the assistant that runs it; title is a copy for display; parent is the chat that opened this one
(null: opened from the task itself), which makes a tree ordered by addedAt; subtask ties a chat to one
subtask and its sub-chats inherit it. A chat belongs to one task. Compass only stores the links (the CLI and the
task sheet show them; the sheet is read-only): opening and closing the chats is up to the assistant. Unlinking a
chat moves its sub-chats up one level, and a repeating task's next occurrence starts with none.task: the id of another task (same domain or another) that it is the same thing as.
Completing or reopening either one does the same to the other; dropping or deleting the task removes the
subtask; the chats of the subtask live in that task. The CLI (task sub … --task|--domain|--link) also closes,
reopens and drops the other side when a subtask is changed; confirming first is up to the caller.tasks.json, it copies the timeZone of goals.json.habits.json){
"version": 1,
"timeZone": "Area/City",
"habits": [{ "id": "h_…", "title": "…", "domain": "sport", "freq": { "type": "weekly", "times": 3 },
"notes": null, "done": ["2026-10-26", "2026-10-28"], "createdAt": "…" }]
}
done: the civil days, sorted). No streaks: each
habit shows how this week (Monday to Sunday) is going.freq: {"type": "daily"} (of 7), {"type": "weekly", "times": N} (N days a week, any of them; it stops showing
in Today once the week has N) or {"type": "days", "days": [1…7]} (those ISO weekdays; only they count).archived: true retires it and keeps
its history.sources.json){
"version": 1,
"timeZone": "Area/City",
"sources": [{ "id": "s_…", "kind": "video", "title": "…", "author": "…", "url": "https://…", "file": "talk.txt",
"domain": null, "status": "doing",
"notes": null, "quotes": [{ "id": "q_…", "text": "…", "page": 12 }],
"started": "2026-10-20", "finished": null, "rating": null,
"olid": null, "cover": null, "createdAt": "…" }]
}
kind is book, video, course, article or podcast.url links to it; file is a path inside the sources/ folder of its domain's folder (a PDF, a transcript),
kept out of the data repo. Both are optional.domain is optional: a source without one shows only in the Sources tab and the Sources card on Home; one with
a domain also shows on that area's page.status: want, doing, done, dropped or skip (not for you: kept only as material). It is shown in the
words of each kind (Reading, Watching, Listening…). Changing it sets the dates: doing sets started (if empty),
done and dropped set finished, want and skip clear both.notes are your own thoughts; quotes are lines from it, each with an optional page. rating: 1-5.olid and cover (books) come from an Open Library search: only the cover's id is stored, the image is not.books.json from before sources ({ "books": [...] }, statuses want, reading, read, dropped) is
moved once with compass sources migrate: every book keeps its data.journal.json, sealed)Once opened with the password, the file is:
{
"version": 1,
"timeZone": "Area/City",
"days": [{ "date": "2026-10-28", "text": "…", "notes": [{ "id": "n_…", "text": "…" }] }]
}
In the repo it is stored sealed (lib/crypt.js):
{ "version": 1, "sealed": "AES-256-GCM", "kdf": { "name": "PBKDF2-SHA256", "iterations": 600000, "salt": "…" }, "iv": "…", "data": "…" }.
One entry per day (its text) plus short notes; days with nothing are not kept.
finance.json, sealed)Sealed like the journal. Once opened:
{
"version": 1,
"timeZone": "Area/City",
"domain": "money",
"currency": "EUR",
"categories": [{ "id": "groceries", "name": "Groceries", "limits": [{ "from": "2026-01", "amount": 250 }] }],
"rules": [{ "id": "r_…", "match": "grocer", "category": "groceries" }],
"accounts": [{ "id": "bank", "name": "Bank", "kind": "bank", "currency": "EUR", "balance": 1000, "rate": 1, "at": "…", "address": null }],
"movements": [{ "id": "m_…", "extId": null, "account": "bank", "date": "2026-01-05", "amount": -20.5, "text": "Grocer", "category": null }],
"months": [{ "month": "2025-12", "total": 1000, "byKind": { "bank": 1000 } }],
"savings": [{ "goal": "g_…", "kind": "networth", "target": 5000, "base": null }]
}
A movement's category is its own, else the first rule whose match it contains, else uncategorized; transfer
(one's own accounts) stays out of the budget. A limit holds from its month on. Kinds: bank, cash, defi, debt
(subtracts), other. months gets last month's net worth on its own when a month is first opened. A goal with
"source": "savings" in goals.json takes its progress and target from savings (net worth reaches target, or
grows by it from base).
cli/compass.mjs reads and changes the same data from a local git checkout of your data repo, with the same
operations as the app (it pulls before reading, commits and pushes after writing, and reapplies its change if the
remote moved meanwhile). A missing tasks.json or domains.json
is created on the first write. Needs Node 20+.
ln -s "$PWD/cli/compass.mjs" ~/.local/bin/compass
mkdir -p ~/.config/compass
echo '{ "repo": "/path/to/your/data-repo", "goals": "goals.json", "tasks": "tasks.json", "domains": "domains.json" }' \
> ~/.config/compass/config.json
compass domain add studies --name Studies --path ~/projects/studies
compass task add "Problem set 3" --domain studies --due fri
compass task add "Water plants" --domain home --repeat "every 3 days"
printf '%s\n' '"Pay the fee" --due +3' '"Book a room" --domain home' \
| compass task add --stdin --domain studies # several tasks, one commit; options here apply to every line
compass task done "problem set" # a task by id or part of its title
compass task link "report" SESSION --title "Draft" [--parent SESSION] [--subtask outline] # record a chat on a task
compass task sub "trip" --add "Book flights" --domain studies # a subtask that is its own task in another domain
compass task chats "report" # its chats as a tree (--prune a,b forgets the ones listed; unlink does the same)
compass task brief "report" # starter text for a new chat: title, notes, subtasks, date, chats
compass task move "water" +2 # or: tomorrow (default), mon…sun, YYYY-MM-DD, none
compass tasks --due week # also: --domain D, --cwd PATH, --due today|overdue, --all
compass today --cwd "$PWD" # what matters in the domain of this folder
compass habit add "Stretch" --domain sport --freq mon,wed,fri # or: daily, 3/week
compass habit done stretch yesterday # also: today (default), -N, YYYY-MM-DD
compass source add "A novel" --find # --find: author and cover from Open Library; --domain D to file it in an area
compass source add "A talk" --kind video --url https://… --file talk.txt --domain work
compass source reading novel # or: want, doing, done, dropped, skip, and each kind's words (watched…)
compass sources --kind video --status done # also: --domain D, --cwd PATH
compass source quote novel "A line worth keeping" --page 41
secret-tool store --label="compass encryption" service compass key encryption # the journal's password, once
compass journal note "Called home" # also: compass journal [DATE], days, search, write
compass money # budget of this month and net worth (also: categories, rule, account, saving)
compass help # every command (goals, sub-tasks, focus, edit…)
compass today is meant to be read at the start of a session (for example by an agent's start hook):
Today · Fri 23 Oct · 2026-10-23 · 2026-W43 (Area/City)
Scope: Studies [studies] in full + agenda of Studies › Course A (from --cwd)
Focus
⭐ [t_…] Exam registration — due 2026-10-24 (tomorrow) · studies
Studies [studies]
· [t_…] Pay the fees — due 2026-10-21 (2 d late)
· [g_…] Study sessions — 2/5 · week · studies
Later
· [t_…] Choose next term's courses — due 2026-12-10 (in 48 d)
No date
· [t_…] Thesis draft — ongoing notes/thesis.md
Studies › Course A [studies/course-a] · agenda only
· [t_…] Problem set 3 — due 2026-10-25 (in 2 d)
It shows the focus tasks, then by domain: tasks that are late, due today or in the next 7 days, goals of the day and
the week, and deadlines within 7 days. With a scope (--cwd or --domain), its own part is shown in full: the domain
and its sub-domains without paths of their own, with the tasks dated later ("Later"), those without a date ("No
date") and every active goal. Its sub-domains with their own paths (they have their own folder) are agenda only, and
its parents are left out (their own view shows it). A root folder (compass domains root PATH, stored as root in domains.json) is the one whose
session oversees everything: from it, every other domain is agenda too. Without a scope it shows everything, dated
only. Its git pull gives up after --timeout seconds (3 by default) and the view uses the local copy, with a
one-line warning.
Plain HTML + JS, no build step, with // @ts-check and JSDoc types.
lib/: shared logic, used by the app and the CLI: period keys and civil dates (period.js), the data model and
operations of each data file (goals.js, tasks.js, domains.js), the Today view (today.js) and the GitHub
API (github.js).app/: the PWA (index.html and sw.js stay at the root): core/ (sync and helpers), modules/ (one folder
per kind of data, each adding its part to an area's page and to Home), views/ (Home, Today, Areas, settings).cli/: the command line.python3 tools/serve.py 8000 # open http://localhost:8000 (no caching, so a reload shows every change)
node --test tests/*.test.mjs # logic and CLI tests (the CLI ones use temporary git repos)
tasks.json for 30 days; the next CLI write moves older ones to the archive of the year they
were closed (tasks-2026.json, same folder), so the file the app downloads stays small. compass tasks --archived
lists them.