@tenkara/afm-linux-arm64 4.0.0-alpha.7 → 4.0.0-alpha.9

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.
Files changed (151) hide show
  1. package/bin/afm +2 -2
  2. package/content/CHANGELOG.md +50 -0
  3. package/content/PRINCIPLES.md +0 -2
  4. package/content/README-light.md +0 -2
  5. package/content/README.md +0 -2
  6. package/content/core/ach.md +0 -5
  7. package/content/core/afm.md +24 -6
  8. package/content/core/prd.md +0 -2
  9. package/content/core/protocol.md +1 -3
  10. package/content/hooks/README.md +0 -2
  11. package/content/interview/bootstrap.md +4 -2
  12. package/content/interview/promotion.md +0 -2
  13. package/content/interview/refactor.md +0 -2
  14. package/content/interview/triggers.md +0 -2
  15. package/content/modules/vireya-ui/ach.patch.md +1 -1
  16. package/content/ops/analyze.md +8 -7
  17. package/content/ops/deliver.md +23 -25
  18. package/content/ops/diagnose.md +1 -4
  19. package/content/ops/evolve.md +62 -13
  20. package/content/ops/export-template.md +1 -1
  21. package/content/ops/generalize.md +1 -4
  22. package/content/ops/policy.md +0 -2
  23. package/content/ops/reflect.md +19 -17
  24. package/content/ops/remediate.md +1 -4
  25. package/content/ops/restructure.md +3 -6
  26. package/content/ops/rule.md +0 -2
  27. package/content/ops/tasks.md +18 -18
  28. package/content/ops/update.md +29 -4
  29. package/content/ops/verify.md +0 -2
  30. package/content/questions/init.questions.yaml +13 -0
  31. package/content/roles/explorer.md +1 -1
  32. package/content/roles/reviewer.md +1 -1
  33. package/content/rubrics/failure-classification.md +0 -2
  34. package/content/rubrics/solid-triggers.md +0 -2
  35. package/content/skills/adr.md +3 -3
  36. package/content/skills/analyze.md +3 -9
  37. package/content/skills/clarify.md +2 -8
  38. package/content/skills/deliver.md +4 -20
  39. package/content/skills/diagnose.md +3 -11
  40. package/content/skills/evolve.md +6 -30
  41. package/content/skills/export-template.md +2 -5
  42. package/content/skills/generalize.md +6 -22
  43. package/content/skills/gotcha.md +3 -3
  44. package/content/skills/init.md +2 -7
  45. package/content/skills/plan.md +3 -9
  46. package/content/skills/policy.md +0 -6
  47. package/content/skills/promote.md +1 -3
  48. package/content/skills/protocol.md +2 -3
  49. package/content/skills/reconcile.md +2 -6
  50. package/content/skills/refactor.md +2 -7
  51. package/content/skills/reflect.md +7 -17
  52. package/content/skills/remediate.md +3 -9
  53. package/content/skills/restructure.md +8 -14
  54. package/content/skills/rule.md +3 -4
  55. package/content/skills/specify.md +3 -9
  56. package/content/skills/tasks.md +5 -15
  57. package/content/skills/update.md +6 -4
  58. package/content/skills/verify.md +1 -3
  59. package/content/templates/adr.template.md +1 -1
  60. package/content/templates/evolution-report.template.md +5 -2
  61. package/content/templates/feature-plan.template.md +0 -2
  62. package/content/templates/feature-research.template.md +1 -2
  63. package/content/templates/feature-spec.template.md +1 -1
  64. package/content/templates/feature-tasks.induced.template.md +58 -34
  65. package/content/templates/feature-tasks.template.md +56 -34
  66. package/content/templates/protocols/p1.md +1 -1
  67. package/content/templates/protocols/p2.md +1 -1
  68. package/content/templates/protocols/p3.md +1 -1
  69. package/package.json +1 -1
  70. package/plugin/agy/plugin.json +1 -1
  71. package/plugin/agy/skills/afm-adr/SKILL.md +7 -3
  72. package/plugin/agy/skills/afm-analyze/SKILL.md +5 -7
  73. package/plugin/agy/skills/afm-clarify/SKILL.md +4 -6
  74. package/plugin/agy/skills/afm-deliver/SKILL.md +6 -20
  75. package/plugin/agy/skills/afm-diagnose/SKILL.md +5 -9
  76. package/plugin/agy/skills/afm-evolve/SKILL.md +9 -29
  77. package/plugin/agy/skills/afm-export-template/SKILL.md +6 -5
  78. package/plugin/agy/skills/afm-generalize/SKILL.md +8 -20
  79. package/plugin/agy/skills/afm-gotcha/SKILL.md +7 -3
  80. package/plugin/agy/skills/afm-init/SKILL.md +3 -6
  81. package/plugin/agy/skills/afm-plan/SKILL.md +5 -7
  82. package/plugin/agy/skills/afm-policy/SKILL.md +2 -4
  83. package/plugin/agy/skills/afm-promote/SKILL.md +4 -2
  84. package/plugin/agy/skills/afm-protocol/SKILL.md +6 -3
  85. package/plugin/agy/skills/afm-reconcile/SKILL.md +5 -5
  86. package/plugin/agy/skills/afm-refactor/SKILL.md +3 -6
  87. package/plugin/agy/skills/afm-reflect/SKILL.md +9 -15
  88. package/plugin/agy/skills/afm-remediate/SKILL.md +5 -7
  89. package/plugin/agy/skills/afm-restructure/SKILL.md +10 -12
  90. package/plugin/agy/skills/afm-rule/SKILL.md +7 -4
  91. package/plugin/agy/skills/afm-specify/SKILL.md +5 -7
  92. package/plugin/agy/skills/afm-tasks/SKILL.md +7 -13
  93. package/plugin/agy/skills/afm-update/SKILL.md +10 -4
  94. package/plugin/agy/skills/afm-verify/SKILL.md +4 -2
  95. package/plugin/claude-code/.claude-plugin/plugin.json +1 -1
  96. package/plugin/claude-code/agents/afm-explorer.md +1 -1
  97. package/plugin/claude-code/agents/afm-reviewer.md +1 -1
  98. package/plugin/claude-code/skills/adr/SKILL.md +7 -3
  99. package/plugin/claude-code/skills/analyze/SKILL.md +5 -7
  100. package/plugin/claude-code/skills/clarify/SKILL.md +4 -6
  101. package/plugin/claude-code/skills/deliver/SKILL.md +6 -20
  102. package/plugin/claude-code/skills/diagnose/SKILL.md +5 -9
  103. package/plugin/claude-code/skills/evolve/SKILL.md +9 -29
  104. package/plugin/claude-code/skills/export-template/SKILL.md +6 -5
  105. package/plugin/claude-code/skills/generalize/SKILL.md +8 -20
  106. package/plugin/claude-code/skills/gotcha/SKILL.md +7 -3
  107. package/plugin/claude-code/skills/init/SKILL.md +3 -6
  108. package/plugin/claude-code/skills/plan/SKILL.md +5 -7
  109. package/plugin/claude-code/skills/policy/SKILL.md +2 -4
  110. package/plugin/claude-code/skills/promote/SKILL.md +4 -2
  111. package/plugin/claude-code/skills/protocol/SKILL.md +6 -3
  112. package/plugin/claude-code/skills/reconcile/SKILL.md +5 -5
  113. package/plugin/claude-code/skills/refactor/SKILL.md +3 -6
  114. package/plugin/claude-code/skills/reflect/SKILL.md +9 -15
  115. package/plugin/claude-code/skills/remediate/SKILL.md +5 -7
  116. package/plugin/claude-code/skills/restructure/SKILL.md +10 -12
  117. package/plugin/claude-code/skills/rule/SKILL.md +7 -4
  118. package/plugin/claude-code/skills/specify/SKILL.md +5 -7
  119. package/plugin/claude-code/skills/tasks/SKILL.md +7 -13
  120. package/plugin/claude-code/skills/update/SKILL.md +10 -4
  121. package/plugin/claude-code/skills/verify/SKILL.md +4 -2
  122. package/plugin/opencode/afm/index.js +245 -0
  123. package/plugin/opencode/afm/tui.js +21 -0
  124. package/plugin/opencode/agents/afm-executor.md +22 -14
  125. package/plugin/opencode/agents/afm-explorer.md +73 -28
  126. package/plugin/opencode/agents/afm-reviewer.md +73 -28
  127. package/plugin/opencode/skills/afm-adr/SKILL.md +7 -3
  128. package/plugin/opencode/skills/afm-analyze/SKILL.md +5 -7
  129. package/plugin/opencode/skills/afm-clarify/SKILL.md +4 -6
  130. package/plugin/opencode/skills/afm-deliver/SKILL.md +6 -20
  131. package/plugin/opencode/skills/afm-diagnose/SKILL.md +5 -9
  132. package/plugin/opencode/skills/afm-evolve/SKILL.md +9 -29
  133. package/plugin/opencode/skills/afm-export-template/SKILL.md +6 -5
  134. package/plugin/opencode/skills/afm-generalize/SKILL.md +8 -20
  135. package/plugin/opencode/skills/afm-gotcha/SKILL.md +7 -3
  136. package/plugin/opencode/skills/afm-init/SKILL.md +3 -6
  137. package/plugin/opencode/skills/afm-plan/SKILL.md +5 -7
  138. package/plugin/opencode/skills/afm-policy/SKILL.md +2 -4
  139. package/plugin/opencode/skills/afm-promote/SKILL.md +4 -2
  140. package/plugin/opencode/skills/afm-protocol/SKILL.md +6 -3
  141. package/plugin/opencode/skills/afm-reconcile/SKILL.md +5 -5
  142. package/plugin/opencode/skills/afm-refactor/SKILL.md +3 -6
  143. package/plugin/opencode/skills/afm-reflect/SKILL.md +9 -15
  144. package/plugin/opencode/skills/afm-remediate/SKILL.md +5 -7
  145. package/plugin/opencode/skills/afm-restructure/SKILL.md +10 -12
  146. package/plugin/opencode/skills/afm-rule/SKILL.md +7 -4
  147. package/plugin/opencode/skills/afm-specify/SKILL.md +5 -7
  148. package/plugin/opencode/skills/afm-tasks/SKILL.md +7 -13
  149. package/plugin/opencode/skills/afm-update/SKILL.md +10 -4
  150. package/plugin/opencode/skills/afm-verify/SKILL.md +4 -2
  151. package/plugin/opencode/afm.js +0 -196
