{"id":31595264,"url":"https://github.com/escherize/tortise_spec","last_synced_at":"2026-02-16T11:34:37.656Z","repository":{"id":310925783,"uuid":"1041127990","full_name":"escherize/tortise_spec","owner":"escherize","description":null,"archived":false,"fork":false,"pushed_at":"2025-08-21T02:13:42.000Z","size":4,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-06T03:59:04.281Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/escherize.png","metadata":{"files":{"readme":"readme.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-20T03:00:14.000Z","updated_at":"2025-08-21T02:13:45.000Z","dependencies_parsed_at":"2025-08-21T04:33:32.581Z","dependency_job_id":"3e657765-2791-465a-9f72-23a3ad5446e8","html_url":"https://github.com/escherize/tortise_spec","commit_stats":null,"previous_names":["escherize/tortise_spec"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/escherize/tortise_spec","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escherize%2Ftortise_spec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escherize%2Ftortise_spec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escherize%2Ftortise_spec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escherize%2Ftortise_spec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/escherize","download_url":"https://codeload.github.com/escherize/tortise_spec/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/escherize%2Ftortise_spec/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29506768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-16T09:05:14.864Z","status":"ssl_error","status_checked_at":"2026-02-16T08:55:59.364Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-06T03:58:50.551Z","updated_at":"2026-02-16T11:34:37.629Z","avatar_url":"https://github.com/escherize.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cTortise File Format Specification v0.2\n\n## 1. Purpose\n\nA single UTF-8 plaintext file that represents a complete directory tree. Optimized for hand-writing and inspection.\n\n## 2. Terminology\n\nKey words MUST, SHOULD, MAY are per RFC 2119/8174.\n\n## 3. Overview\n\nA Tortise file is a sequence of file sections. Each section starts with a file declaration line, followed by the file’s content lines. A fixed delimiter string identifies declaration lines. Blank lines outside sections are ignored.\n\n## 4. Delimiter\n- The delimiter MUST be a non-empty sequence of one or more Unicode characters that are not ASCII space (0x20), horizontal tab (0x09), line feed (0x0A), or carriage return (0x0D). Emoji and symbols are allowed (e.g., 🐢, ❖❖❖, ::, ===, ***).\n- The delimiter is auto-detected from the first non-blank line, which MUST be a valid file declaration.\n- All declarations in the file MUST use the exact same delimiter string. Matching is byte-exact in UTF-8.\n- Writers MUST choose a delimiter such that no content line in the file begins with \u003cDELIM\u003e\u003cSP\u003e. If a collision would occur, choose a different delimiter.\n\n## 5. File declaration\n\nSyntax:\n\n```\n\u003cDELIM\u003e\u003cSP\u003e\u003cPATH\u003e\u003cEOL\u003e\n```\n\nRules:\n- \u003cSP\u003e is one ASCII space (0x20).\n- \u003cPATH\u003e is a POSIX-style relative path using /.\n- Paths MUST NOT be absolute, contain .., or contain NUL.\n- Duplicate \u003cPATH\u003es MUST be rejected.\n\n## 6. Content\n- All lines after a declaration up to the next declaration belong to that file.\n- Content is included verbatim.\n- Each file’s content MUST end with a newline. Readers MUST behave as if a trailing \\n is present. Writers SHOULD include it.\n- Readers MUST accept \\n and \\r\\n; delivered content uses \\n.\n\n7. Collisions\n- A line starting with \u003cDELIM\u003e\u003cSP\u003e is always interpreted as a file declaration.\n- Writers MUST choose a delimiter such that no content line begins with \u003cDELIM\u003e\u003cSP\u003e.\n- If a collision would occur, writers MUST choose a different delimiter.\n\n8. Ignored lines\n- Blank or whitespace-only lines outside any section are ignored.\n- Blank lines inside a section are preserved.\n\n9. Directories\n- Directories are implicit. Implementations MUST create intermediate directories as needed for each \u003cPATH\u003e.\n\n10. Media type and extension\n- Suggested media type: application/vnd.tortise.tree\n- Suggested extension: .tortise\n\n11. Security considerations\n- Reject absolute paths, .., and drive letters.\n- Impose limits (file count, path length, file size).\n- Write atomically with safe permissions.\n\n12. Errors\n\nImplementations MUST error on:\n- Inconsistent delimiter string.\n- Duplicate \u003cPATH\u003e.\n- Disallowed \u003cPATH\u003e (absolute, .., empty, or .).\n\n13. ABNF (informative)\n\nABNF cannot enforce “same delimiter string” globally and is limited for Unicode classes; prose above is normative.\n\ntortise         = *blank-line *(section *blank-line)\nsection         = file-decl *content-line\nfile-decl       = delim SP path-chars EOL\ndelim           = 1*delim-char  ; any non-whitespace Unicode scalar value\n; delim-char is any Unicode scalar value except SPACE, TAB, CR, LF\n; (expressed in prose; UTF-8 byte sequences omitted for brevity)\n\npath-chars      = 1*path-char\npath-char       = %x21-7E except %x20  ; visible ASCII except space\n\ncontent-line    = *VCHAR EOL\nblank-line      = *WSP EOL\nSP              = %x20\nEOL             = %x0A / (%x0D %x0A)\nVCHAR           = %x21-7E\nWSP             = %x20 / %x09\n\n14. Reference parsing algorithm (normative)\n\nnormalize EOL to \"\\n\"; read UTF-8\nskip leading blank/whitespace-only lines\nread first non-blank line L\n  let i = index of first ASCII space (0x20) in L\n  require i \u003e 0\n  DELIM = L[0:i]       ; must contain no SPACE/TAB/CR/LF\n  PATH  = L[i+1:]      ; must be non-empty\ncurrent = new file(PATH)\nfor each subsequent line:\n  if line starts with DELIM + \" \":\n     finalize current; start new file(PATH = text after space)\n  else:\n     append line to current file content verbatim\non close of a section, ensure content ends with \"\\n\"\nvalidate paths and duplicates\n\n15. Writer guidelines\n- Prefer a short delimiter like \u003e or 🐢. If any content would start with \u003cDELIM\u003e\u003cSP\u003e, switch to another sequence (e.g., ===, ***, -\u003e, ❖❖).\n- End the container file with \\n.\n\n16. Example\n\nUsing 🐢 to demonstrate a non-ASCII delimiter:\n\n``` tortise\n🐢 src/util.py\na = 1\n\n🐢 hi.py\nfrom src.util import a\nprint(a)\n\n🐢 config/settings.json\n{ \"debug\": true }\n```\n\nYields three files:\n- src/util.py with content: `a = 1\\n`\n- hi.py with content: `from src.util import a\\nprint(a)\\n`\n- config/settings.json with content: `{ \"debug\": true }`\n\n17. Conformance\n\nA “Tortise reader” MUST implement Sections 4–12. A “Tortise writer” MUST implement Sections 4–8 and SHOULD follow Section 15.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fescherize%2Ftortise_spec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fescherize%2Ftortise_spec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fescherize%2Ftortise_spec/lists"}