@tyvm/knowhow 0.0.129 → 0.0.131

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 (164) hide show
  1. package/package.json +4 -4
  2. package/src/agents/base/base.ts +40 -5
  3. package/src/agents/configurable/ConfigAgent.ts +0 -1
  4. package/src/agents/tools/index.ts +1 -0
  5. package/src/agents/tools/list.ts +2 -0
  6. package/src/agents/tools/skills.ts +79 -0
  7. package/src/agents/tools/startAgentTask.ts +17 -13
  8. package/src/ai.ts +55 -24
  9. package/src/chat/CliChatService.ts +7 -1
  10. package/src/chat/modules/AgentModule.ts +156 -15
  11. package/src/chat/renderer/PlainRenderer.ts +218 -0
  12. package/src/chat/renderer/index.ts +1 -0
  13. package/src/chat/renderer/loadRenderer.ts +2 -0
  14. package/src/chat.ts +2 -2
  15. package/src/cli.ts +10 -0
  16. package/src/clients/anthropic.ts +68 -2
  17. package/src/clients/fireworks.ts +10 -1
  18. package/src/clients/index.ts +5 -3
  19. package/src/clients/meta.ts +9 -0
  20. package/src/clients/openai.ts +28 -5
  21. package/src/clients/pricing/anthropic.ts +11 -0
  22. package/src/clients/pricing/fireworks.ts +12 -0
  23. package/src/clients/pricing/openai.ts +3 -0
  24. package/src/clients/types.ts +27 -4
  25. package/src/clients/xai.ts +2 -2
  26. package/src/commands/agent.ts +70 -18
  27. package/src/commands/agents.ts +556 -0
  28. package/src/commands/behaviors.ts +47 -0
  29. package/src/commands/hash.ts +124 -0
  30. package/src/commands/misc.ts +157 -3
  31. package/src/commands/services.ts +6 -2
  32. package/src/commands/skills.ts +107 -0
  33. package/src/config.ts +51 -1
  34. package/src/generate.ts +507 -0
  35. package/src/index.ts +34 -216
  36. package/src/plugins/PluginBase.ts +0 -2
  37. package/src/plugins/SkillsPlugin.ts +11 -124
  38. package/src/plugins/language.ts +0 -1
  39. package/src/plugins/plugins.ts +24 -13
  40. package/src/plugins/types.ts +8 -2
  41. package/src/services/BehaviorsService.ts +464 -0
  42. package/src/services/KnowhowClient.ts +61 -0
  43. package/src/services/SkillsService.ts +820 -0
  44. package/src/services/Tools.ts +2 -0
  45. package/src/services/index.ts +8 -2
  46. package/src/services/modules/types.ts +2 -0
  47. package/src/types.ts +36 -4
  48. package/ts_build/package.json +4 -4
  49. package/ts_build/src/agents/base/base.d.ts +9 -1
  50. package/ts_build/src/agents/base/base.js +29 -4
  51. package/ts_build/src/agents/base/base.js.map +1 -1
  52. package/ts_build/src/agents/configurable/ConfigAgent.js.map +1 -1
  53. package/ts_build/src/agents/tools/index.d.ts +1 -0
  54. package/ts_build/src/agents/tools/index.js +1 -0
  55. package/ts_build/src/agents/tools/index.js.map +1 -1
  56. package/ts_build/src/agents/tools/list.js +2 -0
  57. package/ts_build/src/agents/tools/list.js.map +1 -1
  58. package/ts_build/src/agents/tools/skills.d.ts +33 -0
  59. package/ts_build/src/agents/tools/skills.js +62 -0
  60. package/ts_build/src/agents/tools/skills.js.map +1 -0
  61. package/ts_build/src/agents/tools/startAgentTask.d.ts +2 -2
  62. package/ts_build/src/agents/tools/startAgentTask.js +13 -12
  63. package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
  64. package/ts_build/src/ai.d.ts +10 -4
  65. package/ts_build/src/ai.js +27 -25
  66. package/ts_build/src/ai.js.map +1 -1
  67. package/ts_build/src/chat/CliChatService.d.ts +1 -1
  68. package/ts_build/src/chat/CliChatService.js +4 -1
  69. package/ts_build/src/chat/CliChatService.js.map +1 -1
  70. package/ts_build/src/chat/modules/AgentModule.d.ts +2 -0
  71. package/ts_build/src/chat/modules/AgentModule.js +130 -4
  72. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  73. package/ts_build/src/chat/modules/CustomCommandsModule.d.ts +1 -1
  74. package/ts_build/src/chat/renderer/PlainRenderer.d.ts +28 -0
  75. package/ts_build/src/chat/renderer/PlainRenderer.js +169 -0
  76. package/ts_build/src/chat/renderer/PlainRenderer.js.map +1 -0
  77. package/ts_build/src/chat/renderer/index.d.ts +1 -0
  78. package/ts_build/src/chat/renderer/index.js +3 -1
  79. package/ts_build/src/chat/renderer/index.js.map +1 -1
  80. package/ts_build/src/chat/renderer/loadRenderer.js +2 -0
  81. package/ts_build/src/chat/renderer/loadRenderer.js.map +1 -1
  82. package/ts_build/src/chat.d.ts +1 -1
  83. package/ts_build/src/chat.js +2 -2
  84. package/ts_build/src/chat.js.map +1 -1
  85. package/ts_build/src/cli.js +9 -0
  86. package/ts_build/src/cli.js.map +1 -1
  87. package/ts_build/src/clients/anthropic.d.ts +2 -0
  88. package/ts_build/src/clients/anthropic.js +39 -1
  89. package/ts_build/src/clients/anthropic.js.map +1 -1
  90. package/ts_build/src/clients/fireworks.js +7 -0
  91. package/ts_build/src/clients/fireworks.js.map +1 -1
  92. package/ts_build/src/clients/index.js +3 -1
  93. package/ts_build/src/clients/index.js.map +1 -1
  94. package/ts_build/src/clients/meta.d.ts +2 -0
  95. package/ts_build/src/clients/meta.js +5 -0
  96. package/ts_build/src/clients/meta.js.map +1 -1
  97. package/ts_build/src/clients/openai.d.ts +1 -0
  98. package/ts_build/src/clients/openai.js +25 -5
  99. package/ts_build/src/clients/openai.js.map +1 -1
  100. package/ts_build/src/clients/pricing/anthropic.d.ts +3 -0
  101. package/ts_build/src/clients/pricing/anthropic.js +6 -0
  102. package/ts_build/src/clients/pricing/anthropic.js.map +1 -1
  103. package/ts_build/src/clients/pricing/fireworks.d.ts +1 -0
  104. package/ts_build/src/clients/pricing/fireworks.js +5 -1
  105. package/ts_build/src/clients/pricing/fireworks.js.map +1 -1
  106. package/ts_build/src/clients/pricing/openai.js +3 -0
  107. package/ts_build/src/clients/pricing/openai.js.map +1 -1
  108. package/ts_build/src/clients/types.d.ts +6 -1
  109. package/ts_build/src/clients/xai.js +1 -1
  110. package/ts_build/src/clients/xai.js.map +1 -1
  111. package/ts_build/src/commands/agent.js +58 -15
  112. package/ts_build/src/commands/agent.js.map +1 -1
  113. package/ts_build/src/commands/agents.d.ts +2 -0
  114. package/ts_build/src/commands/agents.js +460 -0
  115. package/ts_build/src/commands/agents.js.map +1 -0
  116. package/ts_build/src/commands/behaviors.d.ts +2 -0
  117. package/ts_build/src/commands/behaviors.js +43 -0
  118. package/ts_build/src/commands/behaviors.js.map +1 -0
  119. package/ts_build/src/commands/hash.d.ts +2 -0
  120. package/ts_build/src/commands/hash.js +117 -0
  121. package/ts_build/src/commands/hash.js.map +1 -0
  122. package/ts_build/src/commands/misc.js +108 -2
  123. package/ts_build/src/commands/misc.js.map +1 -1
  124. package/ts_build/src/commands/services.js +4 -2
  125. package/ts_build/src/commands/services.js.map +1 -1
  126. package/ts_build/src/commands/skills.d.ts +2 -0
  127. package/ts_build/src/commands/skills.js +85 -0
  128. package/ts_build/src/commands/skills.js.map +1 -0
  129. package/ts_build/src/config.js +34 -0
  130. package/ts_build/src/config.js.map +1 -1
  131. package/ts_build/src/generate.d.ts +16 -0
  132. package/ts_build/src/generate.js +304 -0
  133. package/ts_build/src/generate.js.map +1 -0
  134. package/ts_build/src/index.d.ts +3 -3
  135. package/ts_build/src/index.js +13 -118
  136. package/ts_build/src/index.js.map +1 -1
  137. package/ts_build/src/plugins/PluginBase.d.ts +0 -2
  138. package/ts_build/src/plugins/PluginBase.js.map +1 -1
  139. package/ts_build/src/plugins/SkillsPlugin.d.ts +1 -6
  140. package/ts_build/src/plugins/SkillsPlugin.js +6 -135
  141. package/ts_build/src/plugins/SkillsPlugin.js.map +1 -1
  142. package/ts_build/src/plugins/language.js.map +1 -1
  143. package/ts_build/src/plugins/plugins.d.ts +4 -1
  144. package/ts_build/src/plugins/plugins.js +17 -11
  145. package/ts_build/src/plugins/plugins.js.map +1 -1
  146. package/ts_build/src/plugins/types.d.ts +3 -2
  147. package/ts_build/src/services/BehaviorsService.d.ts +48 -0
  148. package/ts_build/src/services/BehaviorsService.js +382 -0
  149. package/ts_build/src/services/BehaviorsService.js.map +1 -0
  150. package/ts_build/src/services/KnowhowClient.d.ts +8 -0
  151. package/ts_build/src/services/KnowhowClient.js +20 -0
  152. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  153. package/ts_build/src/services/SkillsService.d.ts +60 -0
  154. package/ts_build/src/services/SkillsService.js +641 -0
  155. package/ts_build/src/services/SkillsService.js.map +1 -0
  156. package/ts_build/src/services/Tools.d.ts +2 -0
  157. package/ts_build/src/services/Tools.js.map +1 -1
  158. package/ts_build/src/services/index.d.ts +3 -0
  159. package/ts_build/src/services/index.js +8 -2
  160. package/ts_build/src/services/index.js.map +1 -1
  161. package/ts_build/src/services/modules/index.d.ts +4 -2
  162. package/ts_build/src/services/modules/types.d.ts +2 -0
  163. package/ts_build/src/types.d.ts +14 -4
  164. package/ts_build/src/types.js.map +1 -1
