@datagusto/cli 0.0.0-stage → 0.2.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 (82) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +186 -2
  3. package/bin/run.js +5 -0
  4. package/dist/adapters/claude-code.d.ts +48 -0
  5. package/dist/adapters/claude-code.js +526 -0
  6. package/dist/adapters/claude-code.js.map +1 -0
  7. package/dist/adapters/claude-settings.d.ts +29 -0
  8. package/dist/adapters/claude-settings.js +115 -0
  9. package/dist/adapters/claude-settings.js.map +1 -0
  10. package/dist/adapters/index.d.ts +10 -0
  11. package/dist/adapters/index.js +37 -0
  12. package/dist/adapters/index.js.map +1 -0
  13. package/dist/adapters/kiro-agent.d.ts +29 -0
  14. package/dist/adapters/kiro-agent.js +124 -0
  15. package/dist/adapters/kiro-agent.js.map +1 -0
  16. package/dist/adapters/kiro.d.ts +24 -0
  17. package/dist/adapters/kiro.js +752 -0
  18. package/dist/adapters/kiro.js.map +1 -0
  19. package/dist/adapters/types.d.ts +81 -0
  20. package/dist/adapters/types.js +14 -0
  21. package/dist/adapters/types.js.map +1 -0
  22. package/dist/api/client.d.ts +36 -0
  23. package/dist/api/client.js +85 -0
  24. package/dist/api/client.js.map +1 -0
  25. package/dist/api/errors.d.ts +4 -0
  26. package/dist/api/errors.js +25 -0
  27. package/dist/api/errors.js.map +1 -0
  28. package/dist/commands/remove.d.ts +13 -0
  29. package/dist/commands/remove.js +59 -0
  30. package/dist/commands/remove.js.map +1 -0
  31. package/dist/commands/setup.d.ts +16 -0
  32. package/dist/commands/setup.js +114 -0
  33. package/dist/commands/setup.js.map +1 -0
  34. package/dist/commands/status.d.ts +7 -0
  35. package/dist/commands/status.js +53 -0
  36. package/dist/commands/status.js.map +1 -0
  37. package/dist/display.d.ts +2 -0
  38. package/dist/display.js +12 -0
  39. package/dist/display.js.map +1 -0
  40. package/dist/fs/atomic.d.ts +23 -0
  41. package/dist/fs/atomic.js +65 -0
  42. package/dist/fs/atomic.js.map +1 -0
  43. package/dist/key-input.d.ts +24 -0
  44. package/dist/key-input.js +96 -0
  45. package/dist/key-input.js.map +1 -0
  46. package/dist/platform.d.ts +24 -0
  47. package/dist/platform.js +61 -0
  48. package/dist/platform.js.map +1 -0
  49. package/dist/state/installs.d.ts +24 -0
  50. package/dist/state/installs.js +110 -0
  51. package/dist/state/installs.js.map +1 -0
  52. package/dist/vendor.d.ts +16 -0
  53. package/dist/vendor.js +60 -0
  54. package/dist/vendor.js.map +1 -0
  55. package/package.json +57 -4
  56. package/vendor/claude_code/.claude-plugin/plugin.json +55 -0
  57. package/vendor/claude_code/README.md +267 -0
  58. package/vendor/claude_code/dist/api-client.js +82 -0
  59. package/vendor/claude_code/dist/builtin-tools.generated.js +33 -0
  60. package/vendor/claude_code/dist/extractors.js +201 -0
  61. package/vendor/claude_code/dist/index.js +695 -0
  62. package/vendor/claude_code/dist/monitoring.js +66 -0
  63. package/vendor/claude_code/dist/state.js +71 -0
  64. package/vendor/claude_code/dist/types.js +5 -0
  65. package/vendor/claude_code/hooks/hooks.json +25 -0
  66. package/vendor/claude_code/package.json +26 -0
  67. package/vendor/kiro/.kiro/agents/kiro_default.json +20 -0
  68. package/vendor/kiro/.kiro/hooks/datagusto-post-tool-use.json +10 -0
  69. package/vendor/kiro/.kiro/hooks/datagusto-pre-tool-use.json +10 -0
  70. package/vendor/kiro/.kiro/hooks/datagusto-session-start.json +10 -0
  71. package/vendor/kiro/.kiro/hooks/datagusto-stop.json +10 -0
  72. package/vendor/kiro/.kiro/hooks/datagusto-user-prompt-submit.json +10 -0
  73. package/vendor/kiro/dist/api-client.js +82 -0
  74. package/vendor/kiro/dist/builtin-tools.generated.js +87 -0
  75. package/vendor/kiro/dist/engine.js +65 -0
  76. package/vendor/kiro/dist/extractors.js +201 -0
  77. package/vendor/kiro/dist/index.js +735 -0
  78. package/vendor/kiro/dist/monitoring.js +66 -0
  79. package/vendor/kiro/dist/state.js +71 -0
  80. package/vendor/kiro/dist/types.js +5 -0
  81. package/vendor/kiro/package.json +25 -0
  82. package/vendor/manifest.json +42 -0
