Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 21 additions & 18 deletions .replworks/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- **Project Directory**: the directory in which the command runs. All file access is confined to it.
- **Authoritative Documents**: the five documents named in PRODUCT_SPEC.md.
- **Canonical Text**: the canonical AGENTS.md content, obtained from the single published source defined in PRODUCT_SPEC.md.
- **Helper Script Text**: the exact `ask-human.sh` content obtained from the published source defined in PRODUCT_SPEC.md.
- **Task Ledger**: the ordered list of task entries parsed from one TASKS.md text.
- **Task Entry**: a task ID plus a completion state, as recognized by the Task Ledger Reader.
- **Mark**: one occurrence of the UNVERIFIED text within a heading line of an authoritative document.
Expand Down Expand Up @@ -81,12 +82,13 @@ Task Ledger Reader and Unverified Mark Scanner → Document Line Classifier

- Responsibilities:
- Obtain the Canonical Text for the selected init document first.
- Make no write when the Canonical Text is `Failed`.
- Process items in this order: AGENTS.md, then the `.replworks/` directory, then `.replworks/PRODUCT_SPEC.md`, `.replworks/TECH_STACK.md`, `.replworks/ARCHITECTURE.md`, `.replworks/TASKS.md`.
- When automatic-run is selected, obtain Helper Script Text before any write.
- Make no write when required source text is `Failed`.
- Process items in this order: AGENTS.md, then the `.replworks/` directory, then `.replworks/PRODUCT_SPEC.md`, `.replworks/TECH_STACK.md`, `.replworks/ARCHITECTURE.md`, `.replworks/TASKS.md`, then the helper-script state.
- Mark an item `skipped` when it is present. Otherwise create it (AGENTS.md with the Canonical Text, the directory, and each document as an empty file).
- Create nothing else.
- For standard selection, remove an existing helper script. For automatic-run selection, create the helper-script directory and file only when absent; never replace an existing helper script.
- Stop at the first write failure.
- Inputs: Request with the selected document source, Canonical Text fact, Workspace presence facts.
- Inputs: Request with the selected document source, Canonical Text fact, optional Helper Script Text fact, and Workspace presence facts.
- Outputs: Outcome with one `(item path, created | skipped)` entry per processed item, and execution errors.
- Ownership boundaries:
- Owns the item order and the created/skipped decision.
Expand Down Expand Up @@ -132,9 +134,9 @@ Task Ledger Reader and Unverified Mark Scanner → Document Line Classifier
- Only after that, obtain the Canonical Text for the selected sync document. Record an execution error and stop when it is `Failed`.
- Compare local text and Canonical Text for exact equality.
- When equal: return `UpToDate` and make no write.
- When different: obtain the difference from the Difference Calculator, then replace the AGENTS.md content with the Canonical Text.
- When different: obtain the difference from the Difference Calculator, obtain Helper Script Text only for automatic-run selection, then replace AGENTS.md and adjust helper-script state.
- When replacement fails: keep the computed difference in the Outcome and record an execution error.
- Inputs: Request with the selected document source, local AGENTS.md fact, Canonical Text fact.
- Inputs: Request with the selected document source, local AGENTS.md fact, Canonical Text fact, optional Helper Script Text fact, and helper-script presence fact.
- Outputs: Outcome with `UpToDate | Replaced(difference) | ReplaceFailed(difference)`, and execution errors.
- Ownership boundaries:
- Owns the order of local check, retrieval, comparison, difference, replacement.
Expand All @@ -146,7 +148,7 @@ Task Ledger Reader and Unverified Mark Scanner → Document Line Classifier
- Responsibilities:
- Provide the only access to files in the Project Directory.
- `Read` operations: report presence of a path, read the text of a path. Each returns `Present | Absent | Failed`.
- `Write` operations: create a file only when absent, create a directory only when absent, replace a file's whole content.
- `Write` operations: create a file only when absent, create a directory only when absent, replace a file's whole content, and delete a known-present file.
- Judge presence by the existence of an entry at the path, regardless of entry kind.
- Reject any path outside the Project Directory.
- Report write success only when the full new content is stored.
Expand All @@ -160,16 +162,16 @@ Task Ledger Reader and Unverified Mark Scanner → Document Line Classifier
### C7. Canonical Document Provider

