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
//! `path p incept <provider>` — file/stdin-shaped sibling of `path
//! p export <provider>`. Forwards to [`crate::cmd_export::run`] after
//! resolving the stdin/file/cache-id input.
use anyhow::Result;
use clap::Subcommand;
use std::path::PathBuf;
#[derive(Subcommand, Debug)]
pub enum InceptTarget {
/// Incept a Toolpath document into a Claude Code session layout.
Claude {
/// Input toolpath document (JSON path, cache id, or `-` for
/// stdin). Reads stdin when omitted.
#[arg(short, long)]
input: Option<String>,
/// Target project directory. Defaults to the current
/// directory when neither --project nor --output is given.
#[arg(short, long)]
project: Option<PathBuf>,
/// Output JSONL to this file. Mutually exclusive with
/// --project.
#[arg(short, long, conflicts_with = "project")]
output: Option<PathBuf>,
},
/// Incept a Toolpath document into a Cursor (IDE) composer in
/// `state.vscdb`.
Cursor {
/// Input toolpath document (JSON path, cache id, or `-` for
/// stdin). Reads stdin when omitted.
#[arg(short, long)]
input: Option<String>,
/// Target workspace folder. Defaults to the current
/// directory when neither --project nor --output is given.
#[arg(short, long)]
project: Option<PathBuf>,
/// Output the projected `CursorSession` as pretty JSON to
/// this file. Mutually exclusive with --project.
#[arg(short, long, conflicts_with = "project")]
output: Option<PathBuf>,
},
}
pub fn run(target: InceptTarget) -> Result<()> {
match target {
InceptTarget::Claude {
input,
project,
output,
} => {
let input = resolve_input(input)?;
let (project, output) = default_project(project, output);
crate::cmd_export::run(crate::cmd_export::ExportTarget::Claude {
input,
project,
output,
})
}
InceptTarget::Cursor {
input,
project,
output,
} => {
let input = resolve_input(input)?;
let (project, output) = default_project(project, output);
crate::cmd_export::run(crate::cmd_export::ExportTarget::Cursor {
input,
project,
output,
})
}
}
}
fn resolve_input(input: Option<String>) -> Result<String> {
match input.as_deref() {
None | Some("-") => {
use std::io::Read;
let mut buf = String::new();
std::io::stdin()
.read_to_string(&mut buf)
.map_err(|e| anyhow::anyhow!("Failed to read stdin: {}", e))?;
let mut f = tempfile::NamedTempFile::new()?;
std::io::Write::write_all(&mut f, buf.as_bytes())?;
let (_file, path) = f.keep()?;
Ok(path.to_string_lossy().to_string())
}
Some(s) => Ok(s.to_string()),
}
}
fn default_project(
project: Option<PathBuf>,
output: Option<PathBuf>,
) -> (Option<PathBuf>, Option<PathBuf>) {
if output.is_none() && project.is_none() {
let cwd = std::env::current_dir().expect("cwd");
(Some(cwd), None)
} else {
(project, output)
}
}