@whatasoda/agent-tools 0.1.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 (51) hide show
  1. package/dist/agents/codex-review/body.md +98 -0
  2. package/dist/agents/team-reviewer/body.md +78 -0
  3. package/dist/agents/team-worker/body.md +46 -0
  4. package/dist/scripts/codex-review.js +237 -0
  5. package/dist/scripts/detect-base-branch.js +185 -0
  6. package/dist/scripts/resolve-session.js +76 -0
  7. package/dist/skills/soda-brief/body.md +73 -0
  8. package/dist/skills/soda-discuss/README.md +25 -0
  9. package/dist/skills/soda-discuss/body.md +216 -0
  10. package/dist/skills/soda-fix/body.md +137 -0
  11. package/dist/skills/soda-plan/body.md +333 -0
  12. package/dist/skills/soda-research/body.md +127 -0
  13. package/dist/skills/soda-review/body.md +165 -0
  14. package/dist/skills/soda-review-todos/body.md +19 -0
  15. package/dist/skills/soda-team-init/body.md +313 -0
  16. package/dist/skills/soda-team-init/references/coordination-files.md +188 -0
  17. package/dist/skills/soda-team-run/body.md +329 -0
  18. package/dist/skills/soda-todo/body.md +86 -0
  19. package/dist/src/cli/commands/agent.js +29 -0
  20. package/dist/src/cli/commands/codex-review.js +14 -0
  21. package/dist/src/cli/commands/decision.js +103 -0
  22. package/dist/src/cli/commands/import.js +174 -0
  23. package/dist/src/cli/commands/link.js +52 -0
  24. package/dist/src/cli/commands/list.js +12 -0
  25. package/dist/src/cli/commands/node.js +118 -0
  26. package/dist/src/cli/commands/review.js +23 -0
  27. package/dist/src/cli/commands/session.js +23 -0
  28. package/dist/src/cli/commands/skill.js +29 -0
  29. package/dist/src/cli/commands/tag.js +31 -0
  30. package/dist/src/cli/helpers.js +51 -0
  31. package/dist/src/cli/index.js +48 -0
  32. package/dist/src/cli.js +59 -0
  33. package/dist/src/core/database.js +209 -0
  34. package/dist/src/core/ensure-dirs.js +8 -0
  35. package/dist/src/core/index.js +1 -0
  36. package/dist/src/core/kinds.js +46 -0
  37. package/dist/src/core/schema.sql +36 -0
  38. package/dist/src/core/schemas.js +41 -0
  39. package/dist/src/core/search.js +80 -0
  40. package/dist/src/core/types.js +0 -0
  41. package/dist/src/tui/App.js +130 -0
  42. package/dist/src/tui/actions.js +9 -0
  43. package/dist/src/tui/components/FilterBar.js +46 -0
  44. package/dist/src/tui/components/LinkList.js +53 -0
  45. package/dist/src/tui/components/NodeDetail.js +111 -0
  46. package/dist/src/tui/components/NodeList.js +62 -0
  47. package/dist/src/tui/components/StatusBar.js +90 -0
  48. package/dist/src/tui/hooks/useNavigation.js +57 -0
  49. package/dist/src/tui/hooks/useNodes.js +44 -0
  50. package/dist/src/tui/index.js +4 -0
  51. package/package.json +29 -0
