@hanzlaa/rcode 4.1.2 → 4.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 (67) hide show
  1. package/cli/install.js +176 -13
  2. package/cli/lib/config.cjs +4 -2
  3. package/cli/lib/fsutil.cjs +13 -2
  4. package/cli/lib/homedir.cjs +21 -0
  5. package/cli/lib/schemas.cjs +6 -1
  6. package/cli/nuke.js +13 -8
  7. package/cli/postinstall.js +14 -4
  8. package/cli/rcode-slash-router.cjs +118 -0
  9. package/cli/uninstall.js +59 -1
  10. package/cli/update.js +10 -5
  11. package/dist/rcode.js +234 -230
  12. package/package.json +1 -1
  13. package/server/dashboard.js +26 -7
  14. package/server/lib/api.js +62 -4
  15. package/server/lib/html/client/agents-data.js +22 -18
  16. package/server/lib/html/client/app.js +3 -0
  17. package/server/lib/html/client/components/AgentCard.js +127 -0
  18. package/server/lib/html/client/components/App.js +104 -39
  19. package/server/lib/html/client/components/CommandPalette.js +133 -0
  20. package/server/lib/html/client/components/FileReader.js +116 -0
  21. package/server/lib/html/client/components/FilterChips.js +94 -0
  22. package/server/lib/html/client/components/NotifyCenter.js +117 -0
  23. package/server/lib/html/client/components/OrchPanel.js +80 -52
  24. package/server/lib/html/client/components/PhaseGraph.js +300 -0
  25. package/server/lib/html/client/components/RejectDialog.js +78 -0
  26. package/server/lib/html/client/components/RunnerPicker.js +190 -0
  27. package/server/lib/html/client/components/Sidebar.js +106 -61
  28. package/server/lib/html/client/components/StatusSummaryBar.js +76 -0
  29. package/server/lib/html/client/components/TaskPipeline.js +83 -0
  30. package/server/lib/html/client/components/Topbar.js +86 -39
  31. package/server/lib/html/client/components/dashboard/Blockers.js +57 -0
  32. package/server/lib/html/client/components/dashboard/CompletedTasks.js +47 -0
  33. package/server/lib/html/client/components/dashboard/CurrentPhase.js +107 -0
  34. package/server/lib/html/client/components/dashboard/InProgress.js +72 -0
  35. package/server/lib/html/client/components/dashboard/ProgressDonut.js +101 -0
  36. package/server/lib/html/client/components/dashboard/ProgressTimeline.js +101 -0
  37. package/server/lib/html/client/components/dashboard/ProjectHealth.js +80 -0
  38. package/server/lib/html/client/components/dashboard/RecentDecisions.js +57 -0
  39. package/server/lib/html/client/components/dashboard/Timeline.js +143 -0
  40. package/server/lib/html/client/components/shared.js +47 -11
  41. package/server/lib/html/client/filter-state.js +72 -0
  42. package/server/lib/html/client/icons-client.js +7 -0
  43. package/server/lib/html/client/notify.js +75 -0
  44. package/server/lib/html/client/orchestrator.js +168 -41
  45. package/server/lib/html/client/preact.js +13 -8
  46. package/server/lib/html/client/store.js +70 -6
  47. package/server/lib/html/client/util.js +78 -0
  48. package/server/lib/html/client/vendor/htm.js +1 -0
  49. package/server/lib/html/client/vendor/preact-hooks.js +2 -0
  50. package/server/lib/html/client/vendor/preact.js +2 -0
  51. package/server/lib/html/client/views/AgentsView.js +144 -51
  52. package/server/lib/html/client/views/FilesView.js +20 -103
  53. package/server/lib/html/client/views/KanbanView.js +40 -21
  54. package/server/lib/html/client/views/MemoryView.js +26 -9
  55. package/server/lib/html/client/views/MilestonesView.js +4 -4
  56. package/server/lib/html/client/views/OrchestrationView.js +154 -19
  57. package/server/lib/html/client/views/OverviewView.js +47 -239
  58. package/server/lib/html/client/views/PhasesView.js +50 -6
  59. package/server/lib/html/client/views/RoadmapView.js +6 -3
  60. package/server/lib/html/client/views/SprintsView.js +50 -6
  61. package/server/lib/html/client/views/TasksView.js +4 -3
  62. package/server/lib/html/client.js +21 -4
  63. package/server/lib/html/css.js +2761 -8
  64. package/server/lib/html/icons.js +7 -0
  65. package/server/lib/html/shell.js +10 -3
  66. package/server/lib/scanner.js +376 -39
  67. package/server/orchestrator.js +329 -5
