@ibgib/vcs-gib 0.0.23 → 0.0.29

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.
Files changed (72) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/VCS_CLI_USER_GUIDE.md +94 -89
  3. package/dist/AUTO-GENERATED-version.d.mts +1 -1
  4. package/dist/AUTO-GENERATED-version.mjs +1 -1
  5. package/dist/cli/cli-arg-parser.d.mts +1 -0
  6. package/dist/cli/cli-arg-parser.d.mts.map +1 -1
  7. package/dist/cli/cli-arg-parser.mjs +5 -1
  8. package/dist/cli/cli-arg-parser.mjs.map +1 -1
  9. package/dist/cli/cli-constants.d.mts +6 -0
  10. package/dist/cli/cli-constants.d.mts.map +1 -1
  11. package/dist/cli/cli-constants.mjs +12 -0
  12. package/dist/cli/cli-constants.mjs.map +1 -1
  13. package/dist/cli/commands/add-cmd.d.mts.map +1 -1
  14. package/dist/cli/commands/add-cmd.mjs +11 -3
  15. package/dist/cli/commands/add-cmd.mjs.map +1 -1
  16. package/dist/cli/commands/commit-cmd.d.mts +5 -4
  17. package/dist/cli/commands/commit-cmd.d.mts.map +1 -1
  18. package/dist/cli/commands/commit-cmd.mjs +155 -35
  19. package/dist/cli/commands/commit-cmd.mjs.map +1 -1
  20. package/dist/cli/commands/generate-comment-cmd.d.mts +19 -0
  21. package/dist/cli/commands/generate-comment-cmd.d.mts.map +1 -0
  22. package/dist/cli/commands/generate-comment-cmd.mjs +140 -0
  23. package/dist/cli/commands/generate-comment-cmd.mjs.map +1 -0
  24. package/dist/cli/commands/log-cmd.d.mts +12 -3
  25. package/dist/cli/commands/log-cmd.d.mts.map +1 -1
  26. package/dist/cli/commands/log-cmd.mjs +242 -39
  27. package/dist/cli/commands/log-cmd.mjs.map +1 -1
  28. package/dist/cli/commands/status-cmd.d.mts +5 -6
  29. package/dist/cli/commands/status-cmd.d.mts.map +1 -1
  30. package/dist/cli/commands/status-cmd.mjs +51 -205
  31. package/dist/cli/commands/status-cmd.mjs.map +1 -1
  32. package/dist/cli/vcs-cli-main.d.mts.map +1 -1
  33. package/dist/cli/vcs-cli-main.mjs +25 -4
  34. package/dist/cli/vcs-cli-main.mjs.map +1 -1
  35. package/dist/engine/branch/branch-helper.d.mts +14 -0
  36. package/dist/engine/branch/branch-helper.d.mts.map +1 -1
  37. package/dist/engine/branch/branch-helper.mjs +21 -0
  38. package/dist/engine/branch/branch-helper.mjs.map +1 -1
  39. package/dist/engine/index/index-helper.d.mts.map +1 -1
  40. package/dist/engine/index/index-helper.mjs +3 -0
  41. package/dist/engine/index/index-helper.mjs.map +1 -1
  42. package/dist/engine/item/item-helper.d.mts.map +1 -1
  43. package/dist/engine/item/item-helper.mjs +5 -2
  44. package/dist/engine/item/item-helper.mjs.map +1 -1
  45. package/dist/engine/merge/b2tfs-3way-graft-strategy.d.mts +28 -0
  46. package/dist/engine/merge/b2tfs-3way-graft-strategy.d.mts.map +1 -1
  47. package/dist/engine/merge/b2tfs-3way-graft-strategy.mjs +84 -12
  48. package/dist/engine/merge/b2tfs-3way-graft-strategy.mjs.map +1 -1
  49. package/dist/engine/status/status-helper.d.mts +90 -0
  50. package/dist/engine/status/status-helper.d.mts.map +1 -0
  51. package/dist/engine/status/status-helper.mjs +286 -0
  52. package/dist/engine/status/status-helper.mjs.map +1 -0
  53. package/package.json +3 -3
  54. package/src/AUTO-GENERATED-version.mts +1 -1
  55. package/src/cli/cli-arg-parser.mts +5 -0
  56. package/src/cli/cli-constants.mts +13 -0
  57. package/src/cli/commands/add-cmd.mts +11 -3
  58. package/src/cli/commands/branch-checkout-cmd.respec.mts +54 -0
  59. package/src/cli/commands/commit-cmd.mts +171 -44
  60. package/src/cli/commands/commit-log-cmd.respec.mts +216 -3
  61. package/src/cli/commands/generate-comment-cmd.mts +171 -0
  62. package/src/cli/commands/log-cmd.mts +294 -42
  63. package/src/cli/commands/status-cmd.mts +58 -256
  64. package/src/cli/vcs-cli-main.mts +25 -4
  65. package/src/engine/branch/branch-helper.mts +33 -0
  66. package/src/engine/index/index-helper.mts +3 -0
  67. package/src/engine/item/item-helper.mts +5 -2
  68. package/src/engine/merge/b2tfs-3way-graft-strategy.mts +89 -10
  69. package/src/engine/merge/b2tfs-sync-comments.respec.mts +399 -0
  70. package/src/engine/status/status-helper.mts +383 -0
  71. package/tsconfig.test.tsbuildinfo +1 -1
  72. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,171 @@
