dsh-log-slicer 0.1.0 → 0.1.2
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/CHANGELOG.md +17 -0
- package/bin/log-slicer.mjs +7 -0
- package/package.json +7 -1
- package/src/pipeline.js +3 -1
- package/src/register.js +10 -0
- package/src/render.js +30 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# 变更记录
|
|
2
|
+
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
- **修**:`package.json` 里补上 `dsh.bundle.patch`,指向 `./cordis.patch.yml`。少了这一行,装成插件时宿主既不会读那个 patch,也就不会把两个工具注册进会话——CLI 照样能用,装成插件却是哑的。这是装进 profile 时才发现的问题。
|
|
6
|
+
|
|
7
|
+
## 0.1.1
|
|
8
|
+
|
|
9
|
+
三处都是第一次真用(拿 2026-10-31 上午那段去切)时暴露出来的。
|
|
10
|
+
|
|
11
|
+
- **修**:`--kind work` 的三行头里,「原件」默认值从 `已移走` 改成 `(待填)`。工具并不知道那份原件在哪儿,替人写一个断言是越界。
|
|
12
|
+
- **修**:目录里如果某一卷的名字正好是「卷」,它会撞上表头首格,行被插到表头与分隔行中间,表格就坏了。现在表头与分隔行不参与"这是该卷的行"的判断。
|
|
13
|
+
- **增**:指路句识别。形如「我更新了第一轮」「你可以看看那个文件」「这方面内容我们就结束了」的句子,是他往文件上指,不是他的话。dry-run 会把它们列出来,并提示去原件抄真段落;若那段属三级,就不抄,写一句"按三级不抄在这里"。
|
|
14
|
+
|
|
15
|
+
## 0.1.0
|
|
16
|
+
|
|
17
|
+
第一版。取数 → 清洗 → 切片 → 渲染 → 登记,CLI 与插件两条入口共用 `src/pipeline.js`。
|
package/bin/log-slicer.mjs
CHANGED
|
@@ -119,6 +119,13 @@ function main() {
|
|
|
119
119
|
out.push(`# 图片 ${built.imageCount} 张:`);
|
|
120
120
|
for (const img of built.images) out.push(`# ${img.mediaType || '?'} sha256:${img.sha256}`);
|
|
121
121
|
}
|
|
122
|
+
if (built.pointerLines.length) {
|
|
123
|
+
out.push(`# 注意:有 ${built.pointerLines.length} 轮像"指路句"(往文件上指,不是原话)——`);
|
|
124
|
+
for (const p of built.pointerLines) {
|
|
125
|
+
out.push(`# 第 ${p.index + 1} 轮「${p.phrase}」:${p.text.replace(/\s+/g, ' ')}…`);
|
|
126
|
+
}
|
|
127
|
+
out.push('# 去那几份原件里抄真段落;若属三级,就不抄,写一句"按三级不抄在这里"。');
|
|
128
|
+
}
|
|
122
129
|
out.push('', '===== 正文(将追加) =====', '', built.markdown);
|
|
123
130
|
|
|
124
131
|
const row = indexRow(args.volume || (args.file ? path.basename(args.file, '.md') : '卷'), args.title, built.counts.you);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-log-slicer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Slice one section out of a DeepSeek Harness session log: verbatim turns, images, catalog row, backups and a verification hash — one command, dry-run by default.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"dsh.plugin.json",
|
|
34
34
|
"README.md",
|
|
35
35
|
"README.en.md",
|
|
36
|
+
"CHANGELOG.md",
|
|
36
37
|
"LICENSE"
|
|
37
38
|
],
|
|
38
39
|
"engines": {
|
|
@@ -46,6 +47,11 @@
|
|
|
46
47
|
"optional": true
|
|
47
48
|
}
|
|
48
49
|
},
|
|
50
|
+
"dsh": {
|
|
51
|
+
"bundle": {
|
|
52
|
+
"patch": "./cordis.patch.yml"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
49
55
|
"scripts": {
|
|
50
56
|
"test": "node --test"
|
|
51
57
|
}
|
package/src/pipeline.js
CHANGED
|
@@ -8,7 +8,7 @@ import fs from 'node:fs';
|
|
|
8
8
|
import { decodeFile, latestSessionLog } from './decode.js';
|
|
9
9
|
import { turnsFromText } from './clean.js';
|
|
10
10
|
import { sliceTurns, countTurns } from './slice.js';
|
|
11
|
-
import { renderChat, renderWork, applyImageNotes } from './render.js';
|
|
11
|
+
import { renderChat, renderWork, applyImageNotes, findPointerLines } from './render.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @param {object} options
|
|
@@ -33,6 +33,7 @@ export function buildSection(options = {}) {
|
|
|
33
33
|
kind === 'work' ? renderWork(segment, title, workHeader || {}) : renderChat(segment, title);
|
|
34
34
|
|
|
35
35
|
const images = segment.flatMap((t) => t.images || []);
|
|
36
|
+
const pointerLines = findPointerLines(segment);
|
|
36
37
|
|
|
37
38
|
return {
|
|
38
39
|
log: log.path,
|
|
@@ -44,6 +45,7 @@ export function buildSection(options = {}) {
|
|
|
44
45
|
counts,
|
|
45
46
|
imageCount: images.length,
|
|
46
47
|
images,
|
|
48
|
+
pointerLines,
|
|
47
49
|
markdown,
|
|
48
50
|
};
|
|
49
51
|
}
|
package/src/register.js
CHANGED
|
@@ -28,9 +28,19 @@ export function indexRow(volume, title, turns) {
|
|
|
28
28
|
*/
|
|
29
29
|
export function insertIndexRow(markdown, row, volume) {
|
|
30
30
|
const lines = String(markdown).split('\n');
|
|
31
|
+
const cells = (line) => line.split('|').map((c) => c.trim());
|
|
32
|
+
/** 表头与分隔行不参与"这是该卷的行"的判断——卷名叫「卷」时会撞上表头。 */
|
|
33
|
+
const isStructural = (line) => {
|
|
34
|
+
const t = line.trim();
|
|
35
|
+
if (!t.startsWith('|')) return true;
|
|
36
|
+
if (t.includes('---')) return true;
|
|
37
|
+
const c = cells(t);
|
|
38
|
+
return c[1] === '卷' && (c[2] === '节' || c[2] === '标题');
|
|
39
|
+
};
|
|
31
40
|
let last = -1;
|
|
32
41
|
for (let i = 0; i < lines.length; i++) {
|
|
33
42
|
const t = lines[i].trim();
|
|
43
|
+
if (isStructural(t)) continue;
|
|
34
44
|
if (t.startsWith(`| ${volume} |`) || t.startsWith(`|${volume}|`)) last = i;
|
|
35
45
|
}
|
|
36
46
|
if (last >= 0) {
|
package/src/render.js
CHANGED
|
@@ -32,7 +32,7 @@ export function renderChat(segment, title) {
|
|
|
32
32
|
return blocks.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd() + '\n';
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export function renderWork(segment, title, { what = '(待写)', line = '(待写)', why = '(待写)', origin = '
|
|
35
|
+
export function renderWork(segment, title, { what = '(待写)', line = '(待写)', why = '(待写)', origin = '(待填)' } = {}) {
|
|
36
36
|
const out = [
|
|
37
37
|
`## ${title}`,
|
|
38
38
|
'',
|
|
@@ -54,6 +54,35 @@ export function renderWork(segment, title, { what = '(待写)', line = '(
|
|
|
54
54
|
return out.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd() + '\n';
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* 他的话里有没有"指路句"("我更新了第一轮""你可以看看那个文件")。
|
|
59
|
+
*
|
|
60
|
+
* 这类句子不是原话,是他往文件上指;录进正本会让后来的人以为那儿就是内容。
|
|
61
|
+
* 这里只负责认出来、报给调用方,怎么处理(去原件抄、或按三级不抄)由人决定。
|
|
62
|
+
*/
|
|
63
|
+
const POINTER_PATTERNS = [
|
|
64
|
+
/我(?:已经)?更新了第[一二三四五六七八九十\d]+轮/,
|
|
65
|
+
/你可以(?:看看|看一下|读一下|理解一下)/,
|
|
66
|
+
/这方面内容我们就(?:结束|到此)/,
|
|
67
|
+
/我(?:在)?工作区(?:更新|放)了/,
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
export function findPointerLines(segment) {
|
|
71
|
+
const hits = [];
|
|
72
|
+
segment.forEach((t, i) => {
|
|
73
|
+
if (t.who !== 'user') return;
|
|
74
|
+
const body = tidy(t.text);
|
|
75
|
+
for (const re of POINTER_PATTERNS) {
|
|
76
|
+
const m = body.match(re);
|
|
77
|
+
if (m) {
|
|
78
|
+
hits.push({ index: i, phrase: m[0], text: body.slice(0, 60) });
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return hits;
|
|
84
|
+
}
|
|
85
|
+
|
|
57
86
|
/** 把 `(图未落 …)` 占位替换成正式图注。 */
|
|
58
87
|
export function applyImageNotes(markdown, mapping = {}) {
|
|
59
88
|
let out = markdown;
|