- Responsibilities:
- Obtain the Canonical Text from the source selected by the request. The allowed sources are the two published Markdown documents in PRODUCT_SPEC.md.
- Return the Canonical Text exactly as the canonical content, with no normalization.
- Retrieve at most once per invocation. Later requests in the same invocation reuse the first result, including a failure.
- Obtain Canonical Text from the source selected by the request, and Helper Script Text from its published source when requested. The allowed sources are those listed in PRODUCT_SPEC.md.
- Return each text exactly as the source content, with no normalization.
- Retrieve each requested source at most once per invocation. Later requests in the same invocation reuse the first result, including a failure.
- Treat these as `Failed`: source unreachable, unsuccessful response, or an empty response from the source.
- Keep the source's representation private. Other components never see it.
- Inputs: a source selection. Init supplies the user's selection; commands without a selection use PRODUCT_SPEC.md's default canonical document.
- Outputs: `Present(Canonical Text) | Failed(reason)`.
- Inputs: a source selection and, when needed, the helper-script URL. Init and sync supply the user's selection; commands without a selection use PRODUCT_SPEC.md's default canonical document.
- Outputs: `Present(text) | Failed(reason)`.
- Ownership boundaries:
- Owns all network access and the extraction of the Canonical Text.
- Does not compare, write, print, or access any address other than the canonical source.
- Does not compare, write, print, or access any address other than the requested canonical sources.

### C8. Base Branch Reader

Expand Down Expand Up @@ -321,9 +323,9 @@ Task Ledger Reader and Unverified Mark Scanner → Document Line Classifier
Request → Init Coordinator
Command Interface → validated document selection
Canonical Document Provider(selection) → Canonical Text fact
[automatic-run] → Helper Script Text fact
[Failed] → Outcome(error), no write
[Present] → Workspace presence(AGENTS.md), presence(.replworks/)
→ Workspace Write: create absent items
[Present] → Workspace presence and writes for the standard items and helper-script state
→ Outcome(items, errors) → Command Interface → lines + status
```

Expand Down Expand Up @@ -362,7 +364,8 @@ Request → Sync Coordinator
exact comparison
equal → Outcome(UpToDate)
different → Difference Calculator → difference
→ Workspace Write: replace AGENTS.md
→ [automatic-run] Helper Script Text fact
→ Workspace Write: replace AGENTS.md and adjust helper-script state
→ Outcome(Replaced | ReplaceFailed, difference)
→ Command Interface → lines + status
```
Expand All @@ -375,10 +378,10 @@ Request → Sync Coordinator

### Output line content (owned by Command Interface)

- init: one line per processed item, containing the item path and whether it was created or skipped.
- init: one line per processed item, containing the item path and whether it was created, skipped, or deleted.
- status: complete count, total count, and first incomplete task ID or a no-incomplete-task indication.
- check: violation lines first (Rule Evaluator order), then execution error lines (gathering order). A violation line contains rule identifier, file path, message. An error line contains the subject and message.
- sync: an up-to-date indication, or the difference lines followed by any error line.
- sync: an up-to-date indication, or the difference lines, helper-script state line when changed, followed by any error line.
- sync difference lines: removed lines are red and added lines are green for terminal output; non-terminal output is plain.
- All paths printed are relative to the Project Directory.

Expand Down
21 changes: 17 additions & 4 deletions .replworks/PRODUCT_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
- Canonical AGENTS.md documents:
- https://www.repl.net/documents/agents.md
- https://www.repl.net/documents/agents-auto.md
- Automatic-run helper script:
- https://www.repl.net/scripts/ask-human.sh
- Init document selection: the document selected interactively for the current `repl init` invocation. The selection is not persisted.
- Default canonical document: `https://www.repl.net/documents/agents.md`, used by commands that do not receive a document selection, currently `repl check`.
- Authoritative documents: the five files AGENTS.md, .replworks/PRODUCT_SPEC.md, .replworks/TECH_STACK.md, .replworks/ARCHITECTURE.md, .replworks/TASKS.md.
Expand All @@ -55,6 +57,7 @@
- Text lines printed by commands.
- Exit code of each command.
- AGENTS.md file and .replworks/ directory created by repl init.
- `.replworks/scripts/ask-human.sh` created or removed by the selected init/sync mode.
- AGENTS.md file replaced by repl sync.

## Constraints
Expand All @@ -67,6 +70,8 @@
- Relied behavior: serves the default canonical AGENTS.md content and the first init option.
- https://www.repl.net/documents/agents-auto.md
- Relied behavior: serves the automatic-run AGENTS.md content and the second init option.
- https://www.repl.net/scripts/ask-human.sh
- Relied behavior: serves the exact script content installed for automatic-run projects.
- Git repository of the project directory
- Relied behavior: provides the content of TASKS.md on the base branch.

Expand All @@ -80,7 +85,7 @@
- EL-001: repl init
- Purpose: create AGENTS.md, the .replworks/ directory, and the four authoritative documents under .replworks/ in the project directory.
- Input: an interactive selection of one of the two published AGENTS.md documents.
- Output: one line per created item and one line per skipped item, each containing the item path.
- Output: one line per created, skipped, or deleted item, each containing the item path.
- Exit: 0 on completion, 2 on execution error.
- EL-002: repl status
- Purpose: show task progress.
Expand Down Expand Up @@ -117,12 +122,14 @@