1
+ /**
2
+ * @module generate-comment-cmd
3
+ *
4
+ * Command handler for `vcs generate-comment` (synonyms: `gen-comment`, `gen-msg`, `gcomm`, `gmsg`).
5
+ * Scans repository status or target paths and creates `.ib.<file>.md` or `.ib.<topic>.md` companion metadata files.
6
+ */
7
+
8
+ import path from 'node:path';
9
+ import fs from 'node:fs/promises';
10
+
11
+ import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
12
+ import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
13
+ import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
14
+ import { getRepoStatus } from '../../engine/status/status-helper.mjs';
15
+
16
+ const lcFile = `[generate-comment-cmd]`;
17
+ const logalot = GLOBAL_LOG_A_LOT;
18
+
19
+ export interface GenerateCommentCmdOpts {
20
+ targetDir?: string;
21
+ vcsFolderName?: string;
22
+ paths?: string[];
23
+ folderTopic?: string;
24
+ quiet?: boolean;
25
+ }
26
+
27
+ export interface GenerateCommentCmdResult {
28
+ createdFiles: string[];
29
+ skippedFiles: string[];
30
+ }
31
+
32
+ export async function executeGenerateCommentCmd({
33
+ targetDir = process.cwd(),
34
+ vcsFolderName = '.vcsgib',
35
+ paths = [],
36
+ folderTopic = 'notes',
37
+ quiet = false,
38
+ }: GenerateCommentCmdOpts = {}): Promise<GenerateCommentCmdResult> {
39
+ const lc = `${lcFile}[${executeGenerateCommentCmd.name}]`;
40
+
41
+ try {
42
+ if (logalot) {
43
+ console.log(`${lc} starting executeGenerateCommentCmd (I: 8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e)`);
44
+ }
45
+
46
+ const absTargetDir = path.resolve(targetDir);
47
+
48
+ const { metaspace, localSpace, vcsPath, repoRoot } = await bootstrapVcsMetaspace({
49
+ repoPath: absTargetDir,
50
+ vcsFolderName,
51
+ });
52
+
53
+ // Verify repo exists
54
+ try {
55
+ await fs.access(vcsPath);
56
+ } catch {
57
+ throw new Error(`Not an ibgib repository (directory ${vcsPath} does not exist) (E: 9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f)`);
58
+ }
59
+
60
+ const statusRes = await getRepoStatus({
61
+ metaspace,
62
+ localSpace,
63
+ vcsPath,
64
+ repoRoot,
65
+ vcsFolderName,
66
+ });
67
+
68
+ const activeWorktreeDir = statusRes.activeWorktreeDir;
69
+ const targetRelPaths: string[] = [];
70
+
71
+ if (paths.length > 0) {
72
+ for (const p of paths) {
73
+ const absP = path.resolve(activeWorktreeDir, p);
74
+ const relP = path.relative(activeWorktreeDir, absP).replace(/\\/g, '/');
75
+ if (relP && !targetRelPaths.includes(relP)) {
76
+ targetRelPaths.push(relP);
77
+ }
78
+ }
79
+ } else {
80
+ // Auto-detect from staged and modified items
81
+ for (const s of statusRes.staged) {
82
+ if (!targetRelPaths.includes(s.path)) {
83
+ targetRelPaths.push(s.path);
84
+ }
85
+ }
86
+ for (const m of statusRes.modified) {
87
+ if (!targetRelPaths.includes(m.path)) {
88
+ targetRelPaths.push(m.path);
89
+ }
90
+ }
91
+ for (const u of statusRes.untracked) {
92
+ if (!targetRelPaths.includes(u.path)) {
93
+ targetRelPaths.push(u.path);
94
+ }
95
+ }
96
+ }
97
+
98
+ const createdFiles: string[] = [];
99
+ const skippedFiles: string[] = [];
100
+
101
+ for (const relP of targetRelPaths) {
102
+ const absP = path.join(activeWorktreeDir, relP);
103
+
104
+ let isDirectory = false;
105
+ try {
106
+ const stat = await fs.stat(absP);
107
+ isDirectory = stat.isDirectory();
108
+ } catch {
109
+ // If path doesn't exist yet on disk, guess based on extension
110
+ isDirectory = !path.extname(relP);
111
+ }
112
+
113
+ let commentFileName: string;
114
+ let commentDirPath: string;
115
+
116
+ if (isDirectory) {
117
+ commentDirPath = absP;
118
+ commentFileName = `.ib.${folderTopic}.md`;
119
+ } else {
120
+ commentDirPath = path.dirname(absP);
121
+ const baseName = path.basename(absP);
122
+ commentFileName = `.ib.${baseName}.md`;
123
+ }
124
+
125
+ const commentFullPath = path.join(commentDirPath, commentFileName);
126
+ const commentRelPath = path.relative(activeWorktreeDir, commentFullPath).replace(/\\/g, '/');
127
+
128
+ try {
129
+ await fs.access(commentFullPath);
130
+ // Already exists
131
+ skippedFiles.push(commentRelPath);
132
+ } catch {
133
+ // Does not exist -> create boilerplate template
134
+ const headerTarget = relP ? `\`${relP}\`` : 'root';
135
+ const boilerplate = `# ${headerTarget}\n\n(Enter contemporaneous notes/comments here)\n`;
136
+ await fs.mkdir(commentDirPath, { recursive: true });
137
+ await fs.writeFile(commentFullPath, boilerplate, 'utf8');
138
+ createdFiles.push(commentRelPath);
139
+ }
140
+ }
141
+
142
+ if (!quiet) {
143
+ if (createdFiles.length > 0) {
144
+ console.log(`Generated ${createdFiles.length} comment file(s):`);
145
+ for (const f of createdFiles) {
146
+ console.log(` created: ${f}`);
147
+ }
148
+ }
149
+ if (skippedFiles.length > 0) {
150
+ for (const f of skippedFiles) {
151
+ console.log(` already exists: ${f}`);
152
+ }
153
+ }
154
+ if (createdFiles.length === 0 && skippedFiles.length === 0) {
155
+ console.log(`No modified or staged files found to generate comments for.`);
156
+ }
157
+ }
158
+
159
+ return {
160
+ createdFiles,
161
+ skippedFiles,
162
+ };
163
+ } catch (error) {
164
+ console.error(`${lc} ${extractErrorMsg(error)} (E: 0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a)`);
165
+ throw error;
166
+ } finally {
167
+ if (logalot) {
168
+ console.log(`${lc} complete. (I: 1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b)`);
169
+ }
170
+ }
171
+ }
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Command handler for `vcs log`.
5
5
  * Reads active branch timeline from B2tFSIndexIbGib_V1 in localSpace,
