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 = fs_1.default.existsSync(path_1.default.join(__dirname, '..', '..', '..', 'registry'))
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 = path_1.default.join(__dirname, '..', '..', '..', 'labels.json');
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
- ...tokens
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.102",
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": {