fraim-framework 2.0.102 → 2.0.103
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,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.initProjectCommand = exports.runInitProject = void 0;
|
|
6
|
+
exports.initProjectCommand = exports.runInitProject = exports.findProjectFile = void 0;
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
@@ -70,12 +70,41 @@ const checkGlobalSetup = () => {
|
|
|
70
70
|
return { exists: true, mode: 'integrated', tokens: {} };
|
|
71
71
|
}
|
|
72
72
|
};
|
|
73
|
+
// Robust path resolution utility - walks up directory tree to find target
|
|
74
|
+
const findProjectFile = (filename) => {
|
|
75
|
+
let currentDir = __dirname;
|
|
76
|
+
// Walk up the directory tree to find the target file/directory
|
|
77
|
+
for (let i = 0; i < 10; i++) { // Limit to prevent infinite loop
|
|
78
|
+
const targetPath = path_1.default.join(currentDir, filename);
|
|
79
|
+
if (fs_1.default.existsSync(targetPath)) {
|
|
80
|
+
return targetPath;
|
|
81
|
+
}
|
|
82
|
+
const parentDir = path_1.default.dirname(currentDir);
|
|
83
|
+
if (parentDir === currentDir)
|
|
84
|
+
break; // Reached root
|
|
85
|
+
currentDir = parentDir;
|
|
86
|
+
}
|
|
87
|
+
// Fallback: try from process.cwd()
|
|
88
|
+
const cwdTarget = path_1.default.join(process.cwd(), filename);
|
|
89
|
+
if (fs_1.default.existsSync(cwdTarget)) {
|
|
90
|
+
return cwdTarget;
|
|
91
|
+
}
|
|
92
|
+
// Last resort: use relative path from __dirname
|
|
93
|
+
return path_1.default.join(__dirname, '..', '..', '..', filename);
|
|
94
|
+
};
|
|
95
|
+
exports.findProjectFile = findProjectFile;
|
|
96
|
+
;
|
|
73
97
|
const installGitHubWorkflows = (projectRoot) => {
|
|
74
98
|
const workflowsDir = path_1.default.join(projectRoot, '.github', 'workflows');
|
|
75
|
-
const registryDir =
|
|
76
|
-
? path_1.default.join(__dirname, '..', '..', '..', 'registry')
|
|
77
|
-
: path_1.default.join(__dirname, '..', '..', 'registry');
|
|
99
|
+
const registryDir = (0, exports.findProjectFile)('registry');
|
|
78
100
|
const sourceDir = path_1.default.join(registryDir, 'github', 'workflows');
|
|
101
|
+
if (!fs_1.default.existsSync(sourceDir)) {
|
|
102
|
+
console.log(chalk_1.default.yellow(`Warning: GitHub workflows source directory not found: ${sourceDir}`));
|
|
103
|
+
console.log(chalk_1.default.gray(`Registry directory: ${registryDir}`));
|
|
104
|
+
console.log(chalk_1.default.gray(`Current __dirname: ${__dirname}`));
|
|
105
|
+
console.log(chalk_1.default.gray(`Process cwd: ${process.cwd()}`));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
79
108
|
if (!fs_1.default.existsSync(workflowsDir)) {
|
|
80
109
|
fs_1.default.mkdirSync(workflowsDir, { recursive: true });
|
|
81
110
|
}
|
|
@@ -101,9 +130,10 @@ const createGitHubLabels = (projectRoot) => {
|
|
|
101
130
|
console.log(chalk_1.default.gray('Install gh CLI to enable automatic label creation: https://cli.github.com/'));
|
|
102
131
|
return;
|
|
103
132
|
}
|
|
104
|
-
const labelsPath =
|
|
133
|
+
const labelsPath = (0, exports.findProjectFile)('labels.json');
|
|
105
134
|
if (!fs_1.default.existsSync(labelsPath)) {
|
|
106
135
|
console.log(chalk_1.default.yellow('labels.json not found. Skipping label creation.'));
|
|
136
|
+
console.log(chalk_1.default.gray(`Searched from: ${__dirname}`));
|
|
107
137
|
return;
|
|
108
138
|
}
|
|
109
139
|
try {
|
|
@@ -146,6 +146,33 @@ const promptForMode = async () => {
|
|
|
146
146
|
}
|
|
147
147
|
return response.mode;
|
|
148
148
|
};
|
|
149
|
+
/**
|
|
150
|
+
* Sanitize a token by removing control characters that cause JSON serialization issues
|
|
151
|
+
*/
|
|
152
|
+
const sanitizeToken = (token) => {
|
|
153
|
+
if (!token)
|
|
154
|
+
return token;
|
|
155
|
+
// Remove control characters (0x00-0x1F and 0x7F) that cause JSON escaping issues
|
|
156
|
+
// These characters are not valid in API tokens and are likely copy-paste artifacts
|
|
157
|
+
return token.replace(/[\x00-\x1F\x7F]/g, '');
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Sanitize all tokens in a ProviderTokens object
|
|
161
|
+
*/
|
|
162
|
+
const sanitizeTokens = (tokens) => {
|
|
163
|
+
const sanitized = {};
|
|
164
|
+
Object.entries(tokens).forEach(([providerId, token]) => {
|
|
165
|
+
if (token) {
|
|
166
|
+
const originalToken = token;
|
|
167
|
+
const sanitizedToken = sanitizeToken(token);
|
|
168
|
+
if (originalToken !== sanitizedToken) {
|
|
169
|
+
console.log(chalk_1.default.yellow(`⚠️ Sanitized ${providerId} token: removed ${originalToken.length - sanitizedToken.length} control character(s)`));
|
|
170
|
+
}
|
|
171
|
+
sanitized[providerId] = sanitizedToken;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
return sanitized;
|
|
175
|
+
};
|
|
149
176
|
const saveGlobalConfig = (fraimKey, mode, tokens, configs) => {
|
|
150
177
|
const globalConfigDir = (0, script_sync_utils_1.getUserFraimDir)();
|
|
151
178
|
const globalConfigPath = path_1.default.join(globalConfigDir, 'config.json');
|
|
@@ -162,6 +189,8 @@ const saveGlobalConfig = (fraimKey, mode, tokens, configs) => {
|
|
|
162
189
|
// Ignore parse errors, will create new config
|
|
163
190
|
}
|
|
164
191
|
}
|
|
192
|
+
// Sanitize tokens before saving to prevent JSON serialization issues
|
|
193
|
+
const sanitizedTokens = sanitizeTokens(tokens);
|
|
165
194
|
// Merge provider configs (e.g., jiraConfig)
|
|
166
195
|
const providerConfigs = { ...(existingConfig.providerConfigs || {}) };
|
|
167
196
|
Object.entries(configs).forEach(([providerId, config]) => {
|
|
@@ -176,7 +205,7 @@ const saveGlobalConfig = (fraimKey, mode, tokens, configs) => {
|
|
|
176
205
|
mode: mode,
|
|
177
206
|
tokens: {
|
|
178
207
|
...(existingConfig.tokens || {}),
|
|
179
|
-
...
|
|
208
|
+
...sanitizedTokens
|
|
180
209
|
},
|
|
181
210
|
providerConfigs,
|
|
182
211
|
configuredAt: new Date().toISOString(),
|
|
@@ -64,6 +64,7 @@ This repository uses FRAIM.
|
|
|
64
64
|
- Once you identify the relevant job, call \`get_fraim_job({ job: "<job-name>" })\` to get the full phased instructions.
|
|
65
65
|
- For deeper capability detail, call \`get_fraim_file({ path: "skills/<category>/<skill-name>.md" })\` or \`get_fraim_file({ path: "rules/<category>/<rule-name>.md" })\`.
|
|
66
66
|
- Read \`${projectRulesPath}\` if it exists before doing work.
|
|
67
|
+
- When users ask for next step recommendations, use recommend-next-job skill under \`${employeeSkillsPath}/\` to gather context before suggesting jobs.
|
|
67
68
|
|
|
68
69
|
> [!IMPORTANT]
|
|
69
70
|
> **Job stubs are for discovery only.** When a user @mentions or references any file under \`${employeeJobsPath}/\` or \`${managerJobsPath}/\`, do NOT attempt to execute the job from the stub content. The stub only shows intent and phase names. Always call \`get_fraim_job({ job: "<job-name>" })\` first to get the full phased instructions before doing any work.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fraim-framework",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.103",
|
|
4
4
|
"description": "FRAIM v2: Framework for Rigor-based AI Management - Transform from solo developer to AI manager orchestrating production-ready code with enterprise-grade discipline",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|