@@ -0,0 +1,201 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Datagusto Safety Plugin - Pure extraction & hashing helpers
3
+ // ---------------------------------------------------------------------------
4
+ import { createHash } from "node:crypto";
5
+ import { BUILTIN_TOOLS } from "./builtin-tools.generated.js";
6
+ // -- Tool definition extraction ---------------------------------------------
7
+ const EMPTY_OBJECT_SCHEMA = {
8
+ type: "object",
9
+ properties: {},
10
+ };
11
+ /**
12
+ * Build DatagustoTool[] from the bundled Claude Code built-in tool catalog
13
+ * (see builtin-tools.generated.ts). Unlike OpenClaw, none of Claude Code's
14
+ * hook payloads expose the active tool catalog, so we register this fixed
15
+ * snapshot at the start of each session rather than extracting it per-turn.
16
+ * Output schemas are not exposed by Claude Code either and stay as the
17
+ * empty-object placeholder.
18
+ */
19
+ export function buildBuiltinToolDefinitions() {
20
+ return BUILTIN_TOOLS.map((t) => ({
21
+ name: t.id,
22
+ description: t.description,
23
+ input_schema: t.inputSchema,
24
+ output_schema: { ...EMPTY_OBJECT_SCHEMA },
25
+ }));
26
+ }
27
+ // -- User instruction extraction --------------------------------------------
28
+ /**
29
+ * Returns true if the prompt is a Claude Code slash command (e.g. "/compact",
30
+ * "/code-review") or bash-mode shortcut (e.g. "!ls") that should not be sent
31
+ * to alignment. This replaces OpenClaw's `stripChannelMetadata` /
32
+ * `isOpenClawCommand` pair: Claude Code's `UserPromptSubmit` payload already
33
+ * hands over the raw, unwrapped prompt text (no channel metadata header to
34
+ * strip), so the only filtering needed is for command-mode input.
35
+ */
36
+ export function isClaudeCodeCommand(prompt) {
37
+ const trimmed = prompt.trim();
38
+ return /^\/\w+/.test(trimmed) || trimmed.startsWith("!");
39
+ }
40
+ // -- Stable hashing ---------------------------------------------------------
41
+ /**
42
+ * Recursively sort object keys so that `JSON.stringify` produces
43
+ * a deterministic string regardless of insertion order.
44
+ */
45
+ function stableStringify(value) {
46
+ if (value === null || value === undefined)
47
+ return JSON.stringify(value);
48
+ if (Array.isArray(value)) {
49
+ return "[" + value.map(stableStringify).join(",") + "]";
50
+ }
51
+ if (typeof value === "object") {
52
+ const obj = value;
53
+ const keys = Object.keys(obj).sort();
54
+ return ("{" +
55
+ keys
56
+ .map((k) => JSON.stringify(k) + ":" + stableStringify(obj[k]))
57
+ .join(",") +
58
+ "}");
59
+ }
60
+ return JSON.stringify(value);
61
+ }
62
+ /**
63
+ * SHA-256 hash of a payload using stable (key-sorted) JSON serialization.
64
+ */
65
+ export function hashPayload(payload) {
66
+ const data = stableStringify(payload);
67
+ return createHash("sha256").update(data, "utf8").digest("hex");
68
+ }
69
+ // -- Tool result serialization ----------------------------------------------
70
+ /**
71
+ * Normalize a Claude Code `tool_response` into a plain JSON-safe value
72
+ * suitable for the validate API's `output` field. Unlike OpenClaw's
73
+ * LangChain-style ToolMessage result, Claude Code's `tool_response` is
74
+ * already a plain string or JSON-shaped object, so this only guards
75
+ * against values that don't round-trip through JSON.
76
+ */
77
+ export function serializeToolResult(result) {
78
+ if (result == null || typeof result === "string")
79
+ return result;
80
+ try {
81
+ return JSON.parse(JSON.stringify(result));
82
+ }
83
+ catch {
84
+ return String(result);
85
+ }
86
+ }
87
+ // -- Block reason / phase extraction ------------------------------------------
88
+ // Datagusto returns triggered_guardrails / metadata at the top level of the
89
+ // validate response (see backend/app/api/v1/endpoints/safety.py). Older or
90
+ // alternate shapes nested them under evaluation_result, so we read both.
91
+ function getTriggeredGuardrails(data) {
92
+ return data.triggered_guardrails ?? data.evaluation_result?.triggered_guardrails ?? [];
93
+ }
94
+ /**
95
+ * Extract a human-readable block reason from a ValidateResponse.
96
+ *
97
+ * Priority:
98
+ * 1. `data.message` (if present)
99
+ * 2. `actions[].result.message` from "block" actions in triggered guardrails
100
+ * 3. `actions[].result.warning_message` from "warn" actions
101
+ * 4. guardrail names as fallback
102
+ * 5. "policy violation" as last resort
103
+ */
104
+ export function extractBlockReason(data) {
105
+ if (data.message)
106
+ return data.message;
107
+ const guardrails = getTriggeredGuardrails(data);
108
+ const triggered = guardrails.filter((g) => g.triggered);
109
+ // Collect messages from block actions first, then warn actions
110
+ const blockMessages = [];
111
+ const warnMessages = [];
112
+ for (const g of triggered) {
113
+ for (const action of g.actions ?? []) {
114
+ if (action.action_type === "block" && action.result?.message) {
115
+ blockMessages.push(action.result.message);
116
+ }
117
+ else if (action.action_type === "warn" && action.result?.warning_message) {
118
+ warnMessages.push(action.result.warning_message);
119
+ }
120
+ }
121
+ }
122
+ if (blockMessages.length > 0)
123
+ return blockMessages.join("; ");
124
+ if (warnMessages.length > 0)
125
+ return warnMessages.join("; ");
126
+ // Fallback to guardrail names
127
+ const names = triggered
128
+ .map((g) => g.guardrail_name ?? g.guardrail_id ?? "unknown")
129
+ .join(", ");
130
+ if (names)
131
+ return `triggered: ${names}`;
132
+ return "policy violation";
133
+ }
134
+ /**
135
+ * Extract a short "phase" tag (used in log messages and block reasons).
136
+ *
137
+ * Prefers `metadata.blocked_by` (set by safety_service for branches like
138
+ * "uncategorized_tool"), then falls back to the first triggered guardrail's
139
+ * id / name. Returns undefined if neither is present.
140
+ */
141
+ export function extractPhase(data) {
142
+ const blockedBy = data.metadata?.blocked_by;
143
+ if (typeof blockedBy === "string" && blockedBy)
144
+ return blockedBy;
145
+ const triggered = getTriggeredGuardrails(data).filter((g) => g.triggered);
146
+ if (triggered.length > 0) {
147
+ return triggered[0].guardrail_id ?? triggered[0].guardrail_name;
148
+ }
149
+ return undefined;
150
+ }
151
+ /**
152
+ * Normalize a /guardrails/evaluate response into the flat ValidateResponseExtended
153
+ * shape the PreToolUse/PostToolUse handlers already consume.
154
+ *
155
+ * The evaluate endpoint nests decisions inside `triggered_guardrails[].actions[]`,
156
+ * whereas the handlers read top-level `should_proceed` / `severity` /
157
+ * `injected_instruction`. This maps between them:
158
+ * - block action (or warn with allow_proceed=false) → should_proceed=false
159
+ * - warn action with allow_proceed=true → should_proceed stays true, surface
160
+ * severity + injected_instruction (drives the "ask" on_start / "inject" on_end paths)
161
+ * - modify / allow / none → should_proceed=true
162
+ *
163
+ * `should_proceed` from the response is authoritative and honored first; the
164
+ * action walk only enriches severity/instruction and provides a defensive
165
+ * fallback when actions imply a block the top-level flag didn't reflect.
166
+ */
167
+ export function normalizeEvaluateResponse(data) {
168
+ const triggered = getTriggeredGuardrails(data).filter((g) => g.triggered);
169
+ let hardBlock = false;
170
+ let severity;
171
+ let injectedInstruction;
172
+ for (const g of triggered) {
173
+ for (const action of g.actions ?? []) {
174
+ if (action.action_type === "block") {
175
+ hardBlock = true;
176
+ }
177
+ else if (action.action_type === "warn") {
178
+ const r = action.result ?? {};
179
+ if (r.allow_proceed === false) {
180
+ hardBlock = true;
181
+ }
182
+ else {
183
+ if (r.warning_message && !injectedInstruction) {
184
+ injectedInstruction = r.warning_message;
185
+ }
186
+ if (r.severity && !severity) {
187
+ severity = r.severity;
188
+ }
189
+ }
190
+ }
191
+ }
192
+ }
193
+ const shouldProceed = data.should_proceed !== false && !hardBlock;
194
+ return {
195
+ should_proceed: shouldProceed,
196
+ reason: extractBlockReason(data),
197
+ severity,
198
+ injected_instruction: injectedInstruction,
199
+ };
200
+ }
201
+ //# sourceMappingURL=extractors.js.map