@jwilger/pi-development-system 0.70.0 → 0.72.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jwilger/pi-development-system",
3
- "version": "0.70.0",
3
+ "version": "0.72.0",
4
4
  "description": "A pi extension package representing a seasoned approach to software development using a full AI SDLC.",
5
5
  "keywords": [
6
6
  "pi-package"
@@ -9,4 +9,4 @@ Write the plan for the current work with the work-intake-and-slicing skill.
9
9
  3. Write each task so that someone who sees only that task could finish it. Never write `TBD`.
10
10
  4. Run `devsys_task_check` on every task record you wrote and fix each one until it says ready. Split any that come back too-big.
11
11
  5. Stop and ask the user to review the plan before any implementation starts. Do not start a goal yet: a goal tool may continue on its own and begin implementing before the review.
12
- 6. Only after the user approves the plan: call `devsys_begin_work` (so the review and red-first gates are on while the plan is built). Then, if a `create_goal` tool exists in this session, call it with the objective "complete docs/plan/<slug>.md honouring its Constraints and Progress checklist, one increment at a time, stopping after each increment's Release step". Otherwise tell the user the plan path so they can start a goal on it. Do not depend on any goal tool's file format.
12
+ 6. Only after the user approves the plan: call `devsys_begin_work` (so the review and red-first gates are on while the plan is built; it does nothing if the work is already implementing). Then, if a `create_goal` tool exists in this session, call it with the objective "complete docs/plan/<slug>.md honouring its Constraints and Progress checklist, one increment at a time, stopping after each increment's Release step". Otherwise tell the user the plan path so they can start a goal on it. Do not depend on any goal tool's file format.
@@ -6,6 +6,6 @@ Start new work with the work-intake-and-slicing skill.
6
6
 
7
7
  1. Call `devsys_intake` with `request` set to: $ARGUMENTS (if empty, ask the user what they want done first).
8
8
  2. Show the user the proposed size and artifacts. The tool asks them to confirm or change the size.
9
- 3. Produce the planning artifacts first (brief, journeys, event model, architecture, lens review, in that order, whichever are recommended), then the task record. `review` and `adr-if-needed` are not written up front: review happens on the finished slice, and an ADR when a decision needs one. To skip one, call `devsys_record_departure` with gate `artifact.skipped:<artifact>` and the reason; do not skip silently. The event model and architecture are recommendations, never gates.
10
- 4. For `capability` and `product` the phase stays `planning` while you produce the artifacts; once the user approves them, call `devsys_begin_work` so the review and red-first gates switch on.
9
+ 3. Produce the planning artifacts first (brief, decision register, journeys, event model, architecture, lens review, in that order, whichever are recommended), then the task record. `review` and `adr-if-needed` are not written up front: review happens on the finished slice, and an ADR when a decision needs one. To skip one, call `devsys_record_departure` with gate `artifact.skipped:<artifact>` and the reason; do not skip silently. The event model and architecture are recommendations, never gates.
10
+ 4. For `capability` and `product` the phase stays `planning` while you produce the artifacts and the plan. The single point where it changes is the user approving the whole plan: then call `devsys_begin_work` so the review and red-first gates switch on (it does nothing if already implementing).
11
11
  5. For a confirmed `fix` the user is also asked whether to skip fresh-context review (it is logged if they say yes; if no, the slice needs its review rounds). For `fix` and `change` the phase is already `implementing`: write the task record, then work it test-first.
@@ -13,7 +13,7 @@ const reply = (text: string, isError = false) => ({
13
13
  /**
14
14
  * `devsys_begin_work`: the planning → implementing transition. Intake leaves capability and product work in
15
15
  * `planning`; the review and red-first gates only run while implementing, so without this step the largest work
16
- * would get the fewest gates. Call it once the user has approved the plan.
16
+ * would get the fewest gates. Call it once the user has approved the plan; when already implementing it is a harmless no-op.
17
17
  */
18
18
  export function createBeginWorkTool(deps: {
19
19
  state: SessionState;
@@ -28,6 +28,13 @@ export function createBeginWorkTool(deps: {
28
28
  exposure: "direct",
29
29
  execute(_id, _params: Static<typeof Parameters>) {
30
30
  const { phase, activeSlice } = deps.state.get();
31
+ if (phase === "implementing" && activeSlice !== undefined) {
32
+ return Promise.resolve(
33
+ reply(
34
+ `Already implementing slice ${activeSlice}; the review and red-first gates are on.`,
35
+ ),
36
+ );
37
+ }
31
38
  if (phase !== "planning" || activeSlice === undefined) {
32
39
  return Promise.resolve(
33
40
  reply(
@@ -15,8 +15,11 @@ import {
15
15
  const ID = /^[a-z0-9][a-z0-9-]*$/;
16
16
  const ID_MAX = 60;
17
17
 
18
+ /** ASCII id from a title: accents are folded (Résumé → resume); a title with no ASCII letters or digits becomes `item`. */
18
19
  const slugOf = (title: string): string =>
19
20
  title
21
+ .normalize("NFD")
22
+ .replace(/\p{M}+/gu, "")
20
23
  .toLowerCase()
21
24
  .replace(/[^a-z0-9]+/g, "-")
22
25
  .replace(/^-+/, "")
@@ -79,9 +82,8 @@ const save = async (root: string, item: WorkItem): Promise<TrackerResult<WorkIte
79
82
  return ok(item);
80
83
  };
81
84
 
82
- const freshId = async (root: string, title: string): Promise<string | undefined> => {
83
- const base = slugOf(title);
84
- if (base === "") return undefined;
85
+ const freshId = async (root: string, title: string): Promise<string> => {
86
+ const base = slugOf(title) || "item";
85
87
  const taken = new Set(await itemIds(root));
86
88
  let id = base;
87
89
  for (let n = 2; taken.has(id); n++) id = `${base}-${n}`;
@@ -97,10 +99,8 @@ const listItems = async (root: string, filter: ItemFilter): Promise<TrackerResul
97
99
  const createItem = async (root: string, input: NewWorkItem): Promise<TrackerResult<WorkItem>> => {
98
100
  const problem = unwritable({ title: input.title, labels: input.labels ?? [] });
99
101
  if (problem !== undefined) return trackerError(problem);
100
- const id = await freshId(root, input.title);
101
- if (id === undefined) return trackerError("a work item title needs letters or digits");
102
102
  return save(root, {
103
- id,
103
+ id: await freshId(root, input.title),
104
104
  title: input.title,
105
105
  body: input.body ?? "",
106
106
  status: "open",