========== BEGIN FILE ==========
---
path: pyproject.toml
kind: file
---
````
[project]
name = "sspec"
dynamic = ["version"]
description = "Lightweight AI collaboration spec for solo/small projects"
readme = "README.md"
license = { text = "AGPL-V3" }
requires-python = ">=3.10"
authors = [{ name = "frostime", email = "frostime@foxmail.com" }]
keywords = ["ai", "spec", "collaboration", "agent", "llm"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Environment :: Console",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: GNU Affero General Public License v3",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Topic :: Software Development :: Documentation",
]

dependencies = [
    "click>=8.0",
    "rich>=13.0",
    "pyyaml>=6.0",
    "python-dotenv>=1.0.0",
    "questionary>=2.1.1",
    "prompt_toolkit>=3.0.48",
    "pathspec>=0.12.0"
]

# uv pip install -e ".[dev]"
[project.optional-dependencies]
dev = [
    "pytest>=7.0",
    "ruff>=0.1.0",
]

[project.scripts]
sspec = "sspec.cli:main"

[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/sspec"]

[tool.hatch.version]
source = "vcs"

[tool.ruff]
line-length = 100
target-version = "py310"

[tool.ruff.lint]
select = [
    "E",  # pycodestyle errors
    "W",  # pycodestyle warnings
    "F",  # pyflakes
    "I",  # isort
    "B",  # flake8-bugbear
    "C4", # flake8-comprehensions
    "UP", # pyupgrade
]

[tool.ruff.format]
quote-style = "single"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"

````
========== END FILE ==========

========== BEGIN FILE CHUNK ==========
---
path: README_zh-CN.md
range: L10-L30
---
````

它把项目上下文、请求入口、变更规格、执行计划和 scoped memory 保存在仓库里，而不是只留在聊天记录中。聊天负责推进工作，仓库文件负责保存长期状态。

## sspec 解决什么问题

如果只靠聊天推进 AI 编程，常见问题有：

- 跨会话时上下文和关键决策容易丢失；
- 设计与实现的确认点不明确；
- 项目约定需要反复重述；
- 复杂工作容易在一条聊天线程里不断膨胀，最后难以审阅和追踪。

sspec 会把工作状态明确写进仓库：

- `AGENTS.md` 定义协作协议；
- `.sspec/project.md` 记录项目身份、技术栈、关键路径和约定；
- `.sspec/requests/` 保存请求入口；
- `.sspec/changes/` 保存每次变更的规格、任务、memory 和辅助材料；
- `.sspec/spec-docs/` 保存应当跨变更保留的架构知识；
- `.sspec/asks/` 在关键决策需要显式记录时保存结构化问答。

````
========== END FILE CHUNK ==========

========== BEGIN SHELL OUTPUT ==========
---
label: git status
command: git status
cwd: H:/SrcCode/playground/sspec
exit_code: 0
---
````
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   src/sspec/builtin_tools/context.py
	modified:   src/sspec/builtin_tools/context_service.py

````
========== END SHELL OUTPUT ==========

========== BEGIN FILE TREE ==========
---
label: dist
path: dist
---
````
dist/

Files: 0 | Directories: 0 | Total size: 0.0B
````
========== END FILE TREE ==========

========== BEGIN FILE ==========
---
label: '.vscode/* :: .vscode/launch.json'
path: .vscode/launch.json
glob: .vscode/*
---
````
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug sspec command",
            "type": "python",
            "request": "launch",
            "program": "${workspaceFolder}/scripts/debug_runner.py",
            "console": "integratedTerminal",
            "justMyCode": true,
            "args": ["${input:sspecCommand}"],
            "cwd": "${workspaceFolder}",
            "env": {
                "PYTHONPATH": "${workspaceFolder}/src"
            }
        },
        {
            "name": "Debug sspec command (custom cwd)",
            "type": "python",
            "request": "launch",
            "program": "${workspaceFolder}/scripts/debug_runner.py",
            "console": "integratedTerminal",
            "justMyCode": true,
            "args": ["${input:sspecCommand}", "--cwd", "${input:workingDir}"],
            "cwd": "${workspaceFolder}",
            "env": {
                "PYTHONPATH": "${workspaceFolder}/src"
            }
        }
    ],
    "inputs": [
        {
            "id": "sspecCommand",
            "type": "promptString",
            "description": "Enter sspec command (e.g., 'change new', 'ask create test', 'project status')",
            "default": "project status"
        },
        {
            "id": "workingDir",
            "type": "promptString",
            "description": "Working directory for sspec command (e.g., '${workspaceFolder}/tmp/test-project', or '.' for workspace root)",
            "default": "."
        }
    ]
}
````
========== END FILE ==========

========== BEGIN FILE ==========
---
label: '.vscode/* :: .vscode/settings.json'
path: .vscode/settings.json
glob: .vscode/*
---
````
{
    "[markdown]": {
        "editor.fontFamily": "JetBrains Mono, HarmonyOS Sans SC",
        "editor.fontSize": 16
    },
    "[python]": {
        "editor.fontFamily": "JetBrains Mono"
    },
    "chat.advanced.cli.mcp.enabled": true,
    "chat.agent.maxRequests": 80,
    "chat.cli.mcp.enabled": true,
    "chat.mcp.access": "all",
    "chat.tools.terminal.autoApprove": {
        "/^bash\\b/": true,
        "/^cat\\b/": true,
        "/^cd\\b/": true,
        "/^chmod\\b/": true,
        "/^cp\\b/": true,
        "/^curl\\b/": true,
        "/^diff\\b/": true,
        "/^echo\\b/": true,
        "/^find\\b/": true,
        "/^git\\b/": true,
        "/^go\\b/": true,
        "/^grep\\b/": true,
        "/^head\\b/": true,
        "/^ls\\b/": true,
        "/^mkdir\\b/": true,
        "/^mv\\b/": true,
        "/^node\\b/": true,
        "/^npm\\b/": true,
        "/^pip/": true,
        "/^printf\\b/": true,
        "/^pwd\\b/": true,
        "/^python/": true,
        "/^rm\\b/": true,
        "/^sed\\b/": true,
        "/^sh\\b/": true,
        "/^sort\\b/": true,
        "/^tail\\b/": true,
        "/^tee\\b/": true,
        "/^test\\b/": true,
        "/^touch\\b/": true,
        "/^wc\\b/": true,
        "/^wget\\b/": true,
        "/^which\\b/": true
    },
    "chat.tools.terminal.blockDetectedFileWrites": false,
    "chat.tools.terminal.ignoreDefaultAutoApproveRules": true,
    "editor.fontFamily": "JetBrains Mono",
    "filePathAutocomplete.exclude": [
        ".git",
        ".venv",
        "__pycache__",
        "dist",
        "out",
        "build",
        ".cache",
        ".sspec",
        ".claude",
        "tmp",
        "tests",
        "sample"
    ],
    "filePathAutocomplete.priority": [
        "src/sspec",
        "src/sspec/templates",
        "src"
    ],
    "github.copilot.chat.additionalReadAccessPaths": [
        "C:\\",
        "D:\\",
        "E:\\",
        "F:\\"
    ],
    "workbench.editorAssociations": {
        "*.copilotmd": "vscode.markdown.preview.editor"
    }
}
````
========== END FILE ==========

========== BEGIN FILE ==========
---
label: '.vscode/* :: .vscode/settings.json.copilot-hub-bak'
path: .vscode/settings.json.copilot-hub-bak
glob: .vscode/*
---
````
{
    "filePathAutocomplete.priority": [
        "src/sspec",
        "src/sspec/templates",
        "src"
    ],
    "[python]": {
        "editor.fontFamily": "JetBrains Mono",
        // "editor.letterSpacing": 0,
        // "editor.lineHeight": 0
    },
    "editor.fontFamily": "JetBrains Mono",
    "filePathAutocomplete.exclude": [
        ".git",
        ".venv",
        "__pycache__",
        "dist",
        "out",
        "build",
        ".cache",
        ".sspec",
        ".claude",
        "tmp",
        "tests",
        "sample"
    ],
    "[markdown]": {
        "editor.fontFamily": "JetBrains Mono, HarmonyOS Sans SC",
        "editor.fontSize": 16
    },
    "chat.agent.maxRequests": 80,
    "workbench.editorAssociations": {
        "*.copilotmd": "vscode.markdown.preview.editor",
        // "SKILL.md": "vscode.markdown.preview.editor",
        // "AGENTS.md": "vscode.markdown.preview.editor"
    },
    "chat.tools.terminal.autoApprove": {
        "/^uv run sspec tool now$/": {
            "approve": true,
            "matchCommandLine": true
        }
    }
}
````
========== END FILE ==========
