gcf-aif-ext 0.2.7 → 0.4.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.
Files changed (63) hide show
  1. package/README.md +54 -10
  2. package/commands/gcf.js +452 -30
  3. package/extension.json +22 -5
  4. package/hooks/pre-commit.js +69 -41
  5. package/injections/commit-status.md +7 -4
  6. package/injections/plan-context.md +8 -4
  7. package/injections/review-pillars.md +4 -3
  8. package/lib/anchors.js +60 -35
  9. package/lib/audit-core.js +172 -55
  10. package/lib/bundle.js +278 -0
  11. package/lib/context.js +25 -23
  12. package/lib/design-check.js +631 -0
  13. package/lib/design.js +179 -0
  14. package/lib/gcf-paths.js +13 -2
  15. package/lib/git.js +63 -0
  16. package/lib/id.js +7 -15
  17. package/lib/inbox.js +54 -0
  18. package/lib/index-gen.js +62 -13
  19. package/lib/ledger.js +94 -0
  20. package/lib/linter.js +17 -5
  21. package/lib/live.js +179 -0
  22. package/lib/migrate.js +345 -0
  23. package/lib/plan-check.js +31 -0
  24. package/lib/playtest-list.js +9 -5
  25. package/lib/systems-doc.js +85 -0
  26. package/lib/transitions.js +36 -21
  27. package/lib/unit-ops.js +297 -0
  28. package/lib/validate-item.js +244 -0
  29. package/lib/verdict-fs.js +112 -165
  30. package/lib/work-ops.js +727 -0
  31. package/package.json +1 -1
  32. package/profiles/game.md +68 -0
  33. package/profiles/glossary.md +37 -0
  34. package/profiles/pillars.md +3 -2
  35. package/profiles/system.md +1 -1
  36. package/profiles/unit-constraint.md +52 -0
  37. package/profiles/unit-entity.md +48 -0
  38. package/profiles/unit-mechanic.md +111 -0
  39. package/profiles/unit-resource.md +91 -0
  40. package/profiles/unit-rule.md +52 -0
  41. package/schemas/item.schema.json +105 -0
  42. package/skills/gcf-adopt/SKILL.md +3 -3
  43. package/skills/gcf-audit/SKILL.md +7 -6
  44. package/skills/gcf-card/SKILL.md +31 -17
  45. package/skills/gcf-debate/SKILL.md +17 -14
  46. package/skills/gcf-ingest/SKILL.md +39 -23
  47. package/skills/gcf-init/SKILL.md +5 -5
  48. package/skills/gcf-triage/SKILL.md +35 -0
  49. package/skills/gcf-verdict/SKILL.md +19 -15
  50. package/skills/gcf-vision/SKILL.md +17 -11
  51. package/templates/PILLARS.md +2 -0
  52. package/templates/backlog.md +2 -5
  53. package/templates/cnlp.config.json +0 -1
  54. package/templates/config.yml +7 -0
  55. package/templates/design.md +59 -0
  56. package/templates/game.md +24 -0
  57. package/templates/glossary.md +11 -0
  58. package/templates/item.md +58 -0
  59. package/templates/system.md +1 -1
  60. package/lib/validate-card.js +0 -82
  61. package/profiles/card.md +0 -65
  62. package/schemas/card.schema.json +0 -30
  63. package/templates/card.md +0 -34
package/lib/context.js CHANGED
@@ -1,31 +1,32 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { existsSync } from 'node:fs';
3
3
  import path from 'node:path';
4
- import { listCards, listSystems, readConfig, tokensEst } from './gcf-paths.js';
4
+ import { listItems, listSystems, readConfig, tokensEst } from './gcf-paths.js';
5
5
 
6
6
  /**
7
7
  * Readset assembly — the whole command is deterministic (GCF-PRD §9.1, §10.1):
8
- * PILLARS + touched systems + the card + its depends + graveyard tag hits +
8
+ * PILLARS + LIVE.md + touched systems + the item + its depends + graveyard tag hits +
9
9
  * D2 assumptions whose sys overlaps. Never over_budget-throws: the caller gates.
10
10
  */
