@timetotest/cli 0.2.4 → 0.3.1

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 (114) hide show
  1. package/README.md +49 -40
  2. package/dist/bin/ttt.js +0 -2
  3. package/dist/bin/ttt.js.map +1 -1
  4. package/dist/package.json +8 -3
  5. package/dist/src/commands/chat/ChatApp.js +249 -67
  6. package/dist/src/commands/chat/ChatApp.js.map +1 -1
  7. package/dist/src/commands/chat/OnboardingApp.js +49 -0
  8. package/dist/src/commands/chat/OnboardingApp.js.map +1 -0
  9. package/dist/src/commands/chat/components/Banner.js +1 -1
  10. package/dist/src/commands/chat/components/Banner.js.map +1 -1
  11. package/dist/src/commands/chat/components/ChatInput.js +61 -22
  12. package/dist/src/commands/chat/components/ChatInput.js.map +1 -1
  13. package/dist/src/commands/chat/components/ChatMessage.js +102 -0
  14. package/dist/src/commands/chat/components/ChatMessage.js.map +1 -0
  15. package/dist/src/commands/chat/components/MessageBubble.js +1 -1
  16. package/dist/src/commands/chat/components/MessageBubble.js.map +1 -1
  17. package/dist/src/commands/chat/components/PermissionPrompt.js +92 -0
  18. package/dist/src/commands/chat/components/PermissionPrompt.js.map +1 -0
  19. package/dist/src/commands/chat/components/StatusIndicator.js +21 -5
  20. package/dist/src/commands/chat/components/StatusIndicator.js.map +1 -1
  21. package/dist/src/commands/chat/components/ToolCallDisplay.js +141 -0
  22. package/dist/src/commands/chat/components/ToolCallDisplay.js.map +1 -0
  23. package/dist/src/commands/chat-ink.js +328 -93
  24. package/dist/src/commands/chat-ink.js.map +1 -1
  25. package/dist/src/commands/login.js +5 -5
  26. package/dist/src/commands/login.js.map +1 -1
  27. package/dist/src/commands/test.js +14 -291
  28. package/dist/src/commands/test.js.map +1 -1
  29. package/dist/src/lib/__tests__/code-mode-integration.test.js +381 -0
  30. package/dist/src/lib/__tests__/code-mode-integration.test.js.map +1 -0
  31. package/dist/src/lib/__tests__/config-manager.test.js +81 -0
  32. package/dist/src/lib/__tests__/config-manager.test.js.map +1 -0
  33. package/dist/src/lib/__tests__/mode-persistence-integration.test.js +75 -0
  34. package/dist/src/lib/__tests__/mode-persistence-integration.test.js.map +1 -0
  35. package/dist/src/lib/__tests__/permission-flow-integration.test.js +145 -0
  36. package/dist/src/lib/__tests__/permission-flow-integration.test.js.map +1 -0
  37. package/dist/src/lib/__tests__/permissions.test.js +132 -0
  38. package/dist/src/lib/__tests__/permissions.test.js.map +1 -0
  39. package/dist/src/lib/agent-orchestrator.js +259 -5
  40. package/dist/src/lib/agent-orchestrator.js.map +1 -1
  41. package/dist/src/lib/config.js +40 -0
  42. package/dist/src/lib/config.js.map +1 -1
  43. package/dist/src/lib/context-compactor.js +310 -0
  44. package/dist/src/lib/context-compactor.js.map +1 -0
  45. package/dist/src/lib/http.js +8 -0
  46. package/dist/src/lib/http.js.map +1 -1
  47. package/dist/src/lib/local-tools/code/__tests__/grep-search.test.js +146 -0
  48. package/dist/src/lib/local-tools/code/__tests__/grep-search.test.js.map +1 -0
  49. package/dist/src/lib/local-tools/code/__tests__/list-directory.test.js +192 -0
  50. package/dist/src/lib/local-tools/code/__tests__/list-directory.test.js.map +1 -0
  51. package/dist/src/lib/local-tools/code/__tests__/read-file.test.js +169 -0
  52. package/dist/src/lib/local-tools/code/__tests__/read-file.test.js.map +1 -0
  53. package/dist/src/lib/local-tools/code/__tests__/run-command.test.js +101 -0
  54. package/dist/src/lib/local-tools/code/__tests__/run-command.test.js.map +1 -0
  55. package/dist/src/lib/local-tools/code/__tests__/search-files.test.js +191 -0
  56. package/dist/src/lib/local-tools/code/__tests__/search-files.test.js.map +1 -0
  57. package/dist/src/lib/local-tools/code/grep-search.js +404 -0
  58. package/dist/src/lib/local-tools/code/grep-search.js.map +1 -0
  59. package/dist/src/lib/local-tools/code/index.js +11 -0
  60. package/dist/src/lib/local-tools/code/index.js.map +1 -0
  61. package/dist/src/lib/local-tools/code/list-directory.js +276 -0
  62. package/dist/src/lib/local-tools/code/list-directory.js.map +1 -0
  63. package/dist/src/lib/local-tools/code/read-file.js +301 -0
  64. package/dist/src/lib/local-tools/code/read-file.js.map +1 -0
  65. package/dist/src/lib/local-tools/code/run-command.js +235 -0
  66. package/dist/src/lib/local-tools/code/run-command.js.map +1 -0
  67. package/dist/src/lib/local-tools/code/search-files.js +297 -0
  68. package/dist/src/lib/local-tools/code/search-files.js.map +1 -0
  69. package/dist/src/lib/local-tools/code/types.js +6 -0
  70. package/dist/src/lib/local-tools/code/types.js.map +1 -0
  71. package/dist/src/lib/permissions.js +94 -0
  72. package/dist/src/lib/permissions.js.map +1 -0
  73. package/dist/src/lib/prompts/builder.js +13 -10
  74. package/dist/src/lib/prompts/builder.js.map +1 -1
  75. package/dist/src/lib/prompts/templates.js +78 -0
  76. package/dist/src/lib/prompts/templates.js.map +1 -1
  77. package/dist/src/lib/session-manager.js.map +1 -1
  78. package/dist/src/lib/testing-mode.js +2 -2
  79. package/dist/src/lib/testing-mode.js.map +1 -1
  80. package/dist/src/lib/tool-executor.js +131 -2
  81. package/dist/src/lib/tool-executor.js.map +1 -1
  82. package/dist/src/lib/tool-registry.js +171 -3
  83. package/dist/src/lib/tool-registry.js.map +1 -1
  84. package/dist/src/lib/tool-result-pruner.js +4 -4
  85. package/dist/src/lib/tool-result-pruner.js.map +1 -1
  86. package/dist/src/lib/tui/ink/components/AppFrame.js +17 -0
  87. package/dist/src/lib/tui/ink/components/AppFrame.js.map +1 -0
  88. package/dist/src/lib/tui/ink/components/CommandPalette.js +24 -0
  89. package/dist/src/lib/tui/ink/components/CommandPalette.js.map +1 -0
  90. package/dist/src/lib/tui/ink/components/Pill.js +19 -0
  91. package/dist/src/lib/tui/ink/components/Pill.js.map +1 -0
  92. package/dist/src/lib/tui/ink/components/TimetoTestLogo.js +30 -0
  93. package/dist/src/lib/tui/ink/components/TimetoTestLogo.js.map +1 -0
  94. package/dist/src/lib/tui/ink/theme.js +13 -6
  95. package/dist/src/lib/tui/ink/theme.js.map +1 -1
  96. package/dist/src/lib/tui/interactive-chat.js +35 -35
  97. package/dist/src/lib/tui/interactive-chat.js.map +1 -1
  98. package/dist/src/lib/tui/print.js +18 -18
  99. package/dist/src/lib/tui/print.js.map +1 -1
  100. package/dist/src/lib/tui/prompt.js +3 -3
  101. package/dist/src/lib/tui/prompt.js.map +1 -1
  102. package/dist/src/lib/tui/status.js +1 -1
  103. package/dist/src/lib/tui/status.js.map +1 -1
  104. package/dist/src/lib/update.js +10 -10
  105. package/dist/src/lib/update.js.map +1 -1
  106. package/package.json +8 -3
  107. package/dist/src/commands/start-test.js +0 -180
  108. package/dist/src/commands/start-test.js.map +0 -1
  109. package/dist/src/commands/stream/StreamApp.js +0 -127
  110. package/dist/src/commands/stream/StreamApp.js.map +0 -1
  111. package/dist/src/commands/stream.js +0 -52
  112. package/dist/src/commands/stream.js.map +0 -1
  113. package/dist/src/commands/test/TestRunApp.js +0 -183
  114. package/dist/src/commands/test/TestRunApp.js.map +0 -1
