@ai-content-space/loopx 0.2.8 → 0.2.10

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 (105) hide show
  1. package/README.md +26 -9
  2. package/README.zh-CN.md +26 -9
  3. package/docs/loopx/design/loopx-skill-suite-v1-design.md +12 -0
  4. package/docs/loopx/plans/2026-06-14-loopx-spec-memory-context-loading.md +948 -0
  5. package/docs/loopx/plans/2026-06-15-support-lens-skills-migration.md +1153 -0
  6. package/package.json +6 -1
  7. package/plugins/loopx/.codex-plugin/plugin.json +1 -1
  8. package/plugins/loopx/skills/api-designer/SKILL.md +232 -0
  9. package/plugins/loopx/skills/api-designer/references/error-handling.md +541 -0
  10. package/plugins/loopx/skills/api-designer/references/openapi.md +824 -0
  11. package/plugins/loopx/skills/api-designer/references/pagination.md +494 -0
  12. package/plugins/loopx/skills/api-designer/references/rest-patterns.md +335 -0
  13. package/plugins/loopx/skills/api-designer/references/versioning.md +391 -0
  14. package/plugins/loopx/skills/architecture-designer/SKILL.md +117 -0
  15. package/plugins/loopx/skills/architecture-designer/references/adr-template.md +116 -0
  16. package/plugins/loopx/skills/architecture-designer/references/architecture-patterns.md +346 -0
  17. package/plugins/loopx/skills/architecture-designer/references/database-selection.md +102 -0
  18. package/plugins/loopx/skills/architecture-designer/references/nfr-checklist.md +212 -0
  19. package/plugins/loopx/skills/architecture-designer/references/system-design.md +313 -0
  20. package/plugins/loopx/skills/clarify/SKILL.md +12 -1
  21. package/plugins/loopx/skills/cli-developer/SKILL.md +124 -0
  22. package/plugins/loopx/skills/cli-developer/references/design-patterns.md +221 -0
  23. package/plugins/loopx/skills/cli-developer/references/go-cli.md +540 -0
  24. package/plugins/loopx/skills/cli-developer/references/node-cli.md +383 -0
  25. package/plugins/loopx/skills/cli-developer/references/python-cli.md +422 -0
  26. package/plugins/loopx/skills/cli-developer/references/ux-patterns.md +448 -0
  27. package/plugins/loopx/skills/debug/SKILL.md +1 -1
  28. package/plugins/loopx/skills/doc-readability/SKILL.md +1 -1
  29. package/plugins/loopx/skills/exec/SKILL.md +1 -1
  30. package/plugins/loopx/skills/final-review/SKILL.md +1 -1
  31. package/plugins/loopx/skills/finish/SKILL.md +1 -1
  32. package/plugins/loopx/skills/fix-review/SKILL.md +1 -1
  33. package/plugins/loopx/skills/go-style/SKILL.md +1 -1
  34. package/plugins/loopx/skills/kratos/SKILL.md +2 -1
  35. package/plugins/loopx/skills/plan-to-exec/SKILL.md +12 -1
  36. package/plugins/loopx/skills/refactor-plan/SKILL.md +1 -1
  37. package/plugins/loopx/skills/requirement-analyzer/SKILL.md +161 -0
  38. package/plugins/loopx/skills/requirement-analyzer/references/example-reports.md +170 -0
  39. package/plugins/loopx/skills/requirement-analyzer/references/prd-gap-checklist.md +167 -0
  40. package/plugins/loopx/skills/requirement-analyzer/references/readiness-rubric.md +70 -0
  41. package/plugins/loopx/skills/requirement-analyzer/references/report-template.md +83 -0
  42. package/plugins/loopx/skills/review/SKILL.md +1 -1
  43. package/plugins/loopx/skills/spec/SKILL.md +12 -1
  44. package/plugins/loopx/skills/sql-style/SKILL.md +108 -0
  45. package/plugins/loopx/skills/sql-style/references/database-design.md +402 -0
  46. package/plugins/loopx/skills/sql-style/references/dialect-differences.md +419 -0
  47. package/plugins/loopx/skills/sql-style/references/optimization.md +384 -0
  48. package/plugins/loopx/skills/sql-style/references/query-patterns.md +285 -0
  49. package/plugins/loopx/skills/sql-style/references/window-functions.md +328 -0
  50. package/plugins/loopx/skills/subagent-exec/SKILL.md +1 -1
  51. package/plugins/loopx/skills/tdd/SKILL.md +1 -1
  52. package/plugins/loopx/skills/verify/SKILL.md +1 -1
  53. package/scripts/verify-skills.mjs +0 -2
  54. package/skills/RESOLVER.md +8 -1
  55. package/skills/api-designer/SKILL.md +232 -0
  56. package/skills/api-designer/references/error-handling.md +541 -0
  57. package/skills/api-designer/references/openapi.md +824 -0
  58. package/skills/api-designer/references/pagination.md +494 -0
  59. package/skills/api-designer/references/rest-patterns.md +335 -0
  60. package/skills/api-designer/references/versioning.md +391 -0
  61. package/skills/architecture-designer/SKILL.md +117 -0
  62. package/skills/architecture-designer/references/adr-template.md +116 -0
  63. package/skills/architecture-designer/references/architecture-patterns.md +346 -0
  64. package/skills/architecture-designer/references/database-selection.md +102 -0
  65. package/skills/architecture-designer/references/nfr-checklist.md +212 -0
  66. package/skills/architecture-designer/references/system-design.md +313 -0
  67. package/skills/clarify/SKILL.md +12 -1
  68. package/skills/cli-developer/SKILL.md +124 -0
  69. package/skills/cli-developer/references/design-patterns.md +221 -0
  70. package/skills/cli-developer/references/go-cli.md +540 -0
  71. package/skills/cli-developer/references/node-cli.md +383 -0
  72. package/skills/cli-developer/references/python-cli.md +422 -0
  73. package/skills/cli-developer/references/ux-patterns.md +448 -0
  74. package/skills/debug/SKILL.md +1 -1
  75. package/skills/doc-readability/SKILL.md +1 -1
  76. package/skills/exec/SKILL.md +1 -1
  77. package/skills/final-review/SKILL.md +1 -1
  78. package/skills/finish/SKILL.md +1 -1
  79. package/skills/fix-review/SKILL.md +1 -1
  80. package/skills/go-style/SKILL.md +1 -1
  81. package/skills/kratos/SKILL.md +2 -1
  82. package/skills/plan-to-exec/SKILL.md +12 -1
  83. package/skills/refactor-plan/SKILL.md +1 -1
  84. package/skills/requirement-analyzer/SKILL.md +161 -0
  85. package/skills/requirement-analyzer/references/example-reports.md +170 -0
  86. package/skills/requirement-analyzer/references/prd-gap-checklist.md +167 -0
  87. package/skills/requirement-analyzer/references/readiness-rubric.md +70 -0
  88. package/skills/requirement-analyzer/references/report-template.md +83 -0
  89. package/skills/review/SKILL.md +1 -1
  90. package/skills/spec/SKILL.md +12 -1
  91. package/skills/sql-style/SKILL.md +108 -0
  92. package/skills/sql-style/references/database-design.md +402 -0
  93. package/skills/sql-style/references/dialect-differences.md +419 -0
  94. package/skills/sql-style/references/optimization.md +384 -0
  95. package/skills/sql-style/references/query-patterns.md +285 -0
  96. package/skills/sql-style/references/window-functions.md +328 -0
  97. package/skills/subagent-exec/SKILL.md +1 -1
  98. package/skills/tdd/SKILL.md +1 -1
  99. package/skills/verify/SKILL.md +1 -1
  100. package/src/cli.mjs +4 -1
  101. package/src/context-manifest.mjs +51 -1
  102. package/src/install-discovery.mjs +114 -0
  103. package/src/loopx-context-artifacts.mjs +114 -0
  104. package/src/project-discovery.mjs +1 -0
  105. package/src/workflow.mjs +47 -3
