1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
mod commands;
mod config;
mod markdown;
use clap::{Parser, Subcommand};
use clap_complete::Shell;
use std::path::PathBuf;
use std::process;
#[derive(Parser)]
#[command(
name = "existence",
version,
about = "CLI for the Existence ontology framework"
)]
pub struct Cli {
/// Path to the ontology directory (overrides auto-detection)
#[arg(long, global = true)]
ontology: Option<PathBuf>,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Read a node's full definition
Lookup {
/// Term name (e.g. "existence", "entity")
term: String,
/// Output parsed sections as JSON
#[arg(long)]
json: bool,
},
/// Search across term files by relevance
Search {
/// Search query (matches term names, definitions, and content)
query: String,
/// Output results as JSON
#[arg(long)]
json: bool,
/// Maximum number of results (default 10)
#[arg(long, default_value = "10")]
limit: usize,
},
/// Navigate the scoping chain — list terms at a ring level
Scope {
/// Ring level (0, 1, ...). Omit to list all rings.
ring: Option<u32>,
},
/// Validate ontology nodes against SPEC.md rules
Lint {
/// Path to lint (directory or single file). Defaults to src/
path: Option<String>,
},
/// Generate term relationship graph
Graph {
/// Filter to a specific ring level
ring: Option<u32>,
/// Output format: "dot" (default) or "json"
#[arg(long, default_value = "dot")]
format: String,
},
/// Clone or pull an ontology from a GitHub repo
Fetch {
/// Source in format github:org/repo. If omitted, reads existence.toml
source: Option<String>,
},
/// Create a new ontology node from template
New {
/// Term name (lowercase, hyphens for multi-word, e.g. "domain-model")
term: String,
/// Add to ring N in existence.toml (0, 1, 2)
#[arg(long)]
ring: Option<u32>,
/// Don't open in $EDITOR after creating
#[arg(long)]
no_edit: bool,
/// Pre-fill the ontology section's first line
#[arg(long)]
description: Option<String>,
},
/// Generate shell completion scripts
Completions {
/// Shell to generate completions for (bash, zsh, fish, elvish, powershell)
shell: Shell,
},
/// Set up ~/.claude integration (not yet implemented)
Install,
/// Start local API server (not yet implemented)
Serve,
/// Generate static site + JSON API (not yet implemented)
BuildSite,
/// Suggest relevant terms for a domain (not yet implemented)
Context {
/// Domain name
domain: String,
},
}
fn main() {
let cli = Cli::parse();
let result = match cli.command {
Commands::Lookup { ref term, json } => {
let ontology_dir = resolve_or_exit(cli.ontology.as_deref());
commands::lookup::run(&ontology_dir, term, json)
}
Commands::Search {
ref query,
json,
limit,
} => {
let ontology_dir = resolve_or_exit(cli.ontology.as_deref());
commands::search::run(&ontology_dir, query, json, limit)
}
Commands::Scope { ring } => {
let ontology_dir = resolve_or_exit(cli.ontology.as_deref());
commands::scope::run(&ontology_dir, ring)
}
Commands::Lint { ref path } => {
let ontology_dir = resolve_or_exit(cli.ontology.as_deref());
commands::lint::run(&ontology_dir, path.as_deref())
}
Commands::Graph { ring, ref format } => {
let ontology_dir = resolve_or_exit(cli.ontology.as_deref());
commands::graph::run(&ontology_dir, ring, format)
}
Commands::New {
ref term,
ring,
no_edit,
ref description,
} => {
let ontology_dir = resolve_or_exit(cli.ontology.as_deref());
commands::new::run(&ontology_dir, term, ring, no_edit, description.as_deref())
}
Commands::Fetch { ref source } => {
let ontology_dir = config::resolve_ontology_dir(cli.ontology.as_deref())
.unwrap_or_else(|_| PathBuf::from("."));
commands::fetch::run(&ontology_dir, source.as_deref())
}
Commands::Completions { shell } => {
commands::completions::run(shell);
Ok(())
}
Commands::Install => {
println!("existence install: not yet implemented");
println!("Will set up ~/.claude integration with ontology terms.");
Ok(())
}
Commands::Serve => {
println!("existence serve: not yet implemented");
println!("Will start a local API server for ontology queries.");
Ok(())
}
Commands::BuildSite => {
println!("existence build-site: not yet implemented");
println!("Will generate a static site + JSON API from the ontology.");
Ok(())
}
Commands::Context { ref domain } => {
println!("existence context: not yet implemented");
println!("Will suggest relevant ontology terms for the '{domain}' domain.");
Ok(())
}
};
if let Err(e) = result {
eprintln!("error: {e}");
process::exit(1);
}
}
fn resolve_or_exit(explicit: Option<&std::path::Path>) -> PathBuf {
config::resolve_ontology_dir(explicit).unwrap_or_else(|e| {
eprintln!("error: {e}");
process::exit(1);
})
}