wtf-p 0.4.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.
Files changed (175) hide show
  1. package/README.md +146 -226
  2. package/bin/commands/doctor.js +88 -2
  3. package/bin/commands/install-logic.js +121 -26
  4. package/bin/commands/status.js +22 -4
  5. package/bin/commands/update.js +5 -4
  6. package/bin/install.js +66 -26
  7. package/bin/lib/checkpoint.js +183 -0
  8. package/bin/lib/context-primer.js +214 -0
  9. package/bin/lib/manifest.js +62 -2
  10. package/bin/lib/utils.js +22 -1
  11. package/bin/uninstall.js +270 -111
  12. package/core/write-the-f-paper/references/agent-model-matrix.md +62 -0
  13. package/core/write-the-f-paper/references/checkpoints.md +160 -9
  14. package/core/write-the-f-paper/references/context-fidelity.md +153 -0
  15. package/core/write-the-f-paper/references/deviation-rules.md +150 -0
  16. package/core/write-the-f-paper/references/git-integration.md +80 -0
  17. package/core/write-the-f-paper/references/orchestrator-pattern.md +167 -0
  18. package/core/write-the-f-paper/references/planning-config.md +135 -0
  19. package/core/write-the-f-paper/references/ui-brand.md +186 -0
  20. package/core/write-the-f-paper/templates/UAT.md +80 -0
  21. package/core/write-the-f-paper/templates/base-prefs.yaml +42 -0
  22. package/core/write-the-f-paper/templates/config.json +31 -5
  23. package/core/write-the-f-paper/workflows/create-outline.wcn.md +1 -1
  24. package/core/write-the-f-paper/workflows/execute-outline.md +163 -0
  25. package/core/write-the-f-paper/workflows/execute-outline.wcn.md +73 -0
  26. package/core/write-the-f-paper/workflows/execute-section.wcn.md +141 -169
  27. package/core/write-the-f-paper/workflows/plan-section.wcn.md +96 -103
  28. package/core/write-the-f-paper/workflows/research-gap.md +27 -3
  29. package/core/write-the-f-paper/workflows/research-gap.wcn.md +18 -1
  30. package/core/write-the-f-paper/workflows/verify-work.md +251 -0
  31. package/core/write-the-f-paper/workflows/verify-work.wcn.md +74 -0
  32. package/package.json +19 -7
  33. package/vendors/claude/.claude-plugin/plugin.json +2 -2
  34. package/vendors/claude/agents/wtfp/argument-verifier.md +189 -0
  35. package/vendors/claude/agents/wtfp/citation-expert.md +1 -1
  36. package/vendors/claude/agents/wtfp/citation-formatter.md +1 -1
  37. package/vendors/claude/agents/wtfp/coherence-checker.md +167 -0
  38. package/vendors/claude/agents/wtfp/outliner.md +259 -0
  39. package/vendors/claude/agents/wtfp/plan-checker.md +249 -0
  40. package/vendors/claude/agents/wtfp/prose-polisher.md +166 -0
  41. package/vendors/claude/agents/wtfp/research-synthesizer.md +215 -0
  42. package/vendors/claude/agents/wtfp/section-planner.md +364 -0
  43. package/vendors/claude/agents/wtfp/section-reviewer.md +182 -0
  44. package/vendors/claude/agents/wtfp/section-writer.md +312 -0
  45. package/vendors/claude/commands/wtfp/add-todo.md +108 -0
  46. package/vendors/claude/commands/wtfp/analyze-bib.md +58 -236
  47. package/vendors/claude/commands/wtfp/audit-milestone.md +286 -0
  48. package/vendors/claude/commands/wtfp/check-refs.md +60 -133
  49. package/vendors/claude/commands/wtfp/check-todos.md +152 -0
  50. package/vendors/claude/commands/wtfp/checkpoint.md +105 -0
  51. package/vendors/claude/commands/wtfp/contribute.md +1 -1
  52. package/vendors/claude/commands/wtfp/create-outline.md +109 -195
  53. package/vendors/claude/commands/wtfp/create-poster.md +1 -1
  54. package/vendors/claude/commands/wtfp/create-slides.md +1 -1
  55. package/vendors/claude/commands/wtfp/discuss-section.md +1 -1
  56. package/vendors/claude/commands/wtfp/execute-outline.md +257 -0
  57. package/vendors/claude/commands/wtfp/export-latex.md +1 -1
  58. package/vendors/claude/commands/wtfp/help.md +174 -280
  59. package/vendors/claude/commands/wtfp/insert-section.md +1 -1
  60. package/vendors/claude/commands/wtfp/list-assumptions.md +1 -1
  61. package/vendors/claude/commands/wtfp/map-project.md +35 -124
  62. package/vendors/claude/commands/wtfp/new-paper.md +85 -320
  63. package/vendors/claude/commands/wtfp/pause-writing.md +1 -1
  64. package/vendors/claude/commands/wtfp/plan-milestone-gaps.md +255 -0
  65. package/vendors/claude/commands/wtfp/plan-revision.md +1 -1
  66. package/vendors/claude/commands/wtfp/plan-section.md +165 -190
  67. package/vendors/claude/commands/wtfp/polish-prose.md +66 -118
  68. package/vendors/claude/commands/wtfp/progress.md +82 -254
  69. package/vendors/claude/commands/wtfp/quick.md +107 -0
  70. package/vendors/claude/commands/wtfp/remove-section.md +1 -1
  71. package/vendors/claude/commands/wtfp/report-bug.md +1 -1
  72. package/vendors/claude/commands/wtfp/request-feature.md +1 -1
  73. package/vendors/claude/commands/wtfp/research-gap.md +138 -150
  74. package/vendors/claude/commands/wtfp/resume-writing.md +1 -1
  75. package/vendors/claude/commands/wtfp/review-section.md +55 -333
  76. package/vendors/claude/commands/wtfp/settings.md +197 -0
  77. package/vendors/claude/commands/wtfp/submit-milestone.md +248 -99
  78. package/vendors/claude/commands/wtfp/update.md +112 -0
  79. package/vendors/claude/commands/wtfp/verify-work.md +254 -0
  80. package/vendors/claude/commands/wtfp/write-section.md +144 -180
  81. package/vendors/gemini/agents/wtfp/argument-verifier.md +184 -0
  82. package/vendors/gemini/agents/wtfp/citation-expert.md +41 -0
  83. package/vendors/gemini/agents/wtfp/citation-formatter.md +38 -0
  84. package/vendors/gemini/agents/wtfp/coherence-checker.md +162 -0
  85. package/vendors/gemini/agents/wtfp/outliner.md +252 -0
  86. package/vendors/gemini/agents/wtfp/plan-checker.md +244 -0
  87. package/vendors/gemini/agents/wtfp/prose-polisher.md +158 -0
  88. package/vendors/gemini/agents/wtfp/research-synthesizer.md +205 -0
  89. package/vendors/gemini/agents/wtfp/section-planner.md +355 -0
  90. package/vendors/gemini/agents/wtfp/section-reviewer.md +175 -0
  91. package/vendors/gemini/agents/wtfp/section-writer.md +304 -0
  92. package/vendors/gemini/commands/wtfp/add-todo.toml +102 -0
  93. package/vendors/gemini/commands/wtfp/analyze-bib.toml +126 -0
  94. package/vendors/gemini/commands/wtfp/audit-milestone.toml +278 -0
  95. package/vendors/gemini/commands/wtfp/check-refs.toml +123 -0
  96. package/vendors/gemini/commands/wtfp/check-todos.toml +145 -0
  97. package/vendors/gemini/commands/wtfp/checkpoint.toml +96 -0
  98. package/vendors/gemini/commands/wtfp/contribute.toml +269 -0
  99. package/vendors/gemini/commands/wtfp/create-outline.toml +180 -0
  100. package/vendors/gemini/commands/wtfp/create-poster.toml +36 -0
  101. package/vendors/gemini/commands/wtfp/create-slides.toml +35 -0
  102. package/vendors/gemini/commands/wtfp/discuss-section.toml +62 -0
  103. package/vendors/gemini/commands/wtfp/execute-outline.toml +247 -0
  104. package/vendors/gemini/commands/wtfp/export-latex.toml +209 -0
  105. package/vendors/gemini/commands/wtfp/help.toml +184 -0
  106. package/vendors/gemini/commands/wtfp/insert-section.toml +123 -0
  107. package/vendors/gemini/commands/wtfp/list-assumptions.toml +104 -0
  108. package/vendors/gemini/commands/wtfp/map-project.toml +99 -0
  109. package/vendors/gemini/commands/wtfp/new-paper.toml +160 -0
  110. package/vendors/gemini/commands/wtfp/pause-writing.toml +140 -0
  111. package/vendors/gemini/commands/wtfp/plan-milestone-gaps.toml +249 -0
  112. package/vendors/gemini/commands/wtfp/plan-revision.toml +171 -0
  113. package/vendors/gemini/commands/wtfp/plan-section.toml +212 -0
  114. package/vendors/gemini/commands/wtfp/polish-prose.toml +114 -0
  115. package/vendors/gemini/commands/wtfp/progress.toml +145 -0
  116. package/vendors/gemini/commands/wtfp/quick.toml +96 -0
  117. package/vendors/gemini/commands/wtfp/remove-section.toml +140 -0
  118. package/vendors/gemini/commands/wtfp/report-bug.toml +126 -0
  119. package/vendors/gemini/commands/wtfp/request-feature.toml +155 -0
  120. package/vendors/gemini/commands/wtfp/research-gap.toml +214 -0
  121. package/vendors/gemini/commands/wtfp/resume-writing.toml +136 -0
  122. package/vendors/gemini/commands/wtfp/review-section.toml +121 -0
  123. package/vendors/gemini/commands/wtfp/settings.toml +191 -0
  124. package/vendors/gemini/commands/wtfp/submit-milestone.toml +351 -0
  125. package/vendors/gemini/commands/wtfp/update.toml +107 -0
  126. package/vendors/gemini/commands/wtfp/verify-work.toml +245 -0
  127. package/vendors/gemini/commands/wtfp/write-section.toml +228 -0
  128. package/vendors/opencode/agents/wtfp/argument-verifier.md +184 -0
  129. package/vendors/opencode/agents/wtfp/citation-expert.md +41 -0
  130. package/vendors/opencode/agents/wtfp/citation-formatter.md +38 -0
  131. package/vendors/opencode/agents/wtfp/coherence-checker.md +162 -0
  132. package/vendors/opencode/agents/wtfp/outliner.md +252 -0
  133. package/vendors/opencode/agents/wtfp/plan-checker.md +244 -0
  134. package/vendors/opencode/agents/wtfp/prose-polisher.md +158 -0
  135. package/vendors/opencode/agents/wtfp/research-synthesizer.md +205 -0
  136. package/vendors/opencode/agents/wtfp/section-planner.md +355 -0
  137. package/vendors/opencode/agents/wtfp/section-reviewer.md +175 -0
  138. package/vendors/opencode/agents/wtfp/section-writer.md +304 -0
  139. package/vendors/opencode/commands/wtfp/add-todo.md +103 -0
  140. package/vendors/opencode/commands/wtfp/analyze-bib.md +127 -0
  141. package/vendors/opencode/commands/wtfp/audit-milestone.md +279 -0
  142. package/vendors/opencode/commands/wtfp/check-refs.md +124 -0
  143. package/vendors/opencode/commands/wtfp/check-todos.md +146 -0
  144. package/vendors/opencode/commands/wtfp/checkpoint.md +97 -0
  145. package/vendors/opencode/commands/wtfp/contribute.md +266 -0
  146. package/vendors/opencode/commands/wtfp/create-outline.md +181 -0
  147. package/vendors/opencode/commands/wtfp/create-poster.md +37 -0
  148. package/vendors/opencode/commands/wtfp/create-slides.md +36 -0
  149. package/vendors/opencode/commands/wtfp/discuss-section.md +63 -0
  150. package/vendors/opencode/commands/wtfp/execute-outline.md +248 -0
  151. package/vendors/opencode/commands/wtfp/export-latex.md +210 -0
  152. package/vendors/opencode/commands/wtfp/help.md +185 -0
  153. package/vendors/opencode/commands/wtfp/insert-section.md +124 -0
  154. package/vendors/opencode/commands/wtfp/list-assumptions.md +105 -0
  155. package/vendors/opencode/commands/wtfp/map-project.md +100 -0
  156. package/vendors/opencode/commands/wtfp/new-paper.md +161 -0
  157. package/vendors/opencode/commands/wtfp/pause-writing.md +141 -0
  158. package/vendors/opencode/commands/wtfp/plan-milestone-gaps.md +250 -0
  159. package/vendors/opencode/commands/wtfp/plan-revision.md +174 -0
  160. package/vendors/opencode/commands/wtfp/plan-section.md +214 -0
  161. package/vendors/opencode/commands/wtfp/polish-prose.md +115 -0
  162. package/vendors/opencode/commands/wtfp/progress.md +146 -0
  163. package/vendors/opencode/commands/wtfp/quick.md +97 -0
  164. package/vendors/opencode/commands/wtfp/remove-section.md +141 -0
  165. package/vendors/opencode/commands/wtfp/report-bug.md +127 -0
  166. package/vendors/opencode/commands/wtfp/request-feature.md +156 -0
  167. package/vendors/opencode/commands/wtfp/research-gap.md +215 -0
  168. package/vendors/opencode/commands/wtfp/resume-writing.md +137 -0
  169. package/vendors/opencode/commands/wtfp/review-section.md +123 -0
  170. package/vendors/opencode/commands/wtfp/settings.md +192 -0
  171. package/vendors/opencode/commands/wtfp/submit-milestone.md +352 -0
  172. package/vendors/opencode/commands/wtfp/update.md +108 -0
  173. package/vendors/opencode/commands/wtfp/verify-work.md +246 -0
  174. package/vendors/opencode/commands/wtfp/write-section.md +228 -0
  175. package/vendors/claude/agents/wtfp/citation-retriever.md +0 -31
