@towles/tool 0.0.18 → 0.0.41
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/LICENSE +21 -0
- package/LICENSE.md +9 -10
- package/README.md +121 -78
- package/bin/run.ts +5 -0
- package/package.json +63 -53
- package/patches/prompts.patch +34 -0
- package/src/commands/base.ts +42 -0
- package/src/commands/config.test.ts +15 -0
- package/src/commands/config.ts +43 -0
- package/src/commands/doctor.ts +133 -0
- package/src/commands/gh/branch-clean.ts +110 -0
- package/src/commands/gh/branch.test.ts +124 -0
- package/src/commands/gh/branch.ts +132 -0
- package/src/commands/gh/pr.ts +168 -0
- package/src/commands/index.ts +55 -0
- package/src/commands/install.ts +148 -0
- package/src/commands/journal/daily-notes.ts +66 -0
- package/src/commands/journal/meeting.ts +83 -0
- package/src/commands/journal/note.ts +83 -0
- package/src/commands/journal/utils.ts +399 -0
- package/src/commands/observe/graph.test.ts +89 -0
- package/src/commands/observe/graph.ts +1640 -0
- package/src/commands/observe/report.ts +166 -0
- package/src/commands/observe/session.ts +385 -0
- package/src/commands/observe/setup.ts +180 -0
- package/src/commands/observe/status.ts +146 -0
- package/src/commands/ralph/lib/execution.ts +302 -0
- package/src/commands/ralph/lib/formatter.ts +298 -0
- package/src/commands/ralph/lib/index.ts +4 -0
- package/src/commands/ralph/lib/marker.ts +108 -0
- package/src/commands/ralph/lib/state.ts +191 -0
- package/src/commands/ralph/marker/create.ts +23 -0
- package/src/commands/ralph/plan.ts +73 -0
- package/src/commands/ralph/progress.ts +44 -0
- package/src/commands/ralph/ralph.test.ts +673 -0
- package/src/commands/ralph/run.ts +408 -0
- package/src/commands/ralph/task/add.ts +105 -0
- package/src/commands/ralph/task/done.ts +73 -0
- package/src/commands/ralph/task/list.test.ts +48 -0
- package/src/commands/ralph/task/list.ts +110 -0
- package/src/commands/ralph/task/remove.ts +62 -0
- package/src/config/context.ts +7 -0
- package/src/config/settings.ts +155 -0
- package/src/constants.ts +3 -0
- package/src/types/journal.ts +16 -0
- package/src/utils/anthropic/types.ts +158 -0
- package/src/utils/date-utils.test.ts +96 -0
- package/src/utils/date-utils.ts +54 -0
- package/src/utils/exec.ts +8 -0
- package/src/utils/git/gh-cli-wrapper.test.ts +14 -0
- package/src/utils/git/gh-cli-wrapper.ts +54 -0
- package/src/utils/git/git-wrapper.test.ts +26 -0
- package/src/utils/git/git-wrapper.ts +15 -0
- package/src/utils/git/git.ts +25 -0
- package/src/utils/render.test.ts +71 -0
- package/src/utils/render.ts +34 -0
- package/dist/index.d.mts +0 -1
- package/dist/index.mjs +0 -794
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Explicit command exports for Bun compiled binaries
|
|
2
|
+
// oclif's pattern-based discovery doesn't work with bundled executables
|
|
3
|
+
|
|
4
|
+
import GhBranchClean from "./gh/branch-clean.js";
|
|
5
|
+
import Config from "./config.js";
|
|
6
|
+
import Doctor from "./doctor.js";
|
|
7
|
+
import GhBranch from "./gh/branch.js";
|
|
8
|
+
import GhPr from "./gh/pr.js";
|
|
9
|
+
import Install from "./install.js";
|
|
10
|
+
import RalphRun from "./ralph/run.js";
|
|
11
|
+
import RalphPlan from "./ralph/plan.js";
|
|
12
|
+
import RalphProgress from "./ralph/progress.js";
|
|
13
|
+
import RalphMarkerCreate from "./ralph/marker/create.js";
|
|
14
|
+
import RalphTaskAdd from "./ralph/task/add.js";
|
|
15
|
+
import RalphTaskDone from "./ralph/task/done.js";
|
|
16
|
+
import RalphTaskList from "./ralph/task/list.js";
|
|
17
|
+
import RalphTaskRemove from "./ralph/task/remove.js";
|
|
18
|
+
import JournalDailyNotes from "./journal/daily-notes.js";
|
|
19
|
+
import JournalMeeting from "./journal/meeting.js";
|
|
20
|
+
import JournalNote from "./journal/note.js";
|
|
21
|
+
import ObserveSetup from "./observe/setup.js";
|
|
22
|
+
import ObserveStatus from "./observe/status.js";
|
|
23
|
+
import ObserveReport from "./observe/report.js";
|
|
24
|
+
import ObserveGraph from "./observe/graph.js";
|
|
25
|
+
import ObserveSession from "./observe/session.js";
|
|
26
|
+
|
|
27
|
+
export default {
|
|
28
|
+
config: Config,
|
|
29
|
+
doctor: Doctor,
|
|
30
|
+
"gh:branch": GhBranch,
|
|
31
|
+
"gh:branch-clean": GhBranchClean,
|
|
32
|
+
"gh:pr": GhPr,
|
|
33
|
+
install: Install,
|
|
34
|
+
"ralph:run": RalphRun,
|
|
35
|
+
"ralph:plan": RalphPlan,
|
|
36
|
+
"ralph:progress": RalphProgress,
|
|
37
|
+
"ralph:marker:create": RalphMarkerCreate,
|
|
38
|
+
"ralph:task:add": RalphTaskAdd,
|
|
39
|
+
"ralph:task:done": RalphTaskDone,
|
|
40
|
+
"ralph:task:list": RalphTaskList,
|
|
41
|
+
"ralph:task:remove": RalphTaskRemove,
|
|
42
|
+
"journal:daily-notes": JournalDailyNotes,
|
|
43
|
+
"journal:meeting": JournalMeeting,
|
|
44
|
+
"journal:note": JournalNote,
|
|
45
|
+
"observe:setup": ObserveSetup,
|
|
46
|
+
"observe:status": ObserveStatus,
|
|
47
|
+
"observe:report": ObserveReport,
|
|
48
|
+
"observe:graph": ObserveGraph,
|
|
49
|
+
"observe:session": ObserveSession,
|
|
50
|
+
// Aliases
|
|
51
|
+
graph: ObserveGraph,
|
|
52
|
+
today: JournalDailyNotes,
|
|
53
|
+
pr: GhPr,
|
|
54
|
+
run: RalphRun,
|
|
55
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { Flags } from "@oclif/core";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
import consola from "consola";
|
|
7
|
+
import { BaseCommand } from "./base.js";
|
|
8
|
+
|
|
9
|
+
const CLAUDE_SETTINGS_PATH = path.join(homedir(), ".claude", "settings.json");
|
|
10
|
+
|
|
11
|
+
interface ClaudeSettings {
|
|
12
|
+
cleanupPeriodDays?: number;
|
|
13
|
+
alwaysThinkingEnabled?: boolean;
|
|
14
|
+
hooks?: Record<string, unknown[]>;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Install and configure towles-tool with Claude Code
|
|
20
|
+
*/
|
|
21
|
+
export default class Install extends BaseCommand {
|
|
22
|
+
static override description =
|
|
23
|
+
"Configure Claude Code settings and optionally enable observability";
|
|
24
|
+
|
|
25
|
+
static override examples = [
|
|
26
|
+
"<%= config.bin %> install",
|
|
27
|
+
"<%= config.bin %> install --observability",
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
static override flags = {
|
|
31
|
+
...BaseCommand.baseFlags,
|
|
32
|
+
observability: Flags.boolean({
|
|
33
|
+
char: "o",
|
|
34
|
+
description: "Show OTEL setup instructions and configure SubagentStop hook",
|
|
35
|
+
default: false,
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
async run(): Promise<void> {
|
|
40
|
+
const { flags } = await this.parse(Install);
|
|
41
|
+
|
|
42
|
+
this.log(pc.bold("\n🔧 towles-tool install\n"));
|
|
43
|
+
|
|
44
|
+
// Load or create Claude settings
|
|
45
|
+
let claudeSettings: ClaudeSettings = {};
|
|
46
|
+
if (fs.existsSync(CLAUDE_SETTINGS_PATH)) {
|
|
47
|
+
try {
|
|
48
|
+
const content = fs.readFileSync(CLAUDE_SETTINGS_PATH, "utf-8");
|
|
49
|
+
claudeSettings = JSON.parse(content);
|
|
50
|
+
this.log(pc.dim(`Found existing Claude settings at ${CLAUDE_SETTINGS_PATH}`));
|
|
51
|
+
} catch {
|
|
52
|
+
this.log(
|
|
53
|
+
pc.yellow(`Warning: Could not parse ${CLAUDE_SETTINGS_PATH}, will create fresh settings`),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
this.log(pc.dim(`No Claude settings file found, will create one`));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Configure recommended settings
|
|
61
|
+
let modified = false;
|
|
62
|
+
|
|
63
|
+
// Prevent log deletion (set to ~274 years)
|
|
64
|
+
if (claudeSettings.cleanupPeriodDays !== 99999) {
|
|
65
|
+
claudeSettings.cleanupPeriodDays = 99999;
|
|
66
|
+
modified = true;
|
|
67
|
+
this.log(pc.green("✓ Set cleanupPeriodDays: 99999 (prevent log deletion)"));
|
|
68
|
+
} else {
|
|
69
|
+
this.log(pc.dim("✓ cleanupPeriodDays already set to 99999"));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Enable thinking by default
|
|
73
|
+
if (claudeSettings.alwaysThinkingEnabled !== true) {
|
|
74
|
+
claudeSettings.alwaysThinkingEnabled = true;
|
|
75
|
+
modified = true;
|
|
76
|
+
this.log(pc.green("✓ Set alwaysThinkingEnabled: true"));
|
|
77
|
+
} else {
|
|
78
|
+
this.log(pc.dim("✓ alwaysThinkingEnabled already set to true"));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Save settings if modified
|
|
82
|
+
if (modified) {
|
|
83
|
+
this.saveClaudeSettings(claudeSettings);
|
|
84
|
+
this.log(pc.green(`\n✓ Saved Claude settings to ${CLAUDE_SETTINGS_PATH}`));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Show observability setup if requested
|
|
88
|
+
if (flags.observability) {
|
|
89
|
+
this.log(pc.bold("\n📊 Observability Setup\n"));
|
|
90
|
+
this.showOtelInstructions();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.log(pc.bold(pc.green("\n✅ Installation complete!\n")));
|
|
94
|
+
|
|
95
|
+
// Offer to install plugins from marketplace
|
|
96
|
+
this.log(pc.cyan("To install plugins from the Claude Code marketplace:"));
|
|
97
|
+
this.log(
|
|
98
|
+
pc.dim(" claude /plugins marketplace add https://github.com/ChrisTowles/towles-tool"),
|
|
99
|
+
);
|
|
100
|
+
this.log("");
|
|
101
|
+
|
|
102
|
+
const answer = await consola.prompt("Install tt-core plugin from marketplace now?", {
|
|
103
|
+
type: "confirm",
|
|
104
|
+
initial: true,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (answer) {
|
|
108
|
+
const { x } = await import("tinyexec");
|
|
109
|
+
this.log("");
|
|
110
|
+
|
|
111
|
+
const result = await x("claude", ["plugin", "install", "tt@towles-tool", "--scope", "user"]);
|
|
112
|
+
if (result.stdout) this.log(result.stdout);
|
|
113
|
+
if (result.stderr) this.log(pc.dim(result.stderr));
|
|
114
|
+
if (result.exitCode === 0) {
|
|
115
|
+
this.log(pc.green("✓ tt-core plugin installed"));
|
|
116
|
+
} else {
|
|
117
|
+
this.log(pc.yellow(`Command exited with code ${result.exitCode}`));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.log("");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private saveClaudeSettings(settings: ClaudeSettings): void {
|
|
125
|
+
const dir = path.dirname(CLAUDE_SETTINGS_PATH);
|
|
126
|
+
if (!fs.existsSync(dir)) {
|
|
127
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
128
|
+
}
|
|
129
|
+
fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private showOtelInstructions(): void {
|
|
133
|
+
this.log(pc.cyan("Add these environment variables to your shell profile:\n"));
|
|
134
|
+
|
|
135
|
+
consola.box(`export CLAUDE_CODE_ENABLE_TELEMETRY=1
|
|
136
|
+
export OTEL_METRICS_EXPORTER=otlp
|
|
137
|
+
export OTEL_LOGS_EXPORTER=otlp
|
|
138
|
+
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317`);
|
|
139
|
+
|
|
140
|
+
this.log("");
|
|
141
|
+
this.log(
|
|
142
|
+
pc.dim("For more info, see: https://github.com/anthropics/claude-code-monitoring-guide"),
|
|
143
|
+
);
|
|
144
|
+
this.log("");
|
|
145
|
+
this.log(pc.cyan("Quick cost analysis (no setup required):"));
|
|
146
|
+
this.log(pc.dim(" npx ccusage@latest --breakdown"));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { existsSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import consola from "consola";
|
|
5
|
+
import { colors } from "consola/utils";
|
|
6
|
+
import { BaseCommand } from "../base.js";
|
|
7
|
+
import { JOURNAL_TYPES } from "../../types/journal.js";
|
|
8
|
+
import {
|
|
9
|
+
createJournalContent,
|
|
10
|
+
ensureDirectoryExists,
|
|
11
|
+
ensureTemplatesExist,
|
|
12
|
+
generateJournalFileInfoByType,
|
|
13
|
+
openInEditor,
|
|
14
|
+
} from "./utils.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create or open daily notes journal file
|
|
18
|
+
*/
|
|
19
|
+
export default class DailyNotes extends BaseCommand {
|
|
20
|
+
static override description = "Weekly files with daily sections for ongoing work and notes";
|
|
21
|
+
|
|
22
|
+
static override examples = [
|
|
23
|
+
"<%= config.bin %> journal daily-notes",
|
|
24
|
+
"<%= config.bin %> journal today",
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
async run(): Promise<void> {
|
|
28
|
+
await this.parse(DailyNotes);
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const journalSettings = this.settings.settingsFile.settings.journalSettings;
|
|
32
|
+
const templateDir = journalSettings.templateDir;
|
|
33
|
+
|
|
34
|
+
// Ensure templates exist on first run
|
|
35
|
+
ensureTemplatesExist(templateDir);
|
|
36
|
+
|
|
37
|
+
const currentDate = new Date();
|
|
38
|
+
const fileInfo = generateJournalFileInfoByType({
|
|
39
|
+
journalSettings,
|
|
40
|
+
date: currentDate,
|
|
41
|
+
type: JOURNAL_TYPES.DAILY_NOTES,
|
|
42
|
+
title: "",
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Ensure journal directory exists
|
|
46
|
+
ensureDirectoryExists(path.dirname(fileInfo.fullPath));
|
|
47
|
+
|
|
48
|
+
if (existsSync(fileInfo.fullPath)) {
|
|
49
|
+
consola.info(`Opening existing daily-notes file: ${colors.cyan(fileInfo.fullPath)}`);
|
|
50
|
+
} else {
|
|
51
|
+
const content = createJournalContent({ mondayDate: fileInfo.mondayDate, templateDir });
|
|
52
|
+
consola.info(`Creating new daily-notes file: ${colors.cyan(fileInfo.fullPath)}`);
|
|
53
|
+
writeFileSync(fileInfo.fullPath, content, "utf8");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await openInEditor({
|
|
57
|
+
editor: this.settings.settingsFile.settings.preferredEditor,
|
|
58
|
+
filePath: fileInfo.fullPath,
|
|
59
|
+
folderPath: journalSettings.baseFolder,
|
|
60
|
+
});
|
|
61
|
+
} catch (error) {
|
|
62
|
+
consola.warn(`Error creating daily-notes file:`, error);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { existsSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { Args } from "@oclif/core";
|
|
5
|
+
import consola from "consola";
|
|
6
|
+
import { colors } from "consola/utils";
|
|
7
|
+
import { BaseCommand } from "../base.js";
|
|
8
|
+
import { JOURNAL_TYPES } from "../../types/journal.js";
|
|
9
|
+
import {
|
|
10
|
+
createMeetingContent,
|
|
11
|
+
ensureDirectoryExists,
|
|
12
|
+
ensureTemplatesExist,
|
|
13
|
+
generateJournalFileInfoByType,
|
|
14
|
+
openInEditor,
|
|
15
|
+
} from "./utils.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create or open meeting notes file
|
|
19
|
+
*/
|
|
20
|
+
export default class Meeting extends BaseCommand {
|
|
21
|
+
static override description = "Structured meeting notes with agenda and action items";
|
|
22
|
+
|
|
23
|
+
static override args = {
|
|
24
|
+
title: Args.string({
|
|
25
|
+
description: "Meeting title",
|
|
26
|
+
required: false,
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
static override examples = [
|
|
31
|
+
"<%= config.bin %> journal meeting",
|
|
32
|
+
'<%= config.bin %> journal meeting "Sprint Planning"',
|
|
33
|
+
'<%= config.bin %> journal m "Standup"',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
async run(): Promise<void> {
|
|
37
|
+
const { args } = await this.parse(Meeting);
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const journalSettings = this.settings.settingsFile.settings.journalSettings;
|
|
41
|
+
const templateDir = journalSettings.templateDir;
|
|
42
|
+
|
|
43
|
+
// Ensure templates exist on first run
|
|
44
|
+
ensureTemplatesExist(templateDir);
|
|
45
|
+
|
|
46
|
+
// Prompt for title if not provided
|
|
47
|
+
let title = args.title || "";
|
|
48
|
+
if (title.trim().length === 0) {
|
|
49
|
+
title = await consola.prompt(`Enter meeting title:`, {
|
|
50
|
+
type: "text",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const currentDate = new Date();
|
|
55
|
+
const fileInfo = generateJournalFileInfoByType({
|
|
56
|
+
journalSettings,
|
|
57
|
+
date: currentDate,
|
|
58
|
+
type: JOURNAL_TYPES.MEETING,
|
|
59
|
+
title,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Ensure journal directory exists
|
|
63
|
+
ensureDirectoryExists(path.dirname(fileInfo.fullPath));
|
|
64
|
+
|
|
65
|
+
if (existsSync(fileInfo.fullPath)) {
|
|
66
|
+
consola.info(`Opening existing meeting file: ${colors.cyan(fileInfo.fullPath)}`);
|
|
67
|
+
} else {
|
|
68
|
+
const content = createMeetingContent({ title, date: currentDate, templateDir });
|
|
69
|
+
consola.info(`Creating new meeting file: ${colors.cyan(fileInfo.fullPath)}`);
|
|
70
|
+
writeFileSync(fileInfo.fullPath, content, "utf8");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
await openInEditor({
|
|
74
|
+
editor: this.settings.settingsFile.settings.preferredEditor,
|
|
75
|
+
filePath: fileInfo.fullPath,
|
|
76
|
+
folderPath: journalSettings.baseFolder,
|
|
77
|
+
});
|
|
78
|
+
} catch (error) {
|
|
79
|
+
consola.warn(`Error creating meeting file:`, error);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { existsSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { Args } from "@oclif/core";
|
|
5
|
+
import consola from "consola";
|
|
6
|
+
import { colors } from "consola/utils";
|
|
7
|
+
import { BaseCommand } from "../base.js";
|
|
8
|
+
import { JOURNAL_TYPES } from "../../types/journal.js";
|
|
9
|
+
import {
|
|
10
|
+
createNoteContent,
|
|
11
|
+
ensureDirectoryExists,
|
|
12
|
+
ensureTemplatesExist,
|
|
13
|
+
generateJournalFileInfoByType,
|
|
14
|
+
openInEditor,
|
|
15
|
+
} from "./utils.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create or open general-purpose note file
|
|
19
|
+
*/
|
|
20
|
+
export default class Note extends BaseCommand {
|
|
21
|
+
static override description = "General-purpose notes with structured sections";
|
|
22
|
+
|
|
23
|
+
static override args = {
|
|
24
|
+
title: Args.string({
|
|
25
|
+
description: "Note title",
|
|
26
|
+
required: false,
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
static override examples = [
|
|
31
|
+
"<%= config.bin %> journal note",
|
|
32
|
+
'<%= config.bin %> journal note "Research Notes"',
|
|
33
|
+
'<%= config.bin %> journal n "Ideas"',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
async run(): Promise<void> {
|
|
37
|
+
const { args } = await this.parse(Note);
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const journalSettings = this.settings.settingsFile.settings.journalSettings;
|
|
41
|
+
const templateDir = journalSettings.templateDir;
|
|
42
|
+
|
|
43
|
+
// Ensure templates exist on first run
|
|
44
|
+
ensureTemplatesExist(templateDir);
|
|
45
|
+
|
|
46
|
+
// Prompt for title if not provided
|
|
47
|
+
let title = args.title || "";
|
|
48
|
+
if (title.trim().length === 0) {
|
|
49
|
+
title = await consola.prompt(`Enter note title:`, {
|
|
50
|
+
type: "text",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const currentDate = new Date();
|
|
55
|
+
const fileInfo = generateJournalFileInfoByType({
|
|
56
|
+
journalSettings,
|
|
57
|
+
date: currentDate,
|
|
58
|
+
type: JOURNAL_TYPES.NOTE,
|
|
59
|
+
title,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Ensure journal directory exists
|
|
63
|
+
ensureDirectoryExists(path.dirname(fileInfo.fullPath));
|
|
64
|
+
|
|
65
|
+
if (existsSync(fileInfo.fullPath)) {
|
|
66
|
+
consola.info(`Opening existing note file: ${colors.cyan(fileInfo.fullPath)}`);
|
|
67
|
+
} else {
|
|
68
|
+
const content = createNoteContent({ title, date: currentDate, templateDir });
|
|
69
|
+
consola.info(`Creating new note file: ${colors.cyan(fileInfo.fullPath)}`);
|
|
70
|
+
writeFileSync(fileInfo.fullPath, content, "utf8");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
await openInEditor({
|
|
74
|
+
editor: this.settings.settingsFile.settings.preferredEditor,
|
|
75
|
+
filePath: fileInfo.fullPath,
|
|
76
|
+
folderPath: journalSettings.baseFolder,
|
|
77
|
+
});
|
|
78
|
+
} catch (error) {
|
|
79
|
+
consola.warn(`Error creating note file:`, error);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|