calendit 1.0.3 → 1.20260425.6

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.
Files changed (61) hide show
  1. package/README.md +65 -62
  2. package/dist/commands/accounts.d.ts +4 -0
  3. package/dist/commands/accounts.js +26 -0
  4. package/dist/commands/add.js +8 -0
  5. package/dist/commands/apply.js +16 -2
  6. package/dist/commands/auth.js +11 -2
  7. package/dist/commands/config.js +50 -16
  8. package/dist/commands/macos.d.ts +3 -0
  9. package/dist/commands/macos.js +401 -0
  10. package/dist/commands/onboard.d.ts +2 -0
  11. package/dist/commands/onboard.js +79 -0
  12. package/dist/commands/query.js +4 -24
  13. package/dist/commands/shared.d.ts +3 -2
  14. package/dist/commands/shared.js +21 -5
  15. package/dist/core/accountStatus.d.ts +18 -0
  16. package/dist/core/accountStatus.js +74 -0
  17. package/dist/core/applier.js +4 -26
  18. package/dist/core/auth.js +7 -11
  19. package/dist/core/authStatus.d.ts +20 -0
  20. package/dist/core/authStatus.js +82 -0
  21. package/dist/core/config.d.ts +11 -1
  22. package/dist/core/config.js +73 -6
  23. package/dist/core/datetime.js +3 -2
  24. package/dist/core/errors.d.ts +3 -0
  25. package/dist/core/errors.js +5 -0
  26. package/dist/core/eventkitBridgeFetch.d.ts +26 -0
  27. package/dist/core/eventkitBridgeFetch.js +159 -0
  28. package/dist/core/eventkitEnvFromConfig.d.ts +7 -0
  29. package/dist/core/eventkitEnvFromConfig.js +24 -0
  30. package/dist/core/eventkitHelper.d.ts +50 -0
  31. package/dist/core/eventkitHelper.js +336 -0
  32. package/dist/core/formatter.d.ts +41 -0
  33. package/dist/core/formatter.js +79 -0
  34. package/dist/core/i18n.d.ts +7 -0
  35. package/dist/core/i18n.js +52 -0
  36. package/dist/core/localeBootstrap.d.ts +12 -0
  37. package/dist/core/localeBootstrap.js +74 -0
  38. package/dist/core/logger.d.ts +2 -0
  39. package/dist/core/logger.js +5 -0
  40. package/dist/core/macosBridgeApp.d.ts +12 -0
  41. package/dist/core/macosBridgeApp.js +83 -0
  42. package/dist/core/macosTerminalRelay.d.ts +12 -0
  43. package/dist/core/macosTerminalRelay.js +62 -0
  44. package/dist/core/outlookCalendarList.d.ts +15 -0
  45. package/dist/core/outlookCalendarList.js +27 -0
  46. package/dist/core/timeRangeForQuery.d.ts +39 -0
  47. package/dist/core/timeRangeForQuery.js +78 -0
  48. package/dist/generated/locale-keys.d.ts +3 -0
  49. package/dist/generated/locale-keys.js +90 -0
  50. package/dist/index.js +99 -17
  51. package/dist/locales/en.json +128 -0
  52. package/dist/locales/ja.json +128 -0
  53. package/dist/services/google.d.ts +1 -0
  54. package/dist/services/google.js +9 -4
  55. package/dist/services/macos.d.ts +14 -0
  56. package/dist/services/macos.js +115 -0
  57. package/dist/services/outlook.d.ts +5 -0
  58. package/dist/services/outlook.js +30 -10
  59. package/dist/test_runner.js +11 -2
  60. package/dist/types/index.d.ts +12 -1
  61. package/package.json +16 -5