@@ -18,7 +18,7 @@ import { BaseChatModule } from "./BaseChatModule";
18
18
  import { services } from "../../services/index";
19
19
  import { BaseAgent } from "../../agents/index";
20
20
  import { ChatCommand, ChatMode, ChatContext, ChatService } from "../types";
21
- import { Message } from "../../clients/types";
21
+ import { Message, CompletionOptions } from "../../clients/types";
22
22
  import { ChatInteraction } from "../../types";
23
23
  import { Marked } from "../../utils/index";
24
24
  import { TokenCompressor } from "../../processors/TokenCompressor";
@@ -52,6 +52,11 @@ export class AgentModule extends BaseChatModule {
52
52
  /** Currently active synced agent watcher (for FS or Web agents) */
53
53
  private activeSyncedWatcher: SyncedAgentWatcher | undefined;
54
54
 
55
+ /** Reasoning effort to apply to agents started from this module */
56
+ private reasoningEffort: CompletionOptions["reasoning_effort"] | undefined = undefined;
57
+ /** Whether to request a reasoning summary from supporting models */
58
+ private summarizeReasoning: boolean = true;
59
+
55
60
  /** Stored wire params for rewireAgentRendering */
56
61
  private _wireAgentEvents: EventService | undefined;
57
62
  private _wireTaskId: string | undefined;
@@ -138,6 +143,78 @@ export class AgentModule extends BaseChatModule {
138
143
  }
139
144
  },