@@ -0,0 +1,111 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ export function NodeDetail({ node, focused }) {
4
+ const borderColor = focused ? "cyan" : undefined;
5
+ return jsxDEV_7x81h0kn(Box, {
6
+ flexDirection: "column",
7
+ borderStyle: "single",
8
+ borderColor,
9
+ flexGrow: 2,
10
+ overflow: "hidden",
11
+ children: [
12
+ jsxDEV_7x81h0kn(Text, {
13
+ bold: true,
14
+ underline: true,
15
+ children: [
16
+ " ",
17
+ "Detail ",
18
+ focused ? "(active)" : ""
19
+ ]
20
+ }, undefined, true, undefined, this),
21
+ node === null ? jsxDEV_7x81h0kn(Text, {
22
+ color: "gray",
23
+ children: " Select a node to view details."
24
+ }, undefined, false, undefined, this) : jsxDEV_7x81h0kn(Fragment_8vg9x3sq, {
25
+ children: [
26
+ jsxDEV_7x81h0kn(Box, {
27
+ paddingX: 1,
28
+ flexDirection: "column",
29
+ children: [
30
+ jsxDEV_7x81h0kn(Text, {
31
+ children: [
32
+ jsxDEV_7x81h0kn(Text, {
33
+ bold: true,
34
+ children: "ID: "
35
+ }, undefined, false, undefined, this),
36
+ node.id
37
+ ]
38
+ }, undefined, true, undefined, this),
39
+ jsxDEV_7x81h0kn(Text, {
40
+ children: [
41
+ jsxDEV_7x81h0kn(Text, {
42
+ bold: true,
43
+ children: "Kind: "
44
+ }, undefined, false, undefined, this),
45
+ node.kind
46
+ ]
47
+ }, undefined, true, undefined, this),
48
+ node.tags.length > 0 && jsxDEV_7x81h0kn(Text, {
49
+ children: [
50
+ jsxDEV_7x81h0kn(Text, {
51
+ bold: true,
52
+ children: "Tags: "
53
+ }, undefined, false, undefined, this),
54
+ node.tags.join(", ")
55
+ ]
56
+ }, undefined, true, undefined, this),
57
+ jsxDEV_7x81h0kn(Text, {
58
+ children: [
59
+ jsxDEV_7x81h0kn(Text, {
60
+ bold: true,
61
+ children: "Created: "
62
+ }, undefined, false, undefined, this),
63
+ node.created_at
64
+ ]
65
+ }, undefined, true, undefined, this)
66
+ ]
67
+ }, undefined, true, undefined, this),
68
+ jsxDEV_7x81h0kn(Box, {
69
+ paddingX: 1,
70
+ flexDirection: "column",
71
+ marginTop: 1,
72
+ children: [
73
+ jsxDEV_7x81h0kn(Text, {
74
+ bold: true,
75
+ children: "Body:"
76
+ }, undefined, false, undefined, this),
77
+ jsxDEV_7x81h0kn(Text, {
78
+ children: node.body || "(empty)"
79
+ }, undefined, false, undefined, this)
80
+ ]
81
+ }, undefined, true, undefined, this),
82
+ Object.keys(node.properties).length > 0 && jsxDEV_7x81h0kn(Box, {
83
+ paddingX: 1,
84
+ flexDirection: "column",
85
+ marginTop: 1,
86
+ children: [
87
+ jsxDEV_7x81h0kn(Text, {
88
+ bold: true,
89
+ children: "Properties:"
90
+ }, undefined, false, undefined, this),
91
+ Object.entries(node.properties).map(([k, v]) => jsxDEV_7x81h0kn(Text, {
92
+ children: [
93
+ " ",
94
+ jsxDEV_7x81h0kn(Text, {
95
+ bold: true,
96
+ children: [
97
+ k,
98
+ ":"
99
+ ]
100
+ }, undefined, true, undefined, this),
101
+ " ",
102
+ JSON.stringify(v)
103
+ ]
104
+ }, k, true, undefined, this))
105
+ ]
106
+ }, undefined, true, undefined, this)
107
+ ]
108
+ }, undefined, true, undefined, this)
109
+ ]
110
+ }, undefined, true, undefined, this);
111
+ }
@@ -0,0 +1,62 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ export function NodeList({ nodes, selectedIndex, focused, loading, error }) {
4
+ const borderColor = focused ? "cyan" : undefined;
5
+ return jsxDEV_7x81h0kn(Box, {
6
+ flexDirection: "column",
7
+ borderStyle: "single",
8
+ borderColor,
9
+ flexGrow: 1,
10
+ overflow: "hidden",
11
+ children: [
12
+ jsxDEV_7x81h0kn(Text, {
13
+ bold: true,
14
+ underline: true,
15
+ children: [
16
+ " ",
17
+ "Nodes ",
18
+ focused ? "(active)" : ""
19
+ ]
20
+ }, undefined, true, undefined, this),
21
+ loading && jsxDEV_7x81h0kn(Text, {
22
+ color: "yellow",
23
+ children: " Loading..."
24
+ }, undefined, false, undefined, this),
25
+ error && jsxDEV_7x81h0kn(Text, {
26
+ color: "red",
27
+ children: [
28
+ " Error: ",
29
+ error
30
+ ]
31
+ }, undefined, true, undefined, this),
32
+ !loading && !error && nodes.length === 0 && jsxDEV_7x81h0kn(Text, {
33
+ color: "gray",
34
+ children: " No nodes found."
35
+ }, undefined, false, undefined, this),
36
+ !loading && !error && nodes.map((node, index) => {
37
+ const isSelected = index === selectedIndex;
38
+ const prefix = isSelected ? "> " : " ";
39
+ const tags = node.tags.length > 0 ? ` [${node.tags.join(",")}]` : "";
40
+ const bodyPreview = node.body.slice(0, 40).replace(/\n/g, " ");
41
+ const color = isSelected ? focused ? "cyan" : "white" : undefined;
42
+ return jsxDEV_7x81h0kn(Box, {
43
+ children: jsxDEV_7x81h0kn(Text, {
44
+ color,
45
+ bold: isSelected,
46
+ inverse: isSelected && focused,
47
+ children: [
48
+ prefix,
49
+ jsxDEV_7x81h0kn(Text, {
50
+ bold: true,
51
+ children: node.kind
52
+ }, undefined, false, undefined, this),
53
+ tags,
54
+ " ",
55
+ bodyPreview
56
+ ]
57
+ }, undefined, true, undefined, this)
58
+ }, node.id, false, undefined, this);
59
+ })
60
+ ]
61
+ }, undefined, true, undefined, this);
62
+ }
@@ -0,0 +1,90 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ export function StatusBar({ filterFocused }) {
4
+ if (filterFocused) {
5
+ return jsxDEV_7x81h0kn(Box, {
6
+ borderStyle: "single",
7
+ paddingX: 1,
8
+ flexShrink: 0,
9
+ children: jsxDEV_7x81h0kn(Text, {
10
+ children: [
11
+ jsxDEV_7x81h0kn(Text, {
12
+ bold: true,
13
+ color: "cyan",
14
+ children: "[Enter]"
15
+ }, undefined, false, undefined, this),
16
+ " ",
17
+ "apply filter",
18
+ " ",
19
+ jsxDEV_7x81h0kn(Text, {
20
+ bold: true,
21
+ color: "cyan",
22
+ children: "[Esc]"
23
+ }, undefined, false, undefined, this),
24
+ " ",
25
+ "cancel"
26
+ ]
27
+ }, undefined, true, undefined, this)
28
+ }, undefined, false, undefined, this);
29
+ }
30
+ return jsxDEV_7x81h0kn(Box, {
31
+ borderStyle: "single",
32
+ paddingX: 1,
33
+ flexShrink: 0,
34
+ children: jsxDEV_7x81h0kn(Text, {
35
+ children: [
36
+ jsxDEV_7x81h0kn(Text, {
37
+ bold: true,
38
+ color: "cyan",
39
+ children: "[j/k]"
40
+ }, undefined, false, undefined, this),
41
+ " ",
42
+ "navigate",
43
+ " ",
44
+ jsxDEV_7x81h0kn(Text, {
45
+ bold: true,
46
+ color: "cyan",
47
+ children: "[Tab]"
48
+ }, undefined, false, undefined, this),
49
+ " ",
50
+ "switch panel",
51
+ " ",
52
+ jsxDEV_7x81h0kn(Text, {
53
+ bold: true,
54
+ color: "cyan",
55
+ children: "[/]"
56
+ }, undefined, false, undefined, this),
57
+ " ",
58
+ "filter",
59
+ " ",
60
+ jsxDEV_7x81h0kn(Text, {
61
+ bold: true,
62
+ color: "cyan",
63
+ children: "[b]"
64
+ }, undefined, false, undefined, this),
65
+ "rainstorm",
66
+ " ",
67
+ jsxDEV_7x81h0kn(Text, {
68
+ bold: true,
69
+ color: "cyan",
70
+ children: "[r]"
71
+ }, undefined, false, undefined, this),
72
+ "eview",
73
+ " ",
74
+ jsxDEV_7x81h0kn(Text, {
75
+ bold: true,
76
+ color: "cyan",
77
+ children: "[y]"
78
+ }, undefined, false, undefined, this),
79
+ "copy",
80
+ " ",
81
+ jsxDEV_7x81h0kn(Text, {
82
+ bold: true,
83
+ color: "cyan",
84
+ children: "[q]"
85
+ }, undefined, false, undefined, this),
86
+ "uit"
87
+ ]
88
+ }, undefined, true, undefined, this)
89
+ }, undefined, false, undefined, this);
90
+ }
@@ -0,0 +1,57 @@
1
+ import { useState } from "react";
2
+ const PANEL_ORDER = ["list", "detail", "links"];
3
+ export function useNavigation() {
4
+ const [activePanel, setActivePanel] = useState("list");
5
+ const [selectedListIndex, setSelectedListIndex] = useState(0);
6
+ const [selectedLinkIndex, setSelectedLinkIndex] = useState(0);
7
+ const [filterFocused, setFilterFocused] = useState(false);
8
+ function cyclePanel() {
9
+ const current = PANEL_ORDER.indexOf(activePanel);
10
+ const next = PANEL_ORDER[(current + 1) % PANEL_ORDER.length];
11
+ if (next) {
12
+ setActivePanel(next);
13
+ }
14
+ }
15
+ function focusFilter() {
16
+ setFilterFocused(true);
17
+ }
18
+ function blurFilter() {
19
+ setFilterFocused(false);
20
+ }
21
+ function navigateDown(listLength) {
22
+ if (activePanel === "list") {
23
+ setSelectedListIndex((i) => Math.min(i + 1, Math.max(0, listLength - 1)));
24
+ } else if (activePanel === "links") {
25
+ setSelectedLinkIndex((i) => Math.min(i + 1, Math.max(0, listLength - 1)));
26
+ }
27
+ }
28
+ function navigateUp() {
29
+ if (activePanel === "list") {
30
+ setSelectedListIndex((i) => Math.max(0, i - 1));
31
+ } else if (activePanel === "links") {
32
+ setSelectedLinkIndex((i) => Math.max(0, i - 1));
33
+ }
34
+ }
35
+ function resetListIndex() {
36
+ setSelectedListIndex(0);
37
+ }
38
+ function resetLinkIndex() {
39
+ setSelectedLinkIndex(0);
40
+ }
41
+ return {
42
+ activePanel,
43
+ blurFilter,
44
+ cyclePanel,
45
+ filterFocused,
46
+ focusFilter,
47
+ navigateDown,
48
+ navigateUp,
49
+ resetLinkIndex,
50
+ resetListIndex,
51
+ selectedLinkIndex,
52
+ selectedListIndex,
53
+ setActivePanel,
54
+ setSelectedLinkIndex,
55
+ setSelectedListIndex
56
+ };
57
+ }
@@ -0,0 +1,44 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { Database } from "../../core/database.js";
3
+ import { ensureDbDir } from "../../core/ensure-dirs.js";
4
+ import os from "os";
5
+ import path from "path";
6
+ const DEFAULT_DB_PATH = path.join(os.homedir(), ".soda-agent-tools", "data.db");
7
+ export function useNodes(filter) {
8
+ const [nodes, setNodes] = useState([]);
9
+ const [loading, setLoading] = useState(true);
10
+ const [error, setError] = useState(null);
11
+ const dbRef = useRef(null);
12
+ useEffect(() => {
13
+ const dbPath = process.env["SODA_AGENT_TOOLS_DB"] ?? DEFAULT_DB_PATH;
14
+ ensureDbDir(dbPath);
15
+ dbRef.current = new Database(dbPath);
16
+ return () => {
17
+ dbRef.current?.close();
18
+ dbRef.current = null;
19
+ };
20
+ }, []);
21
+ useEffect(() => {
22
+ if (!dbRef.current) {
23
+ return;
24
+ }
25
+ setLoading(true);
26
+ try {
27
+ const result = dbRef.current.search({
28
+ kind: filter.kind,
29
+ limit: 100,
30
+ offset: 0,
31
+ query: filter.query,
32
+ tags: filter.tag ? [filter.tag] : undefined
33
+ });
34
+ setNodes(result.nodes);
35
+ setError(null);
36
+ } catch (err) {
37
+ setError(err instanceof Error ? err.message : String(err));
38
+ setNodes([]);
39
+ } finally {
40
+ setLoading(false);
41
+ }
42
+ }, [filter.kind, filter.tag, filter.query]);
43
+ return { error, loading, nodes };
44
+ }
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ import { render } from "ink";
3
+ import { App } from "./App.js";
4
+ render(jsxDEV_7x81h0kn(App, {}, undefined, false, undefined, this));
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@whatasoda/agent-tools",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "bin": {
7
+ "sd": "dist/src/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "bun scripts/build.ts",
11
+ "test": "bun test"
12
+ },
13
+ "files": [
14
+ "dist/"
15
+ ],
16
+ "dependencies": {
17
+ "ink": "latest",
18
+ "@inkjs/ui": "latest",
19
+ "react": "latest",
20
+ "zod": "latest",
21
+ "ulid": "latest"
22
+ },
23
+ "devDependencies": {
24
+ "bun-types": "latest",
25
+ "@types/react": "latest",
26
+ "typescript": "latest",
27
+ "ink-testing-library": "latest"
28
+ }
29
+ }