@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
@@ -107,7 +107,11 @@ export class GenericOpenAiClient implements GenericClient {
107
107
  }
108
108
 
109
109
  resolveReasoningEffort(options: CompletionOptions): "low" | "medium" | "high" {
110
- return options.reasoning_effort ?? this.detectReasoningEffort(options.messages);
110
+ const effort = options.reasoning_effort ?? this.detectReasoningEffort(options.messages);
111
+ // "none" is not a valid SDK ReasoningEffort — callers that need "none" should
112
+ // check isReasoningDisabled() separately and skip the reasoning params.
113
+ if (effort === "none") return "low";
114
+ return effort;
111
115
  }
112
116
 
113
117
  /**
@@ -116,7 +120,8 @@ export class GenericOpenAiClient implements GenericClient {
116
120
  * If the requested level is not supported, picks the lowest supported level.
117
121
  */
118
122
  resolveReasoningEffortForModel(options: CompletionOptions): string {
119
- const requested = options.reasoning_effort ?? this.detectReasoningEffort(options.messages);
123
+ const raw = options.reasoning_effort ?? this.detectReasoningEffort(options.messages);
124
+ const requested = raw === "none" ? "none" : raw;
120
125
  const pricing = OpenAiTextPricing[options.model];
121
126
  const supportedLevels = pricing?.reasoningLevels;
122
127
  if (!supportedLevels || supportedLevels.length === 0) {
@@ -130,6 +135,11 @@ export class GenericOpenAiClient implements GenericClient {
130
135
  return supportedLevels[0];
131
136
  }
132
137
 
138
+ /** Returns true when the caller explicitly requested thinking/reasoning to be disabled. */
139
+ isReasoningDisabled(options: CompletionOptions): boolean {
140
+ return options.reasoning_effort === "none";
141
+ }
142
+
133
143
  async createChatCompletion(
134
144
  options: CompletionOptions
135
145
  ): Promise<CompletionResponse> {
@@ -154,7 +164,7 @@ export class GenericOpenAiClient implements GenericClient {
154
164
  model: options.model,
155
165
  messages: openaiMessages,
156
166
  max_tokens: options.max_tokens,
157
- ...(OpenAiReasoningModels.includes(options.model) && {
167
+ ...(OpenAiReasoningModels.includes(options.model) && !this.isReasoningDisabled(options) && {
158
168
  max_tokens: undefined,
159
169
  max_completion_tokens: Math.max(options.max_tokens ?? 0, 16_000),
160
170
  reasoning_effort: this.resolveReasoningEffort(options),
@@ -186,6 +196,11 @@ export class GenericOpenAiClient implements GenericClient {
186
196
  prompt_tokens_details: {
187
197
  cached_tokens: response.usage.prompt_tokens_details?.cached_tokens ?? 0,
188
198
  },
199
+ ...(response.usage.completion_tokens_details?.reasoning_tokens != null && {
200
+ output_tokens_details: {
201
+ reasoning_tokens: response.usage.completion_tokens_details.reasoning_tokens,
202
+ },
203
+ }),
189
204
  } : undefined,
190
205
  usd_cost: usdCost,
191
206
  };
@@ -350,9 +365,12 @@ export class GenericOpenAiClient implements GenericClient {
350
365
  input,
351
366
  ...(instructions && { instructions }),
352
367
  // Don't limit max_output_tokens for Responses API - codex truncates tool call arguments when limited
353
- ...(OpenAiReasoningModels.includes(options.model) && {
368
+ ...(OpenAiReasoningModels.includes(options.model) && !this.isReasoningDisabled(options) && {
354
369
  max_output_tokens: Math.max(options.max_tokens || 0, 16000),
355
- reasoning: { effort: this.resolveReasoningEffortForModel(options) },
370
+ reasoning: {
371
+ effort: this.resolveReasoningEffortForModel(options),
372
+ ...(options.reasoning_summary && { summary: "auto" }),
373
+ },
356
374
  }),
357
375
  ...(tools?.length && {
358
376
  tools,
@@ -372,6 +390,11 @@ export class GenericOpenAiClient implements GenericClient {
372
390
  cached_tokens:
373
391
  response.usage.input_tokens_details?.cached_tokens ?? 0,
374
392
  },
393
+ ...((response.usage as any).output_tokens_details?.reasoning_tokens != null && {
394
+ output_tokens_details: {
395
+ reasoning_tokens: (response.usage as any).output_tokens_details.reasoning_tokens,
396
+ },
397
+ }),
375
398
  }
376
399
  : undefined;
377
400
 
@@ -10,6 +10,13 @@ import { completions, ModelCatalogEntry, ModelPricing } from "./types";
10
10
  export const AnthropicModels = {
11
11
  // Active models
12
12
  Fable5: "claude-fable-5",
13
+ // Short aliases (without date suffix) — Anthropic resolves these to the latest
14
+ // dated version. Pricing entries for the short aliases are added to the pricing
15
+ // table below so cost tracking works regardless of whether the user specifies
16
+ // the full dated name or the short alias.
17
+ Sonnet4_5Short: "claude-sonnet-4-5",
18
+ Haiku4_5Short: "claude-haiku-4-5",
19
+ Opus4_5Short: "claude-opus-4-5",
13
20
  Sonnet5: "claude-sonnet-5",
14
21
  Mythos5: "claude-mythos-5",
15
22
  MythosPreview: "claude-mythos-preview",
@@ -101,6 +108,10 @@ export const AnthropicTextPricing: Record<string, ModelPricing> = {
101
108
  [AnthropicModels.Opus4_1]: { input: 15.0, cache_write: 18.75, cache_hit: 1.5, output: 75.0 },
102
109
  [AnthropicModels.Sonnet4_5]: { input: 3.0, input_gt_200k: 6.0, cache_write: 3.75, cache_hit: 0.3, output: 15.0, output_gt_200k: 22.5 },
103
110
  [AnthropicModels.Haiku4_5]: { input: 1.0, cache_write: 1.25, cache_hit: 0.1, output: 5.0 },
111
+ // Short aliases — same pricing as their dated counterparts
112
+ [AnthropicModels.Sonnet4_5Short]: { input: 3.0, input_gt_200k: 6.0, cache_write: 3.75, cache_hit: 0.3, output: 15.0, output_gt_200k: 22.5 },
113
+ [AnthropicModels.Haiku4_5Short]: { input: 1.0, cache_write: 1.25, cache_hit: 0.1, output: 5.0 },
114
+ [AnthropicModels.Opus4_5Short]: { input: 5.0, cache_write: 6.25, cache_hit: 0.5, output: 25.0 },
104
115
  // Deprecated — pricing retained for cost tracking
105
116
  [AnthropicModels.Opus4]: { input: 15.0, cache_write: 18.75, cache_hit: 1.5, output: 75.0, deprecated: true, deprecationDate: "2026-06-15" },
106
117
  [AnthropicModels.Sonnet4]: { input: 3.0, input_gt_200k: 6.0, cache_write: 3.75, cache_hit: 0.3, output: 15.0, output_gt_200k: 22.5, deprecated: true, deprecationDate: "2026-06-15" },
@@ -40,3 +40,15 @@ export const FireworksTextPricing: Record<string, ModelPricing> = {
40
40
  // OpenAI OSS
41
41
  "accounts/fireworks/models/gpt-oss-120b": { input: 0.15, cache_hit: 0.01, output: 0.60 },
42
42
  };
43
+
44
+ /**
45
+ * Fireworks AI embedding/reranker model pricing (USD per 1M tokens)
46
+ * Source: https://fireworks.ai/pricing
47
+ */
48
+ export const FireworksEmbeddingPricing: Record<string, ModelPricing> = {
49
+ // Qwen3 Embedding 8B — $0.10/M tokens, context 40k
50
+ "accounts/fireworks/models/qwen3-embedding-8b": { input: 0.10, output: 0.10 },
51
+
52
+ // Qwen3 Reranker 8B — $0.20/M tokens, context 40k
53
+ "accounts/fireworks/models/qwen3-reranker-8b": { input: 0.20, output: 0.20 },
54
+ };
@@ -167,6 +167,9 @@ export const OpenAiResponsesOnlyModels: string[] = [
167
167
  OpenAiModels.GPT_55_Pro,
168
168
  OpenAiModels.GPT_55,
169
169
  OpenAiModels.GPT_5_Pro,
170
+ OpenAiModels.GPT_56_Sol,
171
+ OpenAiModels.GPT_56_Terra,
172
+ OpenAiModels.GPT_56_Luna,
170
173
  OpenAiModels.o1,
171
174
  OpenAiModels.o1_Pro,
172
175
  ];
@@ -86,10 +86,26 @@ export interface CompletionOptions extends RetryOptions {
86
86
  tools?: Tool[];
87
87
  tool_choice?: "auto" | "none";
88
88
  max_tokens?: number;
89
- /** Reasoning effort level for models that support it.
90
- * Maps to: OpenAI reasoning_effort, xAI reasoning.effort, Gemini thinkingLevel/thinkingBudget, Anthropic thinking budget.
91
- * "low" = minimal thinking, "medium" = balanced, "high" = maximum reasoning */
92
- reasoning_effort?: "low" | "medium" | "high";
89
+ /**
90
+ * Reasoning effort level for models that support it.
91
+ * Maps to: OpenAI reasoning_effort/reasoning.effort, xAI reasoning.effort,
92
+ * Gemini thinkingLevel/thinkingBudget, Anthropic thinking mode.
93
+ *
94
+ * "none" = disable thinking entirely (Anthropic: thinking.type="disabled",
95
+ * OpenAI: reasoning.effort="none"). Only supported by models that
96
+ * allow disabling thinking (e.g. claude-sonnet-5).
97
+ * "low" = minimal thinking
98
+ * "medium" = balanced (default for most models)
99
+ * "high" = maximum reasoning
100
+ */
101
+ reasoning_effort?: "none" | "low" | "medium" | "high";
102
+ /**
103
+ * When true, request a summarized view of the model's thinking process.
104
+ * - Anthropic: thinking.display = "summarized"
105
+ * - OpenAI: reasoning.summary = "auto"
106
+ * Defaults to false (no summary displayed).
107
+ */
108
+ reasoning_summary?: boolean;
93
109
  /**
94
110
  * When true, hints to the client that this task is long-running and it should
95
111
  * use a long-TTL cache where available.
@@ -115,6 +131,13 @@ export interface TokenUsage {
115
131
  output_tokens?: number;
116
132
  /** Convenience total (prompt + completion) */
117
133
  total_tokens?: number;
134
+ /** Breakdown of output tokens — thinking/reasoning tokens are billed as output
135
+ * but should NOT be counted toward the visible-content heuristics. */
136
+ output_tokens_details?: {
137
+ /** Anthropic: thinking_tokens, OpenAI: reasoning_tokens */
138
+ thinking_tokens?: number;
139
+ reasoning_tokens?: number;
140
+ };
118
141
  /** Cache details */
119
142
  prompt_tokens_details?: {
120
143
  /** Tokens served from the prompt cache (reduces cost) */
@@ -76,9 +76,9 @@ export class GenericXAIClient implements GenericClient {
76
76
  model: options.model,
77
77
  messages: xaiMessages,
78
78
  max_tokens: options.max_tokens,
79
- ...(XaiReasoningModels.includes(options.model) && options.reasoning_effort && {
79
+ ...(XaiReasoningModels.includes(options.model) && options.reasoning_effort && options.reasoning_effort !== "none" && {
80
80
  // grok-3-mini models support reasoning_effort: "low" | "medium" | "high"
81
- reasoning_effort: options.reasoning_effort,
81
+ reasoning_effort: options.reasoning_effort as "low" | "medium" | "high",
82
82
  }),
83
83
  ...(options.tools && {
84
84
  tools: options.tools,
@@ -1,13 +1,31 @@
1
1
  import { Command } from "commander";
2
2
  import { readPromptFile } from "../ai";
3
+ import { BehaviorsService } from "../services/BehaviorsService";
3
4
  import { AgentModule } from "../chat/modules/AgentModule";
4
5
  import { AskModule } from "../chat/modules/AskModule";
5
6
  import { SearchModule } from "../chat/modules/SearchModule";
6
7
  import { SessionsModule } from "../chat/modules/SessionsModule";
7
8
  import { SetupModule } from "../chat/modules/SetupModule";
9
+ import { PlainRenderer } from "../chat/renderer/PlainRenderer";
8
10
  import { loadRenderer } from "../chat/renderer/loadRenderer";
9
11
  import { getConfig } from "../config";
10
12
 
13
+ async function setupRenderer(chatService: any, rendererSpecifier: string): Promise<void> {
14
+ const resolved =
15
+ rendererSpecifier === "basic" && !process.stdout.isTTY ? "plain" : rendererSpecifier;
16
+ try {
17
+ const renderer = await loadRenderer(resolved);
18
+ chatService.setContext({ renderer });
19
+ } catch (err: any) {
20
+ console.warn(`⚠ Could not load renderer "${resolved}": ${err.message}`);
21
+ console.warn(" Falling back to basic renderer.");
22
+ try {
23
+ const fallback = !process.stdout.isTTY ? new PlainRenderer() : await loadRenderer("basic");
24
+ chatService.setContext({ renderer: fallback });
25
+ } catch (_) {}
26
+ }
27
+ }
28
+
11
29
  async function readStdin(): Promise<string> {
12
30
  return new Promise((resolve) => {
13
31
  let data = "";
@@ -27,24 +45,6 @@ async function readStdin(): Promise<string> {
27
45
  });
28
46
  }
29
47
 
30
- /**
31
- * Set up the renderer on the chat service context.
32
- * Priority: CLI --renderer flag > config.chat.renderer > "basic" (ConsoleRenderer)
33
- */
34
- async function setupRenderer(chatService: any, rendererSpecifier: string): Promise<void> {
35
- try {
36
- const renderer = await loadRenderer(rendererSpecifier);
37
- chatService.setContext({ renderer });
38
- } catch (err: any) {
39
- console.warn(`⚠ Could not load renderer "${rendererSpecifier}": ${err.message}`);
40
- console.warn(" Falling back to basic renderer.");
41
- try {
42
- const fallback = await loadRenderer("basic");
43
- chatService.setContext({ renderer: fallback });
44
- } catch (_) {}
45
- }
46
- }
47
-
48
48
  export function addAgentCommand(program: Command, getChatService: () => any): void {
49
49
  program
50
50
  .command("agent")
@@ -67,6 +67,14 @@ export function addAgentCommand(program: Command, getChatService: () => any): vo
67
67
  )
68
68
  .option("--message-id <messageId>", "Knowhow message ID for task tracking")
69
69
  .option("--sync-fs", "Enable filesystem-based synchronization")
70
+ .option(
71
+ "--behavior-id <id>",
72
+ "Force a specific behavior by its ID (skips trigger matching)"
73
+ )
74
+ .option(
75
+ "--behavior-file <path>",
76
+ "Force a specific behavior by its file path (skips trigger matching)"
77
+ )
70
78
  .option(
71
79
  "--task-id <taskId>",
72
80
  "Pre-generated task ID (used with --sync-fs for predictable agent directory path)"
@@ -92,6 +100,7 @@ export function addAgentCommand(program: Command, getChatService: () => any): vo
92
100
  try { config = await getConfig(); } catch (_) {}
93
101
  const rendererSpecifier = options.renderer ?? config.chat?.renderer ?? "basic";
94
102
  await setupRenderer(chatService, rendererSpecifier);
103
+
95
104
  const agentModule = new AgentModule();
96
105
 
97
106
  if (options.resume) {
@@ -131,11 +140,54 @@ export function addAgentCommand(program: Command, getChatService: () => any): vo
131
140
  }
132
141
 
133
142
  await agentModule.initialize(chatService);
143
+
144
+ // Match a behavior from local disk (.knowhow/behaviors/)
145
+ let behaviorSystemPrompt: string | undefined;
146
+ let behaviorModel: string | undefined;
147
+ const behaviorsSvc = new BehaviorsService();
148
+
149
+ let matchedBehavior = null;
150
+
151
+ if (options.behaviorFile) {
152
+ // Load behavior directly from a file path
153
+ matchedBehavior = behaviorsSvc.loadBehaviorFromFile(options.behaviorFile);
154
+ if (matchedBehavior) {
155
+ console.log(`🎯 Using behavior from file: ${options.behaviorFile}`);
156
+ } else {
157
+ console.error(`❌ Could not load behavior from file: ${options.behaviorFile}`);
158
+ process.exit(1);
159
+ }
160
+ } else if (options.behaviorId) {
161
+ // Load behavior by ID (searches local disk behaviors)
162
+ matchedBehavior = behaviorsSvc.findBehaviorById(options.behaviorId);
163
+ if (matchedBehavior) {
164
+ console.log(`🎯 Using behavior by ID: ${options.behaviorId} (${matchedBehavior.name})`);
165
+ } else {
166
+ console.error(`❌ No behavior found with ID: ${options.behaviorId}`);
167
+ process.exit(1);
168
+ }
169
+ } else {
170
+ // Default: match by trigger text
171
+ matchedBehavior = behaviorsSvc.matchBehaviorLocal(input);
172
+ }
173
+
174
+ if (matchedBehavior) {
175
+ console.log(`🎯 Matched behavior: ${matchedBehavior.name}`);
176
+ if (matchedBehavior.instructions) {
177
+ behaviorSystemPrompt = matchedBehavior.instructions;
178
+ }
179
+ if (matchedBehavior.model) {
180
+ behaviorModel = matchedBehavior.model;
181
+ }
182
+ }
183
+
134
184
  const { taskCompleted } = await agentModule.setupAgent({
135
185
  ...options,
136
186
  input,
137
187
  maxTimeLimit: parseInt(options.maxTimeLimit, 10),
138
188
  maxSpendLimit: parseFloat(options.maxSpendLimit),
189
+ ...(behaviorSystemPrompt ? { systemPrompt: behaviorSystemPrompt } : {}),
190
+ ...(behaviorModel ? { model: behaviorModel } : {}),
139
191
  run: true,
140
192
  });
141
193
  await taskCompleted;