faure 0.1.2 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a677cda056e9fa3effb5877c212c24f30315c9c23580119ca2dfe7f2919d372e
4
- data.tar.gz: 985a5cf2150c9b54ebc78d8cc3655069598e326dcaebc32863eefd2f35cd6c97
3
+ metadata.gz: e7329e214012017c8b3e222c24b70410e57f4ae40c24b070b37d22edf56ba699
4
+ data.tar.gz: c141ee9f3e2f7c11e156e7c7091c1a6e7463c07b9d7abd6c0696dccde2bb06b2
5
5
  SHA512:
6
- metadata.gz: 126e4830ed2f38fcbb0cc0f9576c23adb4a46d6db76cf6f447309d4804c0f854db217f398824048f062270cf98d50749421549c2c5a9050fd5e920481dacfe09
7
- data.tar.gz: 714e4cda5268a5ba8c4420d04033fc5d76efd8730e0b7f3df251399d2ee47d6543638a57b7fcf12e09676886e68c187f40461b1a4ecc0a16bcaf12853e809e09
6
+ metadata.gz: 460065142943c13c00c8bd76b9d6fcadc4c68c59e9955ce8b23f4bee4d8bc284d8b51d4e38451b9ec44426556ede92e3b147625c12efe1691f403d3baad45c28
7
+ data.tar.gz: b11f9a311b88924e905c6a658123ceac2cdca7b86cef5931dd056d57927fbc366eb86a855d0dd92ddbac032b0226d56d67a9f7058414f5961e6a34051fd977a7
data/lib/faure/codeur.rb CHANGED
@@ -17,6 +17,7 @@ module Faure
17
17
  @noteable_type = payload.dig('object_attributes', 'noteable_type')
18
18
  @author_id = payload.dig('user', 'id').to_i
19
19
  @issue_iid = payload.dig('issue', 'iid').to_s
20
+ @agents_md = load_agents_md
20
21
  end
21
22
 
22
23
  def run
@@ -34,7 +35,7 @@ module Faure
34
35
  .select { |n| n['body'].include?('🤔 **faure demande :**') }
35
36
  .last
36
37
 
37
- mode = if pending_question && !@note.include?('@faure-codeur')
38
+ mode = if pending_question && !@note.include?("@#{Config::BOT_USERNAME}")
38
39
  :answering_question
39
40
  else
40
41
  :new_instruction
@@ -75,15 +76,17 @@ module Faure
75
76
  abort '[faure] Pas un commentaire sur issue, abandon.' unless @noteable_type == 'Issue'
76
77
  abort '[faure] Commentaire du bot, abandon.' if @author_id == Config::BOT_ID
77
78
  abort '[faure] Commentaire faure:context, abandon.' if @note.include?('<!-- faure:context -->')
78
- abort '[faure] Pas de mention @faure-codeur ni réponse attendue.' \
79
- unless @note.include?('@faure-codeur') || @note.match?(/^[^@]/)
79
+ abort "[faure] Pas de mention @#{Config::BOT_USERNAME} ni réponse attendue." \
80
+ unless @note.include?("@#{Config::BOT_USERNAME}") || @note.match?(/^[^@]/)
80
81
  end
81
82
 
82
83
  def system_prompt
83
84
  lang_instruction = Config::LANG == 'fr' ? 'Réponds en français.' : 'Reply in English.'
85
+ agents_section = @agents_md ? "\n## Project conventions\n\n#{@agents_md}\n" : ''
84
86
  <<~SYSTEM
85
87
  Tu es un agent codeur expert. Tu reçois une tâche de développement et tu dois produire les modifications de fichiers nécessaires.
86
88
  #{lang_instruction}
89
+ #{agents_section}
87
90
  Réponds UNIQUEMENT avec des balises XML :
88
91
  - <file path="chemin/fichier">contenu complet du fichier</file> pour chaque fichier modifié
89
92
  - <question>ta question</question> si tu as besoin de clarifications (peut coexister avec des <file>)
@@ -91,6 +94,29 @@ module Faure
91
94
  SYSTEM
92
95
  end
93
96
 