package/cli/uninstall.js CHANGED
@@ -25,6 +25,7 @@
25
25
  */
26
26
 
27
27
  const fs = require('fs');
28
+ const os = require('os');
28
29
  const path = require('path');
29
30
  const { spawnSync } = require('child_process');
30
31
  const { askConfirm, PromptAbortError } = require('./lib/prompts.cjs');
@@ -128,6 +129,35 @@ function cleanRcodePreCommitHook(cwd) {
128
129
  } catch { return 'skipped'; }
129
130
  }
130
131
 
132
+ /**
133
+ * Remove the rcode slash-router hook entry from a CLI hooks JSON file
134
+ * (codex: ~/.codex/hooks.json UserPromptSubmit, antigravity:
135
+ * ~/.gemini/antigravity/settings.json UserPrompt) while preserving every
136
+ * other entry (herdr's, the user's). Matches the router by command substring.
137
+ * Idempotent + guarded: missing/unparseable files are no-ops.
138
+ * Returns 'removed' | 'unchanged' | 'skipped'.
139
+ */
140
+ function removeSlashRouterHook(jsonPath, eventKey) {
141
+ if (!fs.existsSync(jsonPath)) return 'skipped';
142
+ let root;
143
+ try { root = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); } catch { return 'skipped'; }
144
+ if (!root || typeof root !== 'object' || !root.hooks || !Array.isArray(root.hooks[eventKey])) {
145
+ return 'unchanged';
146
+ }
147
+ const before = root.hooks[eventKey].length;
148
+ root.hooks[eventKey] = root.hooks[eventKey].filter(group =>
149
+ !(Array.isArray(group?.hooks) &&
150
+ group.hooks.some(h => typeof h?.command === 'string' && h.command.includes('rcode-slash-router.cjs'))),
151
+ );
152
+ if (root.hooks[eventKey].length === before) return 'unchanged';
153
+ // Drop the event key entirely if it is now empty, to leave a tidy file.
154
+ if (root.hooks[eventKey].length === 0) delete root.hooks[eventKey];
155
+ try {
156
+ writeFileAtomic(jsonPath, JSON.stringify(root, null, 2) + '\n');
157
+ return 'removed';
158
+ } catch { return 'skipped'; }
159
+ }
160
+
131
161
  /**
132
162
  * Walk a directory and remove all files/subdirs whose name matches a predicate.
133
163
  * Returns the number of entries removed. Always skips local overrides (#382).
@@ -433,7 +463,11 @@ function planToPathList(plan, cwd, options = {}) {
433
463
  * still proceed since the user already confirmed the destructive action.
434
464
  */
