@fluxvita/jovida-cli 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,22 +15,21 @@ The **Jovida Daily CLI** — capture and manage your **Jovida Daily** todos from
15
15
 
16
16
  ## Setup
17
17
 
18
- **1. Install the `jovida` command** so it is on your `PATH`. Pre-release — build from source:
18
+ **1. Install the `jovida` command** so it is on your `PATH`:
19
19
 
20
20
  ```bash
21
- git clone https://github.com/FluxVita/jovida-cli && cd jovida-cli
22
- npm install && npm run build && npm link
21
+ npm i -g @fluxvita/jovida-cli
23
22
  ```
24
23
 
25
- (When published: `npm i -g @fluxvita/jovida-cli`.) Verify with `jovida --version`.
24
+ (Or build from source: `git clone https://github.com/FluxVita/jovida-cli && cd jovida-cli && npm install && npm run build && npm link`.) Verify with `jovida --version`.
26
25
 
27
26
  **2. Install the skill** so your AI knows when/how to use the CLI:
28
27
 
29
28
  ```bash
30
- npx skills add FluxVita/jovida-cli
29
+ jovida skill install
31
30
  ```
32
31
 
33
- Installs `SKILL.md` into detected agents (`~/.codex/skills/jovida-cli/`, `~/.claude/skills/jovida-cli/`, …). Or paste this repo's URL to your agent and ask it to add the skill.
32
+ Copies the bundled `SKILL.md` (kept in lockstep with the CLI version) into your detected agents (`~/.codex/skills/jovida-cli/`, `~/.claude/skills/jovida-cli/`). Add `--all` to install for all known agents even if not detected. (Alternative: `npx skills add FluxVita/jovida-cli`.)
34
33
 
35
34
  **3. Sign in — this is the user's step (interactive; the agent cannot do it):**
36
35
 
@@ -42,16 +41,16 @@ Verify with `jovida whoami`. From here the CLI stays signed in (auto-renews) unt
42
41
 
43
42
  ## Updating
44
43
 
45
- - **Installed from npm** (`@fluxvita/jovida-cli`): `npm i -g @fluxvita/jovida-cli@latest`.
46
- - **Installed from source** (current pre-release): in the cloned repo run `git pull && npm install && npm run build`. The `npm link` symlink persists, so the rebuilt CLI takes effect immediately no need to re-link.
47
- - **The skill updates separately:** re-run `npx skills add FluxVita/jovida-cli` to refresh `SKILL.md` in your agents. Do this whenever the CLI's commands change, so the agent's knowledge stays in sync.
44
+ - **CLI**: `npm i -g @fluxvita/jovida-cli@latest`. (In an interactive terminal the CLI also notifies you when a newer version exists.)
45
+ - **Skill**: run `jovida skill update` after updating the CLI it re-copies the bundled `SKILL.md`, so the agent's knowledge stays in lockstep with the installed CLI version (same npm package, no drift).
46
+ - **From source**: in the cloned repo run `git pull && npm install && npm run build` (the `npm link` symlink persists), then `jovida skill update`.
48
47
 
49
48
  ## Quickstart
50
49
 
51
50
  ```bash
52
51
  jovida create "submit the report by Friday 6pm" --when 2026-06-12T18:00:00+08:00
53
52
  jovida list
54
- jovida show <entry_id>
53
+ jovida view <entry_id>
55
54
  jovida complete <entry_id>
56
55
  ```
57
56
 
@@ -60,7 +59,7 @@ jovida complete <entry_id>
60
59
 
61
60
  ## Commands
62
61
 
63
- `create` · `list` · `show` · `update` · `complete` · `delete` · `login` · `logout` · `whoami`.
62
+ `create` · `list` · `view` · `update` · `complete` · `reopen` · `delete` · `login` · `logout` · `whoami`.
64
63
  Run `jovida help` for usage, or see [`SKILL.md`](./SKILL.md) for flags & field conventions.
65
64
 
66
65
  ## Auth
package/README.zh-CN.md CHANGED
@@ -15,22 +15,21 @@
15
15
 
16
16
  ## 安装
17
17
 
18
- **1. 安装 `jovida` 命令**,让它在 `PATH` 上。预发布——暂时从源码构建:
18
+ **1. 安装 `jovida` 命令**,让它在 `PATH` 上:
19
19
 
20
20
  ```bash
21
- git clone https://github.com/FluxVita/jovida-cli && cd jovida-cli
22
- npm install && npm run build && npm link
21
+ npm i -g @fluxvita/jovida-cli
23
22
  ```
24
23
 
25
- (发布后:`npm i -g @fluxvita/jovida-cli`。)用 `jovida --version` 验证。
24
+ (或从源码构建:`git clone https://github.com/FluxVita/jovida-cli && cd jovida-cli && npm install && npm run build && npm link`。)用 `jovida --version` 验证。
26
25
 
27
26
  **2. 安装 skill**,让 AI 知道何时/如何用 CLI:
28
27
 
29
28
  ```bash
30
- npx skills add FluxVita/jovida-cli
29
+ jovida skill install
31
30
  ```
32
31
 
33
- `SKILL.md` 装进探测到的 agent(`~/.codex/skills/jovida-cli/`、`~/.claude/skills/jovida-cli/`……)。或把本仓库 URL 贴给 agent、让它自行安装。
32
+ 把随包的 `SKILL.md`(与 CLI 同版本)拷进探测到的 agent(`~/.codex/skills/jovida-cli/`、`~/.claude/skills/jovida-cli/`)。加 `--all` 可对所有已知 agent 安装(即使未探测到)。(备选:`npx skills add FluxVita/jovida-cli`。)
34
33
 