11
- export async function buildContext(projectDir, cardId) {
11
+ export async function buildContext(projectDir, itemId) {
12
12
  const root = path.join(path.resolve(projectDir), 'gcf');
13
13
  const config = await readConfig(root);
14
- const cards = await listCards(root);
15
- const card = cards.find((c) => c.data.id === cardId);
16
- if (!card) throw new Error(`Card not found: ${cardId}`);
14
+ const items = await listItems(root);
15
+ const item = items.find((c) => c.data.id === itemId);
16
+ if (!item) throw new Error(`Work item not found: ${itemId}`);
17
17
 
18
- const systems = card.data.system ?? [];
19
- const files = await readsetFiles(root, card, cards);
20
- const cardText = `${card.data.slug} ${systems.join(' ')} ${card.body}`.toLowerCase();
21
- const graveyard_hits = await graveyardHits(root, cardText);
18
+ const systems = item.data.system ?? [];
19
+ const files = await readsetFiles(root, item, items);
20
+ const itemText =
21
+ `${item.data.id} ${item.data.title ?? ''} ${systems.join(' ')} ${item.body}`.toLowerCase();
22
+ const graveyard_hits = await graveyardHits(root, itemText);
22
23
  const d2 = await d2Assumptions(root, systems);
23
24
 
24
25
  const extraTokens = tokensEst(graveyard_hits.join('\n')) + tokensEst(d2.join('\n'));
25
26
  const total_tokens_est = files.reduce((sum, f) => sum + f.tokens_est, 0) + extraTokens;
26
27
 
27
28
  return {
28
- card: cardId,
29
+ item: itemId,
29
30
  files,
30
31
  graveyard_hits,
31
32
  d2,
@@ -35,8 +36,8 @@ export async function buildContext(projectDir, cardId) {
35
36
  };
36
37
  }
37
38
 
38
- /** PILLARS, touched systems, the card, its depends — in that order, skipping absent files. */
39
- async function readsetFiles(root, card, cards) {
39
+ /** PILLARS, LIVE.md, touched systems, the item, its depends — in order, skipping absent files. */
40
+ async function readsetFiles(root, item, items) {
40
41
  const files = [];
41
42
  const push = async (rel) => {
42
43
  const abs = path.join(root, rel);
@@ -46,39 +47,40 @@ async function readsetFiles(root, card, cards) {
46
47
  };
47
48
 
48
49
  await push('PILLARS.md');
49
- const systems = card.data.system ?? [];
50
+ await push('LIVE.md');
51
+ const systems = item.data.system ?? [];
50
52
  for (const s of (await listSystems(root)).filter((d) => systems.includes(d.data.system))) {
51
53
  await push(`systems/${s.name}`);
52
54
  }
53
55
 
54
- await push(`cards/${card.name}`);
55
- for (const depId of card.data.depends ?? []) {
56
- const dep = cards.find((c) => c.data.id === depId);
57
- if (dep) await push(`cards/${dep.name}`);
56
+ await push(`work/${item.name}`);
57
+ for (const depId of item.data.depends ?? []) {
58
+ const dep = items.find((c) => c.data.id === depId);
59
+ if (dep) await push(`work/${dep.name}`);
58
60
  }
59
61
 
60
62
  return files;
61
63
  }
62
64
 
63
- /** Graveyard: headline tags only; a hit is a tag appearing in the card's own text. */
64
- async function graveyardHits(root, cardText) {
65
+ /** Graveyard: headline tags only; a hit is a tag appearing in the item's own text. */
66
+ async function graveyardHits(root, itemText) {
65
67
  const gyFile = path.join(root, 'graveyard.md');
66
68
  if (!existsSync(gyFile)) return [];
67
69
  const hits = [];
68
70
  for (const line of (await readFile(gyFile, 'utf8')).split('\n')) {
69
- const m = line.match(/^- (card-\d{3}) \| .* \| tags: (.+)$/);
71
+ const m = line.match(/^- (\S+) \| .* \| tags: (.+)$/);
70
72
  if (!m) continue;
71
73
  const tags = m[2]
72
74
  .split(',')
73
75
  .map((t) => t.trim().toLowerCase())
74
76
  .filter(Boolean);
75
- if (tags.some((t) => cardText.includes(t))) hits.push(line.trim());
77
+ if (tags.some((t) => itemText.includes(t))) hits.push(line.trim());
76
78
  }
77
79
 
78
80
  return hits;
79
81
  }
80
82
 
81
- /** D2 assumptions whose sys: overlaps the card's systems (GCF-PRD §9.1). */
83
+ /** D2 assumptions whose sys: overlaps the item's systems (GCF-PRD §9.1). */
82
84
  async function d2Assumptions(root, systems) {
83
85
  const oqFile = path.join(root, 'open-questions.md');
84
86
  if (!existsSync(oqFile)) return [];