#![allow(clippy::wildcard_imports)]
use std::collections::BTreeSet;
use std::path::Path;
use std::sync::OnceLock;
use serde_json::{Value, json};
use crate::analytics::chart::{
ChartExport, FORMAT as CHART_FORMAT, SCHEMA_VERSION as CHART_SCHEMA_VERSION, precision_of,
};
use crate::analytics::diagnostics::AnalyticsDiagnosticKind;
use crate::analytics::events::ScopeKind;
use crate::analytics::projection::{PROJECTION_FORMULA_VERSION, ProjectionStatus};
use crate::analytics::series::{Coverage, MAX_SERIES_DAYS, Mode};
use crate::analytics::units::declared_units;
use crate::bulk;
use crate::cli::AnalyticsScopeArgs;
use crate::commands::bulk as cmd_bulk;
use crate::commands::capacity as cmd_capacity;
use crate::commands::{
create, delete, edit, epic as cmd_epic, export, import, lint, list, mv, parse_priority,
parse_selector, roadmap as cmd_roadmap, search, status, templates as cmd_templates, view,
};
use crate::commands::views as cmd_views;
use crate::commands::recurring as cmd_recurring;
use crate::commands::triage as cmd_triage;
use crate::commands::mentions as cmd_mentions;
use crate::commands::time as cmd_time;
use crate::commands::analytics as cmd_analytics;
use crate::config::{Config, InitiativeConfiguration};
use crate::cycle_schedule::config::{CycleScheduleConfiguration, CycleScheduleId};
use crate::cycle_schedule::evaluate::evaluate as evaluate_cycle_schedules;
use crate::cycle_schedule::generate::{
GenerationOutcome as CycleGenerationOutcome, GenerationReport as CycleGenerationReport,
GenerationRequest as CycleGenerationRequest, generate as generate_cycles,
};
use crate::cycle_schedule::identity::{
CycleReference, GeneratedCycle, GeneratedCycleId, GenerationCursor, ScheduleIndex, cadence_rule,
};
use crate::cycle_schedule::lifecycle::{
LifecycleAction as CycleLifecycleAction, LifecycleOutcome as CycleLifecycleOutcome,
LifecycleRequest as CycleLifecycleRequest, apply as apply_cycle_lifecycle,
};
use crate::cycle_schedule::registry::{self, ScheduleDraft, ScheduleEdit};
use crate::cycle_schedule::snapshot::{CycleSnapshot, CycleSnapshotStore};
use crate::capacity::calculate::{CapacityCalculation, CapacityScope};
use crate::capacity::config::CapacityScopeKind;
use crate::capacity::snapshot::CapacitySnapshot;
use crate::capacity::warning::{CapacityWarning, CapacityWarningKind};
use crate::capacity::{CapacityEntry, resolve_cycle, schedule_index};
use crate::cycle_store::CycleStore;
use crate::error::Result as TixResult;
use crate::model::{History, HistoryEventKind, Issue, Status};
use crate::records::RecordStore;
use crate::recurrence::detect::{Detection, detect};
use crate::recurrence::generate::{GenerationOutcome, GenerationRequest, generate};
use crate::recurrence::lifecycle::{
LifecycleAction, LifecycleDiagnostic, LifecycleOutcome, LifecycleRequest,
apply as apply_lifecycle,
};
use crate::recurrence::rule::{DateKey, IntervalUnit, RuleContext, RuleKind};
use crate::recurrence::series::{KeyState, OccurrenceKey, Series, SeriesId, SeriesIndex};
use crate::search::{Corpus, SearchHit};
use crate::storage::Store;
use crate::templates::{
DeleteConfirmed as TemplateDeleteConfirmed, OPTIONS_SOURCES, Template, TemplateDefaults,
TemplateDefinition, TemplateDetail, TemplatePrompt, TemplateReference, TemplateScope,
TemplateService, TemplateWriteError, resolve,
};
use crate::time_tracking::aggregate::{TimeSnapshot, TimeTotal, TimeTotalState};
use crate::time_tracking::diagnostics::TimeDiagnostic;
use crate::time_tracking::entry::TimeEntry;
use crate::time_tracking::filters::TimeFilter;
use crate::time_tracking::service::{
NewTimeEntry, TimeEntryEdit, TimeMutationRequest, apply as apply_time,
};
use crate::triage::{DecisionKind, TriageDecision, TriagePlan, batch, inbox};
use crate::views::divergence::{ActiveViewMarker, ViewDivergence, ViewSurface};
use crate::views::plan::{ViewApplyResult, ViewPlan};
use crate::views::registry::{
SavedView, ViewDiagnostic, ViewDiagnosticKind, ViewId, ViewRefusal, ViewScope, ViewWriteError,
};
use crate::views::service::{DeleteConfirmed, ViewAuditEvent, ViewDefinition, ViewService};
use crate::roadmap::registry::{AssociationKind, InitiativeEdit, NewInitiative};
use crate::roadmap::{
InitiativeSummary, ProgressValue, RoadmapDiagnostic, RoadmapFilter, RoadmapProjection,
RoadmapRow, RowReference, SummaryContribution,
};
use super::protocol;
pub(crate) struct ToolDescriptor {
pub(crate) name: &'static str,
description: &'static str,
input_schema: Value,
}
type ToolOutcome = std::result::Result<Value, ()>;
type ToolOutcome2<T> = std::result::Result<T, ToolOutcome>;
fn proto() -> ToolOutcome {
Err(())
}
fn refuse(text: String) -> ToolOutcome {
Ok(error_result(text))
}
mod registry_analytics;
mod registry_bulk;
mod registry_capacity;
mod registry_cycles;
mod registry_epics;
mod registry_health;
mod registry_initiatives;
mod registry_issues;
mod registry_mentions;
mod registry_projects;
mod registry_recurring;
mod registry_relationships;
mod registry_roadmap;
mod registry_sync;
mod registry_templates;
mod registry_time;
mod registry_triage;
mod registry_views;
type ViewSvc<'a> = ViewService<'a, BTreeSet<String>>;
const NO_INBOX: &str =
"`tix triage` needs a `[triage]` section in tix.toml, and this project has none";
const DELETE_CONFIRMATION: &str = "deleting a view needs --confirm: it removes the saved definition, never its issues or comments";
pub(crate) fn registry() -> &'static [ToolDescriptor] {
static REGISTRY: OnceLock<Vec<ToolDescriptor>> = OnceLock::new();
REGISTRY.get_or_init(|| {
let mut tools = Vec::new();
tools.extend(registry_analytics::descriptors());
tools.extend(registry_bulk::descriptors());
tools.extend(registry_capacity::descriptors());
tools.extend(registry_cycles::descriptors());
tools.extend(registry_epics::descriptors());
tools.extend(registry_health::descriptors());
tools.extend(registry_initiatives::descriptors());
tools.extend(registry_issues::descriptors());
tools.extend(registry_mentions::descriptors());
tools.extend(registry_projects::descriptors());
tools.extend(registry_recurring::descriptors());
tools.extend(registry_relationships::descriptors());
tools.extend(registry_roadmap::descriptors());
tools.extend(registry_sync::descriptors());
tools.extend(registry_templates::descriptors());
tools.extend(registry_time::descriptors());
tools.extend(registry_triage::descriptors());
tools.extend(registry_views::descriptors());
tools
})
}
pub fn list() -> Vec<Value> {
registry()
.iter()
.map(|t| {
json!({
"name": t.name,
"description": t.description,
"inputSchema": t.input_schema,
})
})
.collect()
}
pub fn call(
config: &Config,
store: &Store,
id: protocol::Id,
name: &str,
args: &Value,
) -> std::result::Result<Value, protocol::ErrorResponse> {
if !registry().iter().any(|t| t.name == name) {
return Err(protocol::method_not_found(id));
}
let arguments = if args.is_null() { &Value::Null } else { args };
match dispatch(config, store, name, arguments) {
Some(outcome) => outcome.map_err(|()| protocol::invalid_params(id)),
None => Ok(error_result(format!(
"internal error: tool `{name}` is declared in the registry but has no dispatch arm"
))),
}
}
mod handlers_analytics;
mod handlers_bulk;
mod handlers_capacity;
mod handlers_cycles;
mod handlers_epics;
mod handlers_health;
mod handlers_initiatives;
mod handlers_issues;
mod handlers_mentions;
mod handlers_projects;
mod handlers_recurring;
mod handlers_relationships;
mod handlers_roadmap;
mod handlers_sync;
mod handlers_templates;
mod handlers_time;
mod handlers_triage;
mod handlers_views;
mod args;
mod resolve;
mod schemas;
mod template_values;
mod values;
mod values_epics;
#[allow(clippy::wildcard_imports)]
use args::*;
#[allow(clippy::wildcard_imports)]
use handlers_cycles::*;
#[allow(clippy::wildcard_imports)]
use handlers_health::*;
#[allow(clippy::wildcard_imports)]
use handlers_recurring::*;
#[allow(clippy::wildcard_imports)]
use handlers_roadmap::*;
#[allow(clippy::wildcard_imports, unused_imports)]
use handlers_time::*;
#[allow(clippy::wildcard_imports)]
use resolve::*;
#[allow(clippy::wildcard_imports)]
use schemas::*;
#[allow(clippy::wildcard_imports)]
use template_values::*;
#[allow(clippy::wildcard_imports)]
use values::*;
#[allow(clippy::wildcard_imports)]
use values_epics::*;
fn dispatch(config: &Config, store: &Store, name: &str, arguments: &Value) -> Option<ToolOutcome> {
handlers_analytics::dispatch(config, store, name, arguments)
.or_else(|| handlers_bulk::dispatch(config, store, name, arguments))
.or_else(|| handlers_capacity::dispatch(config, store, name, arguments))
.or_else(|| handlers_cycles::dispatch(config, store, name, arguments))
.or_else(|| handlers_epics::dispatch(config, store, name, arguments))
.or_else(|| handlers_health::dispatch(config, store, name, arguments))
.or_else(|| handlers_initiatives::dispatch(config, store, name, arguments))
.or_else(|| handlers_issues::dispatch(config, store, name, arguments))
.or_else(|| handlers_mentions::dispatch(config, store, name, arguments))
.or_else(|| handlers_projects::dispatch(config, store, name, arguments))
.or_else(|| handlers_recurring::dispatch(config, store, name, arguments))
.or_else(|| handlers_relationships::dispatch(config, store, name, arguments))
.or_else(|| handlers_roadmap::dispatch(config, store, name, arguments))
.or_else(|| handlers_sync::dispatch(config, store, name, arguments))
.or_else(|| handlers_templates::dispatch(config, store, name, arguments))
.or_else(|| handlers_time::dispatch(config, store, name, arguments))
.or_else(|| handlers_triage::dispatch(config, store, name, arguments))
.or_else(|| handlers_views::dispatch(config, store, name, arguments))
}
#[cfg(test)]
mod tests;