35
34
  **3. 登录 —— 这是用户的步骤(交互式;agent 做不了):**
36
35
 
@@ -42,16 +41,16 @@ jovida login # 打开浏览器;登录并点「允许」授权该 CLI
42
41
 
43
42
  ## 更新
44
43
 
45
- - **npm 安装**(`@fluxvita/jovida-cli`):`npm i -g @fluxvita/jovida-cli@latest`。
46
- - **源码安装**(当前预发布):在克隆的仓库里运行 `git pull && npm install && npm run build`。`npm link` 的软链常驻,重新 build 即刻生效——无需重新 link
47
- - **skill 单独更新:** 重跑 `npx skills add FluxVita/jovida-cli` 刷新 agent 里的 `SKILL.md`。CLI 命令有变化时记得做,让 agent 的认知保持同步。
44
+ - **CLI**:`npm i -g @fluxvita/jovida-cli@latest`。(交互式终端里,CLI 还会在有新版时提示你。)
45
+ - **skill**:更新完 CLI 后跑 `jovida skill update`——它重拷随包的 `SKILL.md`,让 agent 认知与已装 CLI 版本同步(同一 npm 包,不漂移)
46
+ - **源码**:在克隆的仓库里 `git pull && npm install && npm run build`(`npm link` 软链常驻),再 `jovida skill update`。
48
47
 
49
48
  ## 快速开始
50
49
 
51
50
  ```bash
52
51
  jovida create "周五下午6点前交报告" --when 2026-06-12T18:00:00+08:00
53
52
  jovida list
54
- jovida show <entry_id>
53
+ jovida view <entry_id>
55
54
  jovida complete <entry_id>
56
55
  ```
57
56
 
@@ -60,7 +59,7 @@ jovida complete <entry_id>
60
59
 
61
60
  ## 命令
62
61
 
63
- `create` · `list` · `show` · `update` · `complete` · `delete` · `login` · `logout` · `whoami`。
62
+ `create` · `list` · `view` · `update` · `complete` · `reopen` · `delete` · `login` · `logout` · `whoami`。
64
63
  `jovida help` 查看用法,或见 [`SKILL.md`](./SKILL.md) 了解参数与字段约定。
65
64
 
66
65
  ## 鉴权
package/SKILL.md CHANGED
@@ -1,94 +1,66 @@
1
1
  ---
2
2
  name: jovida-cli
3
- description: Capture and manage the user's Jovida Daily todos from the command line via the `jovida` CLI — create, list, update, complete, or delete tasks (changes apply immediately, no confirmation step). Use when the conversation surfaces action items, follow-ups, or commitments, or when the user asks to track / remember / organize a task.
3
+ description: Capture and manage the user's Jovida Daily todos via the `jovida` CLI — create, list, update, complete, delete (changes apply immediately, no confirmation, no undo). Use when the conversation surfaces action items, follow-ups, or commitments, or when the user asks to track / remember / organize a task.
4
4
  allowed-tools: Bash(jovida:*)
5
5
  ---
6
6
 
7
7
  # Jovida Daily Todo (CLI)
8
8
 
9
- Help the user capture and manage their **Jovida Daily** todos by shelling out to the `jovida` command-line tool. Run `jovida` commands through your shell — the CLI is the interface to the user's account, and changes sync to their other Jovida devices.
9
+ Help the user capture and manage their **Jovida Daily** todos by shelling out to the `jovida` command. The CLI is the interface to the user's account; changes sync to their other Jovida devices.
10
10
 
11
- > **If the `jovida` command isn't found**, the Jovida Daily CLI isn't installed here tell the user to install it (see the jovida-cli README); don't pretend you tracked anything.
12
- > **Sign-in is required — and it is the user's step.** No anonymous mode; nothing works until the user runs `jovida login` (interactive browser sign-in — you **cannot** do it for them, and **must not** skip it). Be proactive: if you're not sure they're signed in, run `jovida whoami` first. If `whoami` or any command exits `2` (`NOT_SIGNED_IN`), **stop, tell the user to run `jovida login`, and wait until they confirm before retrying** — don't silently drop the task or claim you can't help.
11
+ This skill teaches the *semantics* — when to act, which command, how to compose them. For the **exact flags** of any command, run `jovida <command> --help` (e.g. `jovida create --help`); that is the source of truth and stays in lockstep with the installed version. Don't rely on a flag this skill doesn't name without checking `--help` first.
13
12
 
14
- ## Core mental modelread this first
15
-
16
- Writes apply **immediately**. There is **no proposal or confirmation step**: when you run `jovida create / update / complete / delete`, it takes effect on the user's account at once and syncs to their devices. So:
17
-
18
- - Say "I've **created / completed / deleted** …" — never "I've proposed …".
19
- - Because it's immediate, only run a write when the user **clearly wants that change**. When unsure, ask first (see *Clarify*).
20
- - **There is no undo** from the CLI — be especially careful with `delete` and `complete`.
21
-
22
- ## When to use
23
-
24
- Act when the context holds a real, actionable item:
25
- - an explicit action item, follow-up, or commitment ("I need to…", "remember to…", "before launch, check…");
26
- - the user asks to track / remember / add / organize a task, or to mark one done / remove it.
13
+ > **If `jovida` isn't found**, the CLI isn't installed here tell the user to install it (see the jovida-cli README); don't pretend you tracked anything.
14
+ > **Sign-in is required, and it's the user's step.** No anonymous mode. You **cannot** sign in for them (interactive browser) and **must not** skip it. If unsure they're signed in, run `jovida whoami` first. If `whoami` or any command exits `2` (`NOT_SIGNED_IN`), **stop, ask the user to run `jovida login`, and wait for their confirmation before retrying** — don't silently drop the task or claim you can't help.
27
15
 