@@ -0,0 +1,328 @@
1
+ # Window Functions
2
+
3
+ ## Ranking Functions
4
+
5
+ ```sql
6
+ -- ROW_NUMBER: Sequential numbering within partition
7
+ SELECT
8
+ customer_id,
9
+ order_date,
10
+ total,
11
+ ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) as row_num
12
+ FROM orders;
13
+
14
+ -- Get most recent order per customer
15
+ SELECT *
16
+ FROM (
17
+ SELECT
18
+ customer_id,
19
+ order_id,
20
+ order_date,
21
+ total,
22
+ ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) as rn
23
+ FROM orders
24
+ ) ranked
25
+ WHERE rn = 1;
26
+
27
+ -- RANK: Same values get same rank, gaps in sequence
28
+ SELECT
29
+ student_id,
30
+ score,
31
+ RANK() OVER (ORDER BY score DESC) as rank,
32
+ DENSE_RANK() OVER (ORDER BY score DESC) as dense_rank,
33
+ ROW_NUMBER() OVER (ORDER BY score DESC) as row_num
34
+ FROM exam_results;
35
+ /*
36
+ score=100: rank=1, dense_rank=1, row_num=1
37
+ score=100: rank=1, dense_rank=1, row_num=2
38
+ score=95: rank=3, dense_rank=2, row_num=3
39
+ */
40
+
41
+ -- NTILE: Divide into N buckets
42
+ SELECT
43
+ customer_id,
44
+ total_spent,
45
+ NTILE(4) OVER (ORDER BY total_spent DESC) as quartile
46
+ FROM customer_lifetime_value;
47
+ ```
48
+
49
+ ## Aggregate Window Functions
50
+
51
+ ```sql
52
+ -- Running totals and cumulative sums
53
+ SELECT
54
+ order_date,
55
+ daily_revenue,
56
+ SUM(daily_revenue) OVER (ORDER BY order_date) as cumulative_revenue,
57
+ AVG(daily_revenue) OVER (
58
+ ORDER BY order_date
59
+ ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
60
+ ) as rolling_7day_avg
61
+ FROM daily_sales;
62
+
63
+ -- Moving average with RANGE
64
+ SELECT
65
+ sale_date,
66
+ amount,
67
+ AVG(amount) OVER (
68
+ ORDER BY sale_date
69
+ RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW
70
+ ) as avg_last_7_days
71
+ FROM sales;
72
+
73
+ -- Partition-specific aggregates
74
+ SELECT
75
+ product_id,
76
+ sale_date,
77
+ quantity,
78
+ SUM(quantity) OVER (PARTITION BY product_id ORDER BY sale_date) as cumulative_qty,
79
+ AVG(quantity) OVER (PARTITION BY product_id) as avg_qty_for_product,
80
+ quantity::FLOAT / SUM(quantity) OVER (PARTITION BY product_id) as pct_of_total
81
+ FROM product_sales;
82
+ ```
83
+
84
+ ## LAG and LEAD Functions
85
+
86
+ ```sql
87
+ -- Compare with previous/next row
88
+ SELECT
89
+ order_date,
90
+ total,
91
+ LAG(total) OVER (ORDER BY order_date) as previous_day_total,
92
+ LEAD(total) OVER (ORDER BY order_date) as next_day_total,
93
+ total - LAG(total) OVER (ORDER BY order_date) as day_over_day_change
94
+ FROM daily_orders;
95
+
96
+ -- Find gaps in time series
97
+ SELECT
98
+ event_date,
99
+ LAG(event_date) OVER (ORDER BY event_date) as prev_date,
100
+ event_date - LAG(event_date) OVER (ORDER BY event_date) as days_since_last
101
+ FROM events
102
+ WHERE event_date - LAG(event_date) OVER (ORDER BY event_date) > 7;
103
+
104
+ -- Session analysis with time gaps
105
+ SELECT
106
+ user_id,
107
+ action_time,
108
+ LAG(action_time) OVER (PARTITION BY user_id ORDER BY action_time) as prev_action,
109
+ EXTRACT(EPOCH FROM (
110
+ action_time - LAG(action_time) OVER (PARTITION BY user_id ORDER BY action_time)
111
+ )) / 60 as minutes_since_last_action,
112
+ CASE
113
+ WHEN EXTRACT(EPOCH FROM (
114
+ action_time - LAG(action_time) OVER (PARTITION BY user_id ORDER BY action_time)
115
+ )) / 60 > 30 THEN 1
116
+ ELSE 0
117
+ END as new_session
118
+ FROM user_actions;
119
+ ```
120
+
121
+ ## FIRST_VALUE and LAST_VALUE
122
+
123
+ ```sql
124
+ -- Compare each row to first/last in partition
125
+ SELECT
126
+ product_id,
127
+ price_date,
128
+ price,
129
+ FIRST_VALUE(price) OVER (
130
+ PARTITION BY product_id
131
+ ORDER BY price_date
132
+ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
133
+ ) as initial_price,
134
+ LAST_VALUE(price) OVER (
135
+ PARTITION BY product_id
136
+ ORDER BY price_date
137
+ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
138
+ ) as current_price,
139
+ price - FIRST_VALUE(price) OVER (
140
+ PARTITION BY product_id
141
+ ORDER BY price_date
142
+ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
143
+ ) as price_change_from_start
144
+ FROM product_price_history;
145
+
146
+ -- NTH_VALUE: Get specific positioned value
147
+ SELECT
148
+ sale_date,
149
+ amount,
150
+ NTH_VALUE(amount, 2) OVER (
151
+ ORDER BY sale_date
152
+ ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
153
+ ) as second_day_amount
154
+ FROM daily_sales;
155
+ ```
156
+
157
+ ## Frame Specifications
158
+
159
+ ```sql
160
+ -- ROWS vs RANGE difference
161
+ SELECT
162
+ order_date,
163
+ amount,
164
+ -- ROWS: Physical row offset
165
+ SUM(amount) OVER (
166
+ ORDER BY order_date
167
+ ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING
168
+ ) as sum_5_rows,
169
+ -- RANGE: Logical value range
170
+ SUM(amount) OVER (
171
+ ORDER BY order_date
172
+ RANGE BETWEEN INTERVAL '2 days' PRECEDING AND INTERVAL '2 days' FOLLOWING
173
+ ) as sum_5_day_range
174
+ FROM orders;
175
+
176
+ -- Common frame patterns
177
+ SELECT
178
+ sale_date,
179
+ revenue,
180
+ -- All preceding rows
181
+ SUM(revenue) OVER (
182
+ ORDER BY sale_date
183
+ ROWS UNBOUNDED PRECEDING
184
+ ) as running_total,
185
+ -- Last 3 rows including current
186
+ AVG(revenue) OVER (
187
+ ORDER BY sale_date
188
+ ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
189
+ ) as ma_3,
190
+ -- Entire partition
191
+ SUM(revenue) OVER (
192
+ PARTITION BY EXTRACT(YEAR FROM sale_date)
193
+ ) as yearly_total,
194
+ -- Centered window
195
+ AVG(revenue) OVER (
196
+ ORDER BY sale_date
197
+ ROWS BETWEEN 3 PRECEDING AND 3 FOLLOWING
198
+ ) as centered_ma_7
199
+ FROM sales;
200
+ ```
201
+
202
+ ## Advanced Analytics
203
+
204
+ ```sql
205
+ -- Percentile calculations
206
+ SELECT
207
+ employee_id,
208
+ salary,
209
+ PERCENT_RANK() OVER (ORDER BY salary) as pct_rank,
210
+ CUME_DIST() OVER (ORDER BY salary) as cumulative_dist,
211
+ PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) OVER () as median_salary,
212
+ PERCENTILE_DISC(0.9) WITHIN GROUP (ORDER BY salary) OVER () as p90_salary
213
+ FROM employees;
214
+
215
+ -- Cohort retention analysis
216
+ WITH user_cohorts AS (
217
+ SELECT
218
+ user_id,
219
+ DATE_TRUNC('month', signup_date) as cohort_month,
220
+ DATE_TRUNC('month', activity_date) as activity_month
221
+ FROM user_activity
222
+ ),
223
+ cohort_sizes AS (
224
+ SELECT
225
+ cohort_month,
226
+ COUNT(DISTINCT user_id) as cohort_size
227
+ FROM user_cohorts
228
+ GROUP BY cohort_month
229
+ )
230
+ SELECT
231
+ uc.cohort_month,
232
+ uc.activity_month,
233
+ EXTRACT(MONTH FROM AGE(uc.activity_month, uc.cohort_month)) as months_since_signup,
234
+ COUNT(DISTINCT uc.user_id) as active_users,
235
+ cs.cohort_size,
236
+ ROUND(100.0 * COUNT(DISTINCT uc.user_id) / cs.cohort_size, 2) as retention_pct
237
+ FROM user_cohorts uc
238
+ JOIN cohort_sizes cs ON uc.cohort_month = cs.cohort_month
239
+ GROUP BY uc.cohort_month, uc.activity_month, cs.cohort_size
240
+ ORDER BY uc.cohort_month, months_since_signup;
241
+
242
+ -- Time-series gap filling
243
+ SELECT
244
+ date_series.date,
245
+ COALESCE(s.revenue, 0) as revenue,
246
+ AVG(s.revenue) OVER (
247
+ ORDER BY date_series.date
248
+ ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
249
+ ) as ma_7day
250
+ FROM generate_series(
251
+ '2024-01-01'::DATE,
252
+ '2024-12-31'::DATE,
253
+ '1 day'::INTERVAL
254
+ ) AS date_series(date)
255
+ LEFT JOIN sales s ON date_series.date = s.sale_date;
256
+ ```
257
+
258
+ ## Conditional Aggregation with Windows
259
+
260
+ ```sql
261
+ -- Filter within window function
262
+ SELECT
263
+ product_id,
264
+ sale_date,
265
+ quantity,
266
+ SUM(quantity) FILTER (WHERE quantity > 10) OVER (
267
+ PARTITION BY product_id
268
+ ORDER BY sale_date
269
+ ) as cumulative_large_orders,
270
+ COUNT(*) FILTER (WHERE quantity > 100) OVER (
271
+ PARTITION BY product_id
272
+ ) as total_bulk_orders
273
+ FROM sales;
274
+
275
+ -- Multiple conditions
276
+ SELECT
277
+ customer_id,
278
+ order_date,
279
+ total,
280
+ COUNT(*) FILTER (WHERE total > 1000) OVER (
281
+ PARTITION BY customer_id
282
+ ) as high_value_order_count,
283
+ AVG(total) FILTER (WHERE total < 100) OVER (
284
+ PARTITION BY customer_id
285
+ ) as avg_small_order_value
286
+ FROM orders;
287
+ ```
288
+
289
+ ## Performance Considerations
290
+
291
+ ```sql
292
+ -- Avoid multiple window passes - combine into one
293
+ -- Bad: Multiple scans
294
+ SELECT
295
+ product_id,
296
+ (SELECT AVG(price) FROM products) as avg_price,
297
+ (SELECT MAX(price) FROM products) as max_price
298
+ FROM products;
299
+
300
+ -- Good: Single window pass
301
+ SELECT DISTINCT
302
+ AVG(price) OVER () as avg_price,
303
+ MAX(price) OVER () as max_price
304
+ FROM products;
305
+
306
+ -- Materialize expensive windows
307
+ CREATE MATERIALIZED VIEW product_rankings AS
308
+ SELECT
309
+ product_id,
310
+ category,
311
+ sales_count,
312
+ RANK() OVER (PARTITION BY category ORDER BY sales_count DESC) as category_rank,
313
+ PERCENT_RANK() OVER (ORDER BY sales_count DESC) as overall_percentile
314
+ FROM product_sales_summary;
315
+
316
+ CREATE INDEX idx_product_rankings_category ON product_rankings(category, category_rank);
317
+ ```
318
+
319
+ ## Common Patterns
320
+
321
+ 1. **Top N per Group**: Use ROW_NUMBER() with WHERE rn <= N
322
+ 2. **Running Totals**: SUM() OVER (ORDER BY date)
323
+ 3. **Moving Averages**: AVG() with ROWS BETWEEN N PRECEDING
324
+ 4. **Session Analysis**: LAG() to detect time gaps
325
+ 5. **Deduplication**: ROW_NUMBER() OVER (PARTITION BY key ORDER BY priority) WHERE rn = 1
326
+ 6. **Percentiles**: PERCENT_RANK() or PERCENTILE_CONT()
327
+ 7. **Year-over-Year**: LAG(value, 12) OVER (ORDER BY month)
328
+ 8. **Cohort Analysis**: PARTITION BY cohort_date, aggregate over activity periods
@@ -3,7 +3,7 @@ name: subagent-exec
3
3
  description: "Executes approved loopx implementation plans with fresh subagents per independent task and staged review. Not for planning, unclear requirements, or tightly coupled edits."