435
465
  function createBackup(cwd, plan, options = {}) {
436
- const paths = planToPathList(plan, cwd, { purge: options.purge === true });
466
+ // Tar entry names are always '/'-separated; on Windows planToPathList
467
+ // produces '\'-joined paths that bsdtar may store verbatim. Normalize so
468
+ // the archive (and any `tar -tzf` consumer) sees portable paths.
469
+ const paths = planToPathList(plan, cwd, { purge: options.purge === true })
470
+ .map((p) => p.split(path.sep).join('/'));
437
471
  if (paths.length === 0) {
438
472
  return { ok: false, warning: 'nothing to back up' };
439
473
  }
@@ -746,6 +780,29 @@ async function runUninstall(args) {
746
780
  if (n > 0) console.log(` ✓ removed ${n} Antigravity agents`);
747
781
  }
748
782
 
783
+ // Slash-router hooks (home-dir, CLI-native). Installed only via `--global`
784
+ // for codex/antigravity; remove the rcode UserPromptSubmit/UserPrompt entry
785
+ // from each CLI's hooks JSON, leaving herdr + other hooks intact.
786
+ if (editors.includes('codex')) {
787
+ const r = removeSlashRouterHook(path.join(os.homedir(), '.codex', 'hooks.json'), 'UserPromptSubmit');
788
+ if (r === 'removed') console.log(` ✓ removed rcode slash-router hook from ~/.codex/hooks.json`);
789
+ }
790
+ if (editors.includes('antigravity')) {
791
+ const r = removeSlashRouterHook(path.join(os.homedir(), '.gemini', 'antigravity', 'settings.json'), 'UserPrompt');
792
+ if (r === 'removed') console.log(` ✓ removed rcode slash-router hook from ~/.gemini/antigravity/settings.json`);
793
+ }
794
+ // The router script + command-body copies are shared by both CLIs; remove
795
+ // them once if either is in scope.
796
+ if (editors.includes('codex') || editors.includes('antigravity')) {
797
+ const home = os.homedir();
798
+ for (const dir of [path.join(home, '.rcode', 'slash-commands'), path.join(home, '.rcode', 'bin')]) {
799
+ if (fs.existsSync(dir)) {
800
+ const rr = safeRmSync(dir, home);
801
+ if (rr.ok) console.log(` ✓ removed ${dir}`);
802
+ }
803
+ }
804
+ }
805
+
749
806
  // #706 — gemini removal (.gemini/rcode/{agents,commands})
750
807
  if (editors.includes('gemini')) {
751
808
  let n = 0;
@@ -925,6 +982,7 @@ module.exports.isLocalOverride = isLocalOverride;
925
982
  module.exports.planToPathList = planToPathList;
926
983
  module.exports.discoverKnownActionSkills = discoverKnownActionSkills;
927
984
  module.exports.stripRcodeGitignoreBlock = stripRcodeGitignoreBlock;
985
+ module.exports.removeSlashRouterHook = removeSlashRouterHook;
928
986
 
929
987
  // Direct invocation — allow `node cli/uninstall.js [flags]` to run end-to-end.
930
988
  // When called via cli/index.js, module.exports is invoked directly.
package/cli/update.js CHANGED
@@ -34,6 +34,8 @@ const { spawnSync } = require('child_process');
34
34
  const clack = require('@clack/prompts');
35
35
  const { PromptAbortError } = require('./lib/prompts.cjs');
36
36
  const { writeFileAtomic } = require('./lib/fsutil.cjs');
37
+ // HOME-aware home resolution (#889): os.homedir() ignores HOME on Windows.
38
+ const { homedir } = require('./lib/homedir.cjs');
37
39
  const { verifyInstall, formatReport } = require('./lib/manifest.cjs');
38
40
  const install = require('./install');
39
41
 
@@ -46,7 +48,9 @@ const install = require('./install');
46
48
  function readConfigYaml(configPath) {
47
49
  const text = fs.readFileSync(configPath, 'utf8');
48
50
  const obj = {};
49
- for (const raw of text.split('\n')) {
51
+ // CRLF tolerance (#889): split on \r?\n — a stray \r otherwise defeats the
52
+ // `#.*$` comment strip ($ won't cross the \r) and leaks comments into values.
53
+ for (const raw of text.split(/\r?\n/)) {
50
54
  const line = raw.replace(/#.*$/, '').trimEnd();
51
55
  if (!line) continue;
52
56
  if (line.startsWith(' ')) continue; // ignore nested keys (rare; preserved on disk via raw text path)
@@ -68,14 +72,16 @@ function readConfigYaml(configPath) {
68
72
  * If the key doesn't exist, append it.
69
73
  */
70
74
  function setYamlKey(rawText, key, value) {
71
- const re = new RegExp(`^${key}:\\s*.*$`, 'm');
75
+ // CRLF tolerance (#889): [^\S\n]/[^\r\n] keep the match on one line even
76
+ // when the file uses \r\n (plain \s would walk across the line break).
77
+ const re = new RegExp(`^${key}:[^\\S\\n]*[^\\r\\n]*$`, 'm');
72
78
  const replacement = typeof value === 'string'
73
79
  ? `${key}: "${value.replace(/"/g, '\\"')}"`
74
80
  : `${key}: ${value}`;
75
81
  if (re.test(rawText)) {
76
82
  return rawText.replace(re, replacement);
77
83
  }
78
- return rawText.replace(/\n*$/, '') + `\n${replacement}\n`;
84
+ return rawText.replace(/(?:\r?\n)*$/, '') + `\n${replacement}\n`;
79
85
  }
80
86
 
81
87
  function parseArgs(args) {
@@ -98,8 +104,7 @@ function detectInstalledEditors(cwd) {
98
104
  // .rcode/config.yaml as the canonical signal — if config exists, the
99
105
  // project ran rcode install at least once for claude. The presence of
100
106
  // any commands/agents/skills then becomes secondary evidence.
101
- const os = require('os');
102
- const homeSkills = path.join(os.homedir(), '.claude/skills');
107
+ const homeSkills = path.join(homedir(), '.claude/skills');
103
108
  const projectClaude = (
104
109
  (fs.existsSync(path.join(cwd, '.claude/skills')) &&
105
110
  fs.readdirSync(path.join(cwd, '.claude/skills')).some(n => n.startsWith('rcode-'))) ||