ai-cli-log 1.0.5 → 1.0.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.
package/README.md CHANGED
@@ -10,27 +10,27 @@ npm install -g ai-cli-log
10
10
 
11
11
  ## Usage
12
12
 
13
- Wrap any command with `ai-cli-log` to start a logging session.
13
+ Wrap any command with `ai-cli-log` to start a logging session. The core structure is:
14
14
 
15
15
  ```bash
16
- ai-cli-log <command> [args...]
16
+ ai-cli-log [global-options] run <command-to-log> [args...]
17
17
  ```
18
18
 
19
19
  **Examples:**
20
20
 
21
21
  - **Basic Logging:** Record a session with Google's Gemini CLI.
22
22
  ```bash
23
- ai-cli-log gemini
23
+ ai-cli-log run gemini
24
24
  ```
25
25
  *Logs are saved to the `.ai-cli-log/` directory.*
26
26
 
27
- - **AI-Powered Filenames:** Use an AI summary for the log's filename.
27
+ - **AI-Powered Filenames:** Use an AI summary for the log's filename. The `-s` or `--with-summary` option must come **before** the `run` command.
28
28
  ```bash
29
- ai-cli-log -s <command> [args...]
30
- # or
31
- ai-cli-log --with-summary <command> [args...]
29
+ ai-cli-log -s run <command> [args...]
30
+ # or specify a summarizer
31
+ ai-cli-log --with-summary=gemini-pro run <command> [args...]
32
32
  ```
33
- This will use your default summarizer to generate a descriptive filename like `gemini-20250713-153000-fix-database-connection-error.md`. You can also specify a summarizer: `ai-cli-log -s=my-ollama-summarizer ...`.
33
+ This will use your default (or specified) summarizer to generate a descriptive filename like `gemini-20250713-153000-fix-database-connection-error.md`.
34
34
 
35
35
  ## Configuration
36
36
 
@@ -42,19 +42,19 @@ ai-cli-log <command> [args...]
42
42
 
43
43
  You can create these files manually or by using the interactive setup command.
44
44
 
45
- ### Interactive Setup (`--init`)
45
+ ### Interactive Setup (`init`)
46
46
 
47
- The `--init` command helps you create a configuration file.
47
+ The `init` command helps you create a configuration file.
48
48
 
49
49
  * **To create a global configuration:**
50
50
  ```bash
51
- ai-cli-log --init
51
+ ai-cli-log init
52
52
  ```
53
53
  This saves the configuration to `~/.config/ai-cli-log/config.json`.
54
54
 
55
55
  * **To create a local (project-specific) configuration:**
56
56
  ```bash
57
- ai-cli-log --init --local
57
+ ai-cli-log init --local
58
58
  ```
59
59
  This saves the configuration to `.ai-cli-log/config.json` in the current directory.
60
60
 
@@ -118,7 +118,6 @@ Special thanks to Gemini for its invaluable help in the development of this tool
118
118
 
119
119
  ## TODO
120
120
 
121
- * **Argument Parsing:** Refactor the manual argument parsing to use a robust library like `yargs` or `commander` to improve maintainability and provide standard features like `--help`.
122
121
  * **Markdown Support:** Investigate and potentially implement saving logs in Markdown format, which could include session metadata (e.g., command, timestamp, summary) in a frontmatter block.
123
122
 
124
123
  ---
@@ -135,27 +134,27 @@ npm install -g ai-cli-log
135
134
 
136
135
  ## 使用方法
137
136
 
138
- 使用 `ai-cli-log` 命令来包装任何您想记录的命令。
137
+ 使用 `ai-cli-log` 命令来包装任何您想记录的命令。核心结构是:
139
138
 
140
139
  ```bash
141
- ai-cli-log <command> [args...]
140
+ ai-cli-log [全局选项] run <要记录的命令> [参数...]
142
141
  ```
143
142
 
144
143
  **示例:**
145
144
 
146
145
  - **基本日志记录:** 记录与 Google Gemini CLI 的会话。
147
146
  ```bash
148
- ai-cli-log gemini
147
+ ai-cli-log run gemini
149
148
  ```
150
149
  *日志将保存到 `.ai-cli-log/` 目录中。*
151
150
 
152
- - **AI 驱动的文件名:** 使用 AI 摘要作为日志文件名。
151
+ - **AI 驱动的文件名:** 使用 AI 摘要作为日志文件名。`-s` 或 `--with-summary` 选项必须在 `run` 命令**之前**。
153
152
  ```bash
154
- ai-cli-log -s <命令> [参数...]
155
- #
156
- ai-cli-log --with-summary <命令> [参数...]
153
+ ai-cli-log -s run <命令> [参数...]
154
+ # 或指定一个摘要器
155
+ ai-cli-log --with-summary=gemini-pro run <命令> [参数...]
157
156
  ```
