wtf-p 0.2.0 → 0.3.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.
Files changed (110) hide show
  1. package/README.md +6 -2
  2. package/bin/commands/doctor.js +100 -0
  3. package/bin/commands/install-logic.js +235 -0
  4. package/bin/commands/list.js +88 -0
  5. package/bin/commands/status.js +85 -0
  6. package/bin/commands/update.js +50 -0
  7. package/bin/install.js +78 -629
  8. package/bin/lib/manifest.js +41 -0
  9. package/bin/lib/utils.js +37 -17
  10. package/bin/lib/wcn-compiler.js +226 -0
  11. package/bin/uninstall.js +51 -17
  12. package/core/write-the-f-paper/templates/posters/basic.md +34 -0
  13. package/core/write-the-f-paper/templates/slides/basic.md +31 -0
  14. package/package.json +12 -4
  15. package/vendors/claude/.claude-plugin/plugin.json +12 -0
  16. package/vendors/claude/commands/wtfp/create-poster.md +42 -0
  17. package/vendors/claude/commands/wtfp/create-slides.md +41 -0
  18. package/{commands → vendors/claude/commands}/wtfp/help.md +3 -0
  19. package/vendors/claude/skills/wtfp/echarts/SKILL.md +47 -0
  20. package/vendors/claude/skills/wtfp/marp/SKILL.md +49 -0
  21. /package/{write-the-f-paper → core/write-the-f-paper}/references/checkpoints.md +0 -0
  22. /package/{write-the-f-paper → core/write-the-f-paper}/references/continuation-format.md +0 -0
  23. /package/{write-the-f-paper → core/write-the-f-paper}/references/git-integration.md +0 -0
  24. /package/{write-the-f-paper → core/write-the-f-paper}/references/github-contrib.md +0 -0
  25. /package/{write-the-f-paper → core/write-the-f-paper}/references/imrad-structure.md +0 -0
  26. /package/{write-the-f-paper → core/write-the-f-paper}/references/length-estimation.md +0 -0
  27. /package/{write-the-f-paper → core/write-the-f-paper}/references/plan-format.md +0 -0
  28. /package/{write-the-f-paper → core/write-the-f-paper}/references/principles.md +0 -0
  29. /package/{write-the-f-paper → core/write-the-f-paper}/references/questioning.md +0 -0
  30. /package/{write-the-f-paper → core/write-the-f-paper}/references/research-pitfalls.md +0 -0
  31. /package/{write-the-f-paper → core/write-the-f-paper}/templates/config.json +0 -0
  32. /package/{write-the-f-paper → core/write-the-f-paper}/templates/context.md +0 -0
  33. /package/{write-the-f-paper → core/write-the-f-paper}/templates/continue-here.md +0 -0
  34. /package/{write-the-f-paper → core/write-the-f-paper}/templates/data-inventory.md +0 -0
  35. /package/{write-the-f-paper → core/write-the-f-paper}/templates/discovery.md +0 -0
  36. /package/{write-the-f-paper → core/write-the-f-paper}/templates/issues.md +0 -0
  37. /package/{write-the-f-paper → core/write-the-f-paper}/templates/manuscript.md +0 -0
  38. /package/{write-the-f-paper → core/write-the-f-paper}/templates/narrative-arc.md +0 -0
  39. /package/{write-the-f-paper → core/write-the-f-paper}/templates/outline.md +0 -0
  40. /package/{write-the-f-paper → core/write-the-f-paper}/templates/phase-prompt.md +0 -0
  41. /package/{write-the-f-paper → core/write-the-f-paper}/templates/prior-drafts.md +0 -0
  42. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/argument-structure.md +0 -0
  43. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/bibliography.md +0 -0
  44. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/gaps.md +0 -0
  45. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/imrad.md +0 -0
  46. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/peer-review.md +0 -0
  47. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/style-guide.md +0 -0
  48. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/tools.md +0 -0
  49. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project.md +0 -0
  50. /package/{write-the-f-paper → core/write-the-f-paper}/templates/research.md +0 -0
  51. /package/{write-the-f-paper → core/write-the-f-paper}/templates/review-comments.md +0 -0
  52. /package/{write-the-f-paper → core/write-the-f-paper}/templates/revision-issues.md +0 -0
  53. /package/{write-the-f-paper → core/write-the-f-paper}/templates/roadmap.md +0 -0
  54. /package/{write-the-f-paper → core/write-the-f-paper}/templates/section-archive.md +0 -0
  55. /package/{write-the-f-paper → core/write-the-f-paper}/templates/section-context.md +0 -0
  56. /package/{write-the-f-paper → core/write-the-f-paper}/templates/section.md +0 -0
  57. /package/{write-the-f-paper → core/write-the-f-paper}/templates/state.md +0 -0
  58. /package/{write-the-f-paper → core/write-the-f-paper}/templates/summary.md +0 -0
  59. /package/{write-the-f-paper → core/write-the-f-paper}/venues/acm-cs.yaml +0 -0
  60. /package/{write-the-f-paper → core/write-the-f-paper}/venues/arxiv-ml.yaml +0 -0
  61. /package/{write-the-f-paper → core/write-the-f-paper}/venues/ieee-cs.yaml +0 -0
  62. /package/{write-the-f-paper → core/write-the-f-paper}/venues/nature.yaml +0 -0
  63. /package/{write-the-f-paper → core/write-the-f-paper}/venues/thesis-chapter.yaml +0 -0
  64. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/create-outline.md +0 -0
  65. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/create-outline.wcn.md +0 -0
  66. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/discuss-section.md +0 -0
  67. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/discuss-section.wcn.md +0 -0
  68. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/execute-section.md +0 -0
  69. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/execute-section.wcn.md +0 -0
  70. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/list-assumptions.md +0 -0
  71. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/list-assumptions.wcn.md +0 -0
  72. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/lit-review-phase.md +0 -0
  73. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/lit-review-phase.wcn.md +0 -0
  74. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/map-project.md +0 -0
  75. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/map-project.wcn.md +0 -0
  76. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/plan-section.md +0 -0
  77. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/plan-section.wcn.md +0 -0
  78. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/research-gap.md +0 -0
  79. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/research-gap.wcn.md +0 -0
  80. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/resume-paper.md +0 -0
  81. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/resume-paper.wcn.md +0 -0
  82. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/review-section.md +0 -0
  83. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/review-section.wcn.md +0 -0
  84. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/submit-draft.md +0 -0
  85. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/submit-draft.wcn.md +0 -0
  86. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/transition.md +0 -0
  87. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/transition.wcn.md +0 -0
  88. /package/{commands → vendors/claude/commands}/wtfp/analyze-bib.md +0 -0
  89. /package/{commands → vendors/claude/commands}/wtfp/check-refs.md +0 -0
  90. /package/{commands → vendors/claude/commands}/wtfp/contribute.md +0 -0
  91. /package/{commands → vendors/claude/commands}/wtfp/create-outline.md +0 -0
  92. /package/{commands → vendors/claude/commands}/wtfp/discuss-section.md +0 -0
  93. /package/{commands → vendors/claude/commands}/wtfp/export-latex.md +0 -0
  94. /package/{commands → vendors/claude/commands}/wtfp/insert-section.md +0 -0
  95. /package/{commands → vendors/claude/commands}/wtfp/list-assumptions.md +0 -0
  96. /package/{commands → vendors/claude/commands}/wtfp/map-project.md +0 -0
  97. /package/{commands → vendors/claude/commands}/wtfp/new-paper.md +0 -0
  98. /package/{commands → vendors/claude/commands}/wtfp/pause-writing.md +0 -0
  99. /package/{commands → vendors/claude/commands}/wtfp/plan-revision.md +0 -0
  100. /package/{commands → vendors/claude/commands}/wtfp/plan-section.md +0 -0
  101. /package/{commands → vendors/claude/commands}/wtfp/polish-prose.md +0 -0
  102. /package/{commands → vendors/claude/commands}/wtfp/progress.md +0 -0
  103. /package/{commands → vendors/claude/commands}/wtfp/remove-section.md +0 -0
  104. /package/{commands → vendors/claude/commands}/wtfp/report-bug.md +0 -0
  105. /package/{commands → vendors/claude/commands}/wtfp/request-feature.md +0 -0
  106. /package/{commands → vendors/claude/commands}/wtfp/research-gap.md +0 -0
  107. /package/{commands → vendors/claude/commands}/wtfp/resume-writing.md +0 -0
  108. /package/{commands → vendors/claude/commands}/wtfp/review-section.md +0 -0
  109. /package/{commands → vendors/claude/commands}/wtfp/submit-milestone.md +0 -0
  110. /package/{commands → vendors/claude/commands}/wtfp/write-section.md +0 -0