6
- * walks branch timeline history backward, and displays formatted commit history.
6
+ * walks branch or item timeline history backward, and displays formatted commit and comment history.
7
7
  */
8
8
 
9
9
  import path from 'node:path';
@@ -11,12 +11,13 @@ import fs from 'node:fs/promises';
11
11
 
12
12
  import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
13
13
  import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
14
- import { getIbGibsFromCache_fallbackToSpaces } from '@ibgib/core-gib/dist/common/other/ibgib-helper.mjs';
15
14
  import { CommentIbGib_V1 } from '@ibgib/core-gib/dist/common/comment/comment-types.mjs';
16
15
 
17
16
  import { bootstrapVcsMetaspace } from '../../bootstrap/node-bootstrap.mjs';
18
17
  import { getOrCreateBranchSpace } from '../../engine/branch/branch-space-helper.mjs';
18
+ import { resolveContextualBranchName } from '../../engine/branch/branch-helper.mjs';
19
19
  import { getIbGibWithMetaspaceFallback } from '../../engine/item/item-helper.mjs';
20
+ import { B2tFSItemIbGib_V1, B2TFS_CHILD_ITEM_REL8N_NAME } from '../../engine/item/item-types.mjs';
20
21
  import { B2tFSBranchIbGib_V1 } from '../../engine/branch/branch-types.mjs';
