calendit 1.0.2 → 2026.4.26
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 +81 -62
- package/dist/commands/accounts.d.ts +4 -0
- package/dist/commands/accounts.js +26 -0
- package/dist/commands/add.js +8 -0
- package/dist/commands/apply.js +5 -1
- package/dist/commands/auth.js +11 -2
- package/dist/commands/config.js +50 -16
- package/dist/commands/macos.d.ts +3 -0
- package/dist/commands/macos.js +401 -0
- package/dist/commands/onboard.d.ts +2 -0
- package/dist/commands/onboard.js +79 -0
- package/dist/commands/query.js +2 -2
- package/dist/commands/shared.d.ts +3 -2
- package/dist/commands/shared.js +21 -5
- package/dist/core/accountStatus.d.ts +18 -0
- package/dist/core/accountStatus.js +74 -0
- package/dist/core/auth.js +7 -11
- package/dist/core/authStatus.d.ts +20 -0
- package/dist/core/authStatus.js +82 -0
- package/dist/core/config.d.ts +11 -1
- package/dist/core/config.js +73 -6
- package/dist/core/datetime.js +3 -2
- package/dist/core/errors.d.ts +3 -0
- package/dist/core/errors.js +5 -0
- package/dist/core/eventkitBridgeFetch.d.ts +26 -0
- package/dist/core/eventkitBridgeFetch.js +159 -0
- package/dist/core/eventkitEnvFromConfig.d.ts +7 -0
- package/dist/core/eventkitEnvFromConfig.js +24 -0
- package/dist/core/eventkitHelper.d.ts +50 -0
- package/dist/core/eventkitHelper.js +336 -0
- package/dist/core/formatter.d.ts +41 -0
- package/dist/core/formatter.js +79 -0
- package/dist/core/i18n.d.ts +7 -0
- package/dist/core/i18n.js +52 -0
- package/dist/core/localeBootstrap.d.ts +12 -0
- package/dist/core/localeBootstrap.js +74 -0
- package/dist/core/logger.d.ts +2 -0
- package/dist/core/logger.js +5 -0
- package/dist/core/macosBridgeApp.d.ts +12 -0
- package/dist/core/macosBridgeApp.js +83 -0
- package/dist/core/macosTerminalRelay.d.ts +12 -0
- package/dist/core/macosTerminalRelay.js +62 -0
- package/dist/generated/locale-keys.d.ts +3 -0
- package/dist/generated/locale-keys.js +90 -0
- package/dist/index.js +103 -18
- package/dist/locales/en.json +128 -0
- package/dist/locales/ja.json +128 -0
- package/dist/services/macos.d.ts +14 -0
- package/dist/services/macos.js +115 -0
- package/dist/test_runner.js +11 -2
- package/dist/types/index.d.ts +12 -1
- package/package.json +16 -5
package/README.md
CHANGED
|
@@ -1,94 +1,113 @@
|
|
|
1
|
-
# calendit
|
|
1
|
+
# calendit
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
人間のための Markdown 管理と、AI エージェントのための JSON 管理を両立します。
|
|
3
|
+
**ターミナルから** Google カレンダー、Microsoft Outlook(Graph)、および **この Mac のカレンダー(EventKit)** の予定を、**調べる・追加する・Markdown などのファイルと同期する** ためのコマンドラインツール(CLI)です。
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
ライセンス: **ISC**([LICENSE](LICENSE))。
|
|
6
|
+
作者: **chromatribe - s.ohara**(ivis.klain@chromatri.be)
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
**npm でグローバル導入した直後(AI / Cursor の初回ラリー用)** — 同梱されない `docs/` を **GitHub 上の次の 1 本**から辿ってください(**リポジトリを clone していない場合も有効**):
|
|
10
9
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- **マルチアカウント対応**: 複数の Google / Outlook アカウントを個別に認証し、シームレスに使い分け可能。
|
|
14
|
-
- **堅牢なエラーハンドリング**: 詳細なエラーメッセージと改善のためのヒントを提供。
|
|
15
|
-
- **自律的テスティング**: 期待動作をドキュメント化し、AI が自ら検証・修正を行う高度な開発フロー。
|
|
16
|
-
- **macOS 最適化**: 永続的なトークン管理とローカル時刻の完全サポート。
|
|
10
|
+
- **初回ラリー(対話用プレイブック):** <https://github.com/chromatribe/calendit/blob/main/docs/ai-onboarding-rally.md>
|
|
11
|
+
- **生テキスト URL:** <https://raw.githubusercontent.com/chromatribe/calendit/main/docs/ai-onboarding-rally.md>
|
|
17
12
|
|
|
18
|
-
|
|
13
|
+
`npm install` 直後のターミナルにも、同じ URL を 1 行表示する `postinstall` があります(`package.json`)。
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
---
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
git clone https://github.com/YOUR_USERNAME/calendit.git
|
|
24
|
-
cd calendit
|
|
25
|
-
npm install
|
|
26
|
-
npm run build
|
|
27
|
-
# パスを通す(任意)
|
|
28
|
-
npm link
|
|
29
|
-
```
|
|
17
|
+
## 目的(なぜこのツールがあるか)
|
|
30
18
|
|
|
31
|
-
|
|
19
|
+
1. **人間**が、エディタとターミナルだけで予定をファイル(Markdown 等)として扱えるようにする。
|
|
20
|
+
2. **AI エージェント**(Cursor、Antigravity、GitHub Copilot、その他「ターミナルでコマンドを実行できる」開発支援ツール)が、**同じコマンド**を繰り返し安全に実行できるようにする。
|
|
21
|
+
3. **複数のカレンダー環境**(仕事用 Google、個人用 Outlook、Mac ローカルなど)を **名前付きコンテキスト** で切り替える。
|
|
32
22
|
|
|
33
|
-
|
|
23
|
+
---
|
|
34
24
|
|
|
35
|
-
|
|
36
|
-
- **Outlook**: [docs/setup_outlook.md](docs/setup_outlook.md)
|
|
25
|
+
## 主な機能
|
|
37
26
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
27
|
+
| 機能 | 説明 |
|
|
28
|
+
|------|------|
|
|
29
|
+
| **query** | 指定期間の予定を **Markdown / CSV / JSON** で表示またはファイル出力 |
|
|
30
|
+
| **apply** | ファイルの内容をカレンダーに反映(ID があれば更新、なければ新規。**同期モード**でファイルに無い予定の削除も可) |
|
|
31
|
+
| **add** | 1 件の予定を対話なしで追加(`--dry-run` で確認のみ) |
|
|
32
|
+
| **cal** | カレンダー一覧・作成・削除(macOS コンテキストでは一部未対応) |
|
|
33
|
+
| **config** | API クレデンシャル、コンテキスト、UI 言語の保存 |
|
|
34
|
+
| **auth / accounts** | OAuth ログイン、全コンテキストの **接続状態一覧** |
|
|
35
|
+
| **macos** | EventKit 診断(`doctor`)、カレンダー一覧、IDE 向け **Terminal.app 委譲**(`external`) |
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
calendit config set-outlook --id YOUR_CLIENT_ID
|
|
45
|
-
calendit auth login outlook
|
|
46
|
-
```
|
|
37
|
+
---
|
|
47
38
|
|
|
48
|
-
|
|
39
|
+
## ドキュメント(必読の順)
|
|
49
40
|
|
|
50
|
-
|
|
41
|
+
**非エンジニアでも動かせる手順**から、**コマンド一覧**、**AI 向けの構造化情報**まで、すべて **`docs/`** にあります。
|
|
51
42
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
43
|
+
| 順 | 文書 | 内容 |
|
|
44
|
+
|----|------|------|
|
|
45
|
+
| 0 | **初回ラリー**(npm `-g` では `docs` 未同梱): **[ai-onboarding-rally.md on GitHub](https://github.com/chromatribe/calendit/blob/main/docs/ai-onboarding-rally.md)** | **AI / Cursor 向け:** Google / Outlook / macOS の**分岐**と、次のコマンドの幹。リポ clone なしで参照可 |
|
|
46
|
+
| 1 | **[docs/README.md](docs/README.md)** | `docs/` 全体の目次(**リポジトリを手元に clone した人向け**相対パス) |
|
|
47
|
+
| 1b | **[docs/beginner-guide-ja.md](docs/beginner-guide-ja.md)** | **非エンジニア向け(日本語):** 導入 → 起動 → 登録に集中した全体の地図(詳細は `getting-started` へ) |
|
|
48
|
+
| 2 | **[docs/getting-started.md](docs/getting-started.md)** | **省略なし:** Node の確認 → 取得 → ビルド → Google / Outlook / macOS のどれかでログイン → カレンダー登録 → 動作確認 → `npm test` |
|
|
49
|
+
| 3 | **[docs/commands.md](docs/commands.md)** | **コマンド早見表** + 各サブコマンドのオプション |
|
|
50
|
+
| 4 | **[docs/for-ai-agents.md](docs/for-ai-agents.md)** | リポジトリ構成、環境変数、テスト契約、OAuth の落とし穴 |
|
|
56
51
|
|
|
57
|
-
|
|
52
|
+
プロバイダ別のクラウドコンソール操作:
|
|
58
53
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
calendit query --set work --format md --out today.md
|
|
54
|
+
- [docs/setup_google.md](docs/setup_google.md)
|
|
55
|
+
- [docs/setup_outlook.md](docs/setup_outlook.md)
|
|
62
56
|
|
|
63
|
-
|
|
64
|
-
# --dry-run で変更内容を事前に確認できます
|
|
65
|
-
calendit apply --in today.md --dry-run
|
|
57
|
+
macOS 常駐ブリッジ(上級):
|
|
66
58
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
59
|
+
- [docs/eventkit-bridge.md](docs/eventkit-bridge.md)
|
|
60
|
+
- [native/eventkit-helper/README.md](native/eventkit-helper/README.md)
|
|
61
|
+
- [native/eventkit-bridge/README.md](native/eventkit-bridge/README.md)
|
|
62
|
+
|
|
63
|
+
開発・テストの内部仕様:
|
|
70
64
|
|
|
71
|
-
|
|
65
|
+
- [docs/ux-evaluation.md](docs/ux-evaluation.md)(**UX 検証**の始め方: `npm test` → `npm run ux:link` 等)
|
|
66
|
+
- [docs/development.md](docs/development.md)
|
|
67
|
+
- [docs/tests.md](docs/tests.md)
|
|
72
68
|
|
|
73
|
-
|
|
74
|
-
- [Google カレンダー セットアップガイド](docs/setup_google.md)
|
|
75
|
-
- [Outlook カレンダー セットアップガイド](docs/setup_outlook.md)
|
|
76
|
-
- [開発者向けガイド (テスト・設計)](docs/development.md)
|
|
69
|
+
---
|
|
77
70
|
|
|
78
|
-
##
|
|
71
|
+
## 開発環境向けクイックコマンド(最短)
|
|
79
72
|
|
|
80
|
-
|
|
73
|
+
**前提:** Node.js **18 以上**、Git。
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/chromatribe/calendit.git
|
|
77
|
+
cd calendit
|
|
78
|
+
npm ci # 失敗したら npm install
|
|
79
|
+
npm run build
|
|
80
|
+
npm run ux:link # ビルド + npm link(`npm link` だけでも可)
|
|
81
|
+
calendit --version
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
以降の **人間向けの詳細な手順** は **[docs/getting-started.md](docs/getting-started.md)** に集約しています(**ここでは省略しません**。README からリンク先へ誘導します)。
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 自動テスト
|
|
81
89
|
|
|
82
90
|
```bash
|
|
83
91
|
npm test
|
|
84
92
|
```
|
|
85
93
|
|
|
86
|
-
`docs/tests.md`
|
|
94
|
+
`docs/tests.md` に定義されたケースを実行します(実カレンダーは触らない **モック** が既定)。
|
|
95
|
+
|
|
96
|
+
---
|
|
87
97
|
|
|
88
|
-
##
|
|
98
|
+
## その他おすすめ記載(本 README で触れる項目)
|
|
89
99
|
|
|
90
|
-
|
|
100
|
+
| 項目 | 参照先 |
|
|
101
|
+
|------|--------|
|
|
102
|
+
| 仕様の要約 | [spec/spec.md](spec/spec.md) |
|
|
103
|
+
| 版ごとの変更履歴 | [spec/history/](spec/history/) |
|
|
104
|
+
| ロードマップ | [docs/roadmap.md](docs/roadmap.md) |
|
|
105
|
+
| 手動スモーク(実 API) | [docs/manual-local-smoke.md](docs/manual-local-smoke.md) |
|
|
106
|
+
| 変更ログ(ドキュメント) | [docs/changelog.md](docs/changelog.md) |
|
|
91
107
|
|
|
92
|
-
|
|
108
|
+
---
|
|
93
109
|
|
|
94
|
-
|
|
110
|
+
## バッジ
|
|
111
|
+
|
|
112
|
+
[](https://opensource.org/licenses/ISC)
|
|
113
|
+
[](https://www.typescriptlang.org/)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { loadConfigIfExists } from "./shared.js";
|
|
2
|
+
import { writeStdoutLine } from "../core/logger.js";
|
|
3
|
+
import { buildAccountStatusRows, formatAccountStatusTable } from "../core/accountStatus.js";
|
|
4
|
+
export async function printAccountStatusTable(deps) {
|
|
5
|
+
await loadConfigIfExists(deps.config);
|
|
6
|
+
const contexts = deps.config.getAllContexts();
|
|
7
|
+
const names = Object.keys(contexts);
|
|
8
|
+
if (names.length === 0) {
|
|
9
|
+
writeStdoutLine("コンテキストが定義されていません。`calendit config set-context` で追加してください。");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const rows = await buildAccountStatusRows(contexts, {
|
|
13
|
+
auth: deps.auth,
|
|
14
|
+
outlookCreds: deps.config.getOutlookCreds(),
|
|
15
|
+
});
|
|
16
|
+
writeStdoutLine(formatAccountStatusTable(rows));
|
|
17
|
+
}
|
|
18
|
+
export function registerAccountsCommands(program, deps) {
|
|
19
|
+
const accountsCmd = program.command("accounts").description("Calendar accounts and connection status (all services)");
|
|
20
|
+
accountsCmd
|
|
21
|
+
.command("status")
|
|
22
|
+
.description("Show each context's service, calendar, account, and connection state (google / outlook / macos)")
|
|
23
|
+
.action(async () => {
|
|
24
|
+
await printAccountStatusTable(deps);
|
|
25
|
+
});
|
|
26
|
+
}
|
package/dist/commands/add.js
CHANGED
|
@@ -12,6 +12,7 @@ export function registerAddCommand(program, deps) {
|
|
|
12
12
|
.option("--end <dateTime>", "End date/time")
|
|
13
13
|
.option("--location <text>", "Event location")
|
|
14
14
|
.option("--description <text>", "Event description")
|
|
15
|
+
.option("--attendees <emails>", "Comma-separated attendee emails (macOS EventKit: may be ignored depending on OS)")
|
|
15
16
|
.option("--set <name>", "Use a named context")
|
|
16
17
|
.option("--calendar <id>", "Explicit Calendar ID")
|
|
17
18
|
.option("--dry-run", "Preview addition without applying", false)
|
|
@@ -32,6 +33,12 @@ export function registerAddCommand(program, deps) {
|
|
|
32
33
|
}
|
|
33
34
|
const formattedStart = formatInTimeZone(startDate, timeZone, "yyyy-MM-dd'T'HH:mm:ssXXX");
|
|
34
35
|
const formattedEnd = formatInTimeZone(endDate, timeZone, "yyyy-MM-dd'T'HH:mm:ssXXX");
|
|
36
|
+
const attendeeList = options.attendees
|
|
37
|
+
? options.attendees
|
|
38
|
+
.split(",")
|
|
39
|
+
.map((s) => s.trim())
|
|
40
|
+
.filter(Boolean)
|
|
41
|
+
: undefined;
|
|
35
42
|
logger.info(`Adding event: ${options.summary}`);
|
|
36
43
|
logger.info(`Start: ${formattedStart}`);
|
|
37
44
|
logger.info(`End: ${formattedEnd}`);
|
|
@@ -45,6 +52,7 @@ export function registerAddCommand(program, deps) {
|
|
|
45
52
|
end: formattedEnd,
|
|
46
53
|
location: options.location,
|
|
47
54
|
description: options.description,
|
|
55
|
+
...(attendeeList?.length ? { attendees: attendeeList } : {}),
|
|
48
56
|
});
|
|
49
57
|
logger.info(`✅ Event added successfully: "${options.summary}"`);
|
|
50
58
|
});
|
package/dist/commands/apply.js
CHANGED
|
@@ -33,7 +33,11 @@ export function registerApplyCommand(program, deps) {
|
|
|
33
33
|
inputEvents = Formatter.fromCsv(inputData);
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
|
-
|
|
36
|
+
const parsed = Formatter.fromJson(inputData);
|
|
37
|
+
if (parsed.warnings.length > 0) {
|
|
38
|
+
parsed.warnings.forEach((w) => logger.warn(w));
|
|
39
|
+
}
|
|
40
|
+
inputEvents = parsed.events;
|
|
37
41
|
}
|
|
38
42
|
const applier = new Applier(service);
|
|
39
43
|
if (options.dryRun) {
|
package/dist/commands/auth.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { loadConfigIfExists } from "./shared.js";
|
|
2
2
|
import { ConfigError, ValidationError } from "../core/errors.js";
|
|
3
3
|
import { logger } from "../core/logger.js";
|
|
4
|
+
import { t } from "../core/i18n.js";
|
|
5
|
+
import { printAccountStatusTable } from "./accounts.js";
|
|
4
6
|
export function registerAuthCommands(program, deps) {
|
|
5
7
|
const authCmd = program.command("auth").description("Authentication management");
|
|
8
|
+
authCmd
|
|
9
|
+
.command("status")
|
|
10
|
+
.description("Same as `calendit accounts status` (deprecated name; use accounts status for clarity)")
|
|
11
|
+
.action(async () => {
|
|
12
|
+
logger.info("推奨: 全サービス横断の状態は `calendit accounts status` を使用してください。");
|
|
13
|
+
await printAccountStatusTable(deps);
|
|
14
|
+
});
|
|
6
15
|
authCmd
|
|
7
16
|
.command("login <service>")
|
|
8
17
|
.description("Login to Google or Outlook")
|
|
@@ -14,7 +23,7 @@ export function registerAuthCommands(program, deps) {
|
|
|
14
23
|
if (service === "google") {
|
|
15
24
|
const creds = deps.config.getGoogleCreds();
|
|
16
25
|
if (!creds) {
|
|
17
|
-
throw new ConfigError("
|
|
26
|
+
throw new ConfigError(t("errors.service.googleCredsNotSet"), t("errors.auth.googleLoginHint"));
|
|
18
27
|
}
|
|
19
28
|
logger.info("Google 認証フローを開始します...");
|
|
20
29
|
await deps.auth.loginGoogle(creds.id, creds.secret, accountId);
|
|
@@ -24,7 +33,7 @@ export function registerAuthCommands(program, deps) {
|
|
|
24
33
|
if (service === "outlook") {
|
|
25
34
|
const creds = deps.config.getOutlookCreds();
|
|
26
35
|
if (!creds) {
|
|
27
|
-
throw new ConfigError("
|
|
36
|
+
throw new ConfigError(t("errors.service.outlookCredsNotSet"), t("errors.service.outlookCredsNotSetHint"));
|
|
28
37
|
}
|
|
29
38
|
logger.info("Outlook 認証フローを開始します...");
|
|
30
39
|
await deps.auth.loginOutlook(creds.id, creds.tenantId, accountId);
|
package/dist/commands/config.js
CHANGED
|
@@ -2,8 +2,22 @@ import * as fs from "fs/promises";
|
|
|
2
2
|
import { loadConfigIfExists } from "./shared.js";
|
|
3
3
|
import { ValidationError } from "../core/errors.js";
|
|
4
4
|
import { logger } from "../core/logger.js";
|
|
5
|
+
import { isUiLocale, t } from "../core/i18n.js";
|
|
5
6
|
export function registerConfigCommands(program, deps) {
|
|
6
7
|
const configCmd = program.command("config").description("Configuration management");
|
|
8
|
+
configCmd
|
|
9
|
+
.command("set-locale <code>")
|
|
10
|
+
.description("Set UI language (en or ja) and persist to config")
|
|
11
|
+
.action(async (code) => {
|
|
12
|
+
await loadConfigIfExists(deps.config);
|
|
13
|
+
const c = code.trim().toLowerCase();
|
|
14
|
+
if (!isUiLocale(c)) {
|
|
15
|
+
throw new ValidationError(`Invalid locale: ${code}`, "Use en or ja.");
|
|
16
|
+
}
|
|
17
|
+
deps.config.setUi({ locale: c, localePromptCompleted: true });
|
|
18
|
+
await deps.config.save();
|
|
19
|
+
logger.info(t("config.cmd.localeSet", { locale: c }));
|
|
20
|
+
});
|
|
7
21
|
configCmd
|
|
8
22
|
.command("set-google")
|
|
9
23
|
.description("Set Google API credentials (manually or via JSON file)")
|
|
@@ -35,7 +49,7 @@ export function registerConfigCommands(program, deps) {
|
|
|
35
49
|
}
|
|
36
50
|
deps.config.setGoogleCreds(id, secret);
|
|
37
51
|
await deps.config.save();
|
|
38
|
-
logger.info("
|
|
52
|
+
logger.info(t("config.cmd.googleSaved"));
|
|
39
53
|
});
|
|
40
54
|
configCmd
|
|
41
55
|
.command("set-outlook")
|
|
@@ -46,7 +60,7 @@ export function registerConfigCommands(program, deps) {
|
|
|
46
60
|
await loadConfigIfExists(deps.config);
|
|
47
61
|
deps.config.setOutlookCreds(options.id, options.tenant);
|
|
48
62
|
await deps.config.save();
|
|
49
|
-
logger.info("
|
|
63
|
+
logger.info(t("config.cmd.outlookSaved"));
|
|
50
64
|
});
|
|
51
65
|
configCmd
|
|
52
66
|
.command("check")
|
|
@@ -58,13 +72,33 @@ export function registerConfigCommands(program, deps) {
|
|
|
58
72
|
const contexts = deps.config.getAllContexts();
|
|
59
73
|
const contextEntries = Object.entries(contexts);
|
|
60
74
|
const mask = (value) => (value.length <= 8 ? value : `${value.slice(0, 3)}...${value.slice(-3)}`);
|
|
61
|
-
logger.info("
|
|
62
|
-
logger.info(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
logger.info(t("config.check.header"));
|
|
76
|
+
logger.info(googleCreds
|
|
77
|
+
? t("config.check.googleOk", { mask: mask(googleCreds.id) })
|
|
78
|
+
: t("config.check.googleNotSet"));
|
|
79
|
+
logger.info(outlookCreds
|
|
80
|
+
? t("config.check.outlookOk", { mask: mask(outlookCreds.id) })
|
|
81
|
+
: t("config.check.outlookNotSet"));
|
|
82
|
+
logger.info(contextEntries.length > 0
|
|
83
|
+
? t("config.check.contexts", {
|
|
84
|
+
list: contextEntries.map(([name, ctx]) => `${name} (${ctx.service}/${ctx.calendarId})`).join(", "),
|
|
85
|
+
})
|
|
86
|
+
: t("config.check.contextsNone"));
|
|
87
|
+
logger.info(t("config.check.fileLine"));
|
|
88
|
+
logger.info(t("config.check.uiLocale", { locale: deps.config.getUi()?.locale ?? "en" }));
|
|
89
|
+
});
|
|
90
|
+
configCmd
|
|
91
|
+
.command("set-macos-transport <value>")
|
|
92
|
+
.description("Persist default EventKit transport: auto, bridge, or helper (used when CALENDIT_EVENTKIT_BRIDGE is unset in the shell)")
|
|
93
|
+
.action(async (value) => {
|
|
94
|
+
await loadConfigIfExists(deps.config);
|
|
95
|
+
const v = value.trim().toLowerCase();
|
|
96
|
+
if (v !== "auto" && v !== "bridge" && v !== "helper") {
|
|
97
|
+
throw new ValidationError("Value must be auto, bridge, or helper.");
|
|
98
|
+
}
|
|
99
|
+
deps.config.setEventkitDefaultTransport(v);
|
|
100
|
+
await deps.config.save();
|
|
101
|
+
logger.info(t("config.cmd.macosTransportSet", { value: v }));
|
|
68
102
|
});
|
|
69
103
|
configCmd
|
|
70
104
|
.command("delete-context <name>")
|
|
@@ -73,21 +107,21 @@ export function registerConfigCommands(program, deps) {
|
|
|
73
107
|
await loadConfigIfExists(deps.config);
|
|
74
108
|
const deleted = deps.config.deleteContext(name);
|
|
75
109
|
if (!deleted) {
|
|
76
|
-
throw new ValidationError(
|
|
110
|
+
throw new ValidationError(t("errors.context.missing", { name }), t("errors.context.missingHint", { name }));
|
|
77
111
|
}
|
|
78
112
|
await deps.config.save();
|
|
79
|
-
logger.info(
|
|
113
|
+
logger.info(t("config.cmd.contextDeleted", { name }));
|
|
80
114
|
});
|
|
81
115
|
configCmd
|
|
82
116
|
.command("set-context <name>")
|
|
83
117
|
.description("Set a named context (e.g. work, hobby)")
|
|
84
|
-
.requiredOption("--service <service>", "google or
|
|
85
|
-
.requiredOption("--calendar <id>", "Calendar ID")
|
|
118
|
+
.requiredOption("--service <service>", "google, outlook, or macos")
|
|
119
|
+
.requiredOption("--calendar <id>", "Calendar ID (for macos: EventKit calendarIdentifier from `calendit macos list-calendars`)")
|
|
86
120
|
.option("--account <id>", "Custom account identifier for tokens")
|
|
87
121
|
.action(async (name, options) => {
|
|
88
122
|
await loadConfigIfExists(deps.config);
|
|
89
|
-
if (options.service !== "google" && options.service !== "outlook") {
|
|
90
|
-
throw new ValidationError("Service must be 'google' or '
|
|
123
|
+
if (options.service !== "google" && options.service !== "outlook" && options.service !== "macos") {
|
|
124
|
+
throw new ValidationError("Service must be 'google', 'outlook', or 'macos'.");
|
|
91
125
|
}
|
|
92
126
|
deps.config.setContext(name, {
|
|
93
127
|
service: options.service,
|
|
@@ -95,6 +129,6 @@ export function registerConfigCommands(program, deps) {
|
|
|
95
129
|
accountId: options.account,
|
|
96
130
|
});
|
|
97
131
|
await deps.config.save();
|
|
98
|
-
logger.info(
|
|
132
|
+
logger.info(t("config.cmd.contextSaved", { name }));
|
|
99
133
|
});
|
|
100
134
|
}
|