28
- Do **not** over-capture:
29
- - ignore hypotheticals, brainstorming, and things already done;
30
- - when unsure whether something is a real task, ask rather than writing noise.
31
-
32
- ## Clarify before writing
33
-
34
- A write changes the user's data immediately — and `delete` / `complete` can't be undone here — so a vague or wrong call is costly. Before writing, make sure the essentials are concrete: *what* the task is, and — when time is implied — *which day / deadline / reminder time*. If the user's message leaves these genuinely ambiguous, **ask one brief question first**; don't fill the gaps with guesses.
35
-
36
- - "remind me about the thing tomorrow" → ask *what* the thing is, and roughly *when*, before writing.
37
- - "set a reminder" with no time → ask for the time.
38
- - Don't over-correct: when details are clear ("submit the report by Friday 6pm"), just act.
16
+ ## Core mental model — read this first
39
17
 
40
- ## Commands
18
+ - **Writes apply immediately.** There is no proposal/confirmation step: `create / update / complete / delete` take effect on the user's account at once and sync to their devices. Say "I've created / completed / deleted …", never "I've proposed …". `complete` is reversible (`reopen`), but **`delete` is permanent — no undo** — so be especially careful with it.
19
+ - **Only write when the user clearly wants the change.** When the intent or the essentials (what the task is; which day / deadline / reminder time) are genuinely vague, **ask one short question first** — don't fill gaps with guesses. ("Remind me about the thing tomorrow" → ask *what* and roughly *when* before writing.) But don't over-correct: when it's clear ("submit the report by Friday 6pm"), just act.
20
+ - **Read before you change.** `update / complete / delete` need a **real `entry_id`** — get it from `jovida list` or `jovida view` and parse the JSON. **Never guess an id.**
21
+ - **Output is machine-readable.** Run non-interactively, the CLI prints **JSON on stdout** (parse it); errors go to **stderr** as `{"error":{"code","message"}}` with a non-zero exit code (`2` not signed in · `3` backend/network · `4` not found · `1` usage). `--json` forces JSON.
41
22
 
42
- You invoke these through your shell. When run non-interactively (as you do), output is **JSON on stdout** parse it. Errors go to **stderr** as `{"error":{"code","message"}}` with a **non-zero exit code** (`2` = not signed in, `3` = backend/network, `4` = entry not found, `1` = usage). You can pass `--json` to force JSON.
23
+ ## When to useand when not
43
24
 
44
- Run `jovida help` to confirm it's available and see the current usage before relying on a flag.
25
+ Act when the context holds a **real, actionable** item: an explicit action item, follow-up, or commitment ("I need to…", "remember to…", "before launch, check…"), or when the user asks to track / remember / organize something, or to mark one done / remove it.
45
26
 
46
- **Read** (understand state, and get the real `entry_id` before any update/complete/delete):
47
- - `jovida list [--scope today|upcoming|recent|range|all] [--status pending|completed|all] [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N]`
48
- → `{ "todos": [ { "entry_id", "title", "when", "priority", "status", "category" } ] }`. A scoped view (defaults: `scope=today`, `status=pending`, `limit=20`), **not** a search.
49
- - `jovida show <entry_id>` → the full todo (description, subtasks, remind_at, hint, …).
27
+ Don't over-capture: ignore hypotheticals, brainstorming, and things already done. When unsure whether something is a genuine task, ask rather than writing noise.
50
28
 
51
- **Write** (immediate):
52
- - `jovida create "<title>" [--when <ISO>] [--priority none|low|medium|high] [--remind <ISO> …] [--category <s>] [--desc <s>] [--subtask "<title>" …] [--hint <s>]`
53
- → `{ "entry_id", "status": "created" }`. **One todo per call** — run it again for more.
54
- - **Recurring**: add `--repeat day|week|month|year` (with `--when` as the **first occurrence**) → creates a recurring series, returns `{ "recurring_id", "status": "created" }`. Tune with `--every N` (e.g. every 2 weeks), `--weekdays mon,wed,fri` (weekly), `--day-of-month N` (monthly/yearly), `--month-of-year N` (yearly), `--until YYYY-MM-DD` (end). Occurrences then show up in `list` like normal todos.
55
- - `jovida update <entry_id> [--title <s>] [--when <ISO>] [--priority …] [--remind <ISO> …] [--category <s>] [--desc <s>] [--subtask "<title>" …] [--hint <s>]`
56
- → `{ "entry_id", "status": "updated" }`.
57
- - `jovida complete <entry_id> [<entry_id> ...]` → `{ "entry_ids", "status": "completed" }`.
58
- - `jovida delete <entry_id> [<entry_id> …]` → `{ "entry_ids": […], "status": "deleted" }`. Pass several ids in one call.
29
+ ## Concepts
59
30
 
60
- Quote the title and any value containing spaces. **Keep `--title` / `--desc` to single-line plain text** passing newlines or shell metacharacters as arguments is fragile (they get mangled). For a long note, keep it short and single-line rather than embedding markdown/newlines.
31
+ These shape *what you put in a command*internalize them; flag spelling lives in `--help`.
61
32
 