97
+ def load_agents_md
98
+ faure_path = File.join(Config::PROJECT_DIR, '.faure', 'AGENTS.md')
99
+ root_path = File.join(Config::PROJECT_DIR, 'AGENTS.md')
100
+ path = [faure_path, root_path].find { |p| File.exist?(p) }
101
+ unless path
102
+ warn '[faure] No AGENTS.md found (.faure/AGENTS.md or AGENTS.md) — using generic instructions'
103
+ return nil
104
+ end
105
+
106
+ File.read(path).gsub(/^@(\S+)/) do |_|
107
+ ref = File.join(Config::PROJECT_DIR, $1)
108
+ if File.exist?(ref)
109
+ File.read(ref)
110
+ else
111
+ warn "[faure] .faure/AGENTS.md: reference @#{$1} not found, skipping"
112
+ ''
113
+ end
114
+ end
115
+ rescue => e
116
+ warn "[faure] Could not read .faure/AGENTS.md : #{e.message}"
117
+ nil
118
+ end
119
+
94
120
  def build_prompt(mode, issue, previous_summary, pending_question)
95
121
  case mode
96
122
  when :answering_question
@@ -119,7 +145,7 @@ module Faure
119
145
  Description :
120
146
  #{issue['description']}
121
147
 
122
- #{@note.include?('@faure-codeur') ? "Instruction : #{@note}" : ''}
148
+ #{@note.include?("@#{Config::BOT_USERNAME}") ? "Instruction : #{@note}" : ''}
123
149
  PROMPT
124
150
  end
125
151
  end
data/lib/faure/config.rb CHANGED
@@ -1,11 +1,33 @@
1
+ require 'net/http'
2
+ require 'json'
3
+ require 'uri'
4
+
1
5
  module Faure
2
6
  module Config
3
7
  # Obligatoires
4
8
  API_TOKEN = ENV.fetch('FAURE_API_TOKEN')
5
9
  REVIEWER_ID = ENV.fetch('FAURE_REVIEWER_ID').to_i
6
- BOT_ID = ENV.fetch('FAURE_BOT_ID', '0').to_i
7
10
  OPENAI_URL = ENV.fetch('OPENAI_API_URL', 'http://host.containers.internal:8080')
8
11
 
12
+ # GitLab CI — injectées automatiquement
13
+ GITLAB_URL = ENV.fetch('CI_SERVER_URL', 'https://gitlab.com')
14
+ PROJECT_ID = ENV.fetch('CI_PROJECT_ID')
15
+ PROJECT_DIR = ENV.fetch('CI_PROJECT_DIR', Dir.pwd)
16
+
17
+ # Résolution dynamique du bot via /api/v4/user (FAURE_BOT_ID déprécié)
18
+ _bot = begin
19
+ uri = URI("#{GITLAB_URL}/api/v4/user")
20
+ req = Net::HTTP::Get.new(uri)
21
+ req['PRIVATE-TOKEN'] = API_TOKEN
22
+ res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') { |h| h.request(req) }
23
+ JSON.parse(res.body)
24
+ rescue => e
25
+ warn "[faure] Impossible de résoudre le bot via l'API : #{e.message}"
26
+ {}
27
+ end
28
+ BOT_ID = (_bot['id'] || 0).to_i
29
+ BOT_USERNAME = _bot['username'] || 'faure-bot'
30
+
9
31
  # Modèles — configurables par projet
10
32
  CODER_MODEL = ENV.fetch('FAURE_CODER_MODEL', 'mlx-community/Qwen3-Coder-30B-A3B-Instruct-4bit')
11
33
  AGENT_MODEL = ENV.fetch('FAURE_AGENT_MODEL', 'mlx-community/Qwen3.5-4B-4bit')
@@ -14,10 +36,5 @@ module Faure
14
36
  # Options
15
37
  TARGET_BRANCH = ENV.fetch('FAURE_TARGET_BRANCH', 'main')
16
38
  LANG = ENV.fetch('FAURE_LANG', 'fr')
17
-
18
- # GitLab CI — injectées automatiquement
19
- GITLAB_URL = ENV.fetch('CI_SERVER_URL', 'https://gitlab.com')
20
- PROJECT_ID = ENV.fetch('CI_PROJECT_ID')
21
- PROJECT_DIR = ENV.fetch('CI_PROJECT_DIR', Dir.pwd)
22
39
  end
23
40
  end
data/lib/faure/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Faure
2
- VERSION = '0.1.2'
2
+ VERSION = '0.2.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faure
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roland Laurès
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-16 00:00:00.000000000 Z
11
+ date: 2026-03-19 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Faure orchestre un agent codeur et un agent git via GitLab CI/CD et un
14
14
  backend OpenAI-compatible (mlx_lm, Ollama, OpenAI...).