@glrs-dev/cli 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/README.md +14 -15
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-6RHN2EDH.js +93 -0
- package/dist/chunk-DEODG2LC.js +55 -0
- package/dist/chunk-FSAGM22T.js +17 -0
- package/dist/chunk-GQBZREK5.js +136 -0
- package/dist/chunk-HWMRY35D.js +139 -0
- package/dist/chunk-LMRDQ4GW.js +129 -0
- package/dist/chunk-NLPX2KOF.js +149 -0
- package/dist/chunk-P7PRH4I3.js +177 -0
- package/dist/chunk-VCN7RNLU.js +60 -0
- package/dist/chunk-VJFNIKQJ.js +120 -0
- package/dist/chunk-W37UX3U2.js +35 -0
- package/dist/chunk-YBCA3IP6.js +25 -0
- package/dist/chunk-YGNDPKIW.js +99 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +89 -36
- package/dist/commands/cleanup.d.ts +19 -0
- package/dist/commands/cleanup.js +11 -0
- package/dist/commands/create.d.ts +17 -0
- package/dist/commands/create.js +12 -0
- package/dist/commands/delete.d.ts +17 -0
- package/dist/commands/delete.js +12 -0
- package/dist/commands/go.d.ts +4 -0
- package/dist/commands/go.js +11 -0
- package/dist/commands/list.d.ts +15 -0
- package/dist/commands/list.js +12 -0
- package/dist/commands/switch.d.ts +11 -0
- package/dist/commands/switch.js +12 -0
- package/dist/commands/types.d.ts +10 -0
- package/dist/commands/types.js +0 -0
- package/dist/index.d.ts +16 -19
- package/dist/index.js +4 -1
- package/dist/lib/config.d.ts +14 -0
- package/dist/lib/config.js +14 -0
- package/dist/lib/fmt.d.ts +12 -0
- package/dist/lib/fmt.js +25 -0
- package/dist/lib/git.d.ts +26 -0
- package/dist/lib/git.js +25 -0
- package/dist/lib/registry.d.ts +14 -0
- package/dist/lib/registry.js +13 -0
- package/dist/lib/select.d.ts +21 -0
- package/dist/lib/select.js +10 -0
- package/dist/lib/worktree.d.ts +35 -0
- package/dist/lib/worktree.js +17 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/agents-md-writer.md +89 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/architecture-advisor.md +46 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/build.md +93 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/code-searcher.md +54 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/docs-maintainer.md +128 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/gap-analyzer.md +44 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/lib-reader.md +39 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-builder.md +107 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/pilot-planner.md +153 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/plan-reviewer.md +49 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/plan.md +144 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/prime.md +374 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/qa-reviewer.md +68 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/qa-thorough.md +63 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/research.md +138 -0
- package/dist/vendor/harness-opencode/dist/agents/shared/index.ts +26 -0
- package/dist/vendor/harness-opencode/dist/agents/shared/workflow-mechanics.md +32 -0
- package/dist/vendor/harness-opencode/dist/bin/memory-mcp-launcher.sh +145 -0
- package/dist/vendor/harness-opencode/dist/bin/plan-check.sh +255 -0
- package/dist/vendor/harness-opencode/dist/chunk-VJUETC6A.js +205 -0
- package/dist/vendor/harness-opencode/dist/chunk-VVMP6QWS.js +731 -0
- package/dist/vendor/harness-opencode/dist/chunk-XCZ3NOXR.js +703 -0
- package/dist/vendor/harness-opencode/dist/cli.d.ts +1 -0
- package/dist/vendor/harness-opencode/dist/cli.js +5096 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/autopilot.md +96 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/costs.md +94 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/fresh.md +382 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/init-deep.md +196 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/research.md +27 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/review.md +96 -0
- package/dist/vendor/harness-opencode/dist/commands/prompts/ship.md +104 -0
- package/dist/vendor/harness-opencode/dist/index.d.ts +21 -0
- package/dist/vendor/harness-opencode/dist/index.js +2092 -0
- package/dist/vendor/harness-opencode/dist/install-4EYR56OR.js +9 -0
- package/dist/vendor/harness-opencode/dist/skills/agent-estimation/SKILL.md +159 -0
- package/dist/vendor/harness-opencode/dist/skills/paths.ts +18 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/SKILL.md +49 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/dag-shape.md +47 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/decomposition.md +36 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/first-principles.md +29 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/milestones.md +57 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/self-review.md +46 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/task-context.md +47 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/touches-scope.md +47 -0
- package/dist/vendor/harness-opencode/dist/skills/pilot-planning/rules/verify-design.md +53 -0
- package/dist/vendor/harness-opencode/dist/skills/research/SKILL.md +350 -0
- package/dist/vendor/harness-opencode/dist/skills/research-auto/SKILL.md +283 -0
- package/dist/vendor/harness-opencode/dist/skills/research-local/SKILL.md +268 -0
- package/dist/vendor/harness-opencode/dist/skills/research-web/SKILL.md +119 -0
- package/dist/vendor/harness-opencode/dist/skills/review-plan/SKILL.md +32 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/AGENTS.md +946 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/README.md +60 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/SKILL.md +89 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/architecture-compound-components.md +112 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/state-context-interface.md +191 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-composition-patterns/rules/state-lift-state.md +125 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/AGENTS.md +2975 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/README.md +123 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/SKILL.md +137 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-hoist-static-io.md +142 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/dist/vendor/harness-opencode/dist/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/dist/vendor/harness-opencode/dist/skills/web-design-guidelines/SKILL.md +39 -0
- package/dist/vendor/harness-opencode/package.json +11 -0
- package/package.json +20 -15
- package/LICENSE +0 -21
- package/dist/chunk-TU23AE2F.js +0 -69
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: research
|
|
3
|
+
description: Research orchestrator — decomposes a research query into parallel workstreams, dispatches research skills (research / research-web / research-local / research-auto) as subagents, reviews findings for gaps, iterates, and synthesizes. Use when the user asks to investigate, explore, deep-dive, or understand a complex topic that needs multiple workstreams.
|
|
4
|
+
mode: all
|
|
5
|
+
model: anthropic/claude-opus-4-7
|
|
6
|
+
temperature: 0.3
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# @research — Research Orchestrator Agent
|
|
10
|
+
|
|
11
|
+
You are a research orchestrator. Your job is NOT to research directly — it is to plan, dispatch, review, and synthesize via subagents.
|
|
12
|
+
|
|
13
|
+
**Research Query:** $ARGUMENTS
|
|
14
|
+
|
|
15
|
+
## Core Principle
|
|
16
|
+
|
|
17
|
+
You are an **orchestrator only**. You do NOT:
|
|
18
|
+
- Use Glob, Grep, Read, or any exploration tool directly
|
|
19
|
+
- Synthesize findings yourself
|
|
20
|
+
- Review for gaps yourself
|
|
21
|
+
- Decide workstream classifications yourself
|
|
22
|
+
|
|
23
|
+
Every cognitive task is a subagent. You launch subagents and pass their outputs to other subagents.
|
|
24
|
+
|
|
25
|
+
## How to Invoke Skills
|
|
26
|
+
|
|
27
|
+
The four research skills are bundled with the harness:
|
|
28
|
+
|
|
29
|
+
1. **`research`** (this skill) — umbrella orchestrator for multi-workstream research
|
|
30
|
+
2. **`research-local`** — deep codebase research using parallel Explore subagents
|
|
31
|
+
3. **`research-web`** — multi-agent web research with skeleton-file pattern
|
|
32
|
+
4. **`research-auto`** — autonomous experimentation with `.lab/` directory
|
|
33
|
+
|
|
34
|
+
**To invoke a skill:** Use the Agent tool with a prompt instructing the subagent to read the skill via the Skill tool:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
Agent tool:
|
|
38
|
+
"You are a research agent.
|
|
39
|
+
|
|
40
|
+
## Research Query
|
|
41
|
+
{the full query or sub-question}
|
|
42
|
+
|
|
43
|
+
## Task
|
|
44
|
+
1. Read the bundled {skill-name} skill via the Skill tool and follow every instruction
|
|
45
|
+
2. Focus specifically on: {sub-question}
|
|
46
|
+
3. Report back with your complete findings"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 7-Phase Flow
|
|
50
|
+
|
|
51
|
+
### Phase 1: Plan — Subagent
|
|
52
|
+
|
|
53
|
+
Launch a **general-purpose subagent** to decompose the query into workstreams:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
PROMPT:
|
|
57
|
+
"You are a research planner. Given a research query, decompose it into workstreams
|
|
58
|
+
and classify each by research type.
|
|
59
|
+
|
|
60
|
+
Research Query: [QUERY]
|
|
61
|
+
|
|
62
|
+
For each workstream, provide:
|
|
63
|
+
1. A specific sub-question to answer
|
|
64
|
+
2. Classification: LOCAL, WEB, or AUTO
|
|
65
|
+
3. Why this classification (one sentence)
|
|
66
|
+
4. Dependencies: which other workstreams must complete first (if any)
|
|
67
|
+
|
|
68
|
+
Classification rules:
|
|
69
|
+
- LOCAL: codebase architecture, data flow, patterns, implementations
|
|
70
|
+
- WEB: external knowledge, best practices, market research, comparisons
|
|
71
|
+
- AUTO: experimentation with measurable outcomes (RARE)
|
|
72
|
+
|
|
73
|
+
Output 3-6 workstreams. Mark dependencies explicitly."
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Phase 2: Execute Round 1 — Parallel Agent Dispatches
|
|
77
|
+
|
|
78
|
+
Dispatch **one Agent per workstream**. Launch ALL independent workstreams in a SINGLE message.
|
|
79
|
+
|
|
80
|
+
For LOCAL workstreams: invoke `research-local` skill.
|
|
81
|
+
For WEB workstreams: invoke `research-web` skill.
|
|
82
|
+
For AUTO workstreams: invoke `research-auto` skill.
|
|
83
|
+
|
|
84
|
+
### Phase 3: Review Round 1 — Subagent
|
|
85
|
+
|
|
86
|
+
Launch a **general-purpose subagent** to review all findings and identify gaps.
|
|
87
|
+
|
|
88
|
+
### Phase 4: Execute Round 2 — Fill Gaps (If Needed)
|
|
89
|
+
|
|
90
|
+
If gaps found, dispatch gap-filling agents — ALL in ONE message.
|
|
91
|
+
|
|
92
|
+
### Phase 5: Review Round 2 — Subagent (If Phase 4 Ran)
|
|
93
|
+
|
|
94
|
+
Launch another review subagent with Round 1 + Round 2 findings.
|
|
95
|
+
|
|
96
|
+
### Phase 6: Synthesize — Subagent
|
|
97
|
+
|
|
98
|
+
Launch a **general-purpose subagent** to produce the final synthesis report.
|
|
99
|
+
|
|
100
|
+
### Phase 7: Final Quality Gate — Subagent
|
|
101
|
+
|
|
102
|
+
Launch a **general-purpose subagent** to score the final report (1-5 on 5 dimensions).
|
|
103
|
+
|
|
104
|
+
### Phase 8: Present
|
|
105
|
+
|
|
106
|
+
Present to the user:
|
|
107
|
+
1. Full synthesis report
|
|
108
|
+
2. Quality score
|
|
109
|
+
3. Research metadata (rounds, agents dispatched, modes used)
|
|
110
|
+
4. Follow-up suggestions if quality < 4.0
|
|
111
|
+
|
|
112
|
+
## Parallel Dispatch Rule
|
|
113
|
+
|
|
114
|
+
**ALL independent workstreams in ONE message.** Never sequential. Never one at a time.
|
|
115
|
+
|
|
116
|
+
## Workflow Mechanics Exception
|
|
117
|
+
|
|
118
|
+
If you realize this work should be on its own branch, do NOT ask the user. Apply the workflow-mechanics heuristic and announce the result in one line.
|
|
119
|
+
|
|
120
|
+
## How to Ask the User
|
|
121
|
+
|
|
122
|
+
Use the `question` tool. One question per call. Never bundle questions.
|
|
123
|
+
|
|
124
|
+
## PRIME-Delegation Brief Contract
|
|
125
|
+
|
|
126
|
+
When PRIME passes a brief via task tool:
|
|
127
|
+
- Trust the brief. Don't re-interview on points already resolved.
|
|
128
|
+
- The brief IS the research query — proceed directly to Phase 1.
|
|
129
|
+
- If the brief lacks critical context (e.g., no query provided), ask once then proceed.
|
|
130
|
+
|
|
131
|
+
## Red Flags — STOP
|
|
132
|
+
|
|
133
|
+
- About to use Skill() directly — USE AGENT TOOL with skill-read instruction
|
|
134
|
+
- About to research/synthesize/review yourself — LAUNCH A SUBAGENT
|
|
135
|
+
- About to skip planning/review phases — BOTH ARE MANDATORY
|
|
136
|
+
- About to launch agents sequentially — ONE MESSAGE, ALL INDEPENDENT AGENTS
|
|
137
|
+
- About to present raw outputs — SYNTHESIZE FIRST
|
|
138
|
+
- About to run a 4th round — MAX 3 ROUNDS, THEN PRESENT
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
function readMd(name: string): string {
|
|
8
|
+
// In the bundled dist/index.js, import.meta.url resolves to dist/,
|
|
9
|
+
// but the file is at dist/agents/shared/. In dev (running from src/),
|
|
10
|
+
// import.meta.url resolves to src/agents/shared/.
|
|
11
|
+
const candidates = [
|
|
12
|
+
join(HERE, name), // dev: src/agents/shared/
|
|
13
|
+
join(HERE, "agents", "shared", name), // dist: dist/ → dist/agents/shared/
|
|
14
|
+
join(HERE, "..", "..", "..", "src", "agents", "shared", name), // fallback dev
|
|
15
|
+
];
|
|
16
|
+
for (const p of candidates) {
|
|
17
|
+
try {
|
|
18
|
+
return readFileSync(p, "utf8");
|
|
19
|
+
} catch {
|
|
20
|
+
// try next
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
throw new Error(`Could not find shared file: ${name}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const WORKFLOW_MECHANICS_RULE: string = readMd("workflow-mechanics.md");
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Workflow-mechanics decisions
|
|
2
|
+
|
|
3
|
+
Users run this harness so they don't have to answer questions about *mechanics*. They want the agent to decide, announce, and move. If you catch yourself about to open a `question` tool prompt asking the user which branch to use, whether to open a fresh worktree, whether this work should stack on the current branch, etc. — **stop.** Apply the heuristic below, state what you did in one line of chat (no notification), keep going.
|
|
4
|
+
|
|
5
|
+
## What counts as a workflow-mechanics decision
|
|
6
|
+
|
|
7
|
+
**In scope (you decide — never ask):**
|
|
8
|
+
- Which branch to create or switch to for new work
|
|
9
|
+
- Whether to open a fresh worktree via `/fresh` or stay on the current checkout
|
|
10
|
+
- How to map a ticket ID to a branch name (Linear MCP → use its `branchName` field; otherwise derive a slug using the rules in the `/fresh` command: lowercase, replace non-alphanumeric runs with `-`, infer verb prefix `fix/`/`feat/`/`refactor/`/`docs/`/`chore/`, truncate to 50 chars)
|
|
11
|
+
- Whether to isolate unrelated work onto its own branch when the user is on a feature branch
|
|
12
|
+
- Which base branch to branch from (default: repo default; override only if the user's request mentions a release branch explicitly)
|
|
13
|
+
|
|
14
|
+
**Out of scope (existing rules still apply — don't confuse this section with those):**
|
|
15
|
+
- Deciding whether to update a plan mid-flight — existing Phase 3 rule: report and ask.
|
|
16
|
+
- Deciding whether to push, open a PR, or merge — always user-initiated via `/ship`. Hard rules below are the limit.
|
|
17
|
+
- Commit message wording — `/ship` auto-derives it from the plan and diff, no user review step. The user can amend after the fact if they want.
|
|
18
|
+
- Content decisions (file location, symbol naming, etc.) — follow the trivial-request defaults in Phase 1.
|
|
19
|
+
|
|
20
|
+
## The deterministic heuristic
|
|
21
|
+
|
|
22
|
+
Evaluate these rules in order. Stop at the first match. **No "it depends."** If you're picking between branches, use this table, not judgement.
|
|
23
|
+
|
|
24
|
+
1. **Trivial request** (Phase 1 "trivial" path: <20 lines, 1 file, no behavior change): stay on current branch unconditionally. No branching, no announcement. A typo fix on `main` stays on `main`.
|
|
25
|
+
2. **Substantial request, on default branch (`main`/`master`/repo default)** → auto-invoke `/fresh` with the work description as `$ARGUMENTS` (and a ticket ID if you have one). Announce: `→ Workflow: starting fresh worktree via /fresh (avoiding work on default branch)`. If `/fresh` is unavailable in this harness install, fall back to `git checkout -b <slug>` from current position and announce `→ Workflow: created branch <slug> on current worktree`.
|
|
26
|
+
3. **Detached HEAD** → same as rule 2. Treat detached HEAD as "not on a branch" → needs isolation.
|
|
27
|
+
4. **Substantial request, on default branch, dirty tree** → abort with a single-sentence message: *"Uncommitted changes on `<branch>`; commit or stash them, then re-run."* Do NOT stash automatically — the user's WIP is theirs.
|
|
28
|
+
5. **Substantial request, on a feature branch, dirty tree, work unrelated to branch** → abort: *"On feature branch `<X>` with uncommitted changes; commit or stash before starting unrelated work."*
|
|
29
|
+
6. **Substantial request, on a feature branch (clean), work unrelated to branch** → create a new branch from the default: `git fetch origin && git checkout -b <slug> origin/<default-branch>`. Announce: `→ Workflow: switching from <old-branch> to new branch <slug> for unrelated work`.
|
|
30
|
+
7. **Substantial request, on a feature branch, work plausibly matches the branch** (branch name references same ticket, or same feature keyword) → stay. No announcement (status quo is the expected default).
|
|
31
|
+
|
|
32
|
+
Announcement format: plain chat, prefixed `→ Workflow:`. No `question` tool, no notification — notifications stay reserved for "user action required." Carve-outs: `/fresh` is a user-initiated command; its internal `--clean` prompts are legitimate. `/ship` executes end-to-end without per-step prompts once invoked (see ship.md Stop conditions for the only exceptions). This rule governs *agent-initiated* decisions only.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# memory-mcp-launcher.sh — resolve per-repo MEMORY_FILE_PATH and exec the memory MCP server.
|
|
3
|
+
#
|
|
4
|
+
# Fixes glorious-opencode issue #24: the stock @modelcontextprotocol/server-memory
|
|
5
|
+
# invocation via `npx -y ... ` with a RELATIVE MEMORY_FILE_PATH resolves inside the
|
|
6
|
+
# npx cache directory (because OpenCode does not set cwd for MCP launches), so every
|
|
7
|
+
# project silently shares one volatile file buried in ~/.npm/_npx/<hash>/.
|
|
8
|
+
#
|
|
9
|
+
# This launcher resolves the project root via git (worktree-aware, submodule-aware,
|
|
10
|
+
# bare-repo-aware), sets an ABSOLUTE MEMORY_FILE_PATH, ensures the target directory
|
|
11
|
+
# exists, adds a narrow entry to <repo>/.agent/.gitignore so memory.json is not
|
|
12
|
+
# accidentally committed, then execs the real memory server.
|
|
13
|
+
#
|
|
14
|
+
# CONSTRAINTS (do not break these):
|
|
15
|
+
# - Must be bash 3.2 compatible (macOS default /bin/bash).
|
|
16
|
+
# - MUST NOT write anything to stdout before the final `exec npx`. MCP uses stdio
|
|
17
|
+
# JSON-RPC; any stdout noise corrupts the handshake. All diagnostics go to stderr.
|
|
18
|
+
# - Called via `bash "$HOME/.config/opencode/bin/memory-mcp-launcher.sh"` from the
|
|
19
|
+
# opencode.json `command` array. The executable bit is nice-to-have (stored as
|
|
20
|
+
# 100755 in git via `git update-index --chmod=+x`) but not required.
|
|
21
|
+
#
|
|
22
|
+
# ENV CONTRACT:
|
|
23
|
+
# - MEMORY_MCP_LAUNCHER_DEBUG=1
|
|
24
|
+
# After resolving MEMORY_FILE_PATH, log it to stderr as
|
|
25
|
+
# `[memory-mcp-launcher] MEMORY_FILE_PATH=<path>` and still exec npx. Useful
|
|
26
|
+
# when a user asks "where is my memory going?".
|
|
27
|
+
# - MEMORY_MCP_LAUNCHER_PRINT_AND_EXIT=1
|
|
28
|
+
# Test-only. After resolving MEMORY_FILE_PATH, print it to stderr and exit 0
|
|
29
|
+
# (skipping the npx exec). Enables launcher behavior tests without needing
|
|
30
|
+
# an actual MCP handshake. Intentionally undocumented to end users.
|
|
31
|
+
|
|
32
|
+
set -Eeuo pipefail
|
|
33
|
+
|
|
34
|
+
# Init under -u so we can do `[[ -n "$target_path" ]]` later without risk.
|
|
35
|
+
target_path=""
|
|
36
|
+
fallback_path="${HOME}/.config/opencode/memory/fallback.json"
|
|
37
|
+
|
|
38
|
+
# -------- resolve target --------
|
|
39
|
+
# Only attempt git-based resolution if git is on PATH. If anything goes wrong in
|
|
40
|
+
# this block, we drop through to the fallback path — never hard-fail on a git
|
|
41
|
+
# edge case (bare repo, damaged .git, submodule, worktree with missing main, etc).
|
|
42
|
+
if command -v git >/dev/null 2>&1; then
|
|
43
|
+
|
|
44
|
+
# Bare-repo check FIRST, before any path resolution. A bare repo has no
|
|
45
|
+
# working tree so per-repo memory has no sensible location — fall back.
|
|
46
|
+
_is_bare="$(git rev-parse --is-bare-repository 2>/dev/null || printf "")"
|
|
47
|
+
if [[ "$_is_bare" != "true" ]]; then
|
|
48
|
+
|
|
49
|
+
# Get the current repo's working-tree root. Always returns an absolute path.
|
|
50
|
+
# Returns empty (stderr suppressed) when:
|
|
51
|
+
# - not in a git repo
|
|
52
|
+
# - CWD inside the .git directory itself
|
|
53
|
+
# - git is too old / broken
|
|
54
|
+
_toplevel="$(git rev-parse --show-toplevel 2>/dev/null || printf "")"
|
|
55
|
+
|
|
56
|
+
if [[ -n "$_toplevel" ]]; then
|
|
57
|
+
target_path="$_toplevel"
|
|
58
|
+
|
|
59
|
+
# Worktree-share rewrite. For a `git worktree`, the per-worktree toplevel
|
|
60
|
+
# differs from the MAIN worktree — but we want all worktrees of the same
|
|
61
|
+
# repo to share one memory.json. `--git-common-dir` points at the shared
|
|
62
|
+
# .git dir. Its parent is the main working tree.
|
|
63
|
+
#
|
|
64
|
+
# Resolution is CWD-sensitive (common-dir may be relative), so canonicalize
|
|
65
|
+
# inside a subshell via `cd -P && pwd -P`. Failures fall back to _toplevel.
|
|
66
|
+
_common_dir_raw="$(git rev-parse --git-common-dir 2>/dev/null || printf "")"
|
|
67
|
+
if [[ -n "$_common_dir_raw" ]]; then
|
|
68
|
+
_common_abs="$(cd -P "$_common_dir_raw" 2>/dev/null && pwd -P || printf "")"
|
|
69
|
+
if [[ -n "$_common_abs" ]]; then
|
|
70
|
+
_main_candidate="$(dirname "$_common_abs")"
|
|
71
|
+
# Only rewrite if:
|
|
72
|
+
# (a) candidate differs from current toplevel (i.e., we're in a worktree)
|
|
73
|
+
# (b) candidate has a .git entry (real working tree — gitfile OR dir)
|
|
74
|
+
# Submodules: their common-dir is <super>/.git/modules/<name>, parent is
|
|
75
|
+
# .git/modules which has no .git entry, so (b) rejects and we keep the
|
|
76
|
+
# submodule's own toplevel. Bare repos were already skipped above.
|
|
77
|
+
if [[ "$_main_candidate" != "$_toplevel" ]] \
|
|
78
|
+
&& [[ -e "$_main_candidate/.git" ]]; then
|
|
79
|
+
target_path="$_main_candidate"
|
|
80
|
+
fi
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
85
|
+
unset _is_bare _toplevel _common_dir_raw _common_abs _main_candidate
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# -------- validate target, create dir + narrow gitignore --------
|
|
89
|
+
MEMORY_FILE_PATH=""
|
|
90
|
+
if [[ -n "$target_path" ]]; then
|
|
91
|
+
if mkdir -p "${target_path}/.agent" 2>/dev/null; then
|
|
92
|
+
MEMORY_FILE_PATH="${target_path}/.agent/memory.json"
|
|
93
|
+
|
|
94
|
+
# Narrow gitignore: only ignore `memory.json`. Do NOT touch the file if it
|
|
95
|
+
# already exists — the user (or an earlier run) may have set their own rules
|
|
96
|
+
# and we refuse to clobber sibling tracked content like `.agent/plans/`.
|
|
97
|
+
_gi="${target_path}/.agent/.gitignore"
|
|
98
|
+
if [[ ! -e "$_gi" ]]; then
|
|
99
|
+
_tmp_gi="$(mktemp "${target_path}/.agent/.gitignore.XXXXXX" 2>/dev/null || printf "")"
|
|
100
|
+
if [[ -n "$_tmp_gi" ]]; then
|
|
101
|
+
if printf "memory.json\n" > "$_tmp_gi" 2>/dev/null; then
|
|
102
|
+
mv "$_tmp_gi" "$_gi" 2>/dev/null || rm -f "$_tmp_gi" 2>/dev/null || true
|
|
103
|
+
else
|
|
104
|
+
rm -f "$_tmp_gi" 2>/dev/null || true
|
|
105
|
+
fi
|
|
106
|
+
fi
|
|
107
|
+
unset _tmp_gi
|
|
108
|
+
fi
|
|
109
|
+
unset _gi
|
|
110
|
+
else
|
|
111
|
+
# mkdir failed (read-only mount, perms). Fall through to fallback.
|
|
112
|
+
target_path=""
|
|
113
|
+
fi
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# -------- fallback path --------
|
|
117
|
+
if [[ -z "$MEMORY_FILE_PATH" ]]; then
|
|
118
|
+
# Use bash parameter expansion instead of `dirname` so the launcher works on
|
|
119
|
+
# restricted PATHs (e.g., when the environment is so bare that coreutils isn't
|
|
120
|
+
# on PATH, such as a session started with env -i + a minimal PATH).
|
|
121
|
+
_fb_dir="${fallback_path%/*}"
|
|
122
|
+
if ! mkdir -p "$_fb_dir" 2>/dev/null; then
|
|
123
|
+
printf "[memory-mcp-launcher] ERROR: cannot create %s — memory server cannot run\n" "$_fb_dir" >&2
|
|
124
|
+
exit 1
|
|
125
|
+
fi
|
|
126
|
+
MEMORY_FILE_PATH="$fallback_path"
|
|
127
|
+
unset _fb_dir
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# -------- debug/test hooks --------
|
|
131
|
+
if [[ "${MEMORY_MCP_LAUNCHER_DEBUG:-0}" == "1" ]]; then
|
|
132
|
+
printf "[memory-mcp-launcher] MEMORY_FILE_PATH=%s\n" "$MEMORY_FILE_PATH" >&2
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
if [[ "${MEMORY_MCP_LAUNCHER_PRINT_AND_EXIT:-0}" == "1" ]]; then
|
|
136
|
+
printf "%s\n" "$MEMORY_FILE_PATH" >&2
|
|
137
|
+
exit 0
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
# Intentionally override any pre-existing MEMORY_FILE_PATH in the env. The whole
|
|
141
|
+
# purpose of this launcher is to set the correct path — if a user wants a
|
|
142
|
+
# project-local override, they should set it in their project's opencode.json.
|
|
143
|
+
export MEMORY_FILE_PATH
|
|
144
|
+
|
|
145
|
+
exec npx -y @modelcontextprotocol/server-memory "$@"
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# plan-check.sh — parse a plan file's plan-state fence and report on it.
|
|
3
|
+
#
|
|
4
|
+
# Modes:
|
|
5
|
+
# plan-check.sh <path> Prints a summary line then one line per item:
|
|
6
|
+
# `total=N done=M pending=K invalid=I`
|
|
7
|
+
# `STATUS ID VERIFY` (one per item)
|
|
8
|
+
#
|
|
9
|
+
# plan-check.sh --run <path> Prints the verify command of each PENDING
|
|
10
|
+
# item on stdout, one per line, raw. The
|
|
11
|
+
# caller is responsible for executing them.
|
|
12
|
+
# This script NEVER executes verify commands
|
|
13
|
+
# itself — that would bypass the caller's
|
|
14
|
+
# bash-permission scope.
|
|
15
|
+
#
|
|
16
|
+
# plan-check.sh --check <path>
|
|
17
|
+
# Structural validation only. Exits 1 if any
|
|
18
|
+
# fence item is missing a required field.
|
|
19
|
+
#
|
|
20
|
+
# Fence format, inside `## Acceptance criteria`:
|
|
21
|
+
#
|
|
22
|
+
# ```plan-state
|
|
23
|
+
# - [ ] id: a1
|
|
24
|
+
# intent: Prose description of business intent (one line).
|
|
25
|
+
# tests:
|
|
26
|
+
# - path/to/test.sh::"some test name"
|
|
27
|
+
# - path/to/other.ts::"another test"
|
|
28
|
+
# verify: bash path/to/test.sh
|
|
29
|
+
#
|
|
30
|
+
# - [x] id: a2
|
|
31
|
+
# ...
|
|
32
|
+
# ```
|
|
33
|
+
#
|
|
34
|
+
# Backward compat: a plan without a ```plan-state fence emits the line
|
|
35
|
+
# `legacy` and exits 0 — callers treat it as "old format, fall back".
|
|
36
|
+
#
|
|
37
|
+
# Portability: POSIX bash + awk + grep only. No sed -i.
|
|
38
|
+
|
|
39
|
+
set -eu
|
|
40
|
+
|
|
41
|
+
MODE=""
|
|
42
|
+
PLAN_PATH=""
|
|
43
|
+
|
|
44
|
+
case "${1:-}" in
|
|
45
|
+
--run) MODE=run; PLAN_PATH="${2:-}" ;;
|
|
46
|
+
--check) MODE=check; PLAN_PATH="${2:-}" ;;
|
|
47
|
+
-h|--help|"")
|
|
48
|
+
sed -n '2,34p' "$0"
|
|
49
|
+
exit 0
|
|
50
|
+
;;
|
|
51
|
+
*) MODE=summary; PLAN_PATH="${1:-}" ;;
|
|
52
|
+
esac
|
|
53
|
+
|
|
54
|
+
if [[ -z "${PLAN_PATH:-}" ]]; then
|
|
55
|
+
echo "plan-check.sh: missing plan path" >&2
|
|
56
|
+
exit 2
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
if [[ ! -f "$PLAN_PATH" ]]; then
|
|
60
|
+
echo "plan-check.sh: file not found: $PLAN_PATH" >&2
|
|
61
|
+
exit 2
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Extract the plan-state fence body into a temp file. awk state machine:
|
|
65
|
+
# enter `## Acceptance criteria`, enter ``` plan-state, exit on next ```.
|
|
66
|
+
FENCE_BODY="$(awk '
|
|
67
|
+
/^## Acceptance criteria/ { in_ac = 1; next }
|
|
68
|
+
/^## / && in_ac && !in_fence { in_ac = 0 }
|
|
69
|
+
in_ac && /^```plan-state[[:space:]]*$/ { in_fence = 1; next }
|
|
70
|
+
in_fence && /^```[[:space:]]*$/ { in_fence = 0; next }
|
|
71
|
+
in_fence { print }
|
|
72
|
+
' "$PLAN_PATH")"
|
|
73
|
+
|
|
74
|
+
if [[ -z "$FENCE_BODY" ]]; then
|
|
75
|
+
# No fence found — legacy plan. Report and exit cleanly.
|
|
76
|
+
if [[ "$MODE" == "summary" ]]; then
|
|
77
|
+
echo "legacy (no plan-state fence)"
|
|
78
|
+
fi
|
|
79
|
+
# --run on a legacy plan emits nothing (no commands to run).
|
|
80
|
+
# --check on a legacy plan succeeds (we're accepting legacy plans).
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# Parse items. awk state machine:
|
|
85
|
+
# - A line `- [ ] id: ID` or `- [x] id: ID` starts a new item.
|
|
86
|
+
# - While inside an item, indented keys `intent:`, `tests:`, `verify:` set
|
|
87
|
+
# fields. Under `tests:`, subsequent ` - ...` lines extend the list
|
|
88
|
+
# until the next key or the next item.
|
|
89
|
+
# - Items are separated by one or more blank lines OR by the next `- [`.
|
|
90
|
+
#
|
|
91
|
+
# We emit a tab-delimited record per item:
|
|
92
|
+
# STATUS<TAB>ID<TAB>INTENT<TAB>TESTS<TAB>VERIFY
|
|
93
|
+
# TESTS is a `|`-delimited list. Missing fields are the empty string.
|
|
94
|
+
PARSED="$(echo "$FENCE_BODY" | awk '
|
|
95
|
+
function flush() {
|
|
96
|
+
if (cur_id != "") {
|
|
97
|
+
# Trim trailing/leading whitespace on each field
|
|
98
|
+
gsub(/^[[:space:]]+|[[:space:]]+$/, "", cur_intent)
|
|
99
|
+
gsub(/^[[:space:]]+|[[:space:]]+$/, "", cur_verify)
|
|
100
|
+
gsub(/^\||\|$/, "", cur_tests)
|
|
101
|
+
printf "%s\t%s\t%s\t%s\t%s\n", cur_status, cur_id, cur_intent, cur_tests, cur_verify
|
|
102
|
+
}
|
|
103
|
+
cur_status = ""; cur_id = ""; cur_intent = ""; cur_tests = ""; cur_verify = ""
|
|
104
|
+
in_tests = 0
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/^-[[:space:]]+\[[[:space:]xX[:space:]]\][[:space:]]+id:/ {
|
|
108
|
+
flush()
|
|
109
|
+
status = $0
|
|
110
|
+
sub(/^-[[:space:]]+\[[[:space:]]*/, "", status)
|
|
111
|
+
sub(/\].*$/, "", status)
|
|
112
|
+
# status is either empty/space (" ") -> pending, or "x"/"X" -> done
|
|
113
|
+
if (status ~ /[xX]/) cur_status = "done"; else cur_status = "pending"
|
|
114
|
+
# Capture id
|
|
115
|
+
id_part = $0
|
|
116
|
+
sub(/^.*id:[[:space:]]*/, "", id_part)
|
|
117
|
+
cur_id = id_part
|
|
118
|
+
next
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/^[[:space:]]*intent:/ {
|
|
122
|
+
field = $0
|
|
123
|
+
sub(/^[[:space:]]*intent:[[:space:]]*/, "", field)
|
|
124
|
+
cur_intent = field
|
|
125
|
+
in_tests = 0
|
|
126
|
+
next
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/^[[:space:]]*intent\b/ {
|
|
130
|
+
# already handled
|
|
131
|
+
next
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/^[[:space:]]*tests:/ {
|
|
135
|
+
in_tests = 1
|
|
136
|
+
next
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/^[[:space:]]*verify:/ {
|
|
140
|
+
field = $0
|
|
141
|
+
sub(/^[[:space:]]*verify:[[:space:]]*/, "", field)
|
|
142
|
+
cur_verify = field
|
|
143
|
+
in_tests = 0
|
|
144
|
+
next
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# Continuation lines inside tests: list
|
|
148
|
+
in_tests && /^[[:space:]]+-[[:space:]]/ {
|
|
149
|
+
line = $0
|
|
150
|
+
sub(/^[[:space:]]+-[[:space:]]+/, "", line)
|
|
151
|
+
if (cur_tests == "") cur_tests = line
|
|
152
|
+
else cur_tests = cur_tests "|" line
|
|
153
|
+
next
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
# Continuation line for intent (indented without `-`, after intent is set
|
|
157
|
+
# and before another key). Append with a space separator.
|
|
158
|
+
!in_tests && /^[[:space:]]{4,}[^-[:space:]]/ && cur_id != "" && cur_intent != "" && cur_verify == "" {
|
|
159
|
+
line = $0
|
|
160
|
+
sub(/^[[:space:]]+/, "", line)
|
|
161
|
+
cur_intent = cur_intent " " line
|
|
162
|
+
next
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
END { flush() }
|
|
166
|
+
' 2>&1)"
|
|
167
|
+
|
|
168
|
+
# If PARSED contains awk errors, surface them as invalid.
|
|
169
|
+
if echo "$PARSED" | grep -q '^awk:'; then
|
|
170
|
+
echo "plan-check.sh: parser error" >&2
|
|
171
|
+
echo "$PARSED" >&2
|
|
172
|
+
exit 3
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# Count totals.
|
|
176
|
+
total=0
|
|
177
|
+
done_count=0
|
|
178
|
+
pending_count=0
|
|
179
|
+
invalid_count=0
|
|
180
|
+
invalid_reasons=()
|
|
181
|
+
|
|
182
|
+
while IFS=$'\t' read -r status id intent tests verify; do
|
|
183
|
+
[[ -z "$status" ]] && continue
|
|
184
|
+
total=$((total + 1))
|
|
185
|
+
if [[ -z "$id" ]]; then
|
|
186
|
+
invalid_count=$((invalid_count + 1))
|
|
187
|
+
invalid_reasons+=("missing id")
|
|
188
|
+
continue
|
|
189
|
+
fi
|
|
190
|
+
if [[ -z "$intent" ]]; then
|
|
191
|
+
invalid_count=$((invalid_count + 1))
|
|
192
|
+
invalid_reasons+=("$id: missing intent")
|
|
193
|
+
continue
|
|
194
|
+
fi
|
|
195
|
+
if [[ -z "$tests" ]]; then
|
|
196
|
+
invalid_count=$((invalid_count + 1))
|
|
197
|
+
invalid_reasons+=("$id: missing tests")
|
|
198
|
+
continue
|
|
199
|
+
fi
|
|
200
|
+
if [[ -z "$verify" ]]; then
|
|
201
|
+
invalid_count=$((invalid_count + 1))
|
|
202
|
+
invalid_reasons+=("$id: missing verify")
|
|
203
|
+
continue
|
|
204
|
+
fi
|
|
205
|
+
if [[ "$status" == "done" ]]; then
|
|
206
|
+
done_count=$((done_count + 1))
|
|
207
|
+
else
|
|
208
|
+
pending_count=$((pending_count + 1))
|
|
209
|
+
fi
|
|
210
|
+
done <<< "$PARSED"
|
|
211
|
+
|
|
212
|
+
case "$MODE" in
|
|
213
|
+
summary)
|
|
214
|
+
printf 'total=%d done=%d pending=%d invalid=%d\n' \
|
|
215
|
+
"$total" "$done_count" "$pending_count" "$invalid_count"
|
|
216
|
+
while IFS=$'\t' read -r status id intent tests verify; do
|
|
217
|
+
[[ -z "$status" ]] && continue
|
|
218
|
+
# For the summary-per-item line, prefer displaying the verify
|
|
219
|
+
# command (truncated) so the reader sees what gates each item.
|
|
220
|
+
v="${verify:0:60}"
|
|
221
|
+
if [[ -n "$verify" && ${#verify} -gt 60 ]]; then v="${v}…"; fi
|
|
222
|
+
printf '%s %s %s\n' "$status" "$id" "$v"
|
|
223
|
+
done <<< "$PARSED"
|
|
224
|
+
if [[ "$invalid_count" -gt 0 ]]; then
|
|
225
|
+
echo "invalid:"
|
|
226
|
+
for r in "${invalid_reasons[@]}"; do
|
|
227
|
+
echo " $r"
|
|
228
|
+
done
|
|
229
|
+
fi
|
|
230
|
+
;;
|
|
231
|
+
|
|
232
|
+
run)
|
|
233
|
+
# Emit verify command per PENDING item, one per line. Skip done items,
|
|
234
|
+
# skip invalid items. Caller executes via their own bash permission.
|
|
235
|
+
while IFS=$'\t' read -r status id intent tests verify; do
|
|
236
|
+
[[ -z "$status" ]] && continue
|
|
237
|
+
[[ "$status" == "done" ]] && continue
|
|
238
|
+
[[ -z "$verify" ]] && continue
|
|
239
|
+
[[ -z "$intent" || -z "$tests" ]] && continue
|
|
240
|
+
echo "$verify"
|
|
241
|
+
done <<< "$PARSED"
|
|
242
|
+
;;
|
|
243
|
+
|
|
244
|
+
check)
|
|
245
|
+
# Structural validation. Exit 1 if anything invalid.
|
|
246
|
+
if [[ "$invalid_count" -gt 0 ]]; then
|
|
247
|
+
echo "plan-check: $invalid_count invalid item(s):" >&2
|
|
248
|
+
for r in "${invalid_reasons[@]}"; do
|
|
249
|
+
echo " $r" >&2
|
|
250
|
+
done
|
|
251
|
+
exit 1
|
|
252
|
+
fi
|
|
253
|
+
printf 'ok: %d item(s) pass structural validation\n' "$total"
|
|
254
|
+
;;
|
|
255
|
+
esac
|