@@ -0,0 +1,41 @@
1
+ const path = require('path');
2
+
3
+ const ROOT = path.resolve(__dirname, '../..');
4
+
5
+ const MANIFEST = {
6
+ claude: {
7
+ name: 'Claude Code',
8
+ configDirEnv: 'CLAUDE_CONFIG_DIR',
9
+ defaultDir: '.claude',
10
+ components: [
11
+ {
12
+ id: 'workflows',
13
+ src: path.join(ROOT, 'core', 'write-the-f-paper'),
14
+ dest: 'write-the-f-paper',
15
+ type: 'dir'
16
+ },
17
+ {
18
+ id: 'commands',
19
+ src: path.join(ROOT, 'vendors', 'claude', 'commands', 'wtfp'),
20
+ dest: 'commands/wtfp',
21
+ type: 'dir'
22
+ },
23
+ {
24
+ id: 'skills',
25
+ src: path.join(ROOT, 'vendors', 'claude', 'skills', 'wtfp'),
26
+ dest: 'skills/wtfp',
27
+ type: 'dir'
28
+ },
29
+ {
30
+ id: 'plugin',
31
+ src: path.join(ROOT, 'vendors', 'claude', '.claude-plugin'),
32
+ dest: '.claude-plugin',
33
+ type: 'dir'
34
+ }
35
+ ]
36
+ },
37
+ // Future:
38
+ // gemini: { ... }
39
+ };
40
+
41
+ module.exports = MANIFEST;
package/bin/lib/utils.js CHANGED
@@ -62,17 +62,25 @@ function isValidPath(inputPath) {
62
62
  }