21
22
  import { getB2tFSIndexIbGib, extractBranchNameFromAddr } from '../../engine/index/index-helper.mjs';
22
23
  import { GLOBAL_LOG_A_LOT } from '../../vcs-gib-constants.mjs';
@@ -25,29 +26,36 @@ const lcFile = `[log-cmd]`;
25
26
  const logalot = GLOBAL_LOG_A_LOT;
26
27
 
27
28
  export interface VcsLogItem {
28
- branchAddr: string;
29
+ branchAddr?: string;
29
30
  commitAddr: string;
30
31
  commentAddr?: string;
31
32
  message: string;
32
33
  timestamp: string;
33
34
  author: string;
34
35
  rootItemAddr?: string;
36
+ committedPaths?: string[];
37
+ itemComments?: Array<{ path: string; name?: string; message: string }>;
35
38
  }
36
39
 
37
40
  export interface LogCmdOpts {
38
41
  targetDir?: string;
39
42
  vcsFolderName?: string;
43
+ filePath?: string;
44
+ allComments?: boolean;
40
45
  quiet?: boolean;
41
46
  }
42
47
 
43
48
  export interface LogCmdResult {
44
49
  branchName: string;
50
+ targetPath?: string;
45
51
  commits: VcsLogItem[];
46
52
  }
47
53
 
