ccc-notifier 0.1.0 → 0.2.0

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
@@ -22,7 +22,7 @@ in 1.2k(cache 40%)/ out 480 · 📁 my-app · 今日: $1.85
22
22
  - **$ と ¥ を併記** — USD と JPY の両方を毎回表示します(為替レートは自動取得 + キャッシュ + 固定フォールバックの三段構え)
23
23
  - **プロンプト全文をローカルに履歴保存** — `~/.ccc-notifier/history.jsonl` にそのターンのプロンプト全文を保存します(外部には送信されません)
24
24
  - **HTMLダッシュボード** — `dashboard` コマンドで、サマリー・コスト推移(**日 / 週 / 月**で切替、横スクロールで過去まで)・モデル別/プロジェクト別内訳・検索できるターン履歴を1枚の HTML(完全自己完結・ライト/ダーク対応)に書き出してブラウザで開きます。棒をクリックするとその期間が選択され、内訳・履歴が連動(「通算」で全期間)
25
- - **月予算(monthly budget)** — 月に使える金額(USD)を設定すると、ダッシュボードに**当月の使用額 / 予算・使用率(%)**をプログレスバーで表示します(`init` の対話、または `ccc-notifier budget <金額>` で設定)
25
+ - **月予算(monthly budget)** — 月に使える金額(USD)を設定すると、ダッシュボードに**当月の使用額 / 予算・使用率(%)** をプログレスバーで表示します(`init` の対話、または `ccc-notifier budget <金額>` で設定)
26
26
  - **OS 標準の通知機構のみ使用・追加依存ゼロ** — 通知は macOS では `osascript`、Windows では PowerShell 標準のトースト通知機能のみで送信します(node-notifier 等の外部通知ライブラリには一切依存しません)
27
27
  - **全処理ローカル・フェイルセーフ設計** — 通知や集計の処理が失敗しても、Claude Code 本体の応答は絶対にブロックしません
28
28
 
@@ -72,7 +72,7 @@ node dist/cli.js init
72
72
 
73
73
  対話形式で次の4点を聞かれます。
74
74
 
75
- - 通知チャネル(OS通知のみ / Slackのみ / OS通知+Slack)
75
+ - 通知チャネル(OS通知のみ / Slackのみ / OS通知+Slack / 通知なし(記録・ダッシュボードのみ))
76
76
  - コスト表示ラベル(API換算 / 実額)
77
77
  - USD/JPY のフォールバック為替レート(既定 150円)
78
78
  - 月の予算(USD、既定 $400。`0` で無効。ダッシュボードに当月の使用率を表示。詳細は [月予算](docs/monthly-budget.md))
@@ -95,6 +95,7 @@ CI などから非対話で `init` したい場合は次のフラグが使えま
95
95
  | `--os-only` | Slack を無効化し OS通知のみにする |
96
96
  | `--slack-webhook <url>` | Slack Incoming Webhook URL を指定して有効化 |
97
97
  | `--slack-only` | Slack のみにする(OS 通知を無効化)。`--slack-webhook` と併用が必須 |
