codecartographer-pi 0.22.2 → 0.22.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.
@@ -3,4 +3,4 @@
3
3
  # workspace's framework-owned files (GUIDE.md, templates/, workflow/ pipelines
4
4
  # and VALIDATE.md) predate the running release. Written at release time and
5
5
  # copied verbatim by init — never edit by hand.
6
- scaffold_version: 0.22.2
6
+ scaffold_version: 0.22.3
@@ -0,0 +1,18 @@
1
+ export interface CompletionCandidate {
2
+ value: string;
3
+ label?: string;
4
+ description?: string;
5
+ }
6
+ export interface CompletionItem {
7
+ value: string;
8
+ label: string;
9
+ description?: string;
10
+ }
11
+ /**
12
+ * Complete the last whitespace-separated token of `prefix` from `candidates`,
13
+ * returning items whose `value` is the full argument text Pi should put in
14
+ * the line — every earlier token, then the candidate. A candidate already
15
+ * typed as an earlier token is not offered again. Returns null when nothing
16
+ * matches, which is what Pi expects for "no popup".
17
+ */
18
+ export declare function completeLastToken(prefix: string, candidates: readonly CompletionCandidate[]): CompletionItem[] | null;
@@ -0,0 +1,36 @@
1
+ // Argument completion for the /codecarto-* slash commands.
2
+ //
3
+ // Pi hands `getArgumentCompletions` everything typed after the command name,
4
+ // and when the user accepts an item it replaces all of that text with the
5
+ // item's `value` (pi 0.85: `argumentText = textBeforeCursor.slice(spaceIndex
6
+ // + 1)` on the way in, `applyCompletion` slicing `cursorCol - prefix.length`
7
+ // on the way out). A completer that matches the *last* token but returns a
8
+ // bare flag therefore erases every flag typed before it: with the popup open,
9
+ // `/codecarto-next --auto --llm-steer` became `/codecarto-next --llm-steer` on
10
+ // Enter, the steered phase ran alone, and the auto run never started. The
11
+ // same shape turned `--auto --strict` into `--strict`, the one combination
12
+ // the parser rejects.
13
+ //
14
+ // So: match the last token, but return the whole line.
15
+ /**
16
+ * Complete the last whitespace-separated token of `prefix` from `candidates`,
17
+ * returning items whose `value` is the full argument text Pi should put in
18
+ * the line — every earlier token, then the candidate. A candidate already
19
+ * typed as an earlier token is not offered again. Returns null when nothing
20
+ * matches, which is what Pi expects for "no popup".
21
+ */
22
+ export function completeLastToken(prefix, candidates) {
23
+ const tokens = prefix.split(/\s+/);
24
+ const last = tokens.pop() ?? "";
25
+ const earlier = tokens.filter((token) => token.length > 0);
26
+ const head = earlier.join(" ");
27
+ const typed = new Set(earlier);
28
+ const items = candidates
29
+ .filter((candidate) => candidate.value.startsWith(last) && !typed.has(candidate.value))
30
+ .map((candidate) => ({
31
+ value: head ? `${head} ${candidate.value}` : candidate.value,
32
+ label: candidate.label ?? candidate.value,
33
+ ...(candidate.description && { description: candidate.description }),
34
+ }));
35
+ return items.length > 0 ? items : null;
36
+ }
@@ -6,6 +6,7 @@ import { parseDashboardFlags } from "./dashboard-flags.js";
6
6
  import { narrateDashboard } from "./dashboard-narrator.js";
7
7
  import { parseBroadsideFlags, KNOWN_BROADSIDE_TOKENS } from "./broadside-flags.js";
8
8
  import { parseNextFlags } from "./next-flags.js";
9
+ import { completeLastToken } from "./completions.js";
9
10
  import { buildPiGuideMessage } from "./guide-framing.js";
10
11
  import { isCtxLive, notifyCtx } from "./notify.js";
11
12
  import { phaseCompactionExtension } from "./phase-compaction.js";
@@ -432,12 +433,7 @@ export default function codeCartographerExtension(pi) {
432
433
  });
