atl-fetch 1.0.0 → 1.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
@@ -16,6 +16,9 @@ Atlassian Cloud(Jira / Confluence)から情報を取得する Node.js CLI
16
16
  - **複数出力形式**: JSON / Markdown / YAML
17
17
  - **差分表示**: バージョン間の Unified diff 形式表示
18
18
  - **添付ファイルダウンロード**: 指定ディレクトリへの保存
19
+ - **親切なエラーメッセージ**: エラーコード、原因、解決策を表示
20
+ - **進捗表示**: スピナーによる処理状況の可視化
21
+ - **認証チェック**: `atl-fetch auth check` で設定状態を確認
19
22
 
20
23
  ## 動作要件
21
24
 
@@ -37,7 +40,6 @@ pnpm add -g atl-fetch
37
40
  ### 1. 環境変数の設定
38
41
 
39
42
  ```bash
40
- export ATLASSIAN_DOMAIN="your-domain.atlassian.net"
41
43
  export ATLASSIAN_EMAIL="your-email@example.com"
42
44
  export ATLASSIAN_API_TOKEN="your-api-token"
43
45
  ```
@@ -56,54 +58,63 @@ atl-fetch https://your-domain.atlassian.net/wiki/spaces/SPACE/pages/123456/Page+
56
58
  # Markdown 形式で出力
57
59
  atl-fetch https://your-domain.atlassian.net/browse/PROJECT-123 --format markdown
58
60
 
59
- # ファイルに保存
60
- atl-fetch https://your-domain.atlassian.net/browse/PROJECT-123 --output result.json
61
+ # 添付ファイルをダウンロード
62
+ atl-fetch https://your-domain.atlassian.net/browse/PROJECT-123 --download --dir ./output
63
+
64
+ # ファイルに保存(リダイレクト)
65
+ atl-fetch https://your-domain.atlassian.net/browse/PROJECT-123 > result.json
61
66
  ```
62
67
 
63
68
  ## CLI オプション
64
69
 
65
- | オプション | 短縮形 | 説明 | デフォルト |
66
- | ------------------------ | ---- | ---------------------------- | ------------- |
67
- | `--format` | `-f` | 出力形式(json / markdown / yaml) | json |
68
- | `--output` | `-o` | 出力ファイルパス | stdout |
69
- | `--include-comments` | `-c` | コメントを含める | false |
70
- | `--include-history` | `-h` | 変更履歴を含める | false |
71
- | `--include-attachments` | `-a` | 添付ファイルを含める | false |
72
- | `--download-attachments` | `-d` | 添付ファイルをダウンロード | false |
73
- | `--attachments-dir` | | 添付ファイルの保存先 | ./attachments |
74
- | `--diff` | | 指定バージョンとの差分を表示 | - |
75
- | `--help` | | ヘルプを表示 | - |
76
- | `--version` | | バージョンを表示 | - |
77
-
78
- ## ライブラリとしての使用
79
-
80
- ```typescript
81
- import { fetchJiraIssue, fetchConfluencePage } from 'atl-fetch';
82
-
83
- // Jira Issue を取得
84
- const jiraResult = await fetchJiraIssue({
85
- url: 'https://your-domain.atlassian.net/browse/PROJECT-123',
86
- includeComments: true,
87
- includeHistory: false,
88
- });
89
-
90
- if (jiraResult.isOk()) {
91
- console.log(jiraResult.value);
92
- } else {
93
- console.error(jiraResult.error);
94
- }
95
-
96
- // Confluence ページを取得
97
- const confluenceResult = await fetchConfluencePage({
98
- url: 'https://your-domain.atlassian.net/wiki/spaces/SPACE/pages/123456/Page+Title',
99
- includeHistory: true,
100
- });
101
-
102
- if (confluenceResult.isOk()) {
103
- console.log(confluenceResult.value);
104
- }
70
+ | オプション | 短縮形 | 説明 | デフォルト |
71
+ | ------------ | ---- | ---------------------------- | ----- |
72
+ | `--format` | `-f` | 出力形式(json / markdown / yaml) | json |
73
+ | `--download` | `-d` | 添付ファイルをダウンロード | false |
74
+ | `--dir` | `-o` | 保存先ディレクトリ | - |
75
+ | `--diff` | | 差分のみを出力 | false |
76
+ | `--color` | | カラー出力を有効化 | true |
77
+ | `--verbose` | `-v` | 詳細出力を有効化 | false |
78
+ | `--debug` | | デバッグ出力を有効化(開発者向け) | false |
79
+ | `--help` | `-h` | ヘルプを表示 | - |
80
+ | `--version` | `-V` | バージョンを表示 | - |
81
+
82
+ **注意**: `--dir` は `--download` と一緒に使用する必要があります。
83
+
84
+ ## サブコマンド
85
+
86
+ ### `atl-fetch auth check`
87
+
88
+ 認証情報の設定状態を確認します。
89
+
90
+ ```bash
91
+ atl-fetch auth check
105
92
  ```
