372 lines
20 KiB
Python
372 lines
20 KiB
Python
#!/usr/bin/env python3
|
|
"""Bootstrap Forgejo issues for DisClaw. Run once after labels+milestones exist."""
|
|
|
|
import requests
|
|
import sys
|
|
|
|
TOKEN = "43a3a524105f5ef6049393344cc4d01d3bc96b8b"
|
|
BASE = "http://localhost:3000/api/v1/repos/dev/disclaw"
|
|
HEADERS = {"Authorization": f"token {TOKEN}", "Content-Type": "application/json"}
|
|
|
|
# Label IDs (set by bootstrap-labels step):
|
|
# phase:0=1, phase:1=2, type:feat=8, type:fix=9, type:refactor=10
|
|
# type:test=11, type:docs=12, type:chore=13, type:ci=14
|
|
# priority:p0=15, priority:p1=16, priority:p2=17, security=18, blocked=19
|
|
|
|
PHASE0_ISSUES = [
|
|
{
|
|
"title": "DIS-001: Workspace-Root nach ~/.disclaw/workspaces/ verschieben",
|
|
"milestone": 1,
|
|
"labels": [1, 8, 15, 18],
|
|
"body": (
|
|
"## Ziel\n"
|
|
"Workspaces standardmaessig unter `~/.disclaw/workspaces/<agent>/` anlegen -- ausserhalb des DisClaw-Repos. "
|
|
"Verhindert CLAUDE.md-Walk-Up-Leak.\n\n"
|
|
"## Kontext\n"
|
|
"Claude Code walkt Parent-Directories hoch und konkateniert alle CLAUDE.md. "
|
|
"Workspace unter `disclaw/workspaces/` erbt die DisClaw-Projekt-Identitaet.\n"
|
|
"Referenz: `docs/development-plan.md` Abschnitt 2.9, `docs/cli-feature-answers.md` Frage 8\n\n"
|
|
"## Scope\n"
|
|
"**In:** `disclaw.yaml` Default aendern, Tilde-Expansion in `src/config/loader.ts`, "
|
|
"`new-agent.ts` legt Workspace unter resolvierten Root an, Warning-Log wenn Root innerhalb Repo, README-Migrationshinweis\n"
|
|
"**Out:** Automatische Migration, `--bare`-Flags (DIS-004)\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] `disclaw.yaml` Default ist `workspaces_root: \"~/.disclaw/workspaces\"`\n"
|
|
"- [ ] `src/config/loader.ts` expandiert `~` auf Linux/Mac/Windows korrekt\n"
|
|
"- [ ] `new-agent` legt Workspace unter neuem Root an, DB speichert absoluten Pfad\n"
|
|
"- [ ] Warning-Log wenn Root innerhalb Repo\n"
|
|
"- [ ] Unit-Test `tests/unit/workspace-root-resolve.test.ts` gruen\n"
|
|
"- [ ] README-Abschnitt 'Migration bestehender Workspaces'\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-0/workspace-root-home`\n\n"
|
|
"## Abhaengigkeiten\nKeine."
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-002: shell:false + cross-spawn Windows-Fix im Runner",
|
|
"milestone": 1,
|
|
"labels": [1, 9, 15, 18],
|
|
"body": (
|
|
"## Ziel\n"
|
|
"Command-Injection ueber Discord-Input eliminieren. `shell: true` aus `runner.ts` entfernen. "
|
|
"Windows-`.cmd`-Wrapper korrekt aufloesen, CVE-2024-27980 umgehen.\n\n"
|
|
"## Kontext\n"
|
|
"`src/agent/runner.ts` nutzt aktuell `shell: true` -- Shell-Injection-Vektor. "
|
|
"`spawn('claude', args, { shell: false })` schlaegt auf Windows fehl (nur `claude.cmd`).\n"
|
|
"Referenz: `docs/development-plan.md` Abschnitt 2.2\n\n"
|
|
"## Scope\n"
|
|
"**In:** `cross-spawn` als Dependency, `src/runtime/resolve-claude.ts` mit `resolveClaude()`, "
|
|
"`runner.ts`: `shell: false`, `windowsHide: true`, UTF-8-Encoding\n"
|
|
"**Out:** `sanitizedEnv()` (DIS-003), `--bare`-Flags (DIS-004)\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] Kein `shell: true` im gesamten `src/`-Baum\n"
|
|
"- [ ] `cross-spawn` in `package.json`\n"
|
|
"- [ ] `resolveClaude()` wirft klare Fehlermeldung wenn nicht auffindbar\n"
|
|
"- [ ] Unit-Test `tests/unit/resolve-claude.test.ts` gruen\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-0/harden-spawn`\n\n"
|
|
"## Abhaengigkeiten\nKeine. **Empfohlener Startpunkt.**"
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-003: sanitizedEnv() -- Secrets aus Child-Env entfernen",
|
|
"milestone": 1,
|
|
"labels": [1, 9, 15, 18],
|
|
"body": (
|
|
"## Ziel\n"
|
|
"Kein Secret (DISCORD_BOT_TOKEN etc.) im Environment des `claude`-Kindprozesses. "
|
|
"Heute exfiltrierbar via `echo $DISCORD_BOT_TOKEN`.\n\n"
|
|
"## Kontext\n"
|
|
"`runner.ts` vererbt aktuell `process.env` vollstaendig.\n"
|
|
"Referenz: `docs/development-plan.md` Abschnitt 2.6\n\n"
|
|
"## Scope\n"
|
|
"**In:** `src/runtime/env.ts`: `sanitizedEnv(extra?)` entfernt Token/Secret-Pattern, "
|
|
"setzt `CI=true`, `DISCLAW_AGENT=1`, optionale `env_blocklist` via `disclaw.yaml`, Runner nutzt `sanitizedEnv`\n"
|
|
"**Out:** Whitelist-Modus\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] `sanitizedEnv()` ist pur, keine Seiteneffekte\n"
|
|
"- [ ] Unit-Test `tests/unit/sanitize-env.test.ts` deckt alle Pattern-Cases\n"
|
|
"- [ ] Runner nutzt `sanitizedEnv` an allen Spawn-Stellen\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-0/sanitize-env`\n\n"
|
|
"## Abhaengigkeiten\nKeine. Kann parallel zu DIS-002 laufen."
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-004: Runner mit --bare + --append-system-prompt-file aufrufen",
|
|
"milestone": 1,
|
|
"labels": [1, 8, 15, 18],
|
|
"body": (
|
|
"## Ziel\n"
|
|
"Jeder CLI-Call verwendet `--bare` (skippt CLAUDE.md-Walk-Up, Hooks, Auto-Memory, MCP) "
|
|
"und injiziert Agenten-Identitaet explizit via `--append-system-prompt-file <workspace>/CLAUDE.md`.\n\n"
|
|
"## Kontext\n"
|
|
"Ohne `--bare` walkt Claude Code alle Parent-Dirs hoch.\n"
|
|
"Referenz: `docs/development-plan.md` Abschnitt 2.9, `docs/cli-feature-answers.md` Frage 8\n\n"
|
|
"## Scope\n"
|
|
"**In:** `runner.ts` Args immer mit `--bare` + `--append-system-prompt-file <ws>/CLAUDE.md`, "
|
|
"Integration-Test mit `tests/fixtures/fake-claude.mjs`\n"
|
|
"**Out:** `--resume`-Flag (Phase 2), Streaming-Flags (Phase 5)\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] Args enthalten `[\"--bare\", \"--append-system-prompt-file\", \"<ws>/CLAUDE.md\"]`\n"
|
|
"- [ ] Integration-Test `tests/integration/no-parent-claude-md-leak.test.ts` gruen\n"
|
|
"- [ ] Dokumentations-Kommentar im Runner warum `--bare` zwingend ist\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-0/bare-and-append-identity`\n\n"
|
|
"## Abhaengigkeiten\nBlockiert durch: **#2 (DIS-002)**"
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-005: Path-Traversal-Check in /new-agent",
|
|
"milestone": 1,
|
|
"labels": [1, 9, 15, 18],
|
|
"body": (
|
|
"## Ziel\n"
|
|
"`/new-agent name:<x>` akzeptiert keinen Namen, der aus dem Workspace-Root ausbricht. "
|
|
"Defense in depth: Regex + `path.resolve`-Containment-Check.\n\n"
|
|
"## Kontext\n"
|
|
"Edge-Cases: `..`, Unicode-Homoglyphen, `.`, leere Strings, Windows-reserved Names.\n"
|
|
"Referenz: `docs/development-plan.md` Phase 0 Task 4\n\n"
|
|
"## Scope\n"
|
|
"**In:** Nach Regex in `new-agent.ts`: Containment-Check via `path.resolve`. Saubere User-Fehlermeldung in Discord.\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] Containment-Check aktiv in `src/commands/new-agent.ts`\n"
|
|
"- [ ] Unit-Test `tests/unit/path-traversal.test.ts` deckt `../etc`, `..`, `./foo`, `a/../b`, leerer String, `CON`, Unicode\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-0/path-traversal-check`\n\n"
|
|
"## Abhaengigkeiten\nKeine."
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-006: .claude/settings.json-Template haerten + CLAUDE.md Injection-Klausel",
|
|
"milestone": 1,
|
|
"labels": [1, 8, 15, 18],
|
|
"body": (
|
|
"## Ziel\n"
|
|
"Jeder neue Agent bekommt ein gehaertetes `.claude/settings.json` (Deny-Liste gegen `../**`, `.env`, "
|
|
"gefaehrliche Bash-Muster) und eine `CLAUDE.md` mit Prompt-Injection-Resistenz-Klausel.\n\n"
|
|
"## Kontext\n"
|
|
"Schema bestaetigt via `docs/cli-feature-answers.md` Frage 3.\n"
|
|
"Referenz: `docs/development-plan.md` Abschnitt 2.5\n\n"
|
|
"## Scope\n"
|
|
"**In:** `src/agent/identity.ts` Settings-Template mit vollstaendiger `permissions.deny`-Liste, "
|
|
"`CLAUDE.md`-Template erhaelt Sicherheits-Abschnitt\n"
|
|
"**Out:** PreToolUse-Hook-Script (Phase 4), Profile-spezifische Settings (Phase 4)\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] Frisch erstellter Workspace enthaelt `settings.json` mit vollstaendiger Deny-Liste\n"
|
|
"- [ ] `CLAUDE.md`-Template enthaelt Injection-Resistenz-Absatz\n"
|
|
"- [ ] Unit-Test `tests/unit/identity-templates.test.ts` gruen\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-0/harden-settings-and-claudemd`\n\n"
|
|
"## Abhaengigkeiten\nKeine."
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-007: Lock-File gegen Doppelstart",
|
|
"milestone": 1,
|
|
"labels": [1, 8, 16],
|
|
"body": (
|
|
"## Ziel\n"
|
|
"Zwei gleichzeitig laufende DisClaw-Instanzen auf derselben DB/Workspace-Root werden verhindert.\n\n"
|
|
"## Scope\n"
|
|
"**In:** `src/runtime/lockfile.ts`: `acquireLock(path)` legt `.disclaw.lock` mit PID+Timestamp an. "
|
|
"Stale-Lock-Recovery via process signal check. Graceful Shutdown loescht Lock.\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] Zweiter Startversuch bricht mit 'DisClaw bereits aktiv (PID X)' ab\n"
|
|
"- [ ] Stale-Lock wird automatisch uebernommen\n"
|
|
"- [ ] Unit-Test `tests/unit/lockfile.test.ts` gruen\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-0/startup-lockfile`\n\n"
|
|
"## Abhaengigkeiten\nBlockiert durch: **#1 (DIS-001)**"
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-008: Forgejo Actions CI (build, test, lint)",
|
|
"milestone": 1,
|
|
"labels": [1, 14, 16],
|
|
"body": (
|
|
"## Ziel\n"
|
|
"Jeder PR gegen `main` triggert CI: `npm run build`, `npm test`, `npm run lint`. Matrix: Linux + Windows.\n\n"
|
|
"## Scope\n"
|
|
"**In:** `.forgejo/workflows/ci.yml` mit `pull_request`-Trigger, Matrix `ubuntu-latest`/`windows-latest`, "
|
|
"ESLint-Basis-Config, `npm run lint`-Script\n"
|
|
"**Out:** Deployment-Jobs, Coverage, Release-Automation\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] CI laeuft gruen auf einem Test-PR\n"
|
|
"- [ ] Matrix deckt Linux + Windows ab\n"
|
|
"- [ ] `npm run lint` laeuft lokal\n"
|
|
"- [ ] README-Abschnitt 'CI' vorhanden\n\n"
|
|
"## Branch\n`phase-0/ci-pipeline`\n\n"
|
|
"## Abhaengigkeiten\nSollte nach DIS-002, DIS-003, DIS-005 kommen."
|
|
),
|
|
},
|
|
]
|
|
|
|
PHASE1_ISSUES = [
|
|
{
|
|
"title": "DIS-101: Pino-Logger einfuehren, console.* ersetzen",
|
|
"milestone": 2,
|
|
"labels": [2, 10, 16],
|
|
"body": (
|
|
"## Ziel\nStrukturiertes JSON-Logging ueber `pino`. Child-Logger pro Komponente. Kein `console.*` mehr im Produktionscode.\n\n"
|
|
"## Scope\n**In:** `src/runtime/logger.ts` mit `rootLogger` + `childLogger(component)`, "
|
|
"alle `console.*` in `src/` ersetzen, `pino-pretty` in `devDependencies`\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] `grep -r 'console\\.' src/` liefert keine Treffer\n"
|
|
"- [ ] Pino-Child-Logger mit `component:'runner'` im Output\n"
|
|
"- [ ] Unit-Test `tests/unit/logger.test.ts` gruen\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-1/pino-logger`\n\n## Abhaengigkeiten\nPhase 0 abgeschlossen."
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-102: sanitizeForDiscord -- Pfade aus Agent-Output entfernen",
|
|
"milestone": 2,
|
|
"labels": [2, 8, 16, 18],
|
|
"body": (
|
|
"## Ziel\nAbsolute Pfade, Home-Dir und Token-Patterns vor Discord-Send redaktieren. Verhindert Info-Leak.\n\n"
|
|
"## Scope\n**In:** `src/runtime/sanitize.ts` mit `sanitizeForDiscord(text, { repoRoot, home })`, "
|
|
"Router nutzt Sanitizer vor jedem `channel.send`\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] Test `tests/unit/sanitize-for-discord.test.ts` gruen (Repo-Pfad, Home-Pfad, Token-Regex, keine False-Positives)\n"
|
|
"- [ ] Router nutzt Sanitizer an allen Send-Stellen\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-1/sanitize-for-discord`\n\n## Abhaengigkeiten\nBlockiert durch: **DIS-101**"
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-103: ChannelQueue -- Per-Channel-FIFO",
|
|
"milestone": 2,
|
|
"labels": [2, 8, 15],
|
|
"body": (
|
|
"## Ziel\nMehrere Nachrichten im selben Channel seriell abarbeiten. Parallele Channels bleiben parallel. Keine Race Conditions.\n\n"
|
|
"## Scope\n**In:** `src/runtime/channel-queue.ts` mit `ChannelQueue`-Klasse, Integration in `router.ts`\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] Unit-Test `tests/unit/channel-queue.test.ts`: FIFO, Error-Recovery, Map-Cleanup gruen\n"
|
|
"- [ ] Integration-Test `tests/integration/channel-queue-router.test.ts` gruen\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-1/channel-queue`\n\n## Abhaengigkeiten\nBlockiert durch: **DIS-101**"
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-104: Globaler Semaphore (max_concurrent_agents)",
|
|
"milestone": 2,
|
|
"labels": [2, 8, 16],
|
|
"body": (
|
|
"## Ziel\nGlobaler Cap auf parallele `claude`-Prozesse. Default 4, konfigurierbar via `disclaw.yaml`.\n\n"
|
|
"## Scope\n**In:** `src/runtime/concurrency.ts` mit `Semaphore(max)`, Runner umschliesst Spawn-Call\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] Unit-Test `tests/unit/semaphore.test.ts`: Acquire, Queueing, Release, Error-Path gruen\n"
|
|
"- [ ] Config-Default 4 in `disclaw.yaml`\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-1/concurrency-semaphore`\n\n## Abhaengigkeiten\nKeine harten. Kann parallel zu DIS-103."
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-105: splitForDiscord -- Codeblock-aware Message-Splitting",
|
|
"milestone": 2,
|
|
"labels": [2, 8, 15],
|
|
"body": (
|
|
"## Ziel\nAntworten > 1900 Zeichen korrekt aufteilen, ohne Codeblöcke zu zerreissen. Fences am Split schliessen und im naechsten Chunk wieder oeffnen.\n\n"
|
|
"## Scope\n**In:** `src/discord/split.ts` mit `splitForDiscord(text, limit = 1900): string[]`, Router sendet Chunks sequenziell\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] Unit-Test `tests/unit/split-for-discord.test.ts`: kurz/am Limit/Codeblock/Fence/leer gruen\n"
|
|
"- [ ] Kein Chunk ueberschreitet 2000 Zeichen\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-1/split-for-discord`\n\n## Abhaengigkeiten\nBlockiert durch: **DIS-101**"
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-106: Runner-API-Refactoring -- RunResult-Typ",
|
|
"milestone": 2,
|
|
"labels": [2, 10, 16],
|
|
"body": (
|
|
"## Ziel\n`runner.ts` gibt typsicheren `RunResult` zurueck (discriminated union: success/timeout/cli-error/parse-error).\n\n"
|
|
"## Scope\n**In:** `src/agent/types.ts` mit `RunResult`, `runner.ts` gibt `RunResult`, alle Aufrufer handhaben alle Cases\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] `RunResult` ist discriminated union, kein `any`\n"
|
|
"- [ ] Unit-Test `tests/unit/runner-result.test.ts` gruen\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-1/runner-api-runresult`\n\n## Abhaengigkeiten\nBlockiert durch: **DIS-101**"
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-107: Zod-Schemas fuer Config, agent.yaml, CLI-JSON-Output",
|
|
"milestone": 2,
|
|
"labels": [2, 8, 16],
|
|
"body": (
|
|
"## Ziel\nAlle externen Daten (Config, `agent.yaml`, CLI-Output) via Zod validieren. Fruehzeitige Fehlermeldungen.\n\n"
|
|
"## Scope\n**In:** `src/config/schema.ts`, `src/agent/schema.ts`, `zod` in `dependencies`\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] Alle externen Eingaben durch Zod-Parse\n"
|
|
"- [ ] Unit-Test `tests/unit/schemas.test.ts` gruen\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-1/zod-schemas`\n\n## Abhaengigkeiten\nBlockiert durch: **DIS-101**"
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-108: DB-Schema SSOT -- src/db/schema.sql",
|
|
"milestone": 2,
|
|
"labels": [2, 10, 16],
|
|
"body": (
|
|
"## Ziel\nSQLite-Schema in einer `schema.sql`-Datei (Single Source of Truth). `database.ts` liest und wendet sie an.\n\n"
|
|
"## Scope\n**In:** `src/db/schema.sql` mit allen CREATE TABLE-Statements, `database.ts` ohne Inline-DDL\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] `schema.sql` enthaelt alle Tabellen und Indizes\n"
|
|
"- [ ] `database.ts` hat kein Inline-DDL mehr\n"
|
|
"- [ ] Integration-Test `tests/integration/db-init.test.ts` gruen\n"
|
|
"- [ ] `npm run build && npm test` gruen\n\n"
|
|
"## Branch\n`phase-1/db-schema-ssot`\n\n## Abhaengigkeiten\nBlockiert durch: **DIS-101**"
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-109: .env.example + Vitest-Setup + npm test-Skript",
|
|
"milestone": 2,
|
|
"labels": [2, 13, 16],
|
|
"body": (
|
|
"## Ziel\nNeues Repo-Clone laeuft sofort: `.env.example`, Vitest konfiguriert, `npm test` funktioniert ohne weitere Schritte.\n\n"
|
|
"## Scope\n**In:** `.env.example`, `vitest.config.ts`, `package.json` Scripts, `tests/`-Verzeichnis-Struktur\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] `npm test` laeuft gruen auf frischem Clone (nach `npm ci`)\n"
|
|
"- [ ] `.env.example` deckt alle Pflichtfelder ab\n"
|
|
"- [ ] `vitest.config.ts` konsistent mit `tsconfig.json`\n\n"
|
|
"## Branch\n`phase-1/vitest-setup`\n\n## Abhaengigkeiten\nKeine. Frueher in Phase 1 besser."
|
|
),
|
|
},
|
|
{
|
|
"title": "DIS-110: docs/cli-feature-probe.md -- Manuelles CLI-Regression-Script",
|
|
"milestone": 2,
|
|
"labels": [2, 12, 17],
|
|
"body": (
|
|
"## Ziel\nStrukturiertes Probe-Script verifiziert Annahmen aus `docs/cli-feature-answers.md` gegen die installierte CLI. Muss vor Phase-2-Start ausgefuehrt werden.\n\n"
|
|
"## Scope\n**In:** `docs/cli-feature-probe.md` mit step-by-step Kommandos fuer alle 4 Rest-Unsicherheiten (--resume-Flow, session_id, stream-json-Events, usage-Felder)\n\n"
|
|
"## Definition of Done\n"
|
|
"- [ ] Probe-Dokument enthaelt ausfuehrbare Kommandos fuer alle Unsicherheiten\n"
|
|
"- [ ] Dokument von Developer ausgefuehrt und Ergebnisse eingetragen\n\n"
|
|
"## Branch\n`phase-1/cli-probe`\n\n## Abhaengigkeiten\nKeine. Muss vor Phase-2-Freigabe abgeschlossen sein."
|
|
),
|
|
},
|
|
]
|
|
|
|
def create_issues(issues, phase_name):
|
|
print(f"\n=== {phase_name} ===")
|
|
# Get existing to avoid dupes
|
|
r = requests.get(f"{BASE}/issues?type=issues&state=open&limit=50&page=1", headers=HEADERS)
|
|
existing = {i["title"] for i in r.json()} if r.ok else set()
|
|
r2 = requests.get(f"{BASE}/issues?type=issues&state=closed&limit=50&page=1", headers=HEADERS)
|
|
existing |= {i["title"] for i in r2.json()} if r2.ok else set()
|
|
|
|
for issue in issues:
|
|
if issue["title"] in existing:
|
|
print(f" SKIP (exists): {issue['title'][:60]}")
|
|
continue
|
|
r = requests.post(f"{BASE}/issues", headers=HEADERS, json=issue)
|
|
if r.ok:
|
|
num = r.json()["number"]
|
|
print(f" #{num}: {issue['title'][:70]}")
|
|
else:
|
|
print(f" ERR {r.status_code}: {issue['title'][:60]} -- {r.text[:80]}")
|
|
|
|
create_issues(PHASE0_ISSUES, "Phase 0")
|
|
create_issues(PHASE1_ISSUES, "Phase 1")
|
|
print("\nDone.")
|