acstack 0.48.2 → 0.48.3

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.48.2",
4
+ "version": "0.48.3",
5
5
  "author": {
6
6
  "name": "ac8318740"
7
7
  },
package/README.md CHANGED
@@ -101,9 +101,10 @@ Then, in your project, run `/acstack:setup`.
101
101
  ```
102
102
 
103
103
  - Set `"toolActivation": "eager"` in `~/.pi/agent/extensions/subagent/config.json` to offer the `subagent` tool from the first turn (restart pi after)
104
- - pi has no output styles, so the writing style is a toggle
105
- - Run `/acstack-writing-style on` to add it to every pi session
106
- - Run `/acstack-writing-style off` to remove it
104
+ - pi gets the ACStack writing style from the extension by default
105
+ - Run `/acstack-writing-style default` to use the bundled style again
106
+ - Run `/acstack-writing-style off` to disable style injection
107
+ - Run `/acstack-writing-style custom <path>` to use a user-owned style file
107
108
 
108
109
  ## 2. The three commands, and which one to use
109
110
 
@@ -217,7 +217,10 @@ pi:
217
217
  # markdown, code and artifact previews beside a decision, accepts freeform
218
218
  # answers and notes, and does not require a full-event telemetry env var.
219
219
  question_tool: true
220
- # rpiv-todo, which adds a todo tool to pi.
220
+ # rpiv-todo, which adds a todo tool to pi. When this is on, apply also
221
+ # suggests a line for the user's global ~/.pi/agent/AGENTS.md if their Pi
222
+ # context file does not already mention todo discipline. It never edits that
223
+ # user-owned file for them.
221
224
  todo: true
222
225
  # pi-plugin-manager, which adds pi plugin management commands.
223
226
  plugin_manager: true
@@ -3886,6 +3886,17 @@ pi_npm_pin() { # pi_npm_pin <name>
3886
3886
  [ "$src" != "npm:$1" ]
3887
3887
  }
3888
3888
 
3889
+ pi_suggest_todo_discipline() {
3890
+ local needle="When using a todo list, keep it current as work completes."
3891
+ grep -qsF "$needle" "$PI_DIR"/AGENTS.override.md "$PI_DIR"/AGENTS.md "$PI_DIR"/AGENTS.MD \
3892
+ "$PI_DIR"/CLAUDE.md "$PI_DIR"/CLAUDE.MD && return 0
3893
+ say "pi: todo tool installed. For stricter todo discipline, ask the agent"
3894
+ say "pi: running this setup to hand-edit $PI_DIR/AGENTS.md and add:"
3895
+ say "pi: $needle Before reporting a task as done, check the todo list"
3896
+ say "pi: and mark every completed step completed. Do not leave stale"
3897
+ say "pi: in-progress or pending todos after a commit, PR, merge or workflow gate."
3898
+ }
3899
+
3889
3900
  # The pin apply would move to, or nothing when the installed one is current
3890
3901
  # or newer. A newer pin is one the user chose, so it is never moved back.
3891
3902
  pi_acstack_behind() {
@@ -3977,6 +3988,7 @@ apply_pi() {
3977
3988
  pi_add npm:pi-subagents npm:pi-subagents
3978
3989
  if [ "$(cfg_get pi.todo true)" = "true" ]; then
3979
3990
  pi_add npm:@juicesharp/rpiv-todo npm:@juicesharp/rpiv-todo
3991
+ pi_suggest_todo_discipline
3980
3992
  else
3981
3993
  pi_drop npm:@juicesharp/rpiv-todo
3982
3994
  fi
package/cli/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acstack-cli",
3
- "version": "0.48.2",
3
+ "version": "0.48.3",
4
4
  "description": "CLI for the ACStack agentic coding stack",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -359,6 +359,7 @@ What `apply` does:
359
359
  - Setup leaves Claude Code and Codex alone, and removes bili from them if an older setup added it
360
360
  - Set `pi.billion_context: false` to leave it out, or to remove it later if this setup installed it
361
361
  - It installs `@juicesharp/rpiv-todo` and `pi-plugin-manager` by default
362
+ - When todo is on, `apply` suggests a todo-discipline line for the user's global Pi context file if it is missing
362
363
  - Set `pi.todo: false` or `pi.plugin_manager: false` to leave either package out
363
364
  - It installs `@juicesharp/rpiv-ask-user-question` as pi's question tool by default
364
365
  - ACStack uses it because decisions can include markdown, code and artifact previews
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acstack",
3
- "version": "0.48.2",
3
+ "version": "0.48.3",
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
@@ -10,8 +10,8 @@
10
10
  * before_agent_start list ACStack's skills in the system prompt in every
11
11
  * session, and add orchestrator/AGENTS.md in a
12
12
  * acstack project (hooks/session-start.sh)
13
- * add the writing style and its per-turn reminder when
14
- * the user turned it on (hooks/ac-writing-style-reminder.sh)
13
+ * add the writing style and its per-turn reminder
14
+ * from the SSOT style file (hooks/ac-writing-style-reminder.sh)
15
15
  * agent_before_settle nudge with a continuity-versus-freshness checkpoint
16
16
  * as context fills (hooks/context-pressure.sh)
17
17
  * session_compact re-inject acstack/HANDOFF.md after a compaction, and
@@ -19,9 +19,9 @@
19
19
  * hooks/context-pressure.sh)
20
20
  *
21
21
  * It also registers /acstack <skill>, a /acstack:<skill> command per skill,
22
- * and /acstack-writing-style on|off. Those two forms are the only way a user
23
- * runs an ACStack skill on pi: package.json lists no skills, so pi adds no
24
- * /skill:<name> commands for them.
22
+ * and /acstack-writing-style for the bundled/default/custom style choice.
23
+ * The first two forms are the only way a user runs an ACStack skill on pi:
24
+ * package.json lists no skills, so pi adds no /skill:<name> commands for them.
25
25
  *
26
26
  * Only type imports come from pi, so Node can strip the types and load this
27
27
  * file in tests/test-pi-extension.sh with a stub API.
@@ -45,6 +45,12 @@ function agentDir(): string {
45
45
  return process.env.PI_CODING_AGENT_DIR || path.join(os.homedir(), ".pi", "agent");
46
46
  }
47
47
 
48
+ const settingsFile = (): string => path.join(agentDir(), "acstack.json");
49
+
50
+ type WritingStyleMode = "default" | "off" | "custom";
51
+ type WritingStyleSettings = { mode?: WritingStyleMode; path?: string };
52
+ type AcstackSettings = { writingStyle?: boolean | WritingStyleSettings };
53
+
48
54
  function readText(file: string): string | undefined {
49
55
  try {
50
56
  return fs.readFileSync(file, "utf-8");
@@ -53,6 +59,34 @@ function readText(file: string): string | undefined {
53
59
  }
54
60
  }
55
61
 
62
+ function readSettings(): AcstackSettings {
63
+ try {
64
+ const parsed = JSON.parse(readText(settingsFile()) ?? "{}");
65
+ return parsed && typeof parsed === "object" ? parsed : {};
66
+ } catch {
67
+ return {};
68
+ }
69
+ }
70
+
71
+ function writeSettings(settings: AcstackSettings): void {
72
+ fs.mkdirSync(path.dirname(settingsFile()), { recursive: true });
73
+ fs.writeFileSync(settingsFile(), `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
74
+ }
75
+
76
+ function writingStyleSettings(settings: AcstackSettings = readSettings()): WritingStyleSettings {
77
+ const value = settings.writingStyle;
78
+ if (value === false) return { mode: "off" };
79
+ if (value === true || value === undefined) return { mode: "default" };
80
+ if (value && typeof value === "object") return { mode: value.mode ?? "default", path: value.path };
81
+ return { mode: "default" };
82
+ }
83
+
84
+ function expandUserPath(file: string, cwd = process.cwd()): string {
85
+ if (file === "~") return os.homedir();
86
+ if (file.startsWith(`~${path.sep}`)) return path.join(os.homedir(), file.slice(2));
87
+ return path.isAbsolute(file) ? file : path.resolve(cwd, file);
88
+ }
89
+
56
90
  function report(ctx: ExtensionContext, message: string, level: "info" | "warning" | "error" = "info"): void {
57
91
  if (ctx.hasUI) ctx.ui.notify(message, level);
58
92
  else process.stderr.write(`${message}\n`);
@@ -172,22 +206,6 @@ function linkClis(ctx: ExtensionContext): void {
172
206
  // Writing style
173
207
  // --------------------------------------------------------------------------
174
208
 
175
- const settingsFile = (): string => path.join(agentDir(), "acstack.json");
176
-
177
- function readSettings(): { writingStyle?: boolean } {
178
- try {
179
- const parsed = JSON.parse(readText(settingsFile()) ?? "{}");
180
- return parsed && typeof parsed === "object" ? parsed : {};
181
- } catch {
182
- return {};
183
- }
184
- }
185
-
186
- function writeSettings(settings: { writingStyle?: boolean }): void {
187
- fs.mkdirSync(path.dirname(settingsFile()), { recursive: true });
188
- fs.writeFileSync(settingsFile(), `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
189
- }
190
-
191
209
  function stripFrontmatter(text: string): string {
192
210
  const match = /^---\n[\s\S]*?\n---\n/.exec(text);
193
211
  return (match ? text.slice(match[0].length) : text).trim();
@@ -616,10 +634,16 @@ export default function acstack(pi: ExtensionAPI): void {
616
634
  else report(ctx, `acstack: could not read ${orchestratorFile}`, "warning");
617
635
  }
618
636
 
619
- if (!readSettings().writingStyle) return;
620
- const style = readText(styleFile);
621
- if (!style) return;
637
+ const styleCfg = writingStyleSettings();
638
+ if (styleCfg.mode === "off") return;
639
+ const resolvedStyleFile = styleCfg.mode === "custom" && styleCfg.path ? expandUserPath(styleCfg.path) : styleFile;
640
+ const style = readText(resolvedStyleFile);
641
+ if (!style) {
642
+ report(ctx, `acstack: could not read writing style at ${resolvedStyleFile}`, "warning");
643
+ return;
644
+ }
622
645
  sections["acstack-writing-style"] = stripFrontmatter(style);
646
+ if (event.messageType !== "user") return;
623
647
  const reminder = extractWritingReminder(style);
624
648
  if (!reminder) return;
625
649
  return { message: { customType: "acstack-writing-style-reminder", content: reminder, display: false } };
@@ -686,6 +710,44 @@ export default function acstack(pi: ExtensionAPI): void {
686
710
  else pi.sendUserMessage(message, { deliverAs: "followUp" });
687
711
  };
688
712
 
713
+ pi.registerCommand("acstack-writing-style", {
714
+ description: "Configure ACStack writing style injection: default | off | custom <path>",
715
+ handler: async (args, ctx) => {
716
+ const [choice = "", ...rest] = args.trim().split(/\s+/).filter(Boolean);
717
+ const settings = readSettings();
718
+ const current = writingStyleSettings(settings);
719
+ if (!choice || choice === "show") {
720
+ report(ctx, `acstack: writing style is ${current.mode}${current.path ? ` (${current.path})` : ""} (${settingsFile()}).`);
721
+ return;
722
+ }
723
+ if (choice === "default") {
724
+ settings.writingStyle = { mode: "default" };
725
+ writeSettings(settings);
726
+ report(ctx, `acstack: writing style is default (${settingsFile()}).`);
727
+ return;
728
+ }
729
+ if (choice === "off") {
730
+ settings.writingStyle = { mode: "off" };
731
+ writeSettings(settings);
732
+ report(ctx, `acstack: writing style is off (${settingsFile()}).`);
733
+ return;
734
+ }
735
+ if (choice === "custom") {
736
+ const customPath = rest.join(" ").trim();
737
+ if (!customPath) {
738
+ report(ctx, "acstack: usage is /acstack-writing-style custom <path>", "warning");
739
+ return;
740
+ }
741
+ const resolvedPath = expandUserPath(customPath, ctx.cwd);
742
+ settings.writingStyle = { mode: "custom", path: resolvedPath };
743
+ writeSettings(settings);
744
+ report(ctx, `acstack: writing style is custom (${resolvedPath}) (${settingsFile()}).`);
745
+ return;
746
+ }
747
+ report(ctx, "acstack: usage is /acstack-writing-style [show|default|off|custom <path>]", "warning");
748
+ },
749
+ });
750
+
689
751
  pi.registerCommand("acstack", {
690
752
  description: "Run an ACStack skill: /acstack <skill> [args], e.g. /acstack map",
691
753
  handler: async (args, ctx) => {
@@ -704,20 +766,4 @@ export default function acstack(pi: ExtensionAPI): void {
704
766
  handler: async (args, ctx) => runSkill(name, args, ctx),
705
767
  });
706
768
  }
707
-
708
- pi.registerCommand("acstack-writing-style", {
709
- description: "Turn the ACStack writing style on or off for every pi session (on | off)",
710
- handler: async (args, ctx) => {
711
- const choice = args.trim().toLowerCase();
712
- const settings = readSettings();
713
- if (choice === "on" || choice === "off") {
714
- settings.writingStyle = choice === "on";
715
- writeSettings(settings);
716
- } else if (choice) {
717
- report(ctx, "acstack: usage is /acstack-writing-style on|off", "warning");
718
- return;
719
- }
720
- report(ctx, `acstack: writing style is ${settings.writingStyle ? "on" : "off"} (${settingsFile()}).`);
721
- },
722
- });
723
769
  }