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
|
# Clone tool: design
Date: 2026-10-19. Status: implemented (2026-10-19).
## Goal
Ask the assistant to clone a public repository ("clone this repo:
https://github.com/owner/repo") and have it land in the right folder of
`~/Programming/GIT`: `GITHUB/`, `GITLAB/`, `FORGE/`, or a folder already
approved for that host. Public repositories only, plain `git clone`, no
SSH aliases or keys.
The assistant must not decide two things alone: a host with no folder yet,
and a repository that is already cloned. Both are asked, with a concrete
action, never a bare "skip".
## Tool
`examples/tools/clone.toml`, installed to
`~/.config/desktop-assistant/tools/` like the others. No change to
`assistant.py`.
```toml
command = ["clone.sh", "{url}", "{to}", "{update}"]
timeout = 300
```
- `url`: the repository URL.
- `to`: only when the user approved a new host folder; the folder name.
- `update`: `1` to pull an existing clone.
`examples/tools/clone.sh`:
- Parses `https://host/owner/repo` and `git@host:owner/repo` alike; `repo`
is the last path segment with a trailing `/` and a `.git` suffix removed,
and `owner` is the segment before it (a nested group keeps only its last
segment). `host`, `repo` and `owner` are validated as plain names, so
none can escape the tree.
- Resolves `host -> folder` from the mapping file. Absent, it is seeded
with `github.com -> GITHUB`, `gitlab.com -> GITLAB`,
`forge.slackware.nl -> FORGE`.
- Target is `<root>/<folder>/<owner>-<repo>` (`CLONE_ROOT`, default
`~/Programming/GIT`). The name is just `<repo>` when the URL carries no
owner (`git@host:repo.git`) or when `owner` and `repo` are the same
(`firefly-iii/firefly-iii`), so a generic repo name like `wallpapers`
becomes `evgen2571-wallpapers` while a self-named one is not doubled.
- Known host, target free: `git clone` and report it. No question.
- Unknown host: prints the folder to offer (hostname without its last
label, uppercased: `codeberg.org -> CODEBERG`) and does nothing.
- Target exists: prints "already cloned" and does nothing. With
`update=1`, runs `git pull --ff-only` and reports the result; a diverged
clone is an error, not a merge.
- A `to` given for an unknown host clones there and appends
`host<TAB>folder` to the mapping, so that host is known from then on.
An existing mapping is never duplicated or overwritten.
## Mapping file
`~/.config/desktop-assistant/clone-hosts` (`CLONE_MAP`), one
`host<TAB>folder` line per host, user-editable. It lives outside the tool
directory so reinstalling the tools does not lose it, and the file is the
single source of truth: nothing is hardcoded in the script beyond the
seed.
## Check
`test-clone.sh`: a scratch `CLONE_ROOT` and `CLONE_MAP`, and a `git` stub
on `PATH` that records its argv and creates the target, so no network and
no real tree. Covers seeding, clone argv, trailing slash and `.git`, the
unknown-host message, the approved folder being remembered and used on
the next clone without a question, no duplicate map line, the
already-cloned message, the update argv, and the refused inputs.
Live, against the running model: ask for a known host and an unknown host,
accept the offered folder, ask for the same host again (must ask nothing),
then ask for an existing repo (must offer an update, never a second
clone).
## Not included
Private repositories (no credentials), clone depth or branches, and
opening the clone in an editor or agent after it lands.
|