Make rules configurable via API

- Add Rules struct and rules.json data file
- Read rules from file instead of hardcoding
- Add GET /rules and PUT /rules endpoints

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
egregore 2026-02-02 12:28:57 +00:00
parent fe80562b45
commit 091a25a76c

View file

@ -62,6 +62,11 @@ struct History {
events: Vec<HistoryEvent>, events: Vec<HistoryEvent>,
} }
#[derive(Serialize, Deserialize)]
struct Rules {
rules: Vec<String>,
}
#[derive(Serialize)] #[derive(Serialize)]
struct OrientResponse { struct OrientResponse {
project: ProjectInfo, project: ProjectInfo,
@ -117,6 +122,7 @@ fn health() -> Response {
fn orient() -> Response { fn orient() -> Response {
let status_path = PathBuf::from(DATA_DIR).join("status.json"); let status_path = PathBuf::from(DATA_DIR).join("status.json");
let history_path = PathBuf::from(DATA_DIR).join("history.json"); let history_path = PathBuf::from(DATA_DIR).join("history.json");
let rules_path = PathBuf::from(DATA_DIR).join("rules.json");
let tickets_dir = PathBuf::from(DATA_DIR).join("tickets"); let tickets_dir = PathBuf::from(DATA_DIR).join("tickets");
let context_dir = PathBuf::from(DATA_DIR).join("context"); let context_dir = PathBuf::from(DATA_DIR).join("context");
@ -170,6 +176,9 @@ fn orient() -> Response {
.take(5) .take(5)
.collect(); .collect();
// Load rules from file
let rules_data: Rules = read_json(&rules_path).unwrap_or(Rules { rules: vec![] });
let response = OrientResponse { let response = OrientResponse {
project: ProjectInfo { project: ProjectInfo {
name: "Egregore".to_string(), name: "Egregore".to_string(),
@ -179,14 +188,7 @@ fn orient() -> Response {
status, status,
current_ticket, current_ticket,
suggested_ticket, suggested_ticket,
rules: vec![ rules: rules_data.rules,
"Claim a ticket before starting work".to_string(),
"Never commit secrets to git".to_string(),
"Always use the Python venv (~/.venv)".to_string(),
"Log completions to history".to_string(),
"Load context topics relevant to your ticket".to_string(),
"Commit and push changes to each service's git repo after modifications".to_string(),
],
available_context, available_context,
recent_history, recent_history,
}; };
@ -490,6 +492,32 @@ fn add_log(request: &Request) -> Response {
} }
} }
// ============================================================================
// Rules
// ============================================================================
fn get_rules() -> Response {
let path = PathBuf::from(DATA_DIR).join("rules.json");
match read_json::<Rules>(&path) {
Some(rules) => json_response(rules),
None => json_response(Rules { rules: vec![] }),
}
}
fn update_rules(request: &Request) -> Response {
let path = PathBuf::from(DATA_DIR).join("rules.json");
let rules: Rules = match rouille::input::json_input(request) {
Ok(r) => r,
Err(e) => return error_response(400, &format!("Invalid JSON: {}", e)),
};
match write_json(&path, &rules) {
Ok(_) => json_response(rules),
Err(e) => error_response(500, &e),
}
}
// ============================================================================ // ============================================================================
// Router // Router
// ============================================================================ // ============================================================================
@ -526,6 +554,10 @@ fn handle_request(request: &Request) -> Response {
(GET) ["/history"] => { get_history() }, (GET) ["/history"] => { get_history() },
(POST) ["/log"] => { add_log(request) }, (POST) ["/log"] => { add_log(request) },
// Rules
(GET) ["/rules"] => { get_rules() },
(PUT) ["/rules"] => { update_rules(request) },
_ => Response::empty_404() _ => Response::empty_404()
) )
} }