package/bin/uninstall.js CHANGED
@@ -17,6 +17,7 @@ const {
17
17
  prompt,
18
18
  VERSION_FILE
19
19
  } = require('./lib/utils');
20
+ const MANIFEST = require('./lib/manifest');
20
21
 
21
22
  // Get version from package.json
22
23
  let version = 'unknown';
@@ -32,6 +33,10 @@ try {
32
33
  const args = process.argv.slice(2);
33
34
  const hasGlobal = args.includes('--global') || args.includes('-g');
34
35
  const hasLocal = args.includes('--local') || args.includes('-l');
36
+ const hasClaude = args.includes('--claude');
37
+ const hasGemini = args.includes('--gemini');
38
+ const hasOpenCode = args.includes('--opencode');
39
+ const hasAll = args.includes('--all');
35
40
  const hasForce = args.includes('--force') || args.includes('-f');
36
41
  const hasBackup = args.includes('--backup') || args.includes('-b');
37
42
  const hasDryRun = args.includes('--dry-run') || args.includes('-n');
@@ -86,54 +91,82 @@ if (!hasQuiet) {
86
91
  // ============ Help Text ============
87
92
 
88
93
  if (hasHelp) {
89
- console.log(` ${c.yellow('Usage:')} npx wtf-p-uninstall [options]
94
+ console.log(` ${c.yellow('Usage:')} npx wtf-p uninstall [options]
90
95
 
91
96
  ${c.yellow('Options:')}
92
- ${c.cyan('-g, --global')} Uninstall from Claude config directory
93
- ${c.cyan('-l, --local')} Uninstall from ./.claude in current directory
94
- ${c.cyan('-c, --config-dir <path>')} Specify custom Claude config directory
97
+ ${c.cyan('-g, --global')} Uninstall from home directory
98
+ ${c.cyan('-l, --local')} Uninstall from current project
99
+ ${c.cyan('--claude')} Uninstall from Claude Code
100
+ ${c.cyan('--gemini')} Uninstall from Gemini CLI
101
+ ${c.cyan('--opencode')} Uninstall from OpenCode
102
+ ${c.cyan('--all')} Uninstall from all tools
103
+ ${c.cyan('-c, --config-dir <path>')} Uninstall from a custom directory
95
104
  ${c.cyan('-f, --force')} Skip confirmation prompts
96
105
  ${c.cyan('-b, --backup')} Backup files before removing
97
- ${c.cyan('-n, --dry-run')} Show what would be removed without removing
98
- ${c.cyan('--clean-backups')} Also remove .backup-* files from prior installs
106
+ ${c.cyan('-n, --dry-run')} Preview what would be removed
107
+ ${c.cyan('--clean-backups')} Also remove backup files from prior installs
99
108
  ${c.cyan('--no-color')} Disable colored output
100
109
  ${c.cyan('-q, --quiet')} Suppress non-essential output
101
- ${c.cyan('-h, --help')} Show this help message
110
+ ${c.cyan('-h, --help')} Show this help
102
111
 
103
112
  ${c.yellow('What gets removed:')}
104
- ${c.dim('commands/wtfp/')} - WTF-P slash commands
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
108
- ${c.dim('.wtfp-version')} - Version tracking file
113
+ ${c.dim('commands/wtfp/')} Slash commands (/wtfp:*)
114
+ ${c.dim('write-the-f-paper/')} Writing system files
115
+ ${c.dim('agents/wtfp/')} Agent definitions
116
+ ${c.dim('skills/wtfp/')} Skill definitions
117
+ ${c.dim('.claude-plugin/')} Plugin manifest
118
+ ${c.dim('.wtfp-version')} Version tracking file
109
119
 
110
120
  ${c.yellow('What stays intact:')}
111
- ${c.dim('CLAUDE.md')} - Your personal instructions (never touched)
112
- ${c.dim('commands/')} - Other slash commands you may have
113
- ${c.dim('settings.json')} - Your Claude settings
114
- ${c.dim('Other directories')} - Any other config or plugins
121
+ ${c.dim('CLAUDE.md')} Your personal instructions (never touched)
122
+ ${c.dim('commands/')} Other slash commands you added
123
+ ${c.dim('settings.json')} Your tool settings
124
+ ${c.dim('Everything else')} Nothing outside WTF-P is modified
115
125
 
116
126
  ${c.yellow('Examples:')}
117
- ${c.dim('# Interactive uninstall from ~/.claude')}
118
- npx wtf-p-uninstall --global
119
-
120
127
  ${c.dim('# Preview what would be removed')}
121
- npx wtf-p-uninstall --global --dry-run
128
+ npx wtf-p uninstall --global --dry-run
122
129
 
123
- ${c.dim('# Backup before removing')}
124
- npx wtf-p-uninstall --global --backup
130
+ ${c.dim('# Backup then remove')}
131
+ npx wtf-p uninstall --global --backup
125
132
 
126
- ${c.dim('# Also clean up backup files from prior installs')}
127
- npx wtf-p-uninstall --global --clean-backups
133
+ ${c.dim('# Remove from all tools at once')}
134
+ npx wtf-p uninstall --all
128
135
 
129
- ${c.dim('# Uninstall from custom location')}
130
- npx wtf-p-uninstall --global --config-dir ~/.claude-research
136
+ ${c.dim('# Also clean up leftover backup files')}
137
+ npx wtf-p uninstall --global --clean-backups
131
138
  `);
132
139
  process.exit(0);
133
140
  }
134
141
 
135
142
  // ============ Utilities ============
136
143
 
144
+ /**
145
+ * Get vendor-specific config directory
146
+ */
147
+ function getVendorDir(runtime, explicitConfigDir) {
148
+ // Handle 'claude-local' special case
149
+ if (runtime === 'claude-local') {
150
+ return path.join(process.cwd(), '.claude');
151
+ }
152
+
153
+ const vendorConfig = MANIFEST[runtime];
154
+ if (!vendorConfig) {
155
+ return null;
156
+ }
157
+
158
+ if (explicitConfigDir) {
159
+ return expandTilde(explicitConfigDir);
160
+ }
161
+
162
+ const envDir = process.env[vendorConfig.configDirEnv];
163
+ if (envDir) {
164
+ return expandTilde(envDir);
165
+ }
166
+
167
+ return path.join(os.homedir(), vendorConfig.defaultDir);
168
+ }
169
+
137
170
  /**
138
171
  * Generate backup directory path with timestamp
139
172
  */
@@ -192,28 +225,39 @@ function findBackupFiles(dir, backups = []) {
192
225
  // ============ Uninstall Logic ============
193
226
 
194
227
  /**
195
- * Uninstall from the specified directory
228
+ * Uninstall from the specified runtime
229
+ * @param {string} runtime - 'claude' | 'gemini' | 'opencode' | 'claude-local'
196
230
  */
197
- async function uninstall(isGlobal) {
198
- const claudeDir = getClaudeDir(explicitConfigDir, isGlobal);
199
- const locationLabel = getPathLabel(claudeDir, isGlobal);
231
+ async function uninstall(runtime) {
232
+ const isLocal = runtime === 'claude-local';
233
+ const vendorKey = isLocal ? 'claude' : runtime;
234
+ const vendorConfig = MANIFEST[vendorKey];
235
+
236
+ if (!vendorConfig) {
237
+ out.error(`Unknown runtime: ${runtime}`);
238
+ return;
239
+ }
240
+
241
+ const targetDir = getVendorDir(runtime, explicitConfigDir);
242
+ const locationLabel = getPathLabel(targetDir, !isLocal);
200
243
 
201
244
  out.log(` Checking ${c.cyan(locationLabel)} for WTF-P installation...\n`);
202
245
 
203
246
  // Detect installation
204
- const installed = detectInstallation(claudeDir);
205
- const totalFiles = installed.commandFiles.length + installed.workflowFiles.length + installed.skillFiles.length;
247
+ const installed = detectInstallation(targetDir);
248
+ const totalFiles = installed.commandFiles.length + installed.workflowFiles.length + installed.skillFiles.length
249
+ + (installed.agentFiles || []).length + (installed.mcpFiles || []).length + (installed.binFiles || []).length;
206
250
 
207
251
  if (!installed.hasCommands && !installed.hasWorkflows && !installed.hasSkills) {
208
252
  out.log(` ${c.yellow('No WTF-P installation found in ' + locationLabel)}\n`);
209
253
 
210
254
  // Check for backups if requested
211
255
  if (hasCleanBackups) {
212
- const backups = findBackupFiles(claudeDir);
256
+ const backups = findBackupFiles(targetDir);
213
257
  if (backups.length > 0) {
214
258
  out.log(` Found ${backups.length} backup file(s)/folder(s) to clean:\n`);
215
259
  for (const b of backups.slice(0, 10)) {
216
- out.log(` ${c.dim(b.replace(claudeDir, '.'))}`);
260
+ out.log(` ${c.dim(b.replace(targetDir, '.'))}`);
217
261
  }
218
262
  if (backups.length > 10) {
219
263
  out.log(` ${c.dim('... and ' + (backups.length - 10) + ' more')}`);
@@ -237,7 +281,7 @@ async function uninstall(isGlobal) {
237
281
  } else {
238
282
  fs.unlinkSync(b);
239
283
  }
240
- out.log(` ${c.red('-')} ${c.dim(b.replace(claudeDir, '.'))}`);
284
+ out.log(` ${c.red('-')} ${c.dim(b.replace(targetDir, '.'))}`);
241
285
  }
242
286
  out.log(`\n ${c.green('Cleaned ' + backups.length + ' backup file(s).')}\n`);
243
287
  } else {
@@ -253,10 +297,14 @@ async function uninstall(isGlobal) {
253
297
  // Show what will be removed
254
298
  out.log(` ${c.yellow('Found WTF-P installation:')}\n`);
255
299
 
256
- const commandsDir = path.join(claudeDir, 'commands', 'wtfp');
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');
300
+ const commandsDir = path.join(targetDir, 'commands', 'wtfp');
301
+ const workflowsDir = path.join(targetDir, 'write-the-f-paper');
302
+ const skillsDir = path.join(targetDir, 'skills', 'wtfp');
303
+ const agentsDir = path.join(targetDir, 'agents', 'wtfp');
304
+ const mcpDir = path.join(targetDir, 'mcp');
305
+ const binDir = path.join(targetDir, 'bin');
306
+ const pluginDir = path.join(targetDir, '.claude-plugin');
307
+ const wtfpPluginFile = path.join(pluginDir, 'plugin.json');
260
308
 
261
309
  if (installed.hasCommands) {
262
310
  out.log(` ${c.cyan('commands/wtfp/')} (${installed.commandFiles.length} files)`);
@@ -267,8 +315,17 @@ async function uninstall(isGlobal) {
267
315
  if (installed.hasSkills) {
268
316
  out.log(` ${c.cyan('skills/wtfp/')} (${installed.skillFiles.length} files)`);
269
317
  }
270
- if (fs.existsSync(pluginDir)) {
271
- out.log(` ${c.cyan('.claude-plugin/')} (manifest)`);
318
+ if (fs.existsSync(agentsDir)) {
319
+ out.log(` ${c.cyan('agents/wtfp/')} (agent definitions)`);
320
+ }
321
+ if (fs.existsSync(mcpDir)) {
322
+ out.log(` ${c.cyan('mcp/')} (MCP server)`);
323
+ }
324
+ if (fs.existsSync(binDir)) {
325
+ out.log(` ${c.cyan('bin/')} (scripts)`);
326
+ }
327
+ if (fs.existsSync(wtfpPluginFile)) {
328
+ out.log(` ${c.cyan('.claude-plugin/plugin.json')} (WTF-P plugin manifest)`);
272
329
  }
273
330
  if (installed.version) {
274
331
  out.log(` ${c.cyan(VERSION_FILE)} (version tracking)`);
@@ -278,10 +335,10 @@ async function uninstall(isGlobal) {
278
335
  // Check for backups to clean
279
336
  let backups = [];
280
337
  if (hasCleanBackups) {
281
- const allBackups = findBackupFiles(claudeDir);
338
+ const allBackups = findBackupFiles(targetDir);
282
339
  backups = allBackups.filter(b => {
283
340
  if (installed.hasCommands && b.startsWith(commandsDir)) return false;
284
- if (installed.hasSkill && b.startsWith(skillDir)) return false;
341
+ if (installed.hasSkills && b.startsWith(skillsDir)) return false;
285
342
  return true;
286
343
  });
287
344
  if (backups.length > 0) {
@@ -310,69 +367,111 @@ async function uninstall(isGlobal) {
310
367
 
311
368
  // Backup if requested
312
369
  if (hasBackup) {
313
- const backupDir = getBackupDir(claudeDir);
314
- fs.mkdirSync(backupDir, { recursive: true });
370
+ const backupDirPath = getBackupDir(targetDir);
371
+ fs.mkdirSync(backupDirPath, { recursive: true });
315
372
 
316
373
  if (installed.hasCommands) {
317
- copyDir(commandsDir, path.join(backupDir, 'commands', 'wtfp'));
374
+ copyDir(commandsDir, path.join(backupDirPath, 'commands', 'wtfp'));
318
375
  }
319
376
  if (installed.hasWorkflows) {
320
- copyDir(workflowsDir, path.join(backupDir, 'write-the-f-paper'));
377
+ copyDir(workflowsDir, path.join(backupDirPath, 'write-the-f-paper'));
321
378
  }
322
379
  if (installed.hasSkills) {
323
- copyDir(skillsDir, path.join(backupDir, 'skills', 'wtfp'));
380
+ copyDir(skillsDir, path.join(backupDirPath, 'skills', 'wtfp'));
324
381
  }
325
- if (fs.existsSync(pluginDir)) {
326
- copyDir(pluginDir, path.join(backupDir, '.claude-plugin'));
382
+ if (fs.existsSync(agentsDir)) {
383
+ copyDir(agentsDir, path.join(backupDirPath, 'agents', 'wtfp'));
384
+ }
385
+ if (fs.existsSync(mcpDir)) {
386
+ copyDir(mcpDir, path.join(backupDirPath, 'mcp'));
387
+ }
388
+ if (fs.existsSync(binDir)) {
389
+ copyDir(binDir, path.join(backupDirPath, 'bin'));
390
+ }
391
+ if (fs.existsSync(wtfpPluginFile)) {
392
+ fs.mkdirSync(path.join(backupDirPath, '.claude-plugin'), { recursive: true });
393
+ fs.copyFileSync(wtfpPluginFile, path.join(backupDirPath, '.claude-plugin', 'plugin.json'));
327
394
  }
328
395
 
329
- const backupLabel = backupDir.replace(os.homedir(), '~').replace(process.cwd(), '.');
396
+ const backupLabel = backupDirPath.replace(os.homedir(), '~').replace(process.cwd(), '.');
330
397
  out.log(` ${c.cyan('↻')} Backed up to ${c.dim(backupLabel)}\n`);
331
398
  }
332
399
 
333
- // Remove directories
334
- if (installed.hasCommands) {
335
- removeDir(commandsDir);
336
- out.log(` ${c.red('-')} ${c.dim('commands/wtfp/')}`);
337
-
338
- // Clean up empty commands dir
339
- const parentCommandsDir = path.join(claudeDir, 'commands');
340
- if (fs.existsSync(parentCommandsDir)) {
341
- const remaining = fs.readdirSync(parentCommandsDir);
342
- if (remaining.length === 0) {
343
- fs.rmdirSync(parentCommandsDir);
344
- out.log(` ${c.red('-')} ${c.dim('commands/')} ${c.dim('(empty)')}`);
345
- }
400
+ // Remove directories — only WTF-P subdirectories, never parent dirs with user content
401
+
402
+ // Helper: remove a wtfp subdir and clean up empty parent
403
+ function removeWtfpSubdir(subdir, parentDir, label) {
404
+ if (!fs.existsSync(subdir)) return;
405
+ removeDir(subdir);
406
+ out.log(` ${c.red('-')} ${c.dim(label)}`);
407
+
408
+ // Clean up parent only if empty (no user content remains)
409
+ if (parentDir && fs.existsSync(parentDir)) {
410
+ try {
411
+ const remaining = fs.readdirSync(parentDir);
412
+ if (remaining.length === 0) {
413
+ fs.rmdirSync(parentDir);
414
+ out.log(` ${c.red('-')} ${c.dim(path.basename(parentDir) + '/')} ${c.dim('(empty)')}`);
415
+ }
416
+ } catch { /* ignore */ }
346
417
  }
347
418
  }
348
419
 
420
+ if (installed.hasCommands) {
421
+ removeWtfpSubdir(commandsDir, path.join(targetDir, 'commands'), 'commands/wtfp/');
422
+ }
423
+
349
424
  if (installed.hasWorkflows) {
350
425
  removeDir(workflowsDir);
351
426
  out.log(` ${c.red('-')} ${c.dim('write-the-f-paper/')}`);
352
427
  }
353
428
 
354
429
  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
- }
430
+ removeWtfpSubdir(skillsDir, path.join(targetDir, 'skills'), 'skills/wtfp/');
367
431
  }
368
432
 
369
- if (fs.existsSync(pluginDir)) {
370
- removeDir(pluginDir);
371
- out.log(` ${c.red('-')} ${c.dim('.claude-plugin/')}`);
433
+ // Remove agents/wtfp/ (added in v0.5.0)
434
+ if (fs.existsSync(agentsDir)) {
435
+ removeWtfpSubdir(agentsDir, path.join(targetDir, 'agents'), 'agents/wtfp/');
436
+ }
437
+
438
+ // Remove mcp/ (WTF-P research server)
439
+ if (fs.existsSync(mcpDir)) {
440
+ removeDir(mcpDir);
441
+ out.log(` ${c.red('-')} ${c.dim('mcp/')}`);
442
+ }
443
+
444
+ // Remove bin/ (WTF-P scripts installed to config dir)
445
+ if (fs.existsSync(binDir)) {
446
+ removeDir(binDir);
447
+ out.log(` ${c.red('-')} ${c.dim('bin/')}`);
448
+ }
449
+
450
+ // Remove only WTF-P's plugin.json, not the entire .claude-plugin/ dir
451
+ // (user may have their own plugin configs there)
452
+ if (fs.existsSync(wtfpPluginFile)) {
453
+ try {
454
+ const pluginData = JSON.parse(fs.readFileSync(wtfpPluginFile, 'utf8'));
455
+ if (pluginData.name === 'wtf-p' || pluginData.name === 'write-the-f-paper') {
456
+ fs.unlinkSync(wtfpPluginFile);
457
+ out.log(` ${c.red('-')} ${c.dim('.claude-plugin/plugin.json')}`);
458
+
459
+ // Only remove .claude-plugin/ if empty after our file is gone
460
+ try {
461
+ const remaining = fs.readdirSync(pluginDir);
462
+ if (remaining.length === 0) {
463
+ fs.rmdirSync(pluginDir);
464
+ out.log(` ${c.red('-')} ${c.dim('.claude-plugin/')} ${c.dim('(empty)')}`);
465
+ }
466
+ } catch { /* ignore */ }
467
+ }
468
+ } catch {
469
+ // Can't parse plugin.json — leave it alone for safety
470
+ }
372
471
  }
373
472
 
374
473
  // Remove version file
375
- const versionFile = path.join(claudeDir, VERSION_FILE);
474
+ const versionFile = path.join(targetDir, VERSION_FILE);
376
475
  if (fs.existsSync(versionFile)) {
377
476
  fs.unlinkSync(versionFile);
378
477
  out.log(` ${c.red('-')} ${c.dim(VERSION_FILE)}`);
@@ -391,35 +490,48 @@ async function uninstall(isGlobal) {
391
490
  }
392
491
 
393
492
  out.log(`
394
- ${c.green('Done!')} WTF-P has been uninstalled.
493
+ ${c.green('Done!')} WTF-P has been removed from ${vendorConfig.name}.
395
494
 
396
- ${c.dim('Your CLAUDE.md and other configs remain untouched.')}
495
+ ${c.dim('Your personal config files were not touched.')}
397
496
 
398
- To reinstall: ${c.cyan('npx wtf-p --global')}
497
+ To reinstall later: ${c.cyan('npx wtf-p')}
399
498
  `);
400
499
  }
401
500
 
402
501
  /**
403
- * Prompt for uninstall location
502
+ * Prompt for uninstall location - shows all runtimes with installation status
404
503
  */
405
504
  async function promptLocation() {
406
505
  const rl = createRL();
407
506
 
408
- const globalPath = getClaudeDir(explicitConfigDir, true);
409
- const globalLabel = getPathLabel(globalPath, true);
410
- const localPath = getClaudeDir(null, false);
507
+ // Check installations for all runtimes
508
+ const runtimes = Object.keys(MANIFEST);
509
+ const installations = {};
510
+ let anyInstalled = false;
511
+
512
+ for (const runtime of runtimes) {
513
+ const targetDir = getVendorDir(runtime, null);
514
+ const installed = detectInstallation(targetDir);
515
+ const hasInstall = installed.hasCommands || installed.hasWorkflows || installed.hasSkills;
516
+ const count = installed.commandFiles.length + installed.workflowFiles.length + installed.skillFiles.length;
517
+ installations[runtime] = { installed, hasInstall, count, targetDir };
518
+ if (hasInstall) anyInstalled = true;
519
+ }
411
520
 
412
- // Check what's installed where
413
- const globalInstalled = detectInstallation(globalPath);
521
+ // Also check local .claude
522
+ const localPath = path.join(process.cwd(), '.claude');
414
523
  const localInstalled = detectInstallation(localPath);
415
-
416
- const hasGlobalInstall = globalInstalled.hasCommands || globalInstalled.hasWorkflows || globalInstalled.hasSkills;
417
524
  const hasLocalInstall = localInstalled.hasCommands || localInstalled.hasWorkflows || localInstalled.hasSkills;
525
+ const localCount = localInstalled.commandFiles.length + localInstalled.workflowFiles.length + localInstalled.skillFiles.length;
526
+ if (hasLocalInstall) anyInstalled = true;
418
527
 
419
- if (!hasGlobalInstall && !hasLocalInstall) {
528
+ if (!anyInstalled) {
420
529
  out.log(` ${c.yellow('No WTF-P installation found.')}\n`);
421
530
  out.log(` Checked:`);
422
- out.log(` ${c.dim(globalLabel)}`);
531
+ for (const runtime of runtimes) {
532
+ const label = getPathLabel(installations[runtime].targetDir, true);
533
+ out.log(` ${c.dim(label)}`);
534
+ }
423
535
  out.log(` ${c.dim('./.claude')}\n`);
424
536
  rl.close();
425
537
  return;
@@ -427,38 +539,71 @@ async function promptLocation() {
427
539
 
428
540
  out.log(` ${c.yellow('Where would you like to uninstall from?')}\n`);
429
541
 
430
- if (hasGlobalInstall) {
431
- const count = globalInstalled.commandFiles.length + globalInstalled.workflowFiles.length + globalInstalled.skillFiles.length;
432
- out.log(` ${c.cyan('1)')} Global ${c.dim('(' + globalLabel + ')')} - ${count} files`);
433
- } else {
434
- out.log(` ${c.dim('1) Global (' + globalLabel + ') - not installed')}`);
542
+ let optionNum = 1;
543
+ const options = [];
544
+
545
+ // Show runtime options
546
+ for (const runtime of runtimes) {
547
+ const { hasInstall, count, targetDir } = installations[runtime];
548
+ const label = getPathLabel(targetDir, true);
549
+ const vendorName = MANIFEST[runtime].name;
550
+
551
+ if (hasInstall) {
552
+ out.log(` ${c.cyan(optionNum + ')')} ${vendorName} ${c.dim('(' + label + ')')} - ${count} files`);
553
+ options.push({ num: optionNum, runtime });
554
+ } else {
555
+ out.log(` ${c.dim(optionNum + ') ' + vendorName + ' (' + label + ') - not installed')}`);
556
+ }
557
+ optionNum++;
435
558
  }
436
559
 
560
+ // Show local option
437
561
  if (hasLocalInstall) {
438
- const count = localInstalled.commandFiles.length + localInstalled.workflowFiles.length + localInstalled.skillFiles.length;
439
- out.log(` ${c.cyan('2)')} Local ${c.dim('(./.claude)')} - ${count} files`);
562
+ out.log(` ${c.cyan(optionNum + ')')} Local ${c.dim('(./.claude)')} - ${localCount} files`);
563
+ options.push({ num: optionNum, runtime: 'claude-local' });
440
564
  } else {
441
- out.log(` ${c.dim('2) Local (./.claude) - not installed')}`);
565
+ out.log(` ${c.dim(optionNum + ') Local (./.claude) - not installed')}`);
442
566
  }
567
+ optionNum++;
568
+
569
+ // Show "all" option if multiple installations
570
+ const installedCount = options.length;
571
+ if (installedCount > 1) {
572
+ out.log(` ${c.cyan(optionNum + ')')} All installed runtimes`);
573
+ options.push({ num: optionNum, runtime: 'all-installed' });
574
+ }
575
+
443
576
  out.log('');
444
577
 
445
578
  const answer = await prompt(rl, ` Choice: `);
446
579
  rl.close();
447
580
 
448
- if (answer === '1' && hasGlobalInstall) {
449
- await uninstall(true);
450
- } else if (answer === '2' && hasLocalInstall) {
451
- await uninstall(false);
452
- } else {
581
+ const selected = options.find(o => o.num === parseInt(answer, 10));
582
+
583
+ if (!selected) {
453
584
  out.log(`\n ${c.yellow('Invalid choice or no installation at that location.')}\n`);
585
+ return;
586
+ }
587
+
588
+ if (selected.runtime === 'all-installed') {
589
+ // Uninstall from all installed runtimes
590
+ for (const opt of options) {
591
+ if (opt.runtime !== 'all-installed') {
592
+ await uninstall(opt.runtime);
593
+ }
594
+ }
595
+ } else {
596
+ await uninstall(selected.runtime);
454
597
  }
455
598
  }
456
599
 
457
600
  // ============ Main ============
458
601
 
459
602
  async function main() {
460
- if (hasGlobal && hasLocal) {
461
- out.error('Cannot specify both --global and --local');
603
+ // Validate conflicting flags
604
+ const runtimeFlags = [hasGlobal, hasLocal, hasClaude, hasGemini, hasOpenCode, hasAll].filter(Boolean).length;
605
+ if (runtimeFlags > 1 && !hasAll) {
606
+ out.error('Pick one tool at a time, or use --all to uninstall from everything');
462
607
  process.exit(1);
463
608
  }
464
609
  if (explicitConfigDir && hasLocal) {
@@ -466,11 +611,25 @@ async function main() {
466
611
  process.exit(1);
467
612
  }
468
613
 
469
- if (hasGlobal) {
470
- await uninstall(true);
614
+ // Handle --all first
615
+ if (hasAll) {
616
+ for (const runtime of Object.keys(MANIFEST)) {
617
+ await uninstall(runtime);
618
+ }
619
+ return;
620
+ }
621
+
622
+ // Handle specific runtime flags
623
+ if (hasClaude || hasGlobal) {
624
+ await uninstall('claude');
625
+ } else if (hasGemini) {
626
+ await uninstall('gemini');
627
+ } else if (hasOpenCode) {
628
+ await uninstall('opencode');
471
629
  } else if (hasLocal) {
472
- await uninstall(false);
630
+ await uninstall('claude-local');
473
631
  } else {
632
+ // Interactive mode
474
633
  await promptLocation();
475
634
  }
476
635
  }
@@ -0,0 +1,62 @@
1
+ # Agent Model Matrix
2
+
3
+ Model assignments per agent across quality/balanced/budget profiles. Resolved at runtime from `.planning/config.json` `model_profile` field.
4
+
5
+ ## Lookup Table
6
+
7
+ | Agent | Role | quality | balanced | budget |
8
+ |-------|------|---------|----------|--------|
9
+ | section-planner | Creates PLAN.md with argument decomposition, word budgets, citation planning | opus | opus | sonnet |
10
+ | plan-checker | Pre-write validation of plans (7 dimensions) | sonnet | sonnet | haiku |
11
+ | section-writer | Executes PLAN.md, writes prose, atomic commits | opus | sonnet | sonnet |
12
+ | argument-verifier | Goal-backward verification of written sections | sonnet | sonnet | haiku |
13
+ | section-reviewer | 3-layer verification with reviewer personas | opus | sonnet | haiku |
14
+ | prose-polisher | De-robotize text, voice adjustment, sentence variety | opus | sonnet | haiku |
15
+ | research-synthesizer | Literature discovery, gap analysis, positioning | opus | sonnet | haiku |
16
+ | citation-expert | Search papers, manage BibTeX, identify gaps | sonnet | sonnet | haiku |
17
+ | citation-formatter | BibTeX validation, formatting, deduplication | sonnet | haiku | haiku |
18
+ | citation-retriever | Literature discovery, BibTeX generation | sonnet | haiku | haiku |
19
+ | coherence-checker | Cross-section coherence, argument flow, terminology consistency | sonnet | sonnet | haiku |
20
+ | outliner | Generates outline.md, argument-map.md, narrative-arc.md from PROJECT.md | opus | sonnet | sonnet |
21
+
22
+ ## Resolution Pattern
23
+
24
+ ```bash
25
+ MODEL_PROFILE=$(cat .planning/config.json 2>/dev/null | \
26
+ grep -o '"model_profile"[[:space:]]*:[[:space:]]*"[^"]*"' | \
27
+ grep -o '"[^"]*"$' | tr -d '"' || echo "balanced")
28
+ ```
29
+
30
+ Default: `balanced` when config missing or field absent.
31
+
32
+ ## Orchestrator → Agent Mapping
33
+
34
+ | Orchestrator Command | Primary Agent | Quality Agent |
35
+ |---------------------|---------------|---------------|
36
+ | /wtfp:plan-section | section-planner | plan-checker |
37
+ | /wtfp:write-section | section-writer | argument-verifier |
38
+ | /wtfp:review-section | section-reviewer | — |
39
+ | /wtfp:polish-prose | prose-polisher | — |
40
+ | /wtfp:research-gap | research-synthesizer | — |
41
+ | /wtfp:analyze-bib | citation-expert | — |
42
+ | /wtfp:check-refs | citation-formatter | — |
43
+ | /wtfp:execute-outline | outliner | coherence-checker |
44
+ | /wtfp:create-outline | outliner | — |
45
+
46
+ ## Profile Selection Guide
47
+
48
+ **quality** — Use for final drafts, critical sections (introduction, discussion), submission-ready work. Higher token cost, maximum reasoning depth.
49
+
50
+ **balanced** — Default. Good for most drafting, planning, and revision work. Planner stays at opus for argument quality; writer uses sonnet for cost efficiency.
51
+
52
+ **budget** — Use for iterative rough drafts, boilerplate sections (acknowledgments, author contributions), or when iterating rapidly. Significantly lower cost.
53
+
54
+ ## Setting Profile
55
+
56
+ ```bash
57
+ # Via config.json
58
+ cat .planning/config.json | jq '.model_profile = "quality"' > tmp && mv tmp .planning/config.json
59
+
60
+ # Via command
61
+ /wtfp:settings → model_profile → quality|balanced|budget
62
+ ```