63
63
 
64
64
  /**
65
- * Get the Claude config directory (respects CLAUDE_CONFIG_DIR env var)
65
+ * Get the configuration directory for a specific vendor
66
66
  */
67
- function getClaudeDir(explicitConfigDir, isGlobal = true) {
67
+ function getVendorDir(vendor, explicitConfigDir, isGlobal = true) {
68
68
  if (!isGlobal) {
69
- return path.join(process.cwd(), '.claude');
69
+ return path.join(process.cwd(), `.${vendor}`);
70
70
  }
71
71
 
72
+ const envVar = `${vendor.toUpperCase()}_CONFIG_DIR`;
72
73
  const configDir = normalizePath(explicitConfigDir) ||
73
- normalizePath(process.env.CLAUDE_CONFIG_DIR);
74
+ normalizePath(process.env[envVar]);
74
75
 
75
- return configDir || path.join(os.homedir(), '.claude');
76
+ return configDir || path.join(os.homedir(), `.${vendor}`);
77
+ }
78
+
79
+ /**
80
+ * Get the Claude config directory (respects CLAUDE_CONFIG_DIR env var)
81
+ */
82
+ function getClaudeDir(explicitConfigDir, isGlobal = true) {
83
+ return getVendorDir('claude', explicitConfigDir, isGlobal);
76
84
  }
77
85
 
78
86
  /**
@@ -109,6 +117,9 @@ function readInstalledVersion(claudeDir) {
109
117
  * Write version tracking file
110
118
  */
111
119
  function writeVersionFile(claudeDir, version, installedFiles) {
120
+ if (!fs.existsSync(claudeDir)) {
121
+ fs.mkdirSync(claudeDir, { recursive: true });
122
+ }
112
123
  const versionFile = path.join(claudeDir, VERSION_FILE);
113
124
  const data = {
114
125
  version,
@@ -144,47 +155,56 @@ function simpleChecksum(filePath) {
144
155
  /**
145
156
  * Detect installation state
146
157
  */
147
- function detectInstallation(claudeDir) {
158
+ function detectInstallation(vendorDir) {
148
159
  const result = {
149
160
  hasCommands: false,
150
- hasSkill: false,
161
+ hasWorkflows: false,
162
+ hasSkills: false,
151
163
  version: null,
152
164
  partial: false,
153
165
  corrupt: false,
154
166
  commandFiles: [],
167
+ workflowFiles: [],
155
168
  skillFiles: []
156
169
  };
157
170
 
158
- const commandsDir = path.join(claudeDir, 'commands', 'wtfp');
159
- const skillDir = path.join(claudeDir, 'write-the-f-paper');
171
+ const commandsDir = path.join(vendorDir, 'commands', 'wtfp');
172
+ const workflowsDir = path.join(vendorDir, 'write-the-f-paper');
173
+ const skillsDir = path.join(vendorDir, 'skills', 'wtfp');
160
174
 
161
175
  result.hasCommands = fs.existsSync(commandsDir);
162
- result.hasSkill = fs.existsSync(skillDir);
176
+ result.hasWorkflows = fs.existsSync(workflowsDir);
177
+ result.hasSkills = fs.existsSync(skillsDir);
163
178
 
164
179
  if (result.hasCommands) {
165
180
  result.commandFiles = collectFiles(commandsDir);
166
181
  }
167
- if (result.hasSkill) {
168
- result.skillFiles = collectFiles(skillDir);
182
+ if (result.hasWorkflows) {
183
+ result.workflowFiles = collectFiles(workflowsDir);
184
+ }
185
+ if (result.hasSkills) {
186
+ result.skillFiles = collectFiles(skillsDir);
169
187
  }
170
188
 
171
189
  // Read version info
172
- const versionData = readInstalledVersion(claudeDir);
190
+ const versionData = readInstalledVersion(vendorDir);
173
191
  if (versionData) {
174
192
  result.version = versionData.version;
175
193
  result.corrupt = versionData.corrupt || false;
176
194
 
177
- // Check for partial install (version file exists but missing dirs)
195
+ // Check for partial install
178
196
  if (!versionData.corrupt) {
179
197
  const expectedHasCommands = versionData.manifest?.some(f => f.path.includes('commands/wtfp'));
180
- const expectedHasSkill = versionData.manifest?.some(f => f.path.includes('write-the-f-paper'));
198
+ const expectedHasWorkflows = versionData.manifest?.some(f => f.path.includes('write-the-f-paper'));
199
+ const expectedHasSkills = versionData.manifest?.some(f => f.path.includes('skills/wtfp'));
181
200
 
182
201
  if ((expectedHasCommands && !result.hasCommands) ||
183
- (expectedHasSkill && !result.hasSkill)) {
202
+ (expectedHasWorkflows && !result.hasWorkflows) ||
203
+ (expectedHasSkills && !result.hasSkills)) {
184
204
  result.partial = true;
185
205
  }
186
206
  }
187
- } else if (result.hasCommands || result.hasSkill) {
207
+ } else if (result.hasCommands || result.hasWorkflows || result.hasSkills) {
188
208
  // Files exist but no version file - legacy install
189
209
  result.version = 'legacy';
190
210
  }
@@ -0,0 +1,226 @@
1
+ /**
2
+ * WCN Compiler (Workflow Compression Notation)
3
+ * A robust, state-machine based compiler for converting Verbose Markdown to Compressed WCN.
4
+ */
5
+
6
+ const FLUFF_PATTERNS = [
7
+ /This ensures[^.]*\./g,
8
+ /This is important[^.]*\./g,
9
+ /Note that[^.]*\./g,
10
+ /Keep in mind[^.]*\./g,
11
+ /Remember that[^.]*\./g,
12
+ /As mentioned[^.]*\./g,
13
+ /It is important to[^.]*\./g,
14
+ ];
15
+
16
+ class WCNCompiler {
17
+ constructor() {
18
+ this.lines = [];
19
+ this.output = [];
20
+ this.state = 'ROOT'; // ROOT, STEP, BASH
21
+ this.context = {};
22
+ }
23
+
24
+ compile(markdown) {
25
+ // 1. Pre-process: Remove fluff and normalize
26
+ let cleanMd = markdown;
27
+ FLUFF_PATTERNS.forEach(p => cleanMd = cleanMd.replace(p, ''));
28
+
29
+ this.lines = cleanMd.split('\n');
30
+ this.output = [];
31
+ this.state = 'ROOT';
32
+
33
+ // 2. Parse Line by Line
34
+ for (let i = 0; i < this.lines.length; i++) {
35
+ const line = this.lines[i].trim();
36
+ this.processLine(line, i);
37
+ }
38
+
39
+ // 3. Post-process: Clean up excessive newlines
40
+ return this.output.join('\n').replace(/\n{3,}/g, '\n\n').trim();
41
+ }
42
+
43
+ processLine(line, index) {
44
+ // Handle State Transitions
45
+ if (this.state === 'ROOT') {
46
+ if (line.startsWith('<step')) {
47
+ this.enterStep(line);
48
+ } else if (line.startsWith('<context>')) {
49
+ this.enterContext();
50
+ } else if (line.startsWith('**If') && line.endsWith(':**')) {
51
+ // Handle standalone conditionals (outside steps, rare but possible)
52
+ this.parseConditional(line, index);
53
+ } else if (line.startsWith('**Route')) {
54
+ this.parseRoute(line, index);
55
+ } else if (!line.startsWith('</')) {
56
+ // Pass through other content, but minimal
57
+ // We generally skip prose outside steps in WCN unless it's structural
58
+ if (line.startsWith('#') || line.startsWith('---')) {
59
+ this.output.push(line);
60
+ }
61
+ }
62
+ } else if (this.state === 'STEP') {
63
+ if (line.startsWith('</step>')) {
64
+ this.exitStep();
65
+ } else if (line.startsWith('```bash')) {
66
+ this.state = 'BASH';
67
+ } else if (line.startsWith('**If') && line.endsWith(':**')) {
68
+ this.parseConditional(line, index);
69
+ } else if (line.toLowerCase().startsWith('parse and internalize:')) {
70
+ this.parseParseBlock(index);
71
+ } else if (line.trim().startsWith('- ') || line.trim().startsWith('* ')) {
72
+ // Capture bullet points as instructions
73
+ this.output.push(line.trim());
74
+ } else if (line.trim().startsWith('**') && !line.includes('If')) {
75
+ // Capture bold instructions
76
+ this.output.push(line.trim());
77
+ }
78
+ } else if (this.state === 'BASH') {
79
+ if (line.startsWith('```')) {
80
+ this.state = 'STEP';
81
+ } else {
82
+ // Capture bash command (first line usually sufficient for WCN)
83
+ if (line && !line.startsWith('#')) {
84
+ this.output.push(`RUN: ${line}`);
85
+ // Skip rest of bash block for WCN brevity?
86
+ // Current logic captures all commands. Let's capture all.
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ enterStep(line) {
93
+ this.state = 'STEP';
94
+ // Parse attributes: name="foo" priority="bar"
95
+ const nameMatch = line.match(/name="([^"]+)"/);
96
+ const priorityMatch = line.match(/priority="([^"]+)"/);
97
+
98
+ const name = nameMatch ? nameMatch[1] : 'unknown';
99
+ const pAttr = priorityMatch ? ` p=${priorityMatch[1] === 'first' ? '1' : priorityMatch[1]}` : '';
100
+
101
+ this.output.push(`[step:${name}${pAttr}]`);
102
+ }
103
+
104
+ exitStep() {
105
+ this.output.push('[/step]');
106
+ this.state = 'ROOT';
107
+ }
108
+
109
+ enterContext() {
110
+ // Context is usually multiline, handled by looking ahead or buffering
111
+ // For simplicity, we'll scan ahead here since we have full lines access
112
+ // But sticking to line-by-line:
113
+ // Actually, Regex is better for Context blocks as they are simple <context>...</context>
114
+ // Let's defer this to a specialized Regex pass *before* line processing?
115
+ // Or just look ahead now.
116
+ // Let's skip for now and rely on a pre-pass replacement for Context, similar to current tool.
117
+ // Why? Because Context is global metadata, usually at top.
118
+ }
119
+
120
+ parseConditional(line, index) {
121
+ // **If mode="yolo":**
122
+ const condition = line.replace(/\*\*If\s+/, '').replace(/:\*\*/, '').trim().replace(/["']/g, '');
123
+
124
+ // Look ahead for the action
125
+ // Usually the next line or the line after
126
+ let action = '';
127
+ for (let i = 1; i < 5; i++) {
128
+ const next = this.lines[index + i];
129
+ if (next && next.trim()) {
130
+ action = next.trim();
131
+ break;
132
+ }
133
+ }
134
+
135
+ if (action) {
136
+ // Simplify action text
137
+ if (action.length > 50) action = action.substring(0, 47) + '...';
138
+ this.output.push(`IF ${condition} → ${action}`);
139
+ }
140
+ }
141
+
142
+ parseParseBlock(index) {
143
+ // Parse and internalize:
144
+ // - Item 1
145
+ // - Item 2
146
+ const fields = [];
147
+ for (let i = 1; i < 10; i++) {
148
+ const next = this.lines[index + i];
149
+ if (!next || (!next.trim().startsWith('-') && !next.trim().startsWith('*'))) break;
150
+
151
+ const field = next.replace(/^[-*]\s+/, '').trim().split(' ')[0].toLowerCase().replace(/\s+/g, '_');
152
+ fields.push(field);
153
+ }
154
+
155
+ if (fields.length > 0) {
156
+ this.output.push(`PARSE: ${fields.join(', ')}`);
157
+ }
158
+ }
159
+
160
+ parseRoute(line, index) {
161
+ // **Route A: Title**
162
+ const match = line.match(/\*\*Route\s+([A-Z]):\s*([^*]+)\*\*/);
163
+ if (!match) return;
164
+
165
+ // We need to buffer the route table
166
+ // This is hard to do line-by-line without buffering.
167
+ // Let's assume we handle Routes via Regex pre-pass like the original,
168
+ // OR we detect the start of a route block and switch state.
169
+ // Given the complexity, let's stick to the Regex for Routes/Modes/Context
170
+ // and use State Machine for the detailed STEP logic which is where the fragility usually is.
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Hybrid Compiler:
176
+ * 1. Uses Regex for high-level blocks (Context, Routes, Modes)
177
+ * 2. Uses State Machine for Steps (the most complex part)
178
+ */
179
+ function compile(markdown) {
180
+ let result = markdown;
181
+
182
+ // 1. Remove fluff
183
+ FLUFF_PATTERNS.forEach(p => result = result.replace(p, ''));
184
+
185
+ // 2. Compress Context (Regex is fine here)
186
+ result = result.replace(/<context>\s*([\s\S]*?)<\/context>/g, (match, content) => {
187
+ const files = content.match(/@[^\s\n]+/g) || [];
188
+ if (files.length === 0) return '';
189
+ const simplified = files.map(f => f.replace('@.planning/', ''));
190
+ return `@context{${simplified.join(',')}}`;
191
+ });
192
+
193
+ // 3. Compress Routes (Regex is fine here)
194
+ const routes = [];
195
+ result = result.replace(/\*\*Route\s+([A-Z]):\s*([^*]+)\*\*\s*([\s\S]*?)(?=\*\*Route|<step|$)/g, (match, id, title, content) => {
196
+ // Extract next step logic
197
+ const nextMatch = content.match(/\`\/wtfp:([^`]+)\`/);
198
+ const next = nextMatch ? `/wtfp:${nextMatch[1]}` : 'continue';
199
+ routes.push(` ${title.trim().toLowerCase().replace(/\s+/g, '_')} → "${title.trim()}" → ${next}`);
200
+ return '';
201
+ });
202
+ if (routes.length > 0) {
203
+ result = result + '\n\nROUTE{condition → output → next}:\n' + routes.join('\n');
204
+ }
205
+
206
+ // 4. Compress Modes (Regex is fine here)
207
+ const modes = [];
208
+ result = result.replace(/\*\*If\s+mode="([^"]+)":\*\*\s*\n((?:[-*]\s+[^\n]+\n?)+)/g, (match, mode, items) => {
209
+ const itemList = items.split('\n').filter(l => l.trim()).map(l => l.replace(/^[-*]\s+/, '').trim());
210
+ modes.push(` ${mode} | ${itemList[0] || ''}`);
211
+ return '';
212
+ });
213
+ if (modes.length > 0) {
214
+ result = 'MODE{name,action}:\n' + modes.join('\n') + '\n\n' + result;
215
+ }
216
+
217
+ // 5. Compress Steps using State Machine (The Core Logic)
218
+ const compiler = new WCNCompiler();
219
+ // We only run the compiler on the text that remains (which should be mostly steps)
220
+ // But wait, the compiler expects full text to find steps.
221
+ // Let's pass the text to the compiler, but the compiler only outputs Step blocks and structural elements.
222
+
223
+ return compiler.compile(result);
224
+ }
225
+
226
+ module.exports = { compile, WCNCompiler };
package/bin/uninstall.js CHANGED
@@ -102,7 +102,9 @@ if (hasHelp) {
102
102
 
103
103
  ${c.yellow('What gets removed:')}
104
104
  ${c.dim('commands/wtfp/')} - WTF-P slash commands
105
- ${c.dim('write-the-f-paper/')} - WTF-P skill and workflows
105
+ ${c.dim('write-the-f-paper/')} - WTF-P workflows
106
+ ${c.dim('skills/wtfp/')} - WTF-P skills
107
+ ${c.dim('.claude-plugin/')} - Plugin manifest
106
108
  ${c.dim('.wtfp-version')} - Version tracking file
107
109
 
108
110
  ${c.yellow('What stays intact:')}
@@ -200,9 +202,9 @@ async function uninstall(isGlobal) {
200
202
 
201
203
  // Detect installation
202
204
  const installed = detectInstallation(claudeDir);
203
- const totalFiles = installed.commandFiles.length + installed.skillFiles.length;
205
+ const totalFiles = installed.commandFiles.length + installed.workflowFiles.length + installed.skillFiles.length;
204
206
 
205
- if (!installed.hasCommands && !installed.hasSkill) {
207
+ if (!installed.hasCommands && !installed.hasWorkflows && !installed.hasSkills) {
206
208
  out.log(` ${c.yellow('No WTF-P installation found in ' + locationLabel)}\n`);
207
209
 
208
210
  // Check for backups if requested
@@ -252,13 +254,21 @@ async function uninstall(isGlobal) {
252
254
  out.log(` ${c.yellow('Found WTF-P installation:')}\n`);
253
255
 
254
256
  const commandsDir = path.join(claudeDir, 'commands', 'wtfp');
255
- const skillDir = path.join(claudeDir, 'write-the-f-paper');
257
+ const workflowsDir = path.join(claudeDir, 'write-the-f-paper');
258
+ const skillsDir = path.join(claudeDir, 'skills', 'wtfp');
259
+ const pluginDir = path.join(claudeDir, '.claude-plugin');
256
260
 
257
261
  if (installed.hasCommands) {
258
262
  out.log(` ${c.cyan('commands/wtfp/')} (${installed.commandFiles.length} files)`);
259
263
  }
260
- if (installed.hasSkill) {
261
- out.log(` ${c.cyan('write-the-f-paper/')} (${installed.skillFiles.length} files)`);
264
+ if (installed.hasWorkflows) {
265
+ out.log(` ${c.cyan('write-the-f-paper/')} (${installed.workflowFiles.length} files)`);
266
+ }
267
+ if (installed.hasSkills) {
268
+ out.log(` ${c.cyan('skills/wtfp/')} (${installed.skillFiles.length} files)`);
269
+ }
270
+ if (fs.existsSync(pluginDir)) {
271
+ out.log(` ${c.cyan('.claude-plugin/')} (manifest)`);
262
272
  }
263
273
  if (installed.version) {
264
274
  out.log(` ${c.cyan(VERSION_FILE)} (version tracking)`);
@@ -304,12 +314,16 @@ async function uninstall(isGlobal) {
304
314
  fs.mkdirSync(backupDir, { recursive: true });
305
315
 
306
316
  if (installed.hasCommands) {
307
- const dest = path.join(backupDir, 'commands', 'wtfp');
308
- copyDir(commandsDir, dest);
317
+ copyDir(commandsDir, path.join(backupDir, 'commands', 'wtfp'));
318
+ }
319
+ if (installed.hasWorkflows) {
320
+ copyDir(workflowsDir, path.join(backupDir, 'write-the-f-paper'));
309
321
  }
310
- if (installed.hasSkill) {
311
- const dest = path.join(backupDir, 'write-the-f-paper');
312
- copyDir(skillDir, dest);
322
+ if (installed.hasSkills) {
323
+ copyDir(skillsDir, path.join(backupDir, 'skills', 'wtfp'));
324
+ }
325
+ if (fs.existsSync(pluginDir)) {
326
+ copyDir(pluginDir, path.join(backupDir, '.claude-plugin'));
313
327
  }
314
328
 
315
329
  const backupLabel = backupDir.replace(os.homedir(), '~').replace(process.cwd(), '.');
@@ -332,11 +346,31 @@ async function uninstall(isGlobal) {
332
346
  }
333
347
  }
334
348
 
335
- if (installed.hasSkill) {
336
- removeDir(skillDir);
349
+ if (installed.hasWorkflows) {
350
+ removeDir(workflowsDir);
337
351
  out.log(` ${c.red('-')} ${c.dim('write-the-f-paper/')}`);
338
352
  }
339
353
 
354
+ if (installed.hasSkills) {
355
+ removeDir(skillsDir);
356
+ out.log(` ${c.red('-')} ${c.dim('skills/wtfp/')}`);
357
+
358
+ // Clean up empty skills dir
359
+ const parentSkillsDir = path.join(claudeDir, 'skills');
360
+ if (fs.existsSync(parentSkillsDir)) {
361
+ const remaining = fs.readdirSync(parentSkillsDir);
362
+ if (remaining.length === 0) {
363
+ fs.rmdirSync(parentSkillsDir);
364
+ out.log(` ${c.red('-')} ${c.dim('skills/')} ${c.dim('(empty)')}`);
365
+ }
366
+ }
367
+ }
368
+
369
+ if (fs.existsSync(pluginDir)) {
370
+ removeDir(pluginDir);
371
+ out.log(` ${c.red('-')} ${c.dim('.claude-plugin/')}`);
372
+ }
373
+
340
374
  // Remove version file
341
375
  const versionFile = path.join(claudeDir, VERSION_FILE);
342
376
  if (fs.existsSync(versionFile)) {
@@ -379,8 +413,8 @@ async function promptLocation() {
379
413
  const globalInstalled = detectInstallation(globalPath);
380
414
  const localInstalled = detectInstallation(localPath);
381
415
 
382
- const hasGlobalInstall = globalInstalled.hasCommands || globalInstalled.hasSkill;
383
- const hasLocalInstall = localInstalled.hasCommands || localInstalled.hasSkill;
416
+ const hasGlobalInstall = globalInstalled.hasCommands || globalInstalled.hasWorkflows || globalInstalled.hasSkills;
417
+ const hasLocalInstall = localInstalled.hasCommands || localInstalled.hasWorkflows || localInstalled.hasSkills;
384
418
 
385
419
  if (!hasGlobalInstall && !hasLocalInstall) {
386
420
  out.log(` ${c.yellow('No WTF-P installation found.')}\n`);
@@ -394,14 +428,14 @@ async function promptLocation() {
394
428
  out.log(` ${c.yellow('Where would you like to uninstall from?')}\n`);
395
429
 
396
430
  if (hasGlobalInstall) {
397
- const count = globalInstalled.commandFiles.length + globalInstalled.skillFiles.length;
431
+ const count = globalInstalled.commandFiles.length + globalInstalled.workflowFiles.length + globalInstalled.skillFiles.length;
398
432
  out.log(` ${c.cyan('1)')} Global ${c.dim('(' + globalLabel + ')')} - ${count} files`);
399
433
  } else {
400
434
  out.log(` ${c.dim('1) Global (' + globalLabel + ') - not installed')}`);
401
435
  }
402
436
 
403
437
  if (hasLocalInstall) {
404
- const count = localInstalled.commandFiles.length + localInstalled.skillFiles.length;
438
+ const count = localInstalled.commandFiles.length + localInstalled.workflowFiles.length + localInstalled.skillFiles.length;
405
439
  out.log(` ${c.cyan('2)')} Local ${c.dim('(./.claude)')} - ${count} files`);
406
440
  } else {
407
441
  out.log(` ${c.dim('2) Local (./.claude) - not installed')}`);
@@ -0,0 +1,34 @@
1
+ ---
2
+ marp: true
3
+ theme: uncover
4
+ size: 4:3
5
+ ---
6
+
7
+ # [Poster Title]
8
+
9
+ ## Authors
10
+ Author 1, Author 2
11
+
12
+ ---
13
+
14
+ ## Introduction
15
+ - Problem statement
16
+ - Importance
17
+
18
+ ---
19
+
20
+ ## Methodology
21
+ - Approach
22
+ - Implementation details
23
+
24
+ ---
25
+
26
+ ## Results
27
+ - Key findings
28
+ - Charts (use ECharts!)
29
+
30
+ ---
31
+
32
+ ## Conclusion
33
+ - Summary
34
+ - Future work
@@ -0,0 +1,31 @@
1
+ ---
2
+ marp: true
3
+ theme: gaia
4
+ _class: lead
5
+ ---
6
+
7
+ # [Presentation Title]
8
+ ## [Subtitle]
9
+
10
+ Author Name
11
+
12
+ ---
13
+
14
+ # Agenda
15
+ 1. Introduction
16
+ 2. Problem Statement
17
+ 3. Methodology
18
+ 4. Results
19
+ 5. Conclusion
20
+
21
+ ---
22
+
23
+ # Introduction
24
+ - Context
25
+ - High-level overview
26
+
27
+ ---
28
+
29
+ # Conclusion
30
+ - Final thoughts
31
+ - Q&A
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wtf-p",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "The G.O.A.T. meta-prompting system for 10x researchers who need to submit papers and projects and proposals YESTERDAY.",
5
5
  "bin": {
6
6
  "wtfp": "bin/install.js",
@@ -8,7 +8,7 @@
8
8
  "wtf-p-uninstall": "bin/uninstall.js"
9
9
  },
10
10
  "scripts": {
11
- "test": "node test/sanity.js && node test/paths.js",
11
+ "test": "node test/sanity.js && node test/paths.js && node test/linter.js",
12
12
  "test:integration": "node test/integration.js",
13
13
  "test:all": "npm test && npm run test:integration",
14
14
  "preflight": "node scripts/preflight.js",
@@ -16,9 +16,17 @@
16
16
  },
17
17
  "files": [
18
18
  "bin",
19
- "commands",
20
- "write-the-f-paper"
19
+ "core",
20
+ "vendors"
21
21
  ],
22
+ "peerDependencies": {
23
+ "@marp-team/marp-cli": ">=3.0.0"
24
+ },
25
+ "peerDependenciesMeta": {
26
+ "@marp-team/marp-cli": {
27
+ "optional": true
28
+ }
29
+ },
22
30
  "keywords": [
23
31
  "claude",
24
32
  "claude-code",
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "wtf-p",
3
+ "description": "Academic writing toolkit - Paper, Proposal, Presentation, Poster",
4
+ "version": "0.3.0",
5
+ "author": {
6
+ "name": "akougkas",
7
+ "url": "https://github.com/akougkas"
8
+ },
9
+ "homepage": "https://github.com/akougkas/wtf-p",
10
+ "repository": "https://github.com/akougkas/wtf-p",
11
+ "license": "MIT"
12
+ }
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: wtfp:create-poster
3
+ description: Full workflow for creating an academic poster from project content
4
+ allowed-tools:
5
+ - Read
6
+ - Write
7
+ - Bash
8
+ - AskUserQuestion
9
+ ---
10
+
11
+ <objective>
12
+ Generate an academic poster based on the current project's PROJECT.md and findings.
13
+ Uses the wtfp-marp skill to compile to HTML.
14
+ </objective>
15
+
16
+ <context>
17
+ @~/.claude/write-the-f-paper/references/principles.md
18
+ @~/.claude/write-the-f-paper/templates/posters/basic.md
19
+ </context>
20
+
21
+ <process>
22
+ ## Step 1: Content Synthesis
23
+ - Read `.planning/PROJECT.md` and any drafted sections.
24
+ - Identify key findings, methodology, and conclusion.
25
+
26
+ ## Step 2: User Interview
27
+ AskUserQuestion to determine:
28
+ - Poster title (default to project title)
29
+ - Authors
30
+ - Key visuals/charts needed
31
+ - Target audience
32
+
33
+ ## Step 3: Markdown Generation
34
+ - Create `poster.md` using Marp format.
35
+ - Integrate synthesized content.
36
+
37
+ ## Step 4: Compilation
38
+ - Use `wtfp-marp` skill: `npx @marp-team/marp-cli poster.md -o poster.html`
39
+
40
+ ## Step 5: Review
41
+ - Present the generated poster.html path to the user.
42
+ </process>