162 lines
4.3 KiB
Go
162 lines
4.3 KiB
Go
package gsp
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
type AIInitOptions struct {
|
|
Agents bool
|
|
Skill string
|
|
All bool
|
|
Force bool
|
|
}
|
|
|
|
func InitAIUsage(root string, options AIInitOptions) (InitResult, error) {
|
|
project, err := LoadProject(root)
|
|
if err != nil {
|
|
return InitResult{}, err
|
|
}
|
|
if project.Manifest == nil {
|
|
return InitResult{}, fmt.Errorf("gsp.manifest not found; run gsp init first")
|
|
}
|
|
|
|
result := InitResult{Root: filepath.ToSlash(project.Root)}
|
|
projectName := project.Manifest.Project
|
|
if projectName == "" {
|
|
projectName = filepath.Base(project.Root)
|
|
}
|
|
entry := "project.entry"
|
|
if len(project.Manifest.Entry) > 0 && project.Manifest.Entry[0] != "" {
|
|
entry = project.Manifest.Entry[0]
|
|
}
|
|
scan := "design"
|
|
if len(project.Manifest.Scan) > 0 && project.Manifest.Scan[0] != "" {
|
|
scan = project.Manifest.Scan[0]
|
|
}
|
|
|
|
files := []initFile{
|
|
{
|
|
Path: "README.md",
|
|
Data: projectReadme(projectName, scan, entry),
|
|
},
|
|
{
|
|
Path: "AI_USAGE.md",
|
|
Data: aiUsage(project.Manifest.GSPVersion, scan),
|
|
},
|
|
}
|
|
|
|
if options.All || options.Agents {
|
|
files = append(files, initFile{
|
|
Path: "AGENTS.md",
|
|
Data: agentsUsage(),
|
|
})
|
|
}
|
|
|
|
skill := strings.TrimSpace(options.Skill)
|
|
if options.All && skill == "" {
|
|
skill = "generic"
|
|
}
|
|
if skill != "" {
|
|
path, data, err := skillUsage(skill)
|
|
if err != nil {
|
|
return InitResult{}, err
|
|
}
|
|
files = append(files, initFile{Path: path, Data: data})
|
|
}
|
|
|
|
for _, file := range files {
|
|
path := filepath.Join(project.Root, filepath.FromSlash(file.Path))
|
|
if err := writeInitFile(project.Root, path, []byte(file.Data), options.Force, &result); err != nil {
|
|
return InitResult{}, err
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
type initFile struct {
|
|
Path string
|
|
Data string
|
|
}
|
|
|
|
func projectReadme(projectName, scan, entry string) string {
|
|
return fmt.Sprintf(`# %s
|
|
|
|
This is a GSP project.
|
|
|
|
- Manifest: `+"`gsp.manifest`"+`
|
|
- GSP source: `+"`%s/`"+`
|
|
- Entry GSP: `+"`%s`"+`
|
|
- AI rules: `+"`AI_USAGE.md`"+`
|
|
|
|
`, projectName, scan, entry)
|
|
}
|
|
|
|
func aiUsage(gspVersion, scan string) string {
|
|
if gspVersion == "" {
|
|
gspVersion = DefaultGSPVersion
|
|
}
|
|
return fmt.Sprintf(`# AI Usage
|
|
|
|
- Read `+"`gsp.manifest`"+` first.
|
|
- Use GSP version `+"`%s`"+`.
|
|
- Treat `+"`%s/`"+` as the default GSP source directory.
|
|
- `+"`.gsp`"+` files use YAML.
|
|
- Preserve `+"`id`"+`; do not rename it unless explicitly requested.
|
|
- `+"`id`"+` is the unique identity of a GSP unit.
|
|
- `+"`title`"+` is display text; use `+"`id`"+` when `+"`title`"+` is missing.
|
|
- `+"`links`"+` associates a GSP with paths, folders, URLs, or external addresses.
|
|
- Use only fields valid for the declared GSP version.
|
|
- Do not add non-built-in fields unless they are declared in `+"`gsp.fields`"+`.
|
|
- Run `+"`gsp fields list`"+` before using project custom fields.
|
|
- `+"`with`"+` means related design context.
|
|
- `+"`refines`"+` means single-source refinement.
|
|
- Empty `+"`context`"+` means placeholder.
|
|
- Do not invent missing referenced GSPs silently.
|
|
- Use `+"`gsp validate`"+` after editing GSP files.
|
|
- Use `+"`gsp index`"+` to locate GSP units.
|
|
- Use `+"`gsp trace <id>`"+` to inspect relations.
|
|
- Use `+"`gsp flatten <id>`"+` before implementation or task splitting.
|
|
- Use `+"`gsp pack <id>`"+` when a compact AI context is needed.
|
|
- Use `+"`gsp links <id>`"+` to inspect associated files, folders, URLs, or addresses.
|
|
- Use `+"`gsp impact <id>`"+` before changing shared GSP units.
|
|
- Use `+"`gsp message validate <file>`"+` for agent communication messages.
|
|
- Use `+"`gsp stage-check --stage <stage>`"+` before stage handoff.
|
|
|
|
`, gspVersion, scan)
|
|
}
|
|
|
|
func agentsUsage() string {
|
|
return `# Agent Instructions
|
|
|
|
Read ` + "`AI_USAGE.md`" + ` before working on this GSP project.
|
|
|
|
`
|
|
}
|
|
|
|
func skillUsage(kind string) (string, string, error) {
|
|
switch strings.ToLower(strings.TrimSpace(kind)) {
|
|
case "generic":
|
|
return "skills/gsp/SKILL.md", `# GSP
|
|
|
|
Use this skill when working in a GSP project.
|
|
|
|
Read ` + "`gsp.manifest`" + `, then ` + "`AI_USAGE.md`" + `.
|
|
Use ` + "`gsp validate`" + `, ` + "`gsp index`" + `, and ` + "`gsp flatten <id>`" + ` when handling GSP files.
|
|
|
|
`, nil
|
|
case "codex":
|
|
return ".codex/skills/gsp/SKILL.md", `# GSP
|
|
|
|
Use this skill when working in a GSP project.
|
|
|
|
Read ` + "`gsp.manifest`" + `, then ` + "`AI_USAGE.md`" + `.
|
|
Use GSP Toolkit commands before editing, handoff, implementation, or review.
|
|
|
|
`, nil
|
|
default:
|
|
return "", "", fmt.Errorf("unsupported skill %q; use generic or codex", kind)
|
|
}
|
|
}
|