158
- 这将使用您的默认摘要器生成一个描述性的文件名,例如 `gemini-20250713-153000-fix-database-connection-error.md`。您也可以指定一个摘要器:`ai-cli-log -s=my-ollama-summarizer ...`。
157
+ 这将使用您默认(或指定)的摘要器生成一个描述性的文件名,例如 `gemini-20250713-153000-fix-database-connection-error.md`。
159
158
 
160
159
  ## 配置
161
160
 
@@ -167,19 +166,19 @@ ai-cli-log <command> [args...]
167
166
 
168
167
  您可以通过手动或使用交互式设置命令来创建这些文件。
169
168
 
170
- ### 交互式设置 (`--init`)
169
+ ### 交互式设置 (`init`)
171
170
 
172
- `--init` 命令可以帮助您创建配置文件。
171
+ `init` 命令可以帮助您创建配置文件。
173
172
 
174
173
  * **创建全局配置文件:**
175
174
  ```bash
176
- ai-cli-log --init
175
+ ai-cli-log init
177
176
  ```
178
- 这会将配置保存到 `~/.config/ai-cli-log/config.json`。
177
+ This saves the configuration to `~/.config/ai-cli-log/config.json`.
179
178
 
180
179
  * **创建本地(项目特定)配置文件:**
181
180
  ```bash
182
- ai-cli-log --init --local
181
+ ai-cli-log init --local
183
182
  ```
184
183
  这会将配置保存到当前目录的 `.ai-cli-log/config.json` 中。
185
184
 
package/dist/index.js CHANGED
@@ -44,6 +44,7 @@ const os = __importStar(require("os"));
44
44
  const child_process_1 = require("child_process");
45
45
  const headless_1 = require("@xterm/headless");
46
46
  const readline_1 = __importDefault(require("readline"));
47
+ const commander_1 = require("commander");
47
48
  // --- 1. CONFIGURATION & TYPE DEFINITIONS ---
48
49
  const GLOBAL_CONFIG_DIR = path.join(os.homedir(), '.config', 'ai-cli-log');
49
50
  const GLOBAL_CONFIG_PATH = path.join(GLOBAL_CONFIG_DIR, 'config.json');
@@ -125,7 +126,7 @@ async function handleInitCommand(isLocal) {
125
126
  }
126
127
  const config = readConfig();
127
128
  const summarizersToUpdate = [];