@@ -0,0 +1,245 @@
1
+ // AFM for opencode 2, server side: forwards what the hooks decide on to `afm hook <Event>` with the
2
+ // native tool name, the harness marker and the session, and carries the answer back: a deny throws,
3
+ // injected context rides in the tool's result or the system prompt, and a notice for the operator
4
+ // goes to the terminal client (tui.js) as the `notice` event of the afm RPC. A hook that cannot
5
+ // answer never blocks the tool. All decisions live in the afm CLI; this file only translates
6
+ // opencode's plugin API.
7
+ import { spawn } from "node:child_process";
8
+
9
+ const MARKER = "opencode";
10
+ const TIMEOUT_MS = 20000;
11
+ const LOOKUP_TIMEOUT_MS = 2000;
12
+ // The tools whose calls AFM decides on or records; every other call is not worth a process.
13
+ const FORWARDED = new Set(["write", "edit", "shell", "skill"]);
14
+ // The events that end a turn: opencode cannot hold a session open, so each one only records the handoff.
15
+ const ENDED = new Set([
16
+ "session.execution.succeeded",
17
+ "session.execution.failed",
18
+ "session.execution.interrupted",
19
+ ]);
20
+
21
+ function afmHook(event, payload) {
22
+ return new Promise((resolve) => {
23
+ let out = "";
24
+ const child = spawn("afm", ["hook", event], {
25
+ stdio: ["pipe", "pipe", "ignore"],
26
+ });
27
+ const timer = setTimeout(() => {
28
+ child.kill();
29
+ resolve("");
30
+ }, TIMEOUT_MS);
31
+ child.stdout.on("data", (chunk) => {
32
+ out += chunk;
33
+ });
34
+ child.on("error", () => {
35
+ clearTimeout(timer);
36
+ resolve("");
37
+ });
38
+ child.on("close", () => {
39
+ clearTimeout(timer);
40
+ resolve(out);
41
+ });
42
+ child.stdin.on("error", () => {});
43
+ child.stdin.end(JSON.stringify(payload));
44
+ });
45
+ }
46
+
47
+ /** The `hookSpecificOutput` of an `afm hook` answer; empty when there is none or it is not JSON. */
48
+ function decisionOf(out) {
49
+ try {
50
+ return JSON.parse(out).hookSpecificOutput ?? {};
51
+ } catch {
52
+ return {};
53
+ }
54
+ }
55
+
56
+ /** The `systemMessage` of an `afm hook` answer: what the operator must read, not the model; undefined when there is none. */
57
+ function noticeOf(out) {
58
+ try {
59
+ const notice = JSON.parse(out).systemMessage;
60
+ return typeof notice === "string" && notice !== "" ? notice : undefined;
61
+ } catch {
62
+ return undefined;
63
+ }
64
+ }
65
+
66
+ /** The afm RPC: one event, the notice the terminal client shows as a toast. Plain JSON Schema, no Effect. */
67
+ const RPC = {
68
+ id: "afm",
69
+ methods: {},
70
+ events: {
71
+ notice: {
72
+ schema: {
73
+ type: "object",
74
+ properties: { message: { type: "string" } },
75
+ required: ["message"],
76
+ },
77
+ },
78
+ },
79
+ };
80
+
81
+ const bounded = (promise) => {
82
+ let timer;
83
+ const limit = new Promise((resolve) => {
84
+ timer = setTimeout(resolve, LOOKUP_TIMEOUT_MS, undefined);
85
+ });
86
+ return Promise.race([promise.catch(() => undefined), limit]).finally(() =>
87
+ clearTimeout(timer),
88
+ );
89
+ };
90
+
91
+ /** A tool result's content as a list of parts: the host may hand a plain string. */
92
+ const partsOf = (content) =>
93
+ typeof content === "string"
94
+ ? [{ type: "text", text: content }]
95
+ : Array.isArray(content)
96
+ ? content
97
+ : [];
98
+
99
+ /** The text the model reads in a tool result. */
100
+ const textOf = (result) =>
101
+ partsOf(result?.content)
102
+ .filter((part) => part?.type === "text")
103
+ .map((part) => part.text)
104
+ .join("\n");
105
+
106
+ /**
107
+ * Whether a session is a sub-agent's (it has a parent). The lookup is bounded and its failure is not an
108
+ * error: the call is then treated as the main thread's. Only an answered lookup is remembered, so a failed
109
+ * one is tried again.
110
+ */
111
+ async function isChild(session, sessionID, cache) {
112
+ if (typeof sessionID !== "string") return false;
113
+ if (cache.has(sessionID)) return cache.get(sessionID);
114
+ const info = await bounded(
115
+ Promise.resolve().then(() => session?.get?.({ sessionID })),
116
+ );
117
+ if (info === undefined || info === null) return false;
118
+ const child = typeof info.parentID === "string";
119
+ cache.set(sessionID, child);
120
+ return child;
121
+ }
122
+
123
+ export default {
124
+ id: "afm",
125
+ async setup(ctx) {
126
+ const directory = ctx.location?.directory;
127
+ // A host whose RPC is missing or refuses the definition still runs the hooks: only the toast is lost.
128
+ const rpc = await Promise.resolve()
129
+ .then(() => ctx.rpc?.register?.(RPC, {}))
130
+ .catch(() => undefined);
131
+ const notify = async (message) => {
132
+ try {
133
+ await rpc?.events?.emit?.("notice", { message });
134
+ } catch {
135
+ // nowhere to show it: the next session start tries again
136
+ }
137
+ };
138
+ const children = new Map();
139
+ const starts = new Map();
140
+ const pending = new Map();
141
+ const childOf = (sessionID) => isChild(ctx.session, sessionID, children);
142
+ const base = (sessionID, child) => ({
143
+ harness: MARKER,
144
+ session_id: sessionID,
145
+ cwd: directory,
146
+ ...(child ? { agent_id: sessionID } : {}),
147
+ });
148
+ // The digest a session starts with: the main thread's, or the sub-agent's rules. Asked once.
149
+ const startOf = (sessionID, agent) => {
150
+ if (typeof sessionID !== "string") return Promise.resolve(undefined);
151
+ if (!starts.has(sessionID))
152
+ starts.set(
153
+ sessionID,
154
+ (async () => {
155
+ const child = await childOf(sessionID);
156
+ if (!child) {
157
+ const out = await afmHook("SessionStart", {
158
+ ...base(sessionID, false),
159
+ source: "startup",
160
+ });
161
+ const notice = noticeOf(out);
162
+ if (notice) await notify(notice);
163
+ return decisionOf(out).additionalContext;
164
+ }
165
+ if (typeof agent !== "string") return undefined;
166
+ return decisionOf(
167
+ await afmHook("SubagentStart", {
168
+ ...base(sessionID, true),
169
+ agent_type: agent,
170
+ }),
171
+ ).additionalContext;
172
+ })(),
173
+ );
174
+ return starts.get(sessionID);
175
+ };
176
+
177
+ // Runs before every model request: the system prompt is rebuilt each time, so the digest is added each time.
178
+ await ctx.session.hook("context", async (event) => {
179
+ const text = await startOf(event.sessionID, event.agent);
180
+ if (text) event.system.push({ type: "text", text });
181
+ });
182
+ await ctx.tool.hook("execute.before", async (event) => {
183
+ if (!FORWARDED.has(event.tool)) return;
184
+ const child = await childOf(event.sessionID);
185
+ const decision = decisionOf(
186
+ await afmHook("PreToolUse", {
187
+ ...base(event.sessionID, child),
188
+ tool_name: event.tool,
189
+ tool_input: event.input ?? {},
190
+ }),
191
+ );
192
+ if (decision.permissionDecision === "deny")
193
+ throw new Error(decision.permissionDecisionReason);
194
+ // A PreToolUse cannot speak to the agent before the tool runs: its context joins the result.
195
+ if (decision.additionalContext && event.id)
196
+ pending.set(event.id, decision.additionalContext);
197
+ });
198
+ await ctx.tool.hook("execute.after", async (event) => {
199
+ if (!FORWARDED.has(event.tool)) return;
200
+ const child = await childOf(event.sessionID);
201
+ const exit =
202
+ event.status === "completed" ? event.result?.metadata?.exit : undefined;
203
+ const failed =
204
+ event.tool === "shell" && typeof exit === "number" && exit !== 0;
205
+ const decision = decisionOf(
206
+ await afmHook(failed ? "PostToolUseFailure" : "PostToolUse", {
207
+ ...base(event.sessionID, child),
208
+ tool_name: event.tool,
209
+ tool_input: event.input ?? {},
210
+ ...(failed ? { exit, error: textOf(event.result) } : {}),
211
+ }),
212
+ );
213
+ const context = [pending.get(event.id), decision.additionalContext]
214
+ .filter(Boolean)
215
+ .join("\n\n");
216
+ pending.delete(event.id);
217
+ if (context && event.status === "completed")
218
+ event.result = {
219
+ ...event.result,
220
+ content: [
221
+ ...partsOf(event.result?.content),
222
+ { type: "text", text: context },
223
+ ],
224
+ };
225
+ });
226
+
227
+ const controller = new AbortController();
228
+ (async () => {
229
+ for await (const event of ctx.event.subscribe({
230
+ signal: controller.signal,
231
+ })) {
232
+ if (!ENDED.has(event?.type)) continue;
233
+ const sessionID = event.data?.sessionID;
234
+ if (!(await childOf(sessionID)))
235
+ await afmHook("Stop", base(sessionID, false));
236
+ }
237
+ })().catch(() => {
238
+ // the stream ends with the plugin: nothing is left to record
239
+ });
240
+ return () => {
241
+ controller.abort();
242
+ return rpc?.dispose?.();
243
+ };
244
+ },
245
+ };
@@ -0,0 +1,21 @@
1
+ // AFM for opencode 2, terminal-client side: shows the notice the server side (index.js) emits on the
2
+ // afm RPC as a toast. opencode loads this file in the terminal client only; a server plugin has no screen.
3
+
4
+ /** How long a notice stays: long enough to read a sentence about a failed update. */
5
+ const DURATION_MS = 20000;
6
+
7
+ export default {
8
+ id: "afm",
9
+ setup(ctx) {
10
+ return ctx.data.on("rpc.afm.notice", (event) => {
11
+ const message = event?.data?.message;
12
+ if (typeof message !== "string" || message === "") return;
13
+ ctx.ui.toast.show({
14
+ title: "AFM",
15
+ message,
16
+ variant: "warning",
17
+ duration: DURATION_MS,
18
+ });
19
+ });
20
+ },
21
+ };
@@ -1,20 +1,28 @@
1
1
  ---
