Skip to content
Kward Search API index

Class: Kward::Tools::ContextForTask

Inherits:
Base
  • Object
show all
Defined in:
lib/kward/tools/context_for_task.rb

Overview

Builds a compact, task-shaped bundle from likely workspace files.

Constant Summary collapse

DEFAULT_BUDGET =
4_000
MAX_BUDGET =
20_000
MAX_FILES =
8
MAX_MATCHES_PER_FILE =
8
DEFAULT_EXTENSIONS =
%w[.rb .js .jsx .ts .tsx .py .go .rs .java .cs .cpp .c .h .hpp .md .yml .yaml .json .toml].freeze
SKIP_DIRECTORIES =
%w[.git .yardoc _yardoc node_modules vendor tmp log coverage dist build .bundle].freeze

Instance Attribute Summary

Attributes inherited from Base

#name

Instance Method Summary collapse

Methods inherited from Base

#schema

Constructor Details

#initialize(workspace:, workspace_targets: nil) ⇒ ContextForTask

Builds the tool schema and stores the execution dependency.



19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/kward/tools/context_for_task.rb', line 19

def initialize(workspace:, workspace_targets: nil)
  configure_workspace_targets(workspace, workspace_targets)
  super(
    "context_for_task",
    "Build focused workspace context for a task from outlines and matching excerpts within a byte budget.",
    properties: targeted_properties(
      budget: { type: "integer", description: "Approximate byte budget for the returned context. Default 4000, maximum 20000." },
      paths: { type: "array", items: { type: "string" }, description: "Optional workspace-relative files or directories to focus." },
      task: { type: "string", description: "Task or question to gather context for." }
    ),
    required: ["task"]
  )
end

Instance Method Details

#call(args, conversation, cancellation: nil) ⇒ Object

Executes focused context retrieval.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/kward/tools/context_for_task.rb', line 34

def call(args, conversation, cancellation: nil)
  cancellation&.raise_if_cancelled!
  selected_workspace = workspace_for(args)
  unless selected_workspace.equal?(@workspace)
    return self.class.new(workspace: selected_workspace).call(
      @workspace_targets.without_target(args),
      conversation,
      cancellation: cancellation
    )
  end

  task = argument(args, :task, "").to_s.strip
  return "Error: task is required" if task.empty?

  budget = normalized_budget(argument(args, :budget))
  return budget if budget.is_a?(String)

  focus_paths = Array(argument(args, :paths, [])).map(&:to_s).reject(&:empty?)
  files = candidate_files(focus_paths, cancellation: cancellation)
  return "No readable candidate files found for focused context." if files.empty?

  terms = search_terms(task)
  ranked = rank_files(files, terms)
  return "No matching candidate files found for focused context." if ranked.empty?

  render_context(task: task, budget: budget, terms: terms, ranked: ranked, cancellation: cancellation)
rescue SecurityError, Errno::ENOENT => e
  "Error: #{e.message}"
end