140
145
  },
146
+ {
147
+ name: "reasoning_effort",
148
+ description:
149
+ "Set the reasoning effort for the agent (none, low, medium, high). Use 'none' to disable thinking on models that support it.",
150
+ handler: async (args: string[]): Promise<void> => {
151
+ const validEfforts: CompletionOptions["reasoning_effort"][] = [
152
+ "none",
153
+ "low",
154
+ "medium",
155
+ "high",
156
+ ];
157
+ const effort = args[0] as CompletionOptions["reasoning_effort"];
158
+ if (!effort || !validEfforts.includes(effort)) {
159
+ console.log(
160
+ `Current reasoning effort: ${this.reasoningEffort ?? "(not set — model default)"}`
161
+ );
162
+ console.log(
163
+ `Usage: /reasoning_effort <${validEfforts.join("|")}>`
164
+ );
165
+ return;
166
+ }
167
+ this.reasoningEffort = effort;
168
+ const agent = (this.chatService?.getContext()?.selectedAgent as BaseAgent);
169
+ if (agent) agent.setReasoningEffort(effort);
170
+ console.log(`Reasoning effort set to: ${effort}`);
171
+ },
172
+ },
173
+ {
174
+ name: "effort",
175
+ description: "Alias for /reasoning_effort",
176
+ handler: async (args: string[]): Promise<void> => {
177
+ const validEfforts: CompletionOptions["reasoning_effort"][] = [
178
+ "none",
179
+ "low",
180
+ "medium",
181
+ "high",
182
+ ];
183
+ const effort = args[0] as CompletionOptions["reasoning_effort"];
184
+ if (!effort || !validEfforts.includes(effort)) {
185
+ console.log(
186
+ `Current reasoning effort: ${this.reasoningEffort ?? "(not set — model default)"}`
187
+ );
188
+ console.log(
189
+ `Usage: /effort <${validEfforts.join("|")}>`
190
+ );
191
+ return;
192
+ }
193
+ this.reasoningEffort = effort;
194
+ const agent = (this.chatService?.getContext()?.selectedAgent as BaseAgent);
195
+ if (agent) agent.setReasoningEffort(effort);
196
+ console.log(`Reasoning effort set to: ${effort}`);
197
+ },
198
+ },
199
+ {
200
+ name: "reasoning_summary",
201
+ description:
202
+ "Toggle whether the agent displays a reasoning summary (true/false). Supported by reasoning models only.",
203
+ handler: async (args: string[]): Promise<void> => {
204
+ const val = args[0];
205
+ if (val !== "true" && val !== "false") {
206
+ console.log(
207
+ `Current reasoning summary: ${this.summarizeReasoning}`
208
+ );
209
+ console.log(`Usage: /reasoning_summary <true|false>`);
210
+ return;
211
+ }
212
+ this.summarizeReasoning = val === "true";
213
+ const agent = (this.chatService?.getContext()?.selectedAgent as BaseAgent);
214
+ if (agent) agent.setSummarizeReasoning(this.summarizeReasoning);
215
+ console.log(`Reasoning summary set to: ${this.summarizeReasoning}`);
216
+ },
217
+ },
141
218
  {
142
219
  name: "detach",
143
220
  description: "Detach from the currently attached agent",
@@ -656,22 +733,24 @@ export class AgentModule extends BaseChatModule {
656
733
  run?: boolean; // whether to run immediately
657
734
  taskId?: string; // optional pre-generated taskId
658
735
  }) {
659
- if (!agentConstructors[options.agentName as AgentName]) {
660
- throw new Error(
661
- `Agent "${
662
- options.agentName
663
- }" not found. Available agents: ${Object.keys(agentConstructors).join(
664
- ", "
665
- )}`
666
- );
667
- }
668
-
669
736
  const { input, chatHistory = [], agentName } = options;
670
737
  const agentContext = services().Agents.getAgentContext();
671
- const agent = createAgent(
672
- options.agentName as AgentName,
673
- agentContext
674
- ) as BaseAgent;
738
+
739
+ // Resolve agent: built-in agents first, then fall back to ConfigAgents from knowhow.json
740
+ let agent: BaseAgent;
741
+ if (agentConstructors[options.agentName as AgentName]) {
742
+ agent = createAgent(options.agentName as AgentName, agentContext) as BaseAgent;
743
+ } else {
744
+ // Try to find a ConfigAgent registered via loadAgentsFromConfig (from knowhow.json agents[])
745
+ try {
746
+ const configAgent = services().Agents.getAgent(options.agentName);
747
+ agent = configAgent as unknown as BaseAgent;
748
+ } catch {
749
+ throw new Error(
750
+ `Agent "${options.agentName}" not found. Available agents: ${Object.keys(agentConstructors).join(", ")} (plus any agents defined in knowhow.json)`
751
+ );
752
+ }
753
+ }
675
754
 
676
755
  let done = false;
677
756
  let output = "Done";
@@ -754,6 +833,12 @@ export class AgentModule extends BaseChatModule {
754
833
  ]);