2
2
  description: Writer of one bounded change. Makes the edit its brief names, test first, inside the files the brief grants, and reports what changed; dispatched with a brief.
3
3
  mode: subagent
4
- permission:
5
- "*": deny
6
- read: allow
7
- edit: allow
8
- glob: allow
9
- grep: allow
10
- list: allow
11
- webfetch: deny
12
- websearch: deny
13
- external_directory: deny
14
- task: deny
15
- bash:
16
- "*": allow
17
- "afm_*": allow
4
+ permissions:
5
+ - action: "*"
6
+ resource: "*"
7
+ effect: deny
8
+ - action: "read"
9
+ resource: "*"
10
+ effect: allow
11
+ - action: "glob"
12
+ resource: "*"
13
+ effect: allow
14
+ - action: "grep"
15
+ resource: "*"
16
+ effect: allow
17
+ - action: "edit"
18
+ resource: "*"
19
+ effect: allow
20
+ - action: "shell"
21
+ resource: "*"
22
+ effect: allow
23
+ - action: "afm_*"
24
+ resource: "*"
25
+ effect: allow
18
26
  ---
19
27
  You are an executor. The message that dispatched you is your brief: follow it.
20
28
 
@@ -2,34 +2,79 @@
2
2
  description: Read-only investigator. Answers one question from the code, the docs or the web, cites what it read, and changes nothing; dispatched with a brief.
