wtf-p 0.3.0 → 0.5.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 +146 -208
- package/bin/commands/doctor.js +88 -2
- package/bin/commands/install-logic.js +127 -36
- package/bin/commands/status.js +22 -4
- package/bin/commands/update.js +5 -4
- package/bin/install.js +66 -26
- package/bin/lib/analyze-impact.js +105 -0
- package/bin/lib/bib-format.js +161 -0
- package/bin/lib/bib-index.js +104 -0
- package/bin/lib/checkpoint.js +183 -0
- package/bin/lib/citation-fetcher.js +299 -0
- package/bin/lib/citation-ranker.js +133 -0
- package/bin/lib/context-primer.js +214 -0
- package/bin/lib/manifest.js +80 -2
- package/bin/lib/scholar-lookup.js +188 -0
- package/bin/lib/semantic-scholar.js +184 -0
- package/bin/lib/utils.js +22 -1
- package/bin/uninstall.js +270 -111
- package/core/write-the-f-paper/references/agent-model-matrix.md +62 -0
- package/core/write-the-f-paper/references/checkpoints.md +160 -9
- package/core/write-the-f-paper/references/context-fidelity.md +153 -0
- package/core/write-the-f-paper/references/deviation-rules.md +150 -0
- package/core/write-the-f-paper/references/git-integration.md +80 -0
- package/core/write-the-f-paper/references/orchestrator-pattern.md +167 -0
- package/core/write-the-f-paper/references/planning-config.md +135 -0
- package/core/write-the-f-paper/references/ui-brand.md +186 -0
- package/core/write-the-f-paper/templates/UAT.md +80 -0
- package/core/write-the-f-paper/templates/base-prefs.yaml +42 -0
- package/core/write-the-f-paper/templates/config.json +31 -5
- package/core/write-the-f-paper/workflows/create-outline.wcn.md +1 -1
- package/core/write-the-f-paper/workflows/execute-outline.md +163 -0
- package/core/write-the-f-paper/workflows/execute-outline.wcn.md +73 -0
- package/core/write-the-f-paper/workflows/execute-section.wcn.md +141 -169
- package/core/write-the-f-paper/workflows/plan-section.wcn.md +96 -103
- package/core/write-the-f-paper/workflows/research-gap.md +27 -3
- package/core/write-the-f-paper/workflows/research-gap.wcn.md +18 -1
- package/core/write-the-f-paper/workflows/verify-work.md +251 -0
- package/core/write-the-f-paper/workflows/verify-work.wcn.md +74 -0
- package/package.json +19 -7
- package/vendors/claude/.claude-plugin/plugin.json +2 -2
- package/vendors/claude/agents/wtfp/argument-verifier.md +189 -0
- package/vendors/claude/agents/wtfp/citation-expert.md +45 -0
- package/vendors/claude/agents/wtfp/citation-formatter.md +42 -0
- package/vendors/claude/agents/wtfp/coherence-checker.md +167 -0
- package/vendors/claude/agents/wtfp/outliner.md +259 -0
- package/vendors/claude/agents/wtfp/plan-checker.md +249 -0
- package/vendors/claude/agents/wtfp/prose-polisher.md +166 -0
- package/vendors/claude/agents/wtfp/research-synthesizer.md +215 -0
- package/vendors/claude/agents/wtfp/section-planner.md +364 -0
- package/vendors/claude/agents/wtfp/section-reviewer.md +182 -0
- package/vendors/claude/agents/wtfp/section-writer.md +312 -0
- package/vendors/claude/commands/wtfp/add-todo.md +108 -0
- package/vendors/claude/commands/wtfp/analyze-bib.md +60 -249
- package/vendors/claude/commands/wtfp/audit-milestone.md +286 -0
- package/vendors/claude/commands/wtfp/check-refs.md +60 -131
- package/vendors/claude/commands/wtfp/check-todos.md +152 -0
- package/vendors/claude/commands/wtfp/checkpoint.md +105 -0
- package/vendors/claude/commands/wtfp/contribute.md +1 -1
- package/vendors/claude/commands/wtfp/create-outline.md +109 -195
- package/vendors/claude/commands/wtfp/create-poster.md +1 -1
- package/vendors/claude/commands/wtfp/create-slides.md +1 -1
- package/vendors/claude/commands/wtfp/discuss-section.md +1 -1
- package/vendors/claude/commands/wtfp/execute-outline.md +257 -0
- package/vendors/claude/commands/wtfp/export-latex.md +1 -1
- package/vendors/claude/commands/wtfp/help.md +174 -280
- package/vendors/claude/commands/wtfp/insert-section.md +1 -1
- package/vendors/claude/commands/wtfp/list-assumptions.md +1 -1
- package/vendors/claude/commands/wtfp/map-project.md +35 -124
- package/vendors/claude/commands/wtfp/new-paper.md +88 -362
- package/vendors/claude/commands/wtfp/pause-writing.md +1 -1
- package/vendors/claude/commands/wtfp/plan-milestone-gaps.md +255 -0
- package/vendors/claude/commands/wtfp/plan-revision.md +1 -1
- package/vendors/claude/commands/wtfp/plan-section.md +165 -190
- package/vendors/claude/commands/wtfp/polish-prose.md +66 -118
- package/vendors/claude/commands/wtfp/progress.md +82 -254
- package/vendors/claude/commands/wtfp/quick.md +107 -0
- package/vendors/claude/commands/wtfp/remove-section.md +1 -1
- package/vendors/claude/commands/wtfp/report-bug.md +1 -1
- package/vendors/claude/commands/wtfp/request-feature.md +1 -1
- package/vendors/claude/commands/wtfp/research-gap.md +138 -151
- package/vendors/claude/commands/wtfp/resume-writing.md +1 -1
- package/vendors/claude/commands/wtfp/review-section.md +55 -333
- package/vendors/claude/commands/wtfp/settings.md +197 -0
- package/vendors/claude/commands/wtfp/submit-milestone.md +248 -99
- package/vendors/claude/commands/wtfp/update.md +112 -0
- package/vendors/claude/commands/wtfp/verify-work.md +254 -0
- package/vendors/claude/commands/wtfp/write-section.md +144 -180
- package/vendors/claude/mcp/research-server/package.json +13 -0
- package/vendors/claude/mcp/research-server/src/index.js +133 -0
- package/vendors/gemini/agents/wtfp/argument-verifier.md +184 -0
- package/vendors/gemini/agents/wtfp/citation-expert.md +41 -0
- package/vendors/gemini/agents/wtfp/citation-formatter.md +38 -0
- package/vendors/gemini/agents/wtfp/coherence-checker.md +162 -0
- package/vendors/gemini/agents/wtfp/outliner.md +252 -0
- package/vendors/gemini/agents/wtfp/plan-checker.md +244 -0
- package/vendors/gemini/agents/wtfp/prose-polisher.md +158 -0
- package/vendors/gemini/agents/wtfp/research-synthesizer.md +205 -0
- package/vendors/gemini/agents/wtfp/section-planner.md +355 -0
- package/vendors/gemini/agents/wtfp/section-reviewer.md +175 -0
- package/vendors/gemini/agents/wtfp/section-writer.md +304 -0
- package/vendors/gemini/commands/wtfp/add-todo.toml +102 -0
- package/vendors/gemini/commands/wtfp/analyze-bib.toml +126 -0
- package/vendors/gemini/commands/wtfp/audit-milestone.toml +278 -0
- package/vendors/gemini/commands/wtfp/check-refs.toml +123 -0
- package/vendors/gemini/commands/wtfp/check-todos.toml +145 -0
- package/vendors/gemini/commands/wtfp/checkpoint.toml +96 -0
- package/vendors/gemini/commands/wtfp/contribute.toml +269 -0
- package/vendors/gemini/commands/wtfp/create-outline.toml +180 -0
- package/vendors/gemini/commands/wtfp/create-poster.toml +36 -0
- package/vendors/gemini/commands/wtfp/create-slides.toml +35 -0
- package/vendors/gemini/commands/wtfp/discuss-section.toml +62 -0
- package/vendors/gemini/commands/wtfp/execute-outline.toml +247 -0
- package/vendors/gemini/commands/wtfp/export-latex.toml +209 -0
- package/vendors/gemini/commands/wtfp/help.toml +184 -0
- package/vendors/gemini/commands/wtfp/insert-section.toml +123 -0
- package/vendors/gemini/commands/wtfp/list-assumptions.toml +104 -0
- package/vendors/gemini/commands/wtfp/map-project.toml +99 -0
- package/vendors/gemini/commands/wtfp/new-paper.toml +160 -0
- package/vendors/gemini/commands/wtfp/pause-writing.toml +140 -0
- package/vendors/gemini/commands/wtfp/plan-milestone-gaps.toml +249 -0
- package/vendors/gemini/commands/wtfp/plan-revision.toml +171 -0
- package/vendors/gemini/commands/wtfp/plan-section.toml +212 -0
- package/vendors/gemini/commands/wtfp/polish-prose.toml +114 -0
- package/vendors/gemini/commands/wtfp/progress.toml +145 -0
- package/vendors/gemini/commands/wtfp/quick.toml +96 -0
- package/vendors/gemini/commands/wtfp/remove-section.toml +140 -0
- package/vendors/gemini/commands/wtfp/report-bug.toml +126 -0
- package/vendors/gemini/commands/wtfp/request-feature.toml +155 -0
- package/vendors/gemini/commands/wtfp/research-gap.toml +214 -0
- package/vendors/gemini/commands/wtfp/resume-writing.toml +136 -0
- package/vendors/gemini/commands/wtfp/review-section.toml +121 -0
- package/vendors/gemini/commands/wtfp/settings.toml +191 -0
- package/vendors/gemini/commands/wtfp/submit-milestone.toml +351 -0
- package/vendors/gemini/commands/wtfp/update.toml +107 -0
- package/vendors/gemini/commands/wtfp/verify-work.toml +245 -0
- package/vendors/gemini/commands/wtfp/write-section.toml +228 -0
- package/vendors/opencode/agents/wtfp/argument-verifier.md +184 -0
- package/vendors/opencode/agents/wtfp/citation-expert.md +41 -0
- package/vendors/opencode/agents/wtfp/citation-formatter.md +38 -0
- package/vendors/opencode/agents/wtfp/coherence-checker.md +162 -0
- package/vendors/opencode/agents/wtfp/outliner.md +252 -0
- package/vendors/opencode/agents/wtfp/plan-checker.md +244 -0
- package/vendors/opencode/agents/wtfp/prose-polisher.md +158 -0
- package/vendors/opencode/agents/wtfp/research-synthesizer.md +205 -0
- package/vendors/opencode/agents/wtfp/section-planner.md +355 -0
- package/vendors/opencode/agents/wtfp/section-reviewer.md +175 -0
- package/vendors/opencode/agents/wtfp/section-writer.md +304 -0
- package/vendors/opencode/commands/wtfp/add-todo.md +103 -0
- package/vendors/opencode/commands/wtfp/analyze-bib.md +127 -0
- package/vendors/opencode/commands/wtfp/audit-milestone.md +279 -0
- package/vendors/opencode/commands/wtfp/check-refs.md +124 -0
- package/vendors/opencode/commands/wtfp/check-todos.md +146 -0
- package/vendors/opencode/commands/wtfp/checkpoint.md +97 -0
- package/vendors/opencode/commands/wtfp/contribute.md +266 -0
- package/vendors/opencode/commands/wtfp/create-outline.md +181 -0
- package/vendors/opencode/commands/wtfp/create-poster.md +37 -0
- package/vendors/opencode/commands/wtfp/create-slides.md +36 -0
- package/vendors/opencode/commands/wtfp/discuss-section.md +63 -0
- package/vendors/opencode/commands/wtfp/execute-outline.md +248 -0
- package/vendors/opencode/commands/wtfp/export-latex.md +210 -0
- package/vendors/opencode/commands/wtfp/help.md +185 -0
- package/vendors/opencode/commands/wtfp/insert-section.md +124 -0
- package/vendors/opencode/commands/wtfp/list-assumptions.md +105 -0
- package/vendors/opencode/commands/wtfp/map-project.md +100 -0
- package/vendors/opencode/commands/wtfp/new-paper.md +161 -0
- package/vendors/opencode/commands/wtfp/pause-writing.md +141 -0
- package/vendors/opencode/commands/wtfp/plan-milestone-gaps.md +250 -0
- package/vendors/opencode/commands/wtfp/plan-revision.md +174 -0
- package/vendors/opencode/commands/wtfp/plan-section.md +214 -0
- package/vendors/opencode/commands/wtfp/polish-prose.md +115 -0
- package/vendors/opencode/commands/wtfp/progress.md +146 -0
- package/vendors/opencode/commands/wtfp/quick.md +97 -0
- package/vendors/opencode/commands/wtfp/remove-section.md +141 -0
- package/vendors/opencode/commands/wtfp/report-bug.md +127 -0
- package/vendors/opencode/commands/wtfp/request-feature.md +156 -0
- package/vendors/opencode/commands/wtfp/research-gap.md +215 -0
- package/vendors/opencode/commands/wtfp/resume-writing.md +137 -0
- package/vendors/opencode/commands/wtfp/review-section.md +123 -0
- package/vendors/opencode/commands/wtfp/settings.md +192 -0
- package/vendors/opencode/commands/wtfp/submit-milestone.md +352 -0
- package/vendors/opencode/commands/wtfp/update.md +108 -0
- package/vendors/opencode/commands/wtfp/verify-work.md +246 -0
- package/vendors/opencode/commands/wtfp/write-section.md +228 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WTF-P BibTeX Formatter
|
|
5
|
+
* Standardizes BibTeX entries to the project's strict template.
|
|
6
|
+
* Handles missing fields and assigns provenance metadata.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// --- Parsing Logic ---
|
|
10
|
+
|
|
11
|
+
function parseField(text, field) {
|
|
12
|
+
// Regex to find field="value" or field={value}
|
|
13
|
+
const regex = new RegExp(`\\b${field}\\s*=\\s*[{"']([\\s\\S]*?)[}"']\\s*[,}]`, 'i');
|
|
14
|
+
const match = text.match(regex);
|
|
15
|
+
return match ? match[1].trim() : null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function parseEntryType(text) {
|
|
19
|
+
const match = text.match(/@(\w+)\s*{/);
|
|
20
|
+
return match ? match[1].toLowerCase() : 'misc';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parseKey(text) {
|
|
24
|
+
const match = text.match(/@\w+\s*{\s*([^,]+),/);
|
|
25
|
+
return match ? match[1].trim() : 'unknown_key';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parse(rawEntry) {
|
|
29
|
+
const entryType = parseEntryType(rawEntry);
|
|
30
|
+
const key = parseKey(rawEntry);
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
key,
|
|
34
|
+
entryType,
|
|
35
|
+
author: parseField(rawEntry, 'author'),
|
|
36
|
+
title: parseField(rawEntry, 'title'),
|
|
37
|
+
booktitle: parseField(rawEntry, 'booktitle') || parseField(rawEntry, 'journal'),
|
|
38
|
+
year: parseField(rawEntry, 'year'),
|
|
39
|
+
month: parseField(rawEntry, 'month'),
|
|
40
|
+
abstract: parseField(rawEntry, 'abstract'),
|
|
41
|
+
publisher: parseField(rawEntry, 'publisher'),
|
|
42
|
+
volume: parseField(rawEntry, 'volume'),
|
|
43
|
+
number: parseField(rawEntry, 'number'),
|
|
44
|
+
pages: parseField(rawEntry, 'pages'),
|
|
45
|
+
doi: parseField(rawEntry, 'doi'),
|
|
46
|
+
url: parseField(rawEntry, 'url'),
|
|
47
|
+
keywords: parseField(rawEntry, 'keywords'),
|
|
48
|
+
abbr: parseField(rawEntry, 'abbr'),
|
|
49
|
+
bibtex_show: parseField(rawEntry, 'bibtex_show'),
|
|
50
|
+
selected: parseField(rawEntry, 'selected'),
|
|
51
|
+
projects: parseField(rawEntry, 'projects')
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// --- Formatting Logic ---
|
|
56
|
+
|
|
57
|
+
function format(data, provenance = {}) {
|
|
58
|
+
// Determine Status
|
|
59
|
+
let status = provenance.wtfp_status || 'official';
|
|
60
|
+
const missingFields = [];
|
|
61
|
+
|
|
62
|
+
if (!data.doi) {
|
|
63
|
+
status = 'incomplete';
|
|
64
|
+
missingFields.push('doi');
|
|
65
|
+
}
|
|
66
|
+
if (!data.author || data.author === '{MISSING_AUTHOR}') {
|
|
67
|
+
status = 'incomplete';
|
|
68
|
+
missingFields.push('author');
|
|
69
|
+
}
|
|
70
|
+
if (!data.booktitle || data.booktitle === '{MISSING_VENUE}') {
|
|
71
|
+
missingFields.push('venue');
|
|
72
|
+
}
|
|
73
|
+
if (!data.abstract) {
|
|
74
|
+
if (status !== 'incomplete') status = 'partial';
|
|
75
|
+
missingFields.push('abstract');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Use provided provenance or defaults
|
|
79
|
+
const wtfp_source = provenance.wtfp_source || '';
|
|
80
|
+
const wtfp_citations = provenance.wtfp_citations || '';
|
|
81
|
+
const wtfp_velocity = provenance.wtfp_velocity || '';
|
|
82
|
+
const wtfp_s2_id = provenance.wtfp_s2_id || '';
|
|
83
|
+
const wtfp_scholar_id = provenance.wtfp_scholar_id || '';
|
|
84
|
+
const wtfp_fetched = provenance.wtfp_fetched || new Date().toISOString().split('T')[0];
|
|
85
|
+
|
|
86
|
+
const entryType = data.entryType === 'inproceedings' ? 'conference' : (data.entryType === 'article' ? 'journal' : data.entryType || 'misc');
|
|
87
|
+
|
|
88
|
+
// Clean fields
|
|
89
|
+
const clean = (val, fallback = "") => val || fallback;
|
|
90
|
+
|
|
91
|
+
return `@${entryType}{${data.key || 'unknown'},
|
|
92
|
+
abbr="${clean(data.abbr)}",
|
|
93
|
+
entry_type="${entryType}",
|
|
94
|
+
author="${clean(data.author, "{MISSING_AUTHOR}")}",
|
|
95
|
+
abstract="${clean(data.abstract)}",
|
|
96
|
+
booktitle="${clean(data.booktitle, "{MISSING_VENUE}")}",
|
|
97
|
+
title="${clean(data.title, "{MISSING_TITLE}")}",
|
|
98
|
+
year="${clean(data.year, "{????}")}",
|
|
99
|
+
month="${clean(data.month)}",
|
|
100
|
+
publisher="${clean(data.publisher)}",
|
|
101
|
+
volume="${clean(data.volume)}",
|
|
102
|
+
number="${clean(data.number)}",
|
|
103
|
+
pages="${clean(data.pages)}",
|
|
104
|
+
keywords="${clean(data.keywords)}",
|
|
105
|
+
doi="${clean(data.doi)}",
|
|
106
|
+
url="${clean(data.url, data.doi ? `https://doi.org/${data.doi}` : "")}",
|
|
107
|
+
html="${clean(data.html, data.doi ? `https://doi.org/${data.doi}` : "")}",
|
|
108
|
+
pdf="${clean(data.pdf, "paper.pdf")}",
|
|
109
|
+
google_scholar_id="${clean(data.google_scholar_id)}",
|
|
110
|
+
additional_info="${clean(data.additional_info)}",
|
|
111
|
+
bibtex_show="${clean(data.bibtex_show, "true")}",
|
|
112
|
+
selected="${clean(data.selected, "false")}",
|
|
113
|
+
projects="${clean(data.projects)}",
|
|
114
|
+
wtfp_status="${status}",
|
|
115
|
+
wtfp_source="${wtfp_source}",
|
|
116
|
+
wtfp_citations="${wtfp_citations}",
|
|
117
|
+
wtfp_velocity="${wtfp_velocity}",
|
|
118
|
+
wtfp_s2_id="${wtfp_s2_id}",
|
|
119
|
+
wtfp_scholar_id="${wtfp_scholar_id}",
|
|
120
|
+
wtfp_fetched="${wtfp_fetched}",
|
|
121
|
+
wtfp_missing="${missingFields.join(',')}"
|
|
122
|
+
}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// --- CLI Handling ---
|
|
126
|
+
|
|
127
|
+
if (require.main === module) {
|
|
128
|
+
const MODE = process.argv[2];
|
|
129
|
+
const INPUT = process.argv[3];
|
|
130
|
+
const KEY_ARG = process.argv[4];
|
|
131
|
+
|
|
132
|
+
if (!MODE) {
|
|
133
|
+
console.error('Usage: node bib-format.js <raw_string|--file> <content|filepath> [key]');
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let rawEntry = '';
|
|
138
|
+
|
|
139
|
+
if (MODE === '--file') {
|
|
140
|
+
if (!fs.existsSync(INPUT)) {
|
|
141
|
+
console.error(`File not found: ${INPUT}`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
const content = fs.readFileSync(INPUT, 'utf8');
|
|
145
|
+
const regex = new RegExp(`@\w+\s*{\s*${KEY_ARG}\s*,[\s\S]*?\n}`, 'm');
|
|
146
|
+
const match = content.match(regex);
|
|
147
|
+
if (match) {
|
|
148
|
+
rawEntry = match[0];
|
|
149
|
+
} else {
|
|
150
|
+
console.error(`Key ${KEY_ARG} not found in ${INPUT}`);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
rawEntry = INPUT;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const parsed = parse(rawEntry);
|
|
158
|
+
console.log(format(parsed));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = { parse, format };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WTF-P Bibliography Indexer
|
|
5
|
+
* Indexes and retrieves BibTeX entries without loading the entire file into context.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const ENTRY_REGEX = /@(\w+)\s*\{\s*([^,]+),([^@]*)\}/g;
|
|
9
|
+
|
|
10
|
+
function parseEntries(text) {
|
|
11
|
+
const entries = [];
|
|
12
|
+
let match;
|
|
13
|
+
// Reset regex index just in case
|
|
14
|
+
ENTRY_REGEX.lastIndex = 0;
|
|
15
|
+
|
|
16
|
+
while ((match = ENTRY_REGEX.exec(text)) !== null) {
|
|
17
|
+
const fullText = match[0];
|
|
18
|
+
const type = match[1];
|
|
19
|
+
const key = match[2].trim();
|
|
20
|
+
const body = match[3];
|
|
21
|
+
|
|
22
|
+
// Extract title specifically for indexing
|
|
23
|
+
const titleMatch = body.match(/title\s*=\s*[{"'](.+?)[}"']/i);
|
|
24
|
+
const title = titleMatch ? titleMatch[1] : 'Unknown Title';
|
|
25
|
+
|
|
26
|
+
// Extract year
|
|
27
|
+
const yearMatch = body.match(/year\s*=\s*[{"']?(\d+)[}"']?/i);
|
|
28
|
+
const year = yearMatch ? yearMatch[1] : '????';
|
|
29
|
+
|
|
30
|
+
entries.push({ key, type, title, year, fullText: `@${type}{${key},${body}}` });
|
|
31
|
+
}
|
|
32
|
+
return entries;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function index(content) {
|
|
36
|
+
const entries = parseEntries(content);
|
|
37
|
+
return JSON.stringify(entries.map(e => ({ key: e.key, title: e.title, year: e.year })), null, 2);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getEntry(content, key) {
|
|
41
|
+
const entries = parseEntries(content);
|
|
42
|
+
const entry = entries.find(e => e.key === key);
|
|
43
|
+
return entry ? entry.fullText : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function search(content, query) {
|
|
47
|
+
const entries = parseEntries(content);
|
|
48
|
+
const q = query.toLowerCase();
|
|
49
|
+
const results = entries.filter(e =>
|
|
50
|
+
e.title.toLowerCase().includes(q) ||
|
|
51
|
+
e.key.toLowerCase().includes(q) ||
|
|
52
|
+
e.fullText.toLowerCase().includes(q)
|
|
53
|
+
);
|
|
54
|
+
return JSON.stringify(results.map(e => ({ key: e.key, title: e.title, year: e.year })), null, 2);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// --- CLI Handling ---
|
|
58
|
+
|
|
59
|
+
if (require.main === module) {
|
|
60
|
+
const COMMAND = process.argv[2];
|
|
61
|
+
const BIB_FILE = process.argv[3];
|
|
62
|
+
const ARG = process.argv[4];
|
|
63
|
+
|
|
64
|
+
if (!COMMAND || !BIB_FILE) {
|
|
65
|
+
console.error('Usage: node bib-index.js <command> <bib_file> [arg]');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!fs.existsSync(BIB_FILE)) {
|
|
70
|
+
console.error(`Error: Bibliography file not found: ${BIB_FILE}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const content = fs.readFileSync(BIB_FILE, 'utf8');
|
|
75
|
+
|
|
76
|
+
if (COMMAND === 'index') {
|
|
77
|
+
console.log(index(content));
|
|
78
|
+
}
|
|
79
|
+
else if (COMMAND === 'get') {
|
|
80
|
+
if (!ARG) {
|
|
81
|
+
console.error('Error: Missing citation key');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
const result = getEntry(content, ARG);
|
|
85
|
+
if (result) console.log(result);
|
|
86
|
+
else {
|
|
87
|
+
console.error(`Error: Entry '${ARG}' not found.`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else if (COMMAND === 'search') {
|
|
92
|
+
if (!ARG) {
|
|
93
|
+
console.error('Error: Missing search query');
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
console.log(search(content, ARG));
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
console.error(`Unknown command: ${COMMAND}`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = { index, getEntry, search, parseEntries };
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpoint - Save and restore paper state bundles
|
|
3
|
+
*
|
|
4
|
+
* Captures complete .planning/ state as a git-tagged checkpoint
|
|
5
|
+
* that can be restored in a new session for full context recovery.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create a checkpoint of current paper state
|
|
14
|
+
* @param {Object} options
|
|
15
|
+
* @param {string} options.planningDir - Path to .planning/
|
|
16
|
+
* @param {string} options.label - Checkpoint label (e.g., "pre-discussion", "draft-1")
|
|
17
|
+
* @param {string} [options.note] - Optional note about checkpoint context
|
|
18
|
+
* @returns {Object} - { tag, files, timestamp }
|
|
19
|
+
*/
|
|
20
|
+
function createCheckpoint({ planningDir, label, note }) {
|
|
21
|
+
if (!fs.existsSync(planningDir)) {
|
|
22
|
+
throw new Error('.planning/ directory not found');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
26
|
+
const tag = `wtfp-checkpoint/${label}-${timestamp}`;
|
|
27
|
+
|
|
28
|
+
// Collect all state files
|
|
29
|
+
const files = collectFiles(planningDir);
|
|
30
|
+
|
|
31
|
+
// Stage all planning files
|
|
32
|
+
try {
|
|
33
|
+
execSync(`git add ${planningDir}`, { stdio: 'pipe' });
|
|
34
|
+
} catch (e) {
|
|
35
|
+
// May fail if no changes — that's OK
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check if there are staged changes
|
|
39
|
+
const status = execSync('git status --porcelain', { encoding: 'utf8' });
|
|
40
|
+
const hasChanges = status.trim().length > 0;
|
|
41
|
+
|
|
42
|
+
if (hasChanges) {
|
|
43
|
+
const commitMsg = `checkpoint: ${label}${note ? `\n\n${note}` : ''}`;
|
|
44
|
+
execSync(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`, { stdio: 'pipe' });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Create annotated tag
|
|
48
|
+
const tagMsg = [
|
|
49
|
+
`WTF-P Checkpoint: ${label}`,
|
|
50
|
+
`Timestamp: ${timestamp}`,
|
|
51
|
+
`Files: ${files.length}`,
|
|
52
|
+
note ? `Note: ${note}` : '',
|
|
53
|
+
].filter(Boolean).join('\n');
|
|
54
|
+
|
|
55
|
+
execSync(`git tag -a "${tag}" -m "${tagMsg.replace(/"/g, '\\"')}"`, { stdio: 'pipe' });
|
|
56
|
+
|
|
57
|
+
return { tag, files: files.length, timestamp };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Restore a checkpoint by tag name
|
|
62
|
+
* @param {string} tag - Git tag to restore from
|
|
63
|
+
* @returns {Object} - { restored, files }
|
|
64
|
+
*/
|
|
65
|
+
function restoreCheckpoint(tag) {
|
|
66
|
+
// Verify tag exists
|
|
67
|
+
try {
|
|
68
|
+
execSync(`git tag -l "${tag}"`, { encoding: 'utf8', stdio: 'pipe' });
|
|
69
|
+
} catch (e) {
|
|
70
|
+
throw new Error(`Checkpoint tag not found: ${tag}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Restore .planning/ from the tagged commit
|
|
74
|
+
try {
|
|
75
|
+
execSync(`git checkout "${tag}" -- .planning/`, { stdio: 'pipe' });
|
|
76
|
+
return { restored: true, tag };
|
|
77
|
+
} catch (e) {
|
|
78
|
+
throw new Error(`Failed to restore checkpoint: ${e.message}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* List available checkpoints
|
|
84
|
+
* @returns {Array} - List of { tag, date, label, message }
|
|
85
|
+
*/
|
|
86
|
+
function listCheckpoints() {
|
|
87
|
+
try {
|
|
88
|
+
const tags = execSync('git tag -l "wtfp-checkpoint/*" --sort=-creatordate', {
|
|
89
|
+
encoding: 'utf8',
|
|
90
|
+
stdio: 'pipe',
|
|
91
|
+
}).trim();
|
|
92
|
+
|
|
93
|
+
if (!tags) return [];
|
|
94
|
+
|
|
95
|
+
return tags.split('\n').map(tag => {
|
|
96
|
+
let message = '';
|
|
97
|
+
try {
|
|
98
|
+
message = execSync(`git tag -l "${tag}" -n1`, {
|
|
99
|
+
encoding: 'utf8',
|
|
100
|
+
stdio: 'pipe',
|
|
101
|
+
}).trim();
|
|
102
|
+
} catch (e) { /* ignore */ }
|
|
103
|
+
|
|
104
|
+
const parts = tag.replace('wtfp-checkpoint/', '').split('-');
|
|
105
|
+
const label = parts.slice(0, -6).join('-'); // Remove timestamp portion
|
|
106
|
+
|
|
107
|
+
return { tag, label, message };
|
|
108
|
+
});
|
|
109
|
+
} catch (e) {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Generate a handoff document for session continuity
|
|
116
|
+
* @param {string} planningDir - Path to .planning/
|
|
117
|
+
* @returns {string} - Markdown content for HANDOFF.md
|
|
118
|
+
*/
|
|
119
|
+
function generateHandoff(planningDir) {
|
|
120
|
+
const statePath = path.join(planningDir, 'STATE.md');
|
|
121
|
+
const configPath = path.join(planningDir, 'config.json');
|
|
122
|
+
|
|
123
|
+
const state = fs.existsSync(statePath) ? fs.readFileSync(statePath, 'utf8') : '';
|
|
124
|
+
let config = {};
|
|
125
|
+
try {
|
|
126
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
127
|
+
} catch (e) { /* ignore */ }
|
|
128
|
+
|
|
129
|
+
// Extract position from STATE.md
|
|
130
|
+
const positionMatch = state.match(/Section:\s*(\d+)\s*of\s*(\d+)/i);
|
|
131
|
+
const wordsMatch = state.match(/Words?:\s*(\d+)\s*\/\s*(\d+)/i);
|
|
132
|
+
|
|
133
|
+
const sections = [];
|
|
134
|
+
sections.push('# Session Handoff');
|
|
135
|
+
sections.push(`\nGenerated: ${new Date().toISOString()}`);
|
|
136
|
+
sections.push(`\n## Position`);
|
|
137
|
+
|
|
138
|
+
if (positionMatch) {
|
|
139
|
+
sections.push(`- Section: ${positionMatch[1]} of ${positionMatch[2]}`);
|
|
140
|
+
}
|
|
141
|
+
if (wordsMatch) {
|
|
142
|
+
sections.push(`- Words: ${wordsMatch[1]} / ${wordsMatch[2]}`);
|
|
143
|
+
}
|
|
144
|
+
if (config.model_profile) {
|
|
145
|
+
sections.push(`- Profile: ${config.model_profile}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
sections.push(`\n## Resume With`);
|
|
149
|
+
sections.push('```');
|
|
150
|
+
sections.push('/wtfp:resume-writing');
|
|
151
|
+
sections.push('```');
|
|
152
|
+
|
|
153
|
+
return sections.join('\n');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// --- Helpers ---
|
|
157
|
+
|
|
158
|
+
function collectFiles(dir) {
|
|
159
|
+
const files = [];
|
|
160
|
+
|
|
161
|
+
function walk(d) {
|
|
162
|
+
if (!fs.existsSync(d)) return;
|
|
163
|
+
const entries = fs.readdirSync(d, { withFileTypes: true });
|
|
164
|
+
for (const entry of entries) {
|
|
165
|
+
const full = path.join(d, entry.name);
|
|
166
|
+
if (entry.isDirectory()) {
|
|
167
|
+
walk(full);
|
|
168
|
+
} else {
|
|
169
|
+
files.push(full);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
walk(dir);
|
|
175
|
+
return files;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = {
|
|
179
|
+
createCheckpoint,
|
|
180
|
+
restoreCheckpoint,
|
|
181
|
+
listCheckpoints,
|
|
182
|
+
generateHandoff,
|
|
183
|
+
};
|