Class: Docforge::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/docforge/config.rb

Overview

Layered configuration.

Precedence (highest wins):

1. ENV vars
2. Project config (./.docforge.yml, or --config PATH)
3. Global config (~/.docforge.yml)
4. Built-in defaults

Env vars:

ANTHROPIC_API_KEY   — required, your Anthropic API key
ANTHROPIC_MODEL     — model id (overrides yaml `model`)
DOCFORGE_OUTPUT_DIR — where briefs are written
DOCFORGE_AUTHOR     — byline on the brief

YAML schema (all keys optional — see ‘docforge init` for a template):

model: claude-sonnet-4-5
output_dir: ~/Desktop/Portfolio/Features
author: Your Name
input_files:
  prd: PRD.md
  spec: SPEC.md
  notes: notes.md
system_prompt: ./prompts/feature_brief.md   # relative to this yaml
interview:
  - "Who is the human user of this feature?"
  - "..."

Constant Summary collapse

DEFAULT_MODEL =
"claude-sonnet-4-5"
DEFAULT_OUTPUT_DIR =
File.expand_path("~/Desktop/Portfolio/Features")
DEFAULT_AUTHOR =
"You"
DEFAULT_INPUT_FILES =
{ "prd" => "PRD.md", "spec" => "SPEC.md", "notes" => "notes.md" }.freeze
DEFAULT_INTERVIEW =
[
  "Who is the human user of this feature? Name them (e.g. 'Mira, a content designer at an EdTech client').",
  "What was the world like *before* this shipped — specifically?",
  "What was the single hardest design decision, and why?",
  "What's the one metric you'd brag about (or a design target if pre-production)?",
  "Is there a moment in the build that taught you something worth conveying?"
].freeze
BUNDLED_SYSTEM_PROMPT_PATH =
File.expand_path("../../prompts/system_prompt.md", __dir__)
GLOBAL_CONFIG_PATH =
File.expand_path("~/.docforge.yml")
PROJECT_CONFIG_NAME =
".docforge.yml"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env: ENV, explicit_config_path: nil, cwd: Dir.pwd) ⇒ Config

Returns a new instance of Config.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/docforge/config.rb', line 54

def initialize(env: ENV, explicit_config_path: nil, cwd: Dir.pwd)
  @loaded_config_files = []

  global  = load_yaml(GLOBAL_CONFIG_PATH)
  project = load_yaml(explicit_config_path || File.join(cwd, PROJECT_CONFIG_NAME))
  merged  = global.merge(project)

  @api_key    = env["ANTHROPIC_API_KEY"]
  @model      = env["ANTHROPIC_MODEL"]      || merged["model"]      || DEFAULT_MODEL
  @output_dir = env["DOCFORGE_OUTPUT_DIR"]  || merged["output_dir"] || DEFAULT_OUTPUT_DIR
  @author     = env["DOCFORGE_AUTHOR"]      || merged["author"]     || DEFAULT_AUTHOR
  @output_dir = File.expand_path(@output_dir)

  @input_filenames = DEFAULT_INPUT_FILES.merge(merged["input_files"] || {})
  @interview_questions = Array(merged["interview"]).then { |a| a.empty? ? DEFAULT_INTERVIEW : a }
  @system_prompt_path  = resolve_system_prompt_path(merged["system_prompt"], merged["_loaded_from"])
end

Instance Attribute Details

#api_keyObject (readonly)

Returns the value of attribute api_key.



50
51
52
# File 'lib/docforge/config.rb', line 50

def api_key
  @api_key
end

#authorObject (readonly)

Returns the value of attribute author.



50
51
52
# File 'lib/docforge/config.rb', line 50

def author
  @author
end

#input_filenamesObject (readonly)

Returns the value of attribute input_filenames.



50
51
52
# File 'lib/docforge/config.rb', line 50

def input_filenames
  @input_filenames
end

#interview_questionsObject (readonly)

Returns the value of attribute interview_questions.



50
51
52
# File 'lib/docforge/config.rb', line 50

def interview_questions
  @interview_questions
end

#loaded_config_filesObject (readonly)

Returns the value of attribute loaded_config_files.



50
51
52
# File 'lib/docforge/config.rb', line 50

def loaded_config_files
  @loaded_config_files
end

#modelObject (readonly)

Returns the value of attribute model.



50
51
52
# File 'lib/docforge/config.rb', line 50

def model
  @model
end

#output_dirObject (readonly)

Returns the value of attribute output_dir.



50
51
52
# File 'lib/docforge/config.rb', line 50

def output_dir
  @output_dir
end

#system_prompt_pathObject (readonly)

Returns the value of attribute system_prompt_path.



50
51
52
# File 'lib/docforge/config.rb', line 50

def system_prompt_path
  @system_prompt_path
end

Instance Method Details

#system_prompt_contentObject



72
73
74
# File 'lib/docforge/config.rb', line 72

def system_prompt_content
  @system_prompt_content ||= File.read(@system_prompt_path)
end

#to_hObject



90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/docforge/config.rb', line 90

def to_h
  {
    api_key_set: !api_key.nil? && !api_key.empty?,
    model: model,
    output_dir: output_dir,
    author: author,
    input_filenames: input_filenames,
    system_prompt_path: system_prompt_path,
    interview_question_count: interview_questions.size,
    loaded_config_files: loaded_config_files
  }
end

#validate!Object



76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/docforge/config.rb', line 76

def validate!
  if api_key.nil? || api_key.strip.empty?
    raise ConfigError, <<~MSG
      ANTHROPIC_API_KEY is not set.
      Get one at https://console.anthropic.com/settings/keys and export it:
        export ANTHROPIC_API_KEY=sk-ant-...
    MSG
  end
  unless File.exist?(@system_prompt_path)
    raise ConfigError, "System prompt file not found: #{@system_prompt_path}"
  end
  true
end