62
- **Never put a token in a command** (it lands in shell history / process listings). Signing in is the user's step (interactive browser)see the not-signed-in note above.
33
+ - **A todo's time has two granularities (one `--when`).** A bare **date** (`2026-06-05`) means it *belongs to that day*, no hard deadline; a **datetime** (`2026-06-05T18:00:00+08:00`) is a precise **deadline**. Give whichever you actually know. Don't split "do it Wed, due Fri" if it's due Friday, it's a Friday todo.
34
+ - **Reminders are separate from the time, and a reminder ≠ a deadline.** A reminder is *when to nudge*; each must be at or before the todo's time. "Remind me about X tomorrow" = a todo tomorrow with a reminder tomorrow morning — it does **not** make X *due* at that moment. A todo can carry several reminders.
35
+ - **Recurring = a series, not a todo.** Creating with a repeat rule makes a recurring **series** (you get a `recurring_id`); its individual occurrences then appear in `list` like normal todos, each with its own `entry_id` (and a `recurring_id` linking back). Use a series for a genuinely repeating commitment ("standup every weekday"), not for a handful of distinct dates — make those individual todos.
36
+ - **The rest:** **subtasks** break one task into steps; **category** is a grouping label; **priority** is none/low/medium/high; **hint** is an optional one-line nudge — add it only when it genuinely helps.
63
37
 
64
- ## Field conventions
38
+ ## Commands at a glance
65
39
 
66
- - **`--when`** the todo's time, **one flag, two granularities** (ISO 8601):
67
- - a **date** (`2026-06-05`) → belongs to *that day*, no hard deadline;
68
- - a **datetime** (`2026-06-05T18:00:00+08:00`) → a precise **deadline**.
69
- - Give whichever you actually know. Don't split "do it Wed but due Fri" — if it's due Friday, it's a Friday todo.
70
- - **`--priority`**: `none` | `low` | `medium` | `high`.
71
- - **`--category`**: a grouping label. **`--desc`**: a free-text note.
72
- - **`--subtask "<title>"`** (repeatable): break a task into steps.
73
- - **`--remind <ISO>`** (repeatable) — when to alert the user; **separate from `--when`**:
74
- - each must be **at or before** the todo's time (before the deadline, or any time on a date-only todo's day);
75
- - **reminding ≠ deadline**: "remind me about X tomorrow" → `--when "<tomorrow>" --remind "<tomorrow>T09:00:00+08:00"` (it does *not* make X due);
76
- - if you give only `--remind` and no `--when`, the todo lands on the **latest** reminder's day.
77
- - **`--hint <s>`** — an *optional* one-line nudge shown under the todo. Off by default; add only when it genuinely helps (≤ ~20 chars, never restate the title).
78
- - Recurring tasks are **not supported in the CLI yet** — don't try to express them; create individual todos or tell the user.
40
+ Semantics below; exact flags via `jovida <cmd> --help`.
79
41
 
80
- ## Read before you write
42
+ - **`jovida list`** — a scoped *view* of todos (defaults to today's pending), **not a search**; widen with scope/status/range. Your go-to for finding the `entry_id` you need. Add `--full` to get every field (description, subtasks, reminders) in the same call — one round-trip instead of `list` then `view`.
43
+ - **`jovida view <entry_id>`** — full detail of one todo (description, subtasks, reminders, …).
44
+ - **`jovida create "<title>"`** — add one todo (**one per call**; run again for more). Add a repeat rule to create a recurring series instead.
45
+ - **`jovida update <entry_id>`** — change fields of an existing todo; **only the fields you pass change**. `--remind` / `--subtask` **replace** the whole list (not append).
46
+ - **`jovida complete <id> [<id> …]`** — mark done (pass several ids in one call).
47
+ - **`jovida reopen <id> [<id> …]`** — reopen completed todos (the inverse of `complete`).
48
+ - **`jovida delete <id> [<id> …]`** — permanently remove (several ids in one call; **no undo**).
49
+ - **`jovida whoami` / `login` / `logout`** — session. `login` is the user's interactive step.
81
50
 
82
- `update` / `complete` / `delete` need a **real `entry_id`** get it from `jovida list` or `jovida show` (parse the JSON) first. Never guess an id.
51
+ ## Workflowscomposing the commands
83
52
 
84
- ## Grouping
53
+ Map the user's intent to a sequence; read before any change.
85
54
 
86
- - Several independent todos run `jovida create` once per todo.
87
- - Deleting several related todos at once → **one** `jovida delete` with all the ids.
55
+ - **Capture several items from one message** (meeting notes, a brain-dump): pick out the *real* commitments, then run `jovida create` once per item. Don't cram several tasks into one todo, and don't capture the surrounding discussion.
56
+ - **A deliverable with steps:** one `jovida create` for the outcome, with `--subtask` per step — not many separate todos, when the steps belong to a single result.
57
+ - **Change or reschedule:** `jovida list` (or `view`) to get the `entry_id`, then `jovida update`. To move a deadline, update `--when`. Remember `--remind` / `--subtask` replace the list.
58
+ - **Finish or clean up:** `jovida list` to see what's open, then `jovida complete` (done) or `jovida delete` (remove) — pass all related ids in one call. Prefer `complete` over `delete` unless the item was never real; if you marked one done by mistake, `jovida reopen` undoes it (a `delete` cannot be undone).
59
+ - **A repeating commitment:** create once with a repeat rule (a series). For a few irregular dates, create individual todos instead.
60
+ - **"What's on my plate?"** `jovida list` (today, or widen the scope) and summarize from the JSON. Read-only — don't write anything.
88
61
 
89
- ## Do / Don't
62
+ ## Discipline
90
63
 
91
- - DO `list` / `show` before `update` / `complete` / `delete` (you need the real `entry_id`).
92
- - DO write clear, action-oriented titles (verb-first, concrete).
93
- - DO say "created / updated / completed / deleted" writes are immediate.
94
- - DON'T over-capture; DON'T run an immediate write the user didn't clearly ask for (there's no undo); DON'T claim success if a command exited non-zero — read the error and tell the user (e.g. exit `2` → ask them to `jovida login`).
64
+ - Quote the title and any value containing spaces. Keep titles/descriptions **single-line plain text** newlines and shell metacharacters passed as arguments get mangled.
65
+ - **Never put a token in a command** (it lands in shell history / process listings). Signing in is the user's interactive step.
66
+ - Don't claim success if a command exited non-zeroread the error and tell the user (exit `2` → ask them to `jovida login`).
package/dist/cli.js CHANGED
@@ -4,16 +4,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const ctx_1 = require("./ctx");
5
5
  const create_1 = require("./commands/create");