3
3
  mode: subagent
4
4
  steps: 60
5
- permission:
6
- "*": deny
7
- read: allow
8
- edit: deny
9
- glob: allow
10
- grep: allow
11
- list: allow
12
- webfetch: allow
13
- websearch: allow
14
- external_directory: deny
15
- task: deny
16
- bash:
17
- "*": deny
18
- "afm_approve": allow
19
- "afm_config": allow
20
- "afm_detect": allow
21
- "afm_doctor": allow
22
- "afm_failures_promotable": allow
23
- "afm_feature_lint": allow
24
- "afm_feature_status": allow
25
- "afm_gate": allow
26
- "afm_index": allow
27
- "afm_lang": allow
28
- "afm_next_id": allow
29
- "afm_path": allow
30
- "afm_policy": allow
31
- "afm_preflight": allow
32
- "afm_version": allow
5
+ permissions:
6
+ - action: "*"
7
+ resource: "*"
8
+ effect: deny
9
+ - action: "read"
10
+ resource: "*"
11
+ effect: allow
12
+ - action: "glob"
13
+ resource: "*"
14
+ effect: allow
15
+ - action: "grep"
16
+ resource: "*"
17
+ effect: allow
18
+ - action: "webfetch"
19
+ resource: "*"
20
+ effect: allow
21
+ - action: "websearch"
22
+ resource: "*"
23
+ effect: allow
24
+ - action: "afm_approve"
25
+ resource: "*"
26
+ effect: allow
27
+ - action: "afm_config"
28
+ resource: "*"
29
+ effect: allow
30
+ - action: "afm_detect"
31
+ resource: "*"
32
+ effect: allow
33
+ - action: "afm_doctor"
34
+ resource: "*"
35
+ effect: allow
36
+ - action: "afm_failures_promotable"
37
+ resource: "*"
38
+ effect: allow
39
+ - action: "afm_feature_lint"
40
+ resource: "*"
41
+ effect: allow
42
+ - action: "afm_feature_status"
43
+ resource: "*"
44
+ effect: allow
45
+ - action: "afm_gate"
46
+ resource: "*"
47
+ effect: allow
48
+ - action: "afm_index"
49
+ resource: "*"
50
+ effect: allow
51
+ - action: "afm_lang"
52
+ resource: "*"
53
+ effect: allow
54
+ - action: "afm_next_id"
55
+ resource: "*"
56
+ effect: allow
57
+ - action: "afm_path"
58
+ resource: "*"
59
+ effect: allow
60
+ - action: "afm_policy"
61
+ resource: "*"
62
+ effect: allow
63
+ - action: "afm_preflight"
64
+ resource: "*"
65
+ effect: allow
66
+ - action: "afm_read"
67
+ resource: "*"
68
+ effect: allow
69
+ - action: "afm_sessions"
70
+ resource: "*"
71
+ effect: allow
72
+ - action: "afm_sessions_signals"
73
+ resource: "*"
74
+ effect: allow
75
+ - action: "afm_version"
76
+ resource: "*"
77
+ effect: allow
33
78
  ---