48
54
  export async function executeLogCmd({
49
55
  targetDir = process.cwd(),
50
56
  vcsFolderName = '.vcsgib',
57
+ filePath,
58
+ allComments = false,
51
59
  quiet = false,
52
60
  }: LogCmdOpts = {}): Promise<LogCmdResult> {
53
61
  const lc = `${lcFile}[${executeLogCmd.name}]`;
@@ -77,7 +85,11 @@ export async function executeLogCmd({
77
85
  throw new Error(`Repository index B2tFSIndexIbGib_V1 not found in localSpace (E: 3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a)`);
78
86
  }
79
87
 
80
- const branchName = indexIbGib.data?.activeBranch || 'main';
88
+ const branchName = resolveContextualBranchName({
89
+ targetDir: absTargetDir,
90
+ repoRoot,
91
+ indexIbGib,
92
+ });
81
93
 
82
94
  // Target active branch space b2s_<activeBranch>
83
95
  const { space: activeSpace } = await getOrCreateBranchSpace({
@@ -87,45 +99,122 @@ export async function executeLogCmd({
87
99
  });
88
100
 
89
101
  // 2. Fetch active branch node address matching active branchName
90
- const branchAddrs = [
91
- ...(indexIbGib.rel8ns?.branch || []),
92
- ...(indexIbGib.rel8ns?.branch_tjp || []),
93
- ];
102
+ const branchAddrs = indexIbGib.rel8ns?.branch || indexIbGib.rel8ns?.branch_tjp || [];
94
103
 
95
104
  let currBranchAddr: string | undefined;
96
-
97
- // Match branchName against branchAddrs by space-qualified address or raw atom ib string
98
- for (const addr of branchAddrs) {
99
- const parsedName = extractBranchNameFromAddr(addr);
100
- if (parsedName === branchName) {
101
- currBranchAddr = addr;
102
- break;
103
- }
104
- }
105
-
106
- // Fallback: fetch branch nodes via getIbGibWithMetaspaceFallback to match b.data?.name === branchName
107
- if (!currBranchAddr) {
108
- for (const addr of branchAddrs) {
105
+ if (branchAddrs.length > 0) {
106
+ let branchIbGibs: B2tFSBranchIbGib_V1[] = [];
107
+ for (const bAddr of branchAddrs) {
109
108
  const bIbGib = await getIbGibWithMetaspaceFallback({
110
- addr,
109
+ addr: bAddr,
111
110
  space: activeSpace,
112
111
  metaspace,
113
- }) as B2tFSBranchIbGib_V1;
114
- if (bIbGib?.data?.name === branchName) {
115
- currBranchAddr = addr;
116
- break;
112
+ });
113
+ if (bIbGib) {
114
+ branchIbGibs.push(bIbGib as B2tFSBranchIbGib_V1);
117
115
  }
118
116
  }
117
+
118
+ const matchingBranches = branchIbGibs.filter(b => b?.data?.name === branchName);
119
+ const activeBranchIbGib = matchingBranches.sort((a, b) => (b.data?.n || 0) - (a.data?.n || 0))[0]
120
+ || branchIbGibs.at(-1);
121
+
122
+ if (activeBranchIbGib) {
123
+ currBranchAddr = getIbGibAddr({ ibGib: activeBranchIbGib });
124
+ }
119
125
  }
120
126
 
121
- // Ultimate fallback if no match found
122
- if (!currBranchAddr) {
123
- currBranchAddr = branchAddrs.at(0);
127
+ const commits: VcsLogItem[] = [];
128
+
129
+ // #region File-Specific Log Mode (vcs log <filePath>)
130
+ if (filePath) {
131
+ const normalizedRelPath = path.relative(repoRoot, path.resolve(absTargetDir, filePath)).replace(/\\/g, '/');
132
+
133
+ // Find current root item
134
+ const branchIbGib = currBranchAddr
135
+ ? await getIbGibWithMetaspaceFallback({ addr: currBranchAddr, space: activeSpace, metaspace }) as B2tFSBranchIbGib_V1
136
+ : undefined;
137
+ const rootItemAddr = branchIbGib?.rel8ns?.b2tFS_item?.at(-1) || branchIbGib?.rel8ns?.b2tfs_item?.at(-1);
138
+
139
+ if (!rootItemAddr) {
140
+ throw new Error(`No root item found on branch ${branchName} (E: 4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b)`);
141
+ }
142
+
143
+ const targetItem = await logCmd_findItemByPath({
144
+ rootItemAddr,
145
+ targetRelPath: normalizedRelPath,
146
+ space: activeSpace,
147
+ metaspace,
148
+ });
149
+
150
+ if (!targetItem) {
151
+ throw new Error(`Path '${normalizedRelPath}' not found in active branch snapshot (E: 5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c)`);
152
+ }
153
+
154
+ // Walk target item timeline history backward
155
+ const visitedItemAddrs = new Set<string>();
156
+ let currItem: B2tFSItemIbGib_V1 | undefined = targetItem;
157
+
158
+ while (currItem) {
159
+ const itemAddr = getIbGibAddr({ ibGib: currItem });
160
+ if (visitedItemAddrs.has(itemAddr)) { break; }
161
+ visitedItemAddrs.add(itemAddr);
162
+
163
+ const commentAddrs = currItem.rel8ns?.comment || [];
164
+ for (const cAddr of commentAddrs) {
165
+ const commentIbGib = await getIbGibWithMetaspaceFallback({
166
+ addr: cAddr,
167
+ space: activeSpace,
168
+ metaspace,
169
+ }) as CommentIbGib_V1;
170
+
171
+ if (commentIbGib) {
172
+ commits.push({
173
+ commitAddr: itemAddr,
174
+ commentAddr: cAddr,
175
+ message: commentIbGib.data?.text || '',
176
+ timestamp: commentIbGib.data?.textTimestamp || commentIbGib.data?.timestamp || '',
177
+ author: 'vcs-user',
178
+ });
179
+ }
180
+ }
181
+
182
+ const pastAddr = currItem.rel8ns?.past?.at(0) || currItem.rel8ns?.ancestor?.at(0);
183
+ if (pastAddr) {
184
+ currItem = await getIbGibWithMetaspaceFallback({
185
+ addr: pastAddr,
186
+ space: activeSpace,
187
+ metaspace,
188
+ }) as B2tFSItemIbGib_V1;
189
+ } else {
190
+ currItem = undefined;
191
+ }
192
+ }
193
+
194
+ if (!quiet) {
195
+ console.log(`File: ${normalizedRelPath} (branch: ${branchName}) (I: 6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d)`);
196
+ if (commits.length === 0) {
197
+ console.log('No comments found for this file.');
198
+ } else {
199
+ for (const commit of commits) {
200
+ console.log(`\nitem ${commit.commitAddr}`);
201
+ if (commit.timestamp) { console.log(`Date: ${commit.timestamp}`); }
202
+ console.log(`\n ${commit.message}\n`);
203
+ }
204
+ }
205
+ }
206
+
207
+ return {
208
+ branchName,
209
+ targetPath: normalizedRelPath,
210
+ commits,
211
+ };
124
212
  }
213
+ // #endregion File-Specific Log Mode
214
+
215
+ // #region Standard Branch Log Mode
125
216
  const visitedAddrs = new Set<string>();
126
- const commits: VcsLogItem[] = [];
127
217
 
128
- // 3. Walk timeline history backward
129
218
  while (currBranchAddr && !visitedAddrs.has(currBranchAddr)) {
130
219
  visitedAddrs.add(currBranchAddr);
131
220
 
@@ -139,28 +228,68 @@ export async function executeLogCmd({
139
228
  break;
140
229
  }
141
230
 
142
- const commentAddr = branchIbGib.rel8ns?.comment?.at(0);
143
- const rootItemAddr = branchIbGib.rel8ns?.b2tfs_item?.at(0);
231
+ const rootItemAddr = branchIbGib.rel8ns?.b2tFS_item?.at(-1) || branchIbGib.rel8ns?.b2tfs_item?.at(-1);
232
+
233
+ let rootItem: B2tFSItemIbGib_V1 | undefined;
234
+ if (rootItemAddr) {
235
+ rootItem = await getIbGibWithMetaspaceFallback({
236
+ addr: rootItemAddr,
237
+ space: activeSpace,
238
+ metaspace,
239
+ }) as B2tFSItemIbGib_V1;
240
+ }
241
+
242
+ let commentAddrs = [
243
+ ...(branchIbGib.rel8ns?.comment || []),
244
+ ...(rootItem?.rel8ns?.comment || []),
245
+ ];
246
+ // Deduplicate
247
+ commentAddrs = Array.from(new Set(commentAddrs));
144
248
 
145
- if (commentAddr) {
249
+ let message = '';
250
+ let timestamp = branchIbGib.data?.committedAt || '';
251
+ // If branch has a direct comment, use that first, otherwise use the last added comment on root item
252
+ let primaryCommentAddr = branchIbGib.rel8ns?.comment?.at(-1) || rootItem?.rel8ns?.comment?.at(-1) || commentAddrs.at(0);
253
+
254
+ if (primaryCommentAddr) {
146
255
  const commentIbGib = await getIbGibWithMetaspaceFallback({
147
- addr: commentAddr,
256
+ addr: primaryCommentAddr,
148
257
  space: activeSpace,
149
258
  metaspace,
150
259
  }) as CommentIbGib_V1;
151
260
 
152
- const message = commentIbGib?.data?.text || '';
153
- const timestamp = commentIbGib?.data?.textTimestamp || commentIbGib?.data?.timestamp || branchIbGib.data?.committedAt || '';
154
- const author = 'vcs-user';
261
+ if (commentIbGib?.data?.text) {
262
+ message = commentIbGib.data.text;
263
+ }
264
+ if (commentIbGib?.data?.textTimestamp || commentIbGib?.data?.timestamp) {
265
+ timestamp = commentIbGib.data.textTimestamp || commentIbGib.data.timestamp || timestamp;
266
+ }
267
+ }
268
+
269
+ const committedPaths = branchIbGib.data?.committedPaths || [];
270
+ const itemComments: Array<{ path: string; name?: string; message: string }> = [];
155
271
 
272
+ if (allComments && rootItem) {
273
+ await logCmd_collectAllItemComments({
274
+ item: rootItem,
275
+ space: activeSpace,
276
+ metaspace,
277
+ currentRelPath: '',
278
+ outComments: itemComments,
279
+ });
280
+ }
281
+
282
+ if (message || primaryCommentAddr || (committedPaths && committedPaths.length > 0)) {
156
283
  commits.push({
157
284
  branchAddr: currBranchAddr,
158
- commitAddr: commentAddr,
159
- commentAddr,
160
- message,
285
+ commitAddr: currBranchAddr,
286
+ commentAddr: primaryCommentAddr,
287
+ message: message || '(no commit message)',
161
288
  timestamp,
162
- author,
289
+ author: 'vcs-user',
163
290
  rootItemAddr,
291
+ committedPaths,
292
+ itemComments: itemComments.length > 0 ? itemComments : undefined,
164
293
  });
165
294
  }
166
295
 
@@ -178,6 +307,16 @@ export async function executeLogCmd({
178
307
  console.log(`Author: ${commit.author}`);
179
308
  console.log(`Date: ${commit.timestamp}`);
180
309
  console.log(`\n ${commit.message}\n`);
310
+ if (commit.committedPaths && commit.committedPaths.length > 0) {
311
+ console.log(`Changed: ${commit.committedPaths.join(', ')}`);
312
+ }
313
+ if (commit.itemComments && commit.itemComments.length > 0) {
314
+ console.log('Item Comments:');
315
+ for (const ic of commit.itemComments) {
316
+ const label = ic.name ? `${ic.path} (${ic.name})` : ic.path;
317
+ console.log(` * [${label}]: ${ic.message}`);
318
+ }
319
+ }
181
320
  }
182
321
  }
183
322
  }
@@ -186,6 +325,7 @@ export async function executeLogCmd({
186
325
  branchName,
187
326
  commits,
188
327
  };
328
+ // #endregion Standard Branch Log Mode
189
329
  } catch (error) {
190
330
  console.error(`${lc} ${extractErrorMsg(error)} (E: 5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c)`);
191
331
  throw error;
@@ -195,3 +335,115 @@ export async function executeLogCmd({
195
335
  }
196
336
  }
197
337
  }
338
+
339
+ async function logCmd_findItemByPath({
340
+ rootItemAddr,
341
+ targetRelPath,
342
+ space,
343
+ metaspace,
344
+ }: {
345
+ rootItemAddr: string;
346
+ targetRelPath: string;
347
+ space: any;
348
+ metaspace?: any;
349
+ }): Promise<B2tFSItemIbGib_V1 | undefined> {
350
+ const rootItem = await getIbGibWithMetaspaceFallback({
351
+ addr: rootItemAddr,
352
+ space,
353
+ metaspace,
354
+ }) as B2tFSItemIbGib_V1;
355
+
356
+ if (!rootItem) { return undefined; }
357
+ if (!targetRelPath || targetRelPath === '.' || targetRelPath === '') {
358
+ return rootItem;
359
+ }
360
+
361
+ const segments = targetRelPath.split('/');
362
+ let currentItem: B2tFSItemIbGib_V1 = rootItem;
363
+
364
+ for (const segment of segments) {
365
+ if (currentItem.data?.fsType !== 'folder') {
366
+ return undefined;
367
+ }
368
+
369
+ const childAddrs = currentItem.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
370
+ let nextItem: B2tFSItemIbGib_V1 | undefined;
371
+
372
+ for (const cAddr of childAddrs) {
373
+ const child = await getIbGibWithMetaspaceFallback({
374
+ addr: cAddr,
375
+ space,
376
+ metaspace,
377
+ }) as B2tFSItemIbGib_V1;
378
+
379
+ if (child && child.data?.name === segment) {
380
+ nextItem = child;
381
+ break;
382
+ }
383
+ }
384
+
385
+ if (!nextItem) {
386
+ return undefined;
387
+ }
388
+ currentItem = nextItem;
389
+ }
390
+
391
+ return currentItem;
392
+ }
393
+
394
+ async function logCmd_collectAllItemComments({
395
+ item,
396
+ space,
397
+ metaspace,
398
+ currentRelPath,
399
+ outComments,
400
+ }: {
401
+ item: B2tFSItemIbGib_V1;
402
+ space: any;
403
+ metaspace?: any;
404
+ currentRelPath: string;
405
+ outComments: Array<{ path: string; name?: string; message: string }>;
406
+ }): Promise<void> {
407
+ if (!item || !item.data) { return; }
408
+
409
+ const itemComments = item.rel8ns?.comment || [];
410
+ for (const cAddr of itemComments) {
411
+ const commentIbGib = await getIbGibWithMetaspaceFallback({
412
+ addr: cAddr,
413
+ space,
414
+ metaspace,
415
+ }) as CommentIbGib_V1;
416
+
417
+ if (commentIbGib && commentIbGib.data?.text) {
418
+ outComments.push({
419
+ path: currentRelPath || '(root)',
420
+ name: (commentIbGib.data as any)?.name,
421
+ message: commentIbGib.data.text,
422
+ });
423
+ }
424
+ }
425
+
426
+ if (item.data.fsType === 'folder') {
427
+ const childAddrs = item.rel8ns?.[B2TFS_CHILD_ITEM_REL8N_NAME] || [];
428
+ for (const childAddr of childAddrs) {
429
+ const childItem = await getIbGibWithMetaspaceFallback({
430
+ addr: childAddr,
431
+ space,
432
+ metaspace,
433
+ }) as B2tFSItemIbGib_V1;
434
+
435
+ if (childItem && childItem.data) {
436
+ const childRel = currentRelPath
437
+ ? `${currentRelPath}/${childItem.data.name}`
438
+ : childItem.data.name;
439
+ await logCmd_collectAllItemComments({
440
+ item: childItem,
441
+ space,
442
+ metaspace,
443
+ currentRelPath: childRel,
444
+ outComments,
445
+ });
446
+ }
447
+ }
448
+ }
449
+ }