package/README.md CHANGED
@@ -1,94 +1,97 @@
1
- # calendit 🗓️
1
+ <div align="center">
2
+ <h1>🗓️ calendit</h1>
3
+ <p><b>ターミナルから Google / Outlook / Mac のカレンダーを自在に操るCLIツール</b></p>
4
+
5
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
7
+ [![Node.js](https://img.shields.io/badge/Node.js-18+-green.svg)](https://nodejs.org/)
8
+ [![npm version](https://img.shields.io/npm/v/calendit.svg)](https://www.npmjs.com/package/calendit)
9
+ </div>
2
10
 
3
- ターミナルから Google / Outlook カレンダーを自在に操るための CLI ツール。
4
- 人間のための Markdown 管理と、AI エージェントのための JSON 管理を両立します。
11
+ <br>
5
12
 
6
- [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
7
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
13
+ > **calendit (カレンディット)** は、黒い画面(ターミナル)から、あなたのカレンダーの予定を調べたり、追加したり、Markdownなどのファイルと同期するためのコマンドラインツールです。
14
+ > 人間にとっても、AIエージェントにとっても、使いやすく設計されています。
8
15
 
9
- ## 🌟 特徴
16
+ ## 何ができるの?(主な機能)
10
17
 
11
- - **Markdown 同期**: カレンダーの予定を Markdown で書き出し、メモ帳感覚で編集して一括反映。
12
- - **コンテキスト管理**: 「仕事」「プライベート」などの用途(カレンダーID、認証アカウント)を `--set` 一つで切り替え。
13
- - **マルチアカウント対応**: 複数の Google / Outlook アカウントを個別に認証し、シームレスに使い分け可能。
14
- - **堅牢なエラーハンドリング**: 詳細なエラーメッセージと改善のためのヒントを提供。
15
- - **自律的テスティング**: 期待動作をドキュメント化し、AI が自ら検証・修正を行う高度な開発フロー。
16
- - **macOS 最適化**: 永続的なトークン管理とローカル時刻の完全サポート。
18
+ - 🔍 **予定の確認**: 指定した期間の予定を **Markdown**, **CSV**, **JSON** 形式でスッと表示できます。
19
+ - 📝 **予定の一括反映**: テキストファイルに書いた予定を、そのままカレンダーにまとめて登録・更新できます。
20
+ - ⚡️ **サクッと追加**: `--summary "ランチ" --start "12:00"` のように1行で予定を追加できます。
21
+ - 🔄 **アカウント切り替え**: 仕事用のGoogle、個人のOutlook、このMac専用のカレンダーなどを、名前(コンテキスト)で簡単に使い分けられます。
22
+ - 🛡️ **安全設計**: 間違えてカレンダーを消さないよう、変更前の確認や `--dry-run`(テスト実行)が用意されています。
17
23
 
18
- ## 🚀 クイックスタート
24
+ ---
25
+
26
+ ## 🧍 初めての方・一般ユーザー向け
27
+
28
+ プログラムの知識がなくても大丈夫です。以下の**3ステップ**で使い始められます!
19
29
 
20
30
  ### 1. インストール
31
+ Node.js (バージョン18以上) が入っているパソコンのターミナルで、次のコマンドを実行するだけです。
21
32
 
22
33
  ```bash
23
- git clone https://github.com/YOUR_USERNAME/calendit.git
24
- cd calendit
25
- npm install
26
- npm run build
27
- # パスを通す(任意)
28
- npm link
34
+ npm install -g calendit
29
35
  ```
30
36
 
31
- ### 2. 認証設定
32
-
33
- 各サービスのセットアップガイドに従って、API 認証情報を設定します。
37
+ > [!TIP]
38
+ > インストールができたら `calendit --version` で動作を確認してみましょう。
34
39
 
35
- - **Google**: [docs/setup_google.md](docs/setup_google.md)
36
- - **Outlook**: [docs/setup_outlook.md](docs/setup_outlook.md)
37
-
38
- ```bash
39
- # Google の設定例
40
- calendit config set-google --id YOUR_CLIENT_ID --secret YOUR_CLIENT_SECRET
41
- calendit auth login google
42
-
43
- # Outlook の設定例
44
- calendit config set-outlook --id YOUR_CLIENT_ID
45
- calendit auth login outlook
46
- ```
40
+ ### 2. 初期設定(使い方ガイド)
41
+ インストールが終わったら、どのカレンダーと繋ぐかを設定します。
42
+ 図解付きで分かりやすく解説した**初心者向けガイド**をご用意しました。こちらを見ながら進めてください!
47
43
 
48
- ### 3. コンテキストの設定
44
+ 👉 **[初心者向けガイド(日本語)を読む](docs/beginner-guide-ja.md)**
49
45
 
50
- 用途に応じたカレンダーを「コンテキスト」として登録します。
46
+ ---
51
47
 
52
- ```bash
53
- # 仕事用カレンダーの登録
54
- calendit config set-context work --service google --calendar primary
55
- ```
48
+ ## 🤖 AI・エージェント向け(Cursor / GitHub Copilot 等)
56
49
 
57
- ### 4. 基本操作
50
+ AIエージェント(LLM)が自律的にカレンダーを操作する能力を持たせたい場合は、以下のドキュメントを参照してください。
58
51
 
59
- ```bash
60
- # 今日の予定を Markdown に書き出す
61
- calendit query --set work --format md --out today.md
52
+ - **[AI・エージェント向け 初回オンボーディングラリー](docs/ai-onboarding-rally.md)**
53
+ - エージェントが最初に読み込むべき「対話用プレイブック」です。
54
+ - **[AIエージェント向けリファレンス](docs/for-ai-agents.md)**
55
+ - リポジトリの構成、環境変数、テストの仕組み、OAuthの注意点など、機械可読な構造化情報を提供しています。
62
56
 
63
- # 予定をカレンダーに反映(新規作成・更新)
64
- # --dry-run で変更内容を事前に確認できます
65
- calendit apply --in today.md --dry-run
57
+ ---
66
58
 
67
- # 単発の予定を追加
68
- calendit add --summary "ランチミーティング" --start "today 12:00" --set work
69
- ```
59
+ ## 💻 開発者向け
70
60
 
71
- ## 📖 詳細ドキュメント
61
+ 本リポジトリのソースコードを利用して、自分でビルドしたり開発に参加したりする場合はこちら。
72
62
 
73
- - [コマンドリファレンス](docs/commands.md)
74
- - [Google カレンダー セットアップガイド](docs/setup_google.md)
75
- - [Outlook カレンダー セットアップガイド](docs/setup_outlook.md)
76
- - [開発者向けガイド (テスト・設計)](docs/development.md)
63
+ <details>
64
+ <summary><b>開発環境のセットアップ(クリックして展開)</b></summary>
77
65
 
78
- ## 🛠️ 開発者向け
66
+ Node.js 18以上とGitが必要です。
79
67
 
80
- ### テストの実行
68
+ ```bash
69
+ git clone https://github.com/chromatribe/calendit.git
70
+ cd calendit
71
+ npm ci
72
+ npm run build
73
+ npm run ux:link
74
+ calendit --version
75
+ ```
76
+ > 以降のより詳細な手順は [getting-started.md](docs/getting-started.md) に集約しています。
81
77
 
78
+ ### 自動テスト
82
79
  ```bash
83
80
  npm test
84
81
  ```
82
+ </details>
83
+
84
+ ---
85
85
 
86
- `docs/tests.md` に定義されたテストケースに基づき、自律的に検証が走ります。
86
+ ## 📚 ドキュメント一覧
87
87
 
88
- ## 📄 ライセンス
88
+ 使い方やトラブルシューティングなど、すべてのドキュメントは `docs/` フォルダにあります。
89
+ 目的に合わせてお読みください。
89
90
 
90
- ISC License. 詳細は [LICENSE](LICENSE) を参照してください。
91
+ 👉 **[ドキュメント目次 (docs/README.md)](docs/README.md)**
91
92
 
92
- ## 👤 著者
93
+ ---
93
94
 
94
- **chromatribe - s.ohara** (<ivis.klain@chromatri.be>)
95
+ <div align="center">
96
+ <small>ライセンス: ISC | 作者: chromatribe - s.ohara (ivis.klain@chromatri.be)</small>
97
+ </div>
@@ -0,0 +1,4 @@
1
+ import { Command } from "commander";
2
+ import { CommandDeps } from "./shared.js";
3
+ export declare function printAccountStatusTable(deps: CommandDeps): Promise<void>;
4
+ export declare function registerAccountsCommands(program: Command, deps: CommandDeps): void;
@@ -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
+ }
@@ -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
  });
@@ -4,6 +4,7 @@ import { Applier } from "../core/applier.js";
4
4
  import { getServiceForContext } from "./shared.js";
5
5
  import { ValidationError } from "../core/errors.js";
6
6
  import { logger } from "../core/logger.js";
7
+ import { computeInputAutoRange, mergeApplySyncRanges, parseOptionalTimeRangeForApply, } from "../core/timeRangeForQuery.js";
7
8
  const COLOR_RESET = "\x1b[0m";
8
9
  const COLOR_GREEN = "\x1b[32m";
9
10
  const COLOR_BLUE = "\x1b[34m";
@@ -17,6 +18,8 @@ export function registerApplyCommand(program, deps) {
17
18
  .option("--calendar <id>", "Explicit Calendar ID")
18
19
  .option("--sync", "Delete events not in the file", false)
19
20
  .option("--dry-run", "Preview changes without applying", false)
21
+ .option("--start <iso>", "Optional: widen fetch range (same as query: YYYY-MM-DD, 7d, today, etc.). Use with --end to include events outside the file's dates (e.g. for moves).")
22
+ .option("--end <iso>", "Optional: end of fetch range. Required when using --start unless start is a relative range (e.g. 7d).")
20
23
  .action(async (options) => {
21
24
  if (!options.in) {
22
25
  throw new ValidationError("Input file required.", "Use --in <file> to specify input.");
@@ -33,14 +36,25 @@ export function registerApplyCommand(program, deps) {
33
36
  inputEvents = Formatter.fromCsv(inputData);
34
37
  }
35
38
  else {
36
- inputEvents = JSON.parse(inputData);
39
+ const parsed = Formatter.fromJson(inputData);
40
+ if (parsed.warnings.length > 0) {
41
+ parsed.warnings.forEach((w) => logger.warn(w));
42
+ }
43
+ inputEvents = parsed.events;
37
44
  }
38
45
  const applier = new Applier(service);
46
+ const now = new Date();
47
+ const autoR = computeInputAutoRange(inputEvents);
48
+ const cliR = parseOptionalTimeRangeForApply(options.start, options.end, now);
49
+ const range = mergeApplySyncRanges(autoR, cliR);
50
+ if (range) {
51
+ logger.info(`Sync range: ${range.start.toISOString()} to ${range.end.toISOString()}`);
52
+ }
39
53
  if (options.dryRun) {
40
54
  logger.info("[DRY RUN - 実際の変更は行いません]");
41
55
  }
42
56
  logger.info(`Applying changes to ${calendarId}...`);
43
- const results = await applier.apply(calendarId, inputEvents, undefined, {
57
+ const results = await applier.apply(calendarId, inputEvents, range, {
44
58
  dryRun: options.dryRun,
45
59
  sync: options.sync,
46
60
  });
@@ -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("Google Client ID / Secret が未設定です。", "docs/setup_google.md を参照、または `calendit config set-google --file <path>` を実行してください。");
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("Outlook Client ID が未設定です。", "`calendit config set-outlook --id <id>` を先に実行してください。");
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);
@@ -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("Google credentials saved to config.");
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("Outlook credentials saved to config.");
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("[CONFIG CHECK]");
62
- logger.info(` Google credentials : ${googleCreds ? `OK (id: ${mask(googleCreds.id)})` : "NOT SET (run: calendit config set-google --id <id> --secret <secret>)"}`);
63
- logger.info(` Outlook credentials: ${outlookCreds ? `OK (id: ${mask(outlookCreds.id)})` : "NOT SET (run: calendit config set-outlook --id <id>)"}`);
64
- logger.info(` Contexts : ${contextEntries.length > 0
65
- ? contextEntries.map(([name, ctx]) => `${name} (${ctx.service}/${ctx.calendarId})`).join(", ")
66
- : "none"}`);
67
- logger.info(" Config file : ~/.config/calendit/config.json (or CALENDIT_CONFIG_DIR override)");
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(`Context '${name}' が見つかりません。`, `登録済みのコンテキストを確認するには \`calendit config check\` を実行してください。`);
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(`Context '${name}' deleted.`);
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 outlook")
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 'outlook'.");
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(`Context '${name}' saved.`);
132
+ logger.info(t("config.cmd.contextSaved", { name }));
99
133
  });
100
134
  }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ import { type CommandDeps } from "./shared.js";
3
+ export declare function registerMacosCommands(program: Command, deps: CommandDeps): void;