106
93
 
94
+ 出力例(設定済み):
95
+ ```
96
+ 認証情報チェック
97
+ ────────────────────────────────────────
98
+
99
+ ✓ 認証情報が正しく設定されています
100
+
101
+ 設定状態:
102
+ ATLASSIAN_EMAIL: user@example.com
103
+ ATLASSIAN_API_TOKEN: ***abcd
104
+ ```
105
+
106
+ ## エラーコード
107
+
108
+ 問題が発生した場合、以下のエラーコードと解決策が表示されます。
109
+
110
+ | コード | 説明 | 主な原因 |
111
+ | ------------ | ------------------ | ----------------------- |
112
+ | `ATL-URL-001` | URL の形式が不正 | サポートされていない URL 形式 |
113
+ | `ATL-AUTH-001` | 認証失敗 | API トークンが無効または期限切れ |
114
+ | `ATL-404-001` | リソースが見つからない | URL が間違っている、または削除済み |
115
+ | `ATL-403-001` | アクセス権限なし | 該当リソースへの権限がない |
116
+ | `ATL-NET-001` | ネットワークエラー | インターネット接続の問題 |
117
+
107
118
  ## ライセンス
108
119
 
109
120
  [MIT](LICENSE)
package/dist/cli/cli.d.ts CHANGED
@@ -19,6 +19,10 @@ export interface CliArgs {
19
19
  diff: boolean;
20
20
  /** カラー出力を有効にする */
21
21
  color: boolean;
22
+ /** 詳細出力を有効にする */
23
+ verbose: boolean;
24
+ /** デバッグ出力を有効にする */
25
+ debug: boolean;
22
26
  }