128
- const newPrompt = 'You are a log summarizer. Your response MUST be a valid JSON object with one key: "summary" (a 3-5 word, lowercase, filename-friendly phrase). Example: {"summary": "refactor-database-schema"}. The session content is:';
129
+ const newPrompt = 'You are a log summarizer. Your response MUST be a raw, valid JSON object and nothing else. Do not wrap it in markdown blocks like ```json. The JSON object must have a single key "summary" which is a 3-5 word, lowercase, filename-friendly phrase. Example: {"summary": "refactor-database-schema"}. The session content is:';
129
130
  if (availableTools.includes('gemini')) {
130
131
  const add = await ask('\n> Found Gemini. Add/update the \'gemini-pro\' summarizer? (Y/n): ');
131
132
  if (add.toLowerCase() !== 'n') {
@@ -345,7 +346,10 @@ function runLoggingSession(command, commandArgs, summaryArg) {
345
346
  const config = readConfig();
346
347
  const summarizerName = (typeof summaryArg === 'string' ? summaryArg : config.summarizer.default) || 'default';
347
348
  try {
348
- const summaryData = JSON.parse(rawSummaryJson);
349
+ // Pre-process the raw summary to extract JSON from markdown blocks if present
350
+ const jsonMatch = rawSummaryJson.match(/\{.*\}/s);
351
+ const cleanJson = jsonMatch ? jsonMatch[0] : rawSummaryJson;
352
+ const summaryData = JSON.parse(cleanJson);
349
353
  const slug = summaryData.summary;
350
354
  if (typeof slug !== 'string') {
351
355
  throw new Error('Invalid JSON structure from summarizer: "summary" key is missing or not a string.');
@@ -382,34 +386,39 @@ function runLoggingSession(command, commandArgs, summaryArg) {
382
386
  }
383
387
  // --- 3. MAIN ENTRY POINT & ARGUMENT PARSER ---
384
388
  function main() {
385
- const args = process.argv.slice(2);
386
- if (args.includes('--version') || args.includes('-v')) {
387
- // eslint-disable-next-line @typescript-eslint/no-var-requires
388
- const pkg = require('../package.json');
389
- console.log(pkg.version);
390
- return;
391
- }
392
- if (args.includes('--init')) {
393
- const isLocal = args.includes('--local');
394
- handleInitCommand(isLocal);
395
- return;
396
- }
397
- const summaryArgRaw = args.find(arg => arg.startsWith('--with-summary') || arg.startsWith('-s'));
398
- const otherArgs = args.filter(arg => !arg.startsWith('--with-summary') &&
399
- !arg.startsWith('-s') &&
400
- arg !== '--init' &&
401
- arg !== '--local');
402
- const command = otherArgs[0];
403
- const commandArgs = otherArgs.slice(1);
404
- if (!command) {
405
- console.error('Usage: ai-cli-log [-s[=<summarizer>]] <command> [args...]');
406
- console.error(' ai-cli-log --init [--local]');
407
- process.exit(1);
408
- }
409
- let summaryArg = false;
410
- if (summaryArgRaw) {
411
- summaryArg = summaryArgRaw.includes('=') ? summaryArgRaw.split('=')[1] : true;
389
+ const program = new commander_1.Command();
390
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
391
+ const pkg = require('../package.json');
392
+ program
393
+ .name('ai-cli-log')
394
+ .description('A CLI tool to seamlessly log terminal sessions with AI models.')
395
+ .version(pkg.version, '-v, --version', 'Output the current version')
396
+ .option('-s, --with-summary [summarizer]', 'Enable AI summary for the session. Optionally specify a summarizer.');
397
+ program
398
+ .command('init')
399
+ .description('Initialize or update the configuration file.')
400
+ .option('--local', 'Create the configuration file in the current directory\'s .ai-cli-log folder.', false)
401
+ .action((options) => {
402
+ handleInitCommand(options.local);
403
+ });
404
+ program
405
+ .command('run')
406
+ .usage('<command> [args...]')
407
+ .description("Run a command and log the session. Any options for ai-cli-log itself (like -s) must come before the 'run' command.")
408
+ .argument('<command>', 'The command to execute and log.')
409
+ .argument('[args...]', 'Arguments for the command.')
410
+ .allowUnknownOption()
411
+ .action((command, args) => {
412
+ const options = program.opts();
413
+ let summaryArg = false;
414
+ if (options.withSummary) {
415
+ summaryArg = typeof options.withSummary === 'string' ? options.withSummary : true;
416
+ }
417
+ runLoggingSession(command, args, summaryArg);
418
+ });
419
+ program.parse(process.argv);
420
+ if (!process.argv.slice(2).length) {
421
+ program.help();
412
422
  }
413
- runLoggingSession(command, commandArgs, summaryArg);
414
423
  }
415
424
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-cli-log",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Seamlessly log your AI-powered coding conversations. This command-line interface (CLI) tool captures your terminal interactions with AI models like Gemini and Claude, saving entire sessions as clean plain text documents for easy review and documentation.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -23,12 +23,14 @@
23
23
  },
24
24
  "homepage": "https://github.com/alingse/ai-cli-log#readme",
25
25
  "devDependencies": {
26
+ "@types/commander": "^2.12.0",
26
27
  "@types/node": "^24.0.10",
27
28
  "ts-node": "^10.9.2",
28
29
  "typescript": "^5.8.3"
29
30
  },
30
31
  "dependencies": {
31
32
  "@xterm/headless": "^5.5.0",
33
+ "commander": "^14.0.0",
32
34
  "node-pty": "^1.0.0"
33
35
  }
34
36
  }
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ import * as os from 'os';
7
7
  import { spawn } from 'child_process';
8
8
  import { Terminal } from '@xterm/headless';
9
9
  import readline from 'readline';
10
+ import { Command } from 'commander';
10
11
 
11
12
  // --- 1. CONFIGURATION & TYPE DEFINITIONS ---
12
13
 
@@ -110,7 +111,7 @@ async function handleInitCommand(isLocal: boolean) {
110
111
 
111
112
  const config = readConfig();
112
113
  const summarizersToUpdate: Summarizer[] = [];
113
- const newPrompt = 'You are a log summarizer. Your response MUST be a valid JSON object with one key: "summary" (a 3-5 word, lowercase, filename-friendly phrase). Example: {"summary": "refactor-database-schema"}. The session content is:';
114
+ const newPrompt = 'You are a log summarizer. Your response MUST be a raw, valid JSON object and nothing else. Do not wrap it in markdown blocks like ```json. The JSON object must have a single key "summary" which is a 3-5 word, lowercase, filename-friendly phrase. Example: {"summary": "refactor-database-schema"}. The session content is:';
114
115
 
115
116
  if (availableTools.includes('gemini')) {
116
117
  const add = await ask('\n> Found Gemini. Add/update the \'gemini-pro\' summarizer? (Y/n): ');
@@ -357,7 +358,11 @@ function runLoggingSession(command: string, commandArgs: string[], summaryArg?:
357
358
  const summarizerName = (typeof summaryArg === 'string' ? summaryArg : config.summarizer.default) || 'default';
358
359
 
359
360
  try {
360
- const summaryData = JSON.parse(rawSummaryJson);
361
+ // Pre-process the raw summary to extract JSON from markdown blocks if present
362
+ const jsonMatch = rawSummaryJson.match(/\{.*\}/s);
363
+ const cleanJson = jsonMatch ? jsonMatch[0] : rawSummaryJson;
364
+
365
+ const summaryData = JSON.parse(cleanJson);
361
366
  const slug = summaryData.summary;
362
367
 
363
368
  if (typeof slug !== 'string') {
@@ -399,45 +404,45 @@ function runLoggingSession(command: string, commandArgs: string[], summaryArg?:
399
404
  // --- 3. MAIN ENTRY POINT & ARGUMENT PARSER ---
400
405
 
401
406
  function main() {
402
- const args = process.argv.slice(2);
403
-
404
- if (args.includes('--version') || args.includes('-v')) {
405
- // eslint-disable-next-line @typescript-eslint/no-var-requires
406
- const pkg = require('../package.json');
407
- console.log(pkg.version);
408
- return;
409
- }
407
+ const program = new Command();
408
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
409
+ const pkg = require('../package.json');
410
+
411
+ program
412
+ .name('ai-cli-log')
413
+ .description('A CLI tool to seamlessly log terminal sessions with AI models.')
414
+ .version(pkg.version, '-v, --version', 'Output the current version')
415
+ .option('-s, --with-summary [summarizer]', 'Enable AI summary for the session. Optionally specify a summarizer.');
416
+
417
+ program
418
+ .command('init')
419
+ .description('Initialize or update the configuration file.')
420
+ .option('--local', 'Create the configuration file in the current directory\'s .ai-cli-log folder.', false)
421
+ .action((options) => {
422
+ handleInitCommand(options.local);
423
+ });
410
424
 
411
- if (args.includes('--init')) {
412
- const isLocal = args.includes('--local');
413
- handleInitCommand(isLocal);
414
- return;
415
- }
425
+ program
426
+ .command('run')
427
+ .usage('<command> [args...]')
428
+ .description("Run a command and log the session. Any options for ai-cli-log itself (like -s) must come before the 'run' command.")
429
+ .argument('<command>', 'The command to execute and log.')
430
+ .argument('[args...]', 'Arguments for the command.')
431
+ .allowUnknownOption()
432
+ .action((command, args) => {
433
+ const options = program.opts();
434
+ let summaryArg: string | boolean = false;
435
+ if (options.withSummary) {
436
+ summaryArg = typeof options.withSummary === 'string' ? options.withSummary : true;
437
+ }
438
+ runLoggingSession(command, args, summaryArg);
439
+ });
416
440
 
417
- const summaryArgRaw = args.find(arg => arg.startsWith('--with-summary') || arg.startsWith('-s'));
418
-
419
- const otherArgs = args.filter(arg =>
420
- !arg.startsWith('--with-summary') &&
421
- !arg.startsWith('-s') &&
422
- arg !== '--init' &&
423
- arg !== '--local'
424
- );
425
-
426
- const command = otherArgs[0];
427
- const commandArgs = otherArgs.slice(1);
428
-
429
- if (!command) {
430
- console.error('Usage: ai-cli-log [-s[=<summarizer>]] <command> [args...]');
431
- console.error(' ai-cli-log --init [--local]');
432
- process.exit(1);
433
- }
441
+ program.parse(process.argv);
434
442
 
435
- let summaryArg: string | boolean = false;
436
- if (summaryArgRaw) {
437
- summaryArg = summaryArgRaw.includes('=') ? summaryArgRaw.split('=')[1] : true;
443
+ if (!process.argv.slice(2).length) {
444
+ program.help();
438
445
  }
439
-
440
- runLoggingSession(command, commandArgs, summaryArg);
441
446
  }
442
447
 
443
- main();
448
+ main();