4
4
  when_to_use: "approved implementation plan, independent tasks, subagent execution, staged spec review, code quality review, parallel-capable execution"
5
5
  metadata:
6
- version: "0.2.8"
6
+ version: "0.2.10"
7
7
  ---
8
8
 
9
9
  # Subagent Exec
@@ -3,7 +3,7 @@ name: tdd
3
3
  description: "Guides feature and bugfix implementation through a failing test before production code and red-green-refactor discipline. Not for generated files or throwaway prototypes."
4
4
  when_to_use: "tdd, failing test first, feature implementation, bugfix, regression test, red green refactor, 测试先行"
5
5
  metadata:
6
- version: "0.2.8"
6
+ version: "0.2.10"
7
7
  ---
8
8
 
9
9
  # Test-Driven Development (TDD)
@@ -3,7 +3,7 @@ name: verify
3
3
  description: "Requires fresh verification evidence before claiming work is complete, fixed, passing, review-ready, or ready to commit. Not for speculative confidence or stale results."
4
4
  when_to_use: "verify, completion claim, fixed claim, tests pass, review-ready, commit, fresh evidence, 验证, 完成前检查"
5
5
  metadata:
6
- version: "0.2.8"
6
+ version: "0.2.10"
7
7
  ---
8
8
 
9
9
  # Verification Before Completion