98
+ | `--no-notify` | 通知なし(記録・ダッシュボードのみ)。`--os-only` / `--slack-only` / `--slack-webhook` とは併用不可(例: `npx ccc-notifier init --yes --no-notify`。詳細は [設定](docs/configuration.md#通知なしモード記録ダッシュボードのみ--dashboard-only-mode)) |
98
99
  | `--label <api_equivalent\|actual>` | コスト表示ラベルを指定 |
99
100
  | `--rate <number>` | USD/JPY フォールバックレートを指定 |
100
101
  | `--budget <USD>` | 月予算(USD)を指定(0 で無効)。未指定なら既定 **$400**(既存設定があれば維持) |
@@ -100,12 +100,13 @@ function takeValue(argv, i, prefix) {
100
100
  return argv[i + 1];
101
101
  }
102
102
  function parseInitFlags(argv) {
103
- const flags = { yes: false, osOnly: false, slackOnly: false };
103
+ const flags = { yes: false, osOnly: false, slackOnly: false, noNotify: false };
104
104
  for (let i = 0; i < argv.length; i++) {
105
105
  const a = argv[i];
106
106
  if (a === "--yes" || a === "-y") flags.yes = true;
107
107
  else if (a === "--os-only") flags.osOnly = true;
108
108
  else if (a === "--slack-only") flags.slackOnly = true;
109
+ else if (a === "--no-notify") flags.noNotify = true;
109
110
  else if (a === "--slack-webhook" || a.startsWith("--slack-webhook=")) {
110
111
  flags.slackWebhook = takeValue(argv, i, "--slack-webhook");
111
112
  if (!a.includes("=")) i++;
@@ -174,10 +175,18 @@ function mergeSettings(sPath, command) {
174
175
  async function runInit(argv) {
175
176
  const flags = parseInitFlags(argv);
176
177
  const cfg = readConfig();
178
+ const initialChannel = !cfg.notify.os && !cfg.notify.slack ? "none" : cfg.notify.slack ? cfg.notify.os ? "both" : "slack" : "os";
177
179
  cfg.notify.os = true;
178
180
  const budgetDefault = cfg.monthlyBudgetUSD > 0 ? cfg.monthlyBudgetUSD : DEFAULT_BUDGET_USD;
179
181
  if (flags.yes) {
180
- if (flags.slackOnly) {
182
+ if (flags.noNotify) {
183
+ if (flags.osOnly || flags.slackOnly || flags.slackWebhook !== void 0) {
184
+ console.error("--no-notify \u3068 --os-only / --slack-only / --slack-webhook \u306F\u540C\u6642\u306B\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093");
185
+ return 1;
186
+ }
187
+ cfg.notify.os = false;
188
+ cfg.notify.slack = null;
189
+ } else if (flags.slackOnly) {
181
190
  if (flags.osOnly) {
182
191
  console.error("--slack-only \u3068 --os-only \u306F\u540C\u6642\u306B\u6307\u5B9A\u3067\u304D\u307E\u305B\u3093");
183
192
  return 1;
@@ -233,9 +242,10 @@ async function runInit(argv) {
233
242
  options: [
234
243
  { value: "os", label: "OS \u901A\u77E5\u306E\u307F" },
235
244
  { value: "slack", label: "Slack \u306E\u307F(OS \u901A\u77E5\u306A\u3057)" },
236
- { value: "both", label: "OS \u901A\u77E5 + Slack" }
245
+ { value: "both", label: "OS \u901A\u77E5 + Slack" },
246
+ { value: "none", label: "\u901A\u77E5\u306A\u3057(\u8A18\u9332\u30FB\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u306E\u307F)" }
237
247
  ],
238
- initialValue: "os"
248
+ initialValue: initialChannel
239
249
  });
240
250
  if (p.isCancel(channel)) {
241
251
  p.cancel("\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F");
@@ -259,6 +269,7 @@ async function runInit(argv) {
259
269
  cfg.notify.os = channel === "both";
260
270
  } else {
261
271
  cfg.notify.slack = null;
272
+ cfg.notify.os = channel === "os";
262
273
  }
263
274
  const labelChoice = await p.select({
264
275
  message: "\u30B3\u30B9\u30C8\u8868\u793A\u30E9\u30D9\u30EB",
@@ -313,10 +324,15 @@ async function runInit(argv) {
313
324
  const sPath = settingsPath();
314
325
  const result = mergeSettings(sPath, command);
315
326
  if (!result.ok) return 1;
316
- const testRecord = makeTestRecord();
317
- await notifyOS(testRecord, cfg);
318
- if (cfg.notify.slack) {
319
- await notifySlack(testRecord, cfg);
327
+ const notifyDisabled = !cfg.notify.os && !cfg.notify.slack;
328
+ if (notifyDisabled) {
329
+ console.log("\u30C6\u30B9\u30C8\u901A\u77E5: \u901A\u77E5\u306A\u3057\u30E2\u30FC\u30C9\u306E\u305F\u3081\u30B9\u30AD\u30C3\u30D7\u3057\u307E\u3057\u305F");
330
+ } else {
331
+ const testRecord = makeTestRecord();
332
+ await notifyOS(testRecord, cfg);
333
+ if (cfg.notify.slack) {
334
+ await notifySlack(testRecord, cfg);
335
+ }
320
336
  }
321
337
  console.log("");
322
338
  console.log(`settings \u3092\u66F4\u65B0\u3057\u307E\u3057\u305F: ${sPath}`);
@@ -324,7 +340,13 @@ async function runInit(argv) {
324
340
  result.backupPath ? ` \u30D0\u30C3\u30AF\u30A2\u30C3\u30D7: ${result.backupPath}` : " (settings.json \u3092\u65B0\u898F\u4F5C\u6210\u3057\u305F\u305F\u3081\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u306F\u3042\u308A\u307E\u305B\u3093)"
325
341
  );
326
342
  console.log(` \u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB: ${cccn.configFile}`);
327
- console.log("Claude Code \u3067\u4F55\u304B\u5B9F\u884C\u3059\u308B\u3068\u901A\u77E5\u304C\u5C4A\u304D\u307E\u3059\u3002\u78BA\u8A8D: npx ccc-notifier doctor");
343
+ if (notifyDisabled) {
344
+ console.log(
345
+ "\u901A\u77E5\u306A\u3057\u30E2\u30FC\u30C9\u3067\u3059\u3002Claude Code \u306E\u5229\u7528\u306F\u81EA\u52D5\u3067\u8A18\u9332\u3055\u308C\u307E\u3059\u3002\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9: npx ccc-notifier dashboard"
346
+ );
347
+ } else {
348
+ console.log("Claude Code \u3067\u4F55\u304B\u5B9F\u884C\u3059\u308B\u3068\u901A\u77E5\u304C\u5C4A\u304D\u307E\u3059\u3002\u78BA\u8A8D: npx ccc-notifier doctor");
349
+ }
328
350
  return 0;
329
351
  }
330
352
  async function runUninstall(argv) {
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-KUJZYZSG.js";
5
5
  import {
6
6
  matchesMarker
7
- } from "./chunk-L5MLVTA2.js";
7
+ } from "./chunk-OXMHOVUL.js";
8
8
  import {
9
9
  notifyOS,
10
10
  notifySlack,
@@ -258,6 +258,11 @@ async function checkFx(cfg) {
258
258
  }
259
259
  async function checkNotification(cfg) {
260
260
  try {
261
+ if (!cfg.notify.os && !cfg.notify.slack) {
262
+ log("ok", "\u901A\u77E5\u30C1\u30E3\u30CD\u30EB\u306F\u3059\u3079\u3066\u7121\u52B9\u3067\u3059(\u901A\u77E5\u306A\u3057\u30FB\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u306E\u307F\u30E2\u30FC\u30C9)\u3002\u8A18\u9332\u3068\u30C0\u30C3\u30B7\u30E5\u30DC\u30FC\u30C9\u306F\u52D5\u4F5C\u3057\u307E\u3059");
263
+ log("ok", "\u901A\u77E5\u3092\u6709\u52B9\u306B\u3059\u308B\u306B\u306F npx ccc-notifier init \u3092\u518D\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044");
264
+ return true;
265
+ }
261
266
  if (isMuted()) {
262
267
  const until = readMuteState()?.until;
263
268
  log(
@@ -302,9 +307,6 @@ async function checkNotification(cfg) {
302
307
  `Slack \u306E\u30C6\u30B9\u30C8\u901A\u77E5\u3092\u9001\u4FE1\u3057\u307E\u3057\u305F${dryRun ? dryHint : "(Slack \u30C1\u30E3\u30F3\u30CD\u30EB\u306B\u5C4A\u3044\u305F\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u5C4A\u304B\u306A\u3044\u5834\u5408\u306F error.log \u3092\u53C2\u7167)"}`
303
308
  );
304
309
  }
305
- if (!cfg.notify.os && !cfg.notify.slack) {
306
- log("warn", "OS\u30FBSlack \u3068\u3082\u7121\u52B9\u306A\u305F\u3081\u3001\u30C6\u30B9\u30C8\u901A\u77E5\u306F\u9001\u4FE1\u3057\u3066\u3044\u307E\u305B\u3093");
307
- }
308
310
  return true;
309
311
  } catch (err) {
310
312
  log("warn", `\u30C6\u30B9\u30C8\u901A\u77E5\u306E\u9001\u4FE1\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${errMessage(err)}`);
@@ -630,18 +632,18 @@ async function main(argv) {
630
632
  case "track": {
631
633
  const text = await readStdin();
632
634
  try {
633
- const trackMod = await import("./track-PBHTVZ3L.js");
635
+ const trackMod = await import("./track-76Q5CZM5.js");
634
636
  await trackMod.runTrack(text);
635
637
  } catch {
636
638
  }
637
639
  return 0;
638
640
  }
639
641
  case "init": {
640
- const { runInit } = await import("./setup-RT62VO37.js");
642
+ const { runInit } = await import("./setup-IXPUR4CM.js");
641
643
  return await runInit(rest);
642
644
  }
643
645
  case "uninstall": {
644
- const { runUninstall } = await import("./setup-RT62VO37.js");
646
+ const { runUninstall } = await import("./setup-IXPUR4CM.js");
645
647
  return await runUninstall(rest);
646
648
  }
647
649
  case "doctor":
@@ -3,7 +3,7 @@ import {
3
3
  matchesMarker,
4
4
  runInit,
5
5
  runUninstall
6
- } from "./chunk-L5MLVTA2.js";
6
+ } from "./chunk-OXMHOVUL.js";
7
7
  import "./chunk-4JNZ7BHO.js";
8
8
  import "./chunk-DGXUSPS4.js";
9
9
  import "./chunk-TBFKGFZX.js";
@@ -135,7 +135,7 @@ async function runTrack(stdinText) {
135
135
  }
136
136
  }
137
137
  const tasks = [];
138
- if (record.costUSD >= cfg.minNotifyUSD && !isMuted()) {
138
+ if ((cfg.notify.os || cfg.notify.slack !== null) && record.costUSD >= cfg.minNotifyUSD && !isMuted()) {
139
139
  const todayUSD = cfg.includeDailyTotal ? todayTotalUSD() : void 0;
140
140
  tasks.push(notifyOS(record, cfg, todayUSD));
141
141
  tasks.push(notifySlack(record, cfg, todayUSD));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccc-notifier",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Claude Code Cost notifier: per-prompt cost notifications (USD/JPY) with local history and HTML dashboard",
5
5
  "keywords": [
6
6
  "claude",