project-logbook 1.1.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 +3 -0
- package/dist/commands/new-helpers.d.ts +26 -0
- package/dist/commands/new-helpers.js +103 -0
- package/dist/commands/new-helpers.js.map +1 -0
- package/dist/commands/new.js +91 -92
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/start.d.ts +1 -1
- package/dist/commands/start.js +85 -42
- package/dist/commands/start.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/build-helpers.d.ts +2 -2
- package/dist/lib/build-helpers.js +43 -61
- package/dist/lib/build-helpers.js.map +1 -1
- package/dist/lib/markdown-processors.d.ts +5 -2
- package/dist/lib/markdown-processors.js +9 -7
- package/dist/lib/markdown-processors.js.map +1 -1
- package/dist/templates/CONTRIBUTING.md +35 -3
- package/dist/templates/adr.md +34 -0
- package/dist/templates/index.md +5 -4
- package/dist/templates/steer.txt +14 -1
- package/dist/utils/prompt.d.ts +6 -0
- package/dist/utils/prompt.js +59 -0
- package/dist/utils/prompt.js.map +1 -0
- package/package.json +2 -2
- package/src/templates/CONTRIBUTING.md +35 -3
- package/src/templates/adr.md +34 -0
- package/src/templates/index.md +5 -4
- package/src/templates/steer.txt +14 -1
package/README.md
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare function getGitUsername(): string;
|
|
2
|
+
export declare function detectIdFromBranch(prefix: string): string | undefined;
|
|
3
|
+
export declare function resolveInteractiveId({ input, prefix, nextId, isResearch, hasCustomType, jiraPrefix, }: {
|
|
4
|
+
input: string;
|
|
5
|
+
prefix: string;
|
|
6
|
+
nextId: string;
|
|
7
|
+
isResearch: boolean;
|
|
8
|
+
hasCustomType: boolean;
|
|
9
|
+
jiraPrefix?: string;
|
|
10
|
+
}): string;
|
|
11
|
+
export declare function writeAdrFile({ targetDir, entryId, entrySlug, originalTitle, nextIdNum, id_padded, }: {
|
|
12
|
+
targetDir: string;
|
|
13
|
+
entryId: string;
|
|
14
|
+
entrySlug: string;
|
|
15
|
+
originalTitle: string;
|
|
16
|
+
nextIdNum: number;
|
|
17
|
+
id_padded: string;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
export declare function writeStandardEntryFiles({ entryDir, entryId, entrySlug, originalTitle, isResearch, hasCustomType, }: {
|
|
20
|
+
entryDir: string;
|
|
21
|
+
entryId: string;
|
|
22
|
+
entrySlug: string;
|
|
23
|
+
originalTitle: string;
|
|
24
|
+
isResearch: boolean;
|
|
25
|
+
hasCustomType: boolean;
|
|
26
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import { getTemplatesDir } from '../lib/migrations.js';
|
|
5
|
+
import { getPackageVersion } from '../lib/package-version.js';
|
|
6
|
+
import { getCurrentBranch } from '../lib/git-helpers.js';
|
|
7
|
+
import { highlight, warning as warningStyle } from '../lib/theme.js';
|
|
8
|
+
import { parseTicketId } from '../utils/id.js';
|
|
9
|
+
export function getGitUsername() {
|
|
10
|
+
try {
|
|
11
|
+
return (execSync('git config user.name', { stdio: ['ignore', 'pipe', 'ignore'] })
|
|
12
|
+
.toString()
|
|
13
|
+
.trim() || 'Author');
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return 'Author';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function detectIdFromBranch(prefix) {
|
|
20
|
+
const branchName = getCurrentBranch();
|
|
21
|
+
if (branchName) {
|
|
22
|
+
const match = branchName.match(new RegExp(`(?:^|[/\\-_])(${prefix})-(\\d+)(?:-|$)`, 'i'));
|
|
23
|
+
if (match) {
|
|
24
|
+
const id = `${match[1].toUpperCase()}-${match[2]}`;
|
|
25
|
+
console.log(highlight(`Autodetected ID ${id} from Git branch '${branchName}'.`));
|
|
26
|
+
return id;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
export function resolveInteractiveId({ input, prefix, nextId, isResearch, hasCustomType, jiraPrefix, }) {
|
|
32
|
+
const parsed = parseTicketId(input);
|
|
33
|
+
const bareNumber = /^\d+$/.test(input) ? parseInt(input, 10) : null;
|
|
34
|
+
if (parsed &&
|
|
35
|
+
!isResearch &&
|
|
36
|
+
!hasCustomType &&
|
|
37
|
+
jiraPrefix &&
|
|
38
|
+
parsed.prefix.toUpperCase() !== jiraPrefix.toUpperCase()) {
|
|
39
|
+
console.warn(warningStyle(`Warning: Prefix '${parsed.prefix}' doesn't match configured '${jiraPrefix}'. Using configured prefix.`));
|
|
40
|
+
return `${jiraPrefix}-${parsed.number}`;
|
|
41
|
+
}
|
|
42
|
+
else if (parsed) {
|
|
43
|
+
return input.toUpperCase();
|
|
44
|
+
}
|
|
45
|
+
else if (bareNumber !== null) {
|
|
46
|
+
return `${prefix}-${bareNumber}`;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
console.warn(warningStyle('Invalid ID format. Using auto-generated ID.'));
|
|
50
|
+
return nextId;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export async function writeAdrFile({ targetDir, entryId, entrySlug, originalTitle, nextIdNum, id_padded, }) {
|
|
54
|
+
const adrFile = join(targetDir, `${entryId}-${entrySlug}.md`);
|
|
55
|
+
const templatesDir = getTemplatesDir();
|
|
56
|
+
const adrTemplatePath = join(templatesDir, 'adr.md');
|
|
57
|
+
if (!fs.existsSync(adrTemplatePath)) {
|
|
58
|
+
throw new Error('Template adr.md not found in templates.');
|
|
59
|
+
}
|
|
60
|
+
let raw = await fs.readFile(adrTemplatePath, 'utf8');
|
|
61
|
+
const author = getGitUsername();
|
|
62
|
+
raw = raw
|
|
63
|
+
.replace(/\[ID_NUM\]/g, String(nextIdNum))
|
|
64
|
+
.replace(/\[ID\]/g, id_padded)
|
|
65
|
+
.replace(/\[TITLE\]/g, originalTitle)
|
|
66
|
+
.replace(/\[YAML_TITLE\]/g, JSON.stringify(originalTitle))
|
|
67
|
+
.replace(/\[DATE\]/g, new Date().toISOString())
|
|
68
|
+
.replace(/\[AUTHOR\]/g, author);
|
|
69
|
+
await fs.writeFile(adrFile, raw);
|
|
70
|
+
}
|
|
71
|
+
export async function writeStandardEntryFiles({ entryDir, entryId, entrySlug, originalTitle, isResearch, hasCustomType, }) {
|
|
72
|
+
await fs.mkdirp(entryDir);
|
|
73
|
+
const version = getPackageVersion();
|
|
74
|
+
const now = new Date();
|
|
75
|
+
const replacements = {
|
|
76
|
+
id: entryId,
|
|
77
|
+
slug: entrySlug,
|
|
78
|
+
title: originalTitle,
|
|
79
|
+
yamlTitle: JSON.stringify(originalTitle),
|
|
80
|
+
date: now.toISOString().split('T')[0],
|
|
81
|
+
time: now.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' }),
|
|
82
|
+
fullIso: now.toISOString(),
|
|
83
|
+
version: `v${version}`,
|
|
84
|
+
};
|
|
85
|
+
const templatesDir = getTemplatesDir();
|
|
86
|
+
const getTemplate = (name) => {
|
|
87
|
+
const p = join(templatesDir, name);
|
|
88
|
+
if (fs.existsSync(p))
|
|
89
|
+
return fs.readFileSync(p, 'utf8');
|
|
90
|
+
throw new Error(`Template ${name} not found.`);
|
|
91
|
+
};
|
|
92
|
+
const fillTemplate = (template) => {
|
|
93
|
+
return template.replace(/{{(\w+)}}/g, (_, key) => replacements[key] || `{{${key}}}`);
|
|
94
|
+
};
|
|
95
|
+
let indexTemplate = getTemplate('index.md');
|
|
96
|
+
if (isResearch || hasCustomType) {
|
|
97
|
+
indexTemplate = indexTemplate.replace('dateEnd: "[DATE_END]"', 'dateEnd: "[DATE_END]"\nstatus: active\nupdated: {{fullIso}}');
|
|
98
|
+
}
|
|
99
|
+
await fs.writeFile(join(entryDir, 'index.md'), fillTemplate(indexTemplate));
|
|
100
|
+
await fs.writeFile(join(entryDir, 'ticket.md'), fillTemplate(getTemplate('ticket.md')));
|
|
101
|
+
await fs.writeFile(join(entryDir, 'log.md'), fillTemplate(getTemplate('log.md')));
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=new-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new-helpers.js","sourceRoot":"","sources":["../../src/commands/new-helpers.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/new.js
CHANGED
|
@@ -1,91 +1,104 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { createInterface } from 'node:readline';
|
|
4
|
-
import { getConfig, getLogbookDirPath, getResearchDirPath } from '../lib/config.js';
|
|
4
|
+
import { getConfig, getLogbookDirPath, getResearchDirPath, getAdrDirPath } from '../lib/config.js';
|
|
5
5
|
import { getEntryPath } from '../lib/entry-paths.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { getPackageVersion } from '../lib/package-version.js';
|
|
9
|
-
import { parseTicketId, getNextId } from '../utils/id.js';
|
|
6
|
+
import { header, highlight, errorMessage, successMessage, warningMessage } from '../lib/theme.js';
|
|
7
|
+
import { getNextId } from '../utils/id.js';
|
|
10
8
|
import { sanitiseSlug } from '../utils/slug.js';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
input: process.stdin,
|
|
14
|
-
output: process.stdout,
|
|
15
|
-
});
|
|
9
|
+
import { selectPrompt } from '../utils/prompt.js';
|
|
10
|
+
import { writeAdrFile, writeStandardEntryFiles, detectIdFromBranch, resolveInteractiveId } from './new-helpers.js';
|
|
16
11
|
const question = (prompt) => {
|
|
12
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
17
13
|
return new Promise((resolve) => {
|
|
18
14
|
rl.question(prompt, (answer) => {
|
|
15
|
+
rl.close();
|
|
19
16
|
resolve(answer.trim());
|
|
20
17
|
});
|
|
21
18
|
});
|
|
22
19
|
};
|
|
23
20
|
export async function createNewEntry(id, slug, options) {
|
|
24
21
|
const config = getConfig();
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
let isResearch = !!options?.research, customTypeName = options?.type, isAdr = customTypeName?.toLowerCase() === 'adr';
|
|
23
|
+
if (!id && !slug && !options?.research && !options?.type) {
|
|
24
|
+
const types = [{ label: 'Default Logbook Entry', value: 'logbook', isResearch: false, isAdr: false }];
|
|
25
|
+
if (config.researchDir)
|
|
26
|
+
types.push({ label: 'Research Log', value: 'research', isResearch: true, isAdr: false });
|
|
27
|
+
if (config.adrDir)
|
|
28
|
+
types.push({ label: 'Architecture Decision Record (ADR)', value: 'adr', isResearch: false, isAdr: true });
|
|
29
|
+
if (config.customTypes) {
|
|
30
|
+
for (const [name, cc] of Object.entries(config.customTypes)) {
|
|
31
|
+
if (name.toLowerCase() !== 'adr') {
|
|
32
|
+
types.push({
|
|
33
|
+
label: `${cc.tabTitle || name} (${name})`,
|
|
34
|
+
value: name,
|
|
35
|
+
isResearch: false,
|
|
36
|
+
isAdr: false,
|
|
37
|
+
customTypeName: name,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (types.length > 1) {
|
|
43
|
+
const idx = await selectPrompt('Select the type of entry to create:', types.map((t) => t.label));
|
|
44
|
+
if (idx === -1)
|
|
45
|
+
process.exit(1);
|
|
46
|
+
const choice = types[idx];
|
|
47
|
+
isResearch = choice.isResearch;
|
|
48
|
+
isAdr = choice.isAdr;
|
|
49
|
+
customTypeName = choice.customTypeName;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
27
52
|
const customType = customTypeName && config.customTypes?.[customTypeName];
|
|
28
|
-
if (customTypeName && !customType) {
|
|
53
|
+
if (customTypeName && !customType && !isAdr) {
|
|
29
54
|
console.error(errorMessage('Error', `Custom type "${customTypeName}" is not defined in .project-logbook config.`));
|
|
30
55
|
process.exit(1);
|
|
31
56
|
}
|
|
32
|
-
const targetDir =
|
|
33
|
-
? join(process.cwd(),
|
|
34
|
-
:
|
|
35
|
-
?
|
|
36
|
-
:
|
|
37
|
-
|
|
57
|
+
const targetDir = isAdr
|
|
58
|
+
? getAdrDirPath(config) || join(process.cwd(), 'docs/adr')
|
|
59
|
+
: customType
|
|
60
|
+
? join(process.cwd(), customType.dir)
|
|
61
|
+
: isResearch
|
|
62
|
+
? getResearchDirPath(config) || join(process.cwd(), 'docs/research')
|
|
63
|
+
: getLogbookDirPath(config);
|
|
64
|
+
if (!(await fs.pathExists(targetDir)))
|
|
38
65
|
await fs.mkdirp(targetDir);
|
|
66
|
+
let nextIdNum = 1, id_padded = '';
|
|
67
|
+
if (isAdr && (await fs.pathExists(targetDir))) {
|
|
68
|
+
const parsedIds = (await fs.readdir(targetDir))
|
|
69
|
+
.map((f) => {
|
|
70
|
+
const m = f.match(/^(\d+)-/);
|
|
71
|
+
return m ? parseInt(m[1], 10) : null;
|
|
72
|
+
})
|
|
73
|
+
.filter((n) => n !== null);
|
|
74
|
+
if (parsedIds.length > 0)
|
|
75
|
+
nextIdNum = Math.max(...parsedIds) + 1;
|
|
76
|
+
id_padded = String(nextIdNum).padStart(4, '0');
|
|
39
77
|
}
|
|
40
|
-
let entryId = id;
|
|
78
|
+
let entryId = isAdr ? id_padded : id;
|
|
41
79
|
let entrySlug = slug;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (branchName) {
|
|
46
|
-
const prefix = customType ? customType.prefix : isResearch ? 'RES' : config.jiraPrefix || 'LB';
|
|
47
|
-
const branchRegex = new RegExp(`(?:^|[/\\-_])(${prefix})-(\\d+)(?:-|$)`, 'i');
|
|
48
|
-
const match = branchName.match(branchRegex);
|
|
49
|
-
if (match) {
|
|
50
|
-
entryId = `${match[1].toUpperCase()}-${match[2]}`;
|
|
51
|
-
console.log(highlight(`Autodetected ID ${entryId} from Git branch '${branchName}'.`));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
80
|
+
if (!entryId && !isAdr) {
|
|
81
|
+
const prefix = customType ? customType.prefix : isResearch ? 'RES' : config.jiraPrefix || 'LB';
|
|
82
|
+
entryId = detectIdFromBranch(prefix);
|
|
54
83
|
}
|
|
55
84
|
let originalTitle = '';
|
|
56
|
-
const typeDisplayName = customType ? customTypeName : isResearch ? 'research' : 'logbook';
|
|
57
|
-
// Interactive mode if no parameters provided
|
|
85
|
+
const typeDisplayName = isAdr ? 'ADR' : customType ? customTypeName : isResearch ? 'research' : 'logbook';
|
|
58
86
|
if (!entryId || !entrySlug) {
|
|
59
87
|
console.log(header(`\n📝 Creating a new ${typeDisplayName} entry (interactive mode)\n`));
|
|
60
|
-
if (!entryId) {
|
|
88
|
+
if (!entryId && !isAdr) {
|
|
61
89
|
const prefix = customType ? customType.prefix : isResearch ? 'RES' : config.jiraPrefix || 'LB';
|
|
62
90
|
const nextId = getNextId(targetDir, prefix);
|
|
63
91
|
const input = await question(highlight(`Enter entry ID (e.g. ${nextId}, press Enter for auto-generated): `));
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
else if (parsed) {
|
|
76
|
-
entryId = input.toUpperCase();
|
|
77
|
-
}
|
|
78
|
-
else if (bareNumber !== null) {
|
|
79
|
-
entryId = `${prefix}-${bareNumber}`;
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
console.warn(warningStyle('Invalid ID format. Using auto-generated ID.'));
|
|
83
|
-
entryId = nextId;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
entryId = nextId;
|
|
88
|
-
}
|
|
92
|
+
entryId = input
|
|
93
|
+
? resolveInteractiveId({
|
|
94
|
+
input,
|
|
95
|
+
prefix,
|
|
96
|
+
nextId,
|
|
97
|
+
isResearch,
|
|
98
|
+
hasCustomType: !!customType,
|
|
99
|
+
jiraPrefix: config.jiraPrefix,
|
|
100
|
+
})
|
|
101
|
+
: nextId;
|
|
89
102
|
}
|
|
90
103
|
if (!entrySlug) {
|
|
91
104
|
const input = await question(highlight('Enter a short title (will be converted to slug): '));
|
|
@@ -99,52 +112,38 @@ export async function createNewEntry(id, slug, options) {
|
|
|
99
112
|
else {
|
|
100
113
|
originalTitle = entrySlug;
|
|
101
114
|
}
|
|
102
|
-
rl.close();
|
|
103
115
|
}
|
|
104
116
|
else {
|
|
105
117
|
originalTitle = entrySlug;
|
|
106
|
-
rl.close();
|
|
107
118
|
}
|
|
108
|
-
// Sanitise slug
|
|
109
119
|
const sanitised = sanitiseSlug(entrySlug);
|
|
110
120
|
if (sanitised !== entrySlug) {
|
|
111
121
|
console.warn(warningMessage('Notice', `Slug normalised from '${entrySlug}' to '${sanitised}'.`));
|
|
112
122
|
entrySlug = sanitised;
|
|
113
123
|
}
|
|
124
|
+
if (isAdr) {
|
|
125
|
+
const adrFile = join(targetDir, `${entryId}-${entrySlug}.md`);
|
|
126
|
+
if (await fs.pathExists(adrFile)) {
|
|
127
|
+
console.error(errorMessage('Error', `ADR ${entryId}-${entrySlug}.md already exists.`));
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
await writeAdrFile({ targetDir, entryId: entryId, entrySlug, originalTitle, nextIdNum, id_padded });
|
|
131
|
+
console.log(successMessage('Created', `ADR in ${config.adrDir || 'docs/adr'}/${entryId}-${entrySlug}.md`));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
114
134
|
const entryDir = getEntryPath(targetDir, `${entryId}-${entrySlug}`);
|
|
115
135
|
if (await fs.pathExists(entryDir)) {
|
|
116
136
|
console.error(errorMessage('Error', `Entry ${entryId}-${entrySlug} already exists.`));
|
|
117
137
|
process.exit(1);
|
|
118
138
|
}
|
|
119
|
-
await
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
time: now.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' }),
|
|
128
|
-
fullIso: now.toISOString(),
|
|
129
|
-
version: `v${version}`,
|
|
130
|
-
};
|
|
131
|
-
const templatesDir = getTemplatesDir();
|
|
132
|
-
const getTemplate = (name) => {
|
|
133
|
-
const p = join(templatesDir, name);
|
|
134
|
-
if (fs.existsSync(p))
|
|
135
|
-
return fs.readFileSync(p, 'utf8');
|
|
136
|
-
throw new Error(`Template ${name} not found.`);
|
|
137
|
-
};
|
|
138
|
-
const fillTemplate = (template) => {
|
|
139
|
-
return template.replace(/{{(\w+)}}/g, (_, key) => replacements[key] || `{{${key}}}`);
|
|
140
|
-
};
|
|
141
|
-
let indexTemplate = getTemplate('index.md');
|
|
142
|
-
if (isResearch || customType) {
|
|
143
|
-
indexTemplate = indexTemplate.replace('dateEnd: "[DATE_END]"', 'dateEnd: "[DATE_END]"\nstatus: active\nupdated: {{fullIso}}');
|
|
144
|
-
}
|
|
145
|
-
await fs.writeFile(join(entryDir, 'index.md'), fillTemplate(indexTemplate));
|
|
146
|
-
await fs.writeFile(join(entryDir, 'ticket.md'), fillTemplate(getTemplate('ticket.md')));
|
|
147
|
-
await fs.writeFile(join(entryDir, 'log.md'), fillTemplate(getTemplate('log.md')));
|
|
139
|
+
await writeStandardEntryFiles({
|
|
140
|
+
entryDir,
|
|
141
|
+
entryId: entryId,
|
|
142
|
+
entrySlug,
|
|
143
|
+
originalTitle,
|
|
144
|
+
isResearch,
|
|
145
|
+
hasCustomType: !!customType,
|
|
146
|
+
});
|
|
148
147
|
const relativeDestDir = customType
|
|
149
148
|
? customType.dir
|
|
150
149
|
: isResearch
|
package/dist/commands/new.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/start.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function start(id
|
|
1
|
+
export declare function start(id?: string, cwd?: string): Promise<void>;
|
package/dist/commands/start.js
CHANGED
|
@@ -6,6 +6,8 @@ import matter from 'gray-matter';
|
|
|
6
6
|
import { getConfig, getLogbookDirPath } from '../lib/config.js';
|
|
7
7
|
import { getActiveEntry } from '../lib/session.js';
|
|
8
8
|
import { getLogbookEntries } from '../utils/fs.js';
|
|
9
|
+
import { selectPrompt } from '../utils/prompt.js';
|
|
10
|
+
import { parseTicketId } from '../utils/id.js';
|
|
9
11
|
import { LOCKFILE_NAME } from '../lib/lockfile.js';
|
|
10
12
|
function getGitUsername() {
|
|
11
13
|
try {
|
|
@@ -28,8 +30,6 @@ export async function start(id, cwd = process.cwd()) {
|
|
|
28
30
|
}
|
|
29
31
|
// 2. Resolve folder by ID prefix or exact match
|
|
30
32
|
const config = getConfig();
|
|
31
|
-
// If a bare or suffixed number is given (e.g. "20-fix"), auto-prepend the configured jiraPrefix
|
|
32
|
-
const resolvedId = /^\d+(?:-.*)?$/.test(id) && config.jiraPrefix ? `${config.jiraPrefix}-${id}` : id;
|
|
33
33
|
const logbookDir = getLogbookDirPath(config, cwd);
|
|
34
34
|
const logbookEntries = await getLogbookEntries(logbookDir);
|
|
35
35
|
if (config.researchDir) {
|
|
@@ -52,56 +52,99 @@ export async function start(id, cwd = process.cwd()) {
|
|
|
52
52
|
}
|
|
53
53
|
let slug;
|
|
54
54
|
let entryDir;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
// 2b. Query all logbook entries for frontmatter and folder structure
|
|
63
|
-
// First Pass: Exact Ticket ID Match in Frontmatter
|
|
64
|
-
const exactTicketMatches = logbookEntries.filter((e) => {
|
|
65
|
-
const ticket = typeof e.data?.ticket === 'string' ? e.data.ticket.trim().toLowerCase() : '';
|
|
66
|
-
return ticket === resolvedId.toLowerCase();
|
|
67
|
-
});
|
|
68
|
-
if (exactTicketMatches.length === 1) {
|
|
69
|
-
slug = exactTicketMatches[0].slug;
|
|
70
|
-
entryDir = exactTicketMatches[0].path;
|
|
55
|
+
if (!id) {
|
|
56
|
+
if (logbookEntries.length === 0) {
|
|
57
|
+
console.error(errorStyle(`Error: No entries found in logbook or research directories.`));
|
|
58
|
+
console.error(errorStyle(`Run 'logbook new' to create one first.`));
|
|
59
|
+
process.exit(1);
|
|
71
60
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
61
|
+
// Sort entries descending
|
|
62
|
+
logbookEntries.sort((a, b) => {
|
|
63
|
+
const idA = parseTicketId(a.slug);
|
|
64
|
+
const idB = parseTicketId(b.slug);
|
|
65
|
+
if (idA && idB) {
|
|
66
|
+
if (idA.prefix !== idB.prefix) {
|
|
67
|
+
return idB.prefix.localeCompare(idA.prefix);
|
|
68
|
+
}
|
|
69
|
+
return idB.number - idA.number;
|
|
77
70
|
}
|
|
71
|
+
return b.slug.localeCompare(a.slug);
|
|
72
|
+
});
|
|
73
|
+
const choices = logbookEntries.map((e) => {
|
|
74
|
+
const title = e.data?.title ? `: ${e.data.title}` : '';
|
|
75
|
+
const ticketId = e.data?.ticket || e.slug;
|
|
76
|
+
return `${ticketId}${title}`;
|
|
77
|
+
});
|
|
78
|
+
const selectedIndex = await selectPrompt('Select an entry to start:', choices);
|
|
79
|
+
if (selectedIndex === -1) {
|
|
78
80
|
process.exit(1);
|
|
79
81
|
}
|
|
82
|
+
const selectedEntry = logbookEntries[selectedIndex];
|
|
83
|
+
slug = selectedEntry.slug;
|
|
84
|
+
entryDir = selectedEntry.path;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// If a bare or suffixed number is given (e.g. "20-fix"), auto-prepend the configured jiraPrefix
|
|
88
|
+
const resolvedId = /^\d+(?:-.*)?$/.test(id) && config.jiraPrefix ? `${config.jiraPrefix}-${id}` : id;
|
|
89
|
+
// 2a. Direct folder name match bypasses all other logic
|
|
90
|
+
const exactSlugMatch = logbookEntries.find((e) => e.slug.toLowerCase() === id.toLowerCase());
|
|
91
|
+
if (exactSlugMatch) {
|
|
92
|
+
slug = exactSlugMatch.slug;
|
|
93
|
+
entryDir = exactSlugMatch.path;
|
|
94
|
+
}
|
|
80
95
|
else {
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
96
|
+
// 2b. Query all logbook entries for frontmatter and folder structure
|
|
97
|
+
// First Pass: Exact Ticket ID Match in Frontmatter
|
|
98
|
+
const exactTicketMatches = logbookEntries.filter((e) => {
|
|
99
|
+
const ticket = typeof e.data?.ticket === 'string' ? e.data.ticket.trim().toLowerCase() : '';
|
|
100
|
+
return ticket === resolvedId.toLowerCase();
|
|
85
101
|
});
|
|
86
|
-
if (
|
|
87
|
-
slug =
|
|
88
|
-
entryDir =
|
|
102
|
+
if (exactTicketMatches.length === 1) {
|
|
103
|
+
slug = exactTicketMatches[0].slug;
|
|
104
|
+
entryDir = exactTicketMatches[0].path;
|
|
89
105
|
}
|
|
90
|
-
else if (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
106
|
+
else if (exactTicketMatches.length > 1) {
|
|
107
|
+
const choices = exactTicketMatches.map((e) => {
|
|
108
|
+
const title = e.data?.title ? `: ${e.data.title}` : '';
|
|
109
|
+
return `${e.slug}${title}`;
|
|
110
|
+
});
|
|
111
|
+
const selectedIndex = await selectPrompt(`Multiple entries found with the exact same ticket ID '${resolvedId}'. Select one:`, choices);
|
|
112
|
+
if (selectedIndex === -1) {
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
slug = exactTicketMatches[selectedIndex].slug;
|
|
116
|
+
entryDir = exactTicketMatches[selectedIndex].path;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Second Pass: Fallback Prefix Match on Folder Names
|
|
120
|
+
const prefixMatches = logbookEntries.filter((e) => {
|
|
121
|
+
const prefix = e.slug.split('-').slice(0, resolvedId.split('-').length).join('-');
|
|
122
|
+
return prefix.toLowerCase() === resolvedId.toLowerCase();
|
|
123
|
+
});
|
|
124
|
+
if (prefixMatches.length === 1) {
|
|
125
|
+
slug = prefixMatches[0].slug;
|
|
126
|
+
entryDir = prefixMatches[0].path;
|
|
127
|
+
}
|
|
128
|
+
else if (prefixMatches.length > 1) {
|
|
129
|
+
const choices = prefixMatches.map((e) => {
|
|
130
|
+
const title = e.data?.title ? `: ${e.data.title}` : '';
|
|
131
|
+
const ticketId = e.data?.ticket || e.slug;
|
|
132
|
+
return `${ticketId}${title}`;
|
|
133
|
+
});
|
|
134
|
+
const selectedIndex = await selectPrompt(`Multiple entries found matching prefix '${resolvedId}'. Select one:`, choices);
|
|
135
|
+
if (selectedIndex === -1) {
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
slug = prefixMatches[selectedIndex].slug;
|
|
139
|
+
entryDir = prefixMatches[selectedIndex].path;
|
|
96
140
|
}
|
|
97
|
-
process.exit(1);
|
|
98
141
|
}
|
|
99
142
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
143
|
+
if (!slug || !entryDir) {
|
|
144
|
+
console.error(errorStyle(`Error: No entry found matching ID '${resolvedId}' in logbook or research directories.`));
|
|
145
|
+
console.error(errorStyle(`Run 'logbook new <id> <slug>' to create it first.`));
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
105
148
|
}
|
|
106
149
|
// Warn if branch-resolved entry is different
|
|
107
150
|
const activeFromBranch = await getActiveEntry({}, cwd);
|