Keep content in git¶
ycli sync keeps objects of Yandex 360 as files of a git repository: you pull them, edit them like any text, review the change in a pull request and push it back. Two kinds of object are kept today, and ycli sync kinds lists them:
| Kind | A file | Its path |
|---|---|---|
wiki/page |
Markdown under a short header | wiki/team/onboarding.md for the page team/onboarding |
tracker/trigger |
YAML | tracker/queues/DE/triggers/16.yaml for trigger 16 of the queue DE |
Run every command from the root of the repository. Credentials are the usual ones (see Authenticate).
There are no MCP tools for this: sync works in your working tree, which an MCP server does not have. An agent runs the same commands.
Pull¶
$ ycli sync pull wiki/team
3 written, 0 unchanged, 1 skipped
$ ycli sync pull tracker/queues/DE
pull wiki/team writes the page team as wiki/team.md and every page under it into wiki/team/. An object the kind does not keep, such as a grid among the pages, is listed as skipped. A file looks like this:
---
ycli: wiki/page
hash: 3954dedc1b058bc73e60e88d4436078e9689cc2e811ffb88e1e6cd48563c3a8d
id: 4821
revision: 9
title: Team
---
# Team
The first four keys are the link of the file to its object, written by ycli: do not edit them. hash is the fingerprint of the content as it was read, and it is how every later command tells what changed.
pull writes over whatever is there. Commit your edits before you pull: git is what protects them. ycli --dry-run sync pull wiki/team writes nothing and names the files with uncommitted work it would overwrite.
See what changed¶
$ ycli sync status
$ ycli sync diff
status reads only the files, so it works offline and fits a pre-commit hook. diff reads the server and shows, for each file, the difference from the object as it is now to the file. Both say what push would do with a file:
| State | What it means |
|---|---|
unchanged |
nothing to push |
update |
you edited the file |
create |
the file names no object yet: push creates one |
untracked |
the file names an object and was never pulled from it: pull first |
changed-on-server |
somebody changed the object since you pulled (diff only) |
gone |
the object no longer exists (diff only) |
no-file |
the container holds an object with no file: pull writes it (diff on a directory) |
unsupported |
the API cannot do what the file asks |
unreadable |
the file is not a file of a known kind; the line and the reason are given |
Unchanged files are hidden; --show-unchanged lists them. --kind wiki/page limits a run to one kind, and a path limits it to a file or a directory.
Where the object changed on the server, diff shows your file against the server as it is now. A file keeps only the fingerprint of what was pulled, so diff cannot say which lines are yours and which are theirs: pull into a clean tree and let git show you.
Push¶
$ ycli --dry-run sync push
$ ycli sync push
0 created, 1 updated, 0 deleted, 14 unchanged, 0 stopped, 0 failed
$ git commit -am "Update the onboarding page"
push sends a file only when its object is still as you pulled it. After each write it reads the object again and writes the new link into the file, which is why a commit follows a push.
| Result | What happened |
|---|---|
created, updated, deleted |
done, and read back |
stopped |
the file and its object went apart (changed-on-server, untracked, gone): nothing was sent; pull, then push |
failed |
the API refused, or the service did not keep a value that was sent: the value is named |
A new file needs no link: write ycli: wiki/page, a title and the text, put the file where the page should live, and push creates the page and fills in the rest. A new trigger file is renamed to the id of its trigger.
--on-error abort ends the run at the first failed file; by default the run goes on to the next.
Delete what you deleted¶
push never deletes on its own. To delete the objects whose files you removed, name the commit to compare with:
$ ycli --dry-run sync push --prune main
$ ycli sync push --prune main
ycli takes the deleted files from git and asks before each delete; --yes answers for all. An object that never had a file is never deleted.
Check files before a commit¶
ycli sync validate exits non-zero when a file does not read as a file of its kind or lies where files of its kind do not, and says the line. As a pre-commit hook:
repos:
- repo: local
hooks:
- id: ycli-sync-validate
name: ycli sync validate
entry: ycli sync validate
language: system
pass_filenames: false
Review the plan in a pull request¶
With --exit-code, status and diff exit with 7 when there is something to push and with 8 when a file and its object went apart; without the flag they exit with 0. This job comments the plan on a pull request and fails when a pull is needed first:
name: sync-plan
on: pull_request
jobs:
plan:
runs-on: ubuntu-latest
permissions:
pull-requests: write
env:
YANDEX_ID_OAUTH_TOKEN: ${{ secrets.YANDEX_ID_OAUTH_TOKEN }}
YANDEX_ID_ORGANIZATION_ID: ${{ secrets.YANDEX_ID_ORGANIZATION_ID }}
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v10.2.0
- name: Plan
run: |
uvx yandex-cli==0.142.0 sync diff --exit-code -o json > plan.json || code=$?
jq -r '.[] | "### `\(.path)`: \(.state)\n\n```diff\n\(.diff // .detail // "")\n```\n"' plan.json > plan.md
test -s plan.md || echo "Nothing to push." > plan.md
gh pr comment "${{ github.event.pull_request.number }}" --body-file plan.md
case "${code:-0}" in 0|7) ;; *) exit "$code" ;; esac
Secrets in a difference are masked. Push from the default branch after the merge, with the same two secrets and ycli sync push, then commit the links push wrote.
Checked on 2026-10-08 against a real organization: pull, status, diff and push, with --prune, on Wiki pages; pull, diff and push of an edit on a trigger of a Tracker queue. Creating a trigger from a file, the pre-commit hook and the GitHub Actions job were not run.