wave-agent-sdk 0.19.7 → 0.19.9

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 (75) hide show
  1. package/builtin/plugins/sdd/.wave-plugin/plugin.json +8 -0
  2. package/builtin/plugins/sdd/hooks/hooks.json +14 -0
  3. package/builtin/plugins/sdd/scripts/session-start.js +24 -0
  4. package/builtin/plugins/sdd/scripts/spec-count.js +77 -0
  5. package/builtin/plugins/sdd/skills/specify/SKILL.md +48 -0
  6. package/builtin/plugins/sdd/skills/specify/templates/spec-template.md +47 -0
  7. package/dist/agent.d.ts +8 -0
  8. package/dist/agent.js +30 -10
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.js +1 -0
  11. package/dist/managers/aiManager.d.ts +18 -0
  12. package/dist/managers/aiManager.js +155 -46
  13. package/dist/managers/permissionManager.d.ts +7 -0
  14. package/dist/managers/permissionManager.js +102 -142
  15. package/dist/managers/pluginManager.d.ts +7 -0
  16. package/dist/managers/pluginManager.js +31 -0
  17. package/dist/managers/subagentManager.js +6 -0
  18. package/dist/prompts/index.d.ts +12 -1
  19. package/dist/prompts/index.js +133 -45
  20. package/dist/services/aiService.d.ts +1 -17
  21. package/dist/services/aiService.js +3 -85
  22. package/dist/services/configurationService.d.ts +6 -0
  23. package/dist/services/configurationService.js +31 -0
  24. package/dist/services/remoteSettingsService.js +2 -0
  25. package/dist/services/session.d.ts +3 -1
  26. package/dist/services/session.js +12 -4
  27. package/dist/services/taskManager.d.ts +1 -0
  28. package/dist/services/taskManager.js +34 -5
  29. package/dist/tools/editTool.js +24 -10
  30. package/dist/tools/enterWorktreeTool.js +2 -1
  31. package/dist/tools/grepTool.js +8 -2
  32. package/dist/tools/writeTool.js +36 -0
  33. package/dist/types/configuration.d.ts +5 -0
  34. package/dist/types/permissions.d.ts +0 -2
  35. package/dist/types/processes.d.ts +27 -0
  36. package/dist/types/workflow.d.ts +1 -1
  37. package/dist/utils/bashParser.d.ts +25 -0
  38. package/dist/utils/bashParser.js +103 -0
  39. package/dist/utils/configPaths.d.ts +4 -0
  40. package/dist/utils/configPaths.js +6 -0
  41. package/dist/utils/containerSetup.js +0 -9
  42. package/dist/utils/fileSearch.js +4 -2
  43. package/dist/utils/worktreeSession.d.ts +1 -1
  44. package/dist/utils/worktreeSession.js +1 -1
  45. package/dist/utils/worktreeUtils.d.ts +7 -1
  46. package/dist/utils/worktreeUtils.js +10 -4
  47. package/dist/workflow/types.d.ts +5 -0
  48. package/package.json +1 -1
  49. package/src/agent.ts +29 -10
  50. package/src/index.ts +1 -0
  51. package/src/managers/aiManager.ts +219 -61
  52. package/src/managers/permissionManager.ts +116 -168
  53. package/src/managers/pluginManager.ts +29 -0
  54. package/src/managers/subagentManager.ts +6 -0
  55. package/src/prompts/index.ts +144 -37
  56. package/src/services/aiService.ts +9 -128
  57. package/src/services/configurationService.ts +37 -0
  58. package/src/services/remoteSettingsService.ts +1 -0
  59. package/src/services/session.ts +18 -4
  60. package/src/services/taskManager.ts +46 -7
  61. package/src/tools/editTool.ts +29 -11
  62. package/src/tools/enterWorktreeTool.ts +2 -1
  63. package/src/tools/grepTool.ts +11 -2
  64. package/src/tools/writeTool.ts +43 -0
  65. package/src/types/configuration.ts +5 -0
  66. package/src/types/permissions.ts +0 -2
  67. package/src/types/processes.ts +29 -0
  68. package/src/types/workflow.ts +1 -0
  69. package/src/utils/bashParser.ts +106 -0
  70. package/src/utils/configPaths.ts +7 -0
  71. package/src/utils/containerSetup.ts +0 -11
  72. package/src/utils/fileSearch.ts +6 -2
  73. package/src/utils/worktreeSession.ts +1 -1
  74. package/src/utils/worktreeUtils.ts +14 -4
  75. package/src/workflow/types.ts +6 -0
@@ -476,6 +476,77 @@ export const DANGEROUS_COMMANDS = [
476
476
  "netcat",
477
477
  ];
478
478
 
