ahead-pi 0.8.2 → 0.8.4
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 +1 -1
- package/src/guidance.ts +37 -11
- package/src/index.ts +25 -9
package/package.json
CHANGED
package/src/guidance.ts
CHANGED
|
@@ -424,7 +424,9 @@ export function nextAction(state: RunState, workflow: WorkflowDefinition): Guide
|
|
|
424
424
|
? "Ask AI to challenge the human option and expand alternatives"
|
|
425
425
|
: state.phase.id === "plan"
|
|
426
426
|
? "Ask AI to challenge the human first-pass plan"
|
|
427
|
-
:
|
|
427
|
+
: artifact.title.startsWith("AI ")
|
|
428
|
+
? `Ask AI for ${lowercaseFirst(artifact.title.slice(3))}`
|
|
429
|
+
: `Ask AI to contribute ${artifact.title}`;
|
|
428
430
|
return artifact.required
|
|
429
431
|
? { actor, label, artifactKind: artifact.kind }
|
|
430
432
|
: { actor, label, artifactKind: artifact.kind, optional: true };
|
|
@@ -533,8 +535,10 @@ export function buildArtifactTemplate(
|
|
|
533
535
|
}
|
|
534
536
|
|
|
535
537
|
/**
|
|
536
|
-
* Insert inspiration-only example lines into each field as
|
|
537
|
-
*
|
|
538
|
+
* Insert inspiration-only example lines into each field as plain text with
|
|
539
|
+
* an "ex. - " prefix. Validation strips these lines for the emptiness
|
|
540
|
+
* check and rejects any saved form that still contains them, so no example
|
|
541
|
+
* line can ever persist in a recorded artifact.
|
|
538
542
|
*/
|
|
539
543
|
export function insertFieldExamples(template: string, perField: string[][]): string {
|
|
540
544
|
let updated = template;
|
|
@@ -548,16 +552,27 @@ export function insertFieldExamples(template: string, perField: string[][]): str
|
|
|
548
552
|
}
|
|
549
553
|
updated = updated.replace(
|
|
550
554
|
marker,
|
|
551
|
-
[
|
|
552
|
-
marker,
|
|
553
|
-
"<!-- Examples — inspiration only, not requirements. Delete them and write your own answer. -->",
|
|
554
|
-
...examples.map((example) => `<!-- ~ ${example} ~ -->`),
|
|
555
|
-
].join("\n"),
|
|
555
|
+
[marker, ...examples.map((example) => `ex. - ${example}`)].join("\n"),
|
|
556
556
|
);
|
|
557
557
|
}
|
|
558
558
|
return updated;
|
|
559
559
|
}
|
|
560
560
|
|
|
561
|
+
const EXAMPLE_LINE_PATTERN = /^\s*ex\.\s*-\s?/;
|
|
562
|
+
|
|
563
|
+
function stripExampleLines(text: string): { cleaned: string; hadExamples: boolean } {
|
|
564
|
+
const kept: string[] = [];
|
|
565
|
+
let hadExamples = false;
|
|
566
|
+
for (const line of text.split("\n")) {
|
|
567
|
+
if (EXAMPLE_LINE_PATTERN.test(line)) {
|
|
568
|
+
hadExamples = true;
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
kept.push(line);
|
|
572
|
+
}
|
|
573
|
+
return { cleaned: kept.join("\n").trim(), hadExamples };
|
|
574
|
+
}
|
|
575
|
+
|
|
561
576
|
export function validateArtifactForm(content: string, prompts: string[]): string[] {
|
|
562
577
|
const errors: string[] = [];
|
|
563
578
|
for (const [index, prompt] of prompts.entries()) {
|
|
@@ -575,17 +590,28 @@ export function validateArtifactForm(content: string, prompts: string[]): string
|
|
|
575
590
|
.slice(beginIndex + begin.length, endIndex)
|
|
576
591
|
.replace(/<!--[\s\S]*?-->/g, "")
|
|
577
592
|
.trim();
|
|
578
|
-
|
|
579
|
-
|
|
593
|
+
const { cleaned, hadExamples } = stripExampleLines(response);
|
|
594
|
+
if (!cleaned) {
|
|
595
|
+
errors.push(
|
|
596
|
+
hadExamples ? `${prompt} (replace the “ex. -” example lines with your own answer)` : prompt,
|
|
597
|
+
);
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
if (hadExamples) {
|
|
601
|
+
errors.push(`${prompt} (remove the leftover “ex. -” example lines)`);
|
|
580
602
|
continue;
|
|
581
603
|
}
|
|
582
|
-
if (/^(?:n\/?a|not applicable)\s*[.!]?$/i.test(
|
|
604
|
+
if (/^(?:n\/?a|not applicable)\s*[.!]?$/i.test(cleaned)) {
|
|
583
605
|
errors.push(`${prompt} (explain why it is not applicable)`);
|
|
584
606
|
}
|
|
585
607
|
}
|
|
586
608
|
return errors;
|
|
587
609
|
}
|
|
588
610
|
|
|
611
|
+
function lowercaseFirst(value: string): string {
|
|
612
|
+
return value.charAt(0).toLowerCase() + value.slice(1);
|
|
613
|
+
}
|
|
614
|
+
|
|
589
615
|
function formatArtifactStatus(artifact: ArtifactState): string {
|
|
590
616
|
const owner = artifact.actor === "ai" ? "AI" : artifact.actor === "human" ? "you" : "you/AI";
|
|
591
617
|
return `${artifact.present ? "✓" : "○"} ${artifact.title} [${owner}]`;
|
package/src/index.ts
CHANGED
|
@@ -702,7 +702,7 @@ async function openAheadMode(
|
|
|
702
702
|
});
|
|
703
703
|
} else if (missingRequired.length === 0) {
|
|
704
704
|
actions.push({
|
|
705
|
-
label: `Continue without optional AI contribution ·
|
|
705
|
+
label: `Continue without optional AI contribution · ${state.gate.title}`,
|
|
706
706
|
run: async () => acceptAndContinue(ctx),
|
|
707
707
|
});
|
|
708
708
|
}
|
|
@@ -752,8 +752,12 @@ async function openAheadMode(
|
|
|
752
752
|
});
|
|
753
753
|
}
|
|
754
754
|
|
|
755
|
+
// The generic challenger appears only when the next move is the human's.
|
|
756
|
+
// When the primary action is already an AI contribution, the formal AI
|
|
757
|
+
// artifact owns that job and a second "challenge me" entry is noise.
|
|
755
758
|
if (
|
|
756
759
|
state.allowed_ai_capabilities.length > 0 &&
|
|
760
|
+
action.actor !== "ai" &&
|
|
757
761
|
state.artifacts.some(
|
|
758
762
|
(artifact) => artifact.present && artifact.recorded_by?.kind === "human" && artifact.path,
|
|
759
763
|
)
|
|
@@ -1617,15 +1621,27 @@ async function recordHumanArtifact(
|
|
|
1617
1621
|
const prompts = promptsForArtifact(state.workflow_id, state.phase.id, artifact.kind);
|
|
1618
1622
|
let template = await humanArtifactTemplate(store, state, run, artifact.kind, artifact.title);
|
|
1619
1623
|
if (ctx.hasUI && artifact.kind !== "review-disposition" && prompts.length > 0) {
|
|
1620
|
-
|
|
1621
|
-
|
|
1624
|
+
// Visible progress while drafting: the loader row and footer status show
|
|
1625
|
+
// until the call resolves, so the wait never looks like a hang.
|
|
1626
|
+
const showProgress = ctx.model !== undefined;
|
|
1627
|
+
if (showProgress) {
|
|
1628
|
+
ctx.ui.setWorkingMessage("Drafting example prompts (inspiration only)…");
|
|
1629
|
+
ctx.ui.setStatus("ahead-examples", "Drafting example prompts…");
|
|
1630
|
+
}
|
|
1631
|
+
let draft;
|
|
1632
|
+
try {
|
|
1633
|
+
draft = await draftFieldExamples(ctx, {
|
|
1634
|
+
workflowTitle: engine.getWorkflow(state.workflow_id).title,
|
|
1635
|
+
phaseTitle: state.phase.title,
|
|
1636
|
+
runTitle: run.title,
|
|
1637
|
+
fields: prompts,
|
|
1638
|
+
});
|
|
1639
|
+
} finally {
|
|
1640
|
+
if (showProgress) {
|
|
1641
|
+
ctx.ui.setWorkingMessage();
|
|
1642
|
+
ctx.ui.setStatus("ahead-examples", undefined);
|
|
1643
|
+
}
|
|
1622
1644
|
}
|
|
1623
|
-
const draft = await draftFieldExamples(ctx, {
|
|
1624
|
-
workflowTitle: engine.getWorkflow(state.workflow_id).title,
|
|
1625
|
-
phaseTitle: state.phase.title,
|
|
1626
|
-
runTitle: run.title,
|
|
1627
|
-
fields: prompts,
|
|
1628
|
-
});
|
|
1629
1645
|
if (draft.examples) {
|
|
1630
1646
|
template = insertFieldExamples(template, draft.examples);
|
|
1631
1647
|
} else if (draft.skipped === "no-auth") {
|