@@ -0,0 +1,145 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import { PermissionGuard, } from "../permissions.js";
3
+ describe("Permission Flow Integration", () => {
4
+ let guard;
5
+ beforeEach(() => {
6
+ guard = new PermissionGuard();
7
+ });
8
+ describe("run_command permission flow", () => {
9
+ it("prompts user and allows single execution", async () => {
10
+ const promptFn = vi
11
+ .fn()
12
+ .mockResolvedValue("allow_once");
13
+ guard.setPromptFn(promptFn);
14
+ // First command - should prompt
15
+ const request1 = {
16
+ category: "run_command",
17
+ description: "Run npm install",
18
+ details: { command: "npm", args: ["install"] },
19
+ };
20
+ const result1 = await guard.check(request1);
21
+ expect(result1.allowed).toBe(true);
22
+ expect(promptFn).toHaveBeenCalledTimes(1);
23
+ // Second command - should prompt again (allow_once doesn't persist)
24
+ const request2 = {
25
+ category: "run_command",
26
+ description: "Run npm test",
27
+ details: { command: "npm", args: ["test"] },
28
+ };
29
+ const result2 = await guard.check(request2);
30
+ expect(result2.allowed).toBe(true);
31
+ expect(promptFn).toHaveBeenCalledTimes(2);
32
+ });
33
+ it("grants session permission and skips subsequent prompts", async () => {
34
+ const promptFn = vi
35
+ .fn()
36
+ .mockResolvedValue("allow_session");
37
+ guard.setPromptFn(promptFn);
38
+ // First command - should prompt and grant session
39
+ const request1 = {
40
+ category: "run_command",
41
+ description: "Run npm install",
42
+ details: { command: "npm", args: ["install"] },
43
+ };
44
+ const result1 = await guard.check(request1);
45
+ expect(result1.allowed).toBe(true);
46
+ expect(promptFn).toHaveBeenCalledTimes(1);
47
+ // Second command - should NOT prompt (session granted)
48
+ const request2 = {
49
+ category: "run_command",
50
+ description: "Run npm test",
51
+ details: { command: "npm", args: ["test"] },
52
+ };
53
+ const result2 = await guard.check(request2);
54
+ expect(result2.allowed).toBe(true);
55
+ expect(promptFn).toHaveBeenCalledTimes(1); // Still 1, not called again
56
+ });
57
+ it("denies and provides feedback for alternative approach", async () => {
58
+ const promptFn = vi.fn().mockResolvedValue("deny");
59
+ guard.setPromptFn(promptFn);
60
+ const request = {
61
+ category: "run_command",
62
+ description: "Run dangerous command",
63
+ details: { command: "rm", args: ["-rf", "/"] },
64
+ };
65
+ const result = await guard.check(request);
66
+ expect(result.allowed).toBe(false);
67
+ expect(result.feedback).toContain("rm -rf /");
68
+ expect(result.feedback).toContain("alternative approach");
69
+ });
70
+ });
71
+ describe("mixed permission categories", () => {
72
+ it("tracks session grants per category independently", async () => {
73
+ const promptFn = vi
74
+ .fn()
75
+ .mockResolvedValue("allow_session");
76
+ guard.setPromptFn(promptFn);
77
+ // Grant session for run_command
78
+ await guard.check({
79
+ category: "run_command",
80
+ description: "Run command",
81
+ details: { command: "ls" },
82
+ });
83
+ expect(promptFn).toHaveBeenCalledTimes(1);
84
+ // file_write should still prompt (different category)
85
+ await guard.check({
86
+ category: "file_write",
87
+ description: "Write file",
88
+ details: { path: "/tmp/test.txt" },
89
+ });
90
+ expect(promptFn).toHaveBeenCalledTimes(2);
91
+ // run_command should NOT prompt (session granted)
92
+ await guard.check({
93
+ category: "run_command",
94
+ description: "Another command",
95
+ details: { command: "pwd" },
96
+ });
97
+ expect(promptFn).toHaveBeenCalledTimes(2); // Still 2
98
+ });
99
+ });
100
+ describe("session reset", () => {
101
+ it("requires new prompts after reset", async () => {
102
+ const promptFn = vi
103
+ .fn()
104
+ .mockResolvedValue("allow_session");
105
+ guard.setPromptFn(promptFn);
106
+ // Grant session
107
+ await guard.check({
108
+ category: "run_command",
109
+ description: "Run command",
110
+ details: { command: "ls" },
111
+ });
112
+ expect(promptFn).toHaveBeenCalledTimes(1);
113
+ // Verify session is active
114
+ await guard.check({
115
+ category: "run_command",
116
+ description: "Another command",
117
+ details: { command: "pwd" },
118
+ });
119
+ expect(promptFn).toHaveBeenCalledTimes(1); // No new prompt
120
+ // Reset session
121
+ guard.reset();
122
+ // Should prompt again after reset
123
+ await guard.check({
124
+ category: "run_command",
125
+ description: "Post-reset command",
126
+ details: { command: "echo" },
127
+ });
128
+ expect(promptFn).toHaveBeenCalledTimes(2); // New prompt
129
+ });
130
+ });
131
+ describe("error handling", () => {
132
+ it("handles prompt function errors gracefully", async () => {
133
+ const promptFn = vi.fn().mockRejectedValue(new Error("UI error"));
134
+ guard.setPromptFn(promptFn);
135
+ const request = {
136
+ category: "run_command",
137
+ description: "Run command",
138
+ details: { command: "ls" },
139
+ };
140
+ // Should throw the error (caller handles it)
141
+ await expect(guard.check(request)).rejects.toThrow("UI error");
142
+ });
143
+ });
144
+ });
145
+ //# sourceMappingURL=permission-flow-integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-flow-integration.test.js","sourceRoot":"","sources":["../../../../src/lib/__tests__/permission-flow-integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import { PermissionGuard, } from "../permissions.js";
3
+ describe("PermissionGuard", () => {
4
+ let guard;
5
+ beforeEach(() => {
6
+ guard = new PermissionGuard();
7
+ });
8
+ describe("check without prompt function", () => {
9
+ it("denies by default when no prompt function set", async () => {
10
+ const request = {
11
+ category: "run_command",
12
+ description: "Run ls command",
13
+ details: { command: "ls" },
14
+ };
15
+ const result = await guard.check(request);
16
+ expect(result.allowed).toBe(false);
17
+ expect(result.feedback).toContain("No permission handler configured");
18
+ });
19
+ });
20
+ describe("check with prompt function", () => {
21
+ it("allows when user responds allow_once", async () => {
22
+ const promptFn = vi
23
+ .fn()
24
+ .mockResolvedValue("allow_once");
25
+ guard.setPromptFn(promptFn);
26
+ const request = {
27
+ category: "run_command",
28
+ description: "Run ls command",
29
+ details: { command: "ls" },
30
+ };
31
+ const result = await guard.check(request);
32
+ expect(result.allowed).toBe(true);
33
+ expect(promptFn).toHaveBeenCalledWith(request);
34
+ });
35
+ it("allows and grants session when user responds allow_session", async () => {
36
+ const promptFn = vi
37
+ .fn()
38
+ .mockResolvedValue("allow_session");
39
+ guard.setPromptFn(promptFn);
40
+ const request = {
41
+ category: "run_command",
42
+ description: "Run ls command",
43
+ details: { command: "ls" },
44
+ };
45
+ const result = await guard.check(request);
46
+ expect(result.allowed).toBe(true);
47
+ expect(guard.hasSessionGrant("run_command")).toBe(true);
48
+ });
49
+ it("denies with feedback when user responds deny", async () => {
50
+ const promptFn = vi.fn().mockResolvedValue("deny");
51
+ guard.setPromptFn(promptFn);
52
+ const request = {
53
+ category: "run_command",
54
+ description: "Run rm command",
55
+ details: { command: "rm", args: ["-rf", "/"] },
56
+ };
57
+ const result = await guard.check(request);
58
+ expect(result.allowed).toBe(false);
59
+ expect(result.feedback).toContain("User denied permission");
60
+ expect(result.feedback).toContain("rm -rf /");
61
+ });
62
+ });
63
+ describe("session grants", () => {
64
+ it("allows immediately when session grant exists", async () => {
65
+ const promptFn = vi.fn();
66
+ guard.setPromptFn(promptFn);
67
+ guard.grantSession("run_command");
68
+ const request = {
69
+ category: "run_command",
70
+ description: "Run any command",
71
+ details: { command: "echo" },
72
+ };
73
+ const result = await guard.check(request);
74
+ expect(result.allowed).toBe(true);
75
+ expect(promptFn).not.toHaveBeenCalled();
76
+ });
77
+ it("does not grant session for other categories", async () => {
78
+ guard.grantSession("run_command");
79
+ expect(guard.hasSessionGrant("run_command")).toBe(true);
80
+ expect(guard.hasSessionGrant("file_write")).toBe(false);
81
+ expect(guard.hasSessionGrant("file_delete")).toBe(false);
82
+ });
83
+ });
84
+ describe("reset", () => {
85
+ it("clears all session grants", async () => {
86
+ guard.grantSession("run_command");
87
+ guard.grantSession("file_write");
88
+ guard.grantSession("file_delete");
89
+ guard.reset();
90
+ expect(guard.hasSessionGrant("run_command")).toBe(false);
91
+ expect(guard.hasSessionGrant("file_write")).toBe(false);
92
+ expect(guard.hasSessionGrant("file_delete")).toBe(false);
93
+ });
94
+ });
95
+ describe("feedback messages", () => {
96
+ beforeEach(() => {
97
+ const promptFn = vi.fn().mockResolvedValue("deny");
98
+ guard.setPromptFn(promptFn);
99
+ });
100
+ it("provides feedback for run_command denial", async () => {
101
+ const request = {
102
+ category: "run_command",
103
+ description: "Run npm install",
104
+ details: { command: "npm", args: ["install"] },
105
+ };
106
+ const result = await guard.check(request);
107
+ expect(result.feedback).toContain("npm install");
108
+ expect(result.feedback).toContain("alternative approach");
109
+ });
110
+ it("provides feedback for file_write denial", async () => {
111
+ const request = {
112
+ category: "file_write",
113
+ description: "Write to config file",
114
+ details: { path: "/etc/config.json" },
115
+ };
116
+ const result = await guard.check(request);
117
+ expect(result.feedback).toContain("/etc/config.json");
118
+ expect(result.feedback).toContain("alternative approach");
119
+ });
120
+ it("provides feedback for file_delete denial", async () => {
121
+ const request = {
122
+ category: "file_delete",
123
+ description: "Delete temp file",
124
+ details: { path: "/tmp/test.txt" },
125
+ };
126
+ const result = await guard.check(request);
127
+ expect(result.feedback).toContain("/tmp/test.txt");
128
+ expect(result.feedback).toContain("alternative approach");
129
+ });
130
+ });
131
+ });
132
+ //# sourceMappingURL=permissions.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.test.js","sourceRoot":"","sources":["../../../../src/lib/__tests__/permissions.test.ts"],"names":[],"mappings":"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"}