- FR-001: repl init MUST present the user with exactly these choices, in this order: `https://www.repl.net/documents/agents.md` and `https://www.repl.net/documents/agents-auto.md`, then create AGENTS.md with the content of the selected document when AGENTS.md does not exist.
- FR-002: repl init MUST create the .replworks/ directory in the project directory when it does not exist.
- FR-003: repl init MUST NOT modify an existing file.
- FR-004: repl init MUST NOT create any file or directory other than AGENTS.md, .replworks/, and the four documents defined in FR-036.
- FR-003: repl init MUST NOT modify an existing file except that selecting the standard AGENTS document MUST remove an existing `.replworks/scripts/ask-human.sh`.
- FR-004: repl init MUST NOT create any file or directory other than AGENTS.md, .replworks/, the four documents defined in FR-036, and `.replworks/scripts/ask-human.sh` plus its parent directory when the automatic-run document is selected.
- FR-005: repl init MUST print the path of each item it created.
- FR-006: repl init MUST print the path of each item it skipped because the item already exists.
- FR-007: repl init MUST exit with code 0 when it completes, including when it skipped items.
- FR-036: repl init MUST create .replworks/PRODUCT_SPEC.md, .replworks/TECH_STACK.md, .replworks/ARCHITECTURE.md, and .replworks/TASKS.md as empty files when they do not exist.
- FR-037: When the automatic-run document is selected, repl init MUST create `.replworks/scripts/ask-human.sh` from the exact response text at `https://www.repl.net/scripts/ask-human.sh` when the file does not exist, and MUST leave an existing file unchanged.
- FR-038: When the standard AGENTS document is selected, repl init MUST remove `.replworks/scripts/ask-human.sh` when it exists.

### status

Expand Down Expand Up @@ -156,12 +163,14 @@
- FR-029: repl sync MUST print an indication that AGENTS.md is up to date when its content is identical to the selected document.
- FR-030: repl sync MUST print the line-by-line difference between the local AGENTS.md and the selected document when the contents differ.
- FR-031: repl sync MUST replace the content of AGENTS.md with the selected document's content when the contents differ.
- FR-039: When repl sync replaces AGENTS.md because its contents differ, selecting the automatic-run document MUST create the helper script from the exact script response when the script does not exist, and selecting the standard document MUST remove the helper script when it exists.
- FR-040: When AGENTS.md content is identical to the selected document, repl sync MUST NOT create, modify, or remove the helper script.
- FR-035: repl sync MUST render removed difference lines in red and added difference lines in green when its output is a terminal, and MUST render plain difference lines otherwise.

### all commands

- FR-032: Every command MUST operate only on the project directory.
- FR-033: repl-cli MUST NOT access any network resource other than https://www.repl.net/documents/agents.md and https://www.repl.net/documents/agents-auto.md.
- FR-033: repl-cli MUST NOT access any network resource other than the two published AGENTS documents and `https://www.repl.net/scripts/ask-human.sh`.
- FR-034: Only repl init and repl sync MUST write files.

## User Flows
Expand Down Expand Up @@ -238,6 +247,10 @@
- AC-019: Given a project directory, when repl check runs, then no file is modified. (FR-019, EL-003)
- AC-020: Given an AGENTS.md identical to the selected document, when repl sync runs, then the file is unmodified, the output indicates that AGENTS.md is up to date, and the exit code is 0. (FR-027, FR-028, FR-031, EL-005)
- AC-021: Given an AGENTS.md that differs from the selected document, when repl sync runs, then the output contains the line-by-line difference, AGENTS.md equals the selected document, and the exit code is 0. (FR-027, FR-029, FR-030, FR-031, EL-005)
- AC-041: Given an empty directory and automatic-run selection, repl init creates `.replworks/scripts/ask-human.sh` with the exact live script response.
- AC-042: Given an existing helper script and standard selection, repl init removes the helper script while leaving an existing AGENTS.md unchanged.
- AC-043: Given differing AGENTS.md content, repl sync creates the missing helper script for automatic-run selection or removes an existing helper script for standard selection.
- AC-044: Given identical AGENTS.md content, repl sync leaves the helper script state unchanged and does not request the script URL.
- AC-022: Given that the selected init document is unreachable, when repl init runs, then no file is created or modified, an error line is printed, and the exit code is 2. (ERR-001, EL-001)
- AC-023: Given that the selected sync document is unreachable, when repl sync runs, then AGENTS.md is unmodified, an error line is printed, and the exit code is 2. (ERR-001, EL-005)
- AC-024: Given that https://www.repl.net/documents/agents.md is unreachable and a violation of rule documents-exist exists, when repl check runs, then the violation is reported, an error line is printed, and the exit code is 2. (ERR-001, FR-026, EL-003)
Expand Down
11 changes: 11 additions & 0 deletions .replworks/TASKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,14 @@
- If the input ends or fails before a valid selection, one error line is printed, the exit code is 2, and nothing is created or modified.
- Existing-item skip behavior and init write-failure behavior from T-006 and T-007 remain passing for both selections.
- Unit tests cover selection validation, source selection, and failure handling.