479
+ /**
480
+ * Read-only command set: commands that only read/transform data and write to stdout.
481
+ * When a command in this set is used without write redirections, command substitution,
482
+ * or dangerous flags (e.g. sed -i), it is auto-allowed without a confirmation dialog.
483
+ * Aligned with Claude Code's SEMANTIC_READ_ONLY_COMMANDS, excluding interactive pagers
484
+ * (less, more, man, info), command executors (xargs), and infinite-output generators (yes).
485
+ * FR-019.2 through FR-019.7 in tool-permission-system.md.
486
+ */
487
+ export const READ_ONLY_COMMANDS = [
488
+ "ls",
489
+ "cat",
490
+ "head",
491
+ "tail",
492
+ "wc",
493
+ "sort",
494
+ "uniq",
495
+ "grep",
496
+ "egrep",
497
+ "fgrep",
498
+ "rg",
499
+ "find",
500
+ "which",
501
+ "whereis",
502
+ "file",
503
+ "stat",
504
+ "du",
505
+ "df",
506
+ "free",
507
+ "uptime",
508
+ "uname",
509
+ "hostname",
510
+ "whoami",
511
+ "id",
512
+ "groups",
513
+ "env",
514
+ "printenv",
515
+ "echo",
516
+ "printf",
517
+ "date",
518
+ "true",
519
+ "false",
520
+ "pwd",
521
+ "tree",
522
+ "diff",
523
+ "cmp",
524
+ "md5sum",
525
+ "sha256sum",
526
+ "sha1sum",
527
+ "xxd",
528
+ "od",
529
+ "hexdump",
530
+ "strings",
531
+ "readlink",
532
+ "realpath",
533
+ "basename",
534
+ "dirname",
535
+ "seq",
536
+ "column",
537
+ "jq",
538
+ "yq",
539
+ "cut",
540
+ "paste",
541
+ "tr",
542
+ "awk",
543
+ "sed",
544
+ "test",
545
+ "expr",
546
+ "bc",
547
+ "sleep",
548
+ ];
549
+
479
550
  /**
480
551
  * Registry of commands and their expected subcommand depth for smart prefix extraction.
481
552
  * For example, 'git: 2' means 'git commit' is a valid prefix, but 'git' alone is not.
@@ -634,6 +705,41 @@ export function isDangerousFind(command: string): boolean {
634
705
  });
635
706
  }
636
707
 
708
+ /**
709
+ * Detects command substitution $(...) or backticks `...` in a command string.
710
+ * Commands with substitution are never auto-allowed because the substituted
711
+ * command may be dangerous (e.g. cat $(rm x)). FR-019.6.
712
+ */
713
+ export function hasCommandSubstitution(command: string): boolean {
714
+ // Remove quoted strings first so $() or backticks inside quotes don't trigger
715
+ const stripped = command
716
+ .replace(/"(?:[^"\\]|\\.)*"/g, '""')
717
+ .replace(/'(?:[^'\\]|\\.)*'/g, "''");
718
+ return /\$\([^)]*\)/.test(stripped) || /`[^`]*`/.test(stripped);
719
+ }
720
+
721
+ /**
722
+ * Detects process substitution <(...) or >(...) in a command string.
723
+ * These can execute side effects and are never auto-allowed. FR-019.6.
724
+ */
725
+ export function hasProcessSubstitution(command: string): boolean {
726
+ const stripped = command
727
+ .replace(/"(?:[^"\\]|\\.)*"/g, '""')
728
+ .replace(/'(?:[^'\\]|\\.)*'/g, "''");
729
+ return /[<>]\([^)]*\)/.test(stripped);
730
+ }
731
+
732
+ /**
733
+ * Detects sed in-place edit flag (-i, with optional backup suffix like -i.bak).
734
+ * sed -i modifies files in place and must NOT be auto-allowed. FR-019.5.
735
+ */
736
+ export function hasSedInPlace(command: string): boolean {
737
+ const stripped = stripRedirections(stripEnvVars(command));
738
+ const tokens = stripped.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [];
739
+ if (tokens.length === 0 || tokens[0] !== "sed") return false;
740
+ return tokens.some((token) => /^-i(\..*)?$/.test(token));
741
+ }
742
+
637
743
  /**
638
744
  * Extracts a "smart prefix" from a bash command based on common developer tools.
639
745
  * Returns null if the command is blacklisted or cannot be safely prefix-matched.
@@ -51,6 +51,13 @@ export function getBuiltinSubagentsDir(): string {
51
51
  return join(getPackageRoot(), "builtin", "subagents");
52
52
  }
53
53
 
54
+ /**
55
+ * Get the builtin plugins directory path
56
+ */
57
+ export function getBuiltinPluginsDir(): string {
58
+ return join(getPackageRoot(), "builtin", "plugins");
59
+ }
60
+
54
61
  /**
55
62
  * Get the user-specific configuration file path (legacy function)
56
63
  * @deprecated Use getUserConfigPaths() for better priority support
@@ -256,8 +256,6 @@ export function setupAgentContainer(
256
256
 
257
257
  const decision = await options.canUseTool!(context);
258
258
 
259
- const planFilePath = permissionManager.getPlanFilePath();
260
-
261
259
  if (decision.newPermissionMode) {
262
260
  setPermissionMode(decision.newPermissionMode);
263
261
  }
@@ -266,15 +264,6 @@ export function setupAgentContainer(
266
264
  await addPermissionRule(decision.newPermissionRule);
267
265
  }
268
266
 
269
- if (decision.clearContext) {
270
- messageManager.clearMessages();
271
- if (planFilePath) {
272
- messageManager.addUserMessage({
273
- content: `Implement the plan at ${planFilePath}`,
274
- });
275
- }
276
- }
277
-
278
267
  return decision;
279
268
  }
280
269
  : undefined;
@@ -34,9 +34,13 @@ async function getAllFiles(workingDirectory: string): Promise<string[]> {
34
34
  });
35
35
 
36
36
  child.on("close", (code) => {
37
+ // Exit 2 = some files were unreadable (e.g. device-name files like
38
+ // "nul" on Windows); stdout still holds usable partial results.
39
+ // Spawn failures surface via the 'error' listener instead.
37
40
  if (code !== 0 && code !== 1) {
38
- reject(new Error(`ripgrep failed: ${stderr}`));
39
- return;
41
+ logger.warn(
42
+ `ripgrep exited with code ${code}, keeping partial results: ${stderr.trim()}`,
43
+ );
40
44
  }
41
45
  const files = stdout
42
46
  .trim()
@@ -5,7 +5,7 @@
5
5
  * "WorktreeSession" container slot registered in containerSetup.ts and accessed via
6
6
  * AIManager.getWorktreeSession()/setWorktreeSession()). This keeps worktree state
7
7
  * isolated per session in stdio multi-agent mode — a process-level singleton would
8
- * leak state across concurrent sessions (see specs/047-worktree.md FR-042).
8
+ * leak state across concurrent sessions (see docs/specs/multi-agent/worktree.md FR-042).
9
9
  */
10
10
 
11
11
  export interface WorktreeSession {
@@ -93,8 +93,16 @@ export function getHeadCommit(cwd: string): string {
93
93
 
94
94
  /**
95
95
  * Create a git worktree for use during a session.
96
+ * @param name Worktree name
97
+ * @param cwd Current working directory (will be resolved to main repo root)
98
+ * @param options Optional creation options
99
+ * @param options.baseRef "fresh" (default, origin/<default-branch>) | "head" (local HEAD)
96
100
  */
97
- export function createWorktree(name: string, cwd: string): WorktreeInfo {
101
+ export function createWorktree(
102
+ name: string,
103
+ cwd: string,
104
+ options?: { baseRef?: "fresh" | "head" },
105
+ ): WorktreeInfo {
98
106
  const repoRoot = getGitMainRepoRoot(cwd);
99
107
  if (!repoRoot) {
100
108
  throw new Error(
@@ -107,7 +115,8 @@ export function createWorktree(name: string, cwd: string): WorktreeInfo {
107
115
 
108
116
  const worktreePath = path.join(repoRoot, ".wave", "worktrees", name);
109
117
  const branchName = `worktree-${name}`;
110
- const baseBranch = getDefaultRemoteBranch(cwd);
118
+ const useHead = options?.baseRef === "head";
119
+ const baseBranch = useHead ? "HEAD" : getDefaultRemoteBranch(cwd);
111
120
 
112
121
  // Ensure Wave runtime files are git-excluded in this repo
113
122
  ensureWaveRuntimeFilesExcluded(cwd);
@@ -183,8 +192,9 @@ export function createWorktree(name: string, cwd: string): WorktreeInfo {
183
192
  }
184
193
  }
185
194
  if (
186
- stderr.includes("not a valid object name") ||
187
- stderr.includes("unknown revision")
195
+ !useHead &&
196
+ (stderr.includes("not a valid object name") ||
197
+ stderr.includes("unknown revision"))
188
198
  ) {
189
199
  // Base branch not fetched yet — try fetching then retrying
190
200
  const branchNameOnly = baseBranch.split("/").pop()!;
@@ -42,6 +42,12 @@ export interface WorkflowRun {
42
42
  failedAgentError?: string;
43
43
  }
44
44
 
45
+ /**
46
+ * Serializable workflow run for stdio transport, with the non-serializable
47
+ * `completionPromise` stripped. Returned by the `getWorkflowRuns` RPC.
48
+ */
49
+ export type SerializableWorkflowRun = Omit<WorkflowRun, "completionPromise">;
50
+
45
51
  export interface JournalEntry {
46
52
  agentIndex: number;
47
53
  prompt: string;