34
79
  You are an explorer. The message that dispatched you is your brief: follow it.
35
80
 
@@ -1,34 +1,79 @@
1
1
  ---
2
2
  description: Blind auditor. Checks one piece of work it did not write against its sources and rules, and returns a verdict with evidence; dispatched with a brief.
3
3
  mode: subagent
4
- permission:
5
- "*": deny
6
- read: allow
7
- edit: deny
8
- glob: allow
9
- grep: allow
10
- list: allow
11
- webfetch: allow
12
- websearch: allow
13
- external_directory: deny
14
- task: deny
15
- bash:
16
- "*": deny
17
- "afm_approve": allow
18
- "afm_config": allow
19
- "afm_detect": allow
20
- "afm_doctor": allow
21
- "afm_failures_promotable": allow
22
- "afm_feature_lint": allow
23
- "afm_feature_status": allow
24
- "afm_gate": allow
25
- "afm_index": allow
26
- "afm_lang": allow
27
- "afm_next_id": allow
28
- "afm_path": allow
29
- "afm_policy": allow
30
- "afm_preflight": allow
31
- "afm_version": allow
4
+ permissions:
5
+ - action: "*"
6
+ resource: "*"
7
+ effect: deny
8
+ - action: "read"
9
+ resource: "*"
10
+ effect: allow
11
+ - action: "glob"
12
+ resource: "*"
13
+ effect: allow
14
+ - action: "grep"
15
+ resource: "*"
16
+ effect: allow
17
+ - action: "webfetch"
18
+ resource: "*"
19
+ effect: allow
20
+ - action: "websearch"
21
+ resource: "*"
22
+ effect: allow
23
+ - action: "afm_approve"
24
+ resource: "*"
25
+ effect: allow
26
+ - action: "afm_config"
27
+ resource: "*"
28
+ effect: allow
29
+ - action: "afm_detect"
30
+ resource: "*"
31
+ effect: allow
32
+ - action: "afm_doctor"
33
+ resource: "*"
34
+ effect: allow
35
+ - action: "afm_failures_promotable"
36
+ resource: "*"
37
+ effect: allow
38
+ - action: "afm_feature_lint"
39
+ resource: "*"
40
+ effect: allow
41
+ - action: "afm_feature_status"
42
+ resource: "*"
43
+ effect: allow
44
+ - action: "afm_gate"
45
+ resource: "*"
46
+ effect: allow
47
+ - action: "afm_index"
48
+ resource: "*"
49
+ effect: allow
50
+ - action: "afm_lang"
51
+ resource: "*"
52
+ effect: allow
53
+ - action: "afm_next_id"
54
+ resource: "*"
55
+ effect: allow
56
+ - action: "afm_path"
57
+ resource: "*"
58
+ effect: allow
59
+ - action: "afm_policy"
60
+ resource: "*"
61
+ effect: allow
62
+ - action: "afm_preflight"
63
+ resource: "*"
64
+ effect: allow
65
+ - action: "afm_read"
66
+ resource: "*"
67
+ effect: allow
68
+ - action: "afm_sessions"
69
+ resource: "*"
70
+ effect: allow
71
+ - action: "afm_sessions_signals"
72
+ resource: "*"
73
+ effect: allow
74
+ - action: "afm_version"
75
+ resource: "*"
76
+ effect: allow
32
77
  ---