package/src/cli.mjs CHANGED
@@ -30,7 +30,7 @@ function usage() {
30
30
  ' loopx status [slug] [--json]',
31
31
  ' loopx next <slug> [--json]',
32
32
  ' loopx setup-context',
33
- ' loopx install-skills [--target <codex|claude|all>] [--project] [--mode <copy|symlink>] [--dir <path>] [--yes] [--dry-run] [--json]',
33
+ ' loopx install-skills [--target <codex|claude|all>] [--project] [--mode <copy|symlink>] [--dir <path>] [--add-agent-guidance] [--yes] [--dry-run] [--json]',
34
34
  ' loopx doctor [--json]',
35
35
  ' loopx migrate',
36
36
  ' loopx repair-install',
@@ -60,6 +60,7 @@ async function promptInstallOptions() {
60
60
  const targetAnswer = (await rl.question('Install targets (codex, claude, all) [all]: ')).trim().toLowerCase();
61
61
  const projectAnswer = (await rl.question('Install Claude project skills instead of user skills? [y/N]: ')).trim().toLowerCase();
62
62
  const modeAnswer = (await rl.question('Install mode (copy, symlink) [copy]: ')).trim().toLowerCase();
63
+ const guidanceAnswer = (await rl.question('Add loopx guidance to Codex AGENTS.md / Claude CLAUDE.md? [y/N]: ')).trim().toLowerCase();
63
64
  const proceedAnswer = (await rl.question('Proceed? [y/N]: ')).trim().toLowerCase();
64
65
  if (proceedAnswer !== 'y' && proceedAnswer !== 'yes') {
65
66
  return null;
@@ -69,6 +70,7 @@ async function promptInstallOptions() {
69
70
  targets: target === 'all' ? ['codex', 'claude'] : [target],
70
71
  project: projectAnswer === 'y' || projectAnswer === 'yes',
71
72
  installMethod: modeAnswer === 'symlink' ? 'symlink' : 'copy',
73
+ agentGuidance: guidanceAnswer === 'y' || guidanceAnswer === 'yes',
72
74
  };
73
75
  } finally {
74
76
  rl.close();
@@ -83,6 +85,7 @@ function installOptionsFromArgs(options) {
83
85
  project: Boolean(options.get('--project')),
84
86
  installMethod: options.get('--mode') === 'symlink' ? 'symlink' : 'copy',
85
87
  dir: options.get('--dir'),
88
+ agentGuidance: Boolean(options.get('--add-agent-guidance') || options.get('--add-codex-agents-guidance')),
86
89
  };
87
90
  }
88
91
 
@@ -2,6 +2,7 @@ import { existsSync } from 'node:fs';
2
2
  import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
3
  import { dirname, join, relative, resolve } from 'node:path';
4
4
 
5
+ import { discoverLoopxContextArtifacts } from './loopx-context-artifacts.mjs';
5
6
  import { inspectWorkspaceContext, resolveWorkspaceContextPaths } from './workspace-context.mjs';
6
7
 
7
8
  export const CONTEXT_MANIFEST_SCHEMA_VERSION = 1;
@@ -64,6 +65,53 @@ function stableRows(rows) {
64
65
  .slice(0, MAX_MANIFEST_ROWS);
65
66
  }
66
67
 
68
+ async function loopxRepoContextRows(cwd, stage, priorityStart) {
69
+ const artifacts = await discoverLoopxContextArtifacts(cwd);
70
+ const rows = [];
71
+ let priority = priorityStart;
72
+ if (artifacts.specsRoot) {
73
+ rows.push(row(cwd, {
74
+ stage,
75
+ kind: 'repo-specs',
76
+ path: artifacts.specsRoot,
77
+ reason: 'long_lived_loopx_specs_directory',
78
+ priority: priority++,
79
+ required: false,
80
+ }));
81
+ }
82
+ for (const spec of artifacts.specFiles) {
83
+ rows.push(row(cwd, {
84
+ stage,
85
+ kind: 'repo-spec',
86
+ path: spec.path,
87
+ reason: 'long_lived_loopx_spec',
88
+ priority: priority++,
89
+ required: false,
90
+ }));
91
+ }
92
+ if (artifacts.memorySummary) {
93
+ rows.push(row(cwd, {
94
+ stage,
95
+ kind: 'memory-summary',
96
+ path: artifacts.memorySummary.path,
97
+ reason: 'curated_loopx_project_memory',
98
+ priority: priority++,
99
+ required: false,
100
+ }));
101
+ }
102
+ if (artifacts.memoryIndex) {
103
+ rows.push(row(cwd, {
104
+ stage,
105
+ kind: 'memory-index',
106
+ path: artifacts.memoryIndex.path,
107
+ reason: 'curated_loopx_memory_retrieval_index',
108
+ priority: priority++,
109
+ required: false,
110
+ }));
111
+ }
112
+ return rows;
113
+ }
114
+
67
115
  export async function writeContextManifest(path, rows) {
68
116
  const text = stableRows(rows).map((item) => JSON.stringify(item)).join('\n');
69
117
  await mkdir(dirname(path), { recursive: true });
@@ -141,6 +189,7 @@ export async function generateBuildContextManifest({ cwd, root, state, slug }) {
141
189
  row(cwd, { stage: 'build', kind: 'domain-context', path: contextPaths.domainGlossary, reason: 'domain_vocabulary', priority: 34, required: contextSetup.status !== 'missing' }),
142
190
  row(cwd, { stage: 'build', kind: 'agent-domain', path: contextPaths.agentDomain, reason: 'agent_context_rules', priority: 35, required: false }),
143
191
  row(cwd, { stage: 'build', kind: 'workspace-config', path: join(cwd, '.loopx', 'config.json'), reason: 'project_rules_spec_sources_and_verification_commands', priority: 36, required: false }),
192
+ ...await loopxRepoContextRows(cwd, 'build', 37),
144
193
  ];
145
194
  const manifestPath = buildContextManifestPath(root);
146
195
  await writeContextManifest(manifestPath, rows);
@@ -163,7 +212,8 @@ export async function generateReviewContextManifest({ cwd, root, state, slug })
163
212
  row(cwd, { stage: 'review', kind: 'build-support', path: join(root, 'build-support'), reason: 'build_gate_evidence', priority: 30, required: false }),
164
213
  row(cwd, { stage: 'review', kind: 'agent-domain', path: contextPaths.agentDomain, reason: 'agent_context_rules', priority: 31, required: false }),
165
214
  row(cwd, { stage: 'review', kind: 'workspace-config', path: join(cwd, '.loopx', 'config.json'), reason: 'project_rules_spec_sources_and_verification_commands', priority: 32, required: false }),
166
- row(cwd, { stage: 'review', kind: 'state', path: join(root, 'state.json'), reason: 'workflow_state', priority: 40 }),
215
+ ...await loopxRepoContextRows(cwd, 'review', 33),
216
+ row(cwd, { stage: 'review', kind: 'state', path: join(root, 'state.json'), reason: 'workflow_state', priority: 60 }),
167
217
  ];
168
218
  const manifestPath = reviewContextManifestPath(root);
169
219
  await writeContextManifest(manifestPath, rows);
@@ -29,8 +29,13 @@ const LOOPX_SKILLS = [
29
29
  'tdd',
30
30
  'verify',
31
31
  'doc-readability',
32
+ 'requirement-analyzer',
32
33
  'go-style',
33
34
  'kratos',
35
+ 'api-designer',
36
+ 'architecture-designer',
37
+ 'sql-style',
38
+ 'cli-developer',
34
39
  ];
35
40
  const LOOPX_INSTALLATION_IDENTITY = 'loopx';
36
41
  const LOOPX_MANAGED_SCRIPT_ITEMS = [
@@ -49,6 +54,18 @@ const LOOPX_MANAGED_SCRIPT_ITEMS = [
49
54
  targetRelativePath: '.claude/hooks/loopx-workflow-hook.mjs',
50
55
  },
51
56
  ];
57
+ const LOOPX_AGENT_GUIDANCE_BLOCK_ID = 'specs-and-memory-context';
58
+ const LOOPX_AGENT_GUIDANCE_HEADING = '## loopx Specs And Memory';
59
+ const LOOPX_AGENT_GUIDANCE_CONTENT = [
60
+ LOOPX_AGENT_GUIDANCE_HEADING,
61
+ '',
62
+ 'When working in a repository that uses loopx:',
63
+ '',
64
+ '- If `docs/loopx/specs/` exists, inspect relevant specs before clarify, spec, plan, implementation, or review. Use `docs/loopx/specs/index.md` as a map when present, but do not require it.',
65
+ '- If `.loopx/memory/MEMORY.md` exists, read it as curated project memory.',
66
+ '- If `.loopx/memory/index.jsonl` exists, use it only to find relevant active memory cards.',
67
+ '- Treat current user instructions and named source documents as highest priority, repo specs as binding long-lived rules, and memory as advisory context.',
68
+ ].join('\n');
52
69
  const LOOPX_GOVERNED_SOURCE_ITEMS = [
53
70
  {
54
71
  name: 'loopx-plugin-manifest',
@@ -128,6 +145,19 @@ export function getClaudeSettingsPath(env = process.env) {
128
145
  return resolve(env.LOOPX_CLAUDE_SETTINGS_PATH || join(home, '.claude', 'settings.json'));
129
146
  }
130
147
 
148
+ export function getCodexAgentsPath(env = process.env) {
149
+ const home = resolve(env.LOOPX_HOME || env.HOME || process.cwd());
150
+ return resolve(env.LOOPX_CODEX_AGENTS_PATH || join(home, '.codex', 'AGENTS.md'));
151
+ }
152
+
153
+ export function getClaudeAgentsPath(env = process.env, options = {}) {
154
+ if (options.project === true) {
155
+ return resolve(env.LOOPX_INSTALL_CWD || process.cwd(), 'CLAUDE.md');
156
+ }
157
+ const home = resolve(env.LOOPX_HOME || env.HOME || process.cwd());
158
+ return resolve(env.LOOPX_CLAUDE_AGENTS_PATH || join(home, '.claude', 'CLAUDE.md'));
159
+ }
160
+
131
161
  export function getSkillLockPath(env = process.env) {
132
162
  return resolve(env.LOOPX_SKILL_LOCK_PATH || join(getAgentsRoot(env), '.skill-lock.json'));
133
163
  }
@@ -454,6 +484,85 @@ async function removeInstalledFile(path) {
454
484
  await rm(path, { force: true });
455
485
  }
456
486
 
487
+ function managedBlockMarkers(id) {
488
+ return {
489
+ start: `<!-- loopx:managed:block ${id} -->`,
490
+ end: `<!-- /loopx:managed:block ${id} -->`,
491
+ };
492
+ }
493
+
494
+ function renderManagedBlock(id, content) {
495
+ const markers = managedBlockMarkers(id);
496
+ return `${markers.start}\n${content.trim()}\n${markers.end}`;
497
+ }
498
+
499
+ function managedBlockPattern(id) {
500
+ const escaped = String(id).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
501
+ return new RegExp(`<!--\\s*loopx:managed:block\\s+${escaped}\\s*-->[\\s\\S]*?<!--\\s*\\/loopx:managed:block\\s+${escaped}\\s*-->`);
502
+ }
503
+
504
+ function upsertManagedBlock(existing, id, content) {
505
+ const nextBlock = renderManagedBlock(id, content);
506
+ const pattern = managedBlockPattern(id);
507
+ if (pattern.test(existing)) {
508
+ const nextContent = existing.replace(pattern, nextBlock);
509
+ return {
510
+ content: nextContent,
511
+ changed: nextContent !== existing,
512
+ existed: true,
513
+ };
514
+ }
515
+ const trimmed = existing.trimEnd();
516
+ const contentWithBlock = trimmed
517
+ ? `${trimmed}\n\n${nextBlock}\n`
518
+ : `${nextBlock}\n`;
519
+ return {
520
+ content: contentWithBlock,
521
+ changed: true,
522
+ existed: false,
523
+ };
524
+ }
525
+
526
+ export async function installAgentGuidanceFile(path, options = {}) {
527
+ const content = options.content || LOOPX_AGENT_GUIDANCE_CONTENT;
528
+ const id = options.id || LOOPX_AGENT_GUIDANCE_BLOCK_ID;
529
+ const existing = existsSync(path) ? await readFile(path, 'utf8') : '';
530
+ const existed = existsSync(path);
531
+ const next = upsertManagedBlock(existing, id, content);
532
+ if (!next.changed) {
533
+ return { status: 'already-current', path };
534
+ }
535
+ await ensureDir(dirname(path));
536
+ await writeFile(path, `${next.content.replace(/\s+$/, '')}\n`);
537
+ return {
538
+ status: next.existed ? 'updated' : (existed ? 'installed' : 'created'),
539
+ path,
540
+ };
541
+ }
542
+
543
+ function agentGuidanceEnabled(options = {}) {
544
+ return Boolean(options.agentGuidance || options.codexAgentsGuidance);
545
+ }
546
+
547
+ export async function installAgentGuidance(env = process.env, options = {}) {
548
+ const target = options.target || env.LOOPX_INSTALL_TARGET || 'codex';
549
+ const enabled = agentGuidanceEnabled(options);
550
+ const result = {};
551
+ if (target === 'codex' || target === 'all') {
552
+ const path = getCodexAgentsPath(env);
553
+ result.codex = enabled
554
+ ? await installAgentGuidanceFile(path)
555
+ : { status: 'recommended', path };
556
+ }
557
+ if (target === 'claude' || target === 'all') {
558
+ const path = getClaudeAgentsPath(env, options);
559
+ result.claude = enabled
560
+ ? await installAgentGuidanceFile(path)
561
+ : { status: 'recommended', path };
562
+ }
563
+ return result;
564
+ }
565
+
457
566
  async function canonicalTargetOwnership(skillName, env = process.env, options = {}) {
458
567
  const targetDir = installedSkillDir(skillName, env);
459
568
  const sourceDir = skillSourceDir(skillName, env, options.skillSourceRoot);
@@ -736,11 +845,16 @@ export async function installBundledSkills(env = process.env, options = {}) {
736
845
  items: nextTemplateItems,
737
846
  });
738
847
  const templateGovernance = await inspectTemplateGovernance(baselinePath);
848
+ const agentGuidance = await installAgentGuidance(env, {
849
+ ...options,
850
+ target: options.target || env.LOOPX_INSTALL_TARGET || 'codex',
851
+ });
739
852
  return {
740
853
  ok: conflicts.length === 0,
741
854
  installed,
742
855
  conflicts,
743
856
  skipped,
857
+ agentGuidance,
744
858
  templateGovernance,
745
859
  inspection: await inspectInstallState(env),
746
860
  };