docrev 0.9.13 → 0.9.14
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/.claude/settings.local.json +9 -9
- package/.gitattributes +1 -1
- package/CHANGELOG.md +149 -149
- package/PLAN-tables-and-postprocess.md +850 -850
- package/README.md +391 -391
- package/bin/rev.js +11 -11
- package/bin/rev.ts +145 -145
- package/completions/rev.bash +127 -127
- package/completions/rev.ps1 +210 -210
- package/completions/rev.zsh +207 -207
- package/dev_notes/stress2/build_adversarial.ts +186 -186
- package/dev_notes/stress2/drift_matcher.ts +62 -62
- package/dev_notes/stress2/probe_anchors.ts +35 -35
- package/dev_notes/stress2/project/discussion.before.md +3 -3
- package/dev_notes/stress2/project/discussion.md +3 -3
- package/dev_notes/stress2/project/methods.before.md +20 -20
- package/dev_notes/stress2/project/methods.md +20 -20
- package/dev_notes/stress2/project/rev.yaml +5 -5
- package/dev_notes/stress2/project/sections.yaml +4 -4
- package/dev_notes/stress2/sections.yaml +5 -5
- package/dev_notes/stress2/trace_placement.ts +50 -50
- package/dev_notes/stresstest_boundaries.ts +27 -27
- package/dev_notes/stresstest_drift_apply.ts +43 -43
- package/dev_notes/stresstest_drift_compare.ts +43 -43
- package/dev_notes/stresstest_drift_v2.ts +54 -54
- package/dev_notes/stresstest_inspect.ts +54 -54
- package/dev_notes/stresstest_pstyle.ts +55 -55
- package/dev_notes/stresstest_section_debug.ts +23 -23
- package/dev_notes/stresstest_split.ts +70 -70
- package/dev_notes/stresstest_trace.ts +19 -19
- package/dev_notes/stresstest_verify_no_overwrite.ts +40 -40
- package/dist/lib/build.d.ts +38 -1
- package/dist/lib/build.d.ts.map +1 -1
- package/dist/lib/build.js +68 -30
- package/dist/lib/build.js.map +1 -1
- package/dist/lib/commands/build.d.ts.map +1 -1
- package/dist/lib/commands/build.js +38 -5
- package/dist/lib/commands/build.js.map +1 -1
- package/dist/lib/commands/utilities.js +164 -164
- package/dist/lib/commands/word-tools.js +8 -8
- package/dist/lib/grammar.js +3 -3
- package/dist/lib/pdf-comments.js +44 -44
- package/dist/lib/plugins.js +57 -57
- package/dist/lib/pptx-themes.js +115 -115
- package/dist/lib/spelling.js +2 -2
- package/dist/lib/templates.js +387 -387
- package/dist/lib/themes.js +51 -51
- package/eslint.config.js +27 -27
- package/lib/anchor-match.ts +276 -276
- package/lib/annotations.ts +644 -644
- package/lib/build.ts +1300 -1251
- package/lib/citations.ts +160 -160
- package/lib/commands/build.ts +833 -801
- package/lib/commands/citations.ts +515 -515
- package/lib/commands/comments.ts +1050 -1050
- package/lib/commands/context.ts +174 -174
- package/lib/commands/core.ts +309 -309
- package/lib/commands/doi.ts +435 -435
- package/lib/commands/file-ops.ts +372 -372
- package/lib/commands/history.ts +320 -320
- package/lib/commands/index.ts +87 -87
- package/lib/commands/init.ts +259 -259
- package/lib/commands/merge-resolve.ts +378 -378
- package/lib/commands/preview.ts +178 -178
- package/lib/commands/project-info.ts +244 -244
- package/lib/commands/quality.ts +517 -517
- package/lib/commands/response.ts +454 -454
- package/lib/commands/section-boundaries.ts +82 -82
- package/lib/commands/sections.ts +451 -451
- package/lib/commands/sync.ts +706 -706
- package/lib/commands/text-ops.ts +449 -449
- package/lib/commands/utilities.ts +448 -448
- package/lib/commands/verify-anchors.ts +272 -272
- package/lib/commands/word-tools.ts +340 -340
- package/lib/comment-realign.ts +517 -517
- package/lib/config.ts +84 -84
- package/lib/crossref.ts +781 -781
- package/lib/csl.ts +191 -191
- package/lib/dependencies.ts +98 -98
- package/lib/diff-engine.ts +465 -465
- package/lib/doi-cache.ts +115 -115
- package/lib/doi.ts +897 -897
- package/lib/equations.ts +506 -506
- package/lib/errors.ts +346 -346
- package/lib/format.ts +541 -541
- package/lib/git.ts +326 -326
- package/lib/grammar.ts +303 -303
- package/lib/image-registry.ts +180 -180
- package/lib/import.ts +911 -911
- package/lib/journals.ts +543 -543
- package/lib/merge.ts +633 -633
- package/lib/orcid.ts +144 -144
- package/lib/pdf-comments.ts +263 -263
- package/lib/pdf-import.ts +524 -524
- package/lib/plugins.ts +362 -362
- package/lib/postprocess.ts +188 -188
- package/lib/pptx-color-filter.lua +37 -37
- package/lib/pptx-template.ts +469 -469
- package/lib/pptx-themes.ts +483 -483
- package/lib/protect-restore.ts +520 -520
- package/lib/rate-limiter.ts +94 -94
- package/lib/response.ts +197 -197
- package/lib/restore-references.ts +240 -240
- package/lib/review.ts +327 -327
- package/lib/schema.ts +417 -417
- package/lib/scientific-words.ts +73 -73
- package/lib/sections.ts +335 -335
- package/lib/slides.ts +756 -756
- package/lib/spelling.ts +334 -334
- package/lib/templates.ts +526 -526
- package/lib/themes.ts +742 -742
- package/lib/trackchanges.ts +247 -247
- package/lib/tui.ts +450 -450
- package/lib/types.ts +550 -550
- package/lib/undo.ts +250 -250
- package/lib/utils.ts +69 -69
- package/lib/variables.ts +179 -179
- package/lib/word-extraction.ts +806 -806
- package/lib/word.ts +643 -643
- package/lib/wordcomments.ts +817 -817
- package/package.json +137 -137
- package/scripts/postbuild.js +28 -28
- package/skill/REFERENCE.md +431 -431
- package/skill/SKILL.md +258 -258
- package/tsconfig.json +26 -26
- package/types/index.d.ts +525 -525
package/lib/commands/init.ts
CHANGED
|
@@ -1,259 +1,259 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Init commands: init, new, config
|
|
3
|
-
*
|
|
4
|
-
* Project initialization and configuration commands.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { Command } from 'commander';
|
|
8
|
-
import {
|
|
9
|
-
chalk,
|
|
10
|
-
fs,
|
|
11
|
-
path,
|
|
12
|
-
fmt,
|
|
13
|
-
generateConfig,
|
|
14
|
-
loadConfig,
|
|
15
|
-
saveConfig,
|
|
16
|
-
getTemplate,
|
|
17
|
-
listTemplates,
|
|
18
|
-
generateCustomTemplate,
|
|
19
|
-
getUserName,
|
|
20
|
-
setUserName,
|
|
21
|
-
getConfigPath,
|
|
22
|
-
getDefaultSections,
|
|
23
|
-
setDefaultSections,
|
|
24
|
-
} from './context.js';
|
|
25
|
-
|
|
26
|
-
interface InitOptions {
|
|
27
|
-
dir: string;
|
|
28
|
-
output: string;
|
|
29
|
-
force?: boolean;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface NewOptions {
|
|
33
|
-
template: string;
|
|
34
|
-
sections?: string;
|
|
35
|
-
list?: boolean;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Register init commands with the program
|
|
40
|
-
*/
|
|
41
|
-
export function register(program: Command): void {
|
|
42
|
-
// ==========================================================================
|
|
43
|
-
// INIT command - Generate sections.yaml config
|
|
44
|
-
// ==========================================================================
|
|
45
|
-
|
|
46
|
-
program
|
|
47
|
-
.command('init')
|
|
48
|
-
.description('Generate sections.yaml from existing .md files')
|
|
49
|
-
.option('-d, --dir <directory>', 'Directory to scan', '.')
|
|
50
|
-
.option('-o, --output <file>', 'Output config file', 'sections.yaml')
|
|
51
|
-
.option('--force', 'Overwrite existing config')
|
|
52
|
-
.action((options: InitOptions) => {
|
|
53
|
-
const dir = path.resolve(options.dir);
|
|
54
|
-
|
|
55
|
-
if (!fs.existsSync(dir)) {
|
|
56
|
-
console.error(chalk.red(`Directory not found: ${dir}`));
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const outputPath = path.resolve(options.dir, options.output);
|
|
61
|
-
|
|
62
|
-
if (fs.existsSync(outputPath) && !options.force) {
|
|
63
|
-
console.error(chalk.yellow(`Config already exists: ${outputPath}`));
|
|
64
|
-
console.error(chalk.dim('Use --force to overwrite'));
|
|
65
|
-
process.exit(1);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
console.log(chalk.cyan(`Scanning ${dir} for .md files...`));
|
|
69
|
-
|
|
70
|
-
const config = generateConfig(dir);
|
|
71
|
-
const sectionCount = Object.keys(config.sections).length;
|
|
72
|
-
|
|
73
|
-
if (sectionCount === 0) {
|
|
74
|
-
console.error(chalk.yellow('No .md files found (excluding paper.md, README.md)'));
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
saveConfig(outputPath, config);
|
|
79
|
-
|
|
80
|
-
console.log(chalk.green(`\nCreated ${outputPath} with ${sectionCount} sections:\n`));
|
|
81
|
-
|
|
82
|
-
for (const [file, section] of Object.entries(config.sections)) {
|
|
83
|
-
console.log(` ${chalk.bold(file)}`);
|
|
84
|
-
console.log(chalk.dim(` header: "${section.header}"`));
|
|
85
|
-
if (section.aliases && section.aliases.length > 0) {
|
|
86
|
-
console.log(chalk.dim(` aliases: ${JSON.stringify(section.aliases)}`));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
console.log(chalk.cyan('\nEdit this file to:'));
|
|
91
|
-
console.log(chalk.dim(' • Add aliases for header variations'));
|
|
92
|
-
console.log(chalk.dim(' • Adjust order if needed'));
|
|
93
|
-
console.log(chalk.dim(' • Update headers if they change'));
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// ==========================================================================
|
|
97
|
-
// NEW command - Create new paper project
|
|
98
|
-
// ==========================================================================
|
|
99
|
-
|
|
100
|
-
program
|
|
101
|
-
.command('new')
|
|
102
|
-
.description('Create a new paper project from template')
|
|
103
|
-
.argument('[name]', 'Project directory name')
|
|
104
|
-
.option('-t, --template <name>', 'Template: paper, minimal, thesis, review', 'paper')
|
|
105
|
-
.option('-s, --sections <sections>', 'Comma-separated section names (e.g., intro,methods,results)')
|
|
106
|
-
.option('--list', 'List available templates')
|
|
107
|
-
.action(async (name: string | undefined, options: NewOptions) => {
|
|
108
|
-
if (options.list) {
|
|
109
|
-
console.log(chalk.cyan('Available templates:\n'));
|
|
110
|
-
for (const t of listTemplates()) {
|
|
111
|
-
console.log(` ${chalk.bold(t.id)} - ${t.description}`);
|
|
112
|
-
}
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (!name) {
|
|
117
|
-
console.error(chalk.red('Error: project name is required'));
|
|
118
|
-
console.error(chalk.dim('Usage: rev new <name>'));
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const projectDir = path.resolve(name);
|
|
123
|
-
|
|
124
|
-
if (fs.existsSync(projectDir)) {
|
|
125
|
-
console.error(chalk.red(`Directory already exists: ${name}`));
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
let template;
|
|
130
|
-
let sections: string[] | null = null;
|
|
131
|
-
|
|
132
|
-
// Determine sections: CLI option > user config > prompt
|
|
133
|
-
if (options.sections) {
|
|
134
|
-
// Parse CLI sections
|
|
135
|
-
sections = options.sections.split(',').map((s) => s.trim().toLowerCase().replace(/\.md$/, ''));
|
|
136
|
-
} else {
|
|
137
|
-
// Check user config for default sections
|
|
138
|
-
const defaultSections = getDefaultSections();
|
|
139
|
-
if (defaultSections && defaultSections.length > 0) {
|
|
140
|
-
sections = defaultSections;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// If no sections from CLI or config, and not using a named template with --template, prompt
|
|
145
|
-
// Only prompt if stdin is a TTY (interactive terminal)
|
|
146
|
-
if (!sections && options.template === 'paper') {
|
|
147
|
-
if (process.stdin.isTTY) {
|
|
148
|
-
const rl = (await import('readline')).createInterface({
|
|
149
|
-
input: process.stdin,
|
|
150
|
-
output: process.stdout,
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
const ask = (prompt: string): Promise<string> => new Promise((resolve) => rl.question(prompt, resolve));
|
|
154
|
-
|
|
155
|
-
console.log(chalk.cyan('Enter your document sections (comma-separated):'));
|
|
156
|
-
console.log(chalk.dim(' Example: introduction,methods,results,discussion'));
|
|
157
|
-
console.log(chalk.dim(' Press Enter to use default: introduction,methods,results,discussion\n'));
|
|
158
|
-
|
|
159
|
-
const answer = await ask(chalk.cyan('Sections: '));
|
|
160
|
-
rl.close();
|
|
161
|
-
|
|
162
|
-
if (answer.trim()) {
|
|
163
|
-
sections = answer.split(',').map((s) => s.trim().toLowerCase().replace(/\.md$/, ''));
|
|
164
|
-
} else {
|
|
165
|
-
// Use default paper template sections
|
|
166
|
-
sections = ['introduction', 'methods', 'results', 'discussion'];
|
|
167
|
-
}
|
|
168
|
-
} else {
|
|
169
|
-
// Non-interactive: use default sections
|
|
170
|
-
sections = ['introduction', 'methods', 'results', 'discussion'];
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Generate template based on sections
|
|
175
|
-
if (sections) {
|
|
176
|
-
template = generateCustomTemplate(sections);
|
|
177
|
-
console.log(chalk.cyan(`Creating project with sections: ${sections.join(', ')}\n`));
|
|
178
|
-
} else {
|
|
179
|
-
template = getTemplate(options.template);
|
|
180
|
-
if (!template) {
|
|
181
|
-
console.error(chalk.red(`Unknown template: ${options.template}`));
|
|
182
|
-
console.error(chalk.dim('Use --list to see available templates.'));
|
|
183
|
-
process.exit(1);
|
|
184
|
-
}
|
|
185
|
-
console.log(chalk.cyan(`Creating ${template.name} project in ${name}/...\n`));
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Create directory
|
|
189
|
-
fs.mkdirSync(projectDir, { recursive: true });
|
|
190
|
-
|
|
191
|
-
// Create subdirectories
|
|
192
|
-
for (const subdir of template.directories || []) {
|
|
193
|
-
fs.mkdirSync(path.join(projectDir, subdir), { recursive: true });
|
|
194
|
-
console.log(chalk.dim(` Created ${subdir}/`));
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Create files
|
|
198
|
-
for (const [filename, content] of Object.entries(template.files)) {
|
|
199
|
-
const filePath = path.join(projectDir, filename);
|
|
200
|
-
fs.writeFileSync(filePath, content, 'utf-8');
|
|
201
|
-
console.log(chalk.dim(` Created ${filename}`));
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
console.log(chalk.green(`\nProject created!`));
|
|
205
|
-
console.log(chalk.cyan('\nNext steps:'));
|
|
206
|
-
console.log(chalk.dim(` cd ${name}`));
|
|
207
|
-
console.log(chalk.dim(' # Edit rev.yaml with your paper details'));
|
|
208
|
-
console.log(chalk.dim(' # Write your sections'));
|
|
209
|
-
console.log(chalk.dim(' rev build # Build PDF and DOCX'));
|
|
210
|
-
console.log(chalk.dim(' rev build pdf # Build PDF only'));
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
// ==========================================================================
|
|
214
|
-
// CONFIG command - Set user preferences
|
|
215
|
-
// ==========================================================================
|
|
216
|
-
|
|
217
|
-
program
|
|
218
|
-
.command('config')
|
|
219
|
-
.description('Set user preferences')
|
|
220
|
-
.argument('<key>', 'Config key: user, sections')
|
|
221
|
-
.argument('[value]', 'Value to set')
|
|
222
|
-
.action((key: string, value?: string) => {
|
|
223
|
-
if (key === 'user') {
|
|
224
|
-
if (value) {
|
|
225
|
-
setUserName(value);
|
|
226
|
-
console.log(chalk.green(`User name set to: ${value}`));
|
|
227
|
-
console.log(chalk.dim(`Saved to ${getConfigPath()}`));
|
|
228
|
-
} else {
|
|
229
|
-
const name = getUserName();
|
|
230
|
-
if (name) {
|
|
231
|
-
console.log(`Current user: ${chalk.bold(name)}`);
|
|
232
|
-
} else {
|
|
233
|
-
console.log(chalk.yellow('No user name set.'));
|
|
234
|
-
console.log(chalk.dim('Set with: rev config user "Your Name"'));
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
} else if (key === 'sections') {
|
|
238
|
-
if (value) {
|
|
239
|
-
const sections = value.split(',').map((s) => s.trim().toLowerCase().replace(/\.md$/, ''));
|
|
240
|
-
setDefaultSections(sections);
|
|
241
|
-
console.log(chalk.green(`Default sections set to: ${sections.join(', ')}`));
|
|
242
|
-
console.log(chalk.dim(`Saved to ${getConfigPath()}`));
|
|
243
|
-
} else {
|
|
244
|
-
const sections = getDefaultSections();
|
|
245
|
-
if (sections && sections.length > 0) {
|
|
246
|
-
console.log(`Default sections: ${chalk.bold(sections.join(', '))}`);
|
|
247
|
-
} else {
|
|
248
|
-
console.log(chalk.yellow('No default sections set.'));
|
|
249
|
-
console.log(chalk.dim('Set with: rev config sections "intro,methods,results,discussion"'));
|
|
250
|
-
console.log(chalk.dim('When not set, rev new will prompt for sections.'));
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
} else {
|
|
254
|
-
console.error(chalk.red(`Unknown config key: ${key}`));
|
|
255
|
-
console.error(chalk.dim('Available keys: user, sections'));
|
|
256
|
-
process.exit(1);
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Init commands: init, new, config
|
|
3
|
+
*
|
|
4
|
+
* Project initialization and configuration commands.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Command } from 'commander';
|
|
8
|
+
import {
|
|
9
|
+
chalk,
|
|
10
|
+
fs,
|
|
11
|
+
path,
|
|
12
|
+
fmt,
|
|
13
|
+
generateConfig,
|
|
14
|
+
loadConfig,
|
|
15
|
+
saveConfig,
|
|
16
|
+
getTemplate,
|
|
17
|
+
listTemplates,
|
|
18
|
+
generateCustomTemplate,
|
|
19
|
+
getUserName,
|
|
20
|
+
setUserName,
|
|
21
|
+
getConfigPath,
|
|
22
|
+
getDefaultSections,
|
|
23
|
+
setDefaultSections,
|
|
24
|
+
} from './context.js';
|
|
25
|
+
|
|
26
|
+
interface InitOptions {
|
|
27
|
+
dir: string;
|
|
28
|
+
output: string;
|
|
29
|
+
force?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface NewOptions {
|
|
33
|
+
template: string;
|
|
34
|
+
sections?: string;
|
|
35
|
+
list?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Register init commands with the program
|
|
40
|
+
*/
|
|
41
|
+
export function register(program: Command): void {
|
|
42
|
+
// ==========================================================================
|
|
43
|
+
// INIT command - Generate sections.yaml config
|
|
44
|
+
// ==========================================================================
|
|
45
|
+
|
|
46
|
+
program
|
|
47
|
+
.command('init')
|
|
48
|
+
.description('Generate sections.yaml from existing .md files')
|
|
49
|
+
.option('-d, --dir <directory>', 'Directory to scan', '.')
|
|
50
|
+
.option('-o, --output <file>', 'Output config file', 'sections.yaml')
|
|
51
|
+
.option('--force', 'Overwrite existing config')
|
|
52
|
+
.action((options: InitOptions) => {
|
|
53
|
+
const dir = path.resolve(options.dir);
|
|
54
|
+
|
|
55
|
+
if (!fs.existsSync(dir)) {
|
|
56
|
+
console.error(chalk.red(`Directory not found: ${dir}`));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const outputPath = path.resolve(options.dir, options.output);
|
|
61
|
+
|
|
62
|
+
if (fs.existsSync(outputPath) && !options.force) {
|
|
63
|
+
console.error(chalk.yellow(`Config already exists: ${outputPath}`));
|
|
64
|
+
console.error(chalk.dim('Use --force to overwrite'));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log(chalk.cyan(`Scanning ${dir} for .md files...`));
|
|
69
|
+
|
|
70
|
+
const config = generateConfig(dir);
|
|
71
|
+
const sectionCount = Object.keys(config.sections).length;
|
|
72
|
+
|
|
73
|
+
if (sectionCount === 0) {
|
|
74
|
+
console.error(chalk.yellow('No .md files found (excluding paper.md, README.md)'));
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
saveConfig(outputPath, config);
|
|
79
|
+
|
|
80
|
+
console.log(chalk.green(`\nCreated ${outputPath} with ${sectionCount} sections:\n`));
|
|
81
|
+
|
|
82
|
+
for (const [file, section] of Object.entries(config.sections)) {
|
|
83
|
+
console.log(` ${chalk.bold(file)}`);
|
|
84
|
+
console.log(chalk.dim(` header: "${section.header}"`));
|
|
85
|
+
if (section.aliases && section.aliases.length > 0) {
|
|
86
|
+
console.log(chalk.dim(` aliases: ${JSON.stringify(section.aliases)}`));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log(chalk.cyan('\nEdit this file to:'));
|
|
91
|
+
console.log(chalk.dim(' • Add aliases for header variations'));
|
|
92
|
+
console.log(chalk.dim(' • Adjust order if needed'));
|
|
93
|
+
console.log(chalk.dim(' • Update headers if they change'));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// ==========================================================================
|
|
97
|
+
// NEW command - Create new paper project
|
|
98
|
+
// ==========================================================================
|
|
99
|
+
|
|
100
|
+
program
|
|
101
|
+
.command('new')
|
|
102
|
+
.description('Create a new paper project from template')
|
|
103
|
+
.argument('[name]', 'Project directory name')
|
|
104
|
+
.option('-t, --template <name>', 'Template: paper, minimal, thesis, review', 'paper')
|
|
105
|
+
.option('-s, --sections <sections>', 'Comma-separated section names (e.g., intro,methods,results)')
|
|
106
|
+
.option('--list', 'List available templates')
|
|
107
|
+
.action(async (name: string | undefined, options: NewOptions) => {
|
|
108
|
+
if (options.list) {
|
|
109
|
+
console.log(chalk.cyan('Available templates:\n'));
|
|
110
|
+
for (const t of listTemplates()) {
|
|
111
|
+
console.log(` ${chalk.bold(t.id)} - ${t.description}`);
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!name) {
|
|
117
|
+
console.error(chalk.red('Error: project name is required'));
|
|
118
|
+
console.error(chalk.dim('Usage: rev new <name>'));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const projectDir = path.resolve(name);
|
|
123
|
+
|
|
124
|
+
if (fs.existsSync(projectDir)) {
|
|
125
|
+
console.error(chalk.red(`Directory already exists: ${name}`));
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let template;
|
|
130
|
+
let sections: string[] | null = null;
|
|
131
|
+
|
|
132
|
+
// Determine sections: CLI option > user config > prompt
|
|
133
|
+
if (options.sections) {
|
|
134
|
+
// Parse CLI sections
|
|
135
|
+
sections = options.sections.split(',').map((s) => s.trim().toLowerCase().replace(/\.md$/, ''));
|
|
136
|
+
} else {
|
|
137
|
+
// Check user config for default sections
|
|
138
|
+
const defaultSections = getDefaultSections();
|
|
139
|
+
if (defaultSections && defaultSections.length > 0) {
|
|
140
|
+
sections = defaultSections;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// If no sections from CLI or config, and not using a named template with --template, prompt
|
|
145
|
+
// Only prompt if stdin is a TTY (interactive terminal)
|
|
146
|
+
if (!sections && options.template === 'paper') {
|
|
147
|
+
if (process.stdin.isTTY) {
|
|
148
|
+
const rl = (await import('readline')).createInterface({
|
|
149
|
+
input: process.stdin,
|
|
150
|
+
output: process.stdout,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const ask = (prompt: string): Promise<string> => new Promise((resolve) => rl.question(prompt, resolve));
|
|
154
|
+
|
|
155
|
+
console.log(chalk.cyan('Enter your document sections (comma-separated):'));
|
|
156
|
+
console.log(chalk.dim(' Example: introduction,methods,results,discussion'));
|
|
157
|
+
console.log(chalk.dim(' Press Enter to use default: introduction,methods,results,discussion\n'));
|
|
158
|
+
|
|
159
|
+
const answer = await ask(chalk.cyan('Sections: '));
|
|
160
|
+
rl.close();
|
|
161
|
+
|
|
162
|
+
if (answer.trim()) {
|
|
163
|
+
sections = answer.split(',').map((s) => s.trim().toLowerCase().replace(/\.md$/, ''));
|
|
164
|
+
} else {
|
|
165
|
+
// Use default paper template sections
|
|
166
|
+
sections = ['introduction', 'methods', 'results', 'discussion'];
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
// Non-interactive: use default sections
|
|
170
|
+
sections = ['introduction', 'methods', 'results', 'discussion'];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Generate template based on sections
|
|
175
|
+
if (sections) {
|
|
176
|
+
template = generateCustomTemplate(sections);
|
|
177
|
+
console.log(chalk.cyan(`Creating project with sections: ${sections.join(', ')}\n`));
|
|
178
|
+
} else {
|
|
179
|
+
template = getTemplate(options.template);
|
|
180
|
+
if (!template) {
|
|
181
|
+
console.error(chalk.red(`Unknown template: ${options.template}`));
|
|
182
|
+
console.error(chalk.dim('Use --list to see available templates.'));
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
console.log(chalk.cyan(`Creating ${template.name} project in ${name}/...\n`));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Create directory
|
|
189
|
+
fs.mkdirSync(projectDir, { recursive: true });
|
|
190
|
+
|
|
191
|
+
// Create subdirectories
|
|
192
|
+
for (const subdir of template.directories || []) {
|
|
193
|
+
fs.mkdirSync(path.join(projectDir, subdir), { recursive: true });
|
|
194
|
+
console.log(chalk.dim(` Created ${subdir}/`));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Create files
|
|
198
|
+
for (const [filename, content] of Object.entries(template.files)) {
|
|
199
|
+
const filePath = path.join(projectDir, filename);
|
|
200
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
201
|
+
console.log(chalk.dim(` Created ${filename}`));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log(chalk.green(`\nProject created!`));
|
|
205
|
+
console.log(chalk.cyan('\nNext steps:'));
|
|
206
|
+
console.log(chalk.dim(` cd ${name}`));
|
|
207
|
+
console.log(chalk.dim(' # Edit rev.yaml with your paper details'));
|
|
208
|
+
console.log(chalk.dim(' # Write your sections'));
|
|
209
|
+
console.log(chalk.dim(' rev build # Build PDF and DOCX'));
|
|
210
|
+
console.log(chalk.dim(' rev build pdf # Build PDF only'));
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// ==========================================================================
|
|
214
|
+
// CONFIG command - Set user preferences
|
|
215
|
+
// ==========================================================================
|
|
216
|
+
|
|
217
|
+
program
|
|
218
|
+
.command('config')
|
|
219
|
+
.description('Set user preferences')
|
|
220
|
+
.argument('<key>', 'Config key: user, sections')
|
|
221
|
+
.argument('[value]', 'Value to set')
|
|
222
|
+
.action((key: string, value?: string) => {
|
|
223
|
+
if (key === 'user') {
|
|
224
|
+
if (value) {
|
|
225
|
+
setUserName(value);
|
|
226
|
+
console.log(chalk.green(`User name set to: ${value}`));
|
|
227
|
+
console.log(chalk.dim(`Saved to ${getConfigPath()}`));
|
|
228
|
+
} else {
|
|
229
|
+
const name = getUserName();
|
|
230
|
+
if (name) {
|
|
231
|
+
console.log(`Current user: ${chalk.bold(name)}`);
|
|
232
|
+
} else {
|
|
233
|
+
console.log(chalk.yellow('No user name set.'));
|
|
234
|
+
console.log(chalk.dim('Set with: rev config user "Your Name"'));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} else if (key === 'sections') {
|
|
238
|
+
if (value) {
|
|
239
|
+
const sections = value.split(',').map((s) => s.trim().toLowerCase().replace(/\.md$/, ''));
|
|
240
|
+
setDefaultSections(sections);
|
|
241
|
+
console.log(chalk.green(`Default sections set to: ${sections.join(', ')}`));
|
|
242
|
+
console.log(chalk.dim(`Saved to ${getConfigPath()}`));
|
|
243
|
+
} else {
|
|
244
|
+
const sections = getDefaultSections();
|
|
245
|
+
if (sections && sections.length > 0) {
|
|
246
|
+
console.log(`Default sections: ${chalk.bold(sections.join(', '))}`);
|
|
247
|
+
} else {
|
|
248
|
+
console.log(chalk.yellow('No default sections set.'));
|
|
249
|
+
console.log(chalk.dim('Set with: rev config sections "intro,methods,results,discussion"'));
|
|
250
|
+
console.log(chalk.dim('When not set, rev new will prompt for sections.'));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
console.error(chalk.red(`Unknown config key: ${key}`));
|
|
255
|
+
console.error(chalk.dim('Available keys: user, sections'));
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|