23
27
  /**
24
28
  * CLI 作成オプション
@@ -45,6 +49,10 @@ export declare function createCli(options?: CliOptions): import("yargs").Argv<{
45
49
  diff: boolean;
46
50
  } & {
47
51
  color: boolean;
52
+ } & {
53
+ verbose: boolean;
54
+ } & {
55
+ debug: boolean;
48
56
  }>;
49
57
  /**
50
58
  * CLI を実行する
package/dist/cli/cli.js CHANGED
@@ -6,7 +6,9 @@
6
6
  import { createRequire } from 'node:module';
7
7
  import yargs from 'yargs';
8
8
  import { hideBin } from 'yargs/helpers';
9
+ import { getCredentials } from '../services/auth/auth-service.js';
9
10
  import { fetchAndOutput, fetchAndSave } from '../services/fetch/fetch-service.js';
11
+ import { CliSpinner, colors, formatError, formatSuccess, formatWarn, SPINNER_STEPS } from './spinner.js';
10
12
  const require = createRequire(import.meta.url);
11
13
  const packageJson = require('../../package.json');
12
14
  /**
@@ -25,13 +27,16 @@ export function createCli(options = {}) {
25
27
  const { exitProcess = true } = options;
26
28
  return yargs()
27
29
  .scriptName('atl-fetch')
28
- .usage('Usage: $0 <url> [options]')
30
+ .usage('Usage: $0 <command> [options]')
29
31
  .command('$0 <url>', 'Atlassian Cloud の URL から情報を取得する', (yargs) => {
30
32
  return yargs.positional('url', {
31
33
  demandOption: true,
32
34
  describe: 'Atlassian Cloud URL (Jira Issue または Confluence ページ)',
33
35
  type: 'string',
34
36
  });
37
+ })
38
+ .command('auth', '認証情報を管理する', (yargs) => {
39
+ return yargs.command('check', '認証情報の設定状態を確認する', () => { }, runAuthCheck);
35
40
  })
36
41
  .option('format', {
37
42
  alias: 'f',
@@ -68,6 +73,39 @@ export function createCli(options = {}) {
68
73
  describe: 'カラー出力を有効にする',
69
74
  type: 'boolean',
70
75
  })
76
+ .option('verbose', {
77
+ alias: 'v',
78
+ default: false,
79
+ describe: '詳細出力を有効にする',
80
+ type: 'boolean',
81
+ })
82
+ .option('debug', {
83
+ default: false,
84
+ describe: 'デバッグ出力を有効にする(開発者向け)',
85
+ type: 'boolean',
86
+ })
87
+ .example([
88
+ ['$0 https://mycompany.atlassian.net/browse/PROJ-123', 'Jira Issue を JSON で取得'],
89
+ ['$0 https://mycompany.atlassian.net/browse/PROJ-123 -f markdown', 'Markdown 形式で取得'],
90
+ [
91
+ '$0 https://mycompany.atlassian.net/wiki/spaces/DOCS/pages/123456 -d -o ./output',
92
+ 'Confluence ページを添付ファイルごとダウンロード',
93
+ ],
94
+ ['$0 https://mycompany.atlassian.net/browse/PROJ-123 -v', '詳細モードで実行'],
95
+ ])
96
+ .epilogue(`環境変数:
97
+ ATLASSIAN_EMAIL Atlassian アカウントのメールアドレス(必須)
98
+ ATLASSIAN_API_TOKEN API トークン(必須)
99
+ https://id.atlassian.com/manage-profile/security/api-tokens で生成
100
+
101
+ エラーコード:
102
+ ATL-URL-001 URL の形式が不正
103
+ ATL-AUTH-001 認証失敗
104
+ ATL-404-001 リソースが見つからない
105
+ ATL-403-001 アクセス権限なし
106
+ ATL-NET-001 ネットワークエラー
107
+
108
+ 詳細: https://github.com/semba-yui/atl-fetch`)
71
109
  .help('help')
72
110
  .alias('h', 'help')
73
111
  .version(CLI_VERSION)
@@ -103,29 +141,144 @@ export async function runCli() {
103
141
  const download = argv['download'];
104
142
  const dir = argv['dir'];
105
143
  const colorEnabled = argv['color'];
144
+ const verbose = argv['verbose'];
145
+ const debug = argv['debug'];
146
+ const spinner = new CliSpinner(colorEnabled);
147
+ // verbose モードの場合、処理開始を表示
148
+ if (verbose) {
149
+ console.log(colors.dim(`atl-fetch v${CLI_VERSION}`));
150
+ console.log(colors.dim(`URL: ${url}`));
151
+ console.log(colors.dim(`形式: ${format}`));
152
+ if (download) {
153
+ console.log(colors.dim(`ダウンロードモード: 有効`));
154
+ console.log(colors.dim(`保存先: ${dir ?? process.cwd()}`));
155
+ }
156
+ console.log('');
157
+ }
106
158
  // ダウンロードモード
107
159
  if (download) {
108
160
  const baseDir = dir ?? process.cwd();
161
+ spinner.startStep(SPINNER_STEPS.FETCH_DATA);
109
162
  const result = await fetchAndSave(url, {
110
163
  baseDir,
111
164
  cliVersion: CLI_VERSION,
112
165
  sourceUrl: url,
113
166
  });
114
167
  if (result.isErr()) {
115
- console.error(`エラー: ${result.error.message}`);
168
+ spinner.fail();
169
+ console.error(formatErrorForKind(result.error.kind, result.error.message, url, debug));
116
170
  process.exit(1);
117
171
  }
118
- console.log(`保存完了: ${result.value.directory}`);
172
+ spinner.succeedStep(SPINNER_STEPS.SAVE_FILES);
173
+ console.log(formatSuccess('保存完了', {
174
+ URL: url,
175
+ 保存先: result.value.directory,
176
+ }));
119
177
  return;
120
178
  }
121
179
  // 通常モード(標準出力)
180
+ spinner.startStep(SPINNER_STEPS.FETCH_DATA);
122
181
  const result = await fetchAndOutput(url, {
123
182
  colorEnabled,
124
183
  format,
125
184
  });
126
185
  if (result.isErr()) {
127
- console.error(`エラー: ${result.error.message}`);
186
+ spinner.fail();
187
+ console.error(formatErrorForKind(result.error.kind, result.error.message, url, debug));
128
188
  process.exit(1);
129
189
  }
190
+ spinner.succeedStep(SPINNER_STEPS.FORMAT_OUTPUT);
130
191
  console.log(result.value);
131
192
  }
193
+ /**
194
+ * エラー種別に応じたエラーメッセージを生成する
195
+ *
196
+ * @param kind エラー種別
197
+ * @param message エラーメッセージ
198
+ * @param url 対象URL
199
+ * @param debug デバッグモードが有効かどうか
200
+ * @returns 整形されたエラーメッセージ
201
+ */
202
+ function formatErrorForKind(kind, message, url, debug) {
203
+ let result;
204
+ switch (kind) {
205
+ case 'URL_PARSE_ERROR':
206
+ result = formatError('ATL-URL-001', message, 'URLの形式が正しくありません', `サポートされるURL形式:\n - Jira: https://<site>.atlassian.net/browse/<KEY>\n - Confluence: https://<site>.atlassian.net/wiki/spaces/<SPACE>/pages/<ID>`);
207
+ break;
208
+ case 'AUTH_FAILED':
209
+ result = formatError('ATL-AUTH-001', message, 'APIトークンが無効または期限切れです', `環境変数を確認してください:\n - ATLASSIAN_EMAIL: Atlassianアカウントのメールアドレス\n - ATLASSIAN_API_TOKEN: https://id.atlassian.com/manage-profile/security/api-tokens で生成`);
210
+ break;
211
+ case 'NOT_FOUND':
212
+ result = formatError('ATL-404-001', message, '指定されたリソースが見つかりません', `URL: ${colors.cyan(url)}\n - URLが正しいか確認してください\n - リソースが削除されていないか確認してください`);
213
+ break;
214
+ case 'FORBIDDEN':
215
+ result = formatError('ATL-403-001', message, 'アクセス権限がありません', `URL: ${colors.cyan(url)}\n - 該当リソースへのアクセス権限があるか確認してください`);
216
+ break;
217
+ case 'NETWORK_ERROR':
218
+ result = formatError('ATL-NET-001', message, 'ネットワーク接続に問題があります', '- インターネット接続を確認してください\n - ファイアウォール/プロキシ設定を確認してください');
219
+ break;
220
+ default:
221
+ result = formatError('ATL-ERR-001', message, '予期しないエラーが発生しました', '--debug フラグで詳細を確認してください');
222
+ }
223
+ // デバッグモードの場合、追加情報を表示
224
+ if (debug) {
225
+ result += `\n\n${colors.dim('--- デバッグ情報 ---')}`;
226
+ result += `\n${colors.dim(`エラー種別: ${kind}`)}`;
227
+ result += `\n${colors.dim(`対象URL: ${url}`)}`;
228
+ result += `\n${colors.dim(`タイムスタンプ: ${new Date().toISOString()}`)}`;
229
+ }
230
+ return result;
231
+ }
232
+ /**
233
+ * 認証情報のチェックを実行する
234
+ *
235
+ * 環境変数が正しく設定されているかを確認し、結果を表示する。
236
+ */
237
+ function runAuthCheck() {
238
+ console.log(colors.bold('認証情報チェック'));
239
+ console.log(colors.dim('─'.repeat(40)));
240
+ console.log('');
241
+ const result = getCredentials();
242
+ if (result.isOk()) {
243
+ const email = result.value.email;
244
+ const maskedToken = `***${result.value.apiToken.slice(-4)}`;
245
+ console.log(colors.success('✓ 認証情報が正しく設定されています'));
246
+ console.log('');
247
+ console.log(colors.dim(' 設定状態:'));
248
+ console.log(colors.dim(` ATLASSIAN_EMAIL: ${colors.cyan(email)}`));
249
+ console.log(colors.dim(` ATLASSIAN_API_TOKEN: ${colors.cyan(maskedToken)}`));
250
+ console.log('');
251
+ console.log(colors.dim(' ※ 実際のAPI接続テストは行っていません。'));
252
+ console.log(colors.dim(' トークンが有効かどうかは、実際にURLを取得して確認してください。'));
253
+ }
254
+ else {
255
+ const error = result.error;
256
+ console.log(colors.error('✗ 認証情報の設定に問題があります'));
257
+ console.log('');
258
+ switch (error.kind) {
259
+ case 'MISSING_EMAIL':
260
+ console.log(formatWarn('ATLASSIAN_EMAIL が設定されていません'));
261
+ console.log('');
262
+ console.log(colors.dim(' 設定方法:'));
263
+ console.log(colors.dim(' export ATLASSIAN_EMAIL="your-email@example.com"'));
264
+ break;
265
+ case 'MISSING_TOKEN':
266
+ console.log(formatWarn('ATLASSIAN_API_TOKEN が設定されていません'));
267
+ console.log('');
268
+ console.log(colors.dim(' 設定方法:'));
269
+ console.log(colors.dim(' export ATLASSIAN_API_TOKEN="your-api-token"'));
270
+ console.log('');
271
+ console.log(colors.dim(' トークンの取得:'));
272
+ console.log(colors.dim(' https://id.atlassian.com/manage-profile/security/api-tokens'));
273
+ break;
274
+ case 'INVALID_EMAIL':
275
+ console.log(formatWarn('ATLASSIAN_EMAIL の形式が無効です'));
276
+ console.log('');
277
+ console.log(colors.dim(' 有効なメールアドレス形式で設定してください。'));
278
+ break;
279
+ default:
280
+ console.log(formatWarn(error.message));
281
+ }
282
+ process.exit(1);
283
+ }
284
+ }
@@ -0,0 +1,189 @@
1
+ /**
2
+ * CLI スピナー・進捗表示ユーティリティ
3
+ *
4
+ * ora と picocolors を使用した統一的な進捗表示を提供する。
5
+ */
6
+ /**
7
+ * スピナーのステップ定義
8
+ */
9
+ export interface SpinnerStep {
10
+ /** ステップ名(内部識別用) */
11
+ name: string;
12
+ /** 表示テキスト(進行中) */
13
+ text: string;
14
+ /** 完了時のテキスト */
15
+ successText?: string;
16
+ }
17
+ /**
18
+ * 事前定義されたスピナーステップ
19
+ */
20
+ export declare const SPINNER_STEPS: {
21
+ readonly AUTH_CHECK: {
22
+ readonly name: "auth_check";
23
+ readonly successText: "認証情報を確認しました";
24
+ readonly text: "認証情報を確認中...";
25
+ };
26
+ readonly DOWNLOAD_ATTACHMENTS: {
27
+ readonly name: "download_attachments";
28
+ readonly successText: "添付ファイルをダウンロードしました";
29
+ readonly text: "添付ファイルをダウンロード中...";
30
+ };
31
+ readonly FETCH_CONFLUENCE: {
32
+ readonly name: "fetch_confluence";
33
+ readonly successText: "Confluence ページを取得しました";
34
+ readonly text: "Confluence ページを取得中...";
35
+ };
36
+ readonly FETCH_DATA: {
37
+ readonly name: "fetch_data";
38
+ readonly successText: "データを取得しました";
39
+ readonly text: "データを取得中...";
40
+ };
41
+ readonly FETCH_JIRA: {
42
+ readonly name: "fetch_jira";
43
+ readonly successText: "Jira Issue を取得しました";
44
+ readonly text: "Jira Issue を取得中...";
45
+ };
46
+ readonly FORMAT_OUTPUT: {
47
+ readonly name: "format_output";
48
+ readonly successText: "出力を整形しました";
49
+ readonly text: "出力を整形中...";
50
+ };
51
+ readonly SAVE_FILES: {
52
+ readonly name: "save_files";
53
+ readonly successText: "ファイルを保存しました";
54
+ readonly text: "ファイルを保存中...";
55
+ };
56
+ readonly URL_PARSE: {
57
+ readonly name: "url_parse";
58
+ readonly successText: "URLを解析しました";
59
+ readonly text: "URLを解析中...";
60
+ };
61
+ };
62
+ /**
63
+ * CLI 出力のカラーヘルパー
64
+ */
65
+ export declare const colors: {
66
+ /** 太字 */
67
+ readonly bold: (text: string) => string;
68
+ /** シアン(ファイルパスなど) */
69
+ readonly cyan: (text: string) => string;
70
+ /** 薄いテキスト(グレー) */
71
+ readonly dim: (text: string) => string;
72
+ /** エラーメッセージ(赤) */
73
+ readonly error: (text: string) => string;
74
+ /** 情報メッセージ(青) */
75
+ readonly info: (text: string) => string;
76
+ /** マゼンタ(URL など) */
77
+ readonly magenta: (text: string) => string;
78
+ /** 成功メッセージ(緑) */
79
+ readonly success: (text: string) => string;
80
+ /** 警告メッセージ(黄) */
81
+ readonly warn: (text: string) => string;
82
+ };
83
+ /**
84
+ * CLI スピナー管理クラス
85
+ *
86
+ * 処理の進捗状況をユーザーに表示するためのスピナーを管理する。
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const spinner = new CliSpinner();
91
+ * spinner.start('データを取得中...');
92
+ * // ... 処理 ...
93
+ * spinner.succeed('データを取得しました');
94
+ * ```
95
+ */
96
+ export declare class CliSpinner {
97
+ private spinner;
98
+ private enabled;
99
+ /**
100
+ * CliSpinner インスタンスを作成する
101
+ *
102
+ * @param enabled スピナーを有効にするかどうか(CI環境などでは無効にする)
103
+ */
104
+ constructor(enabled?: boolean);
105
+ /**
106
+ * スピナーを開始する
107
+ *
108
+ * @param text 表示するテキスト
109
+ */
110
+ start(text: string): void;
111
+ /**
112
+ * スピナーのテキストを更新する
113
+ *
114
+ * @param text 新しいテキスト
115
+ */
116
+ update(text: string): void;
117
+ /**
118
+ * スピナーを成功状態で終了する
119
+ *
120
+ * @param text 成功時に表示するテキスト
121
+ */
122
+ succeed(text?: string): void;
123
+ /**
124
+ * スピナーを失敗状態で終了する
125
+ *
126
+ * @param text 失敗時に表示するテキスト
127
+ */
128
+ fail(text?: string): void;
129
+ /**
130
+ * スピナーを警告状態で終了する
131
+ *
132
+ * @param text 警告時に表示するテキスト
133
+ */
134
+ warn(text?: string): void;
135
+ /**
136
+ * スピナーを情報状態で終了する
137
+ *
138
+ * @param text 情報として表示するテキスト
139
+ */
140
+ info(text?: string): void;
141
+ /**
142
+ * スピナーを停止する(状態なし)
143
+ */
144
+ stop(): void;
145
+ /**
146
+ * 事前定義されたステップでスピナーを開始する
147
+ *
148
+ * @param step スピナーステップ定義
149
+ */
150
+ startStep(step: SpinnerStep): void;
151
+ /**
152
+ * 事前定義されたステップを成功状態で終了する
153
+ *
154
+ * @param step スピナーステップ定義
155
+ */
156
+ succeedStep(step: SpinnerStep): void;
157
+ }
158
+ /**
159
+ * エラーメッセージを整形して出力する
160
+ *
161
+ * @param code エラーコード
162
+ * @param message エラーメッセージ
163
+ * @param cause 原因の説明(オプション)
164
+ * @param solution 解決策の説明(オプション)
165
+ * @returns 整形されたエラーメッセージ
166
+ */
167
+ export declare function formatError(code: string, message: string, cause?: string, solution?: string): string;
168
+ /**
169
+ * 成功メッセージを整形する
170
+ *
171
+ * @param message メッセージ
172
+ * @param details 詳細情報(オプション)
173
+ * @returns 整形されたメッセージ
174
+ */
175
+ export declare function formatSuccess(message: string, details?: Record<string, string>): string;
176
+ /**
177
+ * 情報メッセージを整形する
178
+ *
179
+ * @param message メッセージ
180
+ * @returns 整形されたメッセージ
181
+ */
182
+ export declare function formatInfo(message: string): string;
183
+ /**
184
+ * 警告メッセージを整形する
185
+ *
186
+ * @param message メッセージ
187
+ * @returns 整形されたメッセージ
188
+ */
189
+ export declare function formatWarn(message: string): string;