- [X] T-026 Manage ask-human.sh for the selected AGENTS mode
- Satisfies: PRODUCT_SPEC.md § Functional Requirements / init (FR-003, FR-004, FR-037, FR-038); § Functional Requirements / sync (FR-039, FR-040); § Functional Requirements / all commands (FR-033); § Acceptance Criteria (AC-041 through AC-044); ARCHITECTURE.md § Components (C2, C5, C6, C7)
- External boundary: yes
- Acceptance criteria:
- The live script source at `https://www.repl.net/scripts/ask-human.sh` is observed and its response is stored byte-for-byte for automatic-run initialization and synchronization.
- Automatic-run init creates `.replworks/scripts/ask-human.sh` when absent and leaves an existing helper script unchanged.
- Standard init removes an existing helper script and leaves AGENTS.md unchanged when it already exists.
- Sync adjusts helper-script state only when AGENTS.md differs from the selected document; equal content performs no helper-script network request or file operation.
- A failed helper-script retrieval prevents writes to the project for init and prevents AGENTS.md replacement for sync.
- Unit tests cover helper-source retrieval, creation, deletion, skip behavior, and equal-content no-op behavior.
2 changes: 1 addition & 1 deletion .replworks/TECH_STACK.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- Module path MUST be `github.com/replworks/repl-cli`.
- Dependency manager: Go modules. go.mod and go.sum MUST be committed.
- github.com/spf13/cobra v1.x. MUST implement every command.
- Canonical documents MUST be retrieved as unmodified response text over HTTPS with the standard-library `net/http` client. The provider MUST support the two published Markdown document URLs defined in PRODUCT_SPEC.md.
- Canonical documents and the automatic-run helper script MUST be retrieved as unmodified response text over HTTPS with the standard-library `net/http` client. The provider MUST support the two published Markdown document URLs and the helper-script URL defined in PRODUCT_SPEC.md.
- github.com/aymanbagabas/go-udiff v0.x. MUST produce line-by-line diffs.
- Go standard library MUST be used for: net/http, log/slog, os/exec, bufio, regexp, testing, net/http/httptest.
- `git` executable, invoked through os/exec. MUST read TASKS.md from the base branch.
Expand Down
9 changes: 8 additions & 1 deletion internal/canon/canon.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Document string
const (
AgentsDocument Document = "https://www.repl.net/documents/agents.md"
AgentsAutoDocument Document = "https://www.repl.net/documents/agents-auto.md"
AskHumanScript Document = "https://www.repl.net/scripts/ask-human.sh"
// SourceURL is the default document used by commands without a selection.
SourceURL = string(AgentsDocument)
)
Expand Down Expand Up @@ -45,11 +46,17 @@ func New(client *http.Client) *Provider {
// NewForDocument returns a Provider reading document. A nil client uses a
// default client with a timeout.
func NewForDocument(client *http.Client, document Document) *Provider {
return NewForURL(client, string(document))
}

// NewForURL returns a Provider reading url. A nil client uses a default client
// with a timeout.
func NewForURL(client *http.Client, url string) *Provider {
if client == nil {
client = &http.Client{Timeout: requestTimeout}
}

return &Provider{client: client, url: string(document)}
return &Provider{client: client, url: url}
}

// Canonical returns the canonical AGENTS.md. The source is contacted at most
Expand Down
15 changes: 15 additions & 0 deletions internal/canon/canon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,21 @@ func TestCanonicalCanSelectTheAutomaticDocument(t *testing.T) {
}
}

func TestCanonicalCanRetrieveTheAskHumanScript(t *testing.T) {
want := "#!/usr/bin/env bash\necho ask-human\n"
client, transport := serveText(t, want)

got := NewForURL(client, string(AskHumanScript)).Canonical()

if !got.IsPresent() || got.Value() != want {
t.Fatalf("Canonical() = %v, want %q", got, want)
}
requests := transport.requested()
if len(requests) != 1 || requests[0] != string(AskHumanScript) {
t.Fatalf("requests = %q, want one request to %q", requests, AskHumanScript)
}
}

func TestCanonicalRequestsOnlyTheSelectedSource(t *testing.T) {
client, transport := serveText(t, recordedText(t))

Expand Down
Loading
Loading