acstack 0.52.5 → 0.52.6

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "acstack",
3
3
  "description": "Agentic coding stack where planning grows only as far as needed: a map of decisions, tasks, checks, and handoffs behind a pluggable tracker.",
4
- "version": "0.52.5",
4
+ "version": "0.52.6",
5
5
  "author": {
6
6
  "name": "ac8318740"
7
7
  },
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acstack-cli",
3
- "version": "0.52.5",
3
+ "version": "0.52.6",
4
4
  "description": "CLI for the ACStack agentic coding stack",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -206,10 +206,13 @@ if ! command -v python3 >/dev/null 2>&1; then
206
206
  fi
207
207
 
208
208
  python3 - "$claude_md" <<'PY'
209
- import json, sys
209
+ import json, re, sys
210
210
  path = sys.argv[1]
211
211
  with open(path, 'r', encoding='utf-8') as f:
212
212
  content = f.read()
213
+ # SessionStart reaches the lead session, so it keeps the lead block and drops the delegated one.
214
+ content = re.sub(r'<!-- acstack:delegated:start -->[\s\S]*?<!-- acstack:delegated:end -->\n?', '', content)
215
+ content = re.sub(r'<!-- acstack:lead:(start|end) -->\n?', '', content)
213
216
  print(json.dumps({
214
217
  "hookSpecificOutput": {
215
218
  "hookEventName": "SessionStart",
@@ -2,13 +2,31 @@
2
2
 
3
3
  ACStack keeps meaningful tests and chooses verification effort to fit the change.
4
4
 
5
+ <!-- acstack:lead:start -->
5
6
  ## Your role
6
7
 
7
8
  - Understand the request, investigate the code, implement, and verify the result
8
9
  - Work directly on straightforward tasks, including research, tests, code, and checks
9
- - Delegate when another agent materially improves confidence or handles an independent scope
10
+ - Delegate when another agent materially improves confidence, handles an independent scope, or keeps broad discovery out of your context
10
11
  - Honor explicit user requests for stronger process, including separate agents and tests-first work
11
12
  - Follow the shared Implementation discipline below across implement, quick-fix, and map work
13
+ - Keep the user's session continuous
14
+ - Use `/acstack:handoff` to transfer work to a visible agent
15
+ - Use `/acstack:compact-and-continue` to preserve session state before compaction
16
+ <!-- acstack:lead:end -->
17
+ <!-- acstack:delegated:start -->
18
+ ## Your role
19
+
20
+ - You are a delegated session: the brief your parent gave at launch sets your role, scope, and access
21
+ - You may be an investigator, an implementer, or a coordinator of your own scope
22
+ - The shared guidance below applies wherever the brief does not narrow it
23
+ - Stay inside the assigned scope and access
24
+ - Report work outside them to your parent instead of doing it
25
+ - Treat a no-edit instruction as binding even when the launcher does not enforce it
26
+ - Delegate further only when the brief allows it, and never grant a child more access than you have
27
+ - Report results, evidence, and blockers to your parent
28
+ - Your parent owns synthesis and completion unless the brief assigns them to you
29
+ <!-- acstack:delegated:end -->
12
30
 
13
31
  ## Non-negotiable rules
14
32
 
@@ -40,12 +58,15 @@ ACStack runs on Claude Code, Codex, and pi. The skills name each capability once
40
58
 
41
59
  - **Launch a subagent** – start a child agent with its own context, by its ACStack name (`test-writer`, `task-executor`, `task-checker`, `frontend-verifier`)
42
60
  - Claude Code: the Agent tool, with `subagent_type` set to that name
43
- - pi: `Agent` from `@tintinweb/pi-subagents` (`subagent_type`), or `subagent` from `pi-subagents` (`agent`)
61
+ - pi: `Agent` from `@tintinweb/pi-subagents` (`subagent_type`), `subagent` from `pi-subagents` (`agent`), or `team_agent` from ac-teams
62
+ - `team_agent` does not load ACStack agents by name, so put the role's instructions in the brief
63
+ - ac-teams describes its member access controls in its own `docs/access.md`
44
64
  - **Read-only codebase-search subagent** – `Explore` on Claude Code and pi's `Agent`, or `scout` on pi's `subagent`
45
65
  - `debugger` names a debugging agent
46
66
  - On a host without a matching built-in agent, launch a general subagent with that brief
47
67
  - **Agent team** – parallel full sessions, each owning one scope and launching its own subagents
48
68
  - Claude Code has agent teams, with messaging between teammates and plan approval by the lead
69
+ - pi has ac-teams, through `team_agent`
49
70
  - On a host without agent teams, work directly or delegate scopes sequentially
50
71
  - When the task assigns separate test ownership, keep it separate from implementation
51
72
  - **Ask the user a structured question** – the host's question tool (AskUserQuestion on Claude Code)
@@ -215,6 +236,40 @@ flowchart TD
215
236
  - Check the design gate and load design context once per task when it is on
216
237
  - Follow the same procedure for direct work and delegated implementation
217
238
 
239
+ ### Delegate investigation
240
+
241
+ - Prefer delegated investigation when discovery means broad searches, substantial reading, independent hypotheses, or separable research questions
242
+ - Delegation keeps raw discovery out of your context, even when the investigator runs your own model
243
+ - Work directly when the paths are few and known, or when briefing costs more than searching
244
+ - Launch one investigator per separable question, with no fixed count
245
+ - Launch the host's read-only search agent for navigation questions (see Host tools)
246
+ - For other questions, launch a general agent with the investigator brief below
247
+ - With no launcher available, investigate directly and say that delegation was unavailable
248
+ - Choose the model separately from the role
249
+ - Prefer a configured cheaper, capable model for navigation and fact gathering
250
+ - Use a stronger model for hard causal or ambiguous analysis
251
+ - Never hardcode a provider or model; use what the host or user configured
252
+ - Request enforced read-only access when the launcher reports that it can enforce it
253
+ - Expect prompt-only protection on most hosts today
254
+ - A read-only search agent such as `Explore` lacks edit tools; that limits its tools but is not operating-system enforcement
255
+ - Without enforcement, instruct the investigator not to edit project files, and call that protection prompt-only
256
+ - If an enforced launch fails, report the failure and ask before launching without enforcement
257
+ - Own the synthesis: verify the decisive evidence a conclusion rests on, without repeating the whole investigation
258
+
259
+ The investigator brief states:
260
+
261
+ - The question, and why the answer matters
262
+ - The scope: paths, repositories, or sources to search, and what is out of scope
263
+ - The access: enforced read-only, or a prompt-only instruction not to edit project files
264
+ - The report the parent needs back
265
+
266
+ The investigator reports:
267
+
268
+ - Findings, with file paths and line references
269
+ - Which findings rest on evidence and which are hypotheses
270
+ - What it searched, so the parent knows what it did not cover
271
+ - Uncertainties, and the next checks worth running
272
+
218
273
  ### Use independent review where needed
219
274
 
220
275
  - Require independent review for security boundaries, money, irreversible data changes, or unresolved correctness or impact uncertainty
@@ -414,8 +469,8 @@ Launch each agent by the name below (see Host tools for how your host takes it).
414
469
  | Implement task | `task-executor` | Source and tests unless tests have a separate owner |
415
470
  | Verify implementation | `task-checker` | Independent review, coverage adequacy, regressions |
416
471
  | Verify frontend UI | `frontend-verifier` | Real browser, screenshots, non-negotiable |
417
- | Find/locate something | `Explore` / `scout` | Read-only codebase search; `scout` on pi |
418
- | Debug/investigate | `debugger` | Built-in debugging agent |
472
+ | Investigate a question | `Explore` / `scout`, or a general agent | Read-only; brief per Delegate investigation |
473
+ | Debug | `debugger` | Built-in debugging agent |
419
474
 
420
475
  ---
421
476
 
@@ -436,5 +491,3 @@ Launch each agent by the name below (see Host tools for how your host takes it).
436
491
  - Keep living specs current through commit-time sync
437
492
  - Create a map when unresolved questions need durable tracking
438
493
  - Invoke `bridge` before cross-device browser or tunnel work
439
- - Use `/acstack:handoff` to transfer work to a visible agent
440
- - Use `/acstack:compact-and-continue` to preserve session state before compaction
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acstack",
3
- "version": "0.52.5",
3
+ "version": "0.52.6",
4
4
  "private": false,
5
5
  "description": "Agentic coding stack with first-class Claude Code and pi support: skills, orchestrator instructions, and verification agents.",
6
6
  "license": "MIT",
package/pi/extension.ts CHANGED
@@ -80,6 +80,8 @@ function ensureProjectData(ctx: ExtensionContext, root: string): boolean {
80
80
  const SUBAGENT_CHILD_ENV = "PI_SUBAGENT_CHILD";
81
81
  /** pi-subagents sets this instead in a child it places in its own herdr pane. */
82
82
  const HERDR_BRIDGE_CHILD_ENV = "PI_SUBAGENTS_HERDR_BRIDGE";
83
+ /** ac-teams sets this in every member it launches. */
84
+ const AC_TEAMS_MEMBER_ENV = "AC_TEAMS_MEMBER";
83
85
 
84
86
  function agentDir(): string {
85
87
  return process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), ".pi", "agent");
@@ -622,6 +624,18 @@ export function skillsSection(skills: Map<string, SkillInfo>): string | undefine
622
624
  return lines.join("\n");
623
625
  }
624
626
 
627
+ /**
628
+ * The orchestrator text for one kind of session. orchestrator/AGENTS.md marks
629
+ * the lead-only and delegated-only blocks; each session keeps its own block
630
+ * and drops the other.
631
+ */
632
+ export function orchestratorFor(text: string, role: "lead" | "delegated"): string {
633
+ const other = role === "lead" ? "delegated" : "lead";
634
+ return text
635
+ .replace(new RegExp(`<!-- acstack:${other}:start -->[\\s\\S]*?<!-- acstack:${other}:end -->\\n?`, "g"), "")
636
+ .replace(new RegExp(`<!-- acstack:${role}:(start|end) -->\\n?`, "g"), "");
637
+ }
638
+
625
639
  /**
626
640
  * The user message for one ACStack skill, in the same shape pi's own
627
641
  * /skill:<name> expansion produces. Built from this package's file, so a
@@ -640,9 +654,9 @@ export function skillInvocation(name: string, file: string, args: string): strin
640
654
  // --------------------------------------------------------------------------
641
655
 
642
656
  export default function acstack(pi: ExtensionAPI): void {
643
- // A background subagent loads every ambient extension. The orchestrator
644
- // prompt and the nudges belong to the lead session, never to a subagent.
645
- if (process.env[SUBAGENT_CHILD_ENV] === "1" || process.env[HERDR_BRIDGE_CHILD_ENV] === "1") return;
657
+ // A delegated session keeps the shared guidance but not the lead's role,
658
+ // CLI symlinks, launcher warning, context nudges, or handoff reload.
659
+ const delegated = process.env[SUBAGENT_CHILD_ENV] === "1" || process.env[HERDR_BRIDGE_CHILD_ENV] === "1" || Boolean(process.env[AC_TEAMS_MEMBER_ENV]);
646
660
 
647
661
  const orchestratorFile = path.join(packageRoot, "orchestrator", "AGENTS.md");
648
662
  const styleFile = path.join(packageRoot, "output-styles", "ac-writing-style.md");
@@ -651,7 +665,7 @@ export default function acstack(pi: ExtensionAPI): void {
651
665
  let warnedSubagents = false;
652
666
 
653
667
  pi.on("session_start", async (event, ctx) => {
654
- linkClis(ctx);
668
+ if (!delegated) linkClis(ctx);
655
669
  const root = projectRoot(ctx.cwd);
656
670
  if (ensureProjectData(ctx, root)) unsafeProjects.delete(root);
657
671
  else {
@@ -659,6 +673,7 @@ export default function acstack(pi: ExtensionAPI): void {
659
673
  return;
660
674
  }
661
675
 
676
+ if (delegated) return;
662
677
  if (!warnedSubagents && !pi.getAllTools().some((tool) => tool.name === "subagent" || tool.name === "Agent" || tool.name === "team_agent")) {
663
678
  warnedSubagents = true;
664
679
  report(
@@ -686,14 +701,19 @@ export default function acstack(pi: ExtensionAPI): void {
686
701
 
687
702
  if (isProject(ctx.cwd)) {
688
703
  const orchestrator = readText(orchestratorFile);
689
- if (orchestrator) sections["acstack-orchestrator"] = orchestrator;
704
+ if (orchestrator) sections["acstack-orchestrator"] = orchestratorFor(orchestrator, delegated ? "delegated" : "lead");
690
705
  else report(ctx, `acstack: could not read ${orchestratorFile}`, "warning");
691
706
  }
692
707
 
693
708
  const styleCfg = writingStyleSettings();
694
709
  if (styleCfg.mode === "off") return;
695
710
  const resolvedStyleFile = styleCfg.mode === "custom" && styleCfg.path ? expandUserPath(styleCfg.path) : styleFile;
696
- const style = readText(resolvedStyleFile);
711
+ let style = readText(resolvedStyleFile);
712
+ // A sandboxed delegated session may not see a custom file outside its read roots.
713
+ if (!style && delegated && resolvedStyleFile !== styleFile) {
714
+ report(ctx, `acstack: could not read writing style at ${resolvedStyleFile}; using the bundled style`, "warning");
715
+ style = readText(styleFile);
716
+ }
697
717
  if (!style) {
698
718
  report(ctx, `acstack: could not read writing style at ${resolvedStyleFile}`, "warning");
699
719
  return;
@@ -705,7 +725,7 @@ export default function acstack(pi: ExtensionAPI): void {
705
725
  return { message: { customType: "acstack-writing-style-reminder", content: reminder, display: false } };
706
726
  });
707
727
 
708
- pi.on("agent_before_settle", async (event, ctx) => {
728
+ if (!delegated) pi.on("agent_before_settle", async (event, ctx) => {
709
729
  if (event.outcome !== "completed") return;
710
730
  const usage = ctx.getContextUsage();
711
731
  if (!usage || usage.tokens === null) return;
@@ -736,7 +756,7 @@ export default function acstack(pi: ExtensionAPI): void {
736
756
  };
737
757
  });
738
758
 
739
- pi.on("session_compact", async (_event, ctx) => {
759
+ if (!delegated) pi.on("session_compact", async (_event, ctx) => {
740
760
  // The compaction threw the old context away, so the ladder starts over.
741
761
  const key = sessionKey(ctx);
742
762
  if (key) {