33
78
  You are a reviewer. The message that dispatched you is your brief: follow it.
34
79
 
@@ -9,8 +9,10 @@ description: |
9
9
 
10
10
  > **Questions in this host.** `afm ask <op>` prompts only in a terminal, and your shell tool has none: it exits 2. Run `afm ask <op> --json` instead and put each pending question to the operator with the host's question tool when it has one, and otherwise in chat as a numbered list: the `prompt`, then the `options` with their descriptions, the recommended one first and marked. Wait for the answer. Then record each choice with `afm answer <op> <id> <label>` (one label per selected option). Run `afm ask <op>` again: `all questions answered` means the op may go on.
11
11
 
12
- Atomic skill of `afm`. The canonical procedure is the op `afm path ops/adr`. The CLI owns the number, the
13
- template, the frontmatter, the supersede link and the index. This skill keeps only the judgment.
12
+ > **Procedure.** Run `afm read ops/adr.md` and follow it; read an on-demand section when its condition holds.
13
+
14
+ The CLI owns the number, the template, the frontmatter, the supersede link and the index. This skill keeps
15
+ only the judgment.
14
16
 
15
17
  ## When NOT to activate
16
18
 
@@ -37,8 +39,10 @@ project (signal: `afm.md` § 3.1 forward-only exists). A greenfield project forb
37
39
 
38
40
  ## Invariant principles
39
41
 
40
- Single canonical source: `afm path PRINCIPLES`. Universals §1-8 apply. Additional #9 (a retroactive ADR is
42
+ Additional #9 (a retroactive ADR is
41
43
  load-bearing-only) applies ONLY in a refactored project.