6
6
  const list_1 = require("./commands/list");
7
- const show_1 = require("./commands/show");
7
+ const view_1 = require("./commands/view");
8
8
  const update_1 = require("./commands/update");
9
9
  const complete_1 = require("./commands/complete");
10
+ const reopen_1 = require("./commands/reopen");
10
11
  const delete_1 = require("./commands/delete");
11
12
  const login_1 = require("./commands/login");
12
13
  const whoami_1 = require("./commands/whoami");
14
+ const skill_1 = require("./commands/skill");
13
15
  const shared_1 = require("./commands/shared");
14
16
  const session_1 = require("./session");
15
17
  const api_1 = require("./api");
16
18
  const state_1 = require("./state");
19
+ const update_check_1 = require("./lib/update-check");
20
+ const VERSION = require('../package.json').version;
17
21
  // 可重复的值 flag(收集成数组)。其余值 flag 取最后一次,无值则布尔 true。
18
22
  const REPEATABLE = new Set(['remind', 'subtask']);
19
23
  function parse(argv) {
@@ -55,6 +59,7 @@ Usage:
55
59
  jovida login --token <vita-token> # dev-only interim: paste a signed-in vita token
56
60
  jovida logout
57
61
  jovida whoami [--json]
62
+ jovida skill install # copy the bundled skill into detected agents (Codex/Claude)
58
63
 
59
64
  jovida create "<title>" [--when <ISO>] [--priority none|low|medium|high]
60
65
  [--remind <ISO> ...] [--category <s>] [--desc <s>]
@@ -63,15 +68,127 @@ Usage:
63
68
  [--day-of-month N] [--month-of-year N] [--until YYYY-MM-DD]
64
69
  (--repeat requires --when as the first occurrence)
65
70
  jovida list [--scope today|upcoming|recent|range|all] [--status pending|completed|all]
66
- [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--json]
67
- jovida show <entry_id> [--json]
71
+ [--from YYYY-MM-DD] [--to YYYY-MM-DD] [--limit N] [--full] [--json]
72
+ jovida view <entry_id> [--json]
68
73
  jovida update <entry_id> [--title ...] [--when ...] [--priority ...] [--remind ...] [...]
69
74
  jovida complete <entry_id> [<entry_id> ...] [--json]
75
+ jovida reopen <entry_id> [<entry_id> ...] [--json]
70
76
  jovida delete <entry_id> [<entry_id> ...] [--json]
71
77
 
72
78
  Output: JSON is emitted automatically when stdout is not a TTY (use --json/--no-json to force).
73
- Env: JOVIDA_API_URL=<url> (default https://api.jovida.ai) · JOVIDA_HOME=<dir> (default ~/.jovida)
79
+ Exit: 0 ok · 1 usage · 2 not signed in · 3 backend/network · 4 not found
80
+ Env: JOVIDA_API_URL=<url> (default https://tapi.jovida.ai) · JOVIDA_HOME=<dir> (default ~/.jovida)
81
+ JOVIDA_NO_UPDATE_CHECK=1 to silence the "update available" notice
82
+
83
+ Run \`jovida <command> --help\` for details on a command (e.g. jovida create --help).
74
84
  `;
85
+ // 子命令详细 help(`jovida <cmd> --help` / `jovida help <cmd>`)。
86
+ const COMMAND_HELP = {
87
+ create: `jovida create — add a todo (or a recurring series)
88
+
89
+ Usage:
90
+ jovida create "<title>" [options]
91
+
92
+ Options:
93
+ --when <ISO> date "2026-06-15" = that day · datetime "2026-06-15T18:00:00+08:00" = deadline
94
+ --priority <p> none | low | medium | high
95
+ --category <s> free-text label
96
+ --desc <s> description (single line)
97
+ --remind <ISO> ... reminder time(s), repeatable; must be at/before --when
98
+ --subtask "<t>" ... subtask(s), repeatable
99
+ --hint <s> short companion hint
100
+ --json
101
+
102
+ Recurring (creates a series; requires --when as the first occurrence):
103
+ --repeat <unit> day | week | month | year
104
+ --every <N> interval, e.g. --repeat week --every 2 = biweekly
105
+ --weekdays <list> weekly: mon,wed,fri (or 1..7)
106
+ --day-of-month <N> monthly/yearly
107
+ --month-of-year <N> yearly
108
+ --until <YYYY-MM-DD> end date
109
+
110
+ Examples:
111
+ jovida create "submit report" --when 2026-06-20T18:00:00+08:00 --priority high
112
+ jovida create "standup" --when 2026-06-15 --repeat week --weekdays mon,wed,fri
113
+ `,
114
+ list: `jovida list — list todos (a scoped view, not a search)
115
+
116
+ Usage:
117
+ jovida list [options]
118
+
119
+ Options:
120
+ --scope <s> today (default) | upcoming | recent | range | all
121
+ --status <s> pending (default) | completed | all
122
+ --from <YYYY-MM-DD> range start (with --scope range)
123
+ --to <YYYY-MM-DD> range end
124
+ --limit <N> max items (default 20)
125
+ --full JSON: include all fields (description, subtasks, reminders) — one round-trip instead of list + view
126
+ --json
127
+
128
+ Examples:
129
+ jovida list
130
+ jovida list --scope all --status all
131
+ jovida list --scope range --from 2026-06-01 --to 2026-06-30
132
+ jovida list --full # full detail of each todo in one call
133
+ `,
134
+ view: `jovida view — full details of one todo
135
+
136
+ Usage:
137
+ jovida view <entry_id> [--json]
138
+ `,
139
+ update: `jovida update — change fields of an existing todo (only the given fields change)
140
+
141
+ Usage:
142
+ jovida update <entry_id> [options]
143
+
144
+ Options (same as create; --title renames):
145
+ --title <s> --when <ISO> --priority <p> --category <s> --desc <s>
146
+ --remind <ISO> ... (replaces the reminder list)
147
+ --subtask "<t>" ... (replaces the subtask list)
148
+ --hint <s> --json
149
+
150
+ Example:
151
+ jovida update cli_01H... --priority high --when 2026-06-21T09:00:00+08:00
152
+ `,
153
+ complete: `jovida complete — mark one or more todos done
154
+
155
+ Usage:
156
+ jovida complete <entry_id> [<entry_id> ...] [--json]
157
+ `,
158
+ reopen: `jovida reopen — reopen one or more completed todos (the inverse of complete)
159
+
160
+ Usage:
161
+ jovida reopen <entry_id> [<entry_id> ...] [--json]
162
+ `,
163
+ delete: `jovida delete — permanently remove one or more todos (no undo)
164
+
165
+ Usage:
166
+ jovida delete <entry_id> [<entry_id> ...] [--json]
167
+ `,
168
+ login: `jovida login — sign in (OAuth device authorization; opens a browser)
169
+
170
+ Usage:
171
+ jovida login [--json]
172
+ jovida login --token <vita-token> # dev-only interim: paste a signed-in vita token
173
+
174
+ The CLI prints a URL + short code and (best-effort) opens your browser; sign in
175
+ and approve there. It cannot sign in for you.
176
+ `,
177
+ logout: `jovida logout — clear local credentials (~/.jovida)
178
+ `,
179
+ whoami: `jovida whoami — show the signed-in account (online check)
180
+
181
+ Usage:
182
+ jovida whoami [--json]
183
+ `,
184
+ skill: `jovida skill — install/update the agent skill from the bundled SKILL.md
185
+
186
+ Usage:
187
+ jovida skill install # copy SKILL.md into detected agents (~/.codex, ~/.claude → skills/jovida-cli/)
188
+ jovida skill update # same (re-copy; keeps the skill in lockstep with the CLI version)
189
+ jovida skill install --all # install for all known agents even if not detected
190
+ `
191
+ };
75
192
  /** 错误 → 退出码(0 ok / 1 用法 / 2 未登录 / 3 后端 / 4 not found)。 */
76
193
  function exitCodeFor(e) {
77
194
  if (e instanceof session_1.NotSignedInError)
@@ -95,11 +212,17 @@ async function main() {
95
212
  const [cmd, ...rest] = process.argv.slice(2);
96
213
  const { positionals, flags } = parse(rest);
97
214
  if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
98
- console.log(HELP);
215
+ const topic = positionals[0]; // `jovida help <command>`
216
+ console.log(topic && COMMAND_HELP[topic] ? COMMAND_HELP[topic] : HELP);
99
217
  return;
100
218
  }
101
219
  if (cmd === 'version' || cmd === '--version' || cmd === '-v') {
102
- console.log(require('../package.json').version);
220
+ console.log(VERSION);
221
+ return;
222
+ }
223
+ // `jovida <command> --help` / `-h` → that command's detailed help.
224
+ if (flags.help === true || rest.includes('--help') || rest.includes('-h')) {
225
+ console.log(COMMAND_HELP[cmd] ?? HELP);
103
226
  return;
104
227
  }
105
228
  // JSON 输出:非 TTY 自动开;--json / --no-json 强制。
@@ -116,6 +239,9 @@ async function main() {
116
239
  case 'whoami':
117
240
  await (0, whoami_1.cmdWhoami)(ctx, { json });
118
241
  break;
242
+ case 'skill':
243
+ (0, skill_1.cmdSkill)(positionals[0], { all: flags.all === true, json });
244
+ break;
119
245
  case 'create':
120
246
  await (0, create_1.cmdCreate)(ctx, {
121
247
  title: positionals.join(' ').trim(),
@@ -142,11 +268,12 @@ async function main() {
142
268
  from: str(flags.from),
143
269
  to: str(flags.to),
144
270
  limit: num(flags.limit),
271
+ full: flags.full === true,
145
272
  json
146
273
  });
147
274
  break;
148
- case 'show':
149
- await (0, show_1.cmdShow)(ctx, { id: positionals[0], json });
275
+ case 'view':
276
+ await (0, view_1.cmdView)(ctx, { id: positionals[0], json });
150
277
  break;
151
278
  case 'update':
152
279
  await (0, update_1.cmdUpdate)(ctx, {
@@ -165,6 +292,9 @@ async function main() {
165
292
  case 'complete':
166
293
  await (0, complete_1.cmdComplete)(ctx, { ids: positionals, json });
167
294
  break;
295
+ case 'reopen':
296
+ await (0, reopen_1.cmdReopen)(ctx, { ids: positionals, json });
297
+ break;
168
298
  case 'delete':
169
299
  await (0, delete_1.cmdDelete)(ctx, { ids: positionals, json });
170
300
  break;
@@ -173,6 +303,7 @@ async function main() {
173
303
  console.log(HELP);
174
304
  process.exitCode = 1;
175
305
  }
306
+ await (0, update_check_1.maybeNotifyUpdate)(VERSION); // 节流 + 仅 TTY + 永不抛;放最后,不影响命令输出/退出码
176
307
  }
177
308
  main().catch((e) => {
178
309
  const msg = e instanceof Error ? e.message : String(e);
@@ -55,7 +55,8 @@ async function cmdList(ctx, a) {
55
55
  items.sort((x, y) => (anchorSec(x) || Number.POSITIVE_INFINITY) - (anchorSec(y) || Number.POSITIVE_INFINITY));
56
56
  items = items.slice(0, a.limit ?? 20);
57
57
  if (a.json) {
58
- console.log(JSON.stringify({ todos: items.map(convert_1.toListItem) }, null, 2));
58
+ const todos = a.full ? items.map((e) => (0, convert_1.toFullTodo)(e)) : items.map(convert_1.toListItem);
59
+ console.log(JSON.stringify({ todos }, null, 2));
59
60
  return;
60
61
  }
61
62
  if (!items.length) {
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cmdReopen = cmdReopen;
4
+ const shared_1 = require("./shared");
5
+ /**
6
+ * 重新打开一个或多个已完成 entry(清 completedAt)——complete 的逆操作。
7
+ * 一次 pull 解析全部 id → 批量 put;任一 id 不存在则整体失败(不改任何条目)。
8
+ */
9
+ async function cmdReopen(ctx, a) {
10
+ if (!a.ids || a.ids.length === 0) {
11
+ throw new Error('entry_id(s) required: jovida reopen <entry_id> [<entry_id> ...]');
12
+ }
13
+ await ctx.session.ensureSession();
14
+ const snap = await ctx.sync.pull();
15
+ const t = (0, shared_1.nowSec)();
16
+ const reopened = [];
17
+ const missing = [];
18
+ for (const id of a.ids) {
19
+ const e = snap.entries.find((x) => x.entryId === id);
20
+ if (e)
21
+ reopened.push({ ...e, completedAt: 0, updatedAt: t });
22
+ else
23
+ missing.push(id);
24
+ }
25
+ if (missing.length)
26
+ throw new shared_1.NotFoundError(missing.join(', '));
27
+ await ctx.sync.putEntries(reopened);
28
+ if (a.json) {
29
+ console.log(JSON.stringify({ entry_ids: reopened.map((e) => e.entryId), status: 'reopened' }));
30
+ return;
31
+ }
32
+ for (const e of reopened)
33
+ console.log(`✓ reopened ${e.title} (${e.entryId})`);
34
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cmdSkill = cmdSkill;
4
+ const node_os_1 = require("node:os");
5
+ const node_path_1 = require("node:path");
6
+ const node_fs_1 = require("node:fs");
7
+ // 已知 agent → skill 安装位置 <home>/<dir>/skills/jovida-cli/SKILL.md。
8
+ const AGENTS = [
9
+ { name: 'Codex', dir: '.codex' },
10
+ { name: 'Claude Code', dir: '.claude' }
11
+ ];
12
+ const SKILL_NAME = 'jovida-cli';
13
+ // 随包的 SKILL.md(包根)。dist/commands/skill.js → ../../SKILL.md;dev src/commands → 同样解析到仓根。
14
+ function skillSource() {
15
+ return (0, node_path_1.resolve)(__dirname, '..', '..', 'SKILL.md');
16
+ }
17
+ /**
18
+ * 把随 CLI 包发布的 SKILL.md 装 / 更新进 agent 的 skill 目录。
19
+ * 与 CLI 同一个 npm 版本 → 不漂移。`install` 与 `update` 行为相同(覆盖)。
20
+ */
21
+ function cmdSkill(sub, a) {
22
+ if (sub && sub !== 'install' && sub !== 'update') {
23
+ throw new Error('usage: jovida skill install (or: jovida skill update)');
24
+ }
25
+ const src = skillSource();
26
+ if (!(0, node_fs_1.existsSync)(src))
27
+ throw new Error(`bundled SKILL.md not found at ${src}`);
28
+ const installed = [];
29
+ const skipped = [];
30
+ for (const ag of AGENTS) {
31
+ const home = (0, node_path_1.join)((0, node_os_1.homedir)(), ag.dir);
32
+ if (!a.all && !(0, node_fs_1.existsSync)(home)) {
33
+ skipped.push(ag.name);
34
+ continue;
35
+ }
36
+ const dir = (0, node_path_1.join)(home, 'skills', SKILL_NAME);
37
+ (0, node_fs_1.mkdirSync)(dir, { recursive: true });
38
+ (0, node_fs_1.copyFileSync)(src, (0, node_path_1.join)(dir, 'SKILL.md'));
39
+ installed.push((0, node_path_1.join)(dir, 'SKILL.md'));
40
+ }
41
+ if (a.json) {
42
+ console.log(JSON.stringify({ installed, skipped }));
43
+ return;
44
+ }
45
+ if (installed.length === 0) {
46
+ console.log('No agents detected (~/.codex or ~/.claude). Re-run with --all to install for all known agents.');
47
+ return;
48
+ }
49
+ console.log('✓ skill installed/updated:');
50
+ for (const p of installed)
51
+ console.log(` ${p}`);
52
+ if (skipped.length)
53
+ console.log(`(skipped, not detected: ${skipped.join(', ')} — use --all to force)`);
54
+ }
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.cmdShow = cmdShow;
3
+ exports.cmdView = cmdView;
4
4
  const convert_1 = require("../core/convert");
5
5
  const shared_1 = require("./shared");
6
- async function cmdShow(ctx, a) {
6
+ async function cmdView(ctx, a) {
7
7
  if (!a.id)
8
- throw new Error('entry_id required: jovida show <entry_id>');
8
+ throw new Error('entry_id required: jovida view <entry_id>');
9
9
  const e = await (0, shared_1.fetchEntry)(ctx, a.id);
10
10
  const full = (0, convert_1.toFullTodo)(e);
11
11
  if (a.json) {
package/dist/config.js CHANGED
@@ -11,7 +11,8 @@ function loadConfig() {
11
11
  appId: process.env['JOVIDA_APP_ID'] || DEFAULT_APP_ID
12
12
  };
13
13
  }
14
- exports.APP_VERSION = '0.0.1';
14
+ // 单一版本来源:取 package.json,避免与 npm 版本双写漂移(dist/config.js 与 src/config.ts 都解析到包根)。
15
+ exports.APP_VERSION = require('../package.json').version;
15
16
  /** process.platform → Vita-Platform 值。 */
16
17
  function platformName() {
17
18
  if (process.platform === 'darwin')
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.maybeNotifyUpdate = maybeNotifyUpdate;
4
+ // 轻量更新提示:每天最多查一次 npm 最新版,仅交互终端(TTY)、走 stderr(不污染 --json stdout)、
5
+ // 超时 2s、**永不抛错也永不阻塞命令**。CI / NO_UPDATE_NOTIFIER / JOVIDA_NO_UPDATE_CHECK 关闭。
6
+ const state_1 = require("../state");
7
+ const PKG = '@fluxvita/jovida-cli';
8
+ const DAY = 86400;
9
+ const nowSec = () => Math.floor(Date.now() / 1000);
10
+ function isNewer(latest, current) {
11
+ const a = latest.split('.').map((n) => parseInt(n, 10));
12
+ const b = current.split('.').map((n) => parseInt(n, 10));
13
+ for (let i = 0; i < 3; i++) {
14
+ const x = a[i] || 0;
15
+ const y = b[i] || 0;
16
+ if (x > y)
17
+ return true;
18
+ if (x < y)
19
+ return false;
20
+ }
21
+ return false;
22
+ }
23
+ async function fetchLatest() {
24
+ try {
25
+ const ctrl = new AbortController();
26
+ const t = setTimeout(() => ctrl.abort(), 2000);
27
+ const res = await fetch(`https://registry.npmjs.org/${PKG}/latest`, { signal: ctrl.signal });
28
+ clearTimeout(t);
29
+ if (!res.ok)
30
+ return null;
31
+ const j = (await res.json());
32
+ return typeof j.version === 'string' ? j.version : null;
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ async function maybeNotifyUpdate(current) {
39
+ try {
40
+ if (!process.stderr.isTTY)
41
+ return; // 仅给人看;脚本/agent(非 TTY)不打扰
42
+ if (process.env['CI'] || process.env['JOVIDA_NO_UPDATE_CHECK'] || process.env['NO_UPDATE_NOTIFIER'])
43
+ return;
44
+ const { at, latest: cached } = (0, state_1.getUpdateCheck)();
45
+ let latest = cached;
46
+ if (!at || nowSec() - at > DAY) {
47
+ const fresh = await fetchLatest();
48
+ latest = fresh ?? cached;
49
+ (0, state_1.setUpdateCheck)(nowSec(), latest);
50
+ }
51
+ if (latest && isNewer(latest, current)) {
52
+ process.stderr.write(`\n Update available: ${current} → ${latest}\n` +
53
+ ` Run: npm i -g ${PKG}@latest (then: jovida skill update)\n\n`);
54
+ }
55
+ }
56
+ catch {
57
+ /* 更新检查永不影响命令 */
58
+ }
59
+ }
package/dist/state.js CHANGED
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getDeviceId = getDeviceId;
4
4
  exports.getLastServerVersion = getLastServerVersion;
5
5
  exports.setLastServerVersion = setLastServerVersion;
6
+ exports.getUpdateCheck = getUpdateCheck;
7
+ exports.setUpdateCheck = setUpdateCheck;
6
8
  exports.getToken = getToken;
7
9
  exports.setToken = setToken;
8
10
  exports.clearCredentials = clearCredentials;
@@ -53,6 +55,16 @@ function setLastServerVersion(v) {
53
55
  s.lastServerVersion = v;
54
56
  saveState(s);
55
57
  }
58
+ function getUpdateCheck() {
59
+ const s = loadState();
60
+ return { at: s.updateCheckAt ?? 0, latest: s.updateLatest ?? '' };
61
+ }
62
+ function setUpdateCheck(at, latest) {
63
+ const s = loadState();
64
+ s.updateCheckAt = at;
65
+ s.updateLatest = latest;
66
+ saveState(s);
67
+ }
56
68
  function readCreds() {
57
69
  return readJson(CRED);
58
70
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluxvita/jovida-cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Jovida Daily CLI — capture and manage your todos from the command line and AI agents",
5
5
  "license": "MIT",
6
6
  "bin": {