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.
- package/README.md +54 -10
- package/commands/gcf.js +452 -30
- package/extension.json +22 -5
- package/hooks/pre-commit.js +69 -41
- package/injections/commit-status.md +7 -4
- package/injections/plan-context.md +8 -4
- package/injections/review-pillars.md +4 -3
- package/lib/anchors.js +60 -35
- package/lib/audit-core.js +172 -55
- package/lib/bundle.js +278 -0
- package/lib/context.js +25 -23
- package/lib/design-check.js +631 -0
- package/lib/design.js +179 -0
- package/lib/gcf-paths.js +13 -2
- package/lib/git.js +63 -0
- package/lib/id.js +7 -15
- package/lib/inbox.js +54 -0
- package/lib/index-gen.js +62 -13
- package/lib/ledger.js +94 -0
- package/lib/linter.js +17 -5
- package/lib/live.js +179 -0
- package/lib/migrate.js +345 -0
- package/lib/plan-check.js +31 -0
- package/lib/playtest-list.js +9 -5
- package/lib/systems-doc.js +85 -0
- package/lib/transitions.js +36 -21
- package/lib/unit-ops.js +297 -0
- package/lib/validate-item.js +244 -0
- package/lib/verdict-fs.js +112 -165
- package/lib/work-ops.js +727 -0
- package/package.json +1 -1
- package/profiles/game.md +68 -0
- package/profiles/glossary.md +37 -0
- package/profiles/pillars.md +3 -2
- package/profiles/system.md +1 -1
- package/profiles/unit-constraint.md +52 -0
- package/profiles/unit-entity.md +48 -0
- package/profiles/unit-mechanic.md +111 -0
- package/profiles/unit-resource.md +91 -0
- package/profiles/unit-rule.md +52 -0
- package/schemas/item.schema.json +105 -0
- package/skills/gcf-adopt/SKILL.md +3 -3
- package/skills/gcf-audit/SKILL.md +7 -6
- package/skills/gcf-card/SKILL.md +31 -17
- package/skills/gcf-debate/SKILL.md +17 -14
- package/skills/gcf-ingest/SKILL.md +39 -23
- package/skills/gcf-init/SKILL.md +5 -5
- package/skills/gcf-triage/SKILL.md +35 -0
- package/skills/gcf-verdict/SKILL.md +19 -15
- package/skills/gcf-vision/SKILL.md +17 -11
- package/templates/PILLARS.md +2 -0
- package/templates/backlog.md +2 -5
- package/templates/cnlp.config.json +0 -1
- package/templates/config.yml +7 -0
- package/templates/design.md +59 -0
- package/templates/game.md +24 -0
- package/templates/glossary.md +11 -0
- package/templates/item.md +58 -0
- package/templates/system.md +1 -1
- package/lib/validate-card.js +0 -82
- package/profiles/card.md +0 -65
- package/schemas/card.schema.json +0 -30
- 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 {
|
|
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
|
|
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,
|
|
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
|
|
15
|
-
const
|
|
16
|
-
if (!
|
|
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 =
|
|
19
|
-
const files = await readsetFiles(root,
|
|
20
|
-
const
|
|
21
|
-
|
|
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
|
-
|
|
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
|
|
39
|
-
async function readsetFiles(root,
|
|
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
|
-
|
|
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(`
|
|
55
|
-
for (const depId of
|
|
56
|
-
const dep =
|
|
57
|
-
if (dep) await push(`
|
|
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
|
|
64
|
-
async function graveyardHits(root,
|
|
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(/^- (
|
|
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) =>
|
|
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
|
|
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 [];
|