42
44
 
43
45
  Passive mode (`afm-reconcile`) is always on and can propose `afm-adr` when it detects a "novel structural
44
46
  decision" signal. This skill covers only the active creation.
47
+
48
+ **Principles and version.** `afm path PRINCIPLES.md` (universals §1-8 apply). `afm version` prints the version; history: `afm path CHANGELOG.md`.
@@ -9,7 +9,7 @@ description: |
9
9
 
10
10
  > **Questions in this host.** `afm ask <op>` prompts only in a terminal, and your shell tool has none: it exits 2. Run `afm ask <op> --json` instead and put each pending question to the operator with the host's question tool when it has one, and otherwise in chat as a numbered list: the `prompt`, then the `options` with their descriptions, the recommended one first and marked. Wait for the answer. Then record each choice with `afm answer <op> <id> <label>` (one label per selected option). Run `afm ask <op>` again: `all questions answered` means the op may go on.
11
11
 
12
- An atomic skill. The procedure lives in the op: read it with `afm path ops/analyze.md`, end to end, and follow it exactly.
12
+ > **Procedure.** Run `afm read ops/analyze.md` and follow it; read an on-demand section when its condition holds.
13
13
 
14
14
  ## When NOT to activate
15
15
 
@@ -21,16 +21,14 @@ Run `afm preflight` (exit 3: no `docs/.afm-version`, stop and suggest `afm-init`
21
21
 
22
22
  ## Procedure
23
23
 
24
- Start from the mechanical findings: `afm feature lint <NNN> --json` covers the passes A, C, D, E and the structure of `tasks.md`. Add the judgment on top (constitution violations, duplication, the semantic side of inconsistency and ambiguity), build the coverage table, assign severities by the matrix, and report in chat with metrics. Close with the `remediation` question (`afm ask analyze`). Gating mode sorts findings into buckets instead of reporting.
24
+ Start from the mechanical findings: `afm feature lint <NNN> --json` covers the passes A, C, D, E and the structure of `tasks.md`. Add the judgment on top (constitution violations, duplication, the semantic side of inconsistency and ambiguity), build the coverage table, assign severities by the matrix, and report in chat with metrics. Close with the `remediation` question (`afm ask analyze`); a remediation that adds a task runs `afm task add`, never a hand edit of the `tasks:` list, and a task is closed only with `afm task done`. Gating mode sorts findings into buckets instead of reporting.
25
25
 
26
26
  ## Invariant principles
27
27
 
28
- Single canonical source: `afm path PRINCIPLES.md`. Universals §1-8 apply, especially #2 (detection is an autonomous scan, not a question), #5 (no finding without evidence) and #6 (analyze is read-only: it reports in chat, writes no file, and remediates only on request).
28
+ Most relevant here: #2 (detection is an autonomous scan, not a question), #5 (no finding without evidence) and #6 (analyze is read-only: it reports in chat, writes no file, and remediates only on request).
29
29
 
30
30
  ## Continuous doctrine
31
31
 
32
- Passive mode is the always-on `reconcile` skill. This skill covers only active auditing.
32
+ Passive mode (`afm-reconcile`) is always on. This skill covers only active auditing.
33
33
 
34
- ## Version
35
-
36
- `afm version`. History: `afm path CHANGELOG.md`.
34
+ **Principles and version.** `afm path PRINCIPLES.md` (universals §1-8 apply). `afm version` prints the version; history: `afm path CHANGELOG.md`.
@@ -9,7 +9,7 @@ description: |
9
9
 
10
10
  > **Questions in this host.** `afm ask <op>` prompts only in a terminal, and your shell tool has none: it exits 2. Run `afm ask <op> --json` instead and put each pending question to the operator with the host's question tool when it has one, and otherwise in chat as a numbered list: the `prompt`, then the `options` with their descriptions, the recommended one first and marked. Wait for the answer. Then record each choice with `afm answer <op> <id> <label>` (one label per selected option). Run `afm ask <op>` again: `all questions answered` means the op may go on.
11
11
 
12
- An atomic skill. The procedure lives in the op: read it with `afm path ops/clarify.md`, end to end, and follow it literally.
12
+ > **Procedure.** Run `afm read ops/clarify.md` and follow it; read an on-demand section when its condition holds.
13
13
 
14
14
  ## When NOT to activate
15
15
 
@@ -25,12 +25,10 @@ Judgment only: a 9-category scan of the spec, a prioritized queue (at most 5 que
25
25
 
26
26
  ## Invariant principles
27
27
 
28
- Single canonical source: `afm path PRINCIPLES.md`. Universals §1-8 apply. #2 is discover-first: resolve each question yourself through the scan or the docs, then ask. #4 says `[TBD]` beats invention, and a recommendation is a rejectable hypothesis, not a guess. #6 is autonomy-first: write the integration into spec § 7 and report. #8 is recommendation-first questions.
28
+ Most relevant here: #2 is discover-first: resolve each question yourself through the scan or the docs, then ask. #4 says `[TBD]` beats invention, and a recommendation is a rejectable hypothesis, not a guess. #6 is autonomy-first: write the integration into spec § 7 and report. #8 is recommendation-first questions.
29
29
 
30
30
  ## Continuous doctrine
31
31
 
32
- Passive mode is the always-on `reconcile` skill. This skill covers only the active resolution of ambiguity.
32
+ Passive mode (`afm-reconcile`) is always on. This skill covers only the active resolution of ambiguity.
33
33
 
34
- ## Version
35
-
36
- `afm version`. History: `afm path CHANGELOG.md`.
34
+ **Principles and version.** `afm path PRINCIPLES.md` (universals §1-8 apply). `afm version` prints the version; history: `afm path CHANGELOG.md`.
@@ -7,9 +7,9 @@ description: |
7
7
 
8
8
  # AFM — Deliver (orchestrated autonomous delivery)
9
9
 
10
- > **Questions in this host.** `afm ask <op>` prompts only in a terminal, and your shell tool has none: it exits 2. Run `afm ask <op> --json` instead and put each pending question to the operator with the host's question tool when it has one, and otherwise in chat as a numbered list: the `prompt`, then the `options` with their descriptions, the recommended one first and marked. Wait for the answer. Then record each choice with `afm answer <op> <id> <label>` (one label per selected option). Run `afm ask <op>` again: `all questions answered` means the op may go on.
10
+ > **Procedure.** Run `afm read ops/deliver.md` and follow it; read an on-demand section when its condition holds.
11
11
 
12
- The **orchestrator** of the delivery loop. One request takes "build feature X" from discovery to a delivered feature. The procedure lives in the op: read it with `afm path ops/deliver.md`, end to end, and follow it literally. It does not duplicate the sub-steps: it reads and follows `specify`, `plan`, `tasks`, `clarify`, `analyze` and `reconcile` through a suppression contract.
12
+ The **orchestrator** of the delivery loop. One request takes "build feature X" from discovery to a delivered feature. It does not duplicate the sub-steps: it reads and follows `afm-specify`, `afm-plan`, `afm-tasks`, `afm-clarify`, `afm-analyze` and `afm-reconcile` through a suppression contract. External unknowns go through `nasca-research`, or `nasca-campaign` for a deep study; ask the operator if nasca is missing.
13
13
 
14
14
  ## When NOT to activate
15
15
 
@@ -19,26 +19,12 @@ An isolated step ("just open the spec", "just generate the tasks") is the matchi
19
19
 
20
20
  Run `afm preflight`. Exit 0 continues. Exit 3 means no `docs/.afm-version`: stop and suggest `afm-init` (greenfield) or `afm-refactor` (brownfield). Exit 4 or 5: report the line it printed. Do not fall back silently.
21
21
 
22
- ## Procedure
22
+ ## Most relevant here
23
23
 
24
- The CLI holds the state; the skill keeps only the judgment. Drive the loop with `afm feature status <NNN> --json`, which returns the phase to resume at, the next task and the blockers.
25
-
26
- - **Discovery:** scan scoped to the feature, external unknowns through the nasca research flow (`nasca-research`, or `nasca-campaign` for a deep study; ask the operator if nasca is missing), unknowns sorted into resolved-by-scan, researchable and irreducible. One batched, recommendation-first round, capped by the protocol (`afm-protocol`).
27
- - **Draft:** spec, plan and tasks, verified with `afm feature lint <NNN>` plus the judgment passes of `analyze`.
28
- - **Gate:** the earned questions plus the `outcome` question (`afm ask deliver`), then `afm approve <NNN>`. An explicit "go" skips the questions, never the record.
29
- - **Execute:** each task is one `core/afm.md` § 2 cycle, checked with `afm gate --staged`, then `afm task done <NNN> <Txxx>`. Halt on a non-`[P]` failure after the capped retry.
30
- - **Reconcile:** § 8.5 plus a passive sweep.
31
-
32
- If artifacts already exist, resume from the correct phase, including "tasks already exist, run from the first `[ ]`".
33
-
34
- ## Invariant principles
35
-
36
- Single canonical source: `afm path PRINCIPLES.md`. Universals §1-8 apply. The backbone is **#2** (discover-first: resolve unknowns by scan and research, ask only the irreducible part, never guess product or criteria) and **#6** (autonomy-first: approval at checkpoint granularity, the consolidated plan IS the proposal).
24
+ **#2** (discover-first: resolve unknowns by scan and research, ask only the irreducible part, never guess product or criteria) and **#6** (autonomy-first: approval at checkpoint granularity, the consolidated plan IS the proposal).
37
25
 
38
26
  ## Continuous doctrine
39
27
 
40
- Passive mode is the always-on `reconcile` skill. It runs in parallel with `deliver` and takes the closing turn of Phase 4.
41
-
42
- ## Version
28
+ Passive mode (`afm-reconcile`) is always on. It runs in parallel with `afm-deliver` and takes the closing turn of Phase 4 (`core/afm.md` § 8.5 plus a passive sweep).
43
29
 
44
- `afm version`. History: `afm path CHANGELOG.md`.
30
+ **Principles and version.** `afm path PRINCIPLES.md` (universals §1-8 apply). `afm version` prints the version; history: `afm path CHANGELOG.md`.