433
434
  pi.registerCommand("codecarto-init", {
434
435
  description: "Initialize .codecarto/ in the current repository",
435
- getArgumentCompletions: (prefix) => {
436
- const items = Object.keys(PIPELINE_ALIASES)
437
- .filter((value) => value.startsWith(prefix))
438
- .map((value) => ({ value, label: value }));
439
- return items.length > 0 ? items : null;
440
- },
436
+ getArgumentCompletions: (prefix) => completeLastToken(prefix, Object.keys(PIPELINE_ALIASES).map((value) => ({ value }))),
441
437
  handler: async (args, ctx) => {
442
438
  const trimmedArgs = args.trim();
443
439
  const pipelineChoice = resolvePipelineChoice(trimmedArgs);
@@ -536,12 +532,7 @@ export default function codeCartographerExtension(pi) {
536
532
  });
537
533
  pi.registerCommand("codecarto-switch-pipeline", {
538
534
  description: "Switch the active pipeline without losing findings or progress: /codecarto-switch-pipeline <variant>",
539
- getArgumentCompletions: (prefix) => {
540
- const items = Object.keys(PIPELINE_ALIASES)
541
- .filter((value) => value.startsWith(prefix))
542
- .map((value) => ({ value, label: value }));
543
- return items.length > 0 ? items : null;
544
- },
535
+ getArgumentCompletions: (prefix) => completeLastToken(prefix, Object.keys(PIPELINE_ALIASES).map((value) => ({ value }))),
545
536
  handler: async (args, ctx) => {
546
537
  const trimmedArgs = args.trim();
547
538
  if (!trimmedArgs) {
@@ -595,16 +586,17 @@ export default function codeCartographerExtension(pi) {
595
586
  // --strict is offered only once --auto is present, because on its own
596
587
  // it is rejected — suggesting it standalone invites the one error the
597
588
  // parser has.
589
+ // Each item's value is the whole argument line (see completions.ts):
590
+ // accepting `--llm-steer` after `--auto ` keeps the `--auto`.
598
591
  const autoAlreadyTyped = prefix.includes("--auto");
599
- const items = [
600
- { value: "--auto", label: "--auto", description: "run every remaining phase back to back (recommended with --llm-steer)" },
601
- { value: "--llm-steer", label: "--llm-steer", description: "seed each phase from the previous phase's closeout; no effect on the first phase" },
602
- { value: "--no-llm-steer", label: "--no-llm-steer", description: "force steering off when the workspace config turns it on" },
592
+ return completeLastToken(prefix, [
593
+ { value: "--auto", description: "run every remaining phase back to back (recommended with --llm-steer)" },
594
+ { value: "--llm-steer", description: "seed each phase from the previous phase's closeout; no effect on the first phase" },
595
+ { value: "--no-llm-steer", description: "force steering off when the workspace config turns it on" },
603
596
  ...(autoAlreadyTyped
604
- ? [{ value: "--strict", label: "--strict", description: "with --auto: stop on PASS WITH GAPS instead of advancing" }]
597
+ ? [{ value: "--strict", description: "with --auto: stop on PASS WITH GAPS instead of advancing" }]
605
598
  : []),
606
- ].filter((item) => item.value.startsWith(prefix.split(/\s+/).pop() ?? prefix));
607
- return items.length > 0 ? items : null;
599
+ ]);
608
600
  },
609
601
  handler: async (args, ctx) => {
610
602
  const flags = parseNextFlags(args);
@@ -967,10 +959,7 @@ export default function codeCartographerExtension(pi) {
967
959
  description: "Read the packaged CodeCartographer agent guide into the session: /codecarto-guide [topic]",
968
960
  getArgumentCompletions: async (prefix) => {
969
961
  const topics = await listGuideTopics().catch(() => ["overview"]);
970
- const items = topics
971
- .filter((value) => value.startsWith(prefix))
972
- .map((value) => ({ value, label: value }));
973
- return items.length > 0 ? items : null;
962
+ return completeLastToken(prefix, topics.map((value) => ({ value })));
974
963
  },
975
964
  handler: async (args, ctx) => {
976
965
  // The guide is packaged with the extension, not copied into a
@@ -1004,12 +993,9 @@ export default function codeCartographerExtension(pi) {
1004
993
  });
1005
994
  pi.registerCommand("codecarto-broadside", {
1006
995
  description: "Batch reconnaissance (Broad-Side): /codecarto-broadside [submit|collect|status|models] [lenses…] [--model=ID] [--lens-model=LENS:ID] [flags]",
1007
- getArgumentCompletions: (prefix) => {
1008
- const items = KNOWN_BROADSIDE_TOKENS
1009
- .filter((value) => value.startsWith(prefix))
1010
- .map((value) => ({ value, label: value }));
1011
- return items.length > 0 ? items : null;
1012
- },
996
+ // Completes the token under the cursor, so lens names and flags are
997
+ // offered after the action too, and keeps everything typed before it.
998
+ getArgumentCompletions: (prefix) => completeLastToken(prefix, KNOWN_BROADSIDE_TOKENS.map((value) => ({ value }))),
1013
999
  handler: async (args, ctx) => {
1014
1000
  const flags = parseBroadsideFlags(args);
1015
1001
  if (flags.unknown.length > 0) {
@@ -1439,12 +1425,7 @@ export default function codeCartographerExtension(pi) {
1439
1425
  });
1440
1426
  pi.registerCommand("codecarto-dashboard", {
1441
1427
  description: "Regenerate .codecarto/dashboard.html (use --narrate for an LLM executive summary)",
1442
- getArgumentCompletions: (prefix) => {
1443
- const items = ["--narrate"]
1444
- .filter((value) => value.startsWith(prefix))
1445
- .map((value) => ({ value, label: value }));
1446
- return items.length > 0 ? items : null;
1447
- },
1428
+ getArgumentCompletions: (prefix) => completeLastToken(prefix, [{ value: "--narrate" }]),
1448
1429
  handler: async (args, ctx) => {
1449
1430
  const flags = parseDashboardFlags(args);
1450
1431
  if (flags.unknown.length > 0) {
@@ -1515,10 +1496,7 @@ export default function codeCartographerExtension(pi) {
1515
1496
  description: "Apply a post-pipeline amendment from .codecarto/scratch/amendments/, after a preview: /codecarto-amend <name | scratch/amendments/name.yaml>",
1516
1497
  getArgumentCompletions: async (prefix) => {
1517
1498
  const names = await listAmendmentNames(join(sessionCwd ?? process.cwd(), ".codecarto"));
1518
- const items = names
1519
- .filter((value) => value.startsWith(prefix))
1520
- .map((value) => ({ value, label: value }));
1521
- return items.length > 0 ? items : null;
1499
+ return completeLastToken(prefix, names.map((value) => ({ value })));
1522
1500
  },
1523
1501
  handler: async (args, ctx) => {
1524
1502
  const state = await ensureWorkspaceState(ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codecartographer-pi",
3
- "version": "0.22.2",
3
+ "version": "0.22.3",
4
4
  "mcpName": "io.github.HuginnIndustries/codecartographer",
5
5
  "description": "Turn an unfamiliar codebase into a validated reimplementation spec, then synthesize confirmed specs and a product vision into a traceable plan.",
6
6
  "type": "module",