fraim 2.0.363-next.1 → 2.0.364-next.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/dist/registry/stubs/jobs/ai-employee/ai-engineering/publish-mcp-app.md +8 -6
- package/dist/registry/stubs/jobs/ai-employee/dev-ops/setup-remote-hub.md +3 -2
- package/dist/registry/stubs/jobs/ai-employee/engineering/mobile-app-rejection-response.md +5 -6
- package/dist/registry/stubs/jobs/ai-employee/engineering/mobile-app-submission.md +5 -5
- package/dist/registry/stubs/jobs/ai-employee/finance/cloud-credits-application.md +3 -4
- package/dist/registry/stubs/jobs/ai-employee/fraim/adhoc-prompt.md +4 -2
- package/dist/registry/stubs/jobs/ai-employee/fraim/contribute-to-fraim.md +5 -5
- package/dist/registry/stubs/jobs/ai-employee/fraim/file-fraim-issue.md +5 -5
- package/dist/registry/stubs/jobs/ai-employee/fraim/troubleshoot-fraim.md +3 -1
- package/dist/registry/stubs/jobs/ai-employee/management/project-onboarding.md +3 -3
- package/dist/src/cli/commands/workspace-config.js +194 -0
- package/dist/src/cli/utils/agent-adapters.js +1 -1
- package/package.json +1 -1
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|
Publish and distribute a Model Context Protocol (MCP) server application to major AI marketplaces and app stores (like ChatGPT and Claude).
|
|
7
7
|
|
|
8
8
|
## Outcome
|
|
9
|
-
-
|
|
10
|
-
-
|
|
9
|
+
- A reviewable submission package per targeted platform (assets, listing details, verification plan) at `docs/mcp-publishing/{appName}-submission-plan-{YYYY-MM-DD}.md`.
|
|
10
|
+
- After the manager selects `approve_and_publish`, the MCP app is submitted for approval to all targeted platforms, with a status report detailing the submission state, verification challenges, and expected wait times.
|
|
11
11
|
|
|
12
12
|
## Steps
|
|
13
|
-
1. Phase 1: `
|
|
14
|
-
2. Phase 2: `
|
|
15
|
-
3. Phase 3: `
|
|
16
|
-
4. Phase 4: `
|
|
13
|
+
1. Phase 1: `scoping`
|
|
14
|
+
2. Phase 2: `verify-assets`
|
|
15
|
+
3. Phase 3: `prepare-submissions`
|
|
16
|
+
4. Phase 4: `submit`
|
|
17
|
+
5. Phase 5: `address-feedback`
|
|
18
|
+
6. Phase 6: `retrospective`
|
|
17
19
|
|
|
18
20
|
---
|
|
19
21
|
|
|
@@ -16,8 +16,9 @@ Provision a remote machine, install the FRAIM Hub runtime, and register it as a
|
|
|
16
16
|
3. Phase 3: `install`
|
|
17
17
|
4. Phase 4: `configure`
|
|
18
18
|
5. Phase 5: `start`
|
|
19
|
-
6. Phase 6: `
|
|
20
|
-
7. Phase 7: `
|
|
19
|
+
6. Phase 6: `submit`
|
|
20
|
+
7. Phase 7: `address-feedback`
|
|
21
|
+
8. Phase 8: `retrospective`
|
|
21
22
|
|
|
22
23
|
---
|
|
23
24
|
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
# FRAIM Job: mobile-app-rejection-response
|
|
4
4
|
|
|
5
5
|
## Intent
|
|
6
|
-
Respond to an app store rejection — analyze the cited guideline / policy, gather evidence, draft a Resolution Center / Policy Center reply, post it, and decide next steps (clarify-and-resubmit, ship a fixed binary, or escalate via App Review Board / Play appeal). Single job parameterized by `platform` — composes Apple-specific or Play-specific guidance based on a Phase 1 decision. Reused on every rejection cycle. Triggered
|
|
6
|
+
Respond to an app store rejection — analyze the cited guideline / policy, gather evidence, draft a Resolution Center / Policy Center reply, post it, and decide next steps (clarify-and-resubmit, ship a fixed binary, or escalate via App Review Board / Play appeal). Single job parameterized by `platform` — composes Apple-specific or Play-specific guidance based on a Phase 1 decision. Reused on every rejection cycle. Triggered when a rejection is received for a version submitted through `mobile-app-submission`.
|
|
7
7
|
|
|
8
8
|
## Outcome
|
|
9
|
-
- **A
|
|
10
|
-
- **A
|
|
9
|
+
- **A Reviewed Reply**: A drafted Resolution Center (iOS) or Policy Center (Android) reply with its evidence and a next-step plan, reviewed by the manager before anything is sent.
|
|
10
|
+
- **A Posted Reply**: After the manager selects `approve_and_post_reply`, the reply is posted to the store thread.
|
|
11
|
+
- **A Clear Plan**: One of three paths recorded for after the platform responds: (a) Clarify-and-Resubmit, (b) Ship a Fixed Binary, (c) Escalate (App Review Board / Play appeal).
|
|
11
12
|
- **Audit Trail**: The full rejection thread, evidence collected, and decision rationale captured in `docs/<store>/rejections/<submission-id>.md`.
|
|
12
13
|
|
|
13
14
|
## Steps
|
|
@@ -17,9 +18,7 @@ Respond to an app store rejection — analyze the cited guideline / policy, gath
|
|
|
17
18
|
4. Phase 4: `response-drafting`
|
|
18
19
|
5. Phase 5: `submit`
|
|
19
20
|
6. Phase 6: `address-feedback`
|
|
20
|
-
7. Phase 7: `
|
|
21
|
-
8. Phase 8: `re-evaluate-resubmit-or-appeal`
|
|
22
|
-
9. Phase 9: `retrospective`
|
|
21
|
+
7. Phase 7: `retrospective`
|
|
23
22
|
|
|
24
23
|
---
|
|
25
24
|
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
Submit a mobile binary to the destination app store. Single job parameterized by `platform` — composes the App Store Connect (iOS) or Google Play Console (Android) flow based on a Phase 1 decision. Reused on every release. Covers the store-side end-to-end: metadata, screenshots, privacy disclosures, demo creds (where applicable), reviewer notes, and the actual Submit-for-Review click. Routes any rejection into `mobile-app-rejection-response`.
|
|
7
7
|
|
|
8
8
|
## Outcome
|
|
9
|
-
- **
|
|
9
|
+
- **Ready Version**: A store version with all required fields filled and one binary attached, reviewed by the manager before submission.
|
|
10
|
+
- **Submitted Version**: After the manager selects `approve_and_submit`, the version is in `Waiting for Review` / `In Review` / `In review` status.
|
|
10
11
|
- **Demo-Ready Account** (sign-in apps only): Synthetic reviewer account exists and is seeded with realistic data.
|
|
11
12
|
- **Submission Audit Trail**: A submission ID, build/version code, and complete log of fields filled in the project's evidence dir.
|
|
12
|
-
- **Monitoring Hook**:
|
|
13
|
+
- **Monitoring Hook**: The submission identity and expected review window are recorded; a rejection routes into `mobile-app-rejection-response` with full context preserved.
|
|
13
14
|
|
|
14
15
|
## Steps
|
|
15
16
|
1. Phase 1: `platform-selection`
|
|
@@ -20,9 +21,8 @@ Submit a mobile binary to the destination app store. Single job parameterized by
|
|
|
20
21
|
6. Phase 6: `store-specific-fields`
|
|
21
22
|
7. Phase 7: `demo-account-and-review-info`
|
|
22
23
|
8. Phase 8: `submit`
|
|
23
|
-
9. Phase 9: `
|
|
24
|
-
10. Phase 10: `
|
|
25
|
-
11. Phase 11: `retrospective`
|
|
24
|
+
9. Phase 9: `address-feedback`
|
|
25
|
+
10. Phase 10: `retrospective`
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
@@ -16,10 +16,9 @@ Apply for startup cloud credits through one selected provider path: `aws-activat
|
|
|
16
16
|
3. Phase 3: `navigate-to-provider`
|
|
17
17
|
4. Phase 4: `sign-in-to-provider`
|
|
18
18
|
5. Phase 5: `fill-application`
|
|
19
|
-
6. Phase 6: `
|
|
20
|
-
7. Phase 7: `
|
|
21
|
-
8. Phase 8: `
|
|
22
|
-
9. Phase 9: `retrospective`
|
|
19
|
+
6. Phase 6: `submit`
|
|
20
|
+
7. Phase 7: `address-feedback`
|
|
21
|
+
8. Phase 8: `retrospective`
|
|
23
22
|
|
|
24
23
|
---
|
|
25
24
|
|
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
Run any task that does not map to a structured FRAIM catalog job. Use as a fallback when no catalog job matches the user's request — not as a substitute for a structured job when one exists. Agents must not select this job speculatively; it is offered only when no catalog match is found.
|
|
13
13
|
|
|
14
14
|
## Outcome
|
|
15
|
-
The task described in the manager's instructions is complete, with a lightweight record of what happened and a recommendation on whether to formalize the task as a recurring FRAIM job.
|
|
15
|
+
The task described in the manager's instructions is complete and submitted for review, with a lightweight record of what happened and a recommendation on whether to formalize the task as a recurring FRAIM job.
|
|
16
16
|
|
|
17
17
|
## Steps
|
|
18
18
|
1. Phase 1: `run`
|
|
19
|
-
2. Phase 2: `
|
|
19
|
+
2. Phase 2: `submit`
|
|
20
|
+
3. Phase 3: `address-feedback`
|
|
21
|
+
4. Phase 4: `retrospective`
|
|
20
22
|
|
|
21
23
|
---
|
|
22
24
|
|
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
Allow users to contribute their custom Jobs, Skills, Rules, and Values back to the core FRAIM project. This job asks the user which specific capabilities they want to share, packages them, and files a contribution issue in the FRAIM repository.
|
|
7
7
|
|
|
8
8
|
## Outcome
|
|
9
|
-
-
|
|
9
|
+
- Reviewable contribution package at `docs/contributions/{name}-contribution-{YYYY-MM-DD}.md`.
|
|
10
|
+
- After the manager selects `approve_and_file`, a detailed contribution issue filed in the FRAIM repository with the packaged artifacts.
|
|
10
11
|
- **Improved Ecosystem**: The user's proven capabilities are shared with the broader FRAIM community.
|
|
11
12
|
|
|
12
13
|
## Steps
|
|
13
14
|
1. Phase 1: `gather-contribution-intent`
|
|
14
15
|
2. Phase 2: `discover-contribution-artifacts`
|
|
15
|
-
3. Phase 3: `
|
|
16
|
-
4. Phase 4: `
|
|
17
|
-
5. Phase 5: `
|
|
18
|
-
6. Phase 6: `retrospective`
|
|
16
|
+
3. Phase 3: `submit`
|
|
17
|
+
4. Phase 4: `address-feedback`
|
|
18
|
+
5. Phase 5: `retrospective`
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
File a user-readable bug report in the FRAIM repository. The agent elicits the user's own words about impact and symptoms before harvesting any technical context, then structures the issue so the user experience comes first and the technical detail comes last.
|
|
7
7
|
|
|
8
8
|
## Outcome
|
|
9
|
-
-
|
|
9
|
+
- Reviewable issue draft at `docs/fraim-issues/{YYYY-MM-DD}-{slug}.md`.
|
|
10
|
+
- After the manager selects `approve_and_file`, a new GitHub issue filed in the FRAIM repository.
|
|
10
11
|
- **User-Readable Report**: The issue opens with user impact and observable symptoms, followed by plain-language repro steps, with technical context appended last and omitted when nothing is found.
|
|
11
12
|
|
|
12
13
|
## Steps
|
|
13
14
|
1. Phase 1: `gather-issue-details`
|
|
14
|
-
2. Phase 2: `
|
|
15
|
-
3. Phase 3: `
|
|
16
|
-
4. Phase 4: `
|
|
17
|
-
5. Phase 5: `retrospective`
|
|
15
|
+
2. Phase 2: `submit`
|
|
16
|
+
3. Phase 3: `address-feedback`
|
|
17
|
+
4. Phase 4: `retrospective`
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -14,7 +14,9 @@ Resolve a FRAIM issue the user is experiencing — connectivity, sync, Hub start
|
|
|
14
14
|
1. Phase 1: `collect-symptom`
|
|
15
15
|
2. Phase 2: `diagnose`
|
|
16
16
|
3. Phase 3: `repair-or-escalate`
|
|
17
|
-
4. Phase 4: `
|
|
17
|
+
4. Phase 4: `submit`
|
|
18
|
+
5. Phase 5: `address-feedback`
|
|
19
|
+
6. Phase 6: `retrospective`
|
|
18
20
|
|
|
19
21
|
---
|
|
20
22
|
|
|
@@ -7,9 +7,9 @@ Onboard a project into a trustworthy FRAIM-ready state by reusing what FRAIM can
|
|
|
7
7
|
|
|
8
8
|
## Outcome
|
|
9
9
|
- `fraim/config.json` contains the approved project metadata and validation settings needed for future FRAIM work.
|
|
10
|
-
- `fraim/personalized-employee/context/project_context.md`
|
|
11
|
-
- `fraim/personalized-employee/rules/project_rules.md`
|
|
12
|
-
- The user understands what FRAIM confirmed automatically, what still needs input, what
|
|
10
|
+
- `fraim/personalized-employee/context/project_context.md` is a short brief: the project's Objective and its Success criteria, with links to existing project documents for anything more.
|
|
11
|
+
- `fraim/personalized-employee/rules/project_rules.md` holds adopted standing project expectations, with advice on reducing unnecessary startup context.
|
|
12
|
+
- The user understands what FRAIM confirmed automatically, what still needs input, what changed in existing context and rules, and which first jobs fit this project and why.
|
|
13
13
|
|
|
14
14
|
## Steps
|
|
15
15
|
1. Phase 0: `sync`
|
|
@@ -4,6 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.workspaceConfigCommand = void 0;
|
|
7
|
+
exports.checkProjectBrief = checkProjectBrief;
|
|
8
|
+
exports.checkProjectRules = checkProjectRules;
|
|
7
9
|
exports.runWorkspaceConfigValidate = runWorkspaceConfigValidate;
|
|
8
10
|
const commander_1 = require("commander");
|
|
9
11
|
const fs_1 = require("fs");
|
|
@@ -16,11 +18,201 @@ function resolveConfigPath(projectRoot, explicitPath) {
|
|
|
16
18
|
}
|
|
17
19
|
return (0, project_fraim_paths_1.getWorkspaceConfigPath)(projectRoot ? path_1.default.resolve(projectRoot) : process.cwd());
|
|
18
20
|
}
|
|
21
|
+
// Shape of the project brief written by project-onboarding from
|
|
22
|
+
// templates/manager/PROJECT-CONTEXT-TEMPLATE.md.
|
|
23
|
+
const BRIEF_REQUIRED_SECTIONS = ['Objective', 'Success criteria'];
|
|
24
|
+
const BRIEF_ALLOWED_SECTIONS = [...BRIEF_REQUIRED_SECTIONS, 'More detail'];
|
|
25
|
+
const BRIEF_MIN_SUCCESS_ITEMS = 2;
|
|
26
|
+
const BRIEF_MAX_SUCCESS_ITEMS = 4;
|
|
27
|
+
const BRIEF_MAX_WORDS = 150;
|
|
28
|
+
// Suggested shape of project_rules.md; adopted rules may exceed it.
|
|
29
|
+
const RULES_REVIEW_GUIDANCE = 7;
|
|
30
|
+
const RULES_EMPTY_FORMS = ['No project rules adopted yet.', 'No project-specific rules yet.'];
|
|
31
|
+
const LIST_ITEM = /^([-*+]|\d+[.)])\s+/;
|
|
32
|
+
const MARKDOWN_LINK = /\[[^\]]+\]\([^)\s]+(\s+"[^"]*")?\)/;
|
|
33
|
+
function markdownLines(content) {
|
|
34
|
+
let inFence = false;
|
|
35
|
+
return content.split(/\r?\n/).map((text) => {
|
|
36
|
+
const fenceMarker = /^\s*(```|~~~)/.test(text);
|
|
37
|
+
const line = { text, inFence: inFence || fenceMarker };
|
|
38
|
+
if (fenceMarker)
|
|
39
|
+
inFence = !inFence;
|
|
40
|
+
return line;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function headingOf(line) {
|
|
44
|
+
if (line.inFence)
|
|
45
|
+
return null;
|
|
46
|
+
const match = /^(#{1,6})\s+(.*?)\s*#*\s*$/.exec(line.text);
|
|
47
|
+
return match ? { level: match[1].length, title: match[2].trim() } : null;
|
|
48
|
+
}
|
|
49
|
+
function countWords(lines) {
|
|
50
|
+
return lines
|
|
51
|
+
.map((line) => line.trim().replace(LIST_ITEM, ''))
|
|
52
|
+
.join(' ')
|
|
53
|
+
.split(/\s+/)
|
|
54
|
+
.filter(Boolean).length;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check project_context.md against the two-section brief. Returns one message per
|
|
58
|
+
* violation; an empty array means the brief has the template shape.
|
|
59
|
+
*/
|
|
60
|
+
function checkProjectBrief(content) {
|
|
61
|
+
const violations = [];
|
|
62
|
+
const sections = new Map();
|
|
63
|
+
const order = [];
|
|
64
|
+
let current = null;
|
|
65
|
+
for (const line of markdownLines(content)) {
|
|
66
|
+
const heading = headingOf(line);
|
|
67
|
+
if (heading && heading.level >= 3) {
|
|
68
|
+
violations.push(`H3 or deeper heading "${heading.title}" is not allowed; use only the template's H2 sections`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (heading && heading.level === 2) {
|
|
72
|
+
if (!BRIEF_ALLOWED_SECTIONS.includes(heading.title)) {
|
|
73
|
+
violations.push(`heading "${heading.title}" is not allowed; allowed H2 headings are ${BRIEF_ALLOWED_SECTIONS.join(', ')}`);
|
|
74
|
+
current = null;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
current = [];
|
|
78
|
+
if (!sections.has(heading.title))
|
|
79
|
+
sections.set(heading.title, []);
|
|
80
|
+
sections.get(heading.title).push(current);
|
|
81
|
+
order.push(heading.title);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (heading && heading.level === 1) {
|
|
85
|
+
current = null;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (current)
|
|
89
|
+
current.push(line.text);
|
|
90
|
+
}
|
|
91
|
+
for (const name of BRIEF_ALLOWED_SECTIONS) {
|
|
92
|
+
const occurrences = sections.get(name)?.length ?? 0;
|
|
93
|
+
if (occurrences === 0 && BRIEF_REQUIRED_SECTIONS.includes(name)) {
|
|
94
|
+
violations.push(`section "${name}" is missing`);
|
|
95
|
+
}
|
|
96
|
+
if (occurrences > 1)
|
|
97
|
+
violations.push(`section "${name}" appears more than once`);
|
|
98
|
+
}
|
|
99
|
+
const objectiveAt = order.indexOf('Objective');
|
|
100
|
+
const successAt = order.indexOf('Success criteria');
|
|
101
|
+
if (objectiveAt >= 0 && successAt >= 0 && successAt < objectiveAt) {
|
|
102
|
+
violations.push('section "Objective" must come before "Success criteria"');
|
|
103
|
+
}
|
|
104
|
+
const successLines = (sections.get('Success criteria') ?? []).flat();
|
|
105
|
+
const successItems = successLines.filter((line) => LIST_ITEM.test(line)).length;
|
|
106
|
+
if (sections.has('Success criteria') && (successItems < BRIEF_MIN_SUCCESS_ITEMS || successItems > BRIEF_MAX_SUCCESS_ITEMS)) {
|
|
107
|
+
violations.push(`section "Success criteria" has ${successItems} list item${successItems === 1 ? '' : 's'}; ` +
|
|
108
|
+
`it needs ${BRIEF_MIN_SUCCESS_ITEMS} to ${BRIEF_MAX_SUCCESS_ITEMS}`);
|
|
109
|
+
}
|
|
110
|
+
const words = countWords([...(sections.get('Objective') ?? []).flat(), ...successLines]);
|
|
111
|
+
if (words > BRIEF_MAX_WORDS) {
|
|
112
|
+
violations.push(`"Objective" and "Success criteria" together have ${words} words; the limit is ${BRIEF_MAX_WORDS}`);
|
|
113
|
+
}
|
|
114
|
+
const detailLines = (sections.get('More detail') ?? []).flat().filter((line) => line.trim());
|
|
115
|
+
detailLines.forEach((line, index) => {
|
|
116
|
+
if (!LIST_ITEM.test(line.trim()) || !MARKDOWN_LINK.test(line)) {
|
|
117
|
+
violations.push(`section "More detail" line ${index + 1} is not a list item with a Markdown link`);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
return violations;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Return advisory deviations from the concise project-rules template.
|
|
124
|
+
* These findings never reject adopted rules or authorize rewriting them.
|
|
125
|
+
*/
|
|
126
|
+
function checkProjectRules(content) {
|
|
127
|
+
const warnings = [];
|
|
128
|
+
let ruleCount = 0;
|
|
129
|
+
let awaitingWhy = false;
|
|
130
|
+
let spansReported = false;
|
|
131
|
+
let emptyForm = false;
|
|
132
|
+
const closeRule = () => {
|
|
133
|
+
if (ruleCount > 0 && awaitingWhy)
|
|
134
|
+
warnings.push(`rule ${ruleCount} has no "Why:" line`);
|
|
135
|
+
};
|
|
136
|
+
for (const line of markdownLines(content)) {
|
|
137
|
+
const trimmed = line.text.trim();
|
|
138
|
+
if (!trimmed)
|
|
139
|
+
continue;
|
|
140
|
+
const heading = headingOf(line);
|
|
141
|
+
if (heading && heading.level >= 2) {
|
|
142
|
+
warnings.push(`heading "${heading.title}" differs from the suggested flat numbered list`);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (heading)
|
|
146
|
+
continue;
|
|
147
|
+
if (/^\d+[.)]\s+\S/.test(trimmed) && !line.inFence) {
|
|
148
|
+
closeRule();
|
|
149
|
+
ruleCount += 1;
|
|
150
|
+
awaitingWhy = true;
|
|
151
|
+
spansReported = false;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (RULES_EMPTY_FORMS.includes(trimmed)) {
|
|
155
|
+
emptyForm = true;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (ruleCount === 0)
|
|
159
|
+
continue;
|
|
160
|
+
if (awaitingWhy && /^Why:/.test(trimmed)) {
|
|
161
|
+
awaitingWhy = false;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (!spansReported) {
|
|
165
|
+
warnings.push(`rule ${ruleCount} spans more than one line; consider shortening it if the extra detail is unnecessary`);
|
|
166
|
+
spansReported = true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
closeRule();
|
|
170
|
+
if (ruleCount > RULES_REVIEW_GUIDANCE)
|
|
171
|
+
warnings.push(`${ruleCount} numbered rules; above the review guidance of ${RULES_REVIEW_GUIDANCE}, but more are allowed`);
|
|
172
|
+
if (ruleCount === 0 && !emptyForm) {
|
|
173
|
+
warnings.push(`neither numbered rules nor the empty-form sentence "${RULES_EMPTY_FORMS[0]}"`);
|
|
174
|
+
}
|
|
175
|
+
return warnings;
|
|
176
|
+
}
|
|
177
|
+
function checkProjectArtifacts(projectRoot, includeBrief) {
|
|
178
|
+
const artifacts = [
|
|
179
|
+
...(includeBrief ? [{ file: 'project_context.md', directory: 'context', check: checkProjectBrief, advisory: false }] : []),
|
|
180
|
+
{ file: 'project_rules.md', directory: 'rules', check: checkProjectRules, advisory: true }
|
|
181
|
+
];
|
|
182
|
+
const violations = [];
|
|
183
|
+
const warnings = [];
|
|
184
|
+
for (const artifact of artifacts) {
|
|
185
|
+
const filePath = (0, project_fraim_paths_1.getWorkspaceFraimPath)(projectRoot, 'personalized-employee', artifact.directory, artifact.file);
|
|
186
|
+
if (!(0, fs_1.existsSync)(filePath)) {
|
|
187
|
+
violations.push(artifact.file + ': file is missing');
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const findings = artifact.check((0, fs_1.readFileSync)(filePath, 'utf8')).map((finding) => artifact.file + ': ' + finding);
|
|
191
|
+
(artifact.advisory ? warnings : violations).push(...findings);
|
|
192
|
+
}
|
|
193
|
+
return { violations, warnings };
|
|
194
|
+
}
|
|
19
195
|
function runWorkspaceConfigValidate(options) {
|
|
20
196
|
const configPath = resolveConfigPath(options.projectRoot, options.configPath);
|
|
21
197
|
const rawConfig = JSON.parse((0, fs_1.readFileSync)(configPath, 'utf8'));
|
|
22
198
|
(0, fraim_config_contract_1.assertValidWorkspaceFraimConfig)(rawConfig);
|
|
23
199
|
console.log(`Validated ${path_1.default.relative(process.cwd(), configPath) || (0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('config.json')}`);
|
|
200
|
+
if (!options.projectArtifacts && !options.projectRules)
|
|
201
|
+
return;
|
|
202
|
+
const projectRoot = options.projectRoot ? path_1.default.resolve(options.projectRoot) : process.cwd();
|
|
203
|
+
const { violations, warnings } = checkProjectArtifacts(projectRoot, Boolean(options.projectArtifacts));
|
|
204
|
+
for (const warning of warnings)
|
|
205
|
+
console.warn(`Warning: ${warning}`);
|
|
206
|
+
if (warnings.length > 0) {
|
|
207
|
+
console.warn('Warning: Extra rule text is read at startup and can slow starting. Review unnecessary detail or duplication; you may keep these rules.');
|
|
208
|
+
}
|
|
209
|
+
if (violations.length > 0) {
|
|
210
|
+
for (const violation of violations)
|
|
211
|
+
console.error(violation);
|
|
212
|
+
process.exitCode = 1;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
console.log(options.projectArtifacts ? 'Validated project_context.md and checked project_rules.md' : 'Checked project_rules.md');
|
|
24
216
|
}
|
|
25
217
|
exports.workspaceConfigCommand = new commander_1.Command('workspace-config')
|
|
26
218
|
.description('Deterministically validate workspace fraim/config.json')
|
|
@@ -28,4 +220,6 @@ exports.workspaceConfigCommand = new commander_1.Command('workspace-config')
|
|
|
28
220
|
.description('Validate fraim/config.json against the shared workspace config contract')
|
|
29
221
|
.option('--project-root <path>', 'Project root used to resolve fraim/config.json')
|
|
30
222
|
.option('--config-path <path>', 'Explicit path to fraim/config.json')
|
|
223
|
+
.option('--project-artifacts', 'Also validate the brief and report advisory project-rule checks')
|
|
224
|
+
.option('--project-rules', 'Check project rules only, with advisory count and format warnings')
|
|
31
225
|
.action((options) => runWorkspaceConfigValidate(options)));
|
|
@@ -77,7 +77,7 @@ This repository uses FRAIM.
|
|
|
77
77
|
- If no exact or high-confidence job match exists, say that no FRAIM job matches and continue with normal tools or ask one concise clarification.
|
|
78
78
|
- For deeper capability detail, call \`get_fraim_file({ path: "skills/<category>/<skill-name>.md" })\` or \`get_fraim_file({ path: "rules/<category>/<rule-name>.md" })\`.
|
|
79
79
|
- Read \`${projectRulesPath}\` if it exists before doing work.
|
|
80
|
-
- When users ask for next step recommendations, use recommend-
|
|
80
|
+
- When users ask for next step recommendations, use recommend-project-priorities skill under \`${employeeSkillsPath}/\` to gather context before suggesting jobs.
|
|
81
81
|
- ${ide_invocation_surfaces_1.FRAIM_MCP_UNAVAILABLE_MANAGER_GUIDANCE}
|
|
82
82
|
|
|
83
83
|
> [!IMPORTANT]
|