755
834
  }
756
835
 
836
+ // Apply module-level reasoning settings to the agent
837
+ if (this.reasoningEffort !== undefined) {
838
+ agent.setReasoningEffort(this.reasoningEffort);
839
+ }
840
+ agent.setSummarizeReasoning(this.summarizeReasoning);
841
+
757
842
  // Set up message processors like in original startAgent
758
843
 
759
844
  // Register an override for askHuman to use CLI input method
@@ -832,7 +917,63 @@ export class AgentModule extends BaseChatModule {
832
917
  agent.agentEvents.on(agent.eventTypes.agentLog, agentLogHandler);
833
918
  agent.agentEvents.on(agent.eventTypes.agentStatus, agentStatusHandler);
834
919
 
920
+ // Wire tool call / tool result / agent message rendering so that all
921
+ // renderer types (including PlainRenderer for non-TTY) receive events
922
+ // even when running via the CLI `knowhow agent` command (which uses
923
+ // run:true and does NOT go through attachedAgentChatLoop / wireAgentRendering).
924
+ const toolCallHandler = (data: any) => {
925
+ this.renderer.render({
926
+ type: "toolCall",
927
+ taskId,
928
+ agentName: agent.name,
929
+ toolCall: data.toolCall,
930
+ });
931
+ };
932
+ const toolResultHandler = (data: any) => {
933
+ this.renderer.render({
934
+ type: "toolResult",
935
+ taskId,
936
+ agentName: agent.name,
937
+ toolCall: data.toolCall,
938
+ result: data.functionResp,
939
+ });
940
+ };
941
+ const agentSayHandler = (data: any) => {
942
+ this.renderer.render({
943
+ type: "agentMessage",
944
+ taskId,
945
+ agentName: agent.name,
946
+ message: data.message,
947
+ role: "assistant",
948
+ });
949
+ };
950
+ if (agent.eventTypes.toolCall) {
951
+ agent.agentEvents.on(agent.eventTypes.toolCall, toolCallHandler);
952
+ }
953
+ if (agent.eventTypes.toolUsed) {
954
+ agent.agentEvents.on(agent.eventTypes.toolUsed, toolResultHandler);
955
+ }
956
+ if (agent.eventTypes.agentSay) {
957
+ agent.agentEvents.on(agent.eventTypes.agentSay, agentSayHandler);
958
+ }
959
+ // Track the active task on the renderer so it doesn't filter out events
960
+ this.renderer.setActiveTaskId(taskId);
961
+ this.activeAgentTaskId = taskId;
962
+
835
963
  const taskCompleted = new Promise<string>((resolve) => {
964
+ // Remove tool rendering listeners
965
+ if (agent.eventTypes.toolCall) {
966
+ agent.agentEvents.removeListener(agent.eventTypes.toolCall, toolCallHandler);
967
+ }
968
+ if (agent.eventTypes.toolUsed) {
969
+ agent.agentEvents.removeListener(agent.eventTypes.toolUsed, toolResultHandler);
970
+ }
971
+ if (agent.eventTypes.agentSay) {
972
+ agent.agentEvents.removeListener(agent.eventTypes.agentSay, agentSayHandler);
973
+ }
974
+ // Clear active task on renderer
975
+ this.renderer.setActiveTaskId(undefined);
976
+ this.activeAgentTaskId = undefined;
836
977
  agent.agentEvents.once(agent.eventTypes.done, async (doneMsg) => {
837
978
  console.log("🎯 [AgentModule] Task Completed");
838
979
  done = true;
@@ -0,0 +1,218 @@
1
+ /**
2
+ * PlainRenderer - A plain-text, append-only renderer for non-TTY environments.
3
+ *
4
+ * Used automatically when stdout is not a TTY (e.g. redirected to a file,
5
+ * piped, or run in background). Emits simple timestamped log lines with no
6
+ * ANSI codes, no cursor movement, and no spinner — safe for logfiles.
7
+ *
8
+ * Activate explicitly via:
9
+ * knowhow agent --renderer plain ...
10
+ * or it is selected automatically when !process.stdout.isTTY and no renderer
11
+ * is configured.
12
+ */
13
+
14
+ import {
15
+ AgentRenderer,
16
+ LogEvent,
17
+ AgentStatusEvent,
18
+ ToolCallEvent,
19
+ ToolResultEvent,
20
+ AgentMessageEvent,
21
+ AgentDoneEvent,
22
+ RenderEvent,
23
+ } from "./types";
24
+ import { EventEmitter } from "events";
25
+
26
+ function ts(): string {
27
+ return new Date().toISOString();
28
+ }
29
+
30
+ function truncate(s: string, n: number): string {
31
+ return s.length > n ? s.slice(0, n - 1) + "…" : s;
32
+ }
33
+
34
+ function formatArgs(raw: string): string {
35
+ try {
36
+ const obj = JSON.parse(raw || "{}");
37
+ const entries = Object.entries(obj);
38
+ if (!entries.length) return "{}";
39
+ return entries
40
+ .map(([k, v]) => {
41
+ const val = typeof v === "string" ? v : JSON.stringify(v);
42
+ return `${k}=${truncate(val, 80)}`;
43
+ })
44
+ .join(", ");
45
+ } catch {
46
+ return truncate(raw || "", 120);
47
+ }
48
+ }
49
+
50
+ function formatResult(result: any): string {
51
+ try {
52
+ const s = typeof result === "string" ? result : JSON.stringify(result);
53
+ return truncate(s.replace(/\s+/g, " "), 200);
54
+ } catch {
55
+ return truncate(String(result), 200);
56
+ }
57
+ }
58
+
59
+ export class PlainRenderer implements AgentRenderer {
60
+ private activeTaskId: string | undefined;
61
+ private emitter = new EventEmitter();
62
+ private paused = false;
63
+ private bufferedEvents: RenderEvent[] = [];
64
+
65
+ setActiveTaskId(taskId: string | undefined): void {
66
+ this.activeTaskId = taskId;
67
+ }
68
+
69
+ getActiveTaskId(): string | undefined {
70
+ return this.activeTaskId;
71
+ }
72
+
73
+ private isActiveTask(taskId: string): boolean {
74
+ if (!this.activeTaskId) return true;
75
+ return taskId === this.activeTaskId;
76
+ }
77
+
78
+ onLog(handler: (event: LogEvent) => void): void {
79
+ this.emitter.on("log", handler);
80
+ }
81
+
82
+ onAgentStatus(handler: (event: AgentStatusEvent) => void): void {
83
+ this.emitter.on("agentStatus", handler);
84
+ }
85
+
86
+ onToolCall(handler: (event: ToolCallEvent) => void): void {
87
+ this.emitter.on("toolCall", handler);
88
+ }
89
+
90
+ onToolResult(handler: (event: ToolResultEvent) => void): void {
91
+ this.emitter.on("toolResult", handler);
92
+ }
93
+
94
+ onAgentMessage(handler: (event: AgentMessageEvent) => void): void {
95
+ this.emitter.on("agentMessage", handler);
96
+ }
97
+
98
+ onAgentDone(handler: (event: AgentDoneEvent) => void): void {
99
+ this.emitter.on("agentDone", handler);
100
+ }
101
+
102
+ pause(): void {
103
+ this.paused = true;
104
+ }
105
+
106
+ resume(): void {
107
+ this.paused = false;
108
+ const buffered = this.bufferedEvents.splice(0);
109
+ for (const event of buffered) {
110
+ this.render(event);
111
+ }
112
+ }
113
+
114
+ render(event: RenderEvent): void {
115
+ if (!this.isActiveTask(event.taskId)) return;
116
+
117
+ if (this.paused) {
118
+ this.bufferedEvents.push(event);
119
+ return;
120
+ }
121
+
122
+ this.emitter.emit(event.type, event);
123
+
124
+ switch (event.type) {
125
+ case "log":
126
+ this._log(event);
127
+ break;
128
+ case "agentStatus":
129
+ this._status(event);
130
+ break;
131
+ case "toolCall":
132
+ this._toolCall(event);
133
+ break;
134
+ case "toolResult":
135
+ this._toolResult(event);
136
+ break;
137
+ case "agentMessage":
138
+ this._message(event);
139
+ break;
140
+ case "agentDone":
141
+ this._done(event);
142
+ break;
143
+ }
144
+ }
145
+
146
+ private _write(line: string): void {
147
+ process.stdout.write(line + "\n");
148
+ }
149
+
150
+ private _log(e: LogEvent): void {
151
+ const level = e.level.toUpperCase().padEnd(5);
152
+ this._write(`${ts()} [${level}] [${e.agentName}] ${e.message}`);
153
+ }
154
+
155
+ private _status(e: AgentStatusEvent): void {
156
+ const cost = `$${e.details.totalCostUsd.toFixed(3)}`;
157
+ const elapsed = `${Math.floor(e.details.elapsedMs / 1000)}s`;
158
+ const parts = [`cost=${cost}`, `elapsed=${elapsed}`];
159
+ if (e.details.remainingTurns !== undefined) {
160
+ parts.push(`turns_left=${e.details.remainingTurns}`);
161
+ }
162
+ if (e.details.remainingTimeMs !== undefined) {
163
+ parts.push(`time_left=${Math.floor(e.details.remainingTimeMs / 1000)}s`);
164
+ }
165
+ this._write(
166
+ `${ts()} [STATUS] [${e.agentName}] ${e.statusMessage} | ${parts.join(" | ")}`
167
+ );
168
+ }
169
+
170
+ private _toolCall(e: ToolCallEvent): void {
171
+ const args = formatArgs(e.toolCall.function.arguments);
172
+ this._write(
173
+ `${ts()} [TOOL->] [${e.agentName}] ${e.toolCall.function.name}(${args})`
174
+ );
175
+ }
176
+
177
+ private _toolResult(e: ToolResultEvent): void {
178
+ const result = formatResult(e.result);
179
+ this._write(
180
+ `${ts()} [TOOL<-] [${e.agentName}] ${e.toolCall.function.name} => ${result}`
181
+ );
182
+ }
183
+
184
+ private _message(e: AgentMessageEvent): void {
185
+ if (e.role !== "assistant") return;
186
+ // Print the message with line-by-line prefix
187
+ const lines = e.message.split("\n");
188
+ for (const line of lines) {
189
+ if (line.trim()) {
190
+ this._write(`${ts()} [MSG ] [${e.agentName}] ${line}`);
191
+ }
192
+ }
193
+ }
194
+
195
+ private _done(e: AgentDoneEvent): void {
196
+ const cost = e.totalCost ? ` | total_cost=$${e.totalCost.toFixed(4)}` : "";
197
+ this._write(`${ts()} [DONE ] [${e.agentName}] Agent finished${cost}`);
198
+ // Print final output lines
199
+ const lines = e.output.split("\n");
200
+ for (const line of lines) {
201
+ if (line.trim()) {
202
+ this._write(`${ts()} [OUT ] [${e.agentName}] ${line}`);
203
+ }
204
+ }
205
+ }
206
+
207
+ logMessages(events: RenderEvent[], count: number = 20): void {
208
+ const recent = events.slice(-count);
209
+ this._write(`\n[LOGS ] Last ${recent.length} events:`);
210
+ this._write("-".repeat(80));
211
+ for (const event of recent) {
212
+ this.render(event);
213
+ }
214
+ this._write("-".repeat(80));
215
+ }
216
+ }
217
+
218
+ export default PlainRenderer;
@@ -1,5 +1,6 @@
1
1
  export * from "./types";
2
2
  export { ConsoleRenderer } from "./ConsoleRenderer";
3
3
  export { FancyRenderer } from "./FancyRenderer";
4
+ export { PlainRenderer } from "./PlainRenderer";
4
5
  export { CompactRenderer } from "./CompactRenderer";
5
6
  export { loadRenderer, loadRootModule, loadChatModule } from "./loadRenderer";
@@ -12,6 +12,7 @@ import path from "path";
12
12
  import fs from "fs";
13
13
  import { AgentRenderer } from "./types";
14
14
  import { ConsoleRenderer } from "./ConsoleRenderer";
15
+ import { PlainRenderer } from "./PlainRenderer";
15
16
  import { FancyRenderer } from "./FancyRenderer";
16
17
  import { CompactRenderer } from "./CompactRenderer";
17
18
 
@@ -19,6 +20,7 @@ import { CompactRenderer } from "./CompactRenderer";
19
20
  const BUILTIN_RENDERERS: Record<string, () => AgentRenderer> = {
20
21
  basic: () => new ConsoleRenderer(),
21
22
  fancy: () => new FancyRenderer(),
23
+ plain: () => new PlainRenderer(),
22
24
  compact: () => new CompactRenderer(),
23
25
  };
24
26
 
package/src/chat.ts CHANGED
@@ -21,7 +21,7 @@ import { getConfig } from "./config.js";
21
21
  import { loadRenderer, loadRootModule, loadChatModule } from "./chat/renderer/loadRenderer.js";
22
22
  import { AgentModule } from "./chat/modules/AgentModule.js";
23
23
 
24
- async function main() {
24
+ async function main(initialCommand?: string) {
25
25
  try {
26
26
  // Load configuration and plugins
27
27
  let config;
@@ -108,7 +108,7 @@ async function main() {
108
108
  }
109
109
 
110
110
  // Start the chat loop
111
- await chatService.startChatLoop();
111
+ await chatService.startChatLoop(initialCommand);
112
112
  } catch (error) {
113
113
  console.error("Error starting chat:", error);
114
114
  process.exit(1);
package/src/cli.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node --no-node-snapshot
2
+ // Mark this process as running the Knowhow CLI (not just the SDK)
3
+ process.env.KNOWHOW_CLI = "1";
2
4
  import { Command } from "commander";
3
5
  import { version } from "../package.json";
4
6
  import { logger } from "./logger";
@@ -38,6 +40,10 @@ import {
38
40
  } from "./commands/misc";
39
41
  import { addConvertCommand } from "./commands/convert";
40
42
  import { addReplayCommand } from "./commands/replay";
43
+ import { addBehaviorsCommand } from "./commands/behaviors";
44
+ import { addSkillsCommand } from "./commands/skills";
45
+ import { addAgentsCommand } from "./commands/agents";
46
+ import { addHashCommand } from "./commands/hash";
41
47
 
42
48
  // Handle unhandled promise rejections gracefully — particularly from MCP SDK
43
49
  // which fires errors via event emitters that can bypass Promise.allSettled.
@@ -102,6 +108,10 @@ async function main() {
102
108
  addMcpCommands(program);
103
109
  addConvertCommand(program);
104
110
  addReplayCommand(program);
111
+ addBehaviorsCommand(program);
112
+ addSkillsCommand(program);
113
+ addAgentsCommand(program);
114
+ addHashCommand(program);
105
115
 
106
116
  // Load global modules early (before parse) so they can register CLI subcommands.
107
117
  // We pass only the Program in context — no services are spun up at this stage.
@@ -1,6 +1,6 @@
1
1
  import Anthropic from "@anthropic-ai/sdk";
2
2
  import { wait } from "../utils";
3
- import { AnthropicTextPricing } from "./pricing";
3
+ import { AnthropicTextPricing, AnthropicModels } from "./pricing";
4
4
  import { ContextLimits } from "./contextLimits";
5
5
  import { ModelModality } from "./types";
6
6
  import { Models } from "../types";
@@ -24,6 +24,16 @@ import {
24
24
 
25
25
  type MessageParam = Anthropic.MessageParam;
26
26
  type Usage = Anthropic.Usage;
27
+ type ThinkingParam = Anthropic.Messages.ThinkingConfigParam;
28
+
29
+ const alwaysOnModels: readonly string[] = [
30
+ AnthropicModels.Sonnet5, AnthropicModels.Fable5,
31
+ AnthropicModels.Mythos5, AnthropicModels.MythosPreview,
32
+ ];
33
+ const optInModels: readonly string[] = [
34
+ AnthropicModels.Opus4_8, AnthropicModels.Opus4_7,
35
+ AnthropicModels.Opus4_6, AnthropicModels.Sonnet4_6,
36
+ ];
27
37
 
28
38
  export class GenericAnthropicClient implements GenericClient {
29
39
  private client: Anthropic;
@@ -48,6 +58,36 @@ export class GenericAnthropicClient implements GenericClient {
48
58
  }
49
59
  }
50
60
 
61
+ /**
62
+ * Determine the Anthropic `thinking` param from CompletionOptions.
63
+ * Returns undefined if the model does not support thinking.
64
+ */
65
+ getThinkingParam(
66
+ model: string,
67
+ reasoning_effort?: CompletionOptions["reasoning_effort"],
68
+ reasoning_summary?: boolean,
69
+ ): ThinkingParam | undefined {
70
+ const effortIsNone = reasoning_effort === "none";
71
+ const withSummary = reasoning_summary ? ({ display: "summarized" } as const) : {};
72
+
73
+ if (alwaysOnModels.includes(model)) {
74
+ return effortIsNone
75
+ ? { type: "disabled" }
76
+ : { type: "adaptive", ...withSummary };
77
+ }
78
+
79
+ if (optInModels.includes(model)) {
80
+ // Only enable when explicitly requested (not "none" and not undefined).
81
+ if (!effortIsNone && reasoning_effort !== undefined) {
82
+ return { type: "adaptive", ...withSummary };
83
+ }
84
+ return undefined;
85
+ }
86
+
87
+ // Model does not support thinking.
88
+ return undefined;
89
+ }
90
+
51
91
  /**
52
92
  * Clean JSON Schema for Anthropic API compatibility.
53
93
  * Removes unsupported fields like additionalProperties, $ref, $defs, positional.
@@ -360,6 +400,13 @@ export class GenericAnthropicClient implements GenericClient {
360
400
  options: CompletionOptions
361
401
  ): Promise<CompletionResponse> {
362
402
  const longTtl = !!options.long_ttl_cache;
403
+
404
+ const thinkingParam = this.getThinkingParam(
405
+ options.model,
406
+ options.reasoning_effort,
407
+ options.reasoning_summary,
408
+ );
409
+
363
410
  const systemMessage = options.messages
364
411
  .filter((msg) => msg.role === "system")
365
412
  .map((msg) => msg.content || "")
@@ -382,6 +429,7 @@ export class GenericAnthropicClient implements GenericClient {
382
429
  ]
383
430
  : undefined,
384
431
  max_tokens: options.max_tokens || 8000,
432
+ ...(thinkingParam && { thinking: thinkingParam }),
385
433
  ...(tools.length && {
386
434
  tool_choice: { type: "auto" },
387
435
  tools,
@@ -431,6 +479,16 @@ export class GenericAnthropicClient implements GenericClient {
431
479
  total_tokens: (response.usage.input_tokens ?? 0) + (response.usage.output_tokens ?? 0),
432
480
  cache_creation_input_tokens: response.usage.cache_creation_input_tokens ?? 0,
433
481
  cache_read_input_tokens: response.usage.cache_read_input_tokens ?? 0,
482
+ ...((response.usage as any).thinking_input_tokens != null ||
483
+ (response.usage as any).output_tokens_details?.thinking_tokens != null
484
+ ? {
485
+ output_tokens_details: {
486
+ thinking_tokens:
487
+ (response.usage as any).thinking_input_tokens ??
488
+ (response.usage as any).output_tokens_details?.thinking_tokens ?? 0,
489
+ },
490
+ }
491
+ : {}),
434
492
  } : undefined,
435
493
  usd_cost: this.calculateCost(options.model, response.usage),
436
494
  };
@@ -461,7 +519,15 @@ export class GenericAnthropicClient implements GenericClient {
461
519
  const rawP = this.pricesPerMillion()[model];
462
520
  // Fall back to pricing file for unknown/newer models
463
521
  const fallback = AnthropicTextPricing[model as keyof typeof AnthropicTextPricing];
464
- const p: any = rawP || fallback || undefined;
522
+ // Second fallback: short aliases like "claude-sonnet-4-5" don't have their
523
+ // own entry but map to a dated version like "claude-sonnet-4-5-20250929".
524
+ // Find the first pricing entry whose key starts with the short model name.
525
+ const aliasFallback = !rawP && !fallback
526
+ ? Object.entries(AnthropicTextPricing).find(([key]) =>
527
+ key.startsWith(model + "-")
528
+ )?.[1]
529
+ : undefined;
530
+ const p: any = rawP || fallback || aliasFallback || undefined;
465
531
  if (!p) return undefined;
466
532
 
467
533
  const inputTokens = usage.input_tokens ?? 0;
@@ -1,5 +1,5 @@
1
1
  import { HttpClient } from "./http";
2
- import { FireworksTextPricing } from "./pricing/fireworks";
2
+ import { FireworksTextPricing, FireworksEmbeddingPricing } from "./pricing/fireworks";
3
3
  import { CompletionOptions, CompletionResponse } from "./types";
4
4
 
5
5
  type ModelInfo = { id: string; object: string; owned_by: string };
@@ -22,6 +22,15 @@ export class GenericFireworksClient extends HttpClient {
22
22
  * Our pricing map is the authoritative source of truth for available models.
23
23
  */
24
24
  async getModels(_type = "all"): Promise<ModelInfo[]> {
25
+ // Return embedding/reranker models when requested
26
+ if (_type === "embedding") {
27
+ return Object.keys(FireworksEmbeddingPricing).map((id) => ({
28
+ id,
29
+ object: "model",
30
+ owned_by: "fireworks",
31
+ }));
32
+ }
33
+
25
34
  return Object.keys(FireworksTextPricing).map((id) => ({
26
35
  id,
27
36
  object: "model",
@@ -310,9 +310,11 @@ export class AIClient {
310
310
 
311
311
  if (!client) {
312
312
  if (entry.provider === "knowhow") {
313
- console.warn(
314
- `⚠️ Knowhow provider is not logged in. Run 'knowhow login' to enable Knowhow models.`
315
- );
313
+ if (process.env.KNOWHOW_CLI) {
314
+ console.warn(
315
+ `⚠️ Knowhow provider is not logged in. Run 'knowhow login' to enable Knowhow models.`
316
+ );
317
+ }
316
318
  }
317
319
  continue;
318
320
  }
@@ -1,5 +1,6 @@
1
1
  import { HttpClient } from "./http";
2
2
  import { MetaTextPricing } from "./pricing/meta";
3
+ import { ModelModality } from "./types";
3
4
 
4
5
  /**
5
6
  * Meta Model API client — OpenAI-compatible API
@@ -15,4 +16,12 @@ export class GenericMetaClient extends HttpClient {
15
16
  if (apiKey) this.setJwt(apiKey);
16
17
  this.setPrices(MetaTextPricing);
17
18
  }
19
+
20
+ /**
21
+ * Meta Model API only provides chat completion models — no embeddings, images, audio, or video.
22
+ */
23
+ async getModels(type: ModelModality | "all" = "all") {
24
+ if (type === "embedding") return [];
25
+ return super.getModels(type as string);
26
+ }
18
27
  }