尚未偵測到更新。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.