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 CHANGED
@@ -71,3 +71,6 @@ Running `logbook init` creates a `.project-logbook` configuration file in your p
71
71
  }
72
72
  ```
73
73
 
74
+ ### Tests
75
+
76
+ The [Demo App](./demo-app/README.md) README must be compiled to html too!
@@ -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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"}
@@ -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 { getTemplatesDir } from '../lib/migrations.js';
7
- import { header, warning as warningStyle, highlight, errorMessage, successMessage, warningMessage, } from '../lib/theme.js';
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 { getCurrentBranch } from '../lib/git-helpers.js';
12
- const rl = createInterface({
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
- const isResearch = !!options?.research;
26
- const customTypeName = options?.type;
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 = customType
33
- ? join(process.cwd(), customType.dir)
34
- : isResearch
35
- ? getResearchDirPath(config) || join(process.cwd(), 'docs/research')
36
- : getLogbookDirPath(config);
37
- if (!(await fs.pathExists(targetDir))) {
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
- // Autodetect ID from the current Git branch if not provided
43
- if (!entryId) {
44
- const branchName = getCurrentBranch();
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
- if (input) {
65
- const parsed = parseTicketId(input);
66
- const bareNumber = /^\d+$/.test(input) ? parseInt(input, 10) : null;
67
- if (parsed &&
68
- !isResearch &&
69
- !customType &&
70
- config.jiraPrefix &&
71
- parsed.prefix.toUpperCase() !== config.jiraPrefix.toUpperCase()) {
72
- console.warn(warningStyle(`Warning: Prefix '${parsed.prefix}' doesn't match configured '${config.jiraPrefix}'. Using configured prefix.`));
73
- entryId = `${config.jiraPrefix}-${parsed.number}`;
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 fs.mkdirp(entryDir);
120
- const version = getPackageVersion();
121
- const now = new Date();
122
- const replacements = {
123
- id: entryId,
124
- slug: entrySlug,
125
- title: originalTitle,
126
- date: now.toISOString().split('T')[0],
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
@@ -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;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EACL,MAAM,EACN,OAAO,IAAI,YAAY,EACvB,SAAS,EACT,YAAY,EACZ,cAAc,EACd,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,MAAM,EAAE,GAAG,eAAe,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC,KAAK;IACpB,MAAM,EAAE,OAAO,CAAC,MAAM;CACvB,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAmB,EAAE;IACnD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;YAC7B,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EAAW,EAAE,IAAa,EAAE,OAA+C;IAC9G,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC;IACvC,MAAM,cAAc,GAAG,OAAO,EAAE,IAAI,CAAC;IACrC,MAAM,UAAU,GAAG,cAAc,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC,cAAc,CAAC,CAAC;IAE1E,IAAI,cAAc,IAAI,CAAC,UAAU,EAAE,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,gBAAgB,cAAc,8CAA8C,CAAC,CAAC,CAAC;QACnH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,UAAU;QAC1B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,GAAG,CAAC;QACrC,CAAC,CAAC,UAAU;YACV,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,eAAe,CAAC;YACpE,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAEhC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,SAAS,GAAG,IAAI,CAAC;IAErB,4DAA4D;IAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;QACtC,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC;YAC/F,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,iBAAiB,MAAM,iBAAiB,EAAE,GAAG,CAAC,CAAC;YAC9E,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBAClD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,mBAAmB,OAAO,qBAAqB,UAAU,IAAI,CAAC,CAAC,CAAC;YACxF,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,aAAa,GAAG,EAAE,CAAC;IAEvB,MAAM,eAAe,GAAG,UAAU,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAE1F,6CAA6C;IAC7C,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,uBAAuB,eAAe,6BAA6B,CAAC,CAAC,CAAC;QAEzF,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC;YAC/F,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YAC5C,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,wBAAwB,MAAM,qCAAqC,CAAC,CAAC,CAAC;YAC7G,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;gBACpC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBACpE,IACE,MAAM;oBACN,CAAC,UAAU;oBACX,CAAC,UAAU;oBACX,MAAM,CAAC,UAAU;oBACjB,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,UAAU,CAAC,WAAW,EAAE,EAC/D,CAAC;oBACD,OAAO,CAAC,IAAI,CACV,YAAY,CACV,oBAAoB,MAAM,CAAC,MAAM,+BAA+B,MAAM,CAAC,UAAU,6BAA6B,CAC/G,CACF,CAAC;oBACF,OAAO,GAAG,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBACpD,CAAC;qBAAM,IAAI,MAAM,EAAE,CAAC;oBAClB,OAAO,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;gBAChC,CAAC;qBAAM,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;oBAC/B,OAAO,GAAG,GAAG,MAAM,IAAI,UAAU,EAAE,CAAC;gBACtC,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,6CAA6C,CAAC,CAAC,CAAC;oBAC1E,OAAO,GAAG,MAAM,CAAC;gBACnB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,MAAM,CAAC;YACnB,CAAC;QACH,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,mDAAmD,CAAC,CAAC,CAAC;YAC7F,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC;gBAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YACD,aAAa,GAAG,KAAK,CAAC;YACtB,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,aAAa,GAAG,SAAS,CAAC;QAC5B,CAAC;QAED,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;SAAM,CAAC;QACN,aAAa,GAAG,SAAS,CAAC;QAC1B,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;IAED,gBAAgB;IAChB,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IAC1C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,yBAAyB,SAAS,SAAS,SAAS,IAAI,CAAC,CAAC,CAAC;QACjG,SAAS,GAAG,SAAS,CAAC;IACxB,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,SAAS,EAAE,GAAG,OAAO,IAAI,SAAS,EAAE,CAAC,CAAC;IAEpE,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,SAAS,OAAO,IAAI,SAAS,kBAAkB,CAAC,CAAC,CAAC;QACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE1B,MAAM,OAAO,GAAW,iBAAiB,EAAE,CAAC;IAE5C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,YAAY,GAA2B;QAC3C,EAAE,EAAE,OAAO;QACX,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,aAAa;QACpB,IAAI,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,EAAE,GAAG,CAAC,kBAAkB,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAChG,OAAO,EAAE,GAAG,CAAC,WAAW,EAAE;QAC1B,OAAO,EAAE,IAAI,OAAO,EAAE;KACvB,CAAC;IAEF,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;IACvC,MAAM,WAAW,GAAG,CAAC,IAAY,EAAU,EAAE;QAC3C,MAAM,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;QACnC,IAAI,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,aAAa,CAAC,CAAC;IACjD,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CAAC,QAAgB,EAAE,EAAE;QACxC,OAAO,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,IAAI,CAAC,CAAC;IACvF,CAAC,CAAC;IAEF,IAAI,aAAa,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,UAAU,IAAI,UAAU,EAAE,CAAC;QAC7B,aAAa,GAAG,aAAa,CAAC,OAAO,CACnC,uBAAuB,EACvB,6DAA6D,CAC9D,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,aAAa,CAAC,CAAC,CAAC;IAC5E,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACxF,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAElF,MAAM,eAAe,GAAG,UAAU;QAChC,CAAC,CAAC,UAAU,CAAC,GAAG;QAChB,CAAC,CAAC,UAAU;YACV,CAAC,CAAC,MAAM,CAAC,WAAW,IAAI,eAAe;YACvC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;IAExB,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,SAAS,EAAE,GAAG,eAAe,aAAa,eAAe,IAAI,OAAO,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;AACpH,CAAC"}
1
+ {"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- export declare function start(id: string, cwd?: string): Promise<void>;
1
+ export declare function start(id?: string, cwd?: string): Promise<void>;
@@ -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
- // 2a. Direct folder name match bypasses all other logic
56
- const exactSlugMatch = logbookEntries.find((e) => e.slug.toLowerCase() === id.toLowerCase());
57
- if (exactSlugMatch) {
58
- slug = exactSlugMatch.slug;
59
- entryDir = exactSlugMatch.path;
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
- else if (exactTicketMatches.length > 1) {
73
- console.error(errorStyle(`Error: Multiple entries found with the exact same ticket ID '${resolvedId}'.`));
74
- console.error(errorStyle(`Please specify the unique folder name/slug directly instead:`));
75
- for (const match of exactTicketMatches) {
76
- console.error(errorStyle(` logbook start ${match.slug}`));
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
- // Second Pass: Fallback Prefix Match on Folder Names
82
- const prefixMatches = logbookEntries.filter((e) => {
83
- const prefix = e.slug.split('-').slice(0, resolvedId.split('-').length).join('-');
84
- return prefix.toLowerCase() === resolvedId.toLowerCase();
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 (prefixMatches.length === 1) {
87
- slug = prefixMatches[0].slug;
88
- entryDir = prefixMatches[0].path;
102
+ if (exactTicketMatches.length === 1) {
103
+ slug = exactTicketMatches[0].slug;
104
+ entryDir = exactTicketMatches[0].path;
89
105
  }
90
- else if (prefixMatches.length > 1) {
91
- console.error(errorStyle(`Error: Multiple entries found matching prefix '${resolvedId}'.`));
92
- console.error(errorStyle(`Please be more specific. Run one of:`));
93
- for (const match of prefixMatches) {
94
- const ticketId = typeof match.data?.ticket === 'string' ? match.data.ticket : match.slug;
95
- console.error(errorStyle(` logbook start ${ticketId}`));
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
- if (!slug || !entryDir) {
102
- console.error(errorStyle(`Error: No entry found matching ID '${resolvedId}' in logbook or research directories.`));
103
- console.error(errorStyle(`Run 'logbook new <id> <slug>